This commit is contained in:
Ovidiu U
2026-03-13 09:23:15 +00:00
commit 55ae54d2c5
21 changed files with 1071 additions and 0 deletions

46
.htaccess Normal file
View File

@@ -0,0 +1,46 @@
# Enable rewrite engine
RewriteEngine On
# Force HTTPS (handled by Pangolin/Cloudflare, but just in case)
# RewriteCond %{HTTPS} off
# RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Remove trailing slashes (except for directories)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]
# Security headers
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
Header set Referrer-Policy "strict-origin-when-cross-origin"
</IfModule>
# Prevent directory listing
Options -Indexes
# Block access to hidden files
<FilesMatch "^\.">
Require all denied
</FilesMatch>
# Cache static assets
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType image/svg+xml "access plus 1 year"
ExpiresByType image/webp "access plus 1 year"
ExpiresByType font/woff2 "access plus 1 year"
</IfModule>
# Gzip compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE text/html text/css application/javascript text/plain application/json image/svg+xml
</IfModule>
# Custom error pages
ErrorDocument 404 /404.php

10
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,10 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

14
.idea/deployment.xml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PublishConfigData" serverName="ionos - uovidiu.com" remoteFilesAllowedToDisappearOnAutoupload="false">
<serverData>
<paths name="ionos - uovidiu.com">
<serverdata>
<mappings>
<mapping deploy="/" local="$PROJECT_DIR$" web="/" />
</mappings>
</serverdata>
</paths>
</serverData>
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/uovidiu.iml" filepath="$PROJECT_DIR$/.idea/uovidiu.iml" />
</modules>
</component>
</project>

24
.idea/php-docker-settings.xml generated Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpDockerContainerSettings">
<list>
<map>
<entry key="7e064064-f569-4fa2-87dd-a1bd0db5798f">
<value>
<DockerContainerSettings>
<option name="version" value="1" />
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/opt/project" />
<option name="hostPath" value="$PROJECT_DIR$" />
</DockerVolumeBindingImpl>
</list>
</option>
</DockerContainerSettings>
</value>
</entry>
</map>
</list>
</component>
</project>

34
.idea/php.xml generated Normal file
View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpCodeSniffer">
<phpcs_settings>
<phpcs_by_interpreter asDefaultInterpreter="true" interpreter_id="7e064064-f569-4fa2-87dd-a1bd0db5798f" timeout="30000" />
</phpcs_settings>
</component>
<component name="PhpStan">
<PhpStan_settings>
<phpstan_by_interpreter asDefaultInterpreter="true" interpreter_id="7e064064-f569-4fa2-87dd-a1bd0db5798f" timeout="60000" />
</PhpStan_settings>
</component>
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="Psalm">
<Psalm_settings>
<psalm_fixer_by_interpreter asDefaultInterpreter="true" interpreter_id="7e064064-f569-4fa2-87dd-a1bd0db5798f" timeout="60000" />
</Psalm_settings>
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

8
.idea/uovidiu.iml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

14
.idea/webServers.xml generated Normal file
View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="WebServers">
<option name="servers">
<webServer id="0386710e-a468-412b-a338-359f07683c43" name="ionos - uovidiu.com" url="http://uovidiu.com">
<fileTransfer rootFolder="/root/portfolio" accessType="SFTP" host="87.106.99.10" port="22" sshConfigId="c49579e5-6b45-4cda-80e7-5a0d2a1ba815" sshConfig="root@87.106.99.10:22 key" keyPair="true">
<advancedOptions>
<advancedOptions dataProtectionLevel="Private" keepAliveTimeout="0" passiveMode="true" shareSSLContext="true" />
</advancedOptions>
</fileTransfer>
</webServer>
</option>
</component>
</project>

38
404.php Normal file
View File

