Compare commits
No commits in common. "ai-dev" and "master" have entirely different histories.
108
api/contact.php
108
api/contact.php
@ -1,108 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
function respond(int $status, array $payload): void {
|
||||
http_response_code($status);
|
||||
echo json_encode($payload, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
respond(405, ['success' => false, 'message' => 'Método não permitido.']);
|
||||
}
|
||||
|
||||
$name = trim((string)($_POST['name'] ?? ''));
|
||||
$email = trim((string)($_POST['email'] ?? ''));
|
||||
$phone = trim((string)($_POST['phone'] ?? ''));
|
||||
$projectType = trim((string)($_POST['project_type'] ?? ''));
|
||||
$message = trim((string)($_POST['message'] ?? ''));
|
||||
$honeypot = trim((string)($_POST['website'] ?? ''));
|
||||
|
||||
if ($honeypot !== '') {
|
||||
respond(200, ['success' => true, 'message' => 'Pedido recebido. Obrigado.']);
|
||||
}
|
||||
|
||||
$errors = [];
|
||||
if (mb_strlen($name) < 2 || mb_strlen($name) > 120) $errors[] = 'Indica um nome válido.';
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL) || mb_strlen($email) > 160) $errors[] = 'Indica um email válido.';
|
||||
if ($phone !== '' && mb_strlen($phone) > 40) $errors[] = 'O telemóvel é demasiado longo.';
|
||||
if ($projectType === '' || mb_strlen($projectType) > 80) $errors[] = 'Selecciona o tipo de projecto.';
|
||||
if (mb_strlen($message) < 20 || mb_strlen($message) > 1800) $errors[] = 'A mensagem deve ter entre 20 e 1800 caracteres.';
|
||||
if ($errors) {
|
||||
respond(422, ['success' => false, 'message' => implode(' ', $errors)]);
|
||||
}
|
||||
|
||||
$recipient = 'ola@paulogomes.pt';
|
||||
$siteName = 'pg Web e APP Designer';
|
||||
$emailSent = false;
|
||||
$submissionId = null;
|
||||
|
||||
try {
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
$pdo = db();
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS contact_requests (
|
||||
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(120) NOT NULL,
|
||||
email VARCHAR(160) NOT NULL,
|
||||
phone VARCHAR(40) NULL,
|
||||
project_type VARCHAR(80) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
email_sent TINYINT(1) NOT NULL DEFAULT 0,
|
||||
status VARCHAR(30) NOT NULL DEFAULT 'new',
|
||||
ip_address VARCHAR(45) NULL,
|
||||
user_agent VARCHAR(255) NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci");
|
||||
|
||||
$stmt = $pdo->prepare('INSERT INTO contact_requests (name, email, phone, project_type, message, ip_address, user_agent) VALUES (:name, :email, :phone, :project_type, :message, :ip, :user_agent)');
|
||||
$stmt->execute([
|
||||
':name' => $name,
|
||||
':email' => $email,
|
||||
':phone' => $phone !== '' ? $phone : null,
|
||||
':project_type' => $projectType,
|
||||
':message' => $message,
|
||||
':ip' => substr((string)($_SERVER['REMOTE_ADDR'] ?? ''), 0, 45) ?: null,
|
||||
':user_agent' => substr((string)($_SERVER['HTTP_USER_AGENT'] ?? ''), 0, 255) ?: null,
|
||||
]);
|
||||
$submissionId = (int)$pdo->lastInsertId();
|
||||
} catch (Throwable $e) {
|
||||
error_log('Contact DB error: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
require_once __DIR__ . '/../mail/MailService.php';
|
||||
$safeName = htmlspecialchars($name, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$safeEmail = htmlspecialchars($email, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$safePhone = htmlspecialchars($phone !== '' ? $phone : 'Não indicado', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$safeProject = htmlspecialchars($projectType, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
$safeMessage = nl2br(htmlspecialchars($message, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'));
|
||||
$html = "<h2>Novo pedido — {$siteName}</h2>"
|
||||
. "<p><strong>Nome:</strong> {$safeName}</p>"
|
||||
. "<p><strong>Email:</strong> {$safeEmail}</p>"
|
||||
. "<p><strong>Telemóvel:</strong> {$safePhone}</p>"
|
||||
. "<p><strong>Tipo de projecto:</strong> {$safeProject}</p>"
|
||||
. "<p><strong>Website:</strong> {$siteName}</p>"
|
||||
. "<hr><p><strong>Detalhe da mensagem:</strong></p><p>{$safeMessage}</p>";
|
||||
$text = "Novo pedido — {$siteName}\nNome: {$name}\nEmail: {$email}\nTelemóvel: " . ($phone !== '' ? $phone : 'Não indicado') . "\nTipo de projecto: {$projectType}\n\nDetalhe da mensagem:\n{$message}";
|
||||
$result = MailService::sendMail($recipient, $siteName . ' — novo pedido de contacto', $html, $text, ['reply_to' => $email]);
|
||||
$emailSent = !empty($result['success']);
|
||||
if (!$emailSent) error_log('Contact mail error: ' . ($result['error'] ?? 'unknown'));
|
||||
} catch (Throwable $e) {
|
||||
error_log('Contact mail exception: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($submissionId) {
|
||||
try {
|
||||
$stmt = db()->prepare('UPDATE contact_requests SET email_sent = :email_sent WHERE id = :id');
|
||||
$stmt->execute([':email_sent' => $emailSent ? 1 : 0, ':id' => $submissionId]);
|
||||
} catch (Throwable $e) {
|
||||
error_log('Contact DB update error: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
$messageText = $emailSent
|
||||
? 'Mensagem enviada com sucesso. Obrigado — responderei assim que possível.'
|
||||
: 'Pedido guardado. Se o envio por email não estiver configurado no servidor, contacta directamente ola@paulogomes.pt.';
|
||||
|
||||
respond(200, ['success' => true, 'message' => $messageText]);
|
||||
@ -1,431 +1,403 @@
|
||||
:root {
|
||||
--bg: #080808;
|
||||
--surface: rgba(14, 14, 14, 0.86);
|
||||
--surface-strong: #101010;
|
||||
--text: #f5f5f1;
|
||||
--muted: #a7a7a0;
|
||||
--line: rgba(255, 255, 255, 0.12);
|
||||
--line-strong: rgba(255, 255, 255, 0.22);
|
||||
--accent: #ffffff;
|
||||
--accent-text: #050505;
|
||||
--radius-sm: 8px;
|
||||
--radius-md: 12px;
|
||||
--radius-lg: 18px;
|
||||
--space: 1rem;
|
||||
}
|
||||
|
||||
[data-bs-theme="light"] {
|
||||
--bg: #ffffff;
|
||||
--surface: rgba(255, 255, 255, 0.88);
|
||||
--surface-strong: #f6f6f3;
|
||||
--text: #050505;
|
||||
--muted: #60605b;
|
||||
--line: rgba(0, 0, 0, 0.12);
|
||||
--line-strong: rgba(0, 0, 0, 0.2);
|
||||
--accent: #050505;
|
||||
--accent-text: #ffffff;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
a { color: inherit; }
|
||||
.skip-link {
|
||||
position: absolute;
|
||||
left: 1rem;
|
||||
top: -4rem;
|
||||
z-index: 999;
|
||||
background: var(--accent);
|
||||
color: var(--accent-text);
|
||||
padding: .65rem .85rem;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: top .2s ease;
|
||||
}
|
||||
.skip-link:focus { top: 1rem; }
|
||||
.site-shell { position: relative; min-height: 100vh; overflow-x: clip; }
|
||||
.hero-video {
|
||||
position: fixed;
|
||||
top: 300px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100vh - 300px);
|
||||
object-fit: cover;
|
||||
opacity: 0;
|
||||
z-index: 0;
|
||||
filter: grayscale(1) contrast(1.08) brightness(.62);
|
||||
pointer-events: none;
|
||||
}
|
||||
.video-scrim {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 1;
|
||||
background: rgba(0, 0, 0, .46);
|
||||
pointer-events: none;
|
||||
}
|
||||
[data-bs-theme="light"] .video-scrim { background: rgba(255, 255, 255, .58); }
|
||||
.site-header {
|
||||
z-index: 10;
|
||||
background: rgba(8, 8, 8, .72);
|
||||
border-bottom: 1px solid var(--line);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
[data-bs-theme="light"] .site-header { background: rgba(255,255,255,.74); }
|
||||
.navbar { --bs-navbar-color: var(--muted); --bs-navbar-hover-color: var(--text); --bs-navbar-active-color: var(--text); }
|
||||
.brand-mark {
|
||||
color: var(--text);
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-size: clamp(1.35rem, 2vw, 1.9rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -.045em;
|
||||
}
|
||||
.brand-mark sup { font-family: Inter, sans-serif; font-size: .42em; top: -.9em; margin-left: .08rem; }
|
||||
.nav-link { font-size: .9rem; color: var(--muted); transition: color .2s ease; }
|
||||
.nav-link:hover, .nav-link:focus, .nav-link.active { color: var(--text); }
|
||||
.navbar-toggler { border: 1px solid var(--line); border-radius: var(--radius-sm); }
|
||||
.btn { border-radius: var(--radius-sm); transition: transform .18s ease, background-color .18s ease, border-color .18s ease; }
|
||||
.btn:hover { transform: translateY(-1px) scale(1.015); }
|
||||
.btn-primary-dark {
|
||||
background: var(--accent);
|
||||
color: var(--accent-text);
|
||||
border: 1px solid var(--accent);
|
||||
font-weight: 650;
|
||||
padding: .72rem 1.15rem;
|
||||
}
|
||||
.btn-primary-dark:hover, .btn-primary-dark:focus { background: var(--accent); color: var(--accent-text); border-color: var(--accent); }
|
||||
.btn-outline-soft { color: var(--text); border: 1px solid var(--line-strong); background: transparent; padding: .72rem 1.15rem; }
|
||||
.btn-outline-soft:hover { border-color: var(--accent); color: var(--text); }
|
||||
.btn-ghost { color: var(--muted); border: 1px solid var(--line); background: transparent; }
|
||||
.btn-ghost:hover { color: var(--text); border-color: var(--line-strong); }
|
||||
.hero-section {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding-top: calc(8rem - 75px);
|
||||
padding-bottom: 10rem;
|
||||
}
|
||||
.eyebrow {
|
||||
color: var(--muted);
|
||||
font-size: .76rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .12em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.hero-title {
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-size: clamp(3.4rem, 8.4vw, 8.25rem);
|
||||
font-weight: 400;
|
||||
line-height: .95;
|
||||
letter-spacing: -0.045em;
|
||||
max-width: 1180px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.hero-copy {
|
||||
color: var(--muted);
|
||||
font-size: clamp(1rem, 1.3vw, 1.18rem);
|
||||
line-height: 1.75;
|
||||
max-width: 720px;
|
||||
margin-top: 1.75rem;
|
||||
}
|
||||
.hero-actions { margin-top: 2.5rem; }
|
||||
.stats-panel {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 1px;
|
||||
max-width: 820px;
|
||||
margin: 5rem auto 0;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
overflow: hidden;
|
||||
background: var(--line);
|
||||
}
|
||||
.stats-panel div {
|
||||
background: var(--surface);
|
||||
padding: 1.35rem 1rem;
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
.stats-panel strong {
|
||||
display: block;
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-size: clamp(2.3rem, 5vw, 4.2rem);
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
}
|
||||
.stats-panel span { display: block; color: var(--muted); font-size: .9rem; margin-top: .35rem; }
|
||||
.section-pad { position: relative; z-index: 2; padding: 6.5rem 0; }
|
||||
.section-quiet { background: rgba(255,255,255,.025); border-block: 1px solid var(--line); }
|
||||
[data-bs-theme="light"] .section-quiet { background: rgba(0,0,0,.025); }
|
||||
.section-heading { max-width: 760px; margin-bottom: 2.5rem; }
|
||||
.section-heading.compact { margin-inline: auto; text-align: center; }
|
||||
.section-heading h2, .section-title, .contact-card h2 {
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-size: clamp(2.45rem, 5vw, 5.25rem);
|
||||
line-height: .98;
|
||||
letter-spacing: -.04em;
|
||||
font-weight: 400;
|
||||
}
|
||||
.section-heading p:not(.eyebrow), .section-copy { color: var(--muted); line-height: 1.75; }
|
||||
.service-card, .contact-card, .form-card, .timeline > div {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-md);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
.service-card { padding: 1.35rem; transition: transform .2s ease, border-color .2s ease; }
|
||||
.service-card:hover { transform: translateY(-4px); border-color: var(--line-strong); }
|
||||
.service-card span { color: var(--muted); font-size: .78rem; font-weight: 700; letter-spacing: .12em; }
|
||||
.service-card h3 { margin: 1.4rem 0 .8rem; font-size: 1.03rem; font-weight: 750; }
|
||||
.service-card p { color: var(--muted); line-height: 1.7; font-size: .94rem; margin: 0; }
|
||||
.tool-grid { display: flex; flex-wrap: wrap; gap: .65rem; }
|
||||
.tool-grid span {
|
||||
border: 1px solid var(--line);
|
||||
background: var(--surface);
|
||||
color: var(--text);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: .7rem .9rem;
|
||||
font-size: .92rem;
|
||||
}
|
||||
.timeline { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 1rem; margin-top: 2rem; }
|
||||
.timeline > div { padding: 1.25rem; }
|
||||
.timeline strong { color: var(--muted); font-size: .78rem; letter-spacing: .12em; }
|
||||
.timeline h3 { font-size: 1.04rem; margin: 1rem 0 .6rem; }
|
||||
.timeline p { color: var(--muted); margin: 0; line-height: 1.65; }
|
||||
.contact-section { padding-bottom: 7rem; }
|
||||
.contact-card, .form-card { padding: clamp(1.25rem, 3vw, 2rem); }
|
||||
.contact-list { list-style: none; padding: 0; margin: 2rem 0 0; display: grid; gap: 1rem; }
|
||||
.contact-list li { border-top: 1px solid var(--line); padding-top: 1rem; }
|
||||
.contact-list span { display: block; color: var(--muted); font-size: .78rem; text-transform: uppercase; letter-spacing: .11em; margin-bottom: .35rem; }
|
||||
.contact-list a, .contact-list strong { font-size: 1.06rem; text-decoration: none; font-weight: 650; }
|
||||
.contact-list small { display: block; color: var(--muted); margin-top: .25rem; }
|
||||
.form-label { font-size: .86rem; font-weight: 700; color: var(--text); }
|
||||
.form-label span, .form-text, .muted-note { color: var(--muted); }
|
||||
.form-control, .form-select {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: rgba(255,255,255,.04);
|
||||
color: var(--text);
|
||||
min-height: 3rem;
|
||||
}
|
||||
[data-bs-theme="light"] .form-control, [data-bs-theme="light"] .form-select { background-color: rgba(0,0,0,.035); }
|
||||
.form-control:focus, .form-select:focus {
|
||||
color: var(--text);
|
||||
background-color: transparent;
|
||||
border-color: var(--line-strong);
|
||||
box-shadow: 0 0 0 .2rem rgba(128,128,128,.14);
|
||||
}
|
||||
.form-control::placeholder { color: color-mix(in srgb, var(--muted) 75%, transparent); }
|
||||
.secure-pill { border: 1px solid var(--line); border-radius: 999px; padding: .4rem .6rem; color: var(--muted); font-size: .78rem; white-space: nowrap; }
|
||||
.site-footer { position: relative; z-index: 2; border-top: 1px solid var(--line); color: var(--muted); padding: 1.25rem 0; font-size: .86rem; }
|
||||
.toast { background: var(--surface-strong); color: var(--text); border: 1px solid var(--line); }
|
||||
.animate-rise { opacity: 0; transform: translateY(20px); animation: fade-rise .8s ease-out forwards; }
|
||||
.delay-1 { animation-delay: .2s; }
|
||||
.delay-2 { animation-delay: .4s; }
|
||||
@keyframes fade-rise { to { opacity: 1; transform: translateY(0); } }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
html { scroll-behavior: auto; }
|
||||
.animate-rise { animation: none; opacity: 1; transform: none; }
|
||||
.btn, .service-card { transition: none; }
|
||||
}
|
||||
@media (max-width: 991.98px) {
|
||||
.site-header .navbar-collapse { padding-top: 1rem; }
|
||||
.timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.hero-section { padding-top: 7rem; padding-bottom: 6rem; }
|
||||
.stats-panel { grid-template-columns: 1fr; margin-top: 3rem; }
|
||||
.section-pad { padding: 4.5rem 0; }
|
||||
.timeline { grid-template-columns: 1fr; }
|
||||
.hero-video { top: 220px; height: calc(100vh - 220px); }
|
||||
background: linear-gradient(-45deg, #ee7752, #e73c7e, #23a6d5, #23d5ab);
|
||||
background-size: 400% 400%;
|
||||
animation: gradient 15s ease infinite;
|
||||
color: #212529;
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.main-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.portfolio-section { overflow: hidden; }
|
||||
.project-featured, .project-card, .testimonial-card, .cookie-banner {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-lg);
|
||||
backdrop-filter: blur(18px);
|
||||
@keyframes gradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
.project-featured {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.08fr) minmax(320px, .92fr);
|
||||
gap: clamp(1.5rem, 4vw, 3rem);
|
||||
align-items: center;
|
||||
padding: clamp(1.1rem, 3vw, 2rem);
|
||||
box-shadow: 0 30px 90px rgba(0,0,0,.22);
|
||||
|
||||
.chat-container {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 85vh;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
|
||||
backdrop-filter: blur(15px);
|
||||
-webkit-backdrop-filter: blur(15px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.project-visual, .project-thumb {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line);
|
||||
background: #111;
|
||||
isolation: isolate;
|
||||
|
||||
.chat-header {
|
||||
padding: 1.5rem;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
.project-visual { min-height: 360px; border-radius: var(--radius-lg); }
|
||||
.project-visual::before, .project-thumb::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: -1;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,.16), transparent 45%), radial-gradient(circle at 18% 18%, rgba(255,255,255,.24), transparent 28%);
|
||||
|
||||
.chat-messages {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
.real-estate-visual { background: radial-gradient(circle at 20% 20%, rgba(255,255,255,.28), transparent 30%), linear-gradient(135deg, #101010, #2f332f 50%, #090909); }
|
||||
.visual-orb {
|
||||
position: absolute;
|
||||
width: 190px;
|
||||
height: 190px;
|
||||
right: 8%;
|
||||
top: 8%;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 35% 30%, #fff, #8d9388 45%, #222 74%);
|
||||
filter: blur(.1px);
|
||||
opacity: .72;
|
||||
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.mock-browser {
|
||||
position: absolute;
|
||||
left: 8%;
|
||||
bottom: 10%;
|
||||
width: min(72%, 420px);
|
||||
min-height: 250px;
|
||||
padding: 1rem;
|
||||
border-radius: 18px;
|
||||
background: rgba(255,255,255,.09);
|
||||
border: 1px solid rgba(255,255,255,.18);
|
||||
box-shadow: 0 25px 80px rgba(0,0,0,.34);
|
||||
backdrop-filter: blur(16px);
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
.mock-top { display: flex; gap: .35rem; margin-bottom: 1rem; }
|
||||
.mock-top span { width: .58rem; height: .58rem; border-radius: 50%; background: rgba(255,255,255,.55); }
|
||||
.mock-hero { height: 92px; border-radius: 14px; background: linear-gradient(135deg, rgba(255,255,255,.72), rgba(255,255,255,.12)); }
|
||||
.mock-lines { display: grid; gap: .6rem; margin-top: 1rem; }
|
||||
.mock-lines i { display: block; height: .55rem; border-radius: 999px; background: rgba(255,255,255,.22); }
|
||||
.mock-lines i:nth-child(2) { width: 72%; }
|
||||
.mock-lines i:nth-child(3) { width: 54%; }
|
||||
.project-visual > strong {
|
||||
position: absolute;
|
||||
right: 1rem;
|
||||
bottom: .65rem;
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-size: clamp(4rem, 10vw, 8rem);
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
letter-spacing: -.06em;
|
||||
color: rgba(255,255,255,.92);
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 10px;
|
||||
}
|
||||
.project-content { padding: clamp(.25rem, 2vw, 1rem); }
|
||||
.project-type {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: fit-content;
|
||||
color: var(--muted);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 999px;
|
||||
padding: .42rem .68rem;
|
||||
font-size: .74rem;
|
||||
font-weight: 750;
|
||||
letter-spacing: .11em;
|
||||
text-transform: uppercase;
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.project-content h3, .project-card h3 {
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-weight: 400;
|
||||
letter-spacing: -.035em;
|
||||
line-height: 1;
|
||||
|
||||
.message {
|
||||
max-width: 85%;
|
||||
padding: 0.85rem 1.1rem;
|
||||
border-radius: 16px;
|
||||
line-height: 1.5;
|
||||
font-size: 0.95rem;
|
||||
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
||||
animation: fadeIn 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
.project-content h3 { font-size: clamp(2.5rem, 5vw, 5rem); margin: 1.3rem 0 1rem; }
|
||||
.project-card h3 { font-size: clamp(1.9rem, 3vw, 2.55rem); margin: 1rem 0 .75rem; }
|
||||
.project-content p, .project-card p { color: var(--muted); line-height: 1.72; }
|
||||
.project-metric {
|
||||
display: inline-grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
gap: .7rem;
|
||||
align-items: center;
|
||||
margin: .4rem 0 1.4rem;
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
.project-metric strong { font-family: "Instrument Serif", Georgia, serif; font-size: 3.2rem; font-weight: 400; line-height: 1; }
|
||||
.project-metric span { color: var(--muted); max-width: 170px; line-height: 1.2; }
|
||||
.project-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .4rem;
|
||||
color: var(--text);
|
||||
font-weight: 750;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid var(--line-strong);
|
||||
padding-bottom: .2rem;
|
||||
|
||||
.message.visitor {
|
||||
align-self: flex-end;
|
||||
background: linear-gradient(135deg, #212529 0%, #343a40 100%);
|
||||
color: #fff;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
.project-link::after { content: "↗"; font-size: .86rem; transition: transform .18s ease; }
|
||||
.project-link:hover::after { transform: translate(2px, -2px); }
|
||||
.project-card { padding: 1rem; transition: transform .2s ease, border-color .2s ease; }
|
||||
.project-card:hover { transform: translateY(-5px); border-color: var(--line-strong); }
|
||||
.project-thumb { min-height: 190px; border-radius: var(--radius-md); margin-bottom: 1rem; display: grid; place-items: center; }
|
||||
.project-thumb span {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-family: "Instrument Serif", Georgia, serif;
|
||||
font-size: clamp(3rem, 7vw, 5rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -.06em;
|
||||
color: rgba(255,255,255,.94);
|
||||
|
||||
.message.bot {
|
||||
align-self: flex-start;
|
||||
background: #ffffff;
|
||||
color: #212529;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.project-thumb i {
|
||||
position: absolute;
|
||||
width: 130px;
|
||||
height: 130px;
|
||||
border-radius: 28px;
|
||||
background: linear-gradient(145deg, rgba(255,255,255,.7), rgba(255,255,255,.04));
|
||||
transform: rotate(18deg) translate(28px, 12px);
|
||||
box-shadow: 0 30px 70px rgba(0,0,0,.26);
|
||||
|
||||
.chat-input-area {
|
||||
padding: 1.25rem;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.fitflow-visual { background: linear-gradient(135deg, #101010, #24352d 48%, #0d1611); }
|
||||
.community-visual { background: radial-gradient(circle at 25% 20%, rgba(255,255,255,.25), transparent 28%), linear-gradient(135deg, #111, #29323a 55%, #090b0d); }
|
||||
.ocr-visual { background: linear-gradient(135deg, #0a0a0a, #263144 52%, #0c0e16); }
|
||||
.moto-visual { background: radial-gradient(circle at 75% 18%, rgba(255,255,255,.26), transparent 26%), linear-gradient(135deg, #111, #3a3028 52%, #100d0a); }
|
||||
.testimonial-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; margin-top: 2rem; }
|
||||
.testimonial-card { padding: clamp(1.25rem, 3vw, 1.75rem); }
|
||||
.testimonial-card blockquote { margin: 0; color: var(--text); font-size: clamp(1.05rem, 1.5vw, 1.28rem); line-height: 1.65; }
|
||||
.testimonial-card footer { border-top: 1px solid var(--line); margin-top: 1.4rem; padding-top: 1rem; }
|
||||
.testimonial-card strong { display: block; font-weight: 750; }
|
||||
.testimonial-card span { display: block; color: var(--muted); font-size: .9rem; margin-top: .18rem; }
|
||||
.cookie-banner {
|
||||
position: fixed;
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
bottom: 1rem;
|
||||
z-index: 1080;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
box-shadow: 0 24px 80px rgba(0,0,0,.28);
|
||||
opacity: 0;
|
||||
transform: translateY(18px);
|
||||
pointer-events: none;
|
||||
transition: opacity .22s ease, transform .22s ease;
|
||||
|
||||
.chat-input-area form {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
.cookie-banner.is-visible { opacity: 1; transform: translateY(0); pointer-events: auto; }
|
||||
.cookie-banner strong { display: block; margin-bottom: .3rem; }
|
||||
.cookie-banner p { color: var(--muted); margin: 0; line-height: 1.55; font-size: .92rem; }
|
||||
.cookie-actions { display: flex; flex-wrap: wrap; gap: .55rem; justify-content: flex-end; min-width: max-content; }
|
||||
@media (max-width: 991.98px) {
|
||||
.project-featured { grid-template-columns: 1fr; }
|
||||
.testimonial-grid { grid-template-columns: 1fr; }
|
||||
.cookie-banner { align-items: flex-start; flex-direction: column; }
|
||||
.cookie-actions { width: 100%; justify-content: flex-start; min-width: 0; }
|
||||
|
||||
.chat-input-area input {
|
||||
flex: 1;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
padding: 0.75rem 1rem;
|
||||
outline: none;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
@media (max-width: 575.98px) {
|
||||
.project-visual { min-height: 280px; }
|
||||
.mock-browser { width: 78%; min-height: 210px; }
|
||||
.project-metric { grid-template-columns: 1fr; }
|
||||
.cookie-actions .btn { width: 100%; }
|
||||
|
||||
.chat-input-area input:focus {
|
||||
border-color: #23a6d5;
|
||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.2);
|
||||
}
|
||||
|
||||
.chat-input-area button {
|
||||
background: #212529;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.chat-input-area button:hover {
|
||||
background: #000;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* Background Animations */
|
||||
.bg-animations {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blob {
|
||||
position: absolute;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
animation: move 20s infinite alternate cubic-bezier(0.45, 0, 0.55, 1);
|
||||
}
|
||||
|
||||
.blob-1 {
|
||||
top: -10%;
|
||||
left: -10%;
|
||||
background: rgba(238, 119, 82, 0.4);
|
||||
}
|
||||
|
||||
.blob-2 {
|
||||
bottom: -10%;
|
||||
right: -10%;
|
||||
background: rgba(35, 166, 213, 0.4);
|
||||
animation-delay: -7s;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.blob-3 {
|
||||
top: 40%;
|
||||
left: 30%;
|
||||
background: rgba(231, 60, 126, 0.3);
|
||||
animation-delay: -14s;
|
||||
width: 450px;
|
||||
height: 450px;
|
||||
}
|
||||
|
||||
@keyframes move {
|
||||
0% { transform: translate(0, 0) rotate(0deg) scale(1); }
|
||||
33% { transform: translate(150px, 100px) rotate(120deg) scale(1.1); }
|
||||
66% { transform: translate(-50px, 200px) rotate(240deg) scale(0.9); }
|
||||
100% { transform: translate(0, 0) rotate(360deg) scale(1); }
|
||||
}
|
||||
|
||||
.header-link {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Admin Styles */
|
||||
.admin-container {
|
||||
max-width: 900px;
|
||||
margin: 3rem auto;
|
||||
padding: 2.5rem;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,0.15);
|
||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.admin-container h1 {
|
||||
margin-top: 0;
|
||||
color: #212529;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0 8px;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 1rem;
|
||||
color: #6c757d;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.table td {
|
||||
background: #fff;
|
||||
padding: 1rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table tr td:first-child { border-radius: 12px 0 0 12px; }
|
||||
.table tr td:last-child { border-radius: 0 12px 12px 0; }
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
padding: 0.75rem 1rem;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
transition: all 0.3s ease;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
outline: none;
|
||||
border-color: #23a6d5;
|
||||
box-shadow: 0 0 0 3px rgba(35, 166, 213, 0.1);
|
||||
}
|
||||
|
||||
.header-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-links {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.admin-card {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
padding: 2rem;
|
||||
border-radius: 20px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||
margin-bottom: 2.5rem;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.admin-card h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
background: #dc3545;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
background: #212529;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: #0088cc;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 0.8rem 1.5rem;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.webhook-url {
|
||||
font-size: 0.85em;
|
||||
color: #555;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.history-table-container {
|
||||
overflow-x: auto;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
padding: 1rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.history-table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.history-table-time {
|
||||
width: 15%;
|
||||
white-space: nowrap;
|
||||
font-size: 0.85em;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.history-table-user {
|
||||
width: 35%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.history-table-ai {
|
||||
width: 50%;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.no-messages {
|
||||
text-align: center;
|
||||
color: #777;
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@400;500;600;700&display=swap');
|
||||
@ -1,13 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="pg Web e APP Designer">
|
||||
<defs>
|
||||
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stop-color="#ffffff"/>
|
||||
<stop offset="0.52" stop-color="#b8c3b2"/>
|
||||
<stop offset="1" stop-color="#30342f"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="64" height="64" rx="18" fill="#080808"/>
|
||||
<circle cx="46" cy="18" r="12" fill="url(#g)" opacity="0.92"/>
|
||||
<path d="M13 43 C19 25 28 17 43 13 C35 24 28 34 25 51 Z" fill="url(#g)" opacity="0.58"/>
|
||||
<text x="13" y="39" fill="#ffffff" font-family="Georgia, serif" font-size="24" font-weight="400" letter-spacing="-2">pg</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 693 B |
@ -1,122 +1,39 @@
|
||||
(() => {
|
||||
const root = document.body;
|
||||
const themeToggle = document.getElementById('themeToggle');
|
||||
const storedTheme = localStorage.getItem('pg-theme');
|
||||
if (storedTheme === 'light' || storedTheme === 'dark') {
|
||||
root.setAttribute('data-bs-theme', storedTheme);
|
||||
}
|
||||
const syncThemeButton = () => {
|
||||
if (!themeToggle) return;
|
||||
themeToggle.textContent = root.getAttribute('data-bs-theme') === 'dark' ? 'Modo claro' : 'Modo escuro';
|
||||
};
|
||||
syncThemeButton();
|
||||
themeToggle?.addEventListener('click', () => {
|
||||
const next = root.getAttribute('data-bs-theme') === 'dark' ? 'light' : 'dark';
|
||||
root.setAttribute('data-bs-theme', next);
|
||||
localStorage.setItem('pg-theme', next);
|
||||
syncThemeButton();
|
||||
});
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const chatForm = document.getElementById('chat-form');
|
||||
const chatInput = document.getElementById('chat-input');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
|
||||
const video = document.getElementById('heroVideo');
|
||||
let rafId = null;
|
||||
const fadeWindow = 0.5;
|
||||
const monitorVideo = () => {
|
||||
if (video && Number.isFinite(video.duration) && video.duration > 0) {
|
||||
const current = video.currentTime || 0;
|
||||
const duration = video.duration;
|
||||
let opacity = 1;
|
||||
if (current < fadeWindow) opacity = current / fadeWindow;
|
||||
if (duration - current < fadeWindow) opacity = Math.max(0, (duration - current) / fadeWindow);
|
||||
video.style.opacity = String(Math.max(0, Math.min(1, opacity * 0.7)));
|
||||
}
|
||||
rafId = requestAnimationFrame(monitorVideo);
|
||||
};
|
||||
if (video) {
|
||||
video.addEventListener('loadedmetadata', () => {
|
||||
video.currentTime = 0;
|
||||
video.play().catch(() => {});
|
||||
if (!rafId) monitorVideo();
|
||||
const appendMessage = (text, sender) => {
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.classList.add('message', sender);
|
||||
msgDiv.textContent = text;
|
||||
chatMessages.appendChild(msgDiv);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
};
|
||||
|
||||
chatForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const message = chatInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
appendMessage(message, 'visitor');
|
||||
chatInput.value = '';
|
||||
|
||||
try {
|
||||
const response = await fetch('api/chat.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ message })
|
||||
});
|
||||
const data = await response.json();
|
||||
|
||||
// Artificial delay for realism
|
||||
setTimeout(() => {
|
||||
appendMessage(data.reply, 'bot');
|
||||
}, 500);
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
appendMessage("Sorry, something went wrong. Please try again.", 'bot');
|
||||
}
|
||||
});
|
||||
video.addEventListener('ended', () => {
|
||||
video.style.opacity = '0';
|
||||
window.setTimeout(() => {
|
||||
video.currentTime = 0;
|
||||
video.play().catch(() => {});
|
||||
}, 100);
|
||||
});
|
||||
video.load();
|
||||
}
|
||||
|
||||
const parallaxItems = [video, document.querySelector('.stats-panel')].filter(Boolean);
|
||||
const handleParallax = () => {
|
||||
const y = window.scrollY || 0;
|
||||
parallaxItems.forEach((item, idx) => {
|
||||
const speed = idx === 0 ? 0.08 : -0.025;
|
||||
item.style.transform = `translate3d(0, ${y * speed}px, 0)`;
|
||||
});
|
||||
};
|
||||
if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
||||
window.addEventListener('scroll', handleParallax, { passive: true });
|
||||
handleParallax();
|
||||
}
|
||||
|
||||
const form = document.getElementById('contactForm');
|
||||
const alertBox = document.getElementById('formAlert');
|
||||
const submitBtn = document.getElementById('submitBtn');
|
||||
const toastEl = document.getElementById('siteToast');
|
||||
const toastBody = document.getElementById('toastBody');
|
||||
const showAlert = (type, message) => {
|
||||
if (!alertBox) return;
|
||||
alertBox.className = `alert alert-${type}`;
|
||||
alertBox.textContent = message;
|
||||
};
|
||||
const showToast = (message) => {
|
||||
if (toastBody) toastBody.textContent = message;
|
||||
if (toastEl && window.bootstrap) new bootstrap.Toast(toastEl).show();
|
||||
};
|
||||
form?.addEventListener('submit', async (event) => {
|
||||
event.preventDefault();
|
||||
showAlert('secondary', 'A enviar o teu pedido...');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.textContent = 'A enviar...';
|
||||
}
|
||||
try {
|
||||
const response = await fetch('api/contact.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Accept': 'application/json' },
|
||||
body: new FormData(form)
|
||||
});
|
||||
const data = await response.json().catch(() => ({}));
|
||||
if (!response.ok || !data.success) {
|
||||
showAlert('warning', data.message || 'Não foi possível enviar. Revê os campos e tenta novamente.');
|
||||
return;
|
||||
}
|
||||
form.reset();
|
||||
showAlert('success', data.message || 'Mensagem enviada com sucesso.');
|
||||
showToast('Pedido recebido. Obrigado!');
|
||||
} catch (error) {
|
||||
showAlert('danger', 'Erro de ligação. Tenta novamente dentro de instantes.');
|
||||
} finally {
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = 'Enviar mensagem';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
const cookieBanner = document.getElementById("cookieBanner");
|
||||
const cookieAccept = document.getElementById("cookieAccept");
|
||||
const cookieDecline = document.getElementById("cookieDecline");
|
||||
const cookieChoice = localStorage.getItem("pg-cookie-choice");
|
||||
const closeCookieBanner = (choice) => {
|
||||
localStorage.setItem("pg-cookie-choice", choice);
|
||||
cookieBanner?.classList.remove("is-visible");
|
||||
};
|
||||
if (cookieBanner && !cookieChoice) {
|
||||
window.setTimeout(() => cookieBanner.classList.add("is-visible"), 650);
|
||||
}
|
||||
cookieAccept?.addEventListener("click", () => closeCookieBanner("accepted"));
|
||||
cookieDecline?.addEventListener("click", () => closeCookieBanner("declined"));
|
||||
})();
|
||||
});
|
||||
|
||||
470
index.php
470
index.php
@ -1,346 +1,150 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
@date_default_timezone_set('Europe/Lisbon');
|
||||
$projectName = $_SERVER['PROJECT_NAME'] ?? 'pg Web e APP Designer';
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Design e desenvolvimento de websites, aplicações web e mobile em Portugal.';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
@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="pt-PT">
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= htmlspecialchars($projectName, ENT_QUOTES, 'UTF-8') ?> | Websites e aplicações digitais</title>
|
||||
<?php if ($projectDescription): ?>
|
||||
<meta name="description" content="<?= htmlspecialchars($projectDescription, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<?php endif; ?>
|
||||
<meta property="og:title" content="<?= htmlspecialchars($projectName, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="<?= htmlspecialchars($projectName, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<?php endif; ?>
|
||||
<link rel="icon" type="image/svg+xml" href="assets/images/favicon.svg">
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>New Style</title>
|
||||
<?php
|
||||
// Read project preview data from environment
|
||||
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? '';
|
||||
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? '';
|
||||
?>
|
||||
<?php if ($projectDescription): ?>
|
||||
<!-- Meta description -->
|
||||
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
|
||||
<!-- Open Graph meta tags -->
|
||||
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<!-- Twitter meta tags -->
|
||||
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" />
|
||||
<?php endif; ?>
|
||||
<?php if ($projectImageUrl): ?>
|
||||
<!-- Open Graph image -->
|
||||
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<!-- Twitter image -->
|
||||
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
|
||||
<?php endif; ?>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="assets/css/fonts.css?v=2026062603">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="assets/css/custom.css?v=2026062603">
|
||||
<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 data-bs-theme="dark">
|
||||
<a class="skip-link" href="#conteudo">Saltar para o conteúdo</a>
|
||||
|
||||
<div class="site-shell">
|
||||
<video id="heroVideo" class="hero-video" muted playsinline preload="metadata" aria-hidden="true">
|
||||
<source src="https://d8j0ntlcm91z4.cloudfront.net/user_38xzZboKViGWJOttwIXH07lWA1P/hf_20260328_083109_283f3553-e28f-428b-a723-d639c617eb2b.mp4" type="video/mp4">
|
||||
</video>
|
||||
<div class="video-scrim" aria-hidden="true"></div>
|
||||
|
||||
<header class="site-header sticky-top">
|
||||
<nav class="navbar navbar-expand-lg py-3" aria-label="Navegação principal">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<a class="navbar-brand brand-mark" href="#home" aria-label="pg Web e APP Designer início">pg Web e APP Designer<sup>®</sup></a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNav" aria-controls="mainNav" aria-expanded="false" aria-label="Abrir navegação">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="mainNav">
|
||||
<ul class="navbar-nav mx-auto mb-2 mb-lg-0 gap-lg-2">
|
||||
<li class="nav-item"><a class="nav-link active" href="#home">Início</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#servicos">Serviços</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#portfolio">Portfólio</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#ferramentas">Ferramentas</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#processo">Processo</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#testemunhos">Testemunhos</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#contacto">Contacto</a></li>
|
||||
</ul>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button id="themeToggle" class="btn btn-ghost btn-sm" type="button" aria-label="Alternar dark mode">Modo claro</button>
|
||||
<a class="btn btn-primary-dark" href="#contacto">Começar projecto</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main id="conteudo">
|
||||
<section id="home" class="hero-section min-vh-100 d-flex align-items-center">
|
||||
<div class="container-xl px-3 px-lg-4 position-relative z-2">
|
||||
<div class="row justify-content-center text-center">
|
||||
<div class="col-12 col-xl-11">
|
||||
<p class="eyebrow animate-rise">Websites · Web Apps · Mobile UI</p>
|
||||
<h1 class="hero-title animate-rise">Da ideia ao produto digital: websites e apps com presença, clareza e performance.</h1>
|
||||
<p class="hero-copy animate-rise delay-1 mx-auto">Crio experiências digitais para marcas, freelancers e negócios que querem transformar uma primeira impressão num pedido de contacto real.</p>
|
||||
<div class="hero-actions animate-rise delay-2 d-flex flex-column flex-sm-row justify-content-center gap-3">
|
||||
<a class="btn btn-primary-dark btn-lg" href="#contacto">Começar projecto</a>
|
||||
<a class="btn btn-outline-soft btn-lg" href="#servicos">Ver serviços</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stats-panel animate-rise delay-2" aria-label="Métricas do estúdio">
|
||||
<div><strong>8+</strong><span>projectos entregues</span></div>
|
||||
<div><strong>3+</strong><span>anos de experiência</span></div>
|
||||
<div><strong>5+</strong><span>clientes satisfeitos</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="servicos" class="section-pad">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">O que eu faço</p>
|
||||
<h2>Uma entrega completa, pensada para lançar e evoluir o teu produto digital.</h2>
|
||||
<p>Da estratégia à manutenção, cada fase é tratada com detalhe para que o resultado seja bonito, rápido e fácil de usar.</p>
|
||||
</div>
|
||||
<div class="row g-3 g-lg-4">
|
||||
<?php
|
||||
$services = [
|
||||
['01', 'Estratégia Digital', 'Alinhamento de objectivos, público, mensagens principais e prioridades para criar um plano simples de execução.'],
|
||||
['02', 'Branding de Marca', 'Desde a ideia inicial até ao tom visual: nome, posicionamento, referências, identidade e coerência para o digital.'],
|
||||
['03', 'UX/UI Design', 'Desenho de fluxos, wireframes e interfaces responsivas com foco em conversão, leitura e experiência.'],
|
||||
['04', 'Websites One‑Page', 'Páginas rápidas e elegantes para apresentar serviços, captar leads e comunicar confiança desde o primeiro scroll.'],
|
||||
['05', 'Aplicações Web', 'Dashboards, áreas reservadas, formulários e ferramentas à medida com lógica de negócio clara e segura.'],
|
||||
['06', 'Mobile UI', 'Interfaces para apps mobile com navegação intuitiva, componentes consistentes e adaptação a vários ecrãs.'],
|
||||
['07', 'SEO Técnico', 'Estrutura semântica, performance, metadados e boas práticas para melhorar a descoberta orgânica.'],
|
||||
['08', 'Suporte e Manutenção', 'Acompanhamento após entrega: correcções, melhorias, backups, actualizações e evolução contínua.'],
|
||||
];
|
||||
foreach ($services as $service): ?>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<article class="service-card h-100">
|
||||
<span><?= htmlspecialchars($service[0], ENT_QUOTES, 'UTF-8') ?></span>
|
||||
<h3><?= htmlspecialchars($service[1], ENT_QUOTES, 'UTF-8') ?></h3>
|
||||
<p><?= htmlspecialchars($service[2], ENT_QUOTES, 'UTF-8') ?></p>
|
||||
</article>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="portfolio" class="section-pad portfolio-section">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Portfólio</p>
|
||||
<h2>Projectos digitais com foco em clareza, confiança e resultados.</h2>
|
||||
<p>Uma selecção curta com exactamente os projectos principais: website, web apps, comunidade e IA. Sem projectos extra.</p>
|
||||
</div>
|
||||
|
||||
<article class="project-featured" aria-labelledby="portfolio-veigas-title">
|
||||
<div class="project-visual real-estate-visual" aria-hidden="true">
|
||||
<span class="visual-orb"></span>
|
||||
<div class="mock-browser">
|
||||
<div class="mock-top"><span></span><span></span><span></span></div>
|
||||
<div class="mock-hero"></div>
|
||||
<div class="mock-lines"><i></i><i></i><i></i></div>
|
||||
</div>
|
||||
<strong>+320%</strong>
|
||||
</div>
|
||||
<div class="project-content">
|
||||
<span class="project-type">Web site em destaque</span>
|
||||
<h3 id="portfolio-veigas-title">Agência Imobiliária da Veigas</h3>
|
||||
<p>Redesenho completo do web site e da experiência digital para tornar a presença online mais rápida, clara e orientada à captação de contactos.</p>
|
||||
<div class="project-metric"><strong>+320%</strong><span>aumento de tráfego gerado</span></div>
|
||||
<a class="project-link" href="https://imoveisdeportugal.pt/" target="_blank" rel="noopener">Ver projecto</a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<div class="row g-3 g-lg-4 mt-4" aria-label="Outros projectos do portfólio">
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<article class="project-card h-100">
|
||||
<div class="project-thumb fitflow-visual" aria-hidden="true"><span>FF</span><i></i></div>
|
||||
<span class="project-type">Web App</span>
|
||||
<h3>FitFlow</h3>
|
||||
<p>Experiência digital para acompanhar rotinas, progresso e fluxos de treino com uma interface limpa.</p>
|
||||
<a class="project-link" href="https://fitflow.paulogomes.pt/" target="_blank" rel="noopener">Abrir Web App</a>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<article class="project-card h-100">
|
||||
<div class="project-thumb community-visual" aria-hidden="true"><span>?</span><i></i></div>
|
||||
<span class="project-type">Comunidade</span>
|
||||
<h3>Perdidos e Achados</h3>
|
||||
<p>Plataforma comunitária para aproximar pessoas, objectos perdidos e informação útil num só lugar.</p>
|
||||
<a class="project-link" href="https://perdidoseachados.paulogomes.pt/" target="_blank" rel="noopener">Ver comunidade</a>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<article class="project-card h-100">
|
||||
<div class="project-thumb ocr-visual" aria-hidden="true"><span>OCR</span><i></i></div>
|
||||
<span class="project-type">Web site de IA</span>
|
||||
<h3>Imagem com texto: OCR to Image</h3>
|
||||
<p>Interface orientada à conversão de imagens com texto em conteúdo legível, simples e directo.</p>
|
||||
<a class="project-link" href="https://ocrtoimage.paulogomes.pt/" target="_blank" rel="noopener">Ver web site de IA</a>
|
||||
</article>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-xl-3">
|
||||
<article class="project-card h-100">
|
||||
<div class="project-thumb moto-visual" aria-hidden="true"><span>↗</span><i></i></div>
|
||||
<span class="project-type">Web App</span>
|
||||
<h3>Moto trips - As minhas aventuras sobre rodas</h3>
|
||||
<p>Web app para organizar viagens, memórias e aventuras em duas rodas com estética editorial.</p>
|
||||
<a class="project-link" href="https://mototrips.paulogomes.pt/" target="_blank" rel="noopener">Ver Moto trips</a>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="ferramentas" class="section-pad section-quiet">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<div class="row align-items-end g-4">
|
||||
<div class="col-lg-6">
|
||||
<p class="eyebrow">Ferramentas de desenvolvimento</p>
|
||||
<h2 class="section-title">Stack moderna, leve e preparada para produção.</h2>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<p class="section-copy">Escolho tecnologia consoante o projecto: protótipos rápidos, websites em alojamento próprio, aplicações React ou integrações com PHP/MySQL.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tool-grid mt-4">
|
||||
<span>HTML5</span><span>CSS3</span><span>JavaScript</span><span>TypeScript</span><span>React</span><span>Vite</span><span>Tailwind CSS</span><span>Bootstrap</span><span>PHP</span><span>MySQL</span><span>WordPress</span><span>Git</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="processo" class="section-pad">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<div class="section-heading compact">
|
||||
<p class="eyebrow">Processo</p>
|
||||
<h2>Um caminho simples para sair da ideia e chegar ao lançamento.</h2>
|
||||
</div>
|
||||
<div class="timeline">
|
||||
<div><strong>01</strong><h3>Descoberta</h3><p>Entendo o negócio, objectivos, referências e restrições.</p></div>
|
||||
<div><strong>02</strong><h3>Direcção</h3><p>Defino estrutura, conteúdo, navegação e prioridades visuais.</p></div>
|
||||
<div><strong>03</strong><h3>Construção</h3><p>Implemento, testo responsividade e preparo a publicação.</p></div>
|
||||
<div><strong>04</strong><h3>Evolução</h3><p>Recolho feedback e planeio melhorias após o lançamento.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="testemunhos" class="section-pad testimonials-section section-quiet">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<div class="section-heading compact">
|
||||
<p class="eyebrow">Testemunhos</p>
|
||||
<h2>Experiências digitais criadas com proximidade, detalhe e foco no resultado.</h2>
|
||||
</div>
|
||||
<div class="testimonial-grid">
|
||||
<article class="testimonial-card">
|
||||
<blockquote>"O redesenho tornou a navegação mais simples, a marca mais clara e a comunicação muito mais profissional."</blockquote>
|
||||
<footer><strong>Cliente de imobiliário</strong><span>Website e experiência digital</span></footer>
|
||||
</article>
|
||||
<article class="testimonial-card">
|
||||
<blockquote>"A página ficou rápida, bonita e fácil de usar. O processo foi directo e sempre bem explicado."</blockquote>
|
||||
<footer><strong>Fundador de comunidade</strong><span>Produto digital e lançamento</span></footer>
|
||||
</article>
|
||||
<article class="testimonial-card">
|
||||
<blockquote>"Transformou uma ideia pouco estruturada numa web app com identidade, fluxo claro e pronta a evoluir."</blockquote>
|
||||
<footer><strong>Criador independente</strong><span>Web app à medida</span></footer>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="contacto" class="section-pad contact-section">
|
||||
<div class="container-xl px-3 px-lg-4">
|
||||
<div class="row g-4 g-xl-5 align-items-stretch">
|
||||
<div class="col-lg-5">
|
||||
<div class="contact-card h-100">
|
||||
<p class="eyebrow">Contacto</p>
|
||||
<h2>Vamos falar sobre o teu próximo projecto?</h2>
|
||||
<p class="section-copy">Envia uma mensagem com o essencial. Recebes resposta para marcar uma conversa e percebermos o melhor ponto de partida.</p>
|
||||
<ul class="contact-list">
|
||||
<li><span>Email</span><a href="mailto:ola@paulogomes.pt">ola@paulogomes.pt</a></li>
|
||||
<li><span>Telemóvel</span><a href="tel:+351917890864">+351 917 890 864</a><small><em>Chamada para a rede móvel nacional</em></small></li>
|
||||
<li><span>Localização</span><strong>Almada - Portugal</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-7">
|
||||
<form id="contactForm" class="form-card" novalidate>
|
||||
<div class="d-flex justify-content-between align-items-start gap-3 mb-4">
|
||||
<div>
|
||||
<p class="eyebrow mb-2">Pedido rápido</p>
|
||||
<h2 class="h3 mb-0">Contacto</h2>
|
||||
</div>
|
||||
<span class="secure-pill">Resposta por email</span>
|
||||
</div>
|
||||
<div id="formAlert" class="alert d-none" role="alert" aria-live="polite"></div>
|
||||
<input type="text" name="website" class="visually-hidden" tabindex="-1" autocomplete="off" aria-hidden="true">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="name">Nome</label>
|
||||
<input class="form-control" id="name" name="name" type="text" autocomplete="name" maxlength="120" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="email">Email</label>
|
||||
<input class="form-control" id="email" name="email" type="email" autocomplete="email" maxlength="160" required>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="phone">Telemóvel <span>opcional</span></label>
|
||||
<input class="form-control" id="phone" name="phone" type="tel" autocomplete="tel" maxlength="40">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label" for="project_type">Tipo de projecto</label>
|
||||
<select class="form-select" id="project_type" name="project_type" required>
|
||||
<option value="">Seleccionar</option>
|
||||
<option>Website institucional</option>
|
||||
<option>Landing page</option>
|
||||
<option>Aplicação web</option>
|
||||
<option>Aplicação mobile</option>
|
||||
<option>Branding de Marca</option>
|
||||
<option>Manutenção</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="message">Detalhe da mensagem</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="5" maxlength="1800" placeholder="Conta-me o que queres construir, prazo e objectivo principal." required></textarea>
|
||||
<div class="form-text">Mínimo 20 caracteres. Não incluas palavras-passe ou dados sensíveis.</div>
|
||||
</div>
|
||||
<div class="col-12 d-flex flex-column flex-sm-row gap-3 align-items-sm-center justify-content-between pt-2">
|
||||
<button id="submitBtn" class="btn btn-primary-dark btn-lg" type="submit">Enviar mensagem</button>
|
||||
<small class="muted-note">Ao enviar, o pedido é guardado e enviado por email para pg Web e APP Designer.</small>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container-xl px-3 px-lg-4 d-flex flex-column flex-md-row justify-content-between gap-2">
|
||||
<span>© <?= date('Y') ?> pg Web e APP Designer. Todos os direitos reservados.</span>
|
||||
<span>PHP <?= htmlspecialchars(PHP_VERSION, ENT_QUOTES, 'UTF-8') ?> · Lisboa <?= htmlspecialchars($now, ENT_QUOTES, 'UTF-8') ?></span>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
<div id="siteToast" class="toast align-items-center" role="status" aria-live="polite" aria-atomic="true">
|
||||
<div class="d-flex">
|
||||
<div id="toastBody" class="toast-body">Mensagem enviada.</div>
|
||||
<button type="button" class="btn-close me-2 m-auto" data-bs-dismiss="toast" aria-label="Fechar"></button>
|
||||
<body>
|
||||
<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"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : '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>
|
||||
</div>
|
||||
|
||||
<div id="cookieBanner" class="cookie-banner" role="dialog" aria-live="polite" aria-label="Aviso de cookies">
|
||||
<div>
|
||||
<strong>Privacidade e cookies</strong>
|
||||
<p>Este website usa cookies e dados técnicos para compreender como é utilizado, melhorar a experiência, medir desempenho e ajudar a encontrar problemas de navegação. Não vendemos dados pessoais.</p>
|
||||
</div>
|
||||
<div class="cookie-actions">
|
||||
<button id="cookieDecline" class="btn btn-outline-soft btn-sm" type="button">Continuar sem aceitar</button>
|
||||
<button id="cookieAccept" class="btn btn-primary-dark btn-sm" type="button">Aceitar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="assets/js/main.js?v=2026062603" defer></script>
|
||||
</main>
|
||||
<footer>
|
||||
Page updated: <?= htmlspecialchars($now) ?> (UTC)
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://pg-web-e-app-designer-8ee2.dev.flatlogic.app/sitemap.xml
|
||||
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://pg-web-e-app-designer-8ee2.dev.flatlogic.app/</loc>
|
||||
<lastmod>2026-06-26</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
Loading…
x
Reference in New Issue
Block a user