Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

20 changed files with 140 additions and 1044 deletions

187
admin.php
View File

@ -1,187 +0,0 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
// Hardcoded credentials
$valid_username = 'admin';
$valid_password = 'password';
$is_logged_in = isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true;
$error_message = '';
$pdo = db();
$editing_fact = null;
// Handle Login/Logout
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['logout'])) {
$_SESSION = [];
session_destroy();
header("Location: admin.php");
exit;
}
if (isset($_POST['login'])) {
$username = $_POST['username'] ?? '';
$password = $_POST['password'] ?? '';
if ($username === $valid_username && $password === $valid_password) {
$_SESSION['loggedin'] = true;
header("Location: admin.php");
exit;
} else {
$error_message = "Invalid username or password.";
}
}
}
// If logged in, handle CRUD actions
if ($is_logged_in && $pdo) {
// Handle Delete
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['delete_id'])) {
$stmt = $pdo->prepare('DELETE FROM nokia_facts WHERE id = ?');
$stmt->execute([$_POST['delete_id']]);
header("Location: admin.php");
exit;
}
// Handle Add/Update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['fact_text'])) {
$fact_text = trim($_POST['fact_text']);
$fact_id = $_POST['fact_id'] ?? null;
if (!empty($fact_text)) {
if ($fact_id) { // Update
$stmt = $pdo->prepare('UPDATE nokia_facts SET fact = ? WHERE id = ?');
$stmt->execute([$fact_text, $fact_id]);
} else { // Add
$stmt = $pdo->prepare('INSERT INTO nokia_facts (fact) VALUES (?)');
$stmt->execute([$fact_text]);
}
}
header("Location: admin.php");
exit;
}
// Handle Edit - Fetch fact for form
if (isset($_GET['edit_id'])) {
$stmt = $pdo->prepare('SELECT id, fact FROM nokia_facts WHERE id = ?');
$stmt->execute([$_GET['edit_id']]);
$editing_fact = $stmt->fetch();
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin - LAMP Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="bg-light">
<?php include 'navbar.php'; ?>
<main class="container my-5">
<?php if (!$is_logged_in): ?>
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="card shadow-sm mb-4">
<div class="card-body p-4">
<h1 class="h3 mb-3 fw-normal text-center">Admin Login</h1>
<?php if ($error_message): ?>
<div class="alert alert-danger"><?php echo $error_message; ?></div>
<?php endif; ?>
<form method="POST" action="admin.php">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="username" name="username" placeholder="Username" required>
<label for="username">Username</label>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="password" name="password" placeholder="Password" required>
<label for="password">Password</label>
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit" name="login">Sign in</button>
</form>
</div>
</div>
</div>
</div>
<?php else: ?>
<div class="card shadow-sm">
<div class="card-header d-flex justify-content-between align-items-center">
<h2 class="h4 mb-0">Nokia Facts Management</h2>
<form method="POST" action="admin.php" class="mb-0">
<button type="submit" name="logout" class="btn btn-sm btn-outline-secondary">Logout <i class="bi bi-box-arrow-right"></i></button>
</form>
</div>
<div class="card-body">
<div class="mb-4 p-3 border rounded bg-light">
<h3 class="h5"><?php echo $editing_fact ? 'Edit Fact' : 'Add New Fact'; ?></h3>
<form method="POST" action="admin.php">
<input type="hidden" name="fact_id" value="<?php echo $editing_fact['id'] ?? ''; ?>">
<div class="input-group">
<textarea class="form-control" name="fact_text" rows="2" placeholder="Enter a new Nokia fact..."><?php echo htmlspecialchars($editing_fact['fact'] ?? ''); ?></textarea>
<button class="btn btn-<?php echo $editing_fact ? 'primary' : 'success'; ?>" type="submit"><?php echo $editing_fact ? 'Update Fact' : 'Add Fact'; ?></button>
<?php if ($editing_fact): ?>
<a href="admin.php" class="btn btn-secondary">Cancel</a>
<?php endif; ?>
</div>
</form>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th scope="col">Fact</th>
<th scope="col" class="text-end">Actions</th>
</tr>
</thead>
<tbody>
<?php
if ($pdo) {
$stmt = $pdo->query('SELECT id, fact, created_at FROM nokia_facts ORDER BY id DESC');
$facts = $stmt->fetchAll();
foreach ($facts as $fact):
?>
<tr>
<td><?php echo htmlspecialchars($fact['fact']); ?></td>
<td class="text-end">
<a href="admin.php?edit_id=<?php echo $fact['id']; ?>" class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i> Edit</a>
<form method="POST" action="admin.php" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this fact?');">
<input type="hidden" name="delete_id" value="<?php echo $fact['id']; ?>">
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i> Delete</button>
</form>
</td>
</tr>
<?php
endforeach;
if (empty($facts)) {
echo "<tr><td colspan='2'>No facts found. Add one above!</td></tr>";
}
} else {
echo "<tr><td colspan='2'>Database connection not available.</td></tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<?php endif; ?>
</main>
<footer class="bg-white text-center text-muted py-4 mt-5 border-top">
<div class="container">
<p class="mb-0">&copy; 2025 LAMP Demo. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>

View File

@ -1,29 +0,0 @@
<?php
header('Content-Type: application/json');
require_once __DIR__.'/../includes/pexels.php';
$qs = isset($_GET['queries']) ? explode(',', $_GET['queries']) : ['vibe','code','seo'];
$out = [];
foreach ($qs as $q) {
$u = 'https://api.pexels.com/v1/search?query=' . urlencode(trim($q)) . '&orientation=landscape&per_page=1&page=1';
$d = pexels_get($u);
if ($d && !empty($d['photos'])) {
$p = $d['photos'][0];
$src = $p['src']['large'] ?? null;
$dest = __DIR__.'/../assets/images/pexels/'.$p['id'].'.jpg';
if ($src) download_to($src, $dest);
$out[] = [
'src' => 'assets/images/pexels/'.$p['id'].'.jpg',
'photographer' => $p['photographer'] ?? 'Unknown',
'photographer_url' => $p['photographer_url'] ?? '',
];
} else {
// Fallback: Picsum
$out[] = [
'src' => 'https://picsum.photos/seed/'.urlencode(trim($q)).'/800/600',
'photographer' => 'Random Picsum',
'photographer_url' => 'https://picsum.photos/'
];
}
}
echo json_encode($out);
?>

View File

@ -1,36 +0,0 @@
body {
background-color: #F8FAFC;
color: #0F172A;
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}
.hero {
padding: 6rem 1rem;
}
.btn-primary {
background-color: #6366F1;
border-color: #6366F1;
}
.btn-primary:hover {
background-color: #4F46E5;
border-color: #4F46E5;
}
.card {
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.05);
transition: box-shadow 0.3s ease-in-out, transform 0.3s ease-in-out;
}
.card:hover {
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -2px rgb(0 0 0 / 0.05);
transform: translateY(-5px);
}
.card img {
aspect-ratio: 16 / 9;
object-fit: cover;
}

View File

@ -1 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;700&display=swap"); body { font-family: "Roboto Slab", serif; background-color: #D2B48C; color: #333; } .navbar, .card, footer { background-color: #8B4513 !important; color: white; border: 3px solid #654321; } .navbar-brand, .nav-link, h1, h2, h5, p, .card-body { color: #fff !important; } .btn-primary { background-color: #008000; color: #fff; border: 2px solid #006400; } .vibe-preview { padding: 2rem; border: 2px solid #654321; margin-top: 2rem; background-color: #F5DEB3; } .vibe-preview h2, .vibe-preview p { color: #333 !important; }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

View File

View File

@ -1,95 +0,0 @@
<?php
$message_status = '';
$error_message = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
require_once __DIR__ . '/mail/MailService.php';
$name = trim($_POST['name'] ?? '');
$email = trim($_POST['email'] ?? '');
$message = trim($_POST['message'] ?? '');
if (empty($name) || empty($email) || empty($message)) {
$error_message = 'Please fill out all fields.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error_message = 'Please enter a valid email address.';
} else {
$to = getenv('MAIL_TO') ?: 'admin@example.com'; // Fallback recipient
$subject = 'New Contact Form Message from ' . $name;
$res = MailService::sendContactMessage($name, $email, $message, $to, $subject);
if (!empty($res['success'])) {
$message_status = 'success';
} else {
$error_message = 'Sorry, there was an error sending your message. Please try again later.';
// Log the actual error: error_log("MailService Error: " . $res['error']);
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Us - LAMP Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="bg-light">
<?php include 'navbar.php'; ?>
<main class="container my-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card shadow-sm">
<div class="card-body p-5">
<h1 class="display-5 text-center mb-4">Contact Us</h1>
<?php if ($message_status === 'success'): ?>
<div class="alert alert-success text-center">
<h4>Thank You!</h4>
<p>Your message has been sent successfully. We will get back to you shortly.</p>
<a href="index.php" class="btn btn-primary">Return Home</a>
</div>
<?php else: ?>
<?php if ($error_message): ?>
<div class="alert alert-danger">- <?php echo $error_message; ?></div>
<?php endif; ?>
<p class="text-center text-muted mb-4">Have a question or feedback? Fill out the form below to get in touch with us.</p>
<form action="contact.php" method="POST">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="name" name="name" placeholder="Your Name" required value="<?php echo htmlspecialchars($_POST['name'] ?? ''); ?>">
<label for="name">Your Name</label>
</div>
<div class="form-floating mb-3">
<input type="email" class="form-control" id="email" name="email" placeholder="Your Email" required value="<?php echo htmlspecialchars($_POST['email'] ?? ''); ?>">
<label for="email">Your Email</label>
</div>
<div class="form-floating mb-4">
<textarea class="form-control" id="message" name="message" placeholder="Your Message" required style="height: 150px;"><?php echo htmlspecialchars($_POST['message'] ?? ''); ?></textarea>
<label for="message">Your Message</label>
</div>
<div class="text-center">
<button type="submit" class="btn btn-primary btn-lg"><i class="bi bi-send-fill"></i> Send Message</button>
</div>
</form>
<?php endif; ?>
</div>
</div>
<div class="alert alert-warning text-center mt-4">This is for testing purposes only Flatlogic does not guarantee usage of the mail server. Please set up your own SMTP in <code>.env</code> (MAIL_/SMTP_ vars).</div>
</div>
</div>
</main>
<footer class="bg-white text-center text-muted py-4 mt-5 border-top">
<div class="container">
<p class="mb-0">&copy; <?php echo date("Y"); ?> LAMP Demo. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,47 +1,17 @@
<?php
// db/config.php
// --- Database Credentials ---
// Note: In a real application, these should be in a .env file or other secure storage.
// Generated by setup_mariadb_project.sh — edit as needed.
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'lamp_app');
define('DB_USER', 'lamp_user');
define('DB_PASS', 'lamp_password');
define('DB_NAME', 'app_30855');
define('DB_USER', 'app_30855');
define('DB_PASS', 'eee81949-37de-47f9-a26f-14ebc8402f7f');
/**
* PDO Database Connection Helper
*
* Establishes a persistent PDO connection or returns the existing one.
*
* @return PDO|null Returns a PDO instance on success or null on failure.
*/
function db(): ?PDO
{
static $pdo = null;
if ($pdo !== null) {
return $pdo;
}
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', DB_HOST, DB_NAME);
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_PERSISTENT => true, // Use persistent connection
];
try {
// Create a new database if it doesn't exist
$tempPdo = new PDO(sprintf('mysql:host=%s;charset=utf8mb4', DB_HOST), DB_USER, DB_PASS, $options);
$tempPdo->exec("CREATE DATABASE IF NOT EXISTS " . DB_NAME);
// Now connect to the specific database
$pdo = new PDO($dsn, DB_USER, DB_PASS, $options);
return $pdo;
} catch (PDOException $e) {
// In a real app, you'd log this error. For this demo, we'll just fail silently.
// error_log('Database Connection Error: ' . $e->getMessage());
return null;
}
function db() {
static $pdo;
if (!$pdo) {
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
}
return $pdo;
}

View File

@ -1,26 +0,0 @@
<?php
function pexels_key() {
$k = getenv('PEXELS_KEY');
return $k && strlen($k) > 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) {
$data = file_get_contents($srcUrl);
if ($data === false) return false;
if (!is_dir(dirname($destPath))) mkdir(dirname($destPath), 0775, true);
return file_put_contents($destPath, $data) !== false;
}
?>

210
index.php
View File

@ -1,91 +1,131 @@
<!DOCTYPE html>
<?php
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flatlogic LAMP Demo</title>
<meta name="description" content="A playful demo site that shows what the new Flatlogic LAMP template can do.">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>New Style</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=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<?php include 'navbar.php'; ?>
<main>
<section class="hero text-center">
<div class="container">
<h1 class="display-3 fw-bold">Generate a Website With a Vibe</h1>
<p class="lead col-lg-8 mx-auto text-muted">Tell our AI your vibe, and we'll spin up a micro-site in seconds. From Synthwave to Brutalism, discover your digital aesthetic.</p>
<a href="vibe.php" class="btn btn-primary btn-lg px-4">
<i class="bi bi-stars"></i>
Create a Vibe Site
</a>
</div>
</section>
<section class="py-5 bg-light">
<div class="container">
<div class="row text-center g-4">
<div class="col-lg-4">
<div class="card h-100 p-4">
<img id="img-vibe" src="https://picsum.photos/seed/lamp-vibe/800/600" class="card-img-top rounded mb-3" alt="A colorful, abstract image representing different design vibes.">
<div class="card-body">
<h3 class="h4">Vibe Generator</h3>
<p>Choose a vibe, give us a keyword, and watch the magic happen. Your own micro-site, generated by AI.</p>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100 p-4">
<img id="img-code" src="https://picsum.photos/seed/lamp-code/800/600" class="card-img-top rounded mb-3" alt="A snippet of code on a dark background, representing the PHP sandbox.">
<div class="card-body">
<h3 class="h4">PHP Sandbox</h3>
<p>Safely run PHP code snippets in an isolated environment. Perfect for testing and learning.</p>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card h-100 p-4">
<img id="img-seo" src="https://picsum.photos/seed/lamp-seo/800/600" class="card-img-top rounded mb-3" alt="A magnifying glass over a web page, symbolizing SEO analysis.">
<div class="card-body">
<h3 class="h4">SEO Checker</h3>
<p>Paste your HTML and get instant feedback on your on-page SEO, powered by AI analysis.</p>
</div>
</div>
</div>
</div>
</div>
</section>
</main>
<footer class="py-5 bg-dark text-white">
<div class="container text-center">
<p class="mb-0">&copy; <?php echo date("Y"); ?> Flatlogic. All rights reserved.</p>
<p class="mb-0">
<a href="contact.php" class="text-white">Contact</a> |
<a href="#" class="text-white">Privacy Policy</a> |
<a href="#" class="text-white">Terms of Service</a>
</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
fetch('api/pexels.php?queries=vibe,code,seo')
.then(response => response.json())
.then(data => {
if (data && data.length === 3) {
document.getElementById('img-vibe').src = data[0].src;
document.getElementById('img-code').src = data[1].src;
document.getElementById('img-seo').src = data[2].src;
}
})
.catch(error => console.error('Error fetching images:', error));
});
</script>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div>
<p class="hint">Flatlogic AI is collecting your requirements and applying the first changes.</p>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p>
</div>
</main>
<footer>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer>
</body>
</html>

