commit ac7088c5caac3ee891e4585c0bbd82e1a9c197e1 Author: Luftglanz Date: Tue Jul 8 11:54:37 2025 +0200 Initial commit: Luftglanz drone website with integrated AI chat assistant Features: - Complete Luftglanz drone cleaning website - AI chat assistant integrated with OpenAI API - Expert product advice for AGO Quart and Mellerud cleaning products - Formal German language support (Sie form) - Secure PHP backend for API calls - Responsive design with mobile support - Product-specific knowledge base - Safety statements from manufacturers - Multi-page integration (index, products, services, contact) Technical components: - AI chat widget (js/ai-chat.js) - Chat styling (css/components/ai-chat.css) - Backend API (ai-chat-api.php) - Product knowledge base with detailed specifications - Demo and documentation files diff --git a/AI-CHAT-README.md b/AI-CHAT-README.md new file mode 100644 index 0000000..a248557 --- /dev/null +++ b/AI-CHAT-README.md @@ -0,0 +1,191 @@ +# Luftglanz AI Chat System + +Ein intelligenter KI-Assistent für die Luftglanz Website, der Kunden bei Fragen zu Drohnen-Reinigungsservices und AGO Quart Produkten unterstützt. + +## 🚀 Features + +- **Intelligente Antworten**: Basiert auf OpenAI GPT-3.5-turbo mit spezifischem Produktwissen +- **Deutsche Sprache**: Kommuniziert natürlich auf Deutsch +- **Produktwissen**: Umfassendes Wissen über AGO Quart Grünbelagentferner +- **Responsive Design**: Funktioniert perfekt auf Desktop und Mobile +- **Sicher**: Unterstützt sowohl PHP-Backend als auch direkte API-Aufrufe +- **Persistent**: Speichert Konversationshistorie lokal + +## 📁 Dateien + +### JavaScript +- `js/ai-chat.js` - Haupt-Chat-System mit Frontend-Logik +- `css/components/ai-chat.css` - Styling für den Chat-Widget + +### PHP Backend +- `ai-chat-api.php` - Sicherer PHP-Endpoint für OpenAI API-Aufrufe + +### Demo & Test +- `ai-chat-demo.html` - Demo-Seite zum Testen des Chat-Systems + +## 🛠️ Installation + +### 1. Dateien einbinden + +Fügen Sie diese Zeilen in den `` Ihrer HTML-Seiten ein: +```html + +``` + +Fügen Sie vor dem schließenden `` Tag ein: +```html + +``` + +### 2. API-Konfiguration + +**Option A: PHP Backend (Empfohlen)** +- Der API-Key ist sicher im PHP-Backend gespeichert +- Keine weitere Konfiguration nötig + +**Option B: Direkte API-Aufrufe** +- API-Key ist im JavaScript sichtbar (weniger sicher) +- Für Entwicklung und Tests geeignet + +### 3. Server-Anforderungen + +Für PHP Backend: +- PHP 7.0+ +- cURL-Erweiterung aktiviert +- Internetverbindung für OpenAI API + +## 💡 Verwendung + +### Automatische Initialisierung +Der Chat wird automatisch geladen, wenn die Seite vollständig geladen ist: + +```javascript +document.addEventListener('DOMContentLoaded', () => { + window.aiChat = new AIChat(); +}); +``` + +### Manueller Test +Öffnen Sie `ai-chat-demo.html` in Ihrem Browser, um das System zu testen. + +## 🎯 Produktwissen + +Der KI-Assistent verfügt über umfassendes Wissen zu: + +### AGO Quart Grünbelagentferner +- **Funktion**: Hochwirksam gegen Algen, Grünbeläge und Flechten +- **Wirkdauer**: Bis zu 18 Monate aktive Schutzwirkung +- **Anwendung**: Nur auf trockene Flächen, 8h kein Regen +- **Reichweite**: 0,5L Flasche für ca. 100m² (19,90 €) +- **Mischungsverhältnisse**: + - Leichte Beläge: 1:20 mit Wasser + - Starke Beläge: 1:10 mit Wasser + +### Drohnen-Services +- Professionelle Anwendung mit Drohnen +- Präzise, gleichmäßige Verteilung +- Sichere Anwendung auf steilen Dächern +- Kosteneffektiv vs. Gerüstaufbau + +### Sicherheitsinformationen +- Korrekte Anwendungshinweise +- Sicherheitsdatenblätter +- Umweltschutz-Aspekte + +## 🔧 Anpassungen + +### Produktwissen erweitern +Bearbeiten Sie die `productKnowledge` Variable in: +- `js/ai-chat.js` (Zeile ~XX) +- `ai-chat-api.php` (Zeile ~XX) + +### Styling anpassen +Ändern Sie Farben und Design in `css/components/ai-chat.css`: + +```css +:root { + --primary-color: #2980b9; + --accent-color: #00adb8; + /* Weitere Farb-Variablen... */ +} +``` + +### API-Einstellungen +In `js/ai-chat.js` können Sie anpassen: +- `model`: OpenAI Modell (z.B. 'gpt-4') +- `max_tokens`: Maximale Antwortlänge +- `temperature`: Kreativität der Antworten (0.0 - 1.0) + +## 📱 Mobile Optimierung + +Der Chat ist vollständig responsive: +- Tablet: Kompakte Darstellung +- Smartphone: Vollbild-Chat +- Touch-optimierte Bedienung + +## 🔒 Sicherheit + +### PHP Backend Vorteile: +- API-Key nicht im Frontend sichtbar +- Server-seitige Validierung +- Rate-Limiting möglich +- Logging und Monitoring + +### Best Practices: +- Verwenden Sie immer das PHP Backend in Production +- Überwachen Sie API-Kosten regelmäßig +- Implementieren Sie Rate-Limiting bei Bedarf + +## 🐛 Troubleshooting + +### Chat erscheint nicht +- Prüfen Sie Browser-Konsole auf JavaScript-Fehler +- Stellen Sie sicher, dass alle CSS/JS-Dateien geladen werden +- Testen Sie mit `ai-chat-demo.html` + +### API-Fehler +- Prüfen Sie API-Key Gültigkeit +- Kontrollieren Sie Internetverbindung +- Schauen Sie in Browser-Netzwerk-Tab für Details + +### PHP Backend Probleme +- Prüfen Sie PHP-Fehlerlog +- Stellen Sie sicher, dass cURL aktiviert ist +- Testen Sie API-Key mit direktem curl-Aufruf + +## 📊 Monitoring + +### API-Kosten überwachen +- Besuchen Sie das OpenAI Dashboard +- Setzen Sie Ausgabenlimits +- Überwachen Sie monatliche Nutzung + +### Performance +- Chat-Antwortzeiten überwachen +- Conversation History begrenzen (aktuell: 10 Nachrichten) +- Cache häufige Fragen falls nötig + +## 🔄 Updates + +### Produktwissen aktualisieren +1. Bearbeiten Sie die Knowledge-Base in beiden Dateien +2. Testen Sie mit Demo-Seite +3. Deployen Sie auf Live-System + +### OpenAI API Updates +- Überwachen Sie OpenAI Changelogs +- Testen Sie neue Modelle in Development +- Aktualisieren Sie Prompts bei Bedarf + +## 💬 Support + +Bei Fragen oder Problemen: +1. Prüfen Sie die Browser-Konsole +2. Testen Sie mit der Demo-Seite +3. Kontaktieren Sie das Entwicklungsteam + +--- + +**Version**: 1.0 +**Letztes Update**: Juli 2025 +**Kompatibilität**: Alle modernen Browser, PHP 7.0+ diff --git a/ai-chat-api.php b/ai-chat-api.php new file mode 100644 index 0000000..43931d9 --- /dev/null +++ b/ai-chat-api.php @@ -0,0 +1,255 @@ + 'Method not allowed']); + exit; +} + +// Get request data +$input = json_decode(file_get_contents('php://input'), true); + +if (!isset($input['message']) || empty(trim($input['message']))) { + http_response_code(400); + echo json_encode(['error' => 'Message is required']); + exit; +} + +$userMessage = trim($input['message']); +$conversationHistory = $input['history'] ?? []; + +// OpenAI API configuration +$apiKey = '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'; +$apiUrl = 'https://api.openai.com/v1/chat/completions'; + +// Product knowledge base +$productKnowledge = " +LUFTGLANZ PRODUCT KNOWLEDGE BASE: + +1. COMPANY: Luftglanz - Professional drone-based roof and surface cleaning services + +2. MAIN PRODUCTS: + +A) AGO QUART GRÜNBELAGENTFERNER (Green algae and lichen remover) + +PRODUCT DETAILS: +- Name: AGO Quart Algenentferner, Grünbelag- und Flechtenentferner +- Function: Highly effective against algae, green deposits, and lichens +- Active period: Up to 18 months continuous protection +- Application: Apply only on dry surfaces, no rain for 8 hours after application +- Coverage: 0.5L bottle covers approximately 100m² surface area +- Price: 19,90 € (39,80 € per liter) + +MIXING RATIOS: +- Light deposits: 1:20 with water +- Heavy deposits (black algae, lichens): 1:10 with water + +INGREDIENTS: Water, Benzalkonium-Chlorides, Tideceth-8, Cocamidopropyl-Betaine, Isopropyl-Alcohol + +APPLICATION PROCESS: +1. Apply only on dry surfaces +2. Ensure no rain for minimum 8 hours after application +3. Works on surface level, not suitable for moss removal in joints/cracks +4. Cleaning duration depends on contamination severity +5. Active for up to 18 months, provides thorough cleaning and prevents recontamination +6. Never rinse with water after application - product must bind with surface + +EFFECTIVENESS TIMELINE: +- Green deposits: Disappear within days +- Red/black algae: Several months for significant improvement +- Heavy lichen contamination: Up to 2 years for complete removal + +SUITABLE SURFACES: All waterproof surfaces (roofs, terraces, facades, etc.) + +BENEFITS: +- No high-pressure cleaning needed +- No scrubbing required +- Chlorine and acid-free +- Gentle cleaning without material damage +- Long-term protection against recontamination +- Self-cleaning effect through weather interaction + +B) MELLERUD PHOTOVOLTAIK & SOLARANLAGEN REINIGER KONZENTRAT + +PRODUCT DETAILS: +- Name: Mellerud Photovoltaik & Solaranlagen Reiniger Konzentrat +- Size: 0.5L concentrate +- Price: 8,99 € (17,98 € per liter) +- Function: Gentle and effective cleaning solution specifically for solar panels and photovoltaic systems +- Removes: Dust, general dirt, soot residues, fine dust, and pollen +- Special features: Streak-free, residue-free cleaning with water-repellent protection + +MIXING RATIO: +- Standard cleaning: 1:20 with water (ideally distilled water) + +INGREDIENTS & SPECS: +- Composition: Aqueous mixture of surfactants, complexing agents, and effectiveness enhancers +- pH value: 5.70 - 6.50 +- Density: 1.020 kg/l + +APPLICATION PROCESS: +1. Remove coarse dirt, leaves, and debris by lightly sweeping (avoid intensive scrubbing) +2. Mix concentrate 1:20 with water (preferably distilled water) +3. Apply carefully with specialized solar/PV equipment, soft sponge, cloth, or squeegee +4. Allow to work briefly +5. Rinse thoroughly with clear water (preferably distilled water) + +SUITABLE EQUIPMENT: +- Specialized equipment for solar and PV systems +- Particularly soft sponges +- Soft cloths or squeegees +- Avoid intensive scrubbing to prevent damage + +BENEFITS: +- Improves performance and lifespan of solar systems +- Creates sustainable protection against re-contamination through water-repellent effect +- Gentle cleaning without damage to sensitive modules +- Streak-free and residue-free results +- Suitable for all types of solar and PV panels + +SAFETY INFORMATION: +- Causes severe eye irritation +- Keep out of reach of children +- Wash hands thoroughly after use +- In case of eye contact: rinse gently with water for several minutes +- Remove contact lenses if present and continue rinsing +- If eye irritation persists: seek medical advice + +WHY DISTILLED WATER? +- Minimizes residues and promotes streak-free cleaning +- Supports optimal care of PV and solar systems + +DRONE SERVICES: +- Professional application using drones for hard-to-reach areas +- Precise, even distribution of both AGO Quart and Mellerud products +- Safe application on steep roofs and high buildings +- Cost-effective compared to traditional scaffolding methods +- Specialized equipment for solar panel cleaning via drone + +SAFETY INFORMATION: +- Use biocides safely +- Always read labeling and product information +- AGO Quart: Causes skin irritation (H315), serious eye damage (H318), very toxic to aquatic organisms (H410) +- Mellerud: Causes severe eye irritation, keep away from children + +CONTACT INFORMATION: +AGO: AGO Sauberkeit für Haus und Garten! GmbH & Co. KG, Brilloner Str. 39, 59909 Bestwig +Phone: 02904 / 98 98 98-0, Email: info@agoshop.de + +Mellerud: MELLERUD CHEMIE GMBH, Bernhard-Röttgen-Waldweg 20, 41379 Brüggen +Phone: +49 (0) 2163 / 950 90 999, Email: shop@mellerud.de, experten-service@mellerud.de +"; + +$systemPrompt = "Du bist ein hilfsreicher KI-Assistent für Luftglanz, ein Unternehmen für professionelle Drohnen-basierte Dach- und Oberflächenreinigung. + +WICHTIGE ANWEISUNGEN: +- Antworte immer auf Deutsch +- Verwende IMMER die höfliche Anrede \"Sie\" (niemals \"du\" oder \"dir\") +- Sei freundlich, professionell und hilfsbereit +- Nutze AUSSCHLIESSLICH die bereitgestellten Produktinformationen für präzise Antworten +- Gib konkrete Produktdetails, Preise und Anwendungshinweise aus der Wissensbasis +- Bei Fragen zu Solaranlagen: Erkläre dass SOWOHL AGO Quart ALS AUCH Mellerud geeignet sind +- AGO Quart: Laut Hersteller sicher für Solar-Platten, wird von Reinigungsfirmen eingesetzt +- Mellerud: Speziell für Solaranlagen entwickelt mit schonender Formel +- Bei Fragen zu Algen/Grünbelägen auf Dächern, empfehle AGO Quart +- Erwähne die Vorteile der Drohnen-Anwendung wenn relevant +- Erkläre Unterschiede zwischen den Produkten detailliert basierend auf der Wissensbasis +- Gib konkrete Mischungsverhältnisse und Preise an + +WICHTIG: Du kennst diese BEIDEN spezifischen Produkte sehr genau: +1. AGO Quart (19,90€) - für Algen, Grünbeläge, Flechten auf Dächern/Terrassen +2. Mellerud Photovoltaik Reiniger (8,99€) - speziell für Solaranlagen + +PRODUKTWISSEN: " . $productKnowledge . " + +Beantworte Fragen zu: +- Unterschiede zwischen AGO Quart und Mellerud Produkten +- Spezifische Anwendung für Solaranlagen vs. Dächer +- Konkrete Preise, Mischungsverhältnisse und Reichweiten +- Drohnen-Anwendung beider Produkte +- Sicherheitshinweise für beide Produkte +- Warum destilliertes Wasser bei Mellerud wichtig ist +- Kontaktdaten beider Hersteller"; + +// Build messages array +$messages = [ + ['role' => 'system', 'content' => $systemPrompt] +]; + +// Add conversation history (last 10 messages for context) +$recentHistory = array_slice($conversationHistory, -10); +foreach ($recentHistory as $msg) { + $messages[] = $msg; +} + +// Add current user message +$messages[] = ['role' => 'user', 'content' => $userMessage]; + +// Prepare OpenAI request +$data = [ + 'model' => 'gpt-3.5-turbo', + 'messages' => $messages, + 'max_tokens' => 500, + 'temperature' => 0.7, + 'presence_penalty' => 0.1, + 'frequency_penalty' => 0.1 +]; + +// Make API call to OpenAI +$ch = curl_init(); +curl_setopt($ch, CURLOPT_URL, $apiUrl); +curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); +curl_setopt($ch, CURLOPT_POST, true); +curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); +curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json', + 'Authorization: Bearer ' . $apiKey +]); + +$response = curl_exec($ch); +$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); +curl_close($ch); + +if ($httpCode !== 200) { + http_response_code(500); + echo json_encode(['error' => 'OpenAI API error: ' . $httpCode]); + exit; +} + +$responseData = json_decode($response, true); + +if (!isset($responseData['choices'][0]['message']['content'])) { + http_response_code(500); + echo json_encode(['error' => 'Invalid response from OpenAI']); + exit; +} + +$assistantMessage = $responseData['choices'][0]['message']['content']; + +// Return successful response +echo json_encode([ + 'success' => true, + 'message' => $assistantMessage, + 'usage' => $responseData['usage'] ?? null +]); +?> diff --git a/ai-chat-demo.html b/ai-chat-demo.html new file mode 100644 index 0000000..cc6a264 --- /dev/null +++ b/ai-chat-demo.html @@ -0,0 +1,177 @@ + + + + + + AI Chat Test - Luftglanz + + + + + +
+

