Inital Commit

This commit is contained in:
root
2025-06-24 15:43:32 +02:00
commit 23341b8abb
4657 changed files with 549083 additions and 0 deletions

6
html/bilder/about.txt Normal file
View File

@@ -0,0 +1,6 @@
This favicon was generated using the following font:
- Font Title: Leckerli One
- Font Author: Copyright (c) 2011 Gesine Todt (www.gesine-todt.de), with Reserved Font Names "Leckerli"
- Font Source: http://fonts.gstatic.com/s/leckerlione/v16/V8mCoQH8VCsNttEnxnGQ-1itLZxcBtItFw.ttf
- Font License: SIL Open Font License, 1.1 (http://scripts.sil.org/OFL))

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
html/bilder/blog.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

BIN
html/bilder/calc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

BIN
html/bilder/email.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 761 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
html/bilder/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
html/bilder/nextcloud.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
html/bilder/rezepte.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -0,0 +1 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

BIN
html/bilder/stream.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
html/bilder/wiki.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

1
html/drone/assets/fonts Normal file
View File

@@ -0,0 +1 @@
<!-- This file is intentionally left blank. -->

1
html/drone/assets/videos Normal file
View File

@@ -0,0 +1 @@
<!-- This file is intentionally left blank. -->

View File

@@ -0,0 +1,23 @@
.footer {
background-color: #2c3e50;
color: #ecf0f1;
text-align: center;
padding: 20px 0;
position: relative;
bottom: 0;
width: 100%;
}
.footer a {
color: #ecf0f1;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}
.footer p {
margin: 0;
font-size: 14px;
}

View File

@@ -0,0 +1,45 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
form {
display: flex;
flex-direction: column;
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}
label {
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"],
input[type="email"],
textarea {
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
input[type="submit"] {
background-color: #4CAF50;
color: white;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
input[type="submit"]:hover {
background-color: #45a049;
}

View File

@@ -0,0 +1,98 @@
/* Header Component Styles - Updated to match Gallery page */
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95); /* Match gallery page background */
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-img {
height: 40px;
width: auto;
margin-right: 10px;
}
.logo h1 {
font-size: 1.8rem;
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
filter: brightness(0.9);
}
.main-nav ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 1.5rem;
}
.main-nav a {
color: var(--text-dark);
text-decoration: none;
font-weight: 600;
font-size: 1.1rem;
letter-spacing: 0.5px;
padding: 0.5rem 0.8rem;
position: relative;
transition: color 0.3s ease;
}
.main-nav a:hover, .main-nav a.active {
color: var(--primary-color);
}
.main-nav a::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background: var(--gradient-primary);
transition: width 0.3s ease;
}
.main-nav a:hover::after, .main-nav a.active::after {
width: 100%;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.main-nav {
display: none;
position: fixed;
top: 76px;
left: 0;
width: 100%;
height: auto;
max-height: calc(100vh - 76px);
overflow-y: auto;
background: rgba(232, 233, 234, 0.95); /* Changed to match the header background */
padding: 1rem 0;
z-index: 1000;
}
}

View File

@@ -0,0 +1,38 @@
.hero {
background-image: url('../assets/images/hero-background.jpg');
background-size: cover;
background-position: center;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
text-align: center;
padding: 20px;
}
.hero h1 {
font-size: 3rem;
margin: 0;
}
.hero p {
font-size: 1.5rem;
margin: 10px 0 20px;
}
.hero .cta-button {
background-color: #ffcc00;
color: #333;
padding: 15px 30px;
border: none;
border-radius: 5px;
font-size: 1.2rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.hero .cta-button:hover {
background-color: #e6b800;
}

View File

@@ -0,0 +1,70 @@
/* Mobile navigation styles */
/* Mobile toggle button */
.mobile-toggle {
display: none;
font-size: 1.75rem;
color: var(--primary-color);
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
z-index: 1000;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.mobile-toggle {
display: block; /* Show on mobile */
}
.main-nav {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: var(--bg-light);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
z-index: 999;
border-radius: 0 0 10px 10px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.main-nav.active {
display: block;
}
.main-nav ul {
flex-direction: column;
width: 100%;
padding: 1rem 0;
gap: 0;
}
.main-nav li {
margin: 0;
width: 100%;
text-align: center;
}
.main-nav a {
display: block;
padding: 0.75rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
width: 100%;
}
.main-nav a:hover::after,
.main-nav a.active::after {
display: none;
}
/* Ensure footer copyright is visible and properly formatted on mobile */
footer .container p {
text-align: center;
font-size: 0.9rem;
margin: 0;
padding: 1rem 0;
}
}

99
html/drone/css/main.css Normal file
View File

@@ -0,0 +1,99 @@
body {
margin: 0;
font-family: 'Arial', sans-serif;
line-height: 1.6;
}
header {
background: #4CAF50;
color: #fff;
padding: 20px 0;
text-align: center;
}
h1, h2, h3 {
margin: 0;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.5em;
}
main {
padding: 20px;
padding-top: 90px; /* This value should match your header height plus any desired spacing */
}
footer {
background: #333;
color: #fff;
text-align: center;
padding: 10px 0;
position: relative;
bottom: 0;
width: 100%;
}
a {
color: #4CAF50;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
.hero {
background: url('../assets/images/hero-bg.jpg') no-repeat center center/cover;
height: 400px;
display: flex;
align-items: center;
justify-content: center;
color: white;
text-align: center;
margin-top: -30px; /* Adjust to reduce some of the padding for hero sections */
}
.hero h1 {
font-size: 3em;
}
.form-group {
margin-bottom: 15px;
}
input[type="text"],
input[type="email"],
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background: #45a049;
}

46
html/drone/css/normalize.css vendored Normal file
View File

@@ -0,0 +1,46 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, footer,
header, hgroup, main, menu, nav, output, ruby,
section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

40
html/drone/email-test.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
// Simple email test script
echo "<h1>Email Test</h1>";
// Check if mail function exists
if (!function_exists('mail')) {
echo "<p style='color:red'>Error: mail() function is not available!</p>";
echo "<p>Make sure PHP is configured with mail support.</p>";
exit;
}
// Try to send a test email
$to = "monitor@egonetix.de";
$subject = "Test Email from Luftglanz";
$message = "This is a test email sent from " . $_SERVER['SERVER_NAME'] . " at " . date('Y-m-d H:i:s');
$headers = "From: test@" . $_SERVER['SERVER_NAME'] . "\r\n" .
"Reply-To: test@" . $_SERVER['SERVER_NAME'] . "\r\n" .
"X-Mailer: PHP/" . phpversion();
$success = mail($to, $subject, $message, $headers);
if ($success) {
echo "<p style='color:green'>Success! Test email was sent to $to</p>";
} else {
echo "<p style='color:red'>Failed to send email. Check server mail configuration.</p>";
// Check mail configuration
echo "<h2>Mail Configuration:</h2>";
echo "<pre>";
echo "sendmail_path: " . ini_get('sendmail_path') . "\n";
echo "SMTP: " . ini_get('SMTP') . "\n";
echo "smtp_port: " . ini_get('smtp_port') . "\n";
echo "</pre>";
}
// Display PHP info for debugging
echo "<h2>PHP Information:</h2>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "</p>";
?>

73
html/drone/extract.py Executable file
View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python3
# Full Python script to fetch new tickets from Zammad, extract phone numbers, and update the user's profile.
import requests
import re
# Configuration
ZAMMAD_URL = "https://kontakt.luftglanz.de"
API_TOKEN = "iwXhnY-mb1PFqYHZhtjcRQqgJuNTv0ctPkMthbE2yMG1bXm-NyW2ZkMpu9PX1-9P" # Replace this with your actual token
HEADERS = {
"Authorization": f"Token token={API_TOKEN}",
"Content-Type": "application/json"
}
def get_new_tickets():
resp = requests.get(f"{ZAMMAD_URL}/api/v1/tickets?state=new", headers=HEADERS)
resp.raise_for_status()
return resp.json()
def get_ticket_articles(ticket_id):
resp = requests.get(f"{ZAMMAD_URL}/api/v1/ticket_articles/by_ticket/{ticket_id}", headers=HEADERS)
resp.raise_for_status()
return resp.json()
def extract_phone(text):
# Look for "Telefonnummer: <number>" or similar
match = re.search(r'(?:Telefonnummer|Tel\.?|Handy|Mobil)[^\d]*(\+?\d[\d\s\-()]{7,})', text, re.IGNORECASE)
if match:
number = match.group(1).replace(" ", "").replace("-", "")
return number
# Fallback: generic phone number pattern
match = re.search(r'(\+?\d[\d\s\-()]{7,})', text)
if match:
number = match.group(1).replace(" ", "").replace("-", "")
return number
return None
def get_user(user_id):
resp = requests.get(f"{ZAMMAD_URL}/api/v1/users/{user_id}", headers=HEADERS)
resp.raise_for_status()
return resp.json()
def update_user_phone_or_mobile(user_id, phone):
# If phone starts with 01, treat as mobile
field = "mobile" if phone.startswith("01") else "phone"
data = {field: phone}
resp = requests.put(f"{ZAMMAD_URL}/api/v1/users/{user_id}", json=data, headers=HEADERS)
return resp.status_code == 200, field
def process():
tickets = get_new_tickets()
for ticket in tickets:
ticket_id = ticket["id"]
customer_id = ticket["customer_id"]
articles = get_ticket_articles(ticket_id)
for article in articles:
body = article.get("body", "")
phone = extract_phone(body)
if phone:
user = get_user(customer_id)
# Check if the field is already set
field = "mobile" if phone.startswith("01") else "phone"
if not user.get(field):
success, used_field = update_user_phone_or_mobile(customer_id, phone)
print(f"Updated user {user['email']} with {used_field} {phone}: {'' if success else ''}")
break
# Execute the process
process()

View File

@@ -0,0 +1,76 @@
<?php
// Alternative form handler using GET method
// This file works around servers that block POST to PHP files
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Log to help with debugging
error_log("Form-GET-Handler received request: " . date('Y-m-d H:i:s'));
error_log("REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD']);
// Process form data from either GET or POST
$formData = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET;
// Sanitize inputs
$name = isset($formData['name']) ? htmlspecialchars($formData['name']) : '';
$email = isset($formData['email']) ? filter_var($formData['email'], FILTER_SANITIZE_EMAIL) : '';
$phone = isset($formData['phone']) ? htmlspecialchars($formData['phone']) : '';
$message = isset($formData['message']) ? htmlspecialchars($formData['message']) : '';
$subjectField = isset($formData['subject']) ? htmlspecialchars($formData['subject']) : '';
// Log the received data
error_log("Received data - Name: $name, Email: $email, Phone: $phone");
// Simple validation
$success = false;
$errorMessage = '';
if (empty($name) || empty($email) || empty($message) || empty($subjectField)) {
$errorMessage = 'Bitte füllen Sie alle Pflichtfelder aus.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errorMessage = 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
} else {
// Construct message for the email
$emailBody = $message;
if (!empty($phone)) {
$emailBody .= "\n\nTelefonnummer: $phone";
}
// Email details
$to = "kontakt@luftglanz.de";
$subject = $subjectField ?: "Neue Kontaktanfrage von $name";
// Attempt to send email via internal mail function
$mailSent = mail($to, $subject, $emailBody,
"From: $name <luftglanz@egonetix.de>\r\n" .
"Reply-To: $email\r\n" .
"X-Mailer: PHP/" . phpversion()
);
if ($mailSent) {
$success = true;
} else {
$errorMessage = 'Es gab ein Problem beim Senden Ihrer Nachricht. Bitte versuchen Sie es später erneut.';
error_log("Mail sending failed");
}
}
// Handle the response
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
// Ajax request - return JSON
header('Content-Type: application/json');
echo json_encode([
'success' => $success,
'message' => $errorMessage
]);
} else {
// Regular form submission - redirect with status
if ($success) {
header('Location: index.html?form_success=1#contact');
} else {
header('Location: index.html?form_error=' . urlencode($errorMessage) . '#contact');
}
}
?>

63
html/drone/form-test.html Normal file
View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Test</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
form { background: #f8f8f8; padding: 20px; border-radius: 8px; margin-bottom: 30px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input, textarea { width: 100%; padding: 8px; box-sizing: border-box; }
button { background: #4CAF50; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; }
h2 { margin-top: 30px; color: #333; border-bottom: 1px solid #ddd; padding-bottom: 10px; }
</style>
</head>
<body>
<h1>Form Test - Find a Working Method</h1>
<p>This page tests different form submission methods to find one that works on this server.</p>
<h2>Method 1: Standard POST Form (Original)</h2>
<p>This method often gets blocked with a 405 error on some servers.</p>
<form action="process-form.php" method="POST">
<div class="form-group">
<label for="name1">Name:</label>
<input type="text" id="name1" name="name" value="Test User" required>
</div>
<div class="form-group">
<label for="email1">Email:</label>
<input type="email" id="email1" name="email" value="test@example.com" required>
</div>
<div class="form-group">
<label for="message1">Message:</label>
<textarea id="message1" name="message" rows="4" required>This is a test message using POST method.</textarea>
</div>
<button type="submit">Submit with POST</button>
</form>
<h2>Method 2: GET Method Form</h2>
<p>This method uses GET which may bypass the 405 error.</p>
<form action="form-get-handler.php" method="GET">
<div class="form-group">
<label for="name2">Name:</label>
<input type="text" id="name2" name="name" value="Test User" required>
</div>
<div class="form-group">
<label for="email2">Email:</label>
<input type="email" id="email2" name="email" value="test@example.com" required>
</div>
<div class="form-group">
<label for="phone2">Phone:</label>
<input type="tel" id="phone2" name="phone" value="123-456-7890">
</div>
<div class="form-group">
<label for="message2">Message:</label>
<textarea id="message2" name="message" rows="4" required>This is a test message using GET method.</textarea>
</div>
<button type="submit">Submit with GET</button>
</form>
<p><a href="index.html">Back to Main Page</a></p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

BIN
html/drone/images/roof.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

BIN
html/drone/images/roof1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
html/drone/images/roof2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@@ -0,0 +1,11 @@
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="index.html" data-i18n="nav_home">Startseite</a></li>
<li><a href="index.html#services" data-i18n="nav_services">Leistungen</a></li>
<li><a href="index.html#how-it-works" data-i18n="nav_how_it_works">Ablauf</a></li>
<li><a href="index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="index.html#contact" data-i18n="nav_contact">Kontakt</a></li>
<li><a href="pages/products.html" data-i18n="nav_products">Produkte</a></li>
<li><a href="pages/gallery.html" data-i18n="nav_gallery">Galerie</a></li>
</ul>
</nav>

1093
html/drone/index.html Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,334 @@
/**
* Roof Cleaning Drone Background Animations
* Creates animated drones with water spray effects
*/
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Apply the background to the entire site
applyCleanBackground();
// Add animated drones to hero section
enhanceHeroSection();
// Add water droplet effects to service cards
enhanceServiceCards();
});
function applyCleanBackground() {
// Remove any existing star/cosmic backgrounds
const existingStars = document.querySelector('.stars');
if (existingStars) {
existingStars.remove();
}
// Add clean background to body
document.body.style.background = '#f8f9fa';
document.body.style.backgroundImage = `
linear-gradient(120deg, rgba(99, 183, 241, 0.1) 0%, transparent 40%),
linear-gradient(240deg, rgba(220, 237, 249, 0.2) 0%, transparent 40%)
`;
// Add subtle diagonal pattern
const pattern = document.createElement('div');
pattern.className = 'clean-pattern';
pattern.style.position = 'fixed';
pattern.style.top = '0';
pattern.style.left = '0';
pattern.style.width = '100%';
pattern.style.height = '100%';
pattern.style.pointerEvents = 'none';
pattern.style.opacity = '0.03';
pattern.style.zIndex = '-1';
pattern.style.backgroundImage = `
repeating-linear-gradient(
45deg,
#3498db,
#3498db 2px,
transparent 2px,
transparent 10px
)
`;
document.body.appendChild(pattern);
// Update text colors for better contrast on light background
document.querySelectorAll('p, h1, h2, h3, h4, h5, h6').forEach(el => {
// Only change if still using the dark theme colors
if (getComputedStyle(el).color === 'rgb(216, 216, 255)' ||
getComputedStyle(el).color === 'rgb(143, 143, 183)') {
el.style.color = '#505050';
}
});
}
function enhanceHeroSection() {
const heroSection = document.querySelector('.hero');
if (!heroSection) return;
// Remove any existing background
if (heroSection.style.backgroundImage.includes('stars-bg.jpg')) {
heroSection.style.backgroundImage = 'none';
}
// Create a sky gradient background
heroSection.style.background = 'linear-gradient(to bottom, #87CEEB 0%, #e6f7ff 100%)';
heroSection.style.position = 'relative';
heroSection.style.overflow = 'hidden';
// Create animation container
const animationContainer = document.createElement('div');
animationContainer.className = 'drone-animation-container';
animationContainer.style.position = 'absolute';
animationContainer.style.top = '0';
animationContainer.style.left = '0';
animationContainer.style.width = '100%';
animationContainer.style.height = '100%';
animationContainer.style.pointerEvents = 'none';
animationContainer.style.zIndex = '1';
heroSection.appendChild(animationContainer);
// Add roof silhouette
const roofSilhouette = document.createElement('div');
roofSilhouette.className = 'roof-silhouette';
roofSilhouette.style.position = 'absolute';
roofSilhouette.style.bottom = '0';
roofSilhouette.style.left = '0';
roofSilhouette.style.width = '100%';
roofSilhouette.style.height = '20%';
roofSilhouette.style.background = 'url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAwIDIwMCI+PHBhdGggZD0iTTAgMjAwVjE1MEg1MEwxMDAgMTAwTDE1MCAxNTBIMjAwTDI1MCAxMDBMMzAwIDE1MEgzNTBMNDAwIDUwTDQ1MCAxNTBINTAwTDU1MCAxMDBMNjAwIDE1MEg2NTBMNzAwIDEwMEw3NTAgMTUwSDgwMEw4NTAgNTBMOTAwIDE1MEg5NTBMMTAwMCAxMDBMMTA1MCAxNTBIMTEwMEwxMTUwIDEwMEwxMjAwIDE1MFYyMDBIMFoiIGZpbGw9IiNlMGUwZTAiIGZpbGwtb3BhY2l0eT0iMC40Ii8+PC9zdmc+")';
roofSilhouette.style.backgroundSize = 'cover';
roofSilhouette.style.zIndex = '0';
animationContainer.appendChild(roofSilhouette);
// Create multiple drones with spray effects
createCleaningDrone(animationContainer, 20, 30, -1, 20);
createCleaningDrone(animationContainer, 60, 20, 1, 25);
createCleaningDrone(animationContainer, 40, 50, -1, 30);
// Add keyframes for animations
addDroneAnimationStyles();
}
function createCleaningDrone(container, startX, startY, direction, duration) {
// Create drone container
const droneElement = document.createElement('div');
droneElement.className = 'cleaning-drone';
droneElement.style.position = 'absolute';
droneElement.style.left = `${startX}%`;
droneElement.style.top = `${startY}%`;
droneElement.style.width = '60px';
droneElement.style.height = '60px';
droneElement.style.zIndex = '2';
// Calculate unique animation name
const animationName = `drone-path-${startX}-${startY}`;
droneElement.style.animation = `${animationName} ${duration}s linear infinite`;
// Create drone body
const droneBody = document.createElement('div');
droneBody.className = 'drone-body';
droneBody.style.position = 'relative';
droneBody.style.width = '40px';
droneBody.style.height = '40px';
droneBody.style.borderRadius = '50%';
droneBody.style.backgroundColor = '#3498db';
droneBody.style.border = '2px solid #2980b9';
droneBody.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.2)';
droneBody.style.margin = '0 auto';
droneBody.style.zIndex = '2';
droneElement.appendChild(droneBody);
// Add central hub
const droneHub = document.createElement('div');
droneHub.className = 'drone-hub';
droneHub.style.position = 'absolute';
droneHub.style.top = '50%';
droneHub.style.left = '50%';
droneHub.style.width = '15px';
droneHub.style.height = '15px';
droneHub.style.borderRadius = '50%';
droneHub.style.backgroundColor = '#fff';
droneHub.style.transform = 'translate(-50%, -50%)';
droneHub.style.zIndex = '3';
droneBody.appendChild(droneHub);
// Add propellers
for (let i = 0; i < 4; i++) {
const propContainer = document.createElement('div');
propContainer.className = 'propeller-container';
propContainer.style.position = 'absolute';
propContainer.style.width = '15px';
propContainer.style.height = '15px';
// Position propellers around drone
switch(i) {
case 0: // top left
propContainer.style.top = '-10px';
propContainer.style.left = '-10px';
break;
case 1: // top right
propContainer.style.top = '-10px';
propContainer.style.right = '-10px';
break;
case 2: // bottom left
propContainer.style.bottom = '-10px';
propContainer.style.left = '-10px';
break;
case 3: // bottom right
propContainer.style.bottom = '-10px';
propContainer.style.right = '-10px';
break;
}
// Create propeller
const propeller = document.createElement('div');
propeller.className = 'propeller';
propeller.style.width = '100%';
propeller.style.height = '100%';
propeller.style.borderRadius = '50%';
propeller.style.borderTop = '2px solid rgba(255,255,255,0.8)';
propeller.style.borderLeft = '2px solid rgba(255,255,255,0.6)';
propeller.style.animation = 'spin-propeller 0.15s linear infinite';
propContainer.appendChild(propeller);
droneBody.appendChild(propContainer);
}
// Create water spray effect
const waterSpray = document.createElement('div');
waterSpray.className = 'water-spray';
waterSpray.style.position = 'absolute';
waterSpray.style.bottom = '-20px';
waterSpray.style.left = '50%';
waterSpray.style.transform = 'translateX(-50%)';
waterSpray.style.width = '30px';
waterSpray.style.height = '50px';
waterSpray.style.zIndex = '1';
droneElement.appendChild(waterSpray);
// Add water droplets to the spray
for (let i = 0; i < 12; i++) {
const droplet = document.createElement('div');
droplet.className = 'water-droplet';
droplet.style.position = 'absolute';
droplet.style.width = `${3 + Math.random() * 3}px`;
droplet.style.height = `${5 + Math.random() * 7}px`;
droplet.style.backgroundColor = 'rgba(173, 216, 230, 0.7)';
droplet.style.borderRadius = '50%';
// Randomize position within spray
const angle = Math.random() * 30 - 15; // -15 to 15 degrees
const distance = 5 + Math.random() * 45; // 5 to 50px
droplet.style.top = `${distance}px`;
droplet.style.left = '50%';
droplet.style.transform = `translateX(-50%) rotate(${angle}deg)`;
// Different animation duration for each droplet
const fallDuration = 0.5 + Math.random() * 0.5;
droplet.style.animation = `fall-droplet ${fallDuration}s infinite linear`;
waterSpray.appendChild(droplet);
}
// Create path animation for this specific drone
const keyframesStyle = document.createElement('style');
keyframesStyle.innerHTML = `
@keyframes ${animationName} {
0% {
transform: translateX(${direction > 0 ? '-100px' : '100vw'}) translateY(0px) rotate(${direction > 0 ? '0deg' : '180deg'});
}
25% {
transform: translateX(${direction > 0 ? 'calc(20vw)' : 'calc(80vw - 100px)'}) translateY(-20px) rotate(${direction > 0 ? '0deg' : '180deg'});
}
50% {
transform: translateX(${direction > 0 ? 'calc(40vw)' : 'calc(60vw - 100px)'}) translateY(30px) rotate(${direction > 0 ? '0deg' : '180deg'});
}
75% {
transform: translateX(${direction > 0 ? 'calc(60vw)' : 'calc(40vw - 100px)'}) translateY(-15px) rotate(${direction > 0 ? '0deg' : '180deg'});
}
100% {
transform: translateX(${direction > 0 ? '100vw' : '-100px'}) translateY(10px) rotate(${direction > 0 ? '0deg' : '180deg'});
}
}
`;
document.head.appendChild(keyframesStyle);
container.appendChild(droneElement);
}
function addDroneAnimationStyles() {
const styleSheet = document.createElement('style');
styleSheet.innerHTML = `
@keyframes spin-propeller {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fall-droplet {
0% {
opacity: 0.8;
transform: translateX(-50%) translateY(0) scale(1);
}
100% {
opacity: 0;
transform: translateX(-50%) translateY(30px) scale(0.5);
}
}
`;
document.head.appendChild(styleSheet);
}
function enhanceServiceCards() {
const serviceCards = document.querySelectorAll('.service-card');
if (!serviceCards.length) return;
serviceCards.forEach(card => {
// Update card styling to match clean theme
card.style.backgroundColor = 'white';
card.style.borderRadius = '10px';
card.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.1)';
card.style.border = '1px solid rgba(0, 0, 0, 0.05)';
card.style.overflow = 'hidden';
// Add water droplet accent at the top
const waterAccent = document.createElement('div');
waterAccent.className = 'water-accent';
waterAccent.style.height = '5px';
waterAccent.style.width = '100%';
waterAccent.style.position = 'absolute';
waterAccent.style.top = '0';
waterAccent.style.left = '0';
waterAccent.style.background = 'linear-gradient(90deg, #3498db, #00c2cb)';
card.prepend(waterAccent);
// Add subtle cleaning pattern to each card
addCleaningIconToCard(card);
});
}
function addCleaningIconToCard(card) {
// Create a container for the icon
const iconContainer = document.createElement('div');
iconContainer.className = 'cleaning-icon';
iconContainer.style.position = 'absolute';
iconContainer.style.top = '15px';
iconContainer.style.right = '15px';
iconContainer.style.width = '30px';
iconContainer.style.height = '30px';
iconContainer.style.opacity = '0.1';
// Randomly choose between different cleaning icons
const icons = [
// Water droplet
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 20C16.4183 20 20 16.4183 20 12C20 7.58172 12 3 12 3C12 3 4 7.58172 4 12C4 16.4183 7.58172 20 12 20Z" fill="#3498db"/></svg>`,
// Spray bottle
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M8 3H11V5H13V3H16V5H14V7H17L19 10V21H6V10L8 7H11V5H8V3ZM8 10V19H17V10L15.8 8H9.2L8 10Z" fill="#3498db"/></svg>`,
// Drone
`<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 8C13.1046 8 14 7.10457 14 6C14 4.89543 13.1046 4 12 4C10.8954 4 10 4.89543 10 6C10 7.10457 10.8954 8 12 8Z" fill="#3498db"/><path d="M4 10C5.10457 10 6 9.10457 6 8C6 6.89543 5.10457 6 4 6C2.89543 6 2 6.89543 2 8C2 9.10457 2.89543 10 4 10Z" fill="#3498db"/><path d="M20 10C21.1046 10 22 9.10457 22 8C22 6.89543 21.1046 6 20 6C18.8954 6 18 6.89543 18 8C18 9.10457 18.8954 10 20 10Z" fill="#3498db"/><path d="M12 20C13.1046 20 14 19.1046 14 18C14 16.8954 13.1046 16 12 16C10.8954 16 10 16.8954 10 18C10 19.1046 10.8954 20 12 20Z" fill="#3498db"/><path d="M12 8V16M4 10L10 16M20 10L14 16" stroke="#3498db" stroke-width="2" stroke-linecap="round"/></svg>`
];
// Choose a random icon
iconContainer.innerHTML = icons[Math.floor(Math.random() * icons.length)];
card.appendChild(iconContainer);
}
})();

View File

@@ -0,0 +1,139 @@
/**
* Bright Theme Elements and Visual Enhancements
*/
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Add clean sky background to hero section
enhanceHeroSection();
// Add clean visual elements to services section
enhanceServicesSection();
// Add bright UI enhancements
applyBrightUIEnhancements();
});
function enhanceHeroSection() {
const heroSection = document.querySelector('.hero');
if (!heroSection) return;
// Create and add sky background with subtle clouds
heroSection.style.backgroundImage = 'linear-gradient(to bottom, #e6f3ff 0%, #ffffff 100%)';
heroSection.style.position = 'relative';
heroSection.style.overflow = 'hidden';
// Add subtle pattern
const pattern = document.createElement('div');
pattern.className = 'hero-pattern';
pattern.style.position = 'absolute';
pattern.style.top = 0;
pattern.style.left = 0;
pattern.style.right = 0;
pattern.style.bottom = 0;
pattern.style.opacity = 0.3;
pattern.style.backgroundImage = 'url("data:image/svg+xml,%3Csvg width=\'100\' height=\'100\' viewBox=\'0 0 100 100\' xmlns=\'http://www.w3.org/2000/svg\'%3E%3Cpath d=\'M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z\' fill=\'%230078d4\' fill-opacity=\'0.1\' fill-rule=\'evenodd\'/%3E%3C/svg%3E")';
heroSection.insertBefore(pattern, heroSection.firstChild);
// Add animated simple drone icon
const droneContainer = document.createElement('div');
droneContainer.className = 'drone-animation';
droneContainer.style.position = 'absolute';
droneContainer.style.top = '20%';
droneContainer.style.right = '10%';
droneContainer.style.width = '80px';
droneContainer.style.height = '80px';
droneContainer.style.zIndex = '1';
droneContainer.style.animation = 'float 6s ease-in-out infinite';
// Create drone SVG
droneContainer.innerHTML = `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 8C13.1046 8 14 7.10457 14 6C14 4.89543 13.1046 4 12 4C10.8954 4 10 4.89543 10 6C10 7.10457 10.8954 8 12 8Z" fill="#0078d4"/>
<path d="M4 10C5.10457 10 6 9.10457 6 8C6 6.89543 5.10457 6 4 6C2.89543 6 2 6.89543 2 8C2 9.10457 2.89543 10 4 10Z" fill="#0078d4"/>
<path d="M20 10C21.1046 10 22 9.10457 22 8C22 6.89543 21.1046 6 20 6C18.8954 6 18 6.89543 18 8C18 9.10457 18.8954 10 20 10Z" fill="#0078d4"/>
<path d="M12 20C13.1046 20 14 19.1046 14 18C14 16.8954 13.1046 16 12 16C10.8954 16 10 16.8954 10 18C10 19.1046 10.8954 20 12 20Z" fill="#0078d4"/>
<path d="M12 8V16M4 10L10 16M20 10L14 16" stroke="#0078d4" stroke-width="2" stroke-linecap="round"/>
<animate attributeName="opacity" values="0.7;1;0.7" dur="2s" repeatCount="indefinite" />
</svg>`;
heroSection.appendChild(droneContainer);
// Add the floating animation
const style = document.createElement('style');
style.textContent = `
@keyframes float {
0% { transform: translateY(0px) rotate(0deg); }
50% { transform: translateY(-15px) rotate(2deg); }
100% { transform: translateY(0px) rotate(0deg); }
}
`;
document.head.appendChild(style);
}
function enhanceServicesSection() {
const servicesSection = document.getElementById('services');
if (!servicesSection) return;
// Add subtle pattern background
servicesSection.style.backgroundImage = 'linear-gradient(120deg, #ffffff 0%, #f8f9fa 100%)';
servicesSection.style.position = 'relative';
// Add cleaning icons to service cards
const serviceCards = document.querySelectorAll('.service-card');
const icons = [
'<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 21L12 12M12 12L21 3M12 12L3 3M12 12L21 21" stroke="#0078d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
'<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="12" r="9" stroke="#0078d4" stroke-width="2"/><path d="M12 7V12L15 15" stroke="#0078d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
'<svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 3v18M3 12h18" stroke="#0078d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
];
serviceCards.forEach((card, index) => {
const icon = document.createElement('div');
icon.className = 'service-icon';
icon.style.marginBottom = '1.5rem';
icon.innerHTML = icons[index % icons.length];
card.insertBefore(icon, card.firstChild);
});
}
function applyBrightUIEnhancements() {
// Enhance buttons
const buttons = document.querySelectorAll('.btn-primary');
buttons.forEach(button => {
button.style.background = 'var(--primary-color)';
button.style.boxShadow = '0 4px 12px rgba(0, 120, 212, 0.2)';
button.style.border = 'none';
// Add hover effect
button.addEventListener('mouseenter', function() {
this.style.background = 'var(--gradient-primary)';
this.style.transform = 'translateY(-2px)';
});
button.addEventListener('mouseleave', function() {
this.style.background = 'var(--primary-color)';
this.style.transform = 'translateY(0)';
});
});
// Add roof-related decorative elements to sections
const sections = document.querySelectorAll('.section-container');
sections.forEach(section => {
// Add subtle top decoration
const decoration = document.createElement('div');
decoration.className = 'section-decoration';
decoration.style.height = '4px';
decoration.style.width = '100px';
decoration.style.background = 'var(--gradient-primary)';
decoration.style.borderRadius = '2px';
decoration.style.marginBottom = '2rem';
const title = section.querySelector('.section-title');
if (title) {
title.style.display = 'flex';
title.style.flexDirection = 'column';
title.style.alignItems = 'center';
title.style.marginBottom = '3rem';
title.appendChild(decoration);
}
});
}
})();