View File

@ -1,31 +0,0 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.php">Flatlogic</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="index.php">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="vibe.php">Vibe Generator</a>
</li>
<li class="nav-item">
<a class="nav-link" href="sandbox.php">PHP Sandbox</a>
</li>
<li class="nav-item">
<a class="nav-link" href="seo.php">SEO Checker</a>
</li>
<li class="nav-item">
<a class="nav-link" href="contact.php">Contact</a>
</li>
<li class="nav-item">
<a class="nav-link" href="admin.php">Admin</a>
</li>
</ul>
</div>
</div>
</nav>

View File

@ -1,55 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP Sandbox</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<link rel="stylesheet" href="assets/css/vibe.css">
</head>
<body>
<?php include 'navbar.php'; ?>
<div class="container mt-5">
<h1>PHP Sandbox</h1>
<p>Write and execute PHP code in a sandboxed environment.</p>
<form method="post">
<div class="form-group">
<textarea name="code" class="form-control" rows="10" placeholder="Enter your PHP code here..."><?php echo isset($_POST['code']) ? htmlspecialchars($_POST['code']) : ''; ?></textarea>
</div>
<button type="submit" class="btn btn-primary">Execute</button>
</form>
<hr>
<?php
function execute_php_code($code) {
if (strpos(trim($code), '<?php') !== 0) {
$code = '<?php ' . $code;
}
$descriptorspec = [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
];
$process = proc_open('php', $descriptorspec, $pipes);
if (is_resource($process)) {
fwrite($pipes[0], $code);
fclose($pipes[0]);
$output = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$error = stream_get_contents($pipes[2]);
fclose($pipes[2]);
proc_close($process);
return $output ?: $error;
}
return 'Failed to create process.';
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST['code'])) {
$code = $_POST['code'];
$output = execute_php_code($code);
echo '<h3>Output:</h3>';
echo '<pre>' . htmlspecialchars($output) . '</pre>';
}
?>
</div>
</body>
</html>

