init
This commit is contained in:
46
.htaccess
Normal file
46
.htaccess
Normal 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
10
.idea/.gitignore
generated
vendored
Normal 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
14
.idea/deployment.xml
generated
Normal 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
8
.idea/modules.xml
generated
Normal 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
24
.idea/php-docker-settings.xml
generated
Normal 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
34
.idea/php.xml
generated
Normal 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
8
.idea/uovidiu.iml
generated
Normal 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
6
.idea/vcs.xml
generated
Normal 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
14
.idea/webServers.xml
generated
Normal 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
38
404.php
Normal 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
91
CLAUDE.md
Normal 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
175
css/portfolio.css
Normal 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
31
css/screenshot.mjs
Normal 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
317
css/style.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
img/thompson/booking-confirm.png
Normal file
BIN
img/thompson/booking-confirm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 MiB |
BIN
img/thompson/booking-lookup.png
Normal file
BIN
img/thompson/booking-lookup.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 MiB |
BIN
img/thompson/hero.png
Normal file
BIN
img/thompson/hero.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 MiB |
7
includes/footer.php
Normal file
7
includes/footer.php
Normal 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
27
includes/header.php
Normal 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
72
index.php
Normal 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
149
portfolio/index.php
Normal 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'; ?>
|
||||||
Reference in New Issue
Block a user