@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 Ovidiu Ungureanu</title>
<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=JetBrains+Mono:wght@400;700&family=DM+Sans:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<nav class="nav">
<a href="/" class="nav-logo">OU</a>
<div class="nav-links">
<a href="/#about">About</a>
<a href="/#services">Services</a>
<a href="/portfolio">Work</a>
<a href="/#contact">Contact</a>
</div>
</nav>
<header class="hero" style="text-align: center; align-items: center;">
<div class="hero-label">Error 404</div>
<h1>Not<br>Found</h1>
<p class="hero-sub" style="text-align: center;">This page doesn't exist. It probably never did.</p>
<div class="hero-cta">
<a href="/" class="btn">← Back Home</a>
</div>
</header>
<footer class="footer">
<span>© <?php echo date('Y'); ?> Ovidiu Ungureanu</span>
</footer>
</body>
</html>

91
CLAUDE.md Normal file
View File

@@ -0,0 +1,91 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Role
Act as a World-Class Senior Creative Technologist specialising in automotive service industry web design. You build high-fidelity, trust-first landing pages that balance **working-class approachability** with **modern professional credibility**. Every element should communicate: "We know cars. We're honest. We've been here 25 years and we'll be here 25 more." Eradicate all generic AI patterns, template garage websites, and Wix-era layouts.
---
## Always Do First
- **Invoke the `frontend-design` skill** before writing any frontend code, every session, no exceptions.
- **Check `brand_assets/`** before designing. It may contain logos, colour guides, style guides, or images. If assets exist, use them — do not use placeholders where real assets are available. If a logo is present, use it. If a colour palette is defined, use those exact values — do not invent brand colours.
---
## Project Overview
Personal portfolio website for Ovidiu Ungureanu (PHP/MySQL developer). Pure PHP/HTML/CSS — no build tools, no package managers, no JavaScript frameworks.
## Development
No build step required. Serve with any PHP-capable web server (Apache recommended).
**Local development:**
Running using Herd. no extra steps.
Apache is required in production for `.htaccess` rules (`mod_rewrite`, `mod_headers`, `mod_expires`, `mod_deflate`).
### Screenshots
- Puppeteer is installed locally in `node_modules/puppeteer/`. Chrome cache is at `~/.cache/puppeteer/`.
- **Always screenshot from localhost:** `node screenshot.mjs http://localhost:3000`
- Screenshots save automatically to `./temporary screenshots/screenshot-N.png` (auto-incremented, never overwritten).
- Optional label suffix: `node screenshot.mjs http://localhost:3000 label` → saves as `screenshot-N-label.png`.
- `screenshot.mjs` lives in the project root. Use it as-is.
- After screenshotting, read the PNG from `temporary screenshots/` with the Read tool — Claude can see and analyse the image directly.
- When comparing, be specific: "heading is 32px but reference shows ~24px", "card gap is 16px but should be 24px".
- Check: spacing/padding, font size/weight/line-height, colours (exact hex), alignment, border-radius, shadows, image sizing.
## Architecture
### Includes system
Shared partials live in `includes/`:
- `includes/header.php``<!DOCTYPE>`, `<head>`, nav. Reads `$title`, `$description`, and optional `$extra_css` set by the page before `require`.
- `includes/footer.php``<footer>`, `</body>`, `</html>`.
Nav active state is detected automatically via `$_SERVER['REQUEST_URI']` — no manual flags needed.
**Page template pattern:**
```php
<?php
$title = 'Page Title';
$description = 'Meta description.';
$extra_css = '/css/page-specific.css'; // optional
require __DIR__ . '/includes/header.php';
?>
<!-- page body -->
<?php require __DIR__ . '/includes/footer.php'; ?>
```
### Pages
- `index.php` — Homepage (hero, about, services, work CTA, contact)
- `portfolio/index.php` — Portfolio index + Thompson Service Centre case study
- `404.php` — Custom error page
### CSS
- `css/style.css` — Global styles, design tokens, nav, all homepage sections
- `css/portfolio.css` — Portfolio index rows and case study layout (`.cs-*` classes)
### Design system
- **Colors:** `--black: #0a0a0a` · `--white: #f5f0eb` · `--grey: #888` · `--accent: #ff3b00`
- **Fonts:** JetBrains Mono (`--mono`) for headings/labels/UI · DM Sans (`--sans`) for body copy
- **Breakpoints:** 960px (case study reflow), 768px (mobile)
### Adding portfolio projects
Edit the `$projects` array in `portfolio/index.php` and add a corresponding `<article id="…">` case study block below it. Project screenshots go in `img/{project-slug}/`.
Case study image naming convention (see Thompson as reference):
- `hero.png` — full-page screenshot
- `booking-lookup.png`, `booking-confirm.png` — feature screenshots
### Security
All user-facing output uses `htmlspecialchars()`. `.htaccess` sets security headers, disables directory listing, and blocks hidden files. No `http://` URLs in source — all external resources load over HTTPS.