🤖 Luftglanz KI-Assistent Demo

+ +
+

Willkommen zum KI-Assistenten Test!

+

Dieser intelligente Chatbot wurde speziell für Luftglanz entwickelt und verfügt über umfassendes Wissen über:

+
    +
  • AGO Quart Grünbelagentferner - Produktdetails, Anwendung, Dosierung
  • +
  • Drohnen-Reinigungsservices - Vorteile, Anwendungsgebiete, Preise
  • +
  • Oberflächenreinigung - Dächer, Terrassen, Fassaden
  • +
  • Sicherheitshinweise - Korrekte Anwendung und Vorsichtsmaßnahmen
  • +
+
+ +
+

Testen Sie den Assistenten mit diesen Beispielfragen:

+ + + + + + +
+ +
+
+

🎯 Präzise Antworten

+

Basiert auf echten Produktdaten und technischen Spezifikationen

+
+
+

🇩🇪 Auf Deutsch

+

Kommuniziert natürlich in deutscher Sprache

+
+
+

💡 Intelligent

+

Versteht Kontext und gibt passende Empfehlungen

+
+
+

📱 Responsiv

+

Funktioniert perfekt auf allen Geräten

+
+
+ +
+

Der Chat-Button erscheint unten rechts auf der Seite!

+

Klicken Sie darauf, um mit dem KI-Assistenten zu sprechen.