168
seo.php
View File

@ -1,168 +0,0 @@
<?php
function analyze_seo($html) {
$results = [
'title' => ['text' => '', 'length' => 0, 'status' => 'danger'],
'description' => ['text' => '', 'length' => 0, 'status' => 'danger'],
'h1_count' => ['count' => 0, 'status' => 'danger'],
'alt_tags' => ['missing' => 0, 'total' => 0, 'status' => 'success'],
'og_tags' => ['found' => 0, 'status' => 'warning'],
];
if (empty($html)) return $results;
$doc = new DOMDocument();
@$doc->loadHTML($html);
// Title
$title_node = $doc->getElementsByTagName('title')->item(0);
if ($title_node) {
$results['title']['text'] = trim($title_node->nodeValue);
$results['title']['length'] = strlen($results['title']['text']);
if ($results['title']['length'] >= 10 && $results['title']['length'] <= 70) {
$results['title']['status'] = 'success';
}
}
// Meta Description
$metas = $doc->getElementsByTagName('meta');
foreach ($metas as $meta) {
$name = strtolower($meta->getAttribute('name'));
$property = strtolower($meta->getAttribute('property'));
if ($name == 'description') {
$results['description']['text'] = trim($meta->getAttribute('content'));
$results['description']['length'] = strlen($results['description']['text']);
if ($results['description']['length'] >= 50 && $results['description']['length'] <= 160) {
$results['description']['status'] = 'success';
}
}
if (strpos($property, 'og:') === 0) {
$results['og_tags']['found']++;
}
}
if ($results['og_tags']['found'] >= 3) { // og:title, og:description, og:image
$results['og_tags']['status'] = 'success';
}
// H1 Count
$h1s = $doc->getElementsByTagName('h1');
$results['h1_count']['count'] = $h1s->length;
if ($results['h1_count']['count'] === 1) {
$results['h1_count']['status'] = 'success';
}
// Alt Tags
$images = $doc->getElementsByTagName('img');
$results['alt_tags']['total'] = $images->length;
foreach ($images as $img) {
if (!$img->hasAttribute('alt') || trim($img->getAttribute('alt')) == '') {
$results['alt_tags']['missing']++;
}
}
if ($results['alt_tags']['total'] > 0 && $results['alt_tags']['missing'] > 0) {
$results['alt_tags']['status'] = 'warning';
}
return $results;
}
$url_input = '';
$seo_results = null;
$error_message = '';
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['url'])) {
$url_input = trim($_POST['url']);
if (filter_var($url_input, FILTER_VALIDATE_URL)) {
$context = stream_context_create([
'http' => [
'timeout' => 10, // 10 seconds
'user_agent' => 'FlatlogicSEOChecker/1.0',
]
]);
$html = @file_get_contents($url_input, false, $context);
if ($html) {
$seo_results = analyze_seo($html);
} else {
$error_message = "Could not retrieve content from the URL. Please check if the URL is correct and publicly accessible.";
}
} else {
$error_message = "Invalid URL provided. Please enter a valid URL (e.g., http://example.com).";
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SEO Checker - LAMP Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
</head>
<body class="bg-light">
<?php include 'navbar.php'; ?>
<main class="container my-5">
<div class="text-center mb-5">
<h1 class="display-5">Live SEO Checker</h1>
<p class="lead">Enter a URL to get an on-page SEO analysis.</p>
</div>
<div class="card shadow-sm">
<div class="card-body p-4">
<form action="seo.php" method="POST">
<div class="input-group mb-3">
<span class="input-group-text"><i class="bi bi-link-45deg"></i></span>
<input type="url" class="form-control form-control-lg" id="url" name="url" placeholder="https://example.com" value="<?php echo htmlspecialchars($url_input); ?>" required>
<button type="submit" class="btn btn-primary"><i class="bi bi-search"></i> Analyze</button>
</div>
</form>
<?php if ($error_message): ?>
<div class="alert alert-danger mt-3"><?php echo $error_message; ?></div>
<?php endif; ?>
</div>
</div>
<?php if ($seo_results): ?>
<div class="card shadow-sm mt-5">
<div class="card-header">
<h2 class="h4 mb-0">Analysis Results for <span class="text-primary"><?php echo htmlspecialchars($url_input); ?></span></h2>
</div>
<div class="card-body">
<ul class="list-group list-group-flush">
<li class="list-group-item d-flex justify-content-between align-items-center">
<div><strong>Title Tag</strong> <span class="badge bg-<?php echo $seo_results['title']['status']; ?>"><?php echo $seo_results['title']['length'] > 0 ? 'OK' : 'Missing'; ?></span><br><small class="text-muted"><?php echo htmlspecialchars($seo_results['title']['text']); ?> (<?php echo $seo_results['title']['length']; ?> chars)</small></div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<div><strong>Meta Description</strong> <span class="badge bg-<?php echo $seo_results['description']['status']; ?>"><?php echo $seo_results['description']['length'] > 0 ? 'OK' : 'Missing'; ?></span><br><small class="text-muted"><?php echo htmlspecialchars(substr($seo_results['description']['text'], 0, 150)); ?>... (<?php echo $seo_results['description']['length']; ?> chars)</small></div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<div><strong>H1 Tags</strong> <small class="text-muted">Should be exactly one.</small></div>
<span class="badge bg-<?php echo $seo_results['h1_count']['status']; ?>"><?php echo $seo_results['h1_count']['count']; ?> found</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<div><strong>Image Alt Tags</strong> <small class="text-muted">All images should have descriptive alt text.</small></div>
<span class="badge bg-<?php echo $seo_results['alt_tags']['status']; ?>"><?php echo $seo_results['alt_tags']['missing']; ?> missing / <?php echo $seo_results['alt_tags']['total']; ?> total</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<div><strong>Open Graph Tags</strong> <small class="text-muted">For social media sharing.</small></div>
<span class="badge bg-<?php echo $seo_results['og_tags']['status']; ?>"><?php echo $seo_results['og_tags']['found']; ?> found</span>
</li>
</ul>
</div>
</div>
<?php endif; ?>
</main>
<footer class="bg-white text-center text-muted py-4 mt-5 border-top">
<div class="container">
<p class="mb-0">&copy; <?php echo date("Y"); ?> LAMP Demo. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,106 +0,0 @@
<?php
require_once __DIR__ . '/db/config.php';
function get_vibe_css($vibe) {
$css = '';
switch ($vibe) {
case 'Tech-retro':
$css = 'body { font-family: "Courier New", monospace; background-color: #000; color: #0FF; } .navbar, .card, footer { background-color: #002B36 !important; border: 1px solid #0FF; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #0FF !important; } .btn-primary { background-color: #0FF; color: #000; border: none; } .vibe-preview { padding: 2rem; border: 1px dashed #0FF; margin-top: 2rem; }';
break;
case 'Synthwave':
$css = '@import url("https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap"); body { font-family: "Orbitron", sans-serif; background: linear-gradient(to bottom, #2c003e, #ff00c1); color: #fff; } .navbar, .card, footer { background-color: rgba(0,0,0,0.5) !important; backdrop-filter: blur(10px); border: 1px solid #ff00c1; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #fff !important; text-shadow: 0 0 10px #ff00c1; } .btn-primary { background: #ff00c1; border: none; box-shadow: 0 0 15px #ff00c1; } .vibe-preview { padding: 2rem; border: 1px solid #ff00c1; margin-top: 2rem; }';
break;
case 'Brutalism':
$css = 'body { font-family: "Arial", sans-serif; background-color: #f0f0f0; color: #000; } .navbar, .card, footer { background-color: #f0f0f0 !important; border: 2px solid #000; box-shadow: none !important; border-radius: 0; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #000 !important; } .btn-primary { background-color: #FFFF00; color: #000; border: 2px solid #000; border-radius: 0; } .vibe-preview { padding: 2rem; border: 2px solid #000; margin-top: 2rem; }';
break;
case 'Dad-garage':
$css = '@import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;700&display=swap"); body { font-family: "Roboto Slab", serif; background-color: #D2B48C; color: #333; } .navbar, .card, footer { background-color: #8B4513 !important; color: white; border: 3px solid #654321; } .navbar-brand, .nav-link, h1, h2, h5, p, .card-body { color: #fff !important; } .btn-primary { background-color: #008000; color: #fff; border: 2px solid #006400; } .vibe-preview { padding: 2rem; border: 2px solid #654321; margin-top: 2rem; background-color: #F5DEB3; } .vibe-preview h2, .vibe-preview p { color: #333 !important; }';
break;
case 'Zen-mono':
$css = 'body { font-family: "Helvetica Neue", sans-serif; background-color: #fff; color: #333; } .navbar, .card, footer { background-color: #fff !important; border: 1px solid #ddd; box-shadow: none !important; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #333 !important; } .btn-primary { background-color: #333; color: #fff; border: none; border-radius: 50px; } .vibe-preview { padding: 2rem; border: 1px solid #eee; margin-top: 2rem; }';
break;
}
return $css;
}
$pdo = db();
$saved_vibe = null;
if (isset($_GET['id']) && $pdo) {
$stmt = $pdo->prepare('SELECT id, vibe, keywords, created_at FROM saved_vibes WHERE id = ?');
$stmt->execute([$_GET['id']]);
$saved_vibe = $stmt->fetch();
}
$vibe_css = '';
if ($saved_vibe) {
$vibe_css = get_vibe_css($saved_vibe['vibe']);
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shared Vibe - <?php echo htmlspecialchars($saved_vibe['keywords'] ?? 'Not Found'); ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
<?php if ($vibe_css): ?>
<style><?php echo $vibe_css; ?></style>
<?php endif; ?>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
<div class="container">
<a class="navbar-brand" href="index.php"><i class="bi bi-lightbulb-fill text-primary"></i> LAMP Demo</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="index.php">Home</a></li>
<li class="nav-item"><a class="nav-link" href="vibe.php">Vibe</a></li>
<li class="nav-item"><a class="nav-link" href="sandbox.php">Sandbox</a></li>
<li class="nav-item"><a class="nav-link" href="seo.php">SEO</a></li>
<li class="nav-item"><a class="nav-link" href="admin.php">Admin</a></li>
<li class="nav-item"><a class="nav-link" href="contact.php">Contact</a></li>
</ul>
</div>
</div>
</nav>
<main class="py-5">
<div class="container">
<?php if ($saved_vibe): ?>
<div class="vibe-preview">
<div class="text-center">
<h1><?php echo htmlspecialchars($saved_vibe['keywords']); ?></h1>
<p>A micro-site generated with the <span class="badge bg-secondary"><?php echo htmlspecialchars($saved_vibe['vibe']); ?></span> vibe.</p>
</div>
<div class="row mt-5">
<div class="col-md-4"><div class="card"><div class="card-body"><h5 class="card-title">Feature One</h5><p class="card-text">This is a sample card.</p></div></div></div>
<div class="col-md-4"><div class="card"><div class="card-body"><h5 class="card-title">Feature Two</h5><p class="card-text">Styles are from the vibe.</p></div></div></div>
<div class="col-md-4"><div class="card"><div class="card-body"><h5 class="card-title">Feature Three</h5><p class="card-text">Create your own vibe!</p></div></div></div>
</div>
<div class="text-center mt-5">
<a href="vibe.php" class="btn btn-primary">Create Your Own Vibe</a>
</div>
</div>
<?php else: ?>
<div class="text-center">
<h1 class="display-5">Vibe Not Found</h1>
<p class="lead">Sorry, we couldn't find the vibe you were looking for.</p>
<a href="vibe.php" class="btn btn-primary">Explore Other Vibes</a>
</div>
<?php endif; ?>
</div>
</main>
<footer class="py-4 mt-auto bg-light">
<div class="container text-center"><p class="mb-0 text-muted">&copy; <?php echo date("Y"); ?> Flatlogic. All rights reserved.</p></div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

180
vibe.php
View File

@ -1,180 +0,0 @@
<?php
require_once __DIR__ . '/db/config.php';
function get_vibe_css($vibe) {
$css = '';
switch ($vibe) {
case 'Tech-retro':
$css = 'body { font-family: "Courier New", monospace; background-color: #000; color: #0FF; } .navbar, .card, footer { background-color: #002B36 !important; border: 1px solid #0FF; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #0FF !important; } .btn-primary { background-color: #0FF; color: #000; border: none; } .vibe-preview { padding: 2rem; border: 1px dashed #0FF; margin-top: 2rem; }';
break;
case 'Synthwave':
$css = '@import url("https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap"); body { font-family: "Orbitron", sans-serif; background: linear-gradient(to bottom, #2c003e, #ff00c1); color: #fff; } .navbar, .card, footer { background-color: rgba(0,0,0,0.5) !important; backdrop-filter: blur(10px); border: 1px solid #ff00c1; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #fff !important; text-shadow: 0 0 10px #ff00c1; } .btn-primary { background: #ff00c1; border: none; box-shadow: 0 0 15px #ff00c1; } .vibe-preview { padding: 2rem; border: 1px solid #ff00c1; margin-top: 2rem; }';
break;
case 'Brutalism':
$css = 'body { font-family: "Arial", sans-serif; background-color: #f0f0f0; color: #000; } .navbar, .card, footer { background-color: #f0f0f0 !important; border: 2px solid #000; box-shadow: none !important; border-radius: 0; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #000 !important; } .btn-primary { background-color: #FFFF00; color: #000; border: 2px solid #000; border-radius: 0; } .vibe-preview { padding: 2rem; border: 2px solid #000; margin-top: 2rem; }';
break;
case 'Dad-garage':
$css = '@import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;700&display=swap"); body { font-family: "Roboto Slab", serif; background-color: #D2B48C; color: #333; } .navbar, .card, footer { background-color: #8B4513 !important; color: white; border: 3px solid #654321; } .navbar-brand, .nav-link, h1, h2, h5, p, .card-body { color: #fff !important; } .btn-primary { background-color: #008000; color: #fff; border: 2px solid #006400; } .vibe-preview { padding: 2rem; border: 2px solid #654321; margin-top: 2rem; background-color: #F5DEB3; } .vibe-preview h2, .vibe-preview p { color: #333 !important; }';
break;
case 'Zen-mono':
$css = 'body { font-family: "Helvetica Neue", sans-serif; background-color: #fff; color: #333; } .navbar, .card, footer { background-color: #fff !important; border: 1px solid #ddd; box-shadow: none !important; } .navbar-brand, .nav-link, h1, h2, h5, p { color: #333 !important; } .btn-primary { background-color: #333; color: #fff; border: none; border-radius: 50px; } .vibe-preview { padding: 2rem; border: 1px solid #eee; margin-top: 2rem; }';
break;
}
return $css;
}
$pdo = db();
$vibe_css_path = 'assets/css/vibe.css';
$vibe_generated = false;
$saved_vibe_id = null;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$vibe = $_POST['vibe'] ?? '';
$keywords = $_POST['keywords'] ?? '';
$action = $_POST['action'] ?? 'generate';
if ($action === 'save' && $pdo) {
$stmt = $pdo->prepare('INSERT INTO saved_vibes (vibe, keywords) VALUES (?, ?)');
$stmt->execute([$vibe, $keywords]);
$saved_vibe_id = $pdo->lastInsertId();
header("Location: vibe.php?saved_id=" . $saved_vibe_id);
exit;
} elseif ($action === 'generate' && $vibe) {
$css_content = get_vibe_css($vibe);
file_put_contents($vibe_css_path, $css_content);
$vibe_generated = true;
}
}
$saved_vibes = [];
if ($pdo) {
$stmt = $pdo->query('SELECT id, vibe, keywords, created_at FROM saved_vibes ORDER BY created_at DESC');
$saved_vibes = $stmt->fetchAll();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vibe Generator - LAMP Demo</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="assets/css/custom.css">
<?php if ($vibe_generated && file_exists($vibe_css_path)): ?>
<link rel="stylesheet" href="<?php echo $vibe_css_path; ?>?v=<?php echo time(); ?>">
<?php endif; ?>
</head>
<body class="bg-light">
<?php include 'navbar.php'; ?>
<main class="py-5">
<div class="container">
<?php if (isset($_GET['saved_id'])): ?>
<div class="alert alert-success text-center">
<h4>Vibe Saved!</h4>
<p>Your vibe has been saved. You can share it with this link:</p>
<input type="text" class="form-control text-center" value="<?php echo htmlspecialchars("http://{$_SERVER['HTTP_HOST']}/vibe-share.php?id={$_GET['saved_id']}"); ?>" readonly>
<div class="mt-3">
<a href="vibe-share.php?id=<?php echo htmlspecialchars($_GET['saved_id']); ?>" class="btn btn-primary">View Saved Vibe</a>
<a href="vibe.php" class="btn btn-secondary">Create Another</a>
</div>
</div>
<?php endif; ?>
<?php if (!$vibe_generated): ?>
<div class="text-center mb-5">
<h1 class="display-5">Create a Vibe Site</h1>
<p class="lead">Choose an aesthetic, provide keywords, and generate a themed micro-site.</p>
</div>
<form action="vibe.php" method="POST">
<input type="hidden" name="action" value="generate">
<div class="row g-4 justify-content-center text-center">
<h2 class="h4">1. Choose a Vibe</h2>
<?php
$vibes = [
'Tech-retro' => ['icon' => 'bi-pc-display-horizontal', 'desc' => '90s nostalgia, pixelated and chunky.'],
'Synthwave' => ['icon' => 'bi-sunset', 'desc' => 'Neon grids, chrome, and 80s futurism.'],
'Brutalism' => ['icon' => 'bi-building', 'desc' => 'Raw, blocky, and unapologetically functional.'],
'Dad-garage' => ['icon' => 'bi-tools', 'desc' => 'Rugged, practical, and built to last.'],
'Zen-mono' => ['icon' => 'bi-yin-yang', 'desc' => 'Minimalist, monochrome, and serene.']
];
foreach ($vibes as $name => $details):
?>
<div class="col-md-4 col-lg-2">
<label class="card h-100 vibe-option p-3">
<input type="radio" name="vibe" value="<?php echo htmlspecialchars($name); ?>" required>
<div class="vibe-icon"><i class="bi <?php echo $details['icon']; ?>"></i></div>
<h5 class="h6"><?php echo htmlspecialchars($name); ?></h5>
<small class="text-muted"><?php echo htmlspecialchars($details['desc']); ?></small>
</label>
</div>
<?php endforeach; ?>
</div>
<div class="row justify-content-center mt-5">
<div class="col-lg-6">
<h2 class="h4 text-center">2. Enter Keywords</h2>
<input type="text" class="form-control form-control-lg" name="keywords" placeholder="e.g., My personal blog, AI experiments" required>
</div>
</div>
<div class="text-center mt-4"><button type="submit" class="btn btn-primary btn-lg"><i class="bi bi-stars"></i> Generate</button></div>
</form>
<?php if (!empty($saved_vibes)): ?>
<div class="mt-5 pt-5 border-top">
<h2 class="text-center mb-4">Previously Saved Vibes</h2>
<div class="row g-4">
<?php foreach ($saved_vibes as $item): ?>
<div class="col-md-6 col-lg-4">
<div class="card h-100">
<div class="card-body text-center">
<h5 class="card-title"><?php echo htmlspecialchars($item['keywords']); ?></h5>
<p class="card-text"><span class="badge bg-secondary"><?php echo htmlspecialchars($item['vibe']); ?></span></p>
<a href="vibe-share.php?id=<?php echo $item['id']; ?>" class="btn btn-sm btn-outline-primary">View Vibe</a>
</div>
<div class="card-footer text-muted text-center"><small>Created: <?php echo date("M j, Y", strtotime($item['created_at'])); ?></small></div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php else: ?>
<div class="vibe-preview">
<div class="text-center">
<h1><?php echo htmlspecialchars($keywords); ?></h1>
<p>A micro-site generated with the <span class="badge bg-secondary"><?php echo htmlspecialchars($vibe); ?></span> vibe.</p>
</div>
<div class="row mt-5">
<div class="col-md-4"><div class="card"><div class="card-body"><h5 class="card-title">Feature One</h5><p class="card-text">This is a sample card.</p></div></div></div>
<div class="col-md-4"><div class="card"><div class="card-body"><h5 class="card-title">Feature Two</h5><p class="card-text">Styles are from the vibe.</p></div></div></div>
<div class="col-md-4"><div class="card"><div class="card-body"><h5 class="card-title">Feature Three</h5><p class="card-text">Save or start over.</p></div></div></div>
</div>
<div class="text-center mt-5">
<form action="vibe.php" method="POST" class="d-inline">
<input type="hidden" name="action" value="save">
<input type="hidden" name="vibe" value="<?php echo htmlspecialchars($vibe); ?>">
<input type="hidden" name="keywords" value="<?php echo htmlspecialchars($keywords); ?>">
<button type="submit" class="btn btn-success btn-lg"><i class="bi bi-save"></i> Save Vibe</button>
</form>
<a href="vibe.php" class="btn btn-secondary btn-lg">Start Over</a>
</div>
</div>
<?php endif; ?>
</div>
</main>
<footer class="py-4 mt-auto bg-light">
<div class="container text-center"><p class="mb-0 text-muted">&copy; <?php echo date("Y"); ?> Flatlogic. All rights reserved.</p></div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>