175
css/portfolio.css Normal file
View File

@@ -0,0 +1,175 @@
/* ========================================
PORTFOLIO — extends style.css only
======================================== */
.nav-links a.active {
color: var(--accent);
text-decoration: underline;
}
/* — Project card link — */
.project-card-link {
display: block;
text-decoration: none;
color: var(--white);
}
.project-card-inner {
transition: transform 0.1s, box-shadow 0.1s;
}
.project-card-link:hover .project-card-inner {
transform: translate(-2px, -2px);
box-shadow: 10px 10px 0px var(--black);
}
.project-card-head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 0.5rem;
}
.project-card-year {
font-family: var(--mono);
font-size: 0.75rem;
color: var(--grey-text);
}
.project-card-cta {
display: inline-block;
font-family: var(--mono);
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
color: var(--accent);
margin-top: 1.5rem;
}
/* — Tags — */
.tags-row {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.tag {
font-family: var(--mono);
font-size: 0.62rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
padding: 0.3rem 0.65rem;
background: var(--black);
border: 2px solid var(--grey-dark);
color: var(--grey-text);
}
/* — Case study meta grid — */
.cs-meta-grid {
grid-template-columns: repeat(4, 1fr);
}
.cs-meta-grid .service-item h3 {
font-size: 0.95rem;
text-transform: none;
font-weight: 500;
color: var(--white);
}
/* — Browser chrome — */
.browser-chrome {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.6rem 1rem;
background: var(--black);
border: var(--border-orange);
border-bottom: none;
}
.browser-chrome--sm {
padding: 0.45rem 0.75rem;
margin-bottom: 1rem;
}
.browser-dots {
display: flex;
gap: 0.35rem;
flex-shrink: 0;
}
.browser-dots span {
width: 0.5rem;
height: 0.5rem;
border-radius: 50%;
display: block;
}
.browser-dots span:first-child { background: #ff5f57; }
.browser-dots span:nth-child(2) { background: #febc2e; }
.browser-dots span:nth-child(3) { background: #28c840; }
.browser-url {
font-family: var(--mono);
font-size: 0.65rem;
color: var(--grey-text);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* — Screenshots — */
.screenshot-hero-wrap {
width: 70%;
margin: 0 auto;
border: var(--border-orange);
box-shadow: var(--shadow-orange);
}
.screenshot-hero-wrap .browser-chrome {
border: none;
border-bottom: 2px solid var(--accent);
}
.screenshot-hero {
display: block;
width: 100%;
}
.screenshot-card {
padding: 2rem;
}
.screenshot-screen {
display: block;
width: 100%;
border: var(--border-orange);
border-top: none;
}
/* ========================================
RESPONSIVE
======================================== */
@media (max-width: 960px) {
.cs-meta-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.screenshot-hero-wrap {
width: 100%;
}
.cs-meta-grid {
grid-template-columns: 1fr;
}
}

31
css/screenshot.mjs Normal file
View File

@@ -0,0 +1,31 @@
import puppeteer from 'puppeteer';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const url = process.argv[2] || 'http://localhost:3000';
const label = process.argv[3] || '';
const dir = path.join(__dirname, 'temporary screenshots');
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
let n = 1;
const name = () => label ? `screenshot-${n}-${label}.png` : `screenshot-${n}.png`;
while (fs.existsSync(path.join(dir, name()))) n++;
const filepath = path.join(dir, name());
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'],
});
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
// Allow GSAP + web fonts to settle
await new Promise(r => setTimeout(r, 2500));
await page.screenshot({ path: filepath, fullPage: false });
await browser.close();
console.log(`Saved: ${filepath}`);

317
css/style.css Normal file
View File

@@ -0,0 +1,317 @@
/* ========================================
RESET & BASE (ORANGE & GRAY BRUTALIST)
======================================== */
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
/* Industrial Palette */
--black: #000000;
--white: #ffffff;
--grey-dark: #2b2d30; /* Slate Concrete */
--grey-light: #3f4246; /* Industrial Surface */
--grey-text: #b0b0b0;
--accent: #ff5500; /* Safety Orange */
--mono: 'JetBrains Mono', monospace;
--sans: 'DM Sans', sans-serif;
/* Brutalist Variables */
--border-thick: 4px solid var(--black);
--border-orange: 4px solid var(--accent);
--shadow: 8px 8px 0px var(--black);
--shadow-orange: 8px 8px 0px var(--accent);
}
html {
scroll-behavior: smooth;
font-size: 16px;
}
body {
font-family: var(--sans);
background: var(--grey-dark);
color: var(--white);
line-height: 1.2; /* Tighter brutalist spacing */
-webkit-font-smoothing: antialiased;
padding: 10px; /* Outer frame */
}
a {
color: var(--white);
text-decoration: none;
transition: all 0.1s ease;
}
::selection {
background: var(--accent);
color: var(--black);
}
/* ========================================
NAV - THE CONTROL PANEL
======================================== */
.nav {
position: fixed;
top: 20px;
left: 20px;
right: 20px;
z-index: 100;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: var(--black);
border: var(--border-orange);
box-shadow: var(--shadow);
}
.nav-logo {
font-family: var(--mono);
font-weight: 800;
font-size: 1.25rem;
letter-spacing: -0.02em;
color: var(--accent);
}
.nav-links {
display: flex;
gap: 2rem;
font-family: var(--mono);
font-size: 0.8rem;
text-transform: uppercase;
font-weight: 700;
}
.nav-links a:hover {
color: var(--accent);
text-decoration: underline;
}
/* ========================================
HERO - IMPACT ZONE
======================================== */
.hero {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
padding: 10rem 3rem 6rem;
border: var(--border-thick);
background: var(--grey-light);
margin-bottom: 2rem;
box-shadow: var(--shadow);
}
.hero-label {
font-family: var(--mono);
font-size: 0.85rem;
text-transform: uppercase;
background: var(--accent);
color: var(--black);
display: inline-block;
padding: 4px 12px;
font-weight: 900;
margin-bottom: 2rem;
width: fit-content;
}
.hero h1 {
font-family: var(--mono);
font-size: clamp(3rem, 10vw, 8rem);
font-weight: 800;
line-height: 0.85;
letter-spacing: -0.05em;
margin-bottom: 2rem;
text-transform: uppercase;
color: var(--white);
}
.hero-sub {
font-size: 1.25rem;
color: var(--white);
background: var(--black);
padding: 1rem;
max-width: 550px;
margin-bottom: 3rem;
border-left: 8px solid var(--accent);
}
/* ========================================
BUTTONS - INDUSTRIAL ACTUATORS
======================================== */
.btn {
display: inline-block;
font-family: var(--mono);
font-size: 0.9rem;
font-weight: 900;
text-transform: uppercase;
padding: 1.2rem 2.5rem;
background: var(--accent);
color: var(--black);
border: var(--border-thick);
box-shadow: 4px 4px 0px var(--black);
transition: transform 0.1s, box-shadow 0.1s;
}
.btn:hover {
transform: translate(-2px, -2px);
box-shadow: 7px 7px 0px var(--black);
background: var(--white);
}
.btn-ghost {
background: transparent;
border-color: var(--white);
color: var(--white);
}
.btn-ghost:hover {
background: var(--white);
color: var(--black);
}
/* ========================================
SECTIONS - MODULE CONTAINERS
======================================== */
.section {
padding: 5rem 3rem;
background: var(--grey-light);
border: var(--border-thick);
margin-bottom: 2rem;
box-shadow: var(--shadow);
}
.section-label {
font-family: var(--mono);
font-size: 0.8rem;
text-transform: uppercase;
background: var(--black);
color: var(--accent);
padding: 5px 15px;
display: inline-block;
margin-bottom: 3rem;
font-weight: 900;
}
.large-text {
font-size: 1.75rem;
line-height: 1.3;
margin-bottom: 2rem;
font-weight: 700;
color: var(--accent);
}
.section-content p {
color: var(--white);
margin-bottom: 1.5rem;
line-height: 1.6;
font-size: 1.1rem;
}
/* ========================================
SERVICES - GRID BLOCKS
======================================== */
.services-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
background: transparent;
}
.service-item {
background: var(--black);
padding: 3rem;
border: var(--border-orange);
box-shadow: var(--shadow);
transition: transform 0.2s;
}
.service-item:hover {
transform: scale(1.02);
}
.service-num {
font-family: var(--mono);
font-size: 0.8rem;
color: var(--black);
background: var(--accent);
padding: 2px 8px;
display: inline-block;
margin-bottom: 1.5rem;
font-weight: 900;
}
.service-item h3 {
font-family: var(--mono);
font-size: 1.25rem;
font-weight: 800;
margin-bottom: 1rem;
color: var(--accent);
text-transform: uppercase;
}
.service-item p {
font-size: 1rem;
color: var(--grey-text);
}
/* ========================================
CONTACT & FOOTER
======================================== */
.contact-email {
display: block;
font-family: var(--mono);
font-size: clamp(1.2rem, 5vw, 3.5rem);
font-weight: 900;
margin: 2rem 0;
color: var(--white);
text-decoration: underline;
text-decoration-color: var(--accent);
text-underline-offset: 8px;
}
.contact-email:hover {
color: var(--accent);
background: var(--black);
}
.footer {
background: var(--black);
border: var(--border-orange);
color: var(--grey-text);
padding: 2rem 3rem;
margin-top: 4rem;
}
/* ========================================
RESPONSIVE
======================================== */
@media (max-width: 768px) {
.nav {
top: 10px; left: 10px; right: 10px;
padding: 1rem;
}
.hero, .section {
padding: 6rem 1.5rem 3rem;
}
.services-grid {
grid-template-columns: 1fr;
}
.service-item {
padding: 2rem;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 MiB

BIN
img/thompson/hero.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 MiB

7
includes/footer.php Normal file
View File

@@ -0,0 +1,7 @@
<footer class="footer">
<span>© <?php echo date('Y'); ?> Ovidiu Ungureanu</span>
<span>Built with PHP, no JS frameworks were harmed.</span>
</footer>
</body>
</html>

27
includes/header.php Normal file
View File

@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlspecialchars($title); ?></title>
<meta name="description" content="<?php echo htmlspecialchars($description); ?>">
<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=JetBrains+Mono:wght@400;700&family=DM+Sans:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/css/style.css">
<?php if (!empty($extra_css)): ?>
<link rel="stylesheet" href="<?php echo htmlspecialchars($extra_css); ?>">
<?php endif; ?>
</head>
<body>
<?php $current = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); ?>
<nav class="nav">
<a href="/" class="nav-logo">OU</a>
<div class="nav-links">
<a href="/#about">About</a>
<a href="/#services">Services</a>
<a href="/portfolio"<?php echo ($current === '/portfolio' || $current === '/portfolio/') ? ' class="active"' : ''; ?>>Work</a>
<a href="/#contact">Contact</a>
</div>
</nav>

72
index.php Normal file
View File

@@ -0,0 +1,72 @@
<?php
$title = 'Ovidiu Ungureanu — Web Developer';
$description = 'PHP & MySQL developer based in the UK. Custom web applications, ERP systems, and freelance web development.';
require __DIR__ . '/includes/header.php';
?>
<header class="hero">
<div class="hero-label">Web Developer — UK</div>
<h1>Ovidiu<br>Ungureanu</h1>
<p class="hero-sub">I build web applications that work.<br>PHP, MySQL, clean code, no nonsense.</p>
<div class="hero-cta">
<a href="/portfolio" class="btn">View Work →</a>
<a href="#contact" class="btn btn-ghost">Get in Touch</a>
</div>
</header>
<section id="about" class="section">
<div class="section-label">001 — About</div>
<div class="section-content">
<p class="large-text">I'm a PHP/MySQL developer based in the UK, building custom web applications and backend systems. I run a small web agency focused on delivering practical, well-built solutions.</p>
<p>My work ranges from custom ERP platforms to freelance web projects. I care about performance, maintainability, and getting things done right the first time. No frameworks for the sake of frameworks. No unnecessary complexity.</p>
</div>
</section>
<section id="services" class="section">
<div class="section-label">002 — Services</div>
<div class="services-grid">
<div class="service-item">
<span class="service-num">01</span>
<h3>Custom Web Applications</h3>
<p>Bespoke PHP applications built to your exact requirements. Backend systems, admin panels, data management tools.</p>
</div>
<div class="service-item">
<span class="service-num">02</span>
<h3>Database Design & Optimisation</h3>
<p>MySQL schema design, query optimisation, and performance tuning. Making slow systems fast.</p>
</div>
<div class="service-item">
<span class="service-num">03</span>
<h3>Website Development</h3>
<p>Clean, responsive websites. HTML, CSS, JavaScript, Vue.js. Built to load fast and work everywhere.</p>
</div>
<div class="service-item">
<span class="service-num">04</span>
<h3>Legacy System Modernisation</h3>
<p>Upgrading and refactoring existing PHP codebases. Improving what you already have without starting over.</p>
</div>
</div>
</section>
<section class="section section-cta">
<div class="section-label">003 — Work</div>
<div class="section-content">
<p class="large-text">Selected projects and client work.</p>
<a href="/portfolio" class="btn">View Portfolio →</a>
</div>
</section>
<section id="contact" class="section">
<div class="section-label">004 — Contact</div>
<div class="section-content">
<p class="large-text">Got a project in mind?</p>
<a href="mailto:hello@uovidiu.com" class="contact-email">hello@uovidiu.com</a>
<div class="contact-details">
<span>Peterborough, UK</span>
<span>·</span>
<span>Available for freelance</span>
</div>
</div>
</section>
<?php require __DIR__ . '/includes/footer.php'; ?>

149
portfolio/index.php Normal file
View File

@@ -0,0 +1,149 @@
<?php
$title = 'Portfolio — Ovidiu Ungureanu';
$description = 'Selected web development projects by Ovidiu Ungureanu.';
$extra_css = '/css/portfolio.css';
require __DIR__ . '/../includes/header.php';
?>
<header class="hero">
<div class="hero-label">Portfolio — Selected Work</div>
<h1>Selected<br>Work</h1>
<p class="hero-sub">Client projects and builds.</p>
</header>
<!-- PROJECT INDEX -->
<section class="section">
<div class="section-label">001 — Projects</div>
<?php
$projects = [
[
'num' => '01',
'title' => 'Thompson Service Centre',
'type' => 'Website Redesign',
'year' => '2025',
'tags' => ['HTML', 'Tailwind CSS v4', 'GSAP'],
'anchor' => 'thompson',
],
];
foreach ($projects as $p): ?>
<a href="#<?php echo $p['anchor']; ?>" class="project-card-link">
<div class="service-item project-card-inner">
<span class="service-num"><?php echo $p['num']; ?></span>
<div class="project-card-head">
<h3><?php echo htmlspecialchars($p['title']); ?></h3>
<span class="project-card-year"><?php echo htmlspecialchars($p['year']); ?></span>
</div>
<p><?php echo htmlspecialchars($p['type']); ?></p>
<div class="tags-row">
<?php foreach ($p['tags'] as $tag): ?>
<span class="tag"><?php echo htmlspecialchars($tag); ?></span>
<?php endforeach; ?>
</div>
<span class="project-card-cta">View Case Study →</span>
</div>
</a>
<?php endforeach; ?>
</section>
<!-- CASE STUDY: THOMPSON SERVICE CENTRE -->
<section id="thompson" class="section">
<div class="section-label">Case Study — 01</div>
<p class="large-text">Thompson<br>Service Centre</p>
<div class="services-grid cs-meta-grid">
<div class="service-item">
<span class="service-num">Type</span>
<h3>Website Redesign</h3>
</div>
<div class="service-item">
<span class="service-num">Stack</span>
<h3>HTML · Tailwind CSS v4 · GSAP</h3>
</div>
<div class="service-item">
<span class="service-num">Client</span>
<h3>Thompson Service Centre, Peterborough</h3>
</div>
<div class="service-item">
<span class="service-num">Year</span>
<h3>2025</h3>
</div>
</div>
</section>
<section class="section">
<div class="section-label">Project Overview</div>
<div class="section-content">
<p>Thompson Service Centre needed more than a website — they needed a digital presence that matched 25 years of earned trust. We built a high-fidelity landing page that puts credibility front and centre: a bold hero section, a trust bar loaded with real credentials, and service cards that communicate competence without the usual garage-site clichés. Every element was designed to make a first-time visitor feel like they'd already found their mechanic.</p>
</div>
</section>
<section class="section">
<div class="section-label">Homepage</div>
<div class="screenshot-hero-wrap">
<div class="browser-chrome">
<div class="browser-dots">
<span></span><span></span><span></span>
</div>
<span class="browser-url">thompsonservicecentre.co.uk</span>
</div>
<img
src="/img/thompson/hero.png"
alt="Thompson Service Centre — Homepage"
class="screenshot-hero"
loading="lazy"
>
</div>
</section>
<section class="section">
<div class="section-label">Booking Flow</div>
<div class="services-grid">
<div class="service-item screenshot-card">
<span class="service-num">Vehicle Lookup</span>
<div class="browser-chrome browser-chrome--sm">
<div class="browser-dots">
<span></span><span></span><span></span>
</div>
<span class="browser-url">thompsonservicecentre.co.uk/book</span>
</div>
<img
src="/img/thompson/booking-lookup.png"
alt="Vehicle registration lookup"
class="screenshot-screen"
loading="lazy"
>
</div>
<div class="service-item screenshot-card">
<span class="service-num">Booking Form</span>
<div class="browser-chrome browser-chrome--sm">
<div class="browser-dots">
<span></span><span></span><span></span>
</div>
<span class="browser-url">thompsonservicecentre.co.uk/book</span>
</div>
<img
src="/img/thompson/booking-confirm.png"
alt="Vehicle confirmed and booking form"
class="screenshot-screen"
loading="lazy"
>
</div>
</div>
</section>
<section class="section">
<div class="section-label">Craft & Execution</div>
<div class="section-content">
<p>Built with vanilla HTML, Tailwind CSS v4, and GSAP-powered scroll animations, the page loads fast and feels premium on every device. The design system — rooted in Workshop Navy and Safety Orange — draws from real automotive heritage rather than off-the-shelf templates. Noise overlays, layered gradients, and spring-eased micro-interactions give it a tactile warmth that sets it apart from every other garage site in the postcode.</p>
</div>
</section>
<section class="section section-cta">
<div class="section-label">005 — Contact</div>
<div class="section-content">
<p class="large-text">Got a project in mind?</p>
<a href="mailto:hello@uovidiu.com" class="btn">Get in Touch →</a>
</div>
</section>
<?php require __DIR__ . '/../includes/footer.php'; ?>