View File

@@ -0,0 +1,39 @@
document.addEventListener('DOMContentLoaded', function() {
const contactForm = document.getElementById('contact-form');
const nameInput = document.getElementById('name');
const emailInput = document.getElementById('email');
const messageInput = document.getElementById('message');
const submitButton = document.getElementById('submit-button');
contactForm.addEventListener('submit', function(event) {
event.preventDefault();
const name = nameInput.value.trim();
const email = emailInput.value.trim();
const message = messageInput.value.trim();
if (validateForm(name, email, message)) {
// Simulate form submission
console.log('Form submitted:', { name, email, message });
alert('Thank you for your message! We will get back to you soon.');
contactForm.reset();
}
});
function validateForm(name, email, message) {
if (!name || !email || !message) {
alert('Please fill in all fields.');
return false;
}
if (!validateEmail(email)) {
alert('Please enter a valid email address.');
return false;
}
return true;
}
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(String(email).toLowerCase());
}
});

View File

@@ -0,0 +1,86 @@
/**
* Enhanced Styling Controller for Balancer.fi-inspired Theme
*/
(function() {
// Run on page load and after small delay to ensure all elements are available
document.addEventListener('DOMContentLoaded', function() {
enhanceCosmicStyles();
// Apply once more after a slight delay to handle dynamically loaded elements
setTimeout(enhanceCosmicStyles, 500);
});
function enhanceCosmicStyles() {
// Add subtle animations to cards
const cards = document.querySelectorAll('.service-card, .step-card, .contact-method, .gallery-item');
cards.forEach(function(card) {
card.style.transition = 'all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1)';
// Add hover effects if not already present
card.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-10px)';
this.style.boxShadow = '0 15px 30px rgba(0, 0, 0, 0.4), 0 0 15px rgba(0, 194, 255, 0.5)';
});
card.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
this.style.boxShadow = '0 5px 15px rgba(0, 0, 0, 0.2)';
});
});
// Enhance form inputs for cosmic theme
const formElements = document.querySelectorAll('input:not(#chatInput), textarea, select');
formElements.forEach(function(element) {
element.style.backgroundColor = 'rgba(0, 0, 0, 0.2)';
element.style.color = 'var(--text-light)';
element.style.border = '1px solid rgba(255, 255, 255, 0.1)';
element.style.borderRadius = '6px';
element.style.padding = '1rem';
// Add focus effects
element.addEventListener('focus', function() {
this.style.borderColor = 'var(--accent-color)';
this.style.boxShadow = '0 0 0 2px rgba(0, 194, 255, 0.2)';
});
element.addEventListener('blur', function() {
this.style.borderColor = 'rgba(255, 255, 255, 0.1)';
this.style.boxShadow = 'none';
});
});
// Enhance buttons
const buttons = document.querySelectorAll('button[type="submit"], .btn-primary, .cta-button');
buttons.forEach(function(button) {
// Only apply if not already styled
if (!button.hasAttribute('data-cosmic-styled')) {
button.setAttribute('data-cosmic-styled', 'true');
// Apply gradient background if not already set
if (!button.style.background.includes('gradient')) {
button.style.background = 'linear-gradient(90deg, #5846f9, #00c2ff)';
}
button.style.color = 'white';
button.style.border = 'none';
button.style.borderRadius = '6px';
button.style.fontWeight = '600';
button.style.padding = '0.8rem 1.5rem';
button.style.cursor = 'pointer';
button.style.transition = 'all 0.3s ease';
button.style.boxShadow = '0 0 15px rgba(0, 194, 255, 0.5)';
// Add hover effects
button.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-2px)';
this.style.boxShadow = '0 0 25px rgba(0, 194, 255, 0.7)';
});
button.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
this.style.boxShadow = '0 0 15px rgba(0, 194, 255, 0.5)';
});
}
});
}
})();

View File

@@ -0,0 +1,154 @@
/**
* Drone Animation Controller
* Creates floating drone elements with cosmic glow effects
*/
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Only run on the homepage
if (window.location.pathname.endsWith('index.html') || window.location.pathname.endsWith('/')) {
createDroneElements();
}
});
function createDroneElements() {
const heroSection = document.querySelector('.hero');
const servicesSection = document.getElementById('services');
if (heroSection) {
// Create a container for the animated elements
const animationContainer = document.createElement('div');
animationContainer.className = 'animation-container';
animationContainer.style.position = 'absolute';
animationContainer.style.top = '0';
animationContainer.style.left = '0';
animationContainer.style.width = '100%';
animationContainer.style.height = '100%';
animationContainer.style.overflow = 'hidden';
animationContainer.style.pointerEvents = 'none';
animationContainer.style.zIndex = '1';
heroSection.appendChild(animationContainer);
// Create floating drone elements
createFloatingDrone(animationContainer, 'right', '15%', '20%');
createFloatingDrone(animationContainer, 'left', '70%', '60%');
// Create floating particles
for (let i = 0; i < 15; i++) {
createGlowingParticle(animationContainer);
}
}
if (servicesSection) {
// Create subtle background animations for services section
const servicesBg = document.createElement('div');
servicesBg.className = 'services-bg';
servicesBg.style.position = 'absolute';
servicesBg.style.top = '0';
servicesBg.style.left = '0';
servicesBg.style.width = '100%';
servicesBg.style.height = '100%';
servicesBg.style.overflow = 'hidden';
servicesBg.style.pointerEvents = 'none';
servicesBg.style.zIndex = '0';
servicesSection.style.position = 'relative';
servicesSection.insertBefore(servicesBg, servicesSection.firstChild);
// Create subtle floating particles
for (let i = 0; i < 10; i++) {
createGlowingParticle(servicesBg, true);
}
}
}
function createFloatingDrone(container, direction, startX, startY) {
const drone = document.createElement('div');
drone.className = 'floating-drone';
drone.style.position = 'absolute';
drone.style.width = '80px';
drone.style.height = '80px';
drone.style.borderRadius = '50%';
drone.style.background = 'radial-gradient(circle at center, rgba(88, 70, 249, 0.7), rgba(0, 194, 255, 0.4))';
drone.style.boxShadow = '0 0 30px rgba(0, 194, 255, 0.6)';
drone.style.left = startX;
drone.style.top = startY;
drone.style.filter = 'blur(5px)';
// Create propeller effects
const propeller = document.createElement('div');
propeller.className = 'propeller';
propeller.style.position = 'absolute';
propeller.style.width = '100px';
propeller.style.height = '100px';
propeller.style.left = '-10px';
propeller.style.top = '-10px';
propeller.style.borderRadius = '50%';
propeller.style.border = '2px solid rgba(255, 255, 255, 0.2)';
propeller.style.animation = 'spin 2s linear infinite';
drone.appendChild(propeller);
// Add animation properties
drone.style.animation = `float-${direction} 15s ease-in-out infinite`;
container.appendChild(drone);
// Add keyframes for floating animation
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerHTML = `
@keyframes float-${direction} {
0% { transform: translate(0, 0) rotate(0deg); }
25% { transform: translate(${direction === 'left' ? '-' : ''}80px, 40px) rotate(${direction === 'left' ? '-' : ''}5deg); }
50% { transform: translate(${direction === 'left' ? '-' : ''}60px, -30px) rotate(${direction === 'left' ? '-' : ''}10deg); }
75% { transform: translate(${direction === 'left' ? '-' : ''}100px, 50px) rotate(${direction === 'left' ? '-' : ''}3deg); }
100% { transform: translate(0, 0) rotate(0deg); }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
`;
document.head.appendChild(styleSheet);
}
function createGlowingParticle(container, subtle = false) {
const particle = document.createElement('div');
particle.className = 'glowing-particle';
// Set particle style
const size = subtle ? Math.random() * 5 + 2 : Math.random() * 10 + 5;
const opacity = subtle ? Math.random() * 0.3 + 0.1 : Math.random() * 0.5 + 0.2;
particle.style.position = 'absolute';
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.borderRadius = '50%';
particle.style.background = 'radial-gradient(circle at center, rgba(0, 194, 255, 0.8), rgba(88, 70, 249, 0.4))';
particle.style.boxShadow = `0 0 ${size * 2}px rgba(0, 194, 255, ${opacity})`;
// Random position
particle.style.left = `${Math.random() * 100}%`;
particle.style.top = `${Math.random() * 100}%`;
// Add animation with random duration
const duration = Math.random() * 20 + 10;
particle.style.animation = `float-particle ${duration}s ease-in-out infinite`;
container.appendChild(particle);
// Add keyframes for particle animation if not already added
if (!document.querySelector('style[data-animation="particle-float"]')) {
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.setAttribute('data-animation', 'particle-float');
styleSheet.innerHTML = `
@keyframes float-particle {
0% { transform: translate(0, 0); }
25% { transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px); }
50% { transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px); }
75% { transform: translate(${Math.random() * 100 - 50}px, ${Math.random() * 100 - 50}px); }
100% { transform: translate(0, 0); }
}
`;
document.head.appendChild(styleSheet);
}
}
})();