+
+
+ + + + + diff --git a/assets/fonts b/assets/fonts new file mode 100644 index 0000000..5f2e0c2 --- /dev/null +++ b/assets/fonts @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/videos b/assets/videos new file mode 100644 index 0000000..5f2e0c2 --- /dev/null +++ b/assets/videos @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/css/components/ai-chat.css b/css/components/ai-chat.css new file mode 100644 index 0000000..f9cc97a --- /dev/null +++ b/css/components/ai-chat.css @@ -0,0 +1,300 @@ +/* AI Chat Widget Styles */ + +/* Chat Toggle Button */ +#ai-chat-toggle { + position: fixed; + bottom: 20px; + right: 20px; + background: linear-gradient(135deg, #2980b9, #00adb8); + color: white; + border-radius: 50px; + padding: 15px 20px; + cursor: pointer; + box-shadow: 0 4px 20px rgba(41, 128, 185, 0.3); + display: flex; + align-items: center; + gap: 10px; + font-weight: 600; + font-size: 14px; + transition: all 0.3s ease; + z-index: 1000; + border: none; + font-family: 'Inter', 'Segoe UI', Arial, sans-serif; +} + +#ai-chat-toggle:hover { + transform: translateY(-2px); + box-shadow: 0 6px 25px rgba(41, 128, 185, 0.4); +} + +#ai-chat-toggle.active { + background: linear-gradient(135deg, #1f6797, #00858e); + transform: scale(0.95); +} + +#ai-chat-toggle svg { + width: 20px; + height: 20px; +} + +/* Chat Window */ +#ai-chat-window { + position: fixed; + bottom: 90px; + right: 20px; + width: 350px; + height: 500px; + background: white; + border-radius: 15px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15); + transform: translateY(100%) scale(0.8); + opacity: 0; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 999; + overflow: hidden; + font-family: 'Inter', 'Segoe UI', Arial, sans-serif; + border: 1px solid rgba(0, 0, 0, 0.1); +} + +#ai-chat-window.open { + transform: translateY(0) scale(1); + opacity: 1; +} + +/* Chat Header */ +.chat-header { + background: linear-gradient(135deg, #2980b9, #00adb8); + color: white; + padding: 15px 20px; + display: flex; + justify-content: space-between; + align-items: center; + border-radius: 15px 15px 0 0; +} + +.chat-header h3 { + margin: 0; + font-size: 16px; + font-weight: 600; +} + +#chat-close { + background: none; + border: none; + color: white; + font-size: 24px; + cursor: pointer; + padding: 0; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: background 0.2s ease; +} + +#chat-close:hover { + background: rgba(255, 255, 255, 0.2); +} + +/* Chat Messages */ +.chat-messages { + height: 360px; + overflow-y: auto; + padding: 15px; + display: flex; + flex-direction: column; + gap: 12px; + background: #f8f9fa; +} + +.chat-messages::-webkit-scrollbar { + width: 6px; +} + +.chat-messages::-webkit-scrollbar-track { + background: transparent; +} + +.chat-messages::-webkit-scrollbar-thumb { + background: #ccc; + border-radius: 3px; +} + +.chat-messages::-webkit-scrollbar-thumb:hover { + background: #999; +} + +/* Message Styling */ +.message { + display: flex; + margin-bottom: 8px; +} + +.message-content { + max-width: 80%; + padding: 12px 16px; + border-radius: 18px; + font-size: 14px; + line-height: 1.4; + word-wrap: break-word; +} + +.user-message { + justify-content: flex-end; +} + +.user-message .message-content { + background: linear-gradient(135deg, #2980b9, #00adb8); + color: white; + border-bottom-right-radius: 6px; +} + +.bot-message { + justify-content: flex-start; +} + +.bot-message .message-content { + background: white; + color: #333; + border: 1px solid #e1e5e9; + border-bottom-left-radius: 6px; +} + +/* Typing Indicator */ +.typing .message-content { + padding: 16px; +} + +.typing-dots { + display: flex; + gap: 4px; + align-items: center; +} + +.typing-dots span { + width: 8px; + height: 8px; + background: #666; + border-radius: 50%; + animation: typing 1.4s infinite ease-in-out; +} + +.typing-dots span:nth-child(1) { + animation-delay: -0.32s; +} + +.typing-dots span:nth-child(2) { + animation-delay: -0.16s; +} + +@keyframes typing { + 0%, 80%, 100% { + opacity: 0.3; + transform: scale(0.8); + } + 40% { + opacity: 1; + transform: scale(1); + } +} + +/* Chat Input */ +.chat-input-container { + padding: 15px; + background: white; + border-top: 1px solid #e1e5e9; + display: flex; + gap: 10px; + align-items: flex-end; +} + +#chat-input { + flex: 1; + border: 1px solid #ddd; + border-radius: 20px; + padding: 12px 16px; + font-size: 14px; + resize: none; + outline: none; + font-family: inherit; + transition: border-color 0.2s ease; +} + +#chat-input:focus { + border-color: #2980b9; +} + +#chat-send { + background: linear-gradient(135deg, #2980b9, #00adb8); + border: none; + border-radius: 50%; + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; + color: white; +} + +#chat-send:hover { + transform: scale(1.05); + box-shadow: 0 4px 12px rgba(41, 128, 185, 0.3); +} + +#chat-send:active { + transform: scale(0.95); +} + +/* Mobile Responsiveness */ +@media (max-width: 768px) { + #ai-chat-window { + width: calc(100vw - 40px); + height: 450px; + bottom: 80px; + right: 20px; + left: 20px; + } + + #ai-chat-toggle { + bottom: 15px; + right: 15px; + padding: 12px 16px; + font-size: 13px; + } + + #ai-chat-toggle span { + display: none; + } + + .chat-messages { + height: 320px; + } +} + +@media (max-width: 480px) { + #ai-chat-window { + width: calc(100vw - 20px); + height: 400px; + bottom: 70px; + right: 10px; + left: 10px; + } + + #ai-chat-toggle { + bottom: 10px; + right: 10px; + } + + .chat-messages { + height: 280px; + padding: 10px; + } + + .chat-input-container { + padding: 10px; + } +} diff --git a/css/components/footer.css b/css/components/footer.css new file mode 100644 index 0000000..1baa1d3 --- /dev/null +++ b/css/components/footer.css @@ -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; +} \ No newline at end of file diff --git a/css/components/forms.css b/css/components/forms.css new file mode 100644 index 0000000..bb7ef43 --- /dev/null +++ b/css/components/forms.css @@ -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; +} \ No newline at end of file diff --git a/css/components/header.css b/css/components/header.css new file mode 100644 index 0000000..45bbf7c --- /dev/null +++ b/css/components/header.css @@ -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; + } +} \ No newline at end of file diff --git a/css/components/hero.css b/css/components/hero.css new file mode 100644 index 0000000..bf7750e --- /dev/null +++ b/css/components/hero.css @@ -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; +} \ No newline at end of file diff --git a/css/components/mobile-menu.css b/css/components/mobile-menu.css new file mode 100644 index 0000000..7c4efad --- /dev/null +++ b/css/components/mobile-menu.css @@ -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; + } +} diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..8f8fdc8 --- /dev/null +++ b/css/main.css @@ -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; +} \ No newline at end of file diff --git a/css/normalize.css b/css/normalize.css new file mode 100644 index 0000000..34581b8 --- /dev/null +++ b/css/normalize.css @@ -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; +} \ No newline at end of file diff --git a/email-test.php b/email-test.php new file mode 100644 index 0000000..19d8fd6 --- /dev/null +++ b/email-test.php @@ -0,0 +1,40 @@ +Email Test"; + +// Check if mail function exists +if (!function_exists('mail')) { + echo "

