diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..6247e42 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,118 @@ + +/* + Custom Styles for Personal Portfolio +*/ + +:root { + --primary-color: #3B82F6; + --secondary-color: #10B981; + --background-color: #F9FAFB; + --surface-color: #FFFFFF; + --text-color: #1F2937; + --light-text-color: #6B7280; + --border-radius: 0.5rem; + --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); +} + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: var(--background-color); + color: var(--text-color); +} + +.navbar { + transition: background-color 0.3s ease-in-out; +} + +.navbar-scrolled { + background-color: var(--surface-color); + box-shadow: var(--shadow-md); +} + +.hero { + color: white; + padding: 8rem 0; + position: relative; + text-align: center; +} + +.hero::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: linear-gradient(to right, rgba(59, 130, 246, 0.8), rgba(16, 185, 129, 0.8)), url('../images/hero-kittens-45170.jpg'); + background-size: cover; + background-position: center; + z-index: -1; +} + +.hero h1 { + font-size: 3.5rem; + font-weight: 700; +} + +.hero p { + font-size: 1.25rem; +} + +.btn-primary { + background-color: var(--primary-color); + border-color: var(--primary-color); + padding: 0.75rem 1.5rem; + border-radius: var(--border-radius); + font-weight: 600; +} + +.btn-primary:hover { + opacity: 0.9; + background-color: var(--primary-color); + border-color: var(--primary-color); +} + +.section { + padding: 5rem 0; +} + +.section-title { + text-align: center; + margin-bottom: 3rem; + font-size: 2.5rem; + font-weight: 700; +} + +.portfolio-card { + border: none; + border-radius: var(--border-radius); + box-shadow: var(--shadow-md); + overflow: hidden; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.portfolio-card:hover { + transform: translateY(-5px); + box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); +} + +.form-control:focus { + box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25); + border-color: var(--primary-color); +} + +footer { + background-color: var(--surface-color); + padding: 2rem 0; + border-top: 1px solid #e5e7eb; +} + +#contact-alert { + position: fixed; + top: 1rem; + right: 1rem; + z-index: 1050; + display: none; + min-width: 250px; +} diff --git a/assets/images/hero-kittens-45170.jpg b/assets/images/hero-kittens-45170.jpg new file mode 100644 index 0000000..ee824de Binary files /dev/null and b/assets/images/hero-kittens-45170.jpg differ diff --git a/assets/images/kitten-4450241.jpg b/assets/images/kitten-4450241.jpg new file mode 100644 index 0000000..f76974d Binary files /dev/null and b/assets/images/kitten-4450241.jpg differ diff --git a/assets/images/kitten-7778872.jpg b/assets/images/kitten-7778872.jpg new file mode 100644 index 0000000..0adc9d0 Binary files /dev/null and b/assets/images/kitten-7778872.jpg differ diff --git a/assets/images/kitten-7778873.jpg b/assets/images/kitten-7778873.jpg new file mode 100644 index 0000000..9184168 Binary files /dev/null and b/assets/images/kitten-7778873.jpg differ diff --git a/assets/images/pexels/355952.jpg b/assets/images/pexels/355952.jpg new file mode 100644 index 0000000..d6c9e22 Binary files /dev/null and b/assets/images/pexels/355952.jpg differ diff --git a/assets/images/pexels/546819.jpg b/assets/images/pexels/546819.jpg new file mode 100644 index 0000000..ea4f106 Binary files /dev/null and b/assets/images/pexels/546819.jpg differ diff --git a/assets/images/pexels/577585.jpg b/assets/images/pexels/577585.jpg new file mode 100644 index 0000000..43151ea Binary files /dev/null and b/assets/images/pexels/577585.jpg differ diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..e6d5f79 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,75 @@ + +document.addEventListener('DOMContentLoaded', function () { + + // Navbar scroll effect + const navbar = document.querySelector('.navbar'); + if (navbar) { + window.addEventListener('scroll', () => { + if (window.scrollY > 50) { + navbar.classList.add('navbar-scrolled'); + } else { + navbar.classList.remove('navbar-scrolled'); + } + }); + } + + // Contact form submission + const contactForm = document.getElementById('contactForm'); + if (contactForm) { + contactForm.addEventListener('submit', function (e) { + e.preventDefault(); + + const name = document.getElementById('name').value.trim(); + const email = document.getElementById('email').value.trim(); + const message = document.getElementById('message').value.trim(); + const alertEl = document.getElementById('contact-alert'); + const submitButton = contactForm.querySelector('button[type="submit"]'); + + if (!name || !email || !message) { + showAlert('Please fill in all fields.', 'danger'); + return; + } + + if (!/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(email)) { + showAlert('Please enter a valid email address.', 'danger'); + return; + } + + const formData = new FormData(this); + submitButton.disabled = true; + submitButton.innerHTML = ' Sending...'; + + fetch('contact.php', { + method: 'POST', + body: formData + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + showAlert('Message sent successfully! I will get back to you soon.', 'success'); + contactForm.reset(); + } else { + showAlert(data.error || 'An error occurred. Please try again.', 'danger'); + } + }) + .catch(() => { + showAlert('A network error occurred. Please try again.', 'danger'); + }) + .finally(() => { + submitButton.disabled = false; + submitButton.innerHTML = 'Send Message'; + }); + }); + } + + function showAlert(message, type) { + const alertEl = document.getElementById('contact-alert'); + alertEl.innerHTML = message; + alertEl.className = `alert alert-${type}`; + alertEl.style.display = 'block'; + + setTimeout(() => { + alertEl.style.display = 'none'; + }, 5000); + } +}); diff --git a/contact.php b/contact.php new file mode 100644 index 0000000..b774dcf --- /dev/null +++ b/contact.php @@ -0,0 +1,43 @@ + false, 'error' => 'An unknown error occurred.']; + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $name = trim($_POST['name'] ?? ''); + $email = trim($_POST['email'] ?? ''); + $message = trim($_POST['message'] ?? ''); + + if (empty($name) || empty($email) || empty($message)) { + $response['error'] = 'Please fill out all fields.'; + } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) { + $response['error'] = 'Please provide a valid email address.'; + } else { + $subject = 'New Contact Form Submission from ' . htmlspecialchars($name); + + // Using MailService to send the contact message. + // The user's email is automatically set as the Reply-To address. + // The recipient is determined by the MailService configuration (defaults to MAIL_TO in .env). + $result = MailService::sendContactMessage($name, $email, $message, null, $subject); + + if (!empty($result['success'])) { + $response['success'] = true; + unset($response['error']); + } else { + // For security, we don't expose detailed mailer errors to the client. + // The error is logged on the server for debugging. + error_log('MailService Error: ' . ($result['error'] ?? 'Unknown error while sending email.')); + $response['error'] = 'Sorry, there was an issue sending your message. Please try again later.'; + } + } +} else { + http_response_code(405); // Method Not Allowed + $response['error'] = 'Invalid request method.'; +} + +echo json_encode($response); diff --git a/includes/pexels.php b/includes/pexels.php new file mode 100644 index 0000000..156d6fd --- /dev/null +++ b/includes/pexels.php @@ -0,0 +1,55 @@ + 0 ? $k : 'Vc99rnmOhHhJAbgGQoKLZtsaIVfkeownoQNbTj78VemUjKh08ZYRbf18'; +} + +function pexels_get($url) { + $ch = curl_init(); + curl_setopt_array($ch, [ + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => [ 'Authorization: '. pexels_key() ], + CURLOPT_TIMEOUT => 15, + ]); + $resp = curl_exec($ch); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + if ($code >= 200 && $code < 300 && $resp) { + return json_decode($resp, true); + } + return null; +} + +function download_to($srcUrl, $destPath) { + if (!is_dir(dirname($destPath))) { + if (!mkdir(dirname($destPath), 0775, true)) { + return false; + } + } + + $ch = curl_init($srcUrl); + $fp = fopen($destPath, 'wb'); + if (!$fp) { + curl_close($ch); + return false; + } + + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 60); + $result = curl_exec($ch); + $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + fclose($fp); + + if ($result && $http_code >= 200 && $http_code < 300) { + return $destPath; + } + + if (file_exists($destPath)) { + unlink($destPath); + } + return false; +} diff --git a/index.php b/index.php index e13ae95..b51a6a1 100644 --- a/index.php +++ b/index.php @@ -1,131 +1,147 @@ - - + - - - New Style - - - - + + + John Doe - Personal Portfolio + + + + + + + + + + + + + + + + + + + + + + -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

Flatlogic AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

-
-
- + + + +
+
+

Welcome to My Digital Space

+

I build modern, responsive, and beautiful web applications.

+ View My Work +
+
+ +
+
+
+

About Me

+
+
+

I am a passionate web developer with a love for clean code and user-centric design. With a background in computer science and several years of experience, I specialize in creating dynamic and engaging websites. When I'm not coding, I enjoy hiking and photography.

+
+
+
+
+ +
+
+

Portfolio

+
+
+
+ A computer screen with code on it. +
+
Project One
+

A brief description of the project, its purpose, and the technologies used. This is a placeholder.

+
+
+
+
+
+ A person's hands typing on a laptop. +
+
Project Two
+

A brief description of the project, its purpose, and the technologies used. This is a placeholder.

+
+
+
+
+
+ Code on a screen with a blurry background. +
+
Project Three
+

A brief description of the project, its purpose, and the technologies used. This is a placeholder.

+
+
+
+
+
+
+ +
+
+

Get In Touch

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ For testing purposes, emails may be sent to a default address. Please configure your own SMTP settings in the .env file for production use. +
+
+
+
+
+
+ + + + + + + + - + \ No newline at end of file