View File

@@ -0,0 +1,486 @@
/**
* Drone Image Animation with New Background
* Uses a single image background with the drone flying over the roof
*/
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Only run on pages that have a hero section
const heroSection = document.querySelector('.hero');
if (heroSection) {
// Remove any existing backgrounds or animations
if (heroSection.style.backgroundImage) {
heroSection.style.backgroundImage = 'none';
}
// Remove any existing animation containers
const existingAnimations = heroSection.querySelectorAll('.drone-animation-container, .animation-container, .roof-cleaning-animation');
existingAnimations.forEach(el => el.remove());
// Create our animation with the new background
createAnimationWithSingleBackground(heroSection);
}
});
function createAnimationWithSingleBackground(container) {
// Create animation container with more aggressive full-width styling
const animationContainer = document.createElement('div');
animationContainer.className = 'drone-animation-container';
// Position absolutely with negative margins to escape any parent padding
animationContainer.style.position = 'absolute';
animationContainer.style.top = '0';
animationContainer.style.left = '0';
animationContainer.style.right = '0'; // Add right: 0 to ensure full width
animationContainer.style.width = '100vw'; // Use viewport width
animationContainer.style.maxWidth = '100vw'; // Override any max-width restrictions
animationContainer.style.marginLeft = 'calc(50% - 50vw)'; // Center regardless of parent padding
animationContainer.style.marginRight = 'calc(50% - 50vw)'; // Center regardless of parent padding
animationContainer.style.height = '100%';
animationContainer.style.overflow = 'hidden';
animationContainer.style.pointerEvents = 'none';
animationContainer.style.zIndex = '1';
// Add CSS to fix any scrollbar issues
const styleFixForScrollbar = document.createElement('style');
styleFixForScrollbar.textContent = `
html, body {
overflow-x: hidden;
width: 100%;
position: relative;
}
.hero {
width: 100%;
max-width: 100%;
overflow: hidden;
padding-left: 0 !important;
padding-right: 0 !important;
}
`;
document.head.appendChild(styleFixForScrollbar);
// Fix mobile white space by adjusting the parent container on mobile
if (window.innerWidth <= 768) {
// Remove potential white space in parent container
container.style.paddingBottom = '0';
container.style.marginBottom = '0';
}
// Create background using the new image
createFullBackground(animationContainer);
// Create drone elements that will fly over the roof
createFlyingDrones(animationContainer);
// Add everything to the container
container.appendChild(animationContainer);
// Add animation styles
addAnimationStyles();
// Add resize listener to adjust for screen size changes
window.addEventListener('resize', function() {
adjustForScreenSize(animationContainer);
});
// Initial adjustment for screen size
adjustForScreenSize(animationContainer);
// Position the hero content (text) in the sky area
positionHeroContent(container);
}
function createFullBackground(container) {
// Create the full background with enhanced full-width styling
const backgroundElement = document.createElement('div');
backgroundElement.className = 'animation-background';
backgroundElement.style.position = 'absolute';
backgroundElement.style.top = '0';
backgroundElement.style.left = '0';
backgroundElement.style.right = '0'; // Add right: 0
backgroundElement.style.width = '100vw'; // Use viewport width
backgroundElement.style.maxWidth = 'none'; // No max width restrictions
backgroundElement.style.height = '100%';
backgroundElement.style.backgroundImage = 'url("images/animation_background1.png")';
backgroundElement.style.backgroundSize = 'cover';
backgroundElement.style.backgroundPosition = 'center center';
backgroundElement.style.backgroundRepeat = 'no-repeat';
backgroundElement.style.backgroundColor = '#75b0cc';
backgroundElement.style.zIndex = '1';
// Add subtle overlay to improve text visibility
const overlay = document.createElement('div');
overlay.style.position = 'absolute';
overlay.style.top = '0';
overlay.style.left = '0';
overlay.style.width = '100%';
overlay.style.height = '100%';
overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.1)'; /* Subtle darkening */
overlay.style.zIndex = '2';
backgroundElement.appendChild(overlay);
container.appendChild(backgroundElement);
// Add resize event listener for responsive adjustments
window.addEventListener('resize', function() {
adjustBackground(backgroundElement);
});
// Initial adjustment
adjustBackground(backgroundElement);
}
function adjustBackground(backgroundElement) {
const isMobile = window.innerWidth <= 768;
const isPortrait = window.innerHeight > window.innerWidth;
if (isMobile) {
if (isPortrait) {
// Portrait mobile - fix grey area by using cover and adjusting transform
backgroundElement.style.backgroundSize = 'cover';
backgroundElement.style.transform = 'translateY(-15%)'; // Move image up instead of using background-position
backgroundElement.style.height = '100%'; // Ensure full height
} else {
// Landscape mobile - also fix using transform
backgroundElement.style.backgroundSize = 'cover';
backgroundElement.style.transform = 'translateY(-10%)';
backgroundElement.style.height = '100%';
}
} else {
// Desktop - standard positioning
backgroundElement.style.backgroundSize = 'cover';
backgroundElement.style.transform = 'none';
backgroundElement.style.backgroundPosition = 'center center';
}
}
function positionHeroContent(container) {
// Get the hero content (text)
const heroContent = container.querySelector('.hero-content');
if (heroContent) {
// Split the content: Title in sky, rest at top of roof
const headline = heroContent.querySelector('h2');
const otherContent = document.createElement('div');
otherContent.className = 'hero-secondary-content';
// Get all other elements except the headline
const subheading = heroContent.querySelector('p');
const ctaButton = heroContent.querySelector('.btn-primary');
// Set styles for the main container
heroContent.style.position = 'relative';
heroContent.style.zIndex = '5';
heroContent.style.display = 'flex';
heroContent.style.flexDirection = 'column';
heroContent.style.alignItems = 'center';
heroContent.style.justifyContent = 'space-between';
heroContent.style.height = '100%';
heroContent.style.textShadow = '0 2px 4px rgba(0, 0, 0, 0.3)';
// Style headline to be in sky - make consistent across all views
if (headline) {
headline.style.color = '#ffffff';
headline.style.margin = '0 auto 2rem';
headline.style.maxWidth = '100%'; // Changed from 90% to 100% for better text wrapping
headline.style.textAlign = 'center';
headline.style.position = 'relative';
headline.style.zIndex = '6';
headline.style.fontSize = window.innerWidth <= 480 ? '1.5rem' : (window.innerWidth <= 768 ? '1.8rem' : '3rem'); // Reduced from 1.7rem to 1.5rem for smallest screens
headline.style.lineHeight = '1.3'; // Add line height for better text spacing
headline.style.width = '100%'; // Ensure full width
headline.style.wordWrap = 'break-word'; // Improve word wrapping
headline.style.hyphens = 'none'; // Changed from 'auto' to 'none'
headline.style.wordBreak = 'keep-all'; // Prevent words from breaking
headline.style.whiteSpace = 'normal'; // Allow normal wrapping but prevent word splitting
// More mobile-friendly initial positioning
headline.style.marginTop = window.innerWidth <= 768 ? '-3rem' : '-8rem';
}
// Move other content to a separate container
if (subheading && ctaButton) {
// Remove elements from their current position
subheading.remove();
ctaButton.remove();
// Add them to the new container
otherContent.appendChild(subheading);
otherContent.appendChild(ctaButton);
// Style the subheading
subheading.style.color = '#ffffff';
subheading.style.marginBottom = '1.5rem';
// Style the container for other content
otherContent.style.display = 'flex';
otherContent.style.flexDirection = 'column';
otherContent.style.alignItems = 'center';
otherContent.style.position = 'relative';
otherContent.style.zIndex = '6';
// Add the container to the hero content
heroContent.appendChild(otherContent);
}
// Adjust positions for different screen sizes
adjustHeroContentPositioning(heroContent, headline, otherContent);
window.addEventListener('resize', function() {
adjustHeroContentPositioning(heroContent, headline, otherContent);
});
}
}
function adjustHeroContentPositioning(heroContent, headline, otherContent) {
const isMobile = window.innerWidth <= 768;
const isPortrait = window.innerHeight > window.innerWidth;
const isSmallPhone = window.innerWidth <= 480;
// Apply different positioning based on device type and size
if (headline) {
if (isMobile) {
if (isPortrait) {
// Portrait mobile - adjust based on device height
const viewportHeight = window.innerHeight;
if (viewportHeight <= 600) {
// Very small device
headline.style.marginTop = '-1rem';
} else if (viewportHeight <= 800) {
// Medium device
headline.style.marginTop = '-2rem';
} else {
// Larger device
headline.style.marginTop = '-3rem';
}
} else {
// Landscape mobile
headline.style.marginTop = '-2rem';
}
} else {
// Desktop - keep as is
headline.style.marginTop = '-8rem';
}
}
if (otherContent) {
otherContent.style.marginTop = 'auto';
if (isMobile) {
if (isPortrait) {
// Portrait mobile - adjust based on device height
const viewportHeight = window.innerHeight;
if (viewportHeight <= 600) {
// More compact spacing for small devices
otherContent.style.marginBottom = '3rem';
} else if (viewportHeight <= 800) {
otherContent.style.marginBottom = '4rem';
} else {
otherContent.style.marginBottom = '5rem';
}
// Adjust spacing between elements for small devices
if (isSmallPhone) {
const subheading = otherContent.querySelector('p');
if (subheading) {
subheading.style.marginBottom = '1rem';
}
}
} else {
// Landscape mobile
otherContent.style.marginBottom = '3rem';
}
} else {
// Desktop
otherContent.style.marginBottom = '5rem';
}
}
}
function adjustHeroContent(heroContent) {
// This function is replaced by the more specific positioning function above
// Keep it for compatibility but it no longer needs to do anything
}
function createFlyingDrones(container) {
// Create drone path container
const dronePathContainer = document.createElement('div');
dronePathContainer.className = 'drone-path-container';
dronePathContainer.style.position = 'absolute';
dronePathContainer.style.top = '0';
dronePathContainer.style.left = '0';
dronePathContainer.style.width = '100%';
dronePathContainer.style.height = '100%';
dronePathContainer.style.zIndex = '10';
container.appendChild(dronePathContainer);
// Create drone with responsive sizing
createDrone(dronePathContainer, 'drone-path-1', 0);
}
function createDrone(container, pathClass, startDelay = 0, duration = 30) {
// Create drone element using the specified drone image
const droneElement = document.createElement('div');
droneElement.className = `animated-drone ${pathClass}`;
droneElement.style.position = 'absolute';
droneElement.style.width = '440px'; // Will be adjusted for mobile
droneElement.style.height = '300px'; // Will be adjusted for mobile
droneElement.style.top = '45%'; // Position for new background
droneElement.style.left = '-450px';
droneElement.style.zIndex = '15'; // Above all other elements
droneElement.style.animation = `${pathClass} ${duration}s linear infinite`;
droneElement.style.animationDelay = `${startDelay}s`;
// Add the drone image
const droneImage = document.createElement('img');
droneImage.src = 'images/spraying_drone4.png';
droneImage.alt = 'Dachreinigungsdrohne';
droneImage.style.width = '100%';
droneImage.style.height = 'auto';
droneImage.style.filter = 'drop-shadow(0 15px 30px rgba(0,0,0,0.25))';
droneElement.appendChild(droneImage);
container.appendChild(droneElement);
}
function adjustForScreenSize(container) {
const isMobile = window.innerWidth <= 768;
const isPortrait = window.innerHeight > window.innerWidth;
// Fix white space issue on mobile
if (isMobile) {
// Adjust parent container (hero section)
const heroSection = container.closest('.hero');
if (heroSection) {
heroSection.style.paddingBottom = '0';
heroSection.style.marginBottom = '0';
// Ensure full height to bottom of viewport on mobile
container.style.minHeight = '100%';
}
// Adjust the main background element to extend fully
const backgroundEl = container.querySelector('.animation-background');
if (backgroundEl) {
backgroundEl.style.bottom = '0';
backgroundEl.style.height = '100%';
// Add extra height to cover any potential gaps
backgroundEl.style.marginBottom = '-2px';
}
}
const drones = container.querySelectorAll('.animated-drone');
// Adjust drone sizes
drones.forEach(drone => {
if (isMobile) {
if (isPortrait) {
// Portrait mobile - smaller drone
drone.style.width = '150px';
drone.style.height = '100px';
} else {
// Landscape mobile
drone.style.width = '180px';
drone.style.height = '120px';
}
} else {
// Desktop size
drone.style.width = '440px';
drone.style.height = '300px';
}
});
// Adjust background
const background = container.querySelector('.animation-background');
if (background) {
adjustBackground(background);
}
// Add headline adjustment on resize
const headline = document.querySelector('.hero-content h2');
if (headline) {
// Adjust font size based on screen width for better text wrapping
if (window.innerWidth <= 480) {
headline.style.fontSize = '1.5rem';
headline.style.lineHeight = '1.3';
headline.style.maxWidth = '100%';
headline.style.padding = '0 0.5rem'; // Add padding for smallest screens
headline.style.hyphens = 'none';
headline.style.wordBreak = 'keep-all';
headline.style.whiteSpace = 'normal';
} else if (window.innerWidth <= 768) {
headline.style.fontSize = '1.8rem';
headline.style.lineHeight = '1.3';
headline.style.maxWidth = '100%';
headline.style.hyphens = 'none';
headline.style.wordBreak = 'keep-all';
headline.style.whiteSpace = 'normal';
} else {
headline.style.fontSize = '3rem';
headline.style.lineHeight = '1.2';
headline.style.maxWidth = '90%';
headline.style.hyphens = 'none';
headline.style.wordBreak = 'keep-all';
headline.style.whiteSpace = 'normal';
}
}
}
// Add a resize event listener to handle orientation changes
window.addEventListener('resize', function() {
const animationContainer = document.querySelector('.drone-animation-container');
if (animationContainer) {
adjustForScreenSize(animationContainer);
}
});
function addAnimationStyles() {
// Create style element if it doesn't exist
if (!document.getElementById('drone-animation-styles')) {
const styleElement = document.createElement('style');
styleElement.id = 'drone-animation-styles';
// Add responsive animations with adjusted drone path to match new background
styleElement.innerHTML = `
@keyframes drone-path-1 {
/* First half - path adjusted for new background */
0% { left: -450px; top: 45%; transform: rotate(0deg); }
5% { left: 10%; top: 43%; transform: rotate(-5deg); }
10% { left: 20%; top: 45%; transform: rotate(0deg); }
15% { left: 30%; top: 47%; transform: rotate(5deg); }
20% { left: 40%; top: 45%; transform: rotate(0deg); }
25% { left: 50%; top: 48%; transform: rotate(-3deg); }
30% { left: 60%; top: 50%; transform: rotate(0deg); }
35% { left: 70%; top: 52%; transform: rotate(3deg); }
40% { left: 80%; top: 50%; transform: rotate(0deg); }
45% { left: 90%; top: 48%; transform: rotate(-5deg); }
50% { left: 100%; top: 45%; transform: rotate(-10deg); }
/* Second half - return path */
55% { left: 90%; top: 48%; transform: rotate(-5deg) scaleX(-1); }
60% { left: 80%; top: 50%; transform: rotate(0deg) scaleX(-1); }
65% { left: 70%; top: 52%; transform: rotate(3deg) scaleX(-1); }
70% { left: 60%; top: 50%; transform: rotate(0deg) scaleX(-1); }
75% { left: 50%; top: 48%; transform: rotate(-3deg) scaleX(-1); }
80% { left: 40%; top: 45%; transform: rotate(0deg) scaleX(-1); }
85% { left: 30%; top: 47%; transform: rotate(5deg) scaleX(-1); }
90% { left: 20%; top: 45%; transform: rotate(0deg) scaleX(-1); }
95% { left: 10%; top: 43%; transform: rotate(-5deg) scaleX(-1); }
100% { left: -450px; top: 45%; transform: rotate(0deg) scaleX(-1); }
}
/* Mobile-specific animations */
@media (max-width: 768px) {
@keyframes drone-path-1 {
/* Adjusted path for mobile - fly higher to match the shifted background */
0% { left: -220px; top: 45%; transform: rotate(0deg); }
20% { left: 30%; top: 43%; transform: rotate(-5deg); }
40% { left: 70%; top: 47%; transform: rotate(5deg); }
50% { left: 100%; top: 45%; transform: rotate(-5deg); }
60% { left: 70%; top: 47%; transform: rotate(5deg) scaleX(-1); }
80% { left: 30%; top: 43%; transform: rotate(-5deg) scaleX(-1); }
100% { left: -220px; top: 45%; transform: rotate(0deg) scaleX(-1); }
}
}
`;
document.head.appendChild(styleElement);
}
}
})();

View File

@@ -0,0 +1,66 @@
/**
* Language Manager
* Simplified to use only German language
*/
const languageManager = {
currentLang: 'de', // Default language set to German
init: function() {
console.log('Initializing Language Manager');
// Always use German as the language
this.currentLang = 'de';
localStorage.setItem('preferredLanguage', 'de');
// Apply translations
this.applyTranslations();
// Update document language attribute
document.documentElement.lang = this.currentLang;
console.log(`Language Manager initialized with language: ${this.currentLang}`);
},
applyTranslations: function() {
if (typeof translations === 'undefined' || !translations['de']) {
console.error('German translations not available');
return;
}
const elements = document.querySelectorAll('[data-i18n]');
elements.forEach(element => {
const key = element.getAttribute('data-i18n');
const translation = translations['de'][key];
if (translation) {
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
if (element.hasAttribute('placeholder')) {
element.setAttribute('placeholder', translation);
} else {
element.value = translation;
}
} else {
element.textContent = translation;
}
} else {
console.warn(`Missing translation for key: ${key}`);
}
});
const altElements = document.querySelectorAll('[data-i18n-alt]');
altElements.forEach(element => {
const key = element.getAttribute('data-i18n-alt');
const translation = translations['de'][key];
if (translation) {
element.setAttribute('alt', translation);
}
});
console.log(`Applied German translations`);
}
};
// Initialize the language manager when the DOM is ready
document.addEventListener('DOMContentLoaded', () => {
languageManager.init();
});

19
html/drone/js/main.js Normal file
View File

@@ -0,0 +1,19 @@
// This file contains the main JavaScript code for the website, handling general interactivity and functionality.
document.addEventListener('DOMContentLoaded', () => {
// Initialize any necessary components or features
console.log('Roof Drone Cleaning Website Loaded');
// Example: Smooth scroll for anchor links
const scrollLinks = document.querySelectorAll('a[href^="#"]');
scrollLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetElement = document.querySelector(targetId);
targetElement.scrollIntoView({ behavior: 'smooth' });
});
});
// Additional functionality can be added here
});