Error: mail() function is not available!

"; + echo "

Make sure PHP is configured with mail support.

"; + 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 "

Success! Test email was sent to $to

"; +} else { + echo "

Failed to send email. Check server mail configuration.

"; + + // Check mail configuration + echo "

Mail Configuration:

"; + echo "
";
+    echo "sendmail_path: " . ini_get('sendmail_path') . "\n";
+    echo "SMTP: " . ini_get('SMTP') . "\n";
+    echo "smtp_port: " . ini_get('smtp_port') . "\n";
+    echo "
"; +} + +// Display PHP info for debugging +echo "

PHP Information:

"; +echo "

PHP Version: " . phpversion() . "

"; +echo "

Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "

"; +?> diff --git a/extract.py b/extract.py new file mode 100755 index 0000000..8d9f823 --- /dev/null +++ b/extract.py @@ -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: " 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() + + diff --git a/form-get-handler.php b/form-get-handler.php new file mode 100644 index 0000000..a0e7fb4 --- /dev/null +++ b/form-get-handler.php @@ -0,0 +1,76 @@ +\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'); + } +} +?> diff --git a/form-test.html b/form-test.html new file mode 100644 index 0000000..530e612 --- /dev/null +++ b/form-test.html @@ -0,0 +1,63 @@ + + + + + + Form Test + + + +

Form Test - Find a Working Method

+

This page tests different form submission methods to find one that works on this server.

+ +

Method 1: Standard POST Form (Original)

+

This method often gets blocked with a 405 error on some servers.

+
+
+ + +
+
+ + +
+
+ + +
+ +
+ +

Method 2: GET Method Form

+

This method uses GET which may bypass the 405 error.

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +

Back to Main Page

+ + diff --git a/images/animation_background.png b/images/animation_background.png new file mode 100644 index 0000000..3c45a1a Binary files /dev/null and b/images/animation_background.png differ diff --git a/images/animation_background1.png b/images/animation_background1.png new file mode 100644 index 0000000..ee25eda Binary files /dev/null and b/images/animation_background1.png differ diff --git a/images/drone_from_above.jpg b/images/drone_from_above.jpg new file mode 100644 index 0000000..8dad082 Binary files /dev/null and b/images/drone_from_above.jpg differ diff --git a/images/drone_in_action1.jpg b/images/drone_in_action1.jpg new file mode 100644 index 0000000..06e4052 Binary files /dev/null and b/images/drone_in_action1.jpg differ diff --git a/images/drone_in_action2.jpg b/images/drone_in_action2.jpg new file mode 100644 index 0000000..43e4977 Binary files /dev/null and b/images/drone_in_action2.jpg differ diff --git a/images/drone_in_action_solar1.jpg b/images/drone_in_action_solar1.jpg new file mode 100644 index 0000000..aea22f5 Binary files /dev/null and b/images/drone_in_action_solar1.jpg differ diff --git a/images/drone_in_action_solar2.jpg b/images/drone_in_action_solar2.jpg new file mode 100644 index 0000000..6d13cd7 Binary files /dev/null and b/images/drone_in_action_solar2.jpg differ diff --git a/images/drone_realistic1.png b/images/drone_realistic1.png new file mode 100644 index 0000000..74c1ee1 Binary files /dev/null and b/images/drone_realistic1.png differ diff --git a/images/drone_realistic2.png b/images/drone_realistic2.png new file mode 100644 index 0000000..dab7df4 Binary files /dev/null and b/images/drone_realistic2.png differ diff --git a/images/drone_realistic3.png b/images/drone_realistic3.png new file mode 100644 index 0000000..b53376a Binary files /dev/null and b/images/drone_realistic3.png differ diff --git a/images/drone_realistic4.png b/images/drone_realistic4.png new file mode 100644 index 0000000..8b089c9 Binary files /dev/null and b/images/drone_realistic4.png differ diff --git a/images/drone_realistic5.png b/images/drone_realistic5.png new file mode 100644 index 0000000..659f1c7 Binary files /dev/null and b/images/drone_realistic5.png differ diff --git a/images/drone_realistic6.png b/images/drone_realistic6.png new file mode 100644 index 0000000..1984ea3 Binary files /dev/null and b/images/drone_realistic6.png differ diff --git a/images/drone_transparrent_background.png b/images/drone_transparrent_background.png new file mode 100644 index 0000000..e5db95d Binary files /dev/null and b/images/drone_transparrent_background.png differ diff --git a/images/favicon_logo.png b/images/favicon_logo.png new file mode 100644 index 0000000..f12965d Binary files /dev/null and b/images/favicon_logo.png differ diff --git a/images/out_team.jpg b/images/out_team.jpg new file mode 100644 index 0000000..7ebbbe6 Binary files /dev/null and b/images/out_team.jpg differ diff --git a/images/products/ago-quart-05l.jpg b/images/products/ago-quart-05l.jpg new file mode 100644 index 0000000..31686bb Binary files /dev/null and b/images/products/ago-quart-05l.jpg differ diff --git a/images/products/terrase_before_after.jpg b/images/products/terrase_before_after.jpg new file mode 100644 index 0000000..2d8a4bd Binary files /dev/null and b/images/products/terrase_before_after.jpg differ diff --git a/images/roof.png b/images/roof.png new file mode 100644 index 0000000..b081985 Binary files /dev/null and b/images/roof.png differ diff --git a/images/roof1.png b/images/roof1.png new file mode 100644 index 0000000..b1ade59 Binary files /dev/null and b/images/roof1.png differ diff --git a/images/roof2.png b/images/roof2.png new file mode 100644 index 0000000..9744a3b Binary files /dev/null and b/images/roof2.png differ diff --git a/images/solarreiniger.jpg b/images/solarreiniger.jpg new file mode 100644 index 0000000..6e5d846 Binary files /dev/null and b/images/solarreiniger.jpg differ diff --git a/images/spraying_drone.png b/images/spraying_drone.png new file mode 100644 index 0000000..4acfa93 Binary files /dev/null and b/images/spraying_drone.png differ diff --git a/images/spraying_drone1.png b/images/spraying_drone1.png new file mode 100644 index 0000000..5b58395 Binary files /dev/null and b/images/spraying_drone1.png differ diff --git a/images/spraying_drone2.png b/images/spraying_drone2.png new file mode 100644 index 0000000..8865dbc Binary files /dev/null and b/images/spraying_drone2.png differ diff --git a/images/spraying_drone3.png b/images/spraying_drone3.png new file mode 100644 index 0000000..d853ec2 Binary files /dev/null and b/images/spraying_drone3.png differ diff --git a/images/spraying_drone4.png b/images/spraying_drone4.png new file mode 100644 index 0000000..1a57836 Binary files /dev/null and b/images/spraying_drone4.png differ diff --git a/includes/navigation.html b/includes/navigation.html new file mode 100644 index 0000000..990b1c7 --- /dev/null +++ b/includes/navigation.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..1babe40 --- /dev/null +++ b/index.html @@ -0,0 +1,1090 @@ + + + + + + Luftglanz + + + + + + + + + + + + + + + +
+
+
+
Nachricht wurde versendet
+
×
+
+
+ +
+
+
+ + + + + + +
+
+
+ +
+
+
+
+

Revolutionäre Dachreinigung mit Drohnentechnologie

+

Schnelle, effiziente und unkomplizierte Reinigungslösungen für Ihr Dach.

+ Kostenloses Angebot +
+
+
+ +
+
+
+

Unsere Leistungen

+

Wir nutzen modernste Drohnentechnologie für außergewöhnliche Dachreinigungsservices

+
+ +
+
+
Standard Dachreinigung
+

Unser Standard-Dachreinigungsservice entfernt Schmutz, Moos und Algen, damit Ihr Dach optimal aussieht.

+
+ +
+
Solaranlagenreinigung
+

Halten Sie Ihre Solaranlagen mit unserem spezialisierten Reinigungsservice auf höchster Effizienz.

+
+
+
+
+ +
+
+
+

So funktioniert's

+

Unser optimierter Prozess macht die Dachreinigung einfach, sicher und unkompliziert

+
+ +
+
+
1
+
Inspektion vereinbaren
+

Füllen Sie unser einfaches Formular aus, um ein Angebot zu erhalten und Ihre Inspektion zu planen.

+
+ +
+
2
+
Drohnenreinigung
+

Unsere Techniker kommen mit spezialisierten Drohnen und Ausrüstung, um Ihr Dach zu reinigen.

+
+ +
+
3
+
Ergebnisse genießen
+

Bewundern Sie Ihr sauberes Dach und genießen Sie die verlängerte Lebensdauer Ihrer Dachmaterialien.

+
+
+
+
+ +
+
+
+

Häufig gestellte Fragen

+

Finden Sie Antworten auf häufige Fragen zu unseren Drohnen-Dachreinigungsdiensten

+
+ +
+
+
Wie lange dauert die Reinigung?
+
+

Die meisten Dächer können je nach Größe und Komplexität in wenigen Stunden gereinigt werden. Unsere Drohnentechnologie ermöglicht es uns, Aufträge schneller als mit herkömmlichen Methoden abzuschließen.

+
+
+ +
+
Ist die Drohnenreinigung sicher für mein Dach?
+
+

Ja, unsere Drohnen verwenden schonende Sprühmethoden, die Ihr Dach intakt halten. Der Prozess ist sicherer als herkömmliche Reinigungsmethoden, bei denen Arbeiter auf Ihrem Dach laufen müssen.

+
+
+ +
+
Welche Reinigungslösungen verwenden Sie?
+
+

Wir verwenden umweltfreundliche Reinigungslösungen, die Moos, Algen und Schmutz effektiv entfernen, ohne Pflanzen, Tiere oder Wasserquellen rund um Ihr Grundstück zu schädigen.

+
+
+
+
+
+ +
+
+
+

Kontakt

+

Kontaktieren Sie uns für ein kostenloses Angebot oder um mehr über unsere Dienstleistungen zu erfahren

+
+ + + + + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ + +
+

Oder kontaktieren Sie uns direkt:

+

Email: kontakt@luftglanz.de

+
+
+
+
+ +
+
+

© 2025 Luftglanz. Alle Rechte vorbehalten.