View File

@@ -0,0 +1,45 @@
/**
* Mobile navigation functionality
* This script handles the mobile hamburger menu behavior
*/
document.addEventListener('DOMContentLoaded', function() {
// Mobile navigation toggle
const mobileToggle = document.getElementById('mobileToggle');
const mainNav = document.getElementById('mainNav');
if(mobileToggle && mainNav) {
mobileToggle.addEventListener('click', function() {
mainNav.classList.toggle('active');
});
}
// Close mobile menu when a link is clicked
const mobileNavLinks = document.querySelectorAll('.main-nav a');
mobileNavLinks.forEach(link => {
link.addEventListener('click', function() {
if (window.innerWidth <= 768) {
mainNav.classList.remove('active');
}
});
});
// Close menu when clicking outside
document.addEventListener('click', function(event) {
if (window.innerWidth <= 768 &&
!event.target.closest('.main-nav') &&
!event.target.closest('.mobile-toggle') &&
mainNav.classList.contains('active')) {
mainNav.classList.remove('active');
}
});
// Ensure copyright year is 2025 on mobile
const footerText = document.querySelector('footer p[data-i18n="footer_text"]');
if (footerText) {
// Force update the copyright year to 2025 for mobile and desktop
const currentText = footerText.innerHTML;
const updatedText = currentText.replace(/© \d{4}/, '© 2025');
footerText.innerHTML = updatedText;
}
});

View File

@@ -0,0 +1,28 @@
document.addEventListener('DOMContentLoaded', function() {
// Get current page path
const currentPath = window.location.pathname;
const isInPagesDirectory = currentPath.includes('/pages/');
// Handle paths based on directory
const navLinks = document.querySelectorAll('#mainNav a');
navLinks.forEach(link => {
// Get href attribute
let href = link.getAttribute('href');
// Fix paths if in pages directory
if (isInPagesDirectory && href.startsWith('index.html')) {
link.setAttribute('href', '../' + href);
} else if (isInPagesDirectory && href.startsWith('#')) {
link.setAttribute('href', '../index.html' + href);
} else if (!isInPagesDirectory && href.startsWith('pages/')) {
// No change needed for root directory links to pages
}
// Set active class based on current page
if ((currentPath.endsWith('/index.html') || currentPath.endsWith('/')) && href === 'index.html') {
link.classList.add('active');
} else if (currentPath.includes(href) && href !== 'index.html') {
link.classList.add('active');
}
});
});

View File

@@ -0,0 +1,268 @@
/**
* Roof Cleaning Animation
* Shows a drone flying over a dirty roof and cleaning it in a continuous loop
*/
(function() {
document.addEventListener('DOMContentLoaded', function() {
const heroSection = document.querySelector('.hero');
if (heroSection) {
initRoofCleaningAnimation(heroSection);
}
});
function initRoofCleaningAnimation(container) {
// Create canvas container
const canvasContainer = document.createElement('div');
canvasContainer.className = 'roof-cleaning-animation';
canvasContainer.style.position = 'absolute';
canvasContainer.style.top = '0';
canvasContainer.style.left = '0';
canvasContainer.style.width = '100%';
canvasContainer.style.height = '100%';
canvasContainer.style.zIndex = '1';
canvasContainer.style.pointerEvents = 'none';
canvasContainer.style.overflow = 'hidden';
// Create canvas element
const canvas = document.createElement('canvas');
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
canvasContainer.appendChild(canvas);
// Add to container
container.appendChild(canvasContainer);
// Set up the animation
setupAnimation(canvas);
// Handle window resize
window.addEventListener('resize', function() {
canvas.width = container.offsetWidth;
canvas.height = container.offsetHeight;
setupAnimation(canvas);
});
}
function setupAnimation(canvas) {
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// Animation variables
let droneX = -100; // Start off-screen
const droneY = height * 0.4; // Position drone at 40% of height
const droneWidth = 80;
const droneHeight = 40;
const droneSpeed = 2;
const cleaningWidth = 80; // Width of cleaning path
// Create offscreen canvases for better performance
const roofCanvas = createRoofCanvas(width, height);
const cleanCanvas = createCleanCanvas(width, height);
const maskCanvas = document.createElement('canvas');
maskCanvas.width = width;
maskCanvas.height = height;
const maskCtx = maskCanvas.getContext('2d');
// Create drone image
const drone = new Image();
drone.src = 'images/spraying_drone1.png';
// Droplets array for water spray
const droplets = [];
// Animation loop
function animate() {
// Clear the canvas
ctx.clearRect(0, 0, width, height);
// Draw sky background
drawSky(ctx, width, height);
// Update drone position
droneX += droneSpeed;
if (droneX > width + 100) {
// Reset drone position and partially reset mask for continuous loop
droneX = -100;
// Reset left side of mask
maskCtx.clearRect(0, height * 0.45, width * 0.2, height * 0.55);
}
// Update cleaning mask
updateCleaningMask(maskCtx, droneX, droneY, cleaningWidth);
// Draw dirty roof
ctx.drawImage(roofCanvas, 0, 0);
// Apply cleaning mask to show clean areas
ctx.globalCompositeOperation = 'destination-out';
ctx.drawImage(maskCanvas, 0, 0);
ctx.globalCompositeOperation = 'source-over';
// Draw clean roof in masked areas
ctx.drawImage(cleanCanvas, 0, 0);
// Generate water droplets
if (Math.random() < 0.3) { // 30% chance each frame
createDroplet(droplets, droneX + droneWidth/2, droneY + droneHeight/2);
}
// Update and draw water droplets
updateDroplets(ctx, droplets);
// Draw the drone
ctx.drawImage(drone, droneX, droneY, droneWidth, droneHeight);
// Continue animation loop
requestAnimationFrame(animate);
}
// Wait for drone image to load before starting animation
drone.onload = function() {
animate();
};
}
function drawSky(ctx, width, height) {
// Create gradient for sky
const skyGradient = ctx.createLinearGradient(0, 0, 0, height * 0.45);
skyGradient.addColorStop(0, '#87CEEB'); // Sky blue
skyGradient.addColorStop(1, '#e6f7ff'); // Light blue
ctx.fillStyle = skyGradient;
ctx.fillRect(0, 0, width, height * 0.45);
}
function createRoofCanvas(width, height) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Draw dirty roof
const roofY = height * 0.45; // Start roof at 45% of height
const roofHeight = height * 0.55; // Roof takes up 55% of height
// Base roof color (dirty brownish)
ctx.fillStyle = '#8B4513';
ctx.fillRect(0, roofY, width, roofHeight);
// Add roof tiles texture
ctx.fillStyle = '#6B3501';
for (let y = roofY; y < height; y += 20) {
for (let x = 0; x < width; x += 60) {
const offset = (Math.floor(y / 20) % 2) * 30;
ctx.fillRect(x + offset, y, 30, 10);
}
}
// Add dirt and moss spots
for (let i = 0; i < 200; i++) {
const spotX = Math.random() * width;
const spotY = roofY + Math.random() * roofHeight;
const spotSize = 3 + Math.random() * 15;
ctx.fillStyle = Math.random() > 0.5 ?
'rgba(50, 30, 0, 0.4)' : // Dirt
'rgba(30, 70, 30, 0.3)'; // Moss
ctx.beginPath();
ctx.arc(spotX, spotY, spotSize, 0, Math.PI * 2);
ctx.fill();
}
return canvas;
}
function createCleanCanvas(width, height) {
const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
// Draw clean roof
const roofY = height * 0.45;
const roofHeight = height * 0.55;
// Base clean roof color (reddish-brown)
ctx.fillStyle = '#A0522D';
ctx.fillRect(0, roofY, width, roofHeight);
// Add clean roof tiles texture
ctx.fillStyle = '#8B4513';
for (let y = roofY; y < height; y += 20) {
for (let x = 0; x < width; x += 60) {
const offset = (Math.floor(y / 20) % 2) * 30;
ctx.fillRect(x + offset, y, 30, 10);
}
}
return canvas;
}
function updateCleaningMask(ctx, droneX, droneY, cleaningWidth) {
// Create radial gradient for cleaning effect
const gradient = ctx.createRadialGradient(
droneX + 40, droneY + 30, 5,
droneX + 40, droneY + 30, cleaningWidth
);
gradient.addColorStop(0, 'rgba(0, 0, 0, 1)');
gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(droneX + 40, droneY + 30, cleaningWidth, 0, Math.PI * 2);
ctx.fill();
}
function createDroplet(droplets, x, y) {
droplets.push({
x: x,
y: y,
size: 2 + Math.random() * 3,
speedX: -1 + Math.random() * 2,
speedY: 4 + Math.random() * 2,
opacity: 0.7 + Math.random() * 0.3
});
// Limit number of droplets for performance
if (droplets.length > 50) {
droplets.shift();
}
}
function updateDroplets(ctx, droplets) {
// Draw water droplets
ctx.fillStyle = '#82CAFF';
for (let i = 0; i < droplets.length; i++) {
const droplet = droplets[i];
// Update position
droplet.x += droplet.speedX;
droplet.y += droplet.speedY;
// Reduce opacity as it falls
droplet.opacity -= 0.02;
if (droplet.opacity <= 0) {
droplets.splice(i, 1);
i--;
continue;
}
// Draw droplet
ctx.globalAlpha = droplet.opacity;
ctx.beginPath();
ctx.arc(droplet.x, droplet.y, droplet.size, 0, Math.PI * 2);
ctx.fill();
}
ctx.globalAlpha = 1;
}
})();

View File