+
+
+ + + + + + + + \ No newline at end of file diff --git a/js/ai-chat.js b/js/ai-chat.js new file mode 100644 index 0000000..9e6e199 --- /dev/null +++ b/js/ai-chat.js @@ -0,0 +1,427 @@ +// AI Chat System for Luftglanz Website +class AIChat { + constructor() { + this.apiKey = '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'; + this.isOpen = false; + this.conversationHistory = []; + this.isTyping = false; + + // Product knowledge base + this.productKnowledge = ` + LUFTGLANZ PRODUCT KNOWLEDGE BASE: + + 1. COMPANY: Luftglanz - Professional drone-based roof and surface cleaning services + + 2. MAIN PRODUCTS: + + A) AGO QUART GRÜNBELAGENTFERNER (Green algae and lichen remover) + + PRODUCT DETAILS: + - Name: AGO Quart Algenentferner, Grünbelag- und Flechtenentferner + - Function: Highly effective against algae, green deposits, and lichens + - Active period: Up to 18 months continuous protection + - Application: Apply only on dry surfaces, no rain for 8 hours after application + - Coverage: 0.5L bottle covers approximately 100m² surface area + - Price: 19,90 € (39,80 € per liter) + + MIXING RATIOS: + - Light deposits: 1:20 with water + - Heavy deposits (black algae, lichens): 1:10 with water + + INGREDIENTS: Water, Benzalkonium-Chlorides, Tideceth-8, Cocamidopropyl-Betaine, Isopropyl-Alcohol + + APPLICATION PROCESS: + 1. Apply only on dry surfaces + 2. Ensure no rain for minimum 8 hours after application + 3. Works on surface level, not suitable for moss removal in joints/cracks + 4. Cleaning duration depends on contamination severity + 5. Active for up to 18 months, provides thorough cleaning and prevents recontamination + 6. Never rinse with water after application - product must bind with surface + + EFFECTIVENESS TIMELINE: + - Green deposits: Disappear within days + - Red/black algae: Several months for significant improvement + - Heavy lichen contamination: Up to 2 years for complete removal + + SUITABLE SURFACES: All waterproof surfaces (roofs, terraces, facades, etc.) + IMPORTANT: AGO Quart is also SAFE for solar panels! According to the manufacturer: "Sie können die Solar-Platten bedenkenlos einsprühen. Unser AGO Quart greift dies nicht an. Es wird auch sehr erfolgreich von Firmen eingesetzt, die Solaranlagen reinigen." + + BENEFITS: + - No high-pressure cleaning needed + - No scrubbing required + - Chlorine and acid-free + - Gentle cleaning without material damage + - Long-term protection against recontamination + - Self-cleaning effect through weather interaction + + B) MELLERUD PHOTOVOLTAIK & SOLARANLAGEN REINIGER KONZENTRAT + + PRODUCT DETAILS: + - Name: Mellerud Photovoltaik & Solaranlagen Reiniger Konzentrat + - Size: 0.5L concentrate + - Price: 8,99 € (17,98 € per liter) + - Function: Gentle and effective cleaning solution specifically for solar panels and photovoltaic systems + - Removes: Dust, general dirt, soot residues, fine dust, and pollen + - Special features: Streak-free, residue-free cleaning with water-repellent protection + + MIXING RATIO: + - Standard cleaning: 1:20 with water (ideally distilled water) + + INGREDIENTS & SPECS: + - Composition: Aqueous mixture of surfactants, complexing agents, and effectiveness enhancers + - pH value: 5.70 - 6.50 + - Density: 1.020 kg/l + + APPLICATION PROCESS: + 1. Remove coarse dirt, leaves, and debris by lightly sweeping (avoid intensive scrubbing) + 2. Mix concentrate 1:20 with water (preferably distilled water) + 3. Apply carefully with specialized solar/PV equipment, soft sponge, cloth, or squeegee + 4. Allow to work briefly + 5. Rinse thoroughly with clear water (preferably distilled water) + + SUITABLE EQUIPMENT: + - Specialized equipment for solar and PV systems + - Particularly soft sponges + - Soft cloths or squeegees + - Avoid intensive scrubbing to prevent damage + + BENEFITS: + - Improves performance and lifespan of solar systems + - Creates sustainable protection against re-contamination through water-repellent effect + - Gentle cleaning without damage to sensitive modules + - Streak-free and residue-free results + - Suitable for all types of solar and PV panels + + SAFETY INFORMATION: + - Causes severe eye irritation + - Keep out of reach of children + - Wash hands thoroughly after use + - In case of eye contact: rinse gently with water for several minutes + - Remove contact lenses if present and continue rinsing + - If eye irritation persists: seek medical advice + + WHY DISTILLED WATER? + - Minimizes residues and promotes streak-free cleaning + - Supports optimal care of PV and solar systems + + DRONE SERVICES: + - Professional application using drones for hard-to-reach areas + - Precise, even distribution of both AGO Quart and Mellerud products + - Safe application on steep roofs and high buildings + - Cost-effective compared to traditional scaffolding methods + - Specialized equipment for solar panel cleaning via drone + + SAFETY INFORMATION: + - Use biocides safely + - Always read labeling and product information + - AGO Quart: Causes skin irritation (H315), serious eye damage (H318), very toxic to aquatic organisms (H410) + - Mellerud: Causes severe eye irritation, keep away from children + + CONTACT INFORMATION: + AGO: AGO Sauberheit für Haus und Garten! GmbH & Co. KG, Brilloner Str. 39, 59909 Bestwig + Phone: 02904 / 98 98 98-0, Email: info@agoshop.de + + Mellerud: MELLERUD CHEMIE GMBH, Bernhard-Röttgen-Waldweg 20, 41379 Brüggen + Phone: +49 (0) 2163 / 950 90 999, Email: shop@mellerud.de, experten-service@mellerud.de + `; + + this.init(); + } + + init() { + this.createChatWidget(); + this.attachEventListeners(); + this.loadConversationHistory(); + } + + createChatWidget() { + // Chat toggle button + const chatButton = document.createElement('div'); + chatButton.id = 'ai-chat-toggle'; + chatButton.innerHTML = ` + + + + KI-Assistent + `; + + // Chat window + const chatWindow = document.createElement('div'); + chatWindow.id = 'ai-chat-window'; + chatWindow.innerHTML = ` +
+

🤖 Luftglanz KI-Assistent