@@ -0,0 +1,94 @@
/**
* Dynamic Star Background Generator
* Creates an animated cosmic background with stars and nebulae
*/
(function() {
document.addEventListener('DOMContentLoaded', function() {
// Create canvas for star background if it doesn't exist
if (!document.getElementById('stars-canvas')) {
createStarCanvas();
}
});
function createStarCanvas() {
const canvas = document.createElement('canvas');
canvas.id = 'stars-canvas';
canvas.style.position = 'fixed';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.style.zIndex = '-1';
canvas.style.pointerEvents = 'none';
document.body.prepend(canvas);
// Make the canvas responsive
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// Redraw on window resize
window.addEventListener('resize', function() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
drawStarBackground(canvas);
});
// Draw initial star background
drawStarBackground(canvas);
}
function drawStarBackground(canvas) {
const ctx = canvas.getContext('2d');
const width = canvas.width;
const height = canvas.height;
// Clear canvas
ctx.clearRect(0, 0, width, height);
// Create gradient background
const bgGradient = ctx.createLinearGradient(0, 0, 0, height);
bgGradient.addColorStop(0, '#070b1a');
bgGradient.addColorStop(1, '#0c1426');
ctx.fillStyle = bgGradient;
ctx.fillRect(0, 0, width, height);
// Draw nebulae
drawNebula(ctx, width * 0.2, height * 0.3, width * 0.4, height * 0.4, '#5846f9', 0.05);
drawNebula(ctx, width * 0.7, height * 0.7, width * 0.5, height * 0.5, '#00c2ff', 0.05);
// Draw stars
const starCount = Math.floor((width * height) / 1000); // Adaptive star count
for (let i = 0; i < starCount; i++) {
const x = Math.random() * width;
const y = Math.random() * height;
const size = Math.random() * 2;
const opacity = Math.random() * 0.8 + 0.2;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${opacity})`;
ctx.fill();
// Add glow to some stars
if (Math.random() > 0.95) {
ctx.beginPath();
ctx.arc(x, y, size * 4, 0, Math.PI * 2);
const glowColor = Math.random() > 0.5 ? '0, 194, 255' : '88, 70, 249';
const glow = ctx.createRadialGradient(x, y, size, x, y, size * 4);
glow.addColorStop(0, `rgba(${glowColor}, 0.3)`);
glow.addColorStop(1, `rgba(${glowColor}, 0)`);
ctx.fillStyle = glow;
ctx.fill();
}
}
}
function drawNebula(ctx, x, y, width, height, color, opacity) {
const nebulaGradient = ctx.createRadialGradient(x, y, 10, x, y, Math.max(width, height));
nebulaGradient.addColorStop(0, `${color}${Math.floor(opacity * 255).toString(16).padStart(2, '0')}`);
nebulaGradient.addColorStop(1, 'transparent');
ctx.fillStyle = nebulaGradient;
ctx.fillRect(x - width / 2, y - height / 2, width, height);
}
})();

View File

@@ -0,0 +1,267 @@
const translations = {
'de': {
// Header
'header_title': 'Willkommen bei Luftglanz',
'roof_drone': 'Luftglanz',
// Navigation
'nav_home': 'Startseite',
'nav_services': 'Leistungen',
'nav_how_it_works': 'Ablauf',
'nav_faq': 'FAQ',
'nav_contact': 'Kontakt',
'nav_about': 'Über Uns',
'nav_gallery': 'Galerie',
'nav_products': 'Produkte',
// Hero section
'hero_title': 'Revolutionäre Dachreinigung mit Drohnentechnologie',
'hero_subtitle': 'Schnelle, effiziente und unkomplizierte Reinigungslösungen für Ihr Dach.',
// Services section
'services_title': 'Unsere Leistungen',
'services_description': 'Erleben Sie modernste Drohnentechnologie für schnelle und effektive Dachreinigung und Wartung.',
'service_standard_title': 'Umfassende Dachreinigung',
'service_standard_description': 'Unser fortschrittlicher Reinigungsservice entfernt Schmutz, Moos und Algen.',
'service_inspection_title': 'Detaillierte Dachinspektion',
'service_inspection_description': 'Unsere Drohnen bieten hochauflösende Inspektionen, um potenzielle Probleme frühzeitig zu erkennen.',
'service_solar_title': 'Solaranlagen-Reinigung',
'service_solar_description': 'Maximieren Sie die Effizienz Ihrer Solarmodule mit unserem spezialisierten Reinigungsservice.',
// How it works section
'how_it_works_title': 'So funktioniert es',
'how_it_works_description': 'Unser optimierter Prozess sorgt für eine schnelle, effektive und stressfreie Dachreinigung.',
'how_it_works_step1': 'Inspektion vereinbaren',
'how_it_works_step2': 'Drohnenreinigung in Aktion',
'how_it_works_step3': 'Genießen Sie ein sauberes Dach',
'how_it_works_step1_desc': 'Füllen Sie unser einfaches Formular aus, um ein Angebot zu erhalten und Ihre Inspektion zu planen.',
'how_it_works_step2_desc': 'Unsere Techniker kommen mit speziellen Drohnen und Ausrüstung, um Ihr Dach zu reinigen.',
'how_it_works_step3_desc': 'Bewundern Sie Ihr sauberes Dach und genießen Sie die verlängerte Lebensdauer Ihres Dachmaterials.',
// FAQ section
'faq_title': 'Häufig gestellte Fragen',
'faq_q1': 'Wie lange dauert die Reinigung?',
'faq_a1': 'Die meisten Dächer werden innerhalb von 2-4 Stunden gereinigt, abhängig von Größe und Komplexität.',
'faq_q2': 'Ist die Drohnenreinigung sicher für mein Dach?',
'faq_a2': 'Ja, unsere Drohnen verwenden schonende, nicht-invasive Methoden, die Ihr Dach schützen.',
'faq_description': 'Finden Sie Antworten auf häufig gestellte Fragen zu unseren Drohnen-Dachreinigungsdiensten',
// Contact form
'contact_title': 'Kontaktieren Sie uns',
'contact_name': 'Ihr Name',
'contact_email': 'Ihre E-Mail',
'contact_phone': 'Telefonnummer',
'contact_address': 'Ihre Adresse',
'contact_message': 'Ihre Nachricht',
'contact_reset': 'Formular löschen',
'contact_send': 'Absenden',
'contact_description': 'Kontaktieren Sie uns für ein kostenloses Angebot oder um mehr über unsere Dienstleistungen zu erfahren',
// Footer
'footer_text': '© 2025 Luftglanz. Alle Rechte vorbehalten.',
// Language selection
'language_select': 'DE',
// Gallery page
'gallery_title': 'Unsere Arbeit in Aktion',
'gallery_description': 'Entdecken Sie unsere Galerie und sehen Sie, wie unsere Drohnen außergewöhnliche Reinigungsergebnisse liefern.',
'gallery_prev': 'Zurück',
'gallery_next': 'Weiter',
'gallery_image1_alt': 'Drohne reinigt ein Wohnhausdach',
'gallery_image2_alt': 'Drohne führt Dachinspektion durch',
'gallery_image3_alt': 'Drohne reinigt Solarmodule',
'gallery_image4_alt': 'Nahaufnahme des Drohnenreinigungsprozesses',
// Chatbot
'chatbot_title': 'Dachdrohnen-Assistent',
'chatbot_greeting': 'Hallo! Wie kann ich Ihnen heute bei Ihren Dachreinigungsbedürfnissen helfen?',
'chatbot_placeholder': 'Geben Sie hier Ihre Frage ein...',
'chatbot_offline_notice': 'Derzeit offline. Bitte nutzen Sie unser Kontaktformular für Unterstützung.',
// Company name variations
'roof_drone': 'Luftglanz',
'roof_drone_cleaning': 'Luftglanz',
// Additional translations for missing elements
'get_quote': 'Kostenloses Angebot',
// Services page
'services_hero_title': 'Professionelle Drohnen-Dachreinigung',
'services_hero_subtitle': 'Unsere spezialisierten Drohnen sorgen für schnelle und gründliche Reinigung mit fortschrittlicher Technologie',
'service_drone_title': 'Drohnen-Dachreinigung',
'service_drone_description': 'Unsere Drohnen sind mit speziellen Sprühsystemen ausgestattet, die effektiv Schmutz, Moos und Algen entfernen. Dieser kontaktlose Ansatz beseitigt das Risiko von Ziegelschäden und Arbeiterunfällen, die mit herkömmlichen Dachreinigungsmethoden verbunden sind.',
'service_inspection_detailed_title': 'Inspektionsdienstleistungen',
'service_inspection_detailed_description': 'Zusätzlich zur Reinigung können unsere Drohnen detaillierte Inspektionen Ihres Daches durchführen und potenzielle Probleme identifizieren, bevor sie zu kostspieligen Reparaturen werden. Unsere hochauflösenden Kameras erfassen jedes Detail und ermöglichen es uns, fehlende Schindeln, beschädigte Verkleidungen oder andere Probleme zu erkennen, die mit bloßem Auge übersehen werden könnten.',
'service_maintenance_title': 'Wartungspakete',
'service_maintenance_description': 'Wir bieten verschiedene Wartungspakete an, um Ihr Dach das ganze Jahr über in bestem Zustand zu halten und so Langlebigkeit und Ästhetik zu gewährleisten. Regelmäßige Wartung kann die Lebensdauer Ihres Daches um bis zu 25% verlängern und kostspielige Reparaturen verhindern. Unsere Pakete können an Ihre spezifischen Dachanforderungen und Ihr Budget angepasst werden.',
'premium_services_title': 'Premium-Zusatzleistungen',
'premium_services_description': 'Neben unseren Kerndienstleistungen bieten wir spezialisierte Behandlungen an, darunter Abdichtungsanwendungen, UV-Schutzbeschichtungen und Wärmescans zur Identifizierung potenzieller Wärmelecks. Diese zusätzlichen Dienstleistungen tragen dazu bei, die Leistung und Energieeffizienz Ihres Daches zu maximieren und seine Gesamtlebensdauer zu verlängern.',
'environmental_approach_title': 'Unser Effizienzkonzept',
'environmental_approach_description': 'Unsere präzise Drohnenanwendungsmethode reduziert den Wasserverbrauch im Vergleich zu herkömmlichen Hochdruckreinigungstechniken um bis zu 60%. Dies bedeutet eine schnellere, sauberere Reinigung in weniger Zeit.',
// Contact page
'contact_page_description': 'Haben Sie Fragen zu unseren Dienstleistungen oder möchten Sie eine Reinigung planen? Kontaktieren Sie uns mit einer der folgenden Methoden.',
'contact_phone_title': 'Telefon',
'contact_email_title': 'E-Mail',
'contact_address_title': 'Adresse',
// About page
'about_title': 'Über unser Unternehmen',
'about_description': 'Bei Dachdrohnen-Reinigung revolutionieren wir die Dachreinigungsbranche mit modernster Drohnentechnologie. Unser Unternehmen wurde 2020 gegründet und unsere Mission ist es, sichere, schnelle und gründliche Reinigungsdienstleistungen ohne die Risiken traditioneller Dachreinigungsmethoden anzubieten.',
'our_story_title': 'Unsere Geschichte',
'our_story_text': 'Die Idee für Dachdrohnen-Reinigung entstand, als unser Gründer die Gefahren erkannte, denen traditionelle Dachreiniger täglich ausgesetzt sind. Durch die Kombination von Fachwissen in Drohnentechnologie und Reinigungslösungen haben wir eine sicherere und effizientere Methode zur Dachwartung geschaffen.',
'our_team_caption': 'Das professionelle Dachdrohnen-Reinigungsteam',
'our_team_title': 'Unser Team',
'our_team_text': 'Unser Team besteht aus zertifizierten Drohnenbetreibern, Dachwartungsspezialisten und Kundendienstexperten, die sich dafür einsetzen, Ihnen das bestmögliche Erlebnis zu bieten.',
'our_technology_title': 'Unsere Technologie',
'our_technology_text': 'Wir verwenden speziell entwickelte Drohnen, die mit hochmoderner Reinigungsausrüstung ausgestattet sind, um sicherzustellen, dass Ihr Dach schnell und gründlich gereinigt wird.',
// Products page
'products_title': 'Hochwertige Reinigungsprodukte',
'products_subtitle': 'Entdecken Sie unsere professionellen Produkte für eine optimale Dach- und Oberflächenreinigung',
'product_details': 'Details ansehen',
'product_buy': 'Beim Hersteller kaufen',
'application_title': 'Anwendungshinweise',
'application_subtitle': 'So erzielen Sie die besten Ergebnisse mit unseren Produkten',
'ago_quart_alt': 'AGO Quart Grünbelagentferner',
'ago_quart_1l_alt': 'AGO Quart 1 Liter Flasche',
'ago_quart_5l_alt': 'AGO Quart 5 Liter Kanister',
'ago_sprayer_alt': 'AGO Drucksprüher 5 Liter'
},
'en': {
// Header
'header_title': 'Welcome to Luftglanz',
'roof_drone': 'Luftglanz',
// Navigation
'nav_home': 'Home',
'nav_services': 'Services',
'nav_how_it_works': 'How It Works',
'nav_faq': 'FAQ',
'nav_contact': 'Contact',
'nav_about': 'About Us',
'nav_gallery': 'Gallery',
'nav_products': 'Products',
// Hero section
'hero_title': 'Revolutionary Roof Cleaning with Drone Technology',
'hero_subtitle': 'Fast, efficient, and hassle-free cleaning solutions for your roof.',
// Services section
'services_title': 'Our Services',
'services_description': 'Experience cutting-edge drone technology for fast and effective roof cleaning and maintenance.',
'service_standard_title': 'Comprehensive Roof Cleaning',
'service_standard_description': 'Our advanced cleaning service removes dirt, moss, and algae, restoring your roof to its original condition.',
'service_inspection_title': 'Detailed Roof Inspection',
'service_inspection_description': 'Our drones provide high-resolution inspections to identify potential issues early.',
'service_solar_title': 'Solar Panel Cleaning',
'service_solar_description': 'Maximize the efficiency of your solar panels with our specialized cleaning service.',
// How it works section
'how_it_works_title': 'How It Works',
'how_it_works_description': 'Our streamlined process ensures fast, effective, and stress-free roof cleaning.',
'how_it_works_step1': 'Schedule an Inspection',
'how_it_works_step2': 'Drone Cleaning in Action',
'how_it_works_step3': 'Enjoy a Clean Roof',
'how_it_works_step1_desc': 'Fill out our simple form to get a quote and schedule your inspection.',
'how_it_works_step2_desc': 'Our technicians arrive with specialized drones and equipment to clean your roof.',
'how_it_works_step3_desc': 'Admire your clean roof and enjoy the extended lifespan of your roofing materials.',
// FAQ section
'faq_title': 'Frequently Asked Questions',
'faq_q1': 'How long does the cleaning take?',
'faq_a1': 'Most roofs are cleaned within 2-4 hours, depending on size and complexity.',
'faq_q2': 'Is drone cleaning safe for my roof?',
'faq_a2': 'Yes, our drones use gentle, non-invasive methods that protect your roof.',
'faq_description': 'Find answers to common questions about our drone roof cleaning services',
// Contact form
'contact_title': 'Contact Us',
'contact_name': 'Your Name',
'contact_email': 'Your Email',
'contact_phone': 'Phone Number',
'contact_address': 'Your Address',
'contact_message': 'Your Message',
'contact_reset': 'Reset Form',
'contact_send': 'Send',
'contact_description': 'Contact us for a free quote or to learn more about our services',
// Footer
'footer_text': '© 2025 Luftglanz. All rights reserved.',
// Language selection
'language_select': 'EN',
// Gallery page
'gallery_title': 'Our Work in Action',
'gallery_description': 'Explore our gallery and see how our drones deliver exceptional cleaning results.',
'gallery_prev': 'Previous',
'gallery_next': 'Next',
'gallery_image1_alt': 'Drone cleaning a residential roof',
'gallery_image2_alt': 'Drone performing roof inspection',
'gallery_image3_alt': 'Drone cleaning solar panels',
'gallery_image4_alt': 'Close-up of drone cleaning process',
// Chatbot
'chatbot_title': 'Roof Drone Assistant',
'chatbot_greeting': 'Hello! How can I assist you today with your roof cleaning needs?',
'chatbot_placeholder': 'Type your question here...',
'chatbot_offline_notice': 'Currently offline. Please use our contact form for assistance.',
// Company name variations
'roof_drone': 'Luftglanz',
'roof_drone_cleaning': 'Luftglanz',
// Additional translations for missing elements
'get_quote': 'Get a Free Quote',
// Services page
'services_hero_title': 'Professional Drone Roof Cleaning',
'services_hero_subtitle': 'Our specialized drones ensure fast and thorough cleaning with advanced technology',
'service_drone_title': 'Drone Roof Cleaning',
'service_drone_description': 'Our drones are equipped with specialized spray systems that effectively remove dirt, moss, and algae. This contactless approach eliminates the risk of tile damage and worker accidents associated with traditional roof cleaning methods.',
'service_inspection_detailed_title': 'Inspection Services',
'service_inspection_detailed_description': 'In addition to cleaning, our drones can perform detailed inspections of your roof, identifying potential issues before they become costly repairs. Our high-resolution cameras capture every detail, allowing us to spot missing shingles, damaged flashing, or other problems that might be overlooked with the naked eye.',
'service_maintenance_title': 'Maintenance Packages',
'service_maintenance_description': 'We offer various maintenance packages to keep your roof in top condition year-round, ensuring longevity and aesthetics. Regular maintenance can extend the lifespan of your roof by up to 25% and prevent costly repairs. Our packages can be tailored to your specific roof requirements and budget.',
'premium_services_title': 'Premium Add-On Services',
'premium_services_description': 'In addition to our core services, we offer specialized treatments, including sealing applications, UV protection coatings, and thermal scans to identify potential heat leaks. These additional services help maximize your roofs performance and energy efficiency while extending its overall lifespan.',
'environmental_approach_title': 'Our Efficiency Concept',
'environmental_approach_description': 'Our precise drone application method reduces water usage by up to 60% compared to traditional pressure washing techniques. This means faster, cleaner results in less time.',
// Contact page
'contact_page_description': 'Have questions about our services or want to schedule a cleaning? Contact us using one of the following methods.',
'contact_phone_title': 'Phone',
'contact_email_title': 'Email',
'contact_address_title': 'Address',
// About page
'about_title': 'About Our Company',
'about_description': 'At Roof Drone Cleaning, we are revolutionizing the roof cleaning industry with cutting-edge drone technology. Founded in 2020, our mission is to provide safe, fast, and thorough cleaning services without the risks of traditional roof cleaning methods.',
'our_story_title': 'Our Story',
'our_story_text': 'The idea for Roof Drone Cleaning came when our founder recognized the dangers traditional roof cleaners face daily. By combining expertise in drone technology and cleaning solutions, we have created a safer and more efficient method for roof maintenance.',
'our_team_caption': 'The Professional Roof Drone Cleaning Team',
'our_team_title': 'Our Team',
'our_team_text': 'Our team consists of certified drone operators, roof maintenance specialists, and customer service experts dedicated to providing you with the best experience possible.',
'our_technology_title': 'Our Technology',
'our_technology_text': 'We use specially designed drones equipped with state-of-the-art cleaning equipment to ensure your roof is cleaned quickly and thoroughly.',
// Products page
'products_title': 'High-Quality Cleaning Products',
'products_subtitle': 'Discover our professional products for optimal roof and surface cleaning',
'product_details': 'View Details',
'product_buy': 'Buy from Manufacturer',
'application_title': 'Application Instructions',
'application_subtitle': 'Achieve the best results with our products',
'ago_quart_alt': 'AGO Quart Green Stain Remover',
'ago_quart_1l_alt': 'AGO Quart 1 Liter Bottle',
'ago_quart_5l_alt': 'AGO Quart 5 Liter Canister',
'ago_sprayer_alt': 'AGO Pressure Sprayer 5 Liter'
}
};
// Export the translations object
if (typeof module !== 'undefined' && module.exports) {
module.exports = translations;
}

320
html/drone/pages/about.html Normal file
View File

@@ -0,0 +1,320 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Über Uns - Luftglanz</title>
<link rel="icon" href="../images/favicon_logo.png" type="image/png">
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/main.css">
<link rel="stylesheet" href="../css/components/header.css">
<link rel="stylesheet" href="../css/components/footer.css">
<link rel="stylesheet" href="../css/components/hero.css">
<link rel="stylesheet" href="../css/components/forms.css">
<style>
:root {
--primary-color: #3498db; /* Clean blue primary */
--accent-color: #00c2cb; /* Teal accent */
--secondary-color: #505050;
--bg-light: #f8f9fa; /* Very light background */
--card-bg: #ffffff; /* White card background */
--text-dark: #333333;
--text-medium: #505050;
--text-light: #707070;
--gradient-primary: linear-gradient(90deg, #3498db 0%, #00c2cb 100%);
--gradient-secondary: linear-gradient(90deg, #2980b9 0%, #009ea6 100%);
--shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
--spacing-section: 5rem 0;
}
* {
box-sizing: border-box;
max-width: 100%;
}
html, body {
width: 100%;
overflow-x: hidden;
}
body {
margin: 0;
padding: 0;
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
line-height: 1.6;
color: var(--text-medium);
background-color: var(--bg-light);
background-image:
linear-gradient(120deg, rgba(0, 120, 212, 0.05) 0%, transparent 40%),
linear-gradient(240deg, rgba(0, 178, 148, 0.05) 0%, transparent 40%);
position: relative;
overflow-x: hidden;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
}
img {
max-width: 100%;
height: auto;
border-radius: 8px;
display: block;
}
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95); /* Changed to match --bg-light variable */
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
height: auto;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-img {
height: 40px;
width: auto;
margin-right: 10px;
}
.logo h1 {
font-size: 1.4rem;
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.main-nav ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 1.5rem;
}
.main-nav li {
margin-right: 1rem;
margin-bottom: 0.5rem;
}
.main-nav a {
color: var(--text-medium);
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
letter-spacing: 0.5px;
padding: 0.5rem 0;
position: relative;
transition: color 0.3s ease;
}
.main-nav a:hover, .main-nav a.active {
color: var(--accent-color);
}
.main-nav a::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background: var(--gradient-primary);
transition: width 0.3s ease;
}
.main-nav a:hover::after, .main-nav a.active::after {
width: 100%;
}
@media (max-width: 768px) {
.main-nav ul {
flex-direction: column;
}
.main-nav li {
margin-right: 0;
margin-bottom: 0.5rem;
}
}
.section-container {
background-color: var(--card-bg);
border-radius: 10px;
margin-bottom: 2rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
padding: 2.5rem;
border: 1px solid rgba(0, 0, 0, 0.05);
}
.section-container h2 {
color: var(--text-dark);
font-size: 2.2rem;
margin-bottom: 2rem;
position: relative;
display: inline-block;
}
.section-container h2::after {
content: '';
position: absolute;
width: 60px;
height: 3px;
background: var(--gradient-primary);
bottom: -0.75rem;
left: 0;
border-radius: 2px;
}
.section-container h3 {
color: var(--text-dark);
font-size: 1.5rem;
margin-top: 2.5rem;
margin-bottom: 1rem;
}
.section-container p {
color: var(--text-light);
margin-bottom: 1.5rem;
line-height: 1.7;
}
.team-image-container {
margin: 3rem 0 !important;
position: relative;
overflow: hidden;
border-radius: 10px;
box-shadow: var(--shadow);
}
.team-image-container img {
width: 100%;
display: block;
border-radius: 10px;
filter: brightness(0.9);
transition: transform 0.5s ease;
}
.team-image-container:hover img {
transform: scale(1.03);
}
.team-image-container p {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(255, 255, 255, 0.9), transparent);
padding: 2rem 1.5rem 1rem;
margin: 0;
color: var(--text-medium);
font-style: italic;
text-align: center;
}
.stars {
display: none;
}
main {
padding-top: 8rem;
margin-top: 0;
}
footer {
background-color: var(--text-dark);
padding: 2rem 0;
border-top: 1px solid rgba(0, 0, 0, 0.05);
text-align: center;
color: #aaaaaa;
margin-top: 3rem;
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1 data-i18n="roof_drone">Luftglanz</h1>
</div>
<button class="mobile-toggle" id="mobileToggle">
<span></span>
</button>
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="../index.html" data-i18n="nav_home">Home</a></li>
<li><a href="../index.html#services" data-i18n="nav_services">Services</a></li>
<li><a href="../index.html#how-it-works" data-i18n="nav_how_it_works">How It Works</a></li>
<li><a href="../index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="../index.html#contact" data-i18n="nav_contact">Contact</a></li>
<li><a href="products.html" data-i18n="nav_products">Products</a></li>
<li><a href="about.html" class="active" data-i18n="nav_about">About Us</a></li>
<li><a href="gallery.html" data-i18n="nav_gallery">Gallery</a></li>
</ul>
</nav>
<!-- Language switcher removed -->
</div>
</div>
</header>
<main class="container">
<section class="section-container">
<h2 data-i18n="about_title">About Our Company</h2>
<p data-i18n="about_description">Bei Dachdrohnen-Reinigung revolutionieren wir die Dachreinigungsbranche mit modernster Drohnentechnologie. Unser Unternehmen wurde 2020 gegründet und unsere Mission ist es, sichere, schnelle und gründliche Reinigungsdienstleistungen ohne die Risiken traditioneller Dachreinigungsmethoden anzubieten.</p>
<h3 data-i18n="our_story_title">Our Story</h3>
<p data-i18n="our_story_text">The idea for Roof Drone Cleaning came when our founder noticed the dangers that traditional roof cleaners faced daily. By combining expertise in drone technology and cleaning solutions, we created a safer, more efficient method for roof maintenance.</p>
<div class="team-image-container">
<img src="../images/out_team.jpg" alt="Our professional team">
<p data-i18n="our_team_caption">The Roof Drone Cleaning professional team</p>
</div>
<h3 data-i18n="our_team_title">Our Team</h3>
<p data-i18n="our_team_text">Our team consists of certified drone operators, roof maintenance specialists, and customer service experts dedicated to providing you with the best possible experience.</p>
<h3 data-i18n="our_technology_title">Unsere Technologie</h3>
<p data-i18n="our_technology_text">Wir verwenden speziell entwickelte Drohnen, die mit hochmoderner Reinigungsausrüstung ausgestattet sind, um sicherzustellen, dass Ihr Dach schnell und gründlich gereinigt wird.</p>
</section>
</main>
<footer>
<div class="container">
<p data-i18n="footer_text">&copy; 2023 Luftglanz. Alle Rechte vorbehalten.</p>
</div>
</footer>
<script src="../js/translations.js"></script>
<script src="../js/language-manager.js"></script>
</body>
</html>

View File

@@ -0,0 +1,583 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kontakt - Luftglanz</title>
<link rel="icon" href="../images/favicon_logo.png" type="image/png">
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/main.css">
<link rel="stylesheet" href="../css/components/header.css">
<link rel="stylesheet" href="../css/components/footer.css">
<link rel="stylesheet" href="../css/components/hero.css">
<link rel="stylesheet" href="../css/components/forms.css">
<style>
:root {
--primary-color: #3498db; /* Clean blue primary */
--accent-color: #00c2cb; /* Teal accent */
--secondary-color: #505050;
--bg-light: #f8f9fa; /* Very light background */
--card-bg: #ffffff; /* White card background */
--text-dark: #333333;
--text-medium: #505050;
--text-light: #707070;
--gradient-primary: linear-gradient(90deg, #3498db 0%, #00c2cb 100%);
--gradient-secondary: linear-gradient(90deg, #2980b9 0%, #009ea6 100%);
--shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
--spacing-section: 5rem 0;
}
* {
box-sizing: border-box;
max-width: 100%;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
padding: 0;
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
line-height: 1.6;
color: var(--text-medium);
background-color: var(--bg-light);
background-image:
linear-gradient(120deg, rgba(0, 120, 212, 0.05) 0%, transparent 40%),
linear-gradient(240deg, rgba(0, 178, 148, 0.05) 0%, transparent 40%);
position: relative;
overflow-x: hidden;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
position: relative;
z-index: 2;
}
/* Header styling */
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95); /* Changed to match --bg-light variable */
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
height: auto;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-img {
height: 40px;
width: auto;
margin-right: 10px;
}
.logo h1 {
font-size: 1.4rem;
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
/* Navigation styling */
.main-nav ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 1.5rem;
}
.main-nav a {
color: var(--text-medium);
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
letter-spacing: 0.5px;
padding: 0.5rem 0;
position: relative;
transition: color 0.3s ease;
}
.main-nav a:hover, .main-nav a.active {
color: var(--accent-color);
}
.main-nav a::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background: var(--gradient-primary);
transition: width 0.3s ease;
}
.main-nav a:hover::after, .main-nav a.active::after {
width: 100%;
}
/* Section styling */
.section-container {
padding: 2.5rem;
margin-bottom: 2rem;
position: relative;
}
.section-title {
text-align: center;
margin-bottom: 3rem;
position: relative;
color: var(--text-dark);
font-size: 2.5rem;
font-weight: 700;
}
.section-title::after {
content: '';
position: absolute;
width: 60px;
height: 3px;
background: var(--gradient-primary);
bottom: -1rem;
left: 50%;
transform: translateX(-50%);
border-radius: 2px;
}
.section-description {
text-align: center;
color: var(--text-light);
max-width: 800px;
margin: 0 auto 3rem;
font-size: 1.1rem;
line-height: 1.7;
}
/* Contact info cards */
.contact-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-bottom: 3rem;
}
.contact-method {
background-color: var(--card-bg);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 10px;
padding: 2rem;
text-align: center;
transition: all 0.3s ease;
}
.contact-method:hover {
transform: translateY(-10px);
box-shadow: var(--shadow);
}
.contact-method h3 {
color: var(--text-dark);
margin-top: 0;
margin-bottom: 1rem;
font-size: 1.3rem;
position: relative;
display: inline-block;
}
.contact-method h3::after {
content: '';
position: absolute;
width: 30px;
height: 2px;
background: var(--gradient-primary);
bottom: -0.5rem;
left: 50%;
transform: translateX(-50%);
border-radius: 2px;
}
.contact-method p {
color: var(--text-light);
margin-bottom: 0;
}
/* Contact form */
.contact-form {
background-color: var(--card-bg);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 10px;
padding: 2.5rem;
max-width: 800px;
margin: 0 auto;
box-shadow: var(--shadow);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-medium);
font-weight: 500;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 1rem;
background-color: rgba(0, 0, 0, 0.05);
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 6px;
color: var(--text-medium);
font-size: 1rem;
transition: all 0.3s ease;
}
.form-group input:focus,
.form-group textarea:focus,
.form-group select:focus {
outline: none;
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(0, 194, 203, 0.2);
}
.form-actions {
display: flex;
justify-content: space-between;
gap: 1.5rem;
}
.btn-primary {
background: var(--gradient-primary);
color: var(--text-dark);
border: none;
padding: 0.8rem 1.5rem;
font-size: 1rem;
font-weight: 600;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
flex-grow: 1;
box-shadow: var(--shadow);
position: relative;
z-index: 1;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 0 20px rgba(0, 194, 203, 0.6);
}
.btn-primary::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: var(--gradient-secondary);
border-radius: 6px;
z-index: -1;
opacity: 0;
transition: opacity 0.3s ease;
}
.btn-primary:hover::before {
opacity: 1;
}
.btn-secondary {
background-color: rgba(0, 0, 0, 0.05);
color: var(--text-medium);
border: 1px solid rgba(0, 0, 0, 0.1);
padding: 0.8rem 1.5rem;
font-size: 1rem;
font-weight: 500;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-secondary:hover {
background-color: rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
/* Language Switcher - Updated to match About Us page */
.language-switcher {
display: flex;
gap: 0.75rem;
}
.language-switcher button {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--text-medium);
padding: 0.4rem 0.75rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.language-switcher button.active,
.language-switcher button:hover {
background: var(--gradient-primary);
border-color: transparent;
}
/* Stars Background */
.stars {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 0;
}
.star {
position: absolute;
width: 2px;
height: 2px;
background-color: white;
border-radius: 50%;
opacity: 0.5;
animation: twinkling 4s infinite;
}
@keyframes twinkling {
0% { opacity: 0.2; }
50% { opacity: 0.5; }
100% { opacity: 0.2; }
}
/* Footer styling */
footer {
background-color: rgba(255, 255, 255, 0.95);
padding: 2rem 0;
border-top: 1px solid rgba(0, 0, 0, 0.05);
text-align: center;
color: var(--text-light);
margin-top: 3rem;
}
main {
padding-top: 8rem;
}
/* Mobile styles */
@media (max-width: 768px) {
.main-nav {
display: none;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: rgba(255, 255, 255, 0.95);
padding: 1rem 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.main-nav.active {
display: block;
}
.main-nav ul {
flex-direction: column;
gap: 0;
}
.main-nav li {
width: 100%;
text-align: center;
padding: 0.5rem 0;
margin: 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.main-nav li:last-child {
border-bottom: none;
}
.main-nav a {
display: block;
padding: 0.5rem 0;
}
.form-actions {
flex-direction: column;
gap: 1rem;
}
.section-title {
font-size: 2rem;
}
.contact-form {
padding: 1.5rem;
}
.contact-method {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<!-- Stars background -->
<div class="stars" id="stars"></div>
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1 data-i18n="roof_drone">Luftglanz</h1>
</div>
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="../index.html" data-i18n="nav_home">Home</a></li>
<li><a href="../index.html#services" data-i18n="nav_services">Services</a></li>
<li><a href="../index.html#how-it-works" data-i18n="nav_how_it_works">How It Works</a></li>
<li><a href="../index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="#" class="active" data-i18n="nav_contact">Contact</a></li>
<li><a href="products.html" data-i18n="nav_products">Products</a></li>
<li><a href="gallery.html" data-i18n="nav_gallery">Gallery</a></li>
</ul>
</nav>
<!-- Language switcher removed -->
</div>
</div>
</header>
<main>
<section class="section-container">
<div class="container">
<h2 class="section-title" data-i18n="contact_title">Contact Us</h2>
<p class="section-description" data-i18n="contact_page_description">Have questions about our services or want to schedule a cleaning? Get in touch with us using any of the methods below.</p>
<div class="contact-info">
<div class="contact-method">
<h3 data-i18n="contact_phone_title">Phone</h3>
<p>(555) 123-4567</p>
</div>
<div class="contact-method">
<h3 data-i18n="contact_email_title">Email</h3>
<p>info@roofdronecleaning.com</p>
</div>
<div class="contact-method">
<h3 data-i18n="contact_address_title">Address</h3>
<p>123 Cleaning Ave<br>Drone City, DC 10001</p>
</div>
</div>
<form class="contact-form">
<div class="form-group">
<label for="name" data-i18n="contact_name">Name</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email" data-i18n="contact_email">Email</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="phone" data-i18n="contact_phone">Phone (Optional)</label>
<input type="tel" id="phone" name="phone">
</div>
<div class="form-group">
<label for="address" data-i18n="contact_address">Address</label>
<input type="text" id="address" name="address">
</div>
<div class="form-group">
<label for="message" data-i18n="contact_message">Message</label>
<textarea id="message" name="message" required rows="4"></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn-secondary" id="resetForm" data-i18n="contact_reset">Reset</button>
<button type="submit" class="btn-primary" data-i18n="contact_send">Send Message</button>
</div>
</form>
</div>
</section>
</main>
<footer>
<div class="container">
<p data-i18n="footer_text">&copy; 2023 Luftglanz. Alle Rechte vorbehalten.</p>
</div>
</footer>
<script src="../js/translations.js"></script>
<script src="../js/language-manager.js"></script>
<script>
// Add star backgrounds dynamically
document.addEventListener('DOMContentLoaded', function() {
const starsContainer = document.getElementById('stars');
const starsCount = 100;
for(let i = 0; i < starsCount; i++) {
const star = document.createElement('div');
star.classList.add('star');
// Random position
star.style.left = `${Math.random() * 100}%`;
star.style.top = `${Math.random() * 100}%`;
// Random size
const size = Math.random() * 2;
star.style.width = `${size}px`;
star.style.height = `${size}px`;
// Random delay for twinkling
star.style.animationDelay = `${Math.random() * 4}s`;
starsContainer.appendChild(star);
}
// Reset form functionality
const resetButton = document.getElementById('resetForm');
const contactForm = document.querySelector('.contact-form');
if (resetButton && contactForm) {
resetButton.addEventListener('click', function() {
contactForm.reset();
});
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,573 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Galerie - Luftglanz</title>
<link rel="icon" href="../images/favicon_logo.png" type="image/png">
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/main.css">
<link rel="stylesheet" href="../css/components/header.css">
<link rel="stylesheet" href="../css/components/footer.css">
<link rel="stylesheet" href="../css/components/hero.css">
<link rel="stylesheet" href="../css/components/forms.css">
<link rel="stylesheet" href="../css/components/mobile-menu.css">
<style>
:root {
--primary-color: #3498db; /* Clean blue primary */
--accent-color: #00c2cb; /* Teal accent */
--secondary-color: #505050;
--bg-light: #f8f9fa; /* Very light background */
--card-bg: #ffffff; /* White card background */
--text-dark: #333333;
--text-medium: #505050;
--text-light: #707070;
--gradient-primary: linear-gradient(90deg, #3498db 0%, #00c2cb 100%);
--gradient-secondary: linear-gradient(90deg, #2980b9 0%, #009ea6 100%);
--shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
--spacing-section: 5rem 0;
}
* {
box-sizing: border-box;
max-width: 100%;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
padding: 0;
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
line-height: 1.6;
color: var(--text-medium);
background-color: var(--bg-light);
background-image:
linear-gradient(120deg, rgba(0, 120, 212, 0.05) 0%, transparent 40%),
linear-gradient(240deg, rgba(0, 178, 148, 0.05) 0%, transparent 40%);
position: relative;
overflow-x: hidden;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
position: relative;
z-index: 2;
}
/* Header styling */
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95); /* Changed to match --bg-light variable */
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
height: auto;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-img {
height: 40px;
width: auto;
margin-right: 10px;
}
.logo h1 {
font-size: 1.4rem;
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
/* Navigation styling */
.main-nav ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 1.5rem;
}
.main-nav a {
color: var(--text-medium);
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
letter-spacing: 0.5px;
padding: 0.5rem 0;
position: relative;
transition: color 0.3s ease;
}
.main-nav a:hover, .main-nav a.active {
color: var (--accent-color);
}
.main-nav a::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background: var(--gradient-primary);
transition: width 0.3s ease;
}
.main-nav a:hover::after, .main-nav a.active::after {
width: 100%;
}
/* Section styling */
.section-container {
padding: 2rem;
margin-bottom: 2rem;
background-color: var(--card-bg);
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
margin-top: 6rem; /* Increased to move content below header */
padding-top: 2rem;
}
.section-container h2 {
display: block; /* Changed from none to block */
font-size: 2.2rem;
color: var(--text-dark);
margin-bottom: 1.5rem;
text-align: center;
}
.section-container p {
color: var(--text-light);
margin-bottom: 2rem;
text-align: center;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
/* Gallery grid styling */
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.gallery-item {
position: relative;
aspect-ratio: 3/2;
border-radius: 8px;
overflow: hidden;
cursor: pointer;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
}
.gallery-item::after {
content: '🔍';
position: absolute;
bottom: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.8);
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
opacity: 0;
transition: opacity 0.3s ease;
}
.gallery-item:hover::after {
opacity: 1;
}
/* Lightbox styling */
.lightbox {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.lightbox.active {
opacity: 1;
pointer-events: all;
}
.lightbox-content {
position: relative;
max-width: 90%;
max-height: 80%;
display: flex;
justify-content: center;
align-items: center;
}
.lightbox-image {
max-width: 100%;
max-height: 80vh;
border-radius: 4px;
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.3);
}
.lightbox-close {
position: absolute;
top: 20px;
right: 20px;
color: white;
font-size: 30px;
background: rgba(0, 0, 0, 0.5);
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
z-index: 10;
transition: all 0.3s ease;
}
.lightbox-close:hover {
background: rgba(255, 255, 255, 0.2);
transform: rotate(90deg);
}
.lightbox-nav {
position: absolute;
top: 50%;
width: 100%;
display: flex;
justify-content: space-between;
padding: 0 20px;
transform: translateY(-50%);
z-index: 10;
}
.lightbox-nav button {
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
width: 50px;
height: 50px;
border-radius: 50%;
font-size: 24px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-nav button:hover {
background: var(--gradient-primary);
transform: scale(1.1);
}
.lightbox-counter {
position: absolute;
bottom: -40px;
color: white;
font-size: 16px;
text-align: center;
width: 100%;
}
/* Adjust main content to start lower */
main {
padding-top: 5rem; /* Keep this to account for the fixed header */
margin-top: 0;
}
/* Mobile adjustments */
@media (max-width: 768px) {
.gallery-grid {
grid-template-columns: repeat(2, 1fr);
}
.lightbox-nav button {
width: 40px;
height: 40px;
font-size: 20px;
}
}
/* Language Switcher - Updated to match About Us page */
.language-switcher {
display: flex;
gap: 0.75rem;
}
.language-switcher button {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--text-medium);
padding: 0.4rem 0.75rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.language-switcher button.active,
.language-switcher button:hover {
background: var(--gradient-primary);
border-color: transparent;
}
</style>
</head>
<body>
<!-- Remove stars background -->
<!-- <div class="stars" id="stars"></div> -->
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<a href="../index.html" style="text-decoration: none; display: flex; align-items: center;">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1 data-i18n="roof_drone">Luftglanz</h1>
</a>
</div>
<button class="mobile-toggle" id="mobileToggle" aria-label="Toggle navigation">
</button>
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="../index.html" data-i18n="nav_home">Startseite</a></li>
<li><a href="../index.html#services" data-i18n="nav_services">Leistungen</a></li>
<li><a href="../index.html#how-it-works" data-i18n="nav_how_it_works">Ablauf</a></li>
<li><a href="../index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="#" class="contact-link" data-i18n="nav_contact">Kontakt</a></li>
<li><a href="products.html" data-i18n="nav_products">Produkte</a></li>
<li><a href="gallery.html" class="active" data-i18n="nav_gallery">Galerie</a></li>
</ul>
</nav>
</div>
</div>
</header>
<main class="container">
<section class="section-container">
<h2 data-i18n="gallery_title">Unsere Arbeit</h2>
<p data-i18n="gallery_description">Entdecken Sie unsere Galerie, um zu sehen, wie unsere Drohnen außergewöhnliche Reinigungsergebnisse liefern.</p>
<div class="gallery-grid">
<div class="gallery-item">
<img src="../images/drone_realistic1.png" alt="Realistic Drone Cleaning" class="gallery-image" data-i18n-alt="gallery_drone_realistic_alt">
</div>
<div class="gallery-item">
<img src="../images/drone_realistic3.png" alt="Advanced Drone Cleaning Technology" class="gallery-image" data-i18n-alt="gallery_drone_realistic3_alt">
</div>
<div class="gallery-item">
<img src="../images/drone_realistic4.png" alt="Professional Drone Cleaning System" class="gallery-image" data-i18n-alt="gallery_drone_realistic4_alt">
</div>
<div class="gallery-item">
<img src="../images/drone_in_action1.jpg" alt="" data-i18n-alt="gallery_image1_alt">
</div>
<div class="gallery-item">
<img src="../images/drone_realistic5.png" alt="" data-i18n-alt="gallery_image3_alt">
</div>
<div class="gallery-item">
<img src="../images/drone_realistic6.png" alt="" data-i18n-alt="gallery_image4_alt">
</div>
</div>
</section>
</main>
<div class="lightbox" id="lightbox">
<div class="lightbox-content">
<img src="" alt="" class="lightbox-image" id="lightbox-image">
<div class="lightbox-counter" id="lightbox-counter"></div>
</div>
<div class="lightbox-close" id="lightbox-close"></div>
<div class="lightbox-nav">
<button id="lightbox-prev" data-i18n="gallery_prev">Zurück</button>
<button id="lightbox-next" data-i18n="gallery_next">Weiter</button>
</div>
</div>
<footer>
<div class="container">
<p data-i18n="footer_text">&copy; 2025 Luftglanz. Alle Rechte vorbehalten.</p>
</div>
</footer>
<script src="../js/translations.js"></script>
<script src="../js/language-manager.js"></script>
<!-- Remove drone animation script -->
<!-- <script src="../js/drone-image-animation.js"></script> -->
<script src="../js/mobile-menu.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Set up lightbox functionality
setupLightbox();
});
function setupLightbox() {
// Get all necessary lightbox elements
const lightbox = document.getElementById('lightbox');
const lightboxImage = document.getElementById('lightbox-image');
const lightboxClose = document.getElementById('lightbox-close');
const lightboxPrev = document.getElementById('lightbox-prev');
const lightboxNext = document.getElementById('lightbox-next');
const lightboxCounter = document.getElementById('lightbox-counter');
const galleryItems = document.querySelectorAll('.gallery-item img');
// Make sure elements exist
if (!lightbox || !lightboxImage || galleryItems.length === 0) {
console.error('Lightbox elements not found');
return;
}
// Track the current image index
let currentIndex = 0;
// Function to update counter
function updateCounter() {
if (lightboxCounter) {
lightboxCounter.textContent = `${currentIndex + 1} / ${galleryItems.length}`;
}
}
// Open lightbox when gallery image is clicked
galleryItems.forEach((item, index) => {
item.addEventListener('click', function() {
currentIndex = index;
lightboxImage.src = item.src;
lightboxImage.alt = item.alt;
lightbox.classList.add('active');
document.body.style.overflow = 'hidden'; // Prevent scrolling
updateCounter();
});
});
// Navigate to previous image
if (lightboxPrev) {
lightboxPrev.addEventListener('click', function(e) {
e.stopPropagation(); // Prevent event bubbling to lightbox
currentIndex = (currentIndex > 0) ? currentIndex - 1 : galleryItems.length - 1;
lightboxImage.src = galleryItems[currentIndex].src;
lightboxImage.alt = galleryItems[currentIndex].alt;
updateCounter();
});
}
// Navigate to next image
if (lightboxNext) {
lightboxNext.addEventListener('click', function(e) {
e.stopPropagation(); // Prevent event bubbling to lightbox
currentIndex = (currentIndex < galleryItems.length - 1) ? currentIndex + 1 : 0;
lightboxImage.src = galleryItems[currentIndex].src;
lightboxImage.alt = galleryItems[currentIndex].alt;
updateCounter();
});
}
// Close lightbox when close button is clicked
if (lightboxClose) {
lightboxClose.addEventListener('click', function() {
lightbox.classList.remove('active');
document.body.style.overflow = ''; // Re-enable scrolling
});
}
// Close lightbox when clicking outside the image
lightbox.addEventListener('click', function(e) {
if (e.target === lightbox) {
lightbox.classList.remove('active');
document.body.style.overflow = '';
}
});
// Prevent closing when clicking on image
lightboxImage.addEventListener('click', function(e) {
e.stopPropagation();
});
// Keyboard navigation
document.addEventListener('keydown', function(e) {
if (lightbox.classList.contains('active')) {
if (e.key === 'ArrowLeft' && lightboxPrev) {
lightboxPrev.click();
} else if (e.key === 'ArrowRight' && lightboxNext) {
lightboxNext.click();
} else if (e.key === 'Escape') {
lightbox.classList.remove('active');
document.body.style.overflow = '';
}
}
});
}
// Handle contact link with consistent offset
document.addEventListener('DOMContentLoaded', function() {
const contactLink = document.querySelector('.contact-link');
if (contactLink) {
contactLink.addEventListener('click', function(e) {
e.preventDefault();
window.location.href = '../index.html#contact';
// Apply the same offset as the main page
setTimeout(function() {
if (window.location.hash === '#contact') {
const contactSection = document.querySelector('#contact');
if (contactSection) {
window.scrollTo({
top: contactSection.offsetTop - (-5), // Same -5px offset
behavior: 'smooth'
});
}
}
}, 100);
});
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Products Debug - Luftglanz</title>
<style>
/* Minimal styling to ensure visibility */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f0f0f0;
}
.debug-info {
position: fixed;
top: 0;
left: 0;
background-color: red;
color: white;
padding: 10px;
z-index: 9999;
font-weight: bold;
}
header {
background-color: #e8e9ea;
padding: 20px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.logo {
display: flex;
align-items: center;
}
.logo-img {
height: 40px;
margin-right: 10px;
}
main {
padding: 20px;
margin-top: 20px;
background-color: white;
}
.product-card {
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 20px;
background-color: white;
}
</style>
</head>
<body>
<div class="debug-info">DEBUG MODE</div>
<header>
<div class="logo">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1>Luftglanz</h1>
</div>
</header>
<main>
<h2>Products (Debug Version)</h2>
<div class="product-card">
<h3>AGO Quart Grünbelagentferner</h3>
<p>Test product description to verify content visibility</p>
<p><strong>Price:</strong> 24,90 €</p>
<a href="https://www.agoshop.de/gruenbelagentferner/ago-quart/" target="_blank">View on Manufacturer's Site</a>
</div>
<div class="product-card">
<h3>AGO Quart 5 Liter</h3>
<p>Test product description to verify content visibility</p>
<p><strong>Price:</strong> 99,90 €</p>
<a href="https://www.agoshop.de/gruenbelagentferner/ago-quart-5-liter/" target="_blank">View on Manufacturer's Site</a>
</div>
</main>
<script>
// Display debugging information
document.addEventListener('DOMContentLoaded', function() {
console.log('Debug page loaded');
// Check if elements are visible
const header = document.querySelector('header');
const main = document.querySelector('main');
console.log('Header visible:', header.offsetHeight > 0);
console.log('Main visible:', main.offsetHeight > 0);
// Add a timestamp to verify the page is current
const debugInfo = document.querySelector('.debug-info');
debugInfo.textContent += ' - ' + new Date().toLocaleTimeString();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,427 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Produkte - Luftglanz</title>
<link rel="icon" href="../images/favicon_logo.png" type="image/png">
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/main.css">
<link rel="stylesheet" href="../css/components/header.css">
<link rel="stylesheet" href="../css/components/footer.css">
<link rel="stylesheet" href="../css/components/mobile-menu.css">
<style>
:root {
--primary-color: #2980b9;
--accent-color: #00adb8;
--secondary-color: #444444;
--bg-light: #e8e9ea;
--card-bg: #f5f5f5;
--text-dark: #222222;
--text-medium: #444444;
--text-light: #606060;
--gradient-primary: linear-gradient(90deg, #2980b9 0%, #00adb8 100%);
--gradient-secondary: linear-gradient(90deg, #1f6797 0%, #00858e 100%);
--shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
--spacing-section: 5rem 0;
}
body {
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 0;
color: var(--text-medium);
background-color: var(--bg-light);
background-image:
linear-gradient(120deg, rgba(0, 95, 168, 0.05) 0%, transparent 40%),
linear-gradient(240deg, rgba(0, 140, 120, 0.05) 0%, transparent 40%);
line-height: 1.6;
position: relative;
overflow-x: hidden; /* Prevent horizontal scrolling */
font-size: 0.95rem; /* Base font size to match gallery */
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box; /* Ensure padding is included in width */
}
/* Match main padding exactly to gallery page */
main {
padding-top: 5rem !important;
}
/* Fix the product section positioning */
.product-section {
margin-top: 2rem !important; /* Reduced from 6rem to match gallery */
padding: 2rem;
background-color: var(--card-bg);
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
/* Update to exactly match gallery styling */
.main-nav a {
color: var(--text-medium);
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
letter-spacing: 0.5px;
padding: 0.5rem 0; /* Remove horizontal padding */
position: relative;
transition: color 0.3s ease;
}
.main-nav ul {
gap: 1.5rem; /* Match gallery page spacing exactly */
}
.logo h1 {
font-size: 1.4rem; /* Corrected: Changed from 1.7rem to match gallery page exactly */
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.logo-img {
height: 40px; /* Explicitly set to match gallery page */
width: auto;
margin-right: 10px;
}
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95);
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
height: auto;
}
.mobile-toggle {
position: relative;
right: 0;
margin-left: auto;
z-index: 1001;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: nowrap; /* Prevent wrapping that could cause overflow */
width: 100%;
}
/* Product page specific styling improvements */
.product-header h2 {
display: block;
font-size: 2.2rem;
color: var(--text-dark);
margin-bottom: 1.5rem;
text-align: center;
}
.product-header p {
color: var(--text-light);
margin-bottom: 2rem;
text-align: center;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.product-showcase {
display: flex;
flex-direction: column;
gap: 40px;
}
.product-main {
display: flex;
gap: 40px;
align-items: center;
}
.product-image {
flex: 0 0 300px;
}
.product-image img {
width: 100%;
border-radius: 8px;
box-shadow: var(--shadow);
}
.product-info {
flex: 1;
}
.product-title {
font-size: 1.6rem; /* Reduced from 1.8rem */
color: var(--text-dark);
margin: 0 0 15px 0;
}
.product-description {
margin-bottom: 20px;
color: var(--text-medium);
}
.product-description p {
font-size: 0.95rem; /* Explicitly match gallery text size */
}
.product-features {
margin-bottom: 25px;
padding-left: 25px;
}
.product-features li {
margin-bottom: 8px;
font-size: 0.95rem; /* Match gallery text size */
}
.btn-primary {
background-color: var(--primary-color);
color: white;
padding: 10px 22px; /* Slightly smaller button */
border-radius: 5px;
text-decoration: none;
display: inline-block;
font-weight: bold;
font-size: 0.9rem; /* Reduced button text size */
transition: background-color 0.3s;
}
.btn-primary:hover {
background-color: var(--accent-color);
}
/* Mobile styles */
@media (max-width: 768px) {
.product-main {
flex-direction: column;
}
.product-image {
flex: 0 0 auto;
width: 100%;
max-width: 300px;
margin: 0 auto;
}
/* Mobile menu styles */
#mainNav {
display: none;
}
#mainNav.active {
display: block;
position: absolute;
top: 100%;
left: 0;
right: 0;
background: var(--bg-light);
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
z-index: 100;
width: 100%;
max-width: 100vw;
box-sizing: border-box;
}
#mainNav ul {
flex-direction: column;
gap: 0;
}
#mainNav li {
width: 100%;
text-align: center;
}
#mainNav a {
display: block;
padding: 10px;
border-bottom: 1px solid rgba(0,0,0,0.1);
}
#mobileToggle {
display: block !important;
font-size: 1.75rem;
background: none;
border: none;
color: var(--primary-color);
cursor: pointer;
}
.container {
padding: 0 10px; /* Reduce padding on mobile */
}
.logo {
flex-shrink: 1; /* Allow logo to shrink if needed */
}
.logo h1 {
font-size: 1.4rem; /* Slightly smaller on mobile */
}
}
@media (min-width: 769px) {
#mobileToggle {
display: none !important;
}
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<a href="../index.html" style="text-decoration: none; display: flex; align-items: center;">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1 data-i18n="roof_drone">Luftglanz</h1>
</a>
</div>
<button class="mobile-toggle" id="mobileToggle" aria-label="Toggle navigation">
</button>
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="../index.html" data-i18n="nav_home">Startseite</a></li>
<li><a href="../index.html#services" data-i18n="nav_services">Leistungen</a></li>
<li><a href="../index.html#how-it-works" data-i18n="nav_how_it_works">Ablauf</a></li>
<li><a href="../index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="#" class="contact-link" data-i18n="nav_contact">Kontakt</a></li>
<li><a href="products.html" class="active" data-i18n="nav_products">Produkte</a></li>
<li><a href="gallery.html" data-i18n="nav_gallery">Galerie</a></li>
</ul>
</nav>
</div>
</div>
</header>
<main class="container">
<section class="product-section">
<div class="product-header">
<h2>AGO Quart Grünbelagentferner</h2>
<p>Hocheffektive Lösung gegen Grünbeläge, Algen, Moos und Flechten auf allen Oberflächen</p>
</div>
<div class="product-showcase">
<div class="product-main">
<div class="product-image">
<img src="../images/products/ago-quart-05l.jpg" alt="AGO Quart 100 0,5L Flasche">
</div>
<div class="product-info">
<ul class="product-features">
<li>Hochkonzentrierte Wirkformel gegen Grünbeläge</li>
<li>Selbsttätige Langzeitwirkung ohne Abwaschen</li>
<li>Für Dächer, Fassaden, Terrassen, Wege, Zäune</li>
<li>Vorbeugender Schutz über Monate</li>
<li>Einfache Anwendung durch Aufsprühen</li>
<li>Sichtbare Ergebnisse nach 2-3 Tagen</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Updated MELLERUD Solar Panel Cleaner Product Section -->
<section class="product-section">
<div class="product-header">
<h2>MELLERUD Photovoltaik und Solaranlagen-Reiniger</h2>
</div>
<div class="product-showcase">
<div class="product-main">
<div class="product-image">
<img src="../images/solarreiniger.jpg" alt="MELLERUD Photovoltaik und Solaranlagen-Reiniger Konzentrat">
</div>
<div class="product-info">
<div class="product-description">
<p>Speziell entwickelter Reiniger für die effektive und schonende Reinigung von Photovoltaik- und Solaranlagen. Entfernt zuverlässig Verschmutzungen wie Staub, Ruß, Öl, Fett, Vogelkot und Pollen.</p>
</div>
<ul class="product-features">
<li>Materialschonend und rückstandsfrei</li>
<li>Verbessert die Leistung und Erträge der Anlagen</li>
<li>Biologisch abbaubar und umweltfreundlich</li>
<li>Für alle Solar- und Photovoltaikanlagen geeignet</li>
</ul>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<p data-i18n="footer_text">&copy; 2025 Luftglanz. Alle Rechte vorbehalten.</p>
</div>
</footer>
<script src="../js/translations.js"></script>
<script src="../js/language-manager.js"></script>
<script>
// Direct mobile menu implementation
document.addEventListener('DOMContentLoaded', function() {
const toggle = document.getElementById('mobileToggle');
const nav = document.getElementById('mainNav');
if (toggle && nav) {
toggle.addEventListener('click', function() {
console.log('Toggle clicked');
if (nav.classList.contains('active')) {
nav.classList.remove('active');
} else {
nav.classList.add('active');
}
});
}
// Add contact link handler
const contactLink = document.querySelector('.contact-link');
if (contactLink) {
contactLink.addEventListener('click', function(e) {
e.preventDefault();
window.location.href = '../index.html#contact';
// Apply the same offset as the main page
setTimeout(function() {
if (window.location.hash === '#contact') {
const contactSection = document.querySelector('#contact');
if (contactSection) {
window.scrollTo({
top: contactSection.offsetTop - (-5), // Same -5px offset
behavior: 'smooth'
});
}
}
}, 100);
});
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,425 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Leistungen - Luftglanz</title>
<link rel="icon" href="../images/favicon_logo.png" type="image/png">
<link rel="stylesheet" href="../css/normalize.css">
<link rel="stylesheet" href="../css/main.css">
<link rel="stylesheet" href="../css/components/header.css">
<link rel="stylesheet" href="../css/components/footer.css">
<link rel="stylesheet" href="../css/components/hero.css">
<link rel="stylesheet" href="../css/components/forms.css">
<style>
:root {
--primary-color: #3498db; /* Clean blue primary */
--accent-color: #00c2cb; /* Teal accent */
--secondary-color: #505050;
--bg-light: #f8f9fa; /* Very light background */
--card-bg: #ffffff; /* White card background */
--text-dark: #333333;
--text-medium: #505050;
--text-light: #707070;
--gradient-primary: linear-gradient(90deg, #3498db 0%, #00c2cb 100%);
--gradient-secondary: linear-gradient(90deg, #2980b9 0%, #009ea6 100%);
--shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
--spacing-section: 5rem 0;
}
* {
box-sizing: border-box;
max-width: 100%;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
padding: 0;
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
line-height: 1.6;
color: var(--text-medium);
background-color: var(--bg-light);
background-image:
linear-gradient(120deg, rgba(0, 120, 212, 0.05) 0%, transparent 40%),
linear-gradient(240deg, rgba(0, 178, 148, 0.05) 0%, transparent 40%);
position: relative;
overflow-x: hidden;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
position: relative;
z-index: 2;
}
/* Header styling */
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95); /* Changed to match --bg-light variable */
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
height: auto;
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-img {
height: 40px;
width: auto;
margin-right: 10px;
}
.logo h1 {
font-size: 1.4rem;
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
/* Navigation styling */
.main-nav ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 1.5rem;
}
.main-nav a {
color: var(--text-medium);
text-decoration: none;
font-weight: 500;
font-size: 0.9rem;
letter-spacing: 0.5px;
padding: 0.5rem 0;
position: relative;
transition: color 0.3s ease;
}
.main-nav a:hover, .main-nav a.active {
color: var(--accent-color);
}
.main-nav a::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background: var(--gradient-primary);
transition: width 0.3s ease;
}
.main-nav a:hover::after, .main-nav a.active::after {
width: 100%;
}
/* Section styling */
.section-container {
padding: 2.5rem;
margin-bottom: 2rem;
background-color: var(--card-bg);
border-radius: 10px;
border: 1px solid rgba(0, 0, 0, 0.05);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
margin-top: 0;
}
.section-container h2 {
color: var(--text-dark);
font-size: 2.2rem;
margin-bottom: 1rem;
position: relative;
display: inline-block;
}
.section-container h2::after {
content: '';
position: absolute;
width: 60px;
height: 3px;
background: var(--gradient-primary);
bottom: -0.75rem;
left: 0;
border-radius: 2px;
}
.section-container h3 {
color: var(--text-dark);
font-size: 1.5rem;
margin-top: 2.5rem;
margin-bottom: 1rem;
position: relative;
padding-left: 1rem;
}
.section-container h3::before {
content: '';
position: absolute;
left: 0;
top: 0.35rem;
height: 1rem;
width: 3px;
background: var(--gradient-primary);
border-radius: 2px;
}
.section-container p {
color: var(--text-light);
margin-bottom: 1.5rem;
line-height: 1.7;
}
/* Service cards */
.service-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.service-card {
background-color: var(--card-bg);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 10px;
padding: 2rem;
position: relative;
transition: all 0.3s ease;
box-shadow: var(--shadow);
}
.service-card:hover {
transform: translateY(-10px);
box-shadow: var(--shadow);
}
.service-card h3 {
color: var(--text-dark);
font-size: 1.4rem;
margin-top: 0;
padding-left: 0;
}
.service-card h3::before {
display: none;
}
.service-card p {
color: var(--text-light);
}
/* Language Switcher - Updated to match About Us page */
.language-switcher {
display: flex;
gap: 0.75rem;
}
.language-switcher button {
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
color: var(--text-medium);
padding: 0.4rem 0.75rem;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.language-switcher button.active,
.language-switcher button:hover {
background: var(--gradient-primary);
border-color: transparent;
}
/* Remove Stars Background */
.stars {
display: none;
}
/* Footer styling */
footer {
background-color: var(--text-dark);
padding: 2rem 0;
border-top: 1px solid rgba(0, 0, 0, 0.05);
text-align: center;
color: #aaaaaa;
margin-top: 3rem;
}
main {
padding-top: 8rem;
margin-top: 0;
}
/* Mobile styles */
@media (max-width: 768px) {
.main-nav {
display: none;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: rgba(255, 255, 255, 0.95);
padding: 1rem 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.main-nav.active {
display: block;
}
.main-nav ul {
flex-direction: column;
gap: 0;
}
.main-nav li {
width: 100%;
text-align: center;
padding: 0.5rem 0;
margin: 0;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.main-nav li:last-child {
border-bottom: none;
}
.main-nav a {
display: block;
padding: 0.5rem 0;
}
.section-container {
padding: 1.5rem;
}
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1 data-i18n="roof_drone">Luftglanz</h1>
</div>
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="../index.html" data-i18n="nav_home">Home</a></li>
<li><a href="services.html" class="active" data-i18n="nav_services">Services</a></li>
<li><a href="../index.html#how-it-works" data-i18n="nav_how_it_works">How It Works</a></li>
<li><a href="../index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="../index.html#contact" data-i18n="nav_contact">Contact</a></li>
<li><a href="products.html" data-i18n="nav_products">Products</a></li>
<li><a href="gallery.html" data-i18n="nav_gallery">Gallery</a></li>
</ul>
</nav>
</div>
</div>
</header>
<main class="container">
<section class="hero">
<div class="container">
<div class="hero-content">
<h2 data-i18n="services_hero_title">Professional <span>Drone Roof Cleaning</span> Services</h2>
<p data-i18n="services_hero_subtitle">Our specialized drones deliver thorough cleaning with advanced technology</p>
</div>
</div>
</section>
<section class="section-container">
<h2 data-i18n="services_title">Our Services</h2>
<p data-i18n="services_description">We offer professional roof cleaning and maintenance services using cutting-edge drone technology that delivers superior results with enhanced safety.</p>
<div class="service-grid">
<div class="service-card" id="standard">
<h3 data-i18n="service_drone_title">Drone Roof Cleaning</h3>
<p data-i18n="service_drone_description">Unsere Drohnen sind mit speziellen Sprühsystemen ausgestattet, die effektiv Schmutz, Moos und Algen entfernen. Dieser kontaktlose Ansatz beseitigt das Risiko von Ziegelschäden und Arbeiterunfällen, die mit herkömmlichen Dachreinigungsmethoden verbunden sind.</p>
</div>
<div class="service-card" id="inspection">
<h3 data-i18n="service_inspection_detailed_title">Inspection Services</h3>
<p data-i18n="service_inspection_detailed_description">In addition to cleaning, our drones can perform detailed inspections of your roof, identifying potential issues before they become costly repairs. Our high-resolution cameras capture every detail, allowing us to spot missing shingles, damaged flashing, or other concerns that might be missed by the naked eye.</p>
</div>
<div class="service-card" id="maintenance">
<h3 data-i18n="service_maintenance_title">Maintenance Packages</h3>
<p data-i18n="service_maintenance_description">We offer various maintenance packages to keep your roof in top condition year-round, ensuring longevity and aesthetic appeal. Regular maintenance can extend your roof's lifespan by up to 25% while preventing costly repairs. Our packages can be customized to fit your specific roofing needs and budget.</p>
</div>
</div>
<h3 data-i18n="premium_services_title">Premium Add-On Services</h3>
<p data-i18n="premium_services_description">Beyond our core services, we offer specialized treatments including waterproofing applications, UV-protective coatings, and thermal scanning to identify potential heat leaks. These additional services help maximize your roof's performance and energy efficiency while extending its overall lifespan.</p>
<h3 data-i18n="environmental_approach_title">Unser Effizienzkonzept</h3>
<p data-i18n="environmental_approach_description">Unsere präzise Drohnenanwendungsmethode reduziert den Wasserverbrauch im Vergleich zu herkömmlichen Hochdruckreinigungstechniken um bis zu 60%. Dies bedeutet eine schnellere, sauberere Reinigung in weniger Zeit.</p>
</section>
</main>
<footer>
<div class="container">
<p data-i18n="footer_text">&copy; 2023 Luftglanz. Alle Rechte vorbehalten.</p>
</div>
</footer>
<script src="../js/translations.js"></script>
<script src="../js/language-manager.js"></script>
<script src="../js/drone-image-animation.js"></script>
<script>
// Smooth scrolling
document.addEventListener('DOMContentLoaded', function() {
const navLinks = document.querySelectorAll('a[href^="#"]');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
if(this.getAttribute('href') !== '#') {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetElement = document.querySelector(targetId);
if (targetElement) {
window.scrollTo({
top: targetElement.offsetTop - 100,
behavior: 'smooth'
});
history.pushState(null, null, targetId);
}
}
});
});
});
</script>
</body>
</html>

View File

@@ -0,0 +1,24 @@
<header>
<div class="container">
<div class="header-content">
<div class="logo">
<a href="../index.html" style="text-decoration: none; display: flex; align-items: center;">
<img src="../images/favicon_logo.png" alt="Luftglanz Logo" class="logo-img">
<h1 data-i18n="roof_drone">Luftglanz</h1>
</a>
</div>
<nav class="main-nav" id="mainNav">
<ul>
<li><a href="../index.html" data-i18n="nav_home">Home</a></li>
<li><a href="../index.html#services" data-i18n="nav_services">Services</a></li>
<li><a href="../index.html#how-it-works" data-i18n="nav_how_it_works">How It Works</a></li>
<li><a href="../index.html#faq" data-i18n="nav_faq">FAQ</a></li>
<li><a href="../index.html#contact" data-i18n="nav_contact">Contact</a></li>
<li><a href="products.html" data-i18n="nav_products">Products</a></li>
<li><a href="gallery.html" data-i18n="nav_gallery">Gallery</a></li>
</ul>
</nav>
</div>
</div>
</header>

12
html/drone/phptest.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
header('Content-Type: text/html');
echo "<html><body>";
echo "<h1>PHP Test</h1>";
echo "<p>PHP is " . (function_exists('mail') ? 'working' : 'not working') . "!</p>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "</p>";
echo "<pre>PHP Info:\n\n";
phpinfo();
echo "</pre>";
echo "</body></html>";
?>

200
html/drone/process-form.php Normal file
View File

@@ -0,0 +1,200 @@
<?php
// Maximum error reporting and debugging
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
// Store logs in a variable to display on the page
$debug_logs = [];
function debug_log($message) {
global $debug_logs;
$debug_logs[] = $message;
error_log($message);
}
// Enhanced error logging settings
ini_set('log_errors', 1);
ini_set('error_log', 'form_errors.log');
debug_log("Log file location: " . ini_get('error_log'));
debug_log("======= New Form Submission Attempt: " . date('Y-m-d H:i:s') . " =======");
debug_log("Request Method: " . $_SERVER['REQUEST_METHOD']);
debug_log("Server Software: " . $_SERVER['SERVER_SOFTWARE']);
debug_log("HTTP_ACCEPT: " . $_SERVER['HTTP_ACCEPT']);
// Debug all inputs received
foreach ($_REQUEST as $key => $value) {
debug_log("Request parameter: $key = $value");
}
// If direct access without POST, offer a helpful message
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
echo "<html><head><title>Form Processing Error</title>";
echo "<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #d33; }
.message { background: #f8f8f8; border: 1px solid #ddd; padding: 15px; margin: 20px 0; }
.button { display: inline-block; padding: 10px 15px; background: #4CAF50; color: white; text-decoration: none; border-radius: 4px; }
</style></head><body>";
echo "<h1>Form Submission Error</h1>";
echo "<div class='message'><p>This page is meant to be accessed via a form submission.</p>";
echo "<p>It appears you're trying to access it directly. Please submit the form instead.</p>";
echo "<p>Current request method: " . $_SERVER['REQUEST_METHOD'] . " (should be POST)</p></div>";
echo "<a href='index.html#contact' class='button'>Go to Contact Form</a>";
echo "<a href='form-test.html' class='button' style='margin-left: 10px;'>Try Test Form</a>";
echo "</body></html>";
exit;
}
// Contact form processing script
$success = false;
$error_message = '';
// Capture PHP warnings
function captureWarning($errno, $errstr, $errfile, $errline) {
debug_log("WARNING [$errno]: $errstr in $errfile:$errline");
return true;
}
set_error_handler("captureWarning", E_WARNING);
// Check if the form was submitted
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// Log all received POST data
debug_log("Form submission received - POST data: " . print_r($_POST, true));
// Get form data and sanitize inputs
$name = filter_input(INPUT_POST, 'name', FILTER_SANITIZE_STRING);
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$phone = filter_input(INPUT_POST, 'phone', FILTER_SANITIZE_STRING);
$message = filter_input(INPUT_POST, 'message', FILTER_SANITIZE_STRING);
$subjectField = filter_input(INPUT_POST, 'subject', FILTER_SANITIZE_STRING);
// Log sanitized inputs
debug_log("Sanitized form data: Name: $name, Email: $email, Phone: $phone, Message length: " . strlen($message));
// Basic validation
if (empty($name) || empty($email) || empty($message) || empty($subjectField)) {
$error_message = 'Bitte füllen Sie alle Pflichtfelder aus.';
debug_log("Validation error: $error_message");
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$error_message = 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
debug_log("Validation error: $error_message");
} else {
// Prepare email content
$to = "monitor@egonetix.de"; // Recipient email address
$subject = $subjectField ?: "Neue Kontaktanfrage von luftglanz.de";
// Only use the message as the email body, but add phone if present
$email_body = $message;
if (!empty($phone)) {
$email_body .= "\n\nTelefonnummer: $phone";
}
// Additional headers
$headers = "From: $name <luftglanz@egonetix.de>\r\n" .
"Reply-To: $email\r\n" .
"X-Mailer: PHP/" . phpversion();
debug_log("Email headers: " . $headers);
debug_log("Preparing to send email to: $to");
debug_log("Email subject: $subject");
debug_log("Email body: $email_body");
try {
// SMTP settings
debug_log("Setting SMTP server to: srvdc01");
ini_set("SMTP", "srvdc01");
debug_log("SMTP after setting: " . ini_get("SMTP"));
debug_log("Setting smtp_port to: 25");
ini_set("smtp_port", "25");
debug_log("smtp_port after setting: " . ini_get("smtp_port"));
debug_log("Setting sendmail_from to: luftglanz@egonetix.de");
ini_set("sendmail_from", "luftglanz@egonetix.de");
debug_log("sendmail_from after setting: " . ini_get("sendmail_from"));
// Set authentication for sending the email
$auth = base64_encode("luftglanz:dDmws12*");
debug_log("Setting SMTP authentication");
ini_set("smtp_auth", $auth);
debug_log("About to call mail() function");
// Try to send the email with debugging
ob_start(); // Start output buffering to capture any output or warnings
$mail_result = mail($to, $subject, $email_body, $headers);
$output = ob_get_clean(); // Get the buffered output
debug_log("mail() function output: " . $output);
debug_log("mail() function result: " . ($mail_result ? "TRUE" : "FALSE"));
if ($mail_result) {
$success = true;
debug_log("Email appears to have been sent successfully");
} else {
$error = error_get_last();
$error_message = 'Es gab ein Problem beim Senden Ihrer Nachricht. Bitte versuchen Sie es später erneut.';
debug_log("Mail function failed. PHP Error: " . ($error ? print_r($error, true) : 'Unknown error'));
}
} catch (Exception $e) {
$error_message = 'Ein Fehler ist aufgetreten: ' . $e->getMessage();
debug_log("Exception caught: " . $e->getMessage());
debug_log("Exception trace: " . $e->getTraceAsString());
}
}
}
// If not successful, display debugging information
if (!$success) {
// Return error with debugging logs for AJAX requests
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
debug_log("Returning JSON response with debug info");
header('Content-Type: application/json');
echo json_encode([
'success' => false,
'message' => $error_message,
'debug_logs' => $debug_logs
]);
exit;
}
// Display debug information directly on the page
echo "<html><head><title>Form Submission Debug</title>";
echo "<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #d33; }
.debug-container { background: #f8f8f8; border: 1px solid #ddd; padding: 15px; margin: 20px 0; }
.debug-log { font-family: monospace; white-space: pre-wrap; background: #333; color: #fff; padding: 10px; max-height: 400px; overflow: auto; }
.error-message { color: #d33; font-weight: bold; margin: 20px 0; }
.button { display: inline-block; padding: 10px 15px; background: #4CAF50; color: white; text-decoration: none; border-radius: 4px; }
</style></head><body>";
echo "<h1>Form Submission Debugging Output</h1>";
echo "<div class='error-message'>Error: $error_message</div>";
echo "<div class='debug-container'>";
echo "<h2>Debug Logs:</h2>";
echo "<div class='debug-log'>";
foreach ($debug_logs as $log) {
echo htmlspecialchars($log) . "\n";
}
echo "</div></div>";
echo "<a href='index.html#contact' class='button'>Back to Contact Form</a>";
echo "</body></html>";
exit;
}
// For successful AJAX requests
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
debug_log("Returning successful JSON response");
header('Content-Type: application/json');
echo json_encode(['success' => true, 'message' => '']);
exit;
}
// For successful non-AJAX requests, redirect back to the form
debug_log("Redirecting to form with success message");
header('Location: index.html?form_success=1#contact');
debug_log("======= End of Form Submission Processing =======\n");
exit;
?>

60
html/drone/send-email.php Normal file
View File

@@ -0,0 +1,60 @@
<?php
// Simple and reliable email handler based on the successful test
$success = false;
$errorMessage = '';
// Log all incoming requests
error_log("Email form submission received at " . date('Y-m-d H:i:s'));
error_log("REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD']);
// Process form data from either GET or POST
$data = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET;
// Sanitize and validate inputs
$name = isset($data['name']) ? htmlspecialchars($data['name']) : '';
$email = isset($data['email']) ? filter_var($data['email'], FILTER_SANITIZE_EMAIL) : '';
$phone = isset($data['phone']) ? htmlspecialchars($data['phone']) : '';
$message = isset($data['message']) ? htmlspecialchars($data['message']) : '';
$subjectField = isset($data['subject']) ? htmlspecialchars($data['subject']) : '';
// Log the received data
error_log("Form data received - Name: $name, Email: $email, Phone: $phone");
// Basic validation
if (empty($name) || empty($email) || empty($message) || empty($subjectField)) {
$errorMessage = 'Bitte füllen Sie alle Pflichtfelder aus.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errorMessage = 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
} else {
// Format the email body
$emailBody = $message;
if (!empty($phone)) {
$emailBody .= "\n\nTelefonnummer: $phone";
}
// Email details
$to = "kontakt@luftglanz.de";
$subject = $subjectField ?: "Neue Kontaktanfrage von $name";
$headers = "From: $name <luftglanz@egonetix.de>\r\n";
$headers .= "Reply-To: $name <$email>\r\n";
$headers .= "X-Mailer: PHP/" . phpversion();
// Send the email using the mail() function that we confirmed works
$success = mail($to, $subject, $emailBody, $headers);
if (!$success) {
$errorMessage = 'Es gab ein Problem beim Senden Ihrer Nachricht. Bitte versuchen Sie es später erneut.';
error_log("Mail sending failed for $email");
} else {
error_log("Email sent successfully to $to from $email");
}
}
// Redirect back to the form with status
if ($success) {
header('Location: index.html?form_success=1#contact');
} else {
header('Location: index.html?form_error=' . urlencode($errorMessage) . '#contact');
}
exit;
?>

View File

@@ -0,0 +1,50 @@
<?php
// Simple form handler with minimal dependencies
$success = false;
$error = '';
// Log information regardless of method
error_log("Request received at " . date('Y-m-d H:i:s'));
error_log("REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD']);
// Handle either GET or POST
$data = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET;
// Check if we have data
if (!empty($data)) {
error_log("Form data received: " . print_r($data, true));
// Get the name from form data
$name = isset($data['name']) ? $data['name'] : 'Unbekannt';
// Prepare message
$message = isset($data['message']) ? $data['message'] : '';
if (!empty($data['phone'])) {
$message .= "\n\nTelefonnummer: " . $data['phone'];
}
// Get the subject from form data
$subjectField = isset($data['subject']) ? $data['subject'] : '';
// Use basic PHP mail() function with correct sender address
$to = "kontakt@luftglanz.de";
$subject = $subjectField ?: "Neue Kontaktanfrage von $name";
$headers = "From: $name <luftglanz@egonetix.de>\r\n" .
"Reply-To: " . ($data['email'] ?? 'luftglanz@egonetix.de') . "\r\n" .
"X-Mailer: PHP/" . phpversion();
try {
$success = mail($to, $subject, $message, $headers);
error_log("Mail sent: " . ($success ? "Success" : "Failed"));
} catch (Exception $e) {
error_log("Mail exception: " . $e->getMessage());
$error = $e->getMessage();
}
}
// Redirect back to index.html instead of showing debug page
header('Location: index.html?form_status=' . ($success ? 'success' : 'error') .
($error ? '&error_message=' . urlencode($error) : ''));
exit;
?>

4
html/drone/test.php Normal file
View File

@@ -0,0 +1,4 @@
<?php
// Simplest possible PHP test file
echo "PHP is working! Current time: " . date('Y-m-d H:i:s');
?>

View File

@@ -0,0 +1 @@
<!-- This file is intentionally left blank. -->

View File

@@ -0,0 +1 @@
<!-- This file is intentionally left blank. -->

View File

@@ -0,0 +1,23 @@
.footer {
background-color: #2c3e50;
color: #ecf0f1;
text-align: center;
padding: 20px 0;
position: relative;
bottom: 0;
width: 100%;
}
.footer a {
color: #ecf0f1;
text-decoration: none;
}
.footer a:hover {
text-decoration: underline;
}
.footer p {
margin: 0;
font-size: 14px;
}

View File

@@ -0,0 +1,45 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
form {
display: flex;
flex-direction: column;
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ccc;
border-radius: 5px;
background-color: #f9f9f9;
}
label {
margin-bottom: 5px;
font-weight: bold;
}
input[type="text"],
input[type="email"],
textarea {
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 16px;
}
input[type="submit"] {
background-color: #4CAF50;
color: white;
padding: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
input[type="submit"]:hover {
background-color: #45a049;
}

View File

@@ -0,0 +1,98 @@
/* Header Component Styles - Updated to match Gallery page */
header {
padding: 1.5rem 0;
background: rgba(232, 233, 234, 0.95); /* Match gallery page background */
backdrop-filter: blur(8px);
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 1000;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}
.header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-img {
height: 40px;
width: auto;
margin-right: 10px;
}
.logo h1 {
font-size: 1.8rem;
font-weight: 700;
margin: 0;
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
filter: brightness(0.9);
}
.main-nav ul {
display: flex;
list-style: none;
padding: 0;
margin: 0;
gap: 1.5rem;
}
.main-nav a {
color: var(--text-dark);
text-decoration: none;
font-weight: 600;
font-size: 1.1rem;
letter-spacing: 0.5px;
padding: 0.5rem 0.8rem;
position: relative;
transition: color 0.3s ease;
}
.main-nav a:hover, .main-nav a.active {
color: var(--primary-color);
}
.main-nav a::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 0;
background: var(--gradient-primary);
transition: width 0.3s ease;
}
.main-nav a:hover::after, .main-nav a.active::after {
width: 100%;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.main-nav {
display: none;
position: fixed;
top: 76px;
left: 0;
width: 100%;
height: auto;
max-height: calc(100vh - 76px);
overflow-y: auto;
background: rgba(232, 233, 234, 0.95); /* Changed to match the header background */
padding: 1rem 0;
z-index: 1000;
}
}

View File

@@ -0,0 +1,38 @@
.hero {
background-image: url('../assets/images/hero-background.jpg');
background-size: cover;
background-position: center;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: white;
text-align: center;
padding: 20px;
}
.hero h1 {
font-size: 3rem;
margin: 0;
}
.hero p {
font-size: 1.5rem;
margin: 10px 0 20px;
}
.hero .cta-button {
background-color: #ffcc00;
color: #333;
padding: 15px 30px;
border: none;
border-radius: 5px;
font-size: 1.2rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
.hero .cta-button:hover {
background-color: #e6b800;
}

View File

@@ -0,0 +1,70 @@
/* Mobile navigation styles */
/* Mobile toggle button */
.mobile-toggle {
display: none;
font-size: 1.75rem;
color: var(--primary-color);
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
z-index: 1000;
}
/* Mobile responsiveness */
@media (max-width: 768px) {
.mobile-toggle {
display: block; /* Show on mobile */
}
.main-nav {
display: none;
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: var(--bg-light);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
z-index: 999;
border-radius: 0 0 10px 10px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.main-nav.active {
display: block;
}
.main-nav ul {
flex-direction: column;
width: 100%;
padding: 1rem 0;
gap: 0;
}
.main-nav li {
margin: 0;
width: 100%;
text-align: center;
}
.main-nav a {
display: block;
padding: 0.75rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
width: 100%;
}
.main-nav a:hover::after,
.main-nav a.active::after {
display: none;
}
/* Ensure footer copyright is visible and properly formatted on mobile */
footer .container p {
text-align: center;
font-size: 0.9rem;
margin: 0;
padding: 1rem 0;
}
}

View File

@@ -0,0 +1,99 @@
body {
margin: 0;
font-family: 'Arial', sans-serif;
line-height: 1.6;
}
header {
background: #4CAF50;
color: #fff;
padding: 20px 0;
text-align: center;
}
h1, h2, h3 {
margin: 0;
}
h1 {
font-size: 2.5em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.5em;
}
main {
padding: 20px;
padding-top: 90px; /* This value should match your header height plus any desired spacing */
}
footer {
background: #333;
color: #fff;
text-align: center;
padding: 10px 0;
position: relative;
bottom: 0;
width: 100%;
}
a {
color: #4CAF50;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
.hero {
background: url('../assets/images/hero-bg.jpg') no-repeat center center/cover;
height: 400px;
display: flex;
align-items: center;
justify-content: center;
color: white;
text-align: center;
margin-top: -30px; /* Adjust to reduce some of the padding for hero sections */
}
.hero h1 {
font-size: 3em;
}
.form-group {
margin-bottom: 15px;
}
input[type="text"],
input[type="email"],
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
background: #4CAF50;
color: white;
border: none;
padding: 10px 15px;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background: #45a049;
}

46
html/drone_bup/css/normalize.css vendored Normal file
View File

@@ -0,0 +1,46 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, footer,
header, hgroup, main, menu, nav, output, ruby,
section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

View File

@@ -0,0 +1,40 @@
<?php
// Simple email test script
echo "<h1>Email Test</h1>";
// Check if mail function exists
if (!function_exists('mail')) {
echo "<p style='color:red'>Error: mail() function is not available!</p>";
echo "<p>Make sure PHP is configured with mail support.</p>";
exit;
}
// Try to send a test email
$to = "monitor@egonetix.de";
$subject = "Test Email from Luftglanz";
$message = "This is a test email sent from " . $_SERVER['SERVER_NAME'] . " at " . date('Y-m-d H:i:s');
$headers = "From: test@" . $_SERVER['SERVER_NAME'] . "\r\n" .
"Reply-To: test@" . $_SERVER['SERVER_NAME'] . "\r\n" .
"X-Mailer: PHP/" . phpversion();
$success = mail($to, $subject, $message, $headers);
if ($success) {
echo "<p style='color:green'>Success! Test email was sent to $to</p>";
} else {
echo "<p style='color:red'>Failed to send email. Check server mail configuration.</p>";
// Check mail configuration
echo "<h2>Mail Configuration:</h2>";
echo "<pre>";
echo "sendmail_path: " . ini_get('sendmail_path') . "\n";
echo "SMTP: " . ini_get('SMTP') . "\n";
echo "smtp_port: " . ini_get('smtp_port') . "\n";
echo "</pre>";
}
// Display PHP info for debugging
echo "<h2>PHP Information:</h2>";
echo "<p>PHP Version: " . phpversion() . "</p>";
echo "<p>Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "</p>";
?>

View File

@@ -0,0 +1,72 @@
<?php
// Alternative form handler using GET method
// This file works around servers that block POST to PHP files
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Log to help with debugging
error_log("Form-GET-Handler received request: " . date('Y-m-d H:i:s'));
error_log("REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD']);
// Process form data from either GET or POST
$formData = $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET;
// Sanitize inputs
$name = isset($formData['name']) ? htmlspecialchars($formData['name']) : '';
$email = isset($formData['email']) ? filter_var($formData['email'], FILTER_SANITIZE_EMAIL) : '';
$phone = isset($formData['phone']) ? htmlspecialchars($formData['phone']) : '';
$message = isset($formData['message']) ? htmlspecialchars($formData['message']) : '';
// Log the received data
error_log("Received data - Name: $name, Email: $email, Phone: $phone");
// Simple validation
$success = false;
$errorMessage = '';
if (empty($name) || empty($email) || empty($message)) {
$errorMessage = 'Bitte füllen Sie alle Pflichtfelder aus.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errorMessage = 'Bitte geben Sie eine gültige E-Mail-Adresse ein.';
} else {
// Construct message for the email
$emailBody = $message;
// Email details
$to = "kontakt@luftglanz.de";
$subject = "Neue Kontaktanfrage von $name";
// Attempt to send email via internal mail function
$mailSent = mail($to, $subject, $emailBody,
"From: $name <luftglanz@egonetix.de>\r\n" .
"Reply-To: $email\r\n" .
"X-Mailer: PHP/" . phpversion()
);
if ($mailSent) {
$success = true;
} else {
$errorMessage = 'Es gab ein Problem beim Senden Ihrer Nachricht. Bitte versuchen Sie es später erneut.';
error_log("Mail sending failed");
}
}
// Handle the response
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
// Ajax request - return JSON
header('Content-Type: application/json');
echo json_encode([
'success' => $success,
'message' => $errorMessage
]);
} else {
// Regular form submission - redirect with status
if ($success) {
header('Location: index.html?form_success=1#contact');
} else {
header('Location: index.html?form_error=' . urlencode($errorMessage) . '#contact');
}
}
?>

View File

@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Test</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
form { background: #f8f8f8; padding: 20px; border-radius: 8px; margin-bottom: 30px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input, textarea { width: 100%; padding: 8px; box-sizing: border-box; }
button { background: #4CAF50; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; }
h2 { margin-top: 30px; color: #333; border-bottom: 1px solid #ddd; padding-bottom: 10px; }
</style>
</head>
<body>
<h1>Form Test - Find a Working Method</h1>
<p>This page tests different form submission methods to find one that works on this server.</p>
<h2>Method 1: Standard POST Form (Original)</h2>
<p>This method often gets blocked with a 405 error on some servers.</p>
<form action="process-form.php" method="POST">
<div class="form-group">
<label for="name1">Name:</label>
<input type="text" id="name1" name="name" value="Test User" required>
</div>
<div class="form-group">
<label for="email1">Email:</label>
<input type="email" id="email1" name="email" value="test@example.com" required>
</div>
<div class="form-group">
<label for="message1">Message:</label>
<textarea id="message1" name="message" rows="4" required>This is a test message using POST method.</textarea>
</div>
<button type="submit">Submit with POST</button>
</form>
<h2>Method 2: GET Method Form</h2>
<p>This method uses GET which may bypass the 405 error.</p>
<form action="form-get-handler.php" method="GET">
<div class="form-group">
<label for="name2">Name:</label>
<input type="text" id="name2" name="name" value="Test User" required>
</div>
<div class="form-group">
<label for="email2">Email:</label>
<input type="email" id="email2" name="email" value="test@example.com" required>
</div>
<div class="form-group">
<label for="phone2">Phone:</label>
<input type="tel" id="phone2" name="phone" value="123-456-7890">
</div>
<div class="form-group">
<label for="message2">Message:</label>
<textarea id="message2" name="message" rows="4" required>This is a test message using GET method.</textarea>
</div>
<button type="submit">Submit with GET</button>
</form>
<p><a href="index.html">Back to Main Page</a></p>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Some files were not shown because too many files have changed in this diff Show More