+ +
+
+
+
+ Hallo! Ich bin Ihr KI-Assistent für Fragen zu unseren Drohnen-Reinigungsservices und AGO Quart Produkten. Wie kann ich Ihnen helfen? +
+
+
+
+ + +
+ `; + + document.body.appendChild(chatButton); + document.body.appendChild(chatWindow); + } + + attachEventListeners() { + const toggleButton = document.getElementById('ai-chat-toggle'); + const closeButton = document.getElementById('chat-close'); + const sendButton = document.getElementById('chat-send'); + const chatInput = document.getElementById('chat-input'); + + toggleButton.addEventListener('click', () => this.toggleChat()); + closeButton.addEventListener('click', () => this.closeChat()); + sendButton.addEventListener('click', () => this.sendMessage()); + + chatInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + this.sendMessage(); + } + }); + } + + toggleChat() { + this.isOpen = !this.isOpen; + const chatWindow = document.getElementById('ai-chat-window'); + const toggleButton = document.getElementById('ai-chat-toggle'); + + if (this.isOpen) { + chatWindow.classList.add('open'); + toggleButton.classList.add('active'); + } else { + chatWindow.classList.remove('open'); + toggleButton.classList.remove('active'); + } + } + + closeChat() { + this.isOpen = false; + const chatWindow = document.getElementById('ai-chat-window'); + const toggleButton = document.getElementById('ai-chat-toggle'); + + chatWindow.classList.remove('open'); + toggleButton.classList.remove('active'); + } + + async sendMessage() { + const input = document.getElementById('chat-input'); + const message = input.value.trim(); + + if (!message || this.isTyping) return; + + // Add user message + this.addMessage(message, 'user'); + input.value = ''; + + // Show typing indicator + this.showTypingIndicator(); + + try { + // Send to OpenAI + const response = await this.callOpenAI(message); + this.hideTypingIndicator(); + this.addMessage(response, 'bot'); + } catch (error) { + this.hideTypingIndicator(); + this.addMessage('Entschuldigung, es gab einen Fehler bei der Kommunikation mit dem KI-System. Bitte versuchen Sie es erneut.', 'bot'); + console.error('OpenAI API Error:', error); + } + } + + async callOpenAI(userMessage) { + // Try PHP backend first (more secure), fallback to direct API call + try { + return await this.callPHPBackend(userMessage); + } catch (error) { + console.warn('PHP backend failed, trying direct API call:', error); + return await this.callDirectAPI(userMessage); + } + } + + async callPHPBackend(userMessage) { + const response = await fetch('ai-chat-api.php', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + message: userMessage, + history: this.conversationHistory.slice(-10) // Keep last 10 for context + }) + }); + + if (!response.ok) { + throw new Error(`PHP Backend error: ${response.status}`); + } + + const data = await response.json(); + + if (!data.success) { + throw new Error(data.error || 'Backend error'); + } + + // Update conversation history + this.conversationHistory.push( + { role: 'user', content: userMessage }, + { role: 'assistant', content: data.message } + ); + + this.saveConversationHistory(); + + return data.message; + } + + async callDirectAPI(userMessage) { + const systemPrompt = `Du bist ein hilfsreicher KI-Assistent für Luftglanz, ein Unternehmen für professionelle Drohnen-basierte Dach- und Oberflächenreinigung. + + WICHTIGE ANWEISUNGEN: + - Antworte immer auf Deutsch + - Verwende IMMER die höfliche Anrede "Sie" (niemals "du" oder "dir") + - Sei freundlich, professionell und hilfsbereit + - Nutze AUSSCHLIESSLICH die bereitgestellten Produktinformationen für präzise Antworten + - Gib konkrete Produktdetails, Preise und Anwendungshinweise aus der Wissensbasis + - Bei Fragen zu Solaranlagen: Erkläre dass SOWOHL AGO Quart ALS AUCH Mellerud geeignet sind + - AGO Quart: Laut Hersteller sicher für Solar-Platten, wird von Reinigungsfirmen eingesetzt + - Mellerud: Speziell für Solaranlagen entwickelt mit schonender Formel + - Bei Fragen zu Algen/Grünbelägen auf Dächern, empfehle AGO Quart + - Erwähne die Vorteile der Drohnen-Anwendung wenn relevant + - Erkläre Unterschiede zwischen den Produkten detailliert basierend auf der Wissensbasis + - Gib konkrete Mischungsverhältnisse und Preise an + + WICHTIG: Du kennst diese BEIDEN spezifischen Produkte sehr genau: + 1. AGO Quart (19,90€) - für Algen, Grünbeläge, Flechten auf Dächern/Terrassen + 2. Mellerud Photovoltaik Reiniger (8,99€) - speziell für Solaranlagen + + PRODUKTWISSEN: ${this.productKnowledge} + + Beantworte Fragen zu: + - Unterschiede zwischen AGO Quart und Mellerud Produkten + - Spezifische Anwendung für Solaranlagen vs. Dächer + - Konkrete Preise, Mischungsverhältnisse und Reichweiten + - Drohnen-Anwendung beider Produkte + - Sicherheitshinweise für beide Produkte + - Warum destilliertes Wasser bei Mellerud wichtig ist + - Kontaktdaten beider Hersteller`; + + const messages = [ + { role: 'system', content: systemPrompt }, + ...this.conversationHistory.slice(-10), // Keep last 10 messages for context + { role: 'user', content: userMessage } + ]; + + const response = await fetch('https://api.openai.com/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.apiKey}` + }, + body: JSON.stringify({ + model: 'gpt-3.5-turbo', + messages: messages, + max_tokens: 500, + temperature: 0.7, + presence_penalty: 0.1, + frequency_penalty: 0.1 + }) + }); + + if (!response.ok) { + throw new Error(`OpenAI API error: ${response.status}`); + } + + const data = await response.json(); + const assistantMessage = data.choices[0].message.content; + + // Update conversation history + this.conversationHistory.push( + { role: 'user', content: userMessage }, + { role: 'assistant', content: assistantMessage } + ); + + this.saveConversationHistory(); + + return assistantMessage; + } + + addMessage(content, sender) { + const messagesContainer = document.getElementById('chat-messages'); + const messageDiv = document.createElement('div'); + messageDiv.className = `message ${sender}-message`; + + const messageContent = document.createElement('div'); + messageContent.className = 'message-content'; + messageContent.textContent = content; + + messageDiv.appendChild(messageContent); + messagesContainer.appendChild(messageDiv); + + // Auto-scroll to bottom + messagesContainer.scrollTop = messagesContainer.scrollHeight; + } + + showTypingIndicator() { + this.isTyping = true; + const messagesContainer = document.getElementById('chat-messages'); + + const typingDiv = document.createElement('div'); + typingDiv.id = 'typing-indicator'; + typingDiv.className = 'message bot-message typing'; + typingDiv.innerHTML = ` +
+
+ + + +
+
+ `; + + messagesContainer.appendChild(typingDiv); + messagesContainer.scrollTop = messagesContainer.scrollHeight; + } + + hideTypingIndicator() { + this.isTyping = false; + const typingIndicator = document.getElementById('typing-indicator'); + if (typingIndicator) { + typingIndicator.remove(); + } + } + + saveConversationHistory() { + try { + localStorage.setItem('luftglanz_chat_history', JSON.stringify(this.conversationHistory)); + } catch (error) { + console.warn('Could not save conversation history:', error); + } + } + + loadConversationHistory() { + try { + const saved = localStorage.getItem('luftglanz_chat_history'); + if (saved) { + this.conversationHistory = JSON.parse(saved); + } + } catch (error) { + console.warn('Could not load conversation history:', error); + this.conversationHistory = []; + } + } +} + +// Initialize chat when DOM is loaded +document.addEventListener('DOMContentLoaded', () => { + window.aiChat = new AIChat(); +}); diff --git a/js/background-patterns.js b/js/background-patterns.js new file mode 100644 index 0000000..d7c4ad9 --- /dev/null +++ b/js/background-patterns.js @@ -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 + ``, + // Spray bottle + ``, + // Drone + `` + ]; + + // Choose a random icon + iconContainer.innerHTML = icons[Math.floor(Math.random() * icons.length)]; + card.appendChild(iconContainer); + } +})(); diff --git a/js/bright-theme.js b/js/bright-theme.js new file mode 100644 index 0000000..ab672e8 --- /dev/null +++ b/js/bright-theme.js @@ -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 = ` + + + + + + + `; + + 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 = [ + '', + '', + '' + ]; + + 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); + } + }); + } +})(); diff --git a/js/contact-form.js b/js/contact-form.js new file mode 100644 index 0000000..0fafdd4 --- /dev/null +++ b/js/contact-form.js @@ -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()); + } +}); \ No newline at end of file diff --git a/js/dark-mode.js b/js/dark-mode.js new file mode 100644 index 0000000..1a3c564 --- /dev/null +++ b/js/dark-mode.js @@ -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)'; + }); + } + }); + } +})(); diff --git a/js/drone-animation.js b/js/drone-animation.js new file mode 100644 index 0000000..6670388 --- /dev/null +++ b/js/drone-animation.js @@ -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); + } + } +})(); \ No newline at end of file diff --git a/js/drone-image-animation.js b/js/drone-image-animation.js new file mode 100644 index 0000000..e10578f --- /dev/null +++ b/js/drone-image-animation.js @@ -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); + } + } +})(); diff --git a/js/language-manager.js b/js/language-manager.js new file mode 100644 index 0000000..24d106d --- /dev/null +++ b/js/language-manager.js @@ -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(); +}); diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..10b52b1 --- /dev/null +++ b/js/main.js @@ -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 +}); \ No newline at end of file diff --git a/js/mobile-menu.js b/js/mobile-menu.js new file mode 100644 index 0000000..06292e3 --- /dev/null +++ b/js/mobile-menu.js @@ -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; + } +}); diff --git a/js/navigation.js b/js/navigation.js new file mode 100644 index 0000000..92374a2 --- /dev/null +++ b/js/navigation.js @@ -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'); + } + }); +}); \ No newline at end of file diff --git a/js/roof-cleaning-animation.js b/js/roof-cleaning-animation.js new file mode 100644 index 0000000..3f829cf --- /dev/null +++ b/js/roof-cleaning-animation.js @@ -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; + } +})(); diff --git a/js/star-background.js b/js/star-background.js new file mode 100644 index 0000000..a9a2482 --- /dev/null +++ b/js/star-background.js @@ -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); + } +})(); diff --git a/js/translations.js b/js/translations.js new file mode 100644 index 0000000..4b5078b --- /dev/null +++ b/js/translations.js @@ -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 roof’s 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; +} diff --git a/pages/about.html b/pages/about.html new file mode 100644 index 0000000..d75490f --- /dev/null +++ b/pages/about.html @@ -0,0 +1,320 @@ + + + + + + Über Uns - Luftglanz + + + + + + + + + + + +
+
+
+ + + + + + + +
+
+
+ +
+
+

About Our Company

+

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

+

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.

+ +
+ Our professional team +

The Roof Drone Cleaning professional team

+
+ +

Our Team

+

Our team consists of certified drone operators, roof maintenance specialists, and customer service experts dedicated to providing you with the best possible experience.

+ +

Unsere Technologie

+

Wir verwenden speziell entwickelte Drohnen, die mit hochmoderner Reinigungsausrüstung ausgestattet sind, um sicherzustellen, dass Ihr Dach schnell und gründlich gereinigt wird.

+
+
+ +
+
+

© 2023 Luftglanz. Alle Rechte vorbehalten.

+
+
+ + + + + \ No newline at end of file diff --git a/pages/contact.html b/pages/contact.html new file mode 100644 index 0000000..655315a --- /dev/null +++ b/pages/contact.html @@ -0,0 +1,585 @@ + + + + + + Kontakt - Luftglanz + + + + + + + + + + + + +
+ +
+
+
+ + + + + +
+
+
+ +
+
+
+

Contact Us

+

Have questions about our services or want to schedule a cleaning? Get in touch with us using any of the methods below.

+ +
+
+

Phone

+

(555) 123-4567

+
+ +
+

Email

+

info@roofdronecleaning.com

+
+ +
+

Address

+

123 Cleaning Ave
Drone City, DC 10001

+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+

© 2023 Luftglanz. Alle Rechte vorbehalten.

+
+
+ + + + + + + \ No newline at end of file diff --git a/pages/gallery.html b/pages/gallery.html new file mode 100644 index 0000000..818375c --- /dev/null +++ b/pages/gallery.html @@ -0,0 +1,573 @@ + + + + + + Galerie - Luftglanz + + + + + + + + + + + + + + +
+
+
+ + + + + +
+
+
+ +
+
+

Unsere Arbeit

+

Entdecken Sie unsere Galerie, um zu sehen, wie unsere Drohnen außergewöhnliche Reinigungsergebnisse liefern.

+ + +
+
+ + + +
+
+

© 2025 Luftglanz. Alle Rechte vorbehalten.

+
+
+ + + + + + + + + \ No newline at end of file diff --git a/pages/products-debug.html b/pages/products-debug.html new file mode 100644 index 0000000..64ca61d --- /dev/null +++ b/pages/products-debug.html @@ -0,0 +1,103 @@ + + + + + + Products Debug - Luftglanz + + + +
DEBUG MODE
+ +
+ +
+ +
+

Products (Debug Version)

+ +
+

AGO Quart Grünbelagentferner

+

Test product description to verify content visibility

+

Price: 24,90 €

+ View on Manufacturer's Site +
+ +
+

AGO Quart 5 Liter

+

Test product description to verify content visibility

+

Price: 99,90 €

+ View on Manufacturer's Site +
+
+ + + + diff --git a/pages/products.html b/pages/products.html new file mode 100644 index 0000000..f513c85 --- /dev/null +++ b/pages/products.html @@ -0,0 +1,429 @@ + + + + + + Produkte - Luftglanz + + + + + + + + + + + +
+
+
+ + + + + +
+
+
+ +
+
+
+

AGO Quart Grünbelagentferner

+

Hocheffektive Lösung gegen Grünbeläge, Algen, Moos und Flechten auf allen Oberflächen

+
+ +
+
+
+ AGO Quart 100 0,5L Flasche +
+ +
+
    +
  • Hochkonzentrierte Wirkformel gegen Grünbeläge
  • +
  • Selbsttätige Langzeitwirkung ohne Abwaschen
  • +
  • Für Dächer, Fassaden, Terrassen, Wege, Zäune
  • +
  • Vorbeugender Schutz über Monate
  • +
  • Einfache Anwendung durch Aufsprühen
  • +
  • Sichtbare Ergebnisse nach 2-3 Tagen
  • +
+
+
+
+
+ + +
+
+

MELLERUD Photovoltaik und Solaranlagen-Reiniger

+
+ +
+
+
+ MELLERUD Photovoltaik und Solaranlagen-Reiniger Konzentrat +
+ +
+
+

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.

+
+ +
    +
  • Materialschonend und rückstandsfrei
  • +
  • Verbessert die Leistung und Erträge der Anlagen
  • +
  • Biologisch abbaubar und umweltfreundlich
  • +
  • Für alle Solar- und Photovoltaikanlagen geeignet
  • +
+
+
+
+
+
+ +
+
+

© 2025 Luftglanz. Alle Rechte vorbehalten.

+
+
+ + + + + + + \ No newline at end of file diff --git a/pages/services.html b/pages/services.html new file mode 100644 index 0000000..6f639f4 --- /dev/null +++ b/pages/services.html @@ -0,0 +1,427 @@ + + + + + + Leistungen - Luftglanz + + + + + + + + + + + +
+
+
+ + + +
+
+
+ +
+
+
+
+

Professional Drone Roof Cleaning Services

+

Our specialized drones deliver thorough cleaning with advanced technology

+
+
+
+ +
+

Our Services

+

We offer professional roof cleaning and maintenance services using cutting-edge drone technology that delivers superior results with enhanced safety.

+ +
+
+

Drone Roof Cleaning

+

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.

+
+ +
+

Inspection Services

+

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.

+
+ +
+

Maintenance Packages

+

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.

+
+
+ +

Premium Add-On Services

+

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.

+ +

Unser Effizienzkonzept

+

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.

+
+
+ +
+
+

© 2023 Luftglanz. Alle Rechte vorbehalten.

+
+
+ + + + + + + + \ No newline at end of file diff --git a/partials/nav.html b/partials/nav.html new file mode 100644 index 0000000..94d395e --- /dev/null +++ b/partials/nav.html @@ -0,0 +1,24 @@ +
+ +
\ No newline at end of file diff --git a/phptest.php b/phptest.php new file mode 100644 index 0000000..6ce9392 --- /dev/null +++ b/phptest.php @@ -0,0 +1,12 @@ +"; +echo "

PHP Test

"; +echo "

PHP is " . (function_exists('mail') ? 'working' : 'not working') . "!

"; +echo "

PHP Version: " . phpversion() . "

"; +echo "

Server Software: " . $_SERVER['SERVER_SOFTWARE'] . "

"; +echo "
PHP Info:\n\n";
+phpinfo();
+echo "
"; +echo ""; +?> diff --git a/process-form.php b/process-form.php new file mode 100644 index 0000000..a21775e --- /dev/null +++ b/process-form.php @@ -0,0 +1,200 @@ + $value) { + debug_log("Request parameter: $key = $value"); +} + +// If direct access without POST, offer a helpful message +if ($_SERVER['REQUEST_METHOD'] !== 'POST') { + echo "Form Processing Error"; + echo ""; + echo "

Form Submission Error

"; + echo "

This page is meant to be accessed via a form submission.

"; + echo "

It appears you're trying to access it directly. Please submit the form instead.

"; + echo "

Current request method: " . $_SERVER['REQUEST_METHOD'] . " (should be POST)

"; + echo "Go to Contact Form"; + echo "Try Test Form"; + echo ""; + 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 \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 "Form Submission Debug"; + echo ""; + echo "

Form Submission Debugging Output

"; + echo "
Error: $error_message
"; + + echo "
"; + echo "

Debug Logs:

"; + echo "
"; + foreach ($debug_logs as $log) { + echo htmlspecialchars($log) . "\n"; + } + echo "
"; + + echo "Back to Contact Form"; + echo ""; + 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; +?> diff --git a/send-email.php b/send-email.php new file mode 100644 index 0000000..d8444f0 --- /dev/null +++ b/send-email.php @@ -0,0 +1,60 @@ +\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; +?> diff --git a/simple-handler.php b/simple-handler.php new file mode 100644 index 0000000..44c9761 --- /dev/null +++ b/simple-handler.php @@ -0,0 +1,50 @@ +\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; +?> diff --git a/test.php b/test.php new file mode 100644 index 0000000..fb6f08d --- /dev/null +++ b/test.php @@ -0,0 +1,4 @@ +