diff --git a/.continue/mcpServers/new-mcp-server.yaml b/.continue/mcpServers/new-mcp-server.yaml
old mode 100644
new mode 100755
diff --git a/.dockerignore b/.dockerignore
old mode 100644
new mode 100755
diff --git a/.gitea/workflows/deploy-test.yml b/.gitea/workflows/deploy-test.yml
old mode 100644
new mode 100755
diff --git a/.gitignore b/.gitignore
old mode 100644
new mode 100755
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000..cb7276d
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,8 @@
+# Legacy redirects for old URLs pointing to root
+RewriteEngine On
+RewriteRule ^impressum\.html$ /impressum [R=301,L]
+RewriteRule ^datenschutz\.html$ /datenschutz [R=301,L]
+RewriteRule ^haus-schleusingen\.html$ / [R=301,L]
+
+# Everything else goes to public/
+RewriteRule ^(.*)$ public/$1 [L]
diff --git a/.htmlhintrc b/.htmlhintrc
old mode 100644
new mode 100755
diff --git a/.husky/pre-commit b/.husky/pre-commit
old mode 100644
new mode 100755
diff --git a/.prettierignore b/.prettierignore
old mode 100644
new mode 100755
diff --git a/.prettierrc b/.prettierrc
old mode 100644
new mode 100755
diff --git a/.stylelintrc.json b/.stylelintrc.json
old mode 100644
new mode 100755
diff --git a/AGENTS.md b/AGENTS.md
old mode 100644
new mode 100755
diff --git a/Dockerfile b/Dockerfile
old mode 100644
new mode 100755
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
diff --git a/app/controllers/Controller.php b/app/controllers/Controller.php
new file mode 100644
index 0000000..309b848
--- /dev/null
+++ b/app/controllers/Controller.php
@@ -0,0 +1,25 @@
+view = new View();
+ }
+
+ protected function render(string $view, array $data = [], string $layout = 'main'): void
+ {
+ foreach ($data as $key => $value) {
+ $this->view->assign($key, $value);
+ }
+ $this->view->render($view, $layout);
+ }
+}
diff --git a/app/controllers/DatenschutzController.php b/app/controllers/DatenschutzController.php
new file mode 100644
index 0000000..9bb59b6
--- /dev/null
+++ b/app/controllers/DatenschutzController.php
@@ -0,0 +1,18 @@
+render('datenschutz/index', [
+ 'pageTitle' => 'Datenschutzerklärung – Haus Schleusingen',
+ 'pageDescription' => 'Datenschutzerklärung der Website haus-schleusingen.de',
+ 'robots' => 'noindex',
+ 'canonical' => 'https://haus-schleusingen.de/datenschutz',
+ ]);
+ }
+}
diff --git a/app/controllers/HomeController.php b/app/controllers/HomeController.php
new file mode 100644
index 0000000..0d7b51a
--- /dev/null
+++ b/app/controllers/HomeController.php
@@ -0,0 +1,174 @@
+ '', 'lname' => '', 'email' => '', 'phone' => '', 'interest' => 'Besichtigung anfragen', 'message' => ''];
+ }
+
+ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $formData['fname'] = $normalizeContactValue((string) ($_POST['fname'] ?? ''));
+ $formData['lname'] = $normalizeContactValue((string) ($_POST['lname'] ?? ''));
+ $formData['email'] = $normalizeContactValue((string) ($_POST['email'] ?? ''));
+ $formData['phone'] = $normalizeContactValue((string) ($_POST['phone'] ?? ''));
+ $formData['interest'] = $normalizeContactValue((string) ($_POST['interest'] ?? ''));
+ $formData['message'] = $normalizeContactValue((string) ($_POST['message'] ?? ''));
+
+ $honeypot = $normalizeContactValue((string) ($_POST['website'] ?? ''));
+ if ($honeypot !== '') {
+ header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
+ $_SESSION['form_success'] = true;
+ exit;
+ } else {
+ if ($formData['fname'] === '') {
+ $formErrors[] = 'Bitte geben Sie Ihren Vornamen an.';
+ }
+ if ($formData['lname'] === '') {
+ $formErrors[] = 'Bitte geben Sie Ihren Nachnamen an.';
+ }
+ if ($formData['email'] === '' || !filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) {
+ $formErrors[] = 'Bitte geben Sie eine gültige E-Mail-Adresse an.';
+ }
+ if ($formData['message'] === '') {
+ $formErrors[] = 'Bitte geben Sie eine Nachricht ein.';
+ }
+
+ if ($containsHeaderInjection($formData['email']) || $containsHeaderInjection($formData['fname'] . ' ' . $formData['lname'])) {
+ $formErrors[] = 'Ungültige Zeichen in den Eingabefeldern.';
+ }
+
+ $formTime = isset($_POST['form_time']) ? (int) $_POST['form_time'] : 0;
+ if ($formTime > 0 && (time() - $formTime) < 3) {
+ $formErrors[] = 'Das Formular wurde zu schnell abgeschickt. Bitte versuchen Sie es erneut.';
+ }
+
+ $lastSubmit = $_SESSION['last_contact_submit'] ?? 0;
+ if ($lastSubmit && (time() - $lastSubmit) < 60) {
+ $formErrors[] = 'Bitte warten Sie einen Moment vor der nächsten Anfrage.';
+ }
+
+ if (empty($formErrors)) {
+ $to = 'mki@kies-media.de';
+ $subject = 'Kontaktanfrage: ' . $formData['interest'];
+ $body = "Von: {$formData['fname']} {$formData['lname']}\n"
+ . "E-Mail: {$formData['email']}\n";
+ if ($formData['phone'] !== '') {
+ $body .= "Telefon: {$formData['phone']}\n";
+ }
+ $body .= "Anliegen: {$formData['interest']}\n\n"
+ . $formData['message'];
+
+ $headers = "From: {$formData['email']}\r\n";
+ $headers .= "Reply-To: {$formData['email']}\r\n";
+ $headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
+ $headers .= "X-Mailer: PHP/" . phpversion();
+
+ $mailSent = mail($to, $subject, $body, $headers);
+
+ if ($mailSent) {
+ $_SESSION['last_contact_submit'] = time();
+ header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
+ $_SESSION['form_success'] = true;
+ exit;
+ } else {
+ $formErrors[] = 'Leider konnte die E-Mail nicht gesendet werden. Bitte versuchen Sie es später erneut oder schreiben Sie uns direkt an mki@kies-media.de.';
+ }
+ }
+ }
+ if (!empty($formErrors)) {
+ header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
+ $_SESSION['form_errors'] = $formErrors;
+ $_SESSION['form_data'] = $formData;
+ exit;
+ }
+ }
+
+ $this->render('home/index', [
+ 'formSuccess' => $formSuccess,
+ 'formErrors' => $formErrors,
+ 'formData' => $formData,
+ 'escapeContactValue' => $escapeContactValue,
+ 'pageTitle' => 'Einfamilienhaus mieten Schleusingen | 227 m², 6 Zimmer | 1.300 € Kaltmiete',
+ 'pageDescription' => 'Einfamilienhaus zur Langzeitmiete in Schleusingen: 227 m² Wohnfläche, 6 Zimmer, 3 Etagen mit Dachterrasse. Kaltmiete 1.300 €. Bahnhofstraße 10, 98553 Schleusingen. Ab sofort verfügbar.',
+ 'canonical' => 'https://haus-schleusingen.de/',
+ 'openGraph' => [
+ 'ogTitle' => 'Einfamilienhaus zur Miete in Schleusingen – 227 m², 6 Zimmer',
+ 'ogDescription' => 'Großzügiges Einfamilienhaus zur Langzeitmiete: 227 m², 6 Zimmer, 3 Etagen + Dachterrasse. Kaltmiete 1.300 €. Ab sofort verfügbar in Schleusingen.',
+ 'ogImage' => 'https://haus-schleusingen.de/bilder/Außenansicht-2.png',
+ 'ogUrl' => 'https://haus-schleusingen.de/',
+ ],
+ 'structuredData' => json_encode([
+ '@context' => 'https://schema.org',
+ '@type' => 'RealEstateListing',
+ 'name' => 'Einfamilienhaus zur Miete in Schleusingen',
+ 'description' => 'Großzügiges Einfamilienhaus zur Langzeitmiete: 227 m² Wohnfläche, 6 Zimmer, 3 Etagen mit Dachterrasse. Kaltmiete 1.300 €.',
+ 'url' => 'https://haus-schleusingen.de/',
+ 'image' => 'https://haus-schleusingen.de/bilder/Außenansicht-2.png',
+ 'datePosted' => '2026-05-14',
+ 'address' => [
+ '@type' => 'PostalAddress',
+ 'streetAddress' => 'Bahnhofstraße 10',
+ 'addressLocality' => 'Schleusingen',
+ 'postalCode' => '98553',
+ 'addressCountry' => 'DE',
+ ],
+ 'offers' => [
+ '@type' => 'Offer',
+ 'price' => '1300',
+ 'priceCurrency' => 'EUR',
+ 'priceSpecification' => [
+ '@type' => 'UnitPriceSpecification',
+ 'price' => '1300',
+ 'priceCurrency' => 'EUR',
+ 'unitCode' => 'MON',
+ 'description' => 'Kaltmiete pro Monat',
+ ],
+ ],
+ 'floorSize' => [
+ '@type' => 'QuantitativeValue',
+ 'value' => '227',
+ 'unitCode' => 'MTK',
+ ],
+ 'numberOfRooms' => [
+ '@type' => 'QuantitativeValue',
+ 'value' => '6',
+ ],
+ ]),
+ ]);
+ }
+}
diff --git a/app/controllers/ImpressumController.php b/app/controllers/ImpressumController.php
new file mode 100644
index 0000000..a7bef04
--- /dev/null
+++ b/app/controllers/ImpressumController.php
@@ -0,0 +1,18 @@
+render('impressum/index', [
+ 'pageTitle' => 'Impressum – Haus Schleusingen',
+ 'pageDescription' => 'Impressum der Website haus-schleusingen.de',
+ 'robots' => 'noindex',
+ 'canonical' => 'https://haus-schleusingen.de/impressum',
+ ]);
+ }
+}
diff --git a/app/core/Router.php b/app/core/Router.php
new file mode 100644
index 0000000..2f17cb2
--- /dev/null
+++ b/app/core/Router.php
@@ -0,0 +1,60 @@
+routes[$path] = [
+ 'controller' => $controller,
+ 'action' => $action,
+ ];
+ }
+
+ public function dispatch(string $uri): void
+ {
+ // Normalize: strip query string and trailing slash
+ $path = parse_url($uri, PHP_URL_PATH);
+ $path = rtrim($path, '/') ?: '/';
+
+ // Direct match
+ if (isset($this->routes[$path])) {
+ $this->execute($this->routes[$path]);
+ return;
+ }
+
+ // Legacy .html redirect (301)
+ if (preg_match('#^/(impressum|datenschutz)\.html$#', $path, $m)) {
+ header('Location: /' . $m[1], true, 301);
+ exit;
+ }
+
+ // 404
+ http_response_code(404);
+ echo '
404 – Seite nicht gefunden ';
+ echo 'Zurück zur Startseite
';
+ }
+
+ private function execute(array $route): void
+ {
+ $controllerClass = $route['controller'];
+ $action = $route['action'];
+
+ if (!class_exists($controllerClass)) {
+ throw new \RuntimeException("Controller {$controllerClass} nicht gefunden.");
+ }
+
+ $controller = new $controllerClass();
+
+ if (!method_exists($controller, $action)) {
+ throw new \RuntimeException("Action {$action} in {$controllerClass} nicht gefunden.");
+ }
+
+ $controller->$action();
+ }
+}
diff --git a/app/core/View.php b/app/core/View.php
new file mode 100644
index 0000000..47210a5
--- /dev/null
+++ b/app/core/View.php
@@ -0,0 +1,46 @@
+viewsPath = $viewsPath ?? dirname(__DIR__) . '/views';
+ }
+
+ public function assign(string $key, mixed $value): void
+ {
+ $this->data[$key] = $value;
+ }
+
+ public function render(string $view, string $layout = 'main'): void
+ {
+ $viewFile = $this->viewsPath . '/' . $view . '.php';
+ $layoutFile = $this->viewsPath . '/layouts/' . $layout . '.php';
+
+ if (!file_exists($viewFile)) {
+ throw new \RuntimeException("View {$view} nicht gefunden: {$viewFile}");
+ }
+
+ if (!file_exists($layoutFile)) {
+ throw new \RuntimeException("Layout {$layout} nicht gefunden: {$layoutFile}");
+ }
+
+ // Extract data to variables for the view
+ extract($this->data, EXTR_SKIP);
+
+ // Capture view content
+ ob_start();
+ require $viewFile;
+ $content = ob_get_clean();
+
+ // Render layout with $content
+ require $layoutFile;
+ }
+}
diff --git a/datenschutz.html b/app/views/datenschutz/index.php
similarity index 60%
rename from datenschutz.html
rename to app/views/datenschutz/index.php
index 578b169..7bb1594 100644
--- a/datenschutz.html
+++ b/app/views/datenschutz/index.php
@@ -1,110 +1,12 @@
-
-
-
-
-
- Datenschutzerklärung – Haus Schleusingen
-
-
-
-
-
-
-
-
Bahnhofstraße 10
- Jetzt anfragen
+ Jetzt anfragen
@@ -209,15 +111,13 @@
Zur Ausübung Ihrer Rechte wenden Sie sich bitte an: mki@kies-media.de
- ← Zurück zum Objekt
+ ← Zurück zum Objekt
-
-
diff --git a/app/views/home/index.php b/app/views/home/index.php
new file mode 100644
index 0000000..6358e9f
--- /dev/null
+++ b/app/views/home/index.php
@@ -0,0 +1,504 @@
+ Zum Inhalt springen
+
+ Bahnhofstraße 10
+
+
+
+
+
+ Jetzt anfragen
+
+
+
+
+
+
+
+
+
Zur Langzeitmiete · Ab sofort verfügbar
+
+ Großzügiges
+
+ Einfamilienhaus
+
+ in Schleusingen
+
+
+ Schleusinger Bahnhofstraße 10
+ 227 m² Wohnfläche
+ 6 Zimmer
+ 3 Etagen + Dachterrasse
+
+
+
+
+
+
+
+
+
+
+
Das Objekt
+
Wohnen mit Charakter und viel Raum
+
+ Vermietet wird ein vollständiges Einfamilienhaus in ruhiger Lage von Schleusingen. Das
+ Haus verbindet historischen Charme mit modernem Wohnkomfort auf drei großzügigen Etagen.
+
+
+ Garage für zwei Fahrzeuge, großzügige Dachterrasse mit 35,8 m², vollausgestattete Küche,
+ Vollbad sowie Abstell- und Nutzräume machen das Haus zu einem außergewöhnlichen
+ Mietobjekt.
+
+
+
+
154,9 m²
+
Nutzfläche
+
+
+
35,8 m²
+
Dachterrasse
+
+
+
+
+
+
+
+
+
+
Wohnzimmer · 42,6 m²
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Außenansicht
+
+
+
+
+
+
+
Wohnzimmer · 42,6 m²
+
+
+
+
+
+
+
Küche · 18,4 m²
+
+
+
+
+
+
+
Schlafzimmer · 18 m²
+
+
+
+
+
+
+
Badezimmer · 9,8 m²
+
+
+
+
+
+
+
Kinderzimmer 1 · 21,7 m²
+
+
+
+
+
+
+
Kinderzimmer 2 · 15,7 m²
+
+
+
+
+
+
+
Kinderzimmer Detail
+
+
+
+
+
+
+
Gästezimmer · 11,5 m²
+
+
+
+
+
+
+
Wohnbereich
+
+
+
+
+
+
+
Wohnbereich Detail
+
+
+
+
+
+
+
Hausansicht
+
+
+
+
+
+ Raumaufteilung
+ Großzügig auf allen Etagen
+
+
+
+
+
+
Flur20,1 m²
+
WC0,8 m²
+
Garage / Partykeller42,6 m²
+
Abstellraum 19,9 m²
+
Abstellraum 27,8 m²
+
Heizungskeller18,3 m²
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Flur20,1 m²
+
Wohnzimmer42,6 m²
+
Gästezimmer11,5 m²
+
Badezimmer9,8 m²
+
Küche18,4 m²
+
Schlafzimmer18,0 m²
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Flur13,9 m²
+
Kinderzimmer 121,7 m²
+
Kinderzimmer 215,7 m²
+
Spielzimmer6,3 m²
+
Ankleidezimmer1,4 m²
+
Dachterrasse9,0 m² (25% von 35,8 m²)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Dachboden unten (ungeheizt)52 m²
+
Dachboden Mitte (ungeheizt)31 m²
+
Dachboden oben (ungeheizt)11 m²
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Mietkonditionen
+
Transparente Preisgestaltung
+
+
+
Kaltmiete
+
1.300 €
+
pro Monat
+
+
+
Gesamtmiete warm
+
1.600 €
+
inkl. 300 € Nebenkosten
+
+
+
Kaution
+
2.600 €
+
2 Nettokaltmieten
+
+
+
+
+ Verfügbarkeit
+ Ab sofort · unbefristete Laufzeit
+
+
+ Nebenkosten
+ Vorauszahlung 300 €/Monat, jährliche Abrechnung
+
+
+ Energieausweis
+ Wird bei Mietbeginn übergeben · Erdgasheizung
+
+
+ Haustiere
+ Auf Anfrage
+
+
+
+
+
+
+ Standort
+ Zentral und ruhig zugleich
+
+
+
🛒
+
+
Einkaufen & Versorgung
+
Supermärkte, Ärzte, Apotheken und Schulen sind fußläufig erreichbar
+
+
+
+
🚌
+
+
Öffentlicher Nahverkehr
+
Zentrale Bushaltestelle ca. 200 m entfernt — direkte Verbindungen in die Region
+
+
+
+
🏛
+
+
Innenstadt Schleusingen
+
Wochenmarkt und Stadtmitte nur ca. 500 m entfernt
+
+
+
+
📍
+
+
Genaue Adresse
+
Schleusinger Bahnhofstraße 10 98533 Schleusingen, Thüringen
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
×
+
+
diff --git a/impressum.html b/app/views/impressum/index.php
similarity index 53%
rename from impressum.html
rename to app/views/impressum/index.php
index 8480798..e246e54 100644
--- a/impressum.html
+++ b/app/views/impressum/index.php
@@ -1,110 +1,12 @@
-
-
-
-
-
- Impressum – Haus Schleusingen
-
-
-
-
-
-
-
-
Bahnhofstraße 10
- Jetzt anfragen
+ Jetzt anfragen
@@ -171,15 +73,13 @@
Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.
- ← Zurück zum Objekt
+ ← Zurück zum Objekt
-
-
diff --git a/app/views/layouts/main.php b/app/views/layouts/main.php
new file mode 100644
index 0000000..02ce463
--- /dev/null
+++ b/app/views/layouts/main.php
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+ = htmlspecialchars($pageTitle) ?>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ = $content ?>
+
+
+
+
diff --git a/docker-preview.png b/docker-preview.png
old mode 100644
new mode 100755
diff --git a/eslint.config.js b/eslint.config.js
old mode 100644
new mode 100755
index 37a76da..d8421f2
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -13,7 +13,6 @@ module.exports = [
sourceType: "script",
globals: {
...globals.browser,
- ...globals.jquery,
},
},
plugins: {
diff --git a/index.php b/index.php
deleted file mode 100644
index 93ef33f..0000000
--- a/index.php
+++ /dev/null
@@ -1,828 +0,0 @@
- '', 'lname' => '', 'email' => '', 'phone' => '', 'interest' => 'Besichtigung anfragen', 'message' => ''];
-}
-
-if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- // Collect and normalize input
- $formData['fname'] = normalizeContactValue((string) ($_POST['fname'] ?? ''));
- $formData['lname'] = normalizeContactValue((string) ($_POST['lname'] ?? ''));
- $formData['email'] = normalizeContactValue((string) ($_POST['email'] ?? ''));
- $formData['phone'] = normalizeContactValue((string) ($_POST['phone'] ?? ''));
- $formData['interest'] = normalizeContactValue((string) ($_POST['interest'] ?? ''));
- $formData['message'] = normalizeContactValue((string) ($_POST['message'] ?? ''));
-
- // Honeypot check – hidden field must be empty
- $honeypot = normalizeContactValue((string) ($_POST['website'] ?? ''));
- if ($honeypot !== '') {
- // Bot detected – pretend success
- header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
- $_SESSION['form_success'] = true;
- exit;
- } else {
- // Server-side validation
- if ($formData['fname'] === '') {
- $formErrors[] = 'Bitte geben Sie Ihren Vornamen an.';
- }
- if ($formData['lname'] === '') {
- $formErrors[] = 'Bitte geben Sie Ihren Nachnamen an.';
- }
- if ($formData['email'] === '' || !filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) {
- $formErrors[] = 'Bitte geben Sie eine gültige E-Mail-Adresse an.';
- }
- if ($formData['message'] === '') {
- $formErrors[] = 'Bitte geben Sie eine Nachricht ein.';
- }
-
- // Header injection check
- if (containsHeaderInjection($formData['email']) || containsHeaderInjection($formData['fname'] . ' ' . $formData['lname'])) {
- $formErrors[] = 'Ungültige Zeichen in den Eingabefeldern.';
- }
-
- // Minimum time check – form submitted too fast (< 3 seconds)
- $formTime = isset($_POST['form_time']) ? (int) $_POST['form_time'] : 0;
- if ($formTime > 0 && (time() - $formTime) < 3) {
- $formErrors[] = 'Das Formular wurde zu schnell abgeschickt. Bitte versuchen Sie es erneut.';
- }
-
- // Session rate limit – max 1 submission per 60 seconds
- $lastSubmit = $_SESSION['last_contact_submit'] ?? 0;
- if ($lastSubmit && (time() - $lastSubmit) < 60) {
- $formErrors[] = 'Bitte warten Sie einen Moment vor der nächsten Anfrage.';
- }
-
- // Send email if no errors
- if (empty($formErrors)) {
- $to = 'mki@kies-media.de';
- $subject = 'Kontaktanfrage: ' . $formData['interest'];
- $body = "Von: {$formData['fname']} {$formData['lname']}\n"
- . "E-Mail: {$formData['email']}\n";
- if ($formData['phone'] !== '') {
- $body .= "Telefon: {$formData['phone']}\n";
- }
- $body .= "Anliegen: {$formData['interest']}\n\n"
- . $formData['message'];
-
- $headers = "From: {$formData['email']}\r\n";
- $headers .= "Reply-To: {$formData['email']}\r\n";
- $headers .= "Content-Type: text/plain; charset=UTF-8\r\n";
- $headers .= "X-Mailer: PHP/" . phpversion();
-
- $mailSent = mail($to, $subject, $body, $headers);
-
- if ($mailSent) {
- $_SESSION['last_contact_submit'] = time();
- header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
- $_SESSION['form_success'] = true;
- exit;
- } else {
- $formErrors[] = 'Leider konnte die E-Mail nicht gesendet werden. Bitte versuchen Sie es später erneut oder schreiben Sie uns direkt an mki@kies-media.de.';
- }
- }
- }
- if (!empty($formErrors)) {
- header('Location: ' . $_SERVER['REQUEST_URI'] . '#form-result');
- $_SESSION['form_errors'] = $formErrors;
- $_SESSION['form_data'] = $formData;
- exit;
- }
-}
-?>
-
-
-
-
-
- Einfamilienhaus mieten Schleusingen | 227 m², 6 Zimmer | 1.300 € Kaltmiete
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Bahnhofstraße 10
-
-
-
-
-
- Jetzt anfragen
-
-
-
-
-
-
-
-
-
Zur Langzeitmiete · Ab sofort verfügbar
-
- Großzügiges
-
- Einfamilienhaus
-
- in Schleusingen
-
-
- Schleusinger Bahnhofstraße 10
- 227 m² Wohnfläche
- 6 Zimmer
- 3 Etagen + Dachterrasse
-
-
-
-
-
-
-
-
-
-
Das Objekt
-
Wohnen mit Charakter und viel Raum
-
- Vermietet wird ein vollständiges Einfamilienhaus in ruhiger Lage von Schleusingen. Das
- Haus verbindet historischen Charme mit modernem Wohnkomfort auf drei großzügigen Etagen.
-
-
- Garage für zwei Fahrzeuge, großzügige Dachterrasse mit 35,8 m², vollausgestattete Küche,
- Vollbad sowie Abstell- und Nutzräume machen das Haus zu einem außergewöhnlichen
- Mietobjekt.
-
-
-
-
154,9 m²
-
Nutzfläche
-
-
-
35,8 m²
-
Dachterrasse
-
-
-
-
-
-
-
-
-
-
Wohnzimmer · 42,6 m²
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Außenansicht
-
-
-
-
-
-
-
Wohnzimmer · 42,6 m²
-
-
-
-
-
-
-
Küche · 18,4 m²
-
-
-
-
-
-
-
Schlafzimmer · 18 m²
-
-
-
-
-
-
-
Badezimmer · 9,8 m²
-
-
-
-
-
-
-
Kinderzimmer 1 · 21,7 m²
-
-
-
-
-
-
-
Kinderzimmer 2 · 15,7 m²
-
-
-
-
-
-
-
Kinderzimmer Detail
-
-
-
-
-
-
-
Gästezimmer · 11,5 m²
-
-
-
-
-
-
-
Wohnbereich
-
-
-
-
-
-
-
Wohnbereich Detail
-
-
-
-
-
-
-
Hausansicht
-
-
-
-
-
- Raumaufteilung
- Großzügig auf allen Etagen
-
-
-
-
-
-
- Flur
- 20,1 m²
-
-
- WC
- 0,8 m²
-
-
- Garage / Partykeller
- 42,6 m²
-
-
- Abstellraum 1
- 9,9 m²
-
-
- Abstellraum 2
- 7,8 m²
-
-
- Heizungskeller
- 18,3 m²
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Flur
- 20,1 m²
-
-
- Wohnzimmer
- 42,6 m²
-
-
- Gästezimmer
- 11,5 m²
-
-
- Badezimmer
- 9,8 m²
-
-
- Küche
- 18,4 m²
-
-
- Schlafzimmer
- 18,0 m²
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Flur
- 13,9 m²
-
-
- Kinderzimmer 1
- 21,7 m²
-
-
- Kinderzimmer 2
- 15,7 m²
-
-
- Spielzimmer
- 6,3 m²
-
-
- Ankleidezimmer
- 1,4 m²
-
-
- Dachterrasse
- 9,0 m² (25% von 35,8 m²)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Dachboden unten (ungeheizt)
- 52 m²
-
-
- Dachboden Mitte (ungeheizt)
- 31 m²
-
-
- Dachboden oben (ungeheizt)
- 11 m²
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Mietkonditionen
-
Transparente Preisgestaltung
-
-
-
Kaltmiete
-
1.300 €
-
pro Monat
-
-
-
Gesamtmiete warm
-
1.600 €
-
inkl. 300 € Nebenkosten
-
-
-
Kaution
-
2.600 €
-
2 Nettokaltmieten
-
-
-
-
- Verfügbarkeit
- Ab sofort · unbefristete Laufzeit
-
-
- Nebenkosten
- Vorauszahlung 300 €/Monat, jährliche Abrechnung
-
-
- Energieausweis
- Wird bei Mietbeginn übergeben · Erdgasheizung
-
-
- Haustiere
- Auf Anfrage
-
-
-
-
-
-
- Standort
- Zentral und ruhig zugleich
-
-
-
🛒
-
-
Einkaufen & Versorgung
-
- Supermärkte, Ärzte, Apotheken und Schulen sind fußläufig erreichbar
-
-
-
-
-
🚌
-
-
Öffentlicher Nahverkehr
-
- Zentrale Bushaltestelle ca. 200 m entfernt — direkte Verbindungen in die Region
-
-
-
-
-
🏛
-
-
Innenstadt Schleusingen
-
Wochenmarkt und Stadtmitte nur ca. 500 m entfernt
-
-
-
-
📍
-
-
Genaue Adresse
-
- Schleusinger Bahnhofstraße 10
-
- 98533 Schleusingen, Thüringen
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
×
-
-
-
-
-
-
diff --git a/js/haus-schleusingen.js b/js/haus-schleusingen.js
deleted file mode 100644
index 1d6bf06..0000000
--- a/js/haus-schleusingen.js
+++ /dev/null
@@ -1,131 +0,0 @@
-$(function () {
- // Navbar scroll
- $(window).on("scroll", function () {
- if ($(this).scrollTop() > 60) $("#navbar").addClass("scrolled");
- else $("#navbar").removeClass("scrolled");
- });
-
- // Hero animation on load
- setTimeout(function () {
- $("#heroContent").addClass("visible");
- $("#heroBg").addClass("loaded");
- }, 200);
-
- // Scroll animations
- function checkVisible() {
- $(".fact, [data-animate]").each(function () {
- var el = $(this);
- var top = el.offset().top;
- var windowBottom = $(window).scrollTop() + $(window).height();
- if (windowBottom > top + 60) {
- el.addClass("visible");
- el.css({ opacity: 1, transform: "translateY(0)" });
- }
- });
- }
- $("[data-animate]").css({
- opacity: 0,
- transform: "translateY(30px)",
- transition: "opacity 0.8s ease, transform 0.8s ease",
- });
- $(window).on("scroll", checkVisible);
- checkVisible();
-
- // Floor accordion
- $(".floor-header").on("click", function () {
- var item = $(this).closest(".floor-item");
- var isOpen = item.hasClass("open");
- $(".floor-item").removeClass("open");
- $(".floor-body").slideUp(300);
- if (!isOpen) {
- item.addClass("open");
- item.find(".floor-body").slideDown(300);
- }
- });
-
- // Lightbox – gallery grid items
- $(document).on("click", ".grid-item", function () {
- var src = $(this).data("img") || $(this).find("img").attr("src");
- $("#lightboxImg").off("error").on("error", function () {
- // WebP fallback: try original format
- if ($(this).attr('src').endsWith('.webp')) {
- $(this).attr('src', src.replace(/\.webp$/, '.png'));
- }
- });
- $("#lightboxImg").attr("src", src);
- $("#lightbox").addClass("open");
- $("body").css("overflow", "hidden");
- });
-
- // Lightbox – floor plan images in Raumaufteilung
- $(document).on("click", ".floor-plan img[data-img]", function () {
- var src = $(this).data("img");
- $("#lightboxImg").off("error").on("error", function () {
- if ($(this).attr('src').endsWith('.webp')) {
- $(this).attr('src', src.replace(/\.webp$/, '.png'));
- }
- });
- $("#lightboxImg").attr("src", src);
- $("#lightbox").addClass("open");
- $("body").css("overflow", "hidden");
- });
- $("#lightboxClose, #lightbox").on("click", function (e) {
- if (e.target === this) {
- $("#lightbox").removeClass("open");
- $("body").css("overflow", "");
- }
- });
- $(document).on("keydown", function (e) {
- if (e.key === "Escape") {
- $("#lightbox").removeClass("open");
- $("body").css("overflow", "");
- }
- });
-
- // Form submit is handled server-side by PHP – no JS intervention needed.
-});
-
-// Mobile hamburger menu (vanilla JS)
-(function () {
- var hamburger = document.querySelector(".nav-hamburger");
- var nav = document.getElementById("navbar");
- var overlay = document.querySelector(".nav-mobile-overlay");
- var links = nav ? nav.querySelectorAll(".nav-links a") : [];
-
- function toggleMenu() {
- var isOpen = hamburger.classList.toggle("active");
- nav.classList.toggle("mobile-open", isOpen);
- if (overlay) overlay.classList.toggle("active", isOpen);
- hamburger.setAttribute("aria-expanded", isOpen);
- document.body.style.overflow = isOpen ? "hidden" : "";
- }
-
- function closeMenu() {
- hamburger.classList.remove("active");
- nav.classList.remove("mobile-open");
- if (overlay) overlay.classList.remove("active");
- hamburger.setAttribute("aria-expanded", "false");
- document.body.style.overflow = "";
- }
-
- if (hamburger) {
- hamburger.addEventListener("click", toggleMenu);
- }
-
- if (overlay) {
- overlay.addEventListener("click", closeMenu);
- }
-
- links.forEach(function (link) {
- link.addEventListener("click", closeMenu);
- });
-
- document.addEventListener("keydown", function (e) {
- if (e.key === "Escape") closeMenu();
- });
-
- // Close on resize to desktop
- window.addEventListener("resize", function () {
- if (window.innerWidth > 900) closeMenu();
- });
-})();
diff --git a/nginx.conf b/nginx.conf
old mode 100644
new mode 100755
diff --git a/package.json b/package.json
old mode 100644
new mode 100755
diff --git a/page-preview.png b/page-preview.png
old mode 100644
new mode 100755
diff --git a/public/.htaccess b/public/.htaccess
new file mode 100644
index 0000000..9be60ac
--- /dev/null
+++ b/public/.htaccess
@@ -0,0 +1,15 @@
+# Enable rewrite engine
+RewriteEngine On
+
+# Legacy redirects (301) – must be before the catch-all
+RewriteRule ^impressum\.html$ /impressum [R=301,L]
+RewriteRule ^datenschutz\.html$ /datenschutz [R=301,L]
+RewriteRule ^haus-schleusingen\.html$ / [R=301,L]
+
+# Serve existing files/directories directly (css, js, images, fonts, etc.)
+RewriteCond %{REQUEST_FILENAME} -f [OR]
+RewriteCond %{REQUEST_FILENAME} -d
+RewriteRule ^ - [L]
+
+# Route everything else through the front controller
+RewriteRule ^(.*)$ index.php [QSA,L]
diff --git a/bilder/Außenansicht-2-small.png b/public/bilder/Außenansicht-2-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Außenansicht-2-small.png
rename to public/bilder/Außenansicht-2-small.png
diff --git a/bilder/Außenansicht-2-small.webp b/public/bilder/Außenansicht-2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Außenansicht-2-small.webp
rename to public/bilder/Außenansicht-2-small.webp
diff --git a/bilder/Außenansicht-2.png b/public/bilder/Außenansicht-2.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Außenansicht-2.png
rename to public/bilder/Außenansicht-2.png
diff --git a/bilder/Außenansicht-2.webp b/public/bilder/Außenansicht-2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Außenansicht-2.webp
rename to public/bilder/Außenansicht-2.webp
diff --git a/bilder/Bad-2-small.jpg b/public/bilder/Bad-2-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-2-small.jpg
rename to public/bilder/Bad-2-small.jpg
diff --git a/bilder/Bad-2-small.webp b/public/bilder/Bad-2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-2-small.webp
rename to public/bilder/Bad-2-small.webp
diff --git a/bilder/Bad-2.jpeg b/public/bilder/Bad-2.jpeg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-2.jpeg
rename to public/bilder/Bad-2.jpeg
diff --git a/bilder/Bad-2.webp b/public/bilder/Bad-2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-2.webp
rename to public/bilder/Bad-2.webp
diff --git a/bilder/Bad-3-small.jpg b/public/bilder/Bad-3-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-3-small.jpg
rename to public/bilder/Bad-3-small.jpg
diff --git a/bilder/Bad-3-small.webp b/public/bilder/Bad-3-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-3-small.webp
rename to public/bilder/Bad-3-small.webp
diff --git a/bilder/Bad-3.jpeg b/public/bilder/Bad-3.jpeg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-3.jpeg
rename to public/bilder/Bad-3.jpeg
diff --git a/bilder/Bad-3.webp b/public/bilder/Bad-3.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-3.webp
rename to public/bilder/Bad-3.webp
diff --git a/bilder/Bad-4-small.jpg b/public/bilder/Bad-4-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-4-small.jpg
rename to public/bilder/Bad-4-small.jpg
diff --git a/bilder/Bad-4-small.webp b/public/bilder/Bad-4-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-4-small.webp
rename to public/bilder/Bad-4-small.webp
diff --git a/bilder/Bad-4.jpeg b/public/bilder/Bad-4.jpeg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-4.jpeg
rename to public/bilder/Bad-4.jpeg
diff --git a/bilder/Bad-4.webp b/public/bilder/Bad-4.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-4.webp
rename to public/bilder/Bad-4.webp
diff --git a/bilder/Bad-small.jpg b/public/bilder/Bad-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-small.jpg
rename to public/bilder/Bad-small.jpg
diff --git a/bilder/Bad-small.webp b/public/bilder/Bad-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad-small.webp
rename to public/bilder/Bad-small.webp
diff --git a/bilder/Bad.jpg b/public/bilder/Bad.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad.jpg
rename to public/bilder/Bad.jpg
diff --git a/bilder/Bad.webp b/public/bilder/Bad.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Bad.webp
rename to public/bilder/Bad.webp
diff --git a/bilder/Kinderzimmer 2-small.png b/public/bilder/Kinderzimmer 2-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 2-small.png
rename to public/bilder/Kinderzimmer 2-small.png
diff --git a/bilder/Kinderzimmer 2-small.webp b/public/bilder/Kinderzimmer 2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 2-small.webp
rename to public/bilder/Kinderzimmer 2-small.webp
diff --git a/bilder/Kinderzimmer 2.jpg b/public/bilder/Kinderzimmer 2.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 2.jpg
rename to public/bilder/Kinderzimmer 2.jpg
diff --git a/bilder/Kinderzimmer 2.webp b/public/bilder/Kinderzimmer 2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 2.webp
rename to public/bilder/Kinderzimmer 2.webp
diff --git a/bilder/Kinderzimmer 3-small.png b/public/bilder/Kinderzimmer 3-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 3-small.png
rename to public/bilder/Kinderzimmer 3-small.png
diff --git a/bilder/Kinderzimmer 3-small.webp b/public/bilder/Kinderzimmer 3-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 3-small.webp
rename to public/bilder/Kinderzimmer 3-small.webp
diff --git a/bilder/Kinderzimmer 3.jpg b/public/bilder/Kinderzimmer 3.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 3.jpg
rename to public/bilder/Kinderzimmer 3.jpg
diff --git a/bilder/Kinderzimmer 3.webp b/public/bilder/Kinderzimmer 3.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer 3.webp
rename to public/bilder/Kinderzimmer 3.webp
diff --git a/bilder/Kinderzimmer-small.png b/public/bilder/Kinderzimmer-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer-small.png
rename to public/bilder/Kinderzimmer-small.png
diff --git a/bilder/Kinderzimmer-small.webp b/public/bilder/Kinderzimmer-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer-small.webp
rename to public/bilder/Kinderzimmer-small.webp
diff --git a/bilder/Kinderzimmer.png b/public/bilder/Kinderzimmer.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer.png
rename to public/bilder/Kinderzimmer.png
diff --git a/bilder/Kinderzimmer.webp b/public/bilder/Kinderzimmer.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Kinderzimmer.webp
rename to public/bilder/Kinderzimmer.webp
diff --git a/bilder/Küche 1-small.jpg b/public/bilder/Küche 1-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Küche 1-small.jpg
rename to public/bilder/Küche 1-small.jpg
diff --git a/bilder/Küche 1-small.webp b/public/bilder/Küche 1-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Küche 1-small.webp
rename to public/bilder/Küche 1-small.webp
diff --git a/bilder/Küche 1.jpg b/public/bilder/Küche 1.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Küche 1.jpg
rename to public/bilder/Küche 1.jpg
diff --git a/bilder/Küche 1.webp b/public/bilder/Küche 1.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/Küche 1.webp
rename to public/bilder/Küche 1.webp
diff --git a/public/bilder/favicon/apple-touch-icon.png b/public/bilder/favicon/apple-touch-icon.png
new file mode 100755
index 0000000..b34ffd2
Binary files /dev/null and b/public/bilder/favicon/apple-touch-icon.png differ
diff --git a/public/bilder/favicon/favicon-16x16.png b/public/bilder/favicon/favicon-16x16.png
new file mode 100755
index 0000000..44929fe
Binary files /dev/null and b/public/bilder/favicon/favicon-16x16.png differ
diff --git a/public/bilder/favicon/favicon-32x32.png b/public/bilder/favicon/favicon-32x32.png
new file mode 100755
index 0000000..6352df8
Binary files /dev/null and b/public/bilder/favicon/favicon-32x32.png differ
diff --git a/public/bilder/favicon/favicon.ico b/public/bilder/favicon/favicon.ico
new file mode 100755
index 0000000..c49b767
Binary files /dev/null and b/public/bilder/favicon/favicon.ico differ
diff --git a/public/bilder/favicon/site.webmanifest b/public/bilder/favicon/site.webmanifest
new file mode 100755
index 0000000..f6f3493
--- /dev/null
+++ b/public/bilder/favicon/site.webmanifest
@@ -0,0 +1,10 @@
+{
+ "name": "Haus Schleusingen",
+ "short_name": "HS",
+ "icons": [
+ { "src": "/bilder/favicon/favicon-32x32.png", "sizes": "32x32", "type": "image/png" },
+ { "src": "/bilder/favicon/favicon-16x16.png", "sizes": "16x16", "type": "image/png" }
+ ],
+ "theme_color": "#1c1917",
+ "background_color": "#fafaf9"
+}
diff --git a/bilder/grundrisse/Dachboden unten 2-small.jpg b/public/bilder/grundrisse/Dachboden unten 2-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten 2-small.jpg
rename to public/bilder/grundrisse/Dachboden unten 2-small.jpg
diff --git a/bilder/grundrisse/Dachboden unten 2-small.webp b/public/bilder/grundrisse/Dachboden unten 2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten 2-small.webp
rename to public/bilder/grundrisse/Dachboden unten 2-small.webp
diff --git a/bilder/grundrisse/Dachboden unten 2.png b/public/bilder/grundrisse/Dachboden unten 2.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten 2.png
rename to public/bilder/grundrisse/Dachboden unten 2.png
diff --git a/bilder/grundrisse/Dachboden unten 2.webp b/public/bilder/grundrisse/Dachboden unten 2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten 2.webp
rename to public/bilder/grundrisse/Dachboden unten 2.webp
diff --git a/bilder/grundrisse/Dachboden unten-small.jpg b/public/bilder/grundrisse/Dachboden unten-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten-small.jpg
rename to public/bilder/grundrisse/Dachboden unten-small.jpg
diff --git a/bilder/grundrisse/Dachboden unten-small.webp b/public/bilder/grundrisse/Dachboden unten-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten-small.webp
rename to public/bilder/grundrisse/Dachboden unten-small.webp
diff --git a/bilder/grundrisse/Dachboden unten.png b/public/bilder/grundrisse/Dachboden unten.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten.png
rename to public/bilder/grundrisse/Dachboden unten.png
diff --git a/bilder/grundrisse/Dachboden unten.webp b/public/bilder/grundrisse/Dachboden unten.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/Dachboden unten.webp
rename to public/bilder/grundrisse/Dachboden unten.webp
diff --git a/bilder/grundrisse/EG 3D-small.jpg b/public/bilder/grundrisse/EG 3D-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG 3D-small.jpg
rename to public/bilder/grundrisse/EG 3D-small.jpg
diff --git a/bilder/grundrisse/EG 3D-small.webp b/public/bilder/grundrisse/EG 3D-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG 3D-small.webp
rename to public/bilder/grundrisse/EG 3D-small.webp
diff --git a/bilder/grundrisse/EG 3D.png b/public/bilder/grundrisse/EG 3D.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG 3D.png
rename to public/bilder/grundrisse/EG 3D.png
diff --git a/bilder/grundrisse/EG 3D.webp b/public/bilder/grundrisse/EG 3D.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG 3D.webp
rename to public/bilder/grundrisse/EG 3D.webp
diff --git a/bilder/grundrisse/EG-small.jpg b/public/bilder/grundrisse/EG-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG-small.jpg
rename to public/bilder/grundrisse/EG-small.jpg
diff --git a/bilder/grundrisse/EG-small.webp b/public/bilder/grundrisse/EG-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG-small.webp
rename to public/bilder/grundrisse/EG-small.webp
diff --git a/bilder/grundrisse/EG.png b/public/bilder/grundrisse/EG.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG.png
rename to public/bilder/grundrisse/EG.png
diff --git a/bilder/grundrisse/EG.webp b/public/bilder/grundrisse/EG.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/EG.webp
rename to public/bilder/grundrisse/EG.webp
diff --git a/bilder/grundrisse/OG 1 2-small.jpg b/public/bilder/grundrisse/OG 1 2-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 2-small.jpg
rename to public/bilder/grundrisse/OG 1 2-small.jpg
diff --git a/bilder/grundrisse/OG 1 2-small.webp b/public/bilder/grundrisse/OG 1 2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 2-small.webp
rename to public/bilder/grundrisse/OG 1 2-small.webp
diff --git a/bilder/grundrisse/OG 1 2.png b/public/bilder/grundrisse/OG 1 2.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 2.png
rename to public/bilder/grundrisse/OG 1 2.png
diff --git a/bilder/grundrisse/OG 1 2.webp b/public/bilder/grundrisse/OG 1 2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 2.webp
rename to public/bilder/grundrisse/OG 1 2.webp
diff --git a/bilder/grundrisse/OG 1 3D-small.jpg b/public/bilder/grundrisse/OG 1 3D-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 3D-small.jpg
rename to public/bilder/grundrisse/OG 1 3D-small.jpg
diff --git a/bilder/grundrisse/OG 1 3D-small.webp b/public/bilder/grundrisse/OG 1 3D-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 3D-small.webp
rename to public/bilder/grundrisse/OG 1 3D-small.webp
diff --git a/bilder/grundrisse/OG 1 3D.png b/public/bilder/grundrisse/OG 1 3D.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 3D.png
rename to public/bilder/grundrisse/OG 1 3D.png
diff --git a/bilder/grundrisse/OG 1 3D.webp b/public/bilder/grundrisse/OG 1 3D.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 1 3D.webp
rename to public/bilder/grundrisse/OG 1 3D.webp
diff --git a/bilder/grundrisse/OG 2 3D-small.jpg b/public/bilder/grundrisse/OG 2 3D-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 3D-small.jpg
rename to public/bilder/grundrisse/OG 2 3D-small.jpg
diff --git a/bilder/grundrisse/OG 2 3D-small.webp b/public/bilder/grundrisse/OG 2 3D-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 3D-small.webp
rename to public/bilder/grundrisse/OG 2 3D-small.webp
diff --git a/bilder/grundrisse/OG 2 3D.png b/public/bilder/grundrisse/OG 2 3D.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 3D.png
rename to public/bilder/grundrisse/OG 2 3D.png
diff --git a/bilder/grundrisse/OG 2 3D.webp b/public/bilder/grundrisse/OG 2 3D.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 3D.webp
rename to public/bilder/grundrisse/OG 2 3D.webp
diff --git a/bilder/grundrisse/OG 2 grundriss-small.jpg b/public/bilder/grundrisse/OG 2 grundriss-small.jpg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 grundriss-small.jpg
rename to public/bilder/grundrisse/OG 2 grundriss-small.jpg
diff --git a/bilder/grundrisse/OG 2 grundriss-small.webp b/public/bilder/grundrisse/OG 2 grundriss-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 grundriss-small.webp
rename to public/bilder/grundrisse/OG 2 grundriss-small.webp
diff --git a/bilder/grundrisse/OG 2 grundriss.png b/public/bilder/grundrisse/OG 2 grundriss.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 grundriss.png
rename to public/bilder/grundrisse/OG 2 grundriss.png
diff --git a/bilder/grundrisse/OG 2 grundriss.webp b/public/bilder/grundrisse/OG 2 grundriss.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/grundrisse/OG 2 grundriss.webp
rename to public/bilder/grundrisse/OG 2 grundriss.webp
diff --git a/bilder/kinderzimmer 2 2-small.png b/public/bilder/kinderzimmer 2 2-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/kinderzimmer 2 2-small.png
rename to public/bilder/kinderzimmer 2 2-small.png
diff --git a/bilder/kinderzimmer 2 2-small.webp b/public/bilder/kinderzimmer 2 2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/kinderzimmer 2 2-small.webp
rename to public/bilder/kinderzimmer 2 2-small.webp
diff --git a/bilder/kinderzimmer 2 2.jpeg b/public/bilder/kinderzimmer 2 2.jpeg
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/kinderzimmer 2 2.jpeg
rename to public/bilder/kinderzimmer 2 2.jpeg
diff --git a/bilder/kinderzimmer 2 2.webp b/public/bilder/kinderzimmer 2 2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/kinderzimmer 2 2.webp
rename to public/bilder/kinderzimmer 2 2.webp
diff --git a/bilder/schlafzimmer-small.png b/public/bilder/schlafzimmer-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/schlafzimmer-small.png
rename to public/bilder/schlafzimmer-small.png
diff --git a/bilder/schlafzimmer-small.webp b/public/bilder/schlafzimmer-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/schlafzimmer-small.webp
rename to public/bilder/schlafzimmer-small.webp
diff --git a/bilder/schlafzimmer.png b/public/bilder/schlafzimmer.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/schlafzimmer.png
rename to public/bilder/schlafzimmer.png
diff --git a/bilder/schlafzimmer.webp b/public/bilder/schlafzimmer.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/schlafzimmer.webp
rename to public/bilder/schlafzimmer.webp
diff --git a/bilder/wohnzimmer2-small.png b/public/bilder/wohnzimmer2-small.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/wohnzimmer2-small.png
rename to public/bilder/wohnzimmer2-small.png
diff --git a/bilder/wohnzimmer2-small.webp b/public/bilder/wohnzimmer2-small.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/wohnzimmer2-small.webp
rename to public/bilder/wohnzimmer2-small.webp
diff --git a/bilder/wohnzimmer2.png b/public/bilder/wohnzimmer2.png
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/wohnzimmer2.png
rename to public/bilder/wohnzimmer2.png
diff --git a/bilder/wohnzimmer2.webp b/public/bilder/wohnzimmer2.webp
old mode 100644
new mode 100755
similarity index 100%
rename from bilder/wohnzimmer2.webp
rename to public/bilder/wohnzimmer2.webp
diff --git a/css/haus-schleusingen.css b/public/css/haus-schleusingen.css
old mode 100644
new mode 100755
similarity index 95%
rename from css/haus-schleusingen.css
rename to public/css/haus-schleusingen.css
index 68a09b1..a4bab37
--- a/css/haus-schleusingen.css
+++ b/public/css/haus-schleusingen.css
@@ -1,7 +1,55 @@
+/* SKIP LINK */
+.skip-link {
+ position: absolute;
+ left: -9999px;
+ top: 0;
+ background: var(--accent);
+ color: var(--white);
+ padding: 0.75rem 1.5rem;
+ font-size: 0.85rem;
+ font-weight: 500;
+ z-index: 200;
+ text-decoration: none;
+ transition: none;
+}
+
+.skip-link:focus {
+ left: 0;
+ outline: 2px solid var(--white);
+ outline-offset: 2px;
+}
+
+/* FOCUS VISIBLE */
+*:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+.lightbox-close:focus-visible {
+ outline: 2px solid var(--white);
+ outline-offset: 2px;
+}
+
+button:focus-visible,
+a:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+.grid-item:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+.floor-header:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: -2px;
+}
+
:root {
--cream: #f5f0e8;
--warm: #e8dfd0;
- --stone: #9e9485;
+ --stone: #7a7062;
--dark: #1c1a17;
--charcoal: #2e2b26;
--accent: #8b6914;
@@ -1168,7 +1216,7 @@ footer {
gap: 0;
z-index: 95;
border-bottom: 1px solid var(--warm);
- animation: slideDown 0.3s ease;
+ animation: slide-down 0.3s ease;
}
nav.mobile-open .nav-links a {
@@ -1191,11 +1239,12 @@ footer {
background: transparent;
}
- @keyframes slideDown {
+ @keyframes slide-down {
from {
opacity: 0;
transform: translateY(-10px);
}
+
to {
opacity: 1;
transform: translateY(0);
diff --git a/fonts/CormorantGaramond-Light.ttf b/public/fonts/CormorantGaramond-Light.ttf
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/CormorantGaramond-Light.ttf
rename to public/fonts/CormorantGaramond-Light.ttf
diff --git a/fonts/CormorantGaramond-Regular.ttf b/public/fonts/CormorantGaramond-Regular.ttf
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/CormorantGaramond-Regular.ttf
rename to public/fonts/CormorantGaramond-Regular.ttf
diff --git a/fonts/CormorantGaramond-SemiBold.ttf b/public/fonts/CormorantGaramond-SemiBold.ttf
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/CormorantGaramond-SemiBold.ttf
rename to public/fonts/CormorantGaramond-SemiBold.ttf
diff --git a/fonts/DMSans-Light.ttf b/public/fonts/DMSans-Light.ttf
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/DMSans-Light.ttf
rename to public/fonts/DMSans-Light.ttf
diff --git a/fonts/DMSans-Medium.ttf b/public/fonts/DMSans-Medium.ttf
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/DMSans-Medium.ttf
rename to public/fonts/DMSans-Medium.ttf
diff --git a/fonts/DMSans-Regular.ttf b/public/fonts/DMSans-Regular.ttf
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/DMSans-Regular.ttf
rename to public/fonts/DMSans-Regular.ttf
diff --git a/fonts/fonts.css b/public/fonts/fonts.css
old mode 100644
new mode 100755
similarity index 100%
rename from fonts/fonts.css
rename to public/fonts/fonts.css
diff --git a/public/index.php b/public/index.php
new file mode 100644
index 0000000..005abcc
--- /dev/null
+++ b/public/index.php
@@ -0,0 +1,44 @@
+addRoute('/', \App\Controllers\HomeController::class, 'index');
+$router->addRoute('/impressum', \App\Controllers\ImpressumController::class, 'index');
+$router->addRoute('/datenschutz', \App\Controllers\DatenschutzController::class, 'index');
+
+// Dispatch
+$uri = $_SERVER['REQUEST_URI'] ?? '/';
+$router->dispatch($uri);
diff --git a/public/js/haus-schleusingen.js b/public/js/haus-schleusingen.js
new file mode 100755
index 0000000..205e143
--- /dev/null
+++ b/public/js/haus-schleusingen.js
@@ -0,0 +1,259 @@
+document.addEventListener("DOMContentLoaded", function () {
+ // Navbar scroll
+ var navbar = document.getElementById("navbar");
+ window.addEventListener("scroll", function () {
+ if (window.scrollY > 60) navbar.classList.add("scrolled");
+ else navbar.classList.remove("scrolled");
+ });
+
+ // Hero animation on load
+ setTimeout(function () {
+ document.getElementById("heroContent").classList.add("visible");
+ document.getElementById("heroBg").classList.add("loaded");
+ }, 200);
+
+ // Scroll animations via IntersectionObserver
+ var animElements = document.querySelectorAll(".fact, [data-animate]");
+ animElements.forEach(function (el) {
+ el.style.opacity = "0";
+ el.style.transform = "translateY(30px)";
+ el.style.transition = "opacity 0.8s ease, transform 0.8s ease";
+ });
+
+ if ("IntersectionObserver" in window) {
+ var observer = new IntersectionObserver(
+ function (entries) {
+ entries.forEach(function (entry) {
+ if (entry.isIntersecting) {
+ entry.target.classList.add("visible");
+ entry.target.style.opacity = "1";
+ entry.target.style.transform = "translateY(0)";
+ observer.unobserve(entry.target);
+ }
+ });
+ },
+ { rootMargin: "0px 0px -60px 0px" }
+ );
+ animElements.forEach(function (el) {
+ observer.observe(el);
+ });
+ } else {
+ // Fallback: show all immediately
+ animElements.forEach(function (el) {
+ el.classList.add("visible");
+ el.style.opacity = "1";
+ el.style.transform = "translateY(0)";
+ });
+ }
+
+ // Floor accordion
+ // Floor accordion (vanilla JS + a11y)
+ document.querySelectorAll(".floor-header").forEach(function (header) {
+ header.addEventListener("click", function () {
+ var item = this.closest(".floor-item");
+ var isOpen = item.classList.contains("open");
+ var allItems = document.querySelectorAll(".floor-item");
+
+ // Close all
+ allItems.forEach(function (fi) {
+ fi.classList.remove("open");
+ var hdr = fi.querySelector(".floor-header");
+ if (hdr) hdr.setAttribute("aria-expanded", "false");
+ var body = fi.querySelector(".floor-body");
+ if (body) body.style.display = "none";
+ });
+
+ // Open clicked if it was closed
+ if (!isOpen) {
+ item.classList.add("open");
+ this.setAttribute("aria-expanded", "true");
+ var body = item.querySelector(".floor-body");
+ if (body) body.style.display = "block";
+ }
+ });
+ });
+
+ // Accordion keyboard handler (Enter/Space)
+ document.querySelectorAll(".floor-header").forEach(function (header) {
+ header.addEventListener("keydown", function (e) {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ this.click();
+ }
+ });
+ });
+
+ // Lightbox – track last focused element for focus return
+ var lightboxTrigger = null;
+
+ function openLightbox(src) {
+ lightboxTrigger = document.activeElement;
+ var img = document.getElementById("lightboxImg");
+ // WebP fallback: if .webp fails, try original format
+ img.onerror = function () {
+ if (img.getAttribute('src').endsWith('.webp')) {
+ img.setAttribute('src', src.replace(/\.webp$/, '.png'));
+ }
+ };
+ img.setAttribute("src", src);
+ img.setAttribute("alt", "");
+ document.getElementById("lightbox").classList.add("open");
+ document.body.style.overflow = "hidden";
+ // Set focus to close button
+ setTimeout(function () {
+ document.getElementById("lightboxClose").focus();
+ }, 50);
+ }
+
+ function closeLightbox() {
+ document.getElementById("lightbox").classList.remove("open");
+ document.body.style.overflow = "";
+ // Return focus to trigger
+ if (lightboxTrigger) {
+ lightboxTrigger.focus();
+ lightboxTrigger = null;
+ }
+ }
+
+ // Lightbox – gallery grid items
+ document.querySelectorAll(".grid-item").forEach(function (item) {
+ item.addEventListener("click", function () {
+ var src = this.dataset.img || this.querySelector("img").getAttribute("src");
+ openLightbox(src);
+ });
+ });
+
+ // Gallery keyboard handler (Enter/Space)
+ document.querySelectorAll(".grid-item").forEach(function (item) {
+ item.setAttribute("tabindex", "0");
+ item.addEventListener("keydown", function (e) {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ this.click();
+ }
+ });
+ });
+
+ // Lightbox – floor plan images in Raumaufteilung
+ document.querySelectorAll(".floor-plan img[data-img]").forEach(function (img) {
+ img.addEventListener("click", function () {
+ openLightbox(this.dataset.img);
+ });
+ });
+
+
+
+ document.getElementById("lightboxClose").addEventListener("click", closeLightbox);
+ document.getElementById("lightbox").addEventListener("click", function (e) {
+ if (e.target === this) closeLightbox();
+ });
+ document.addEventListener("keydown", function (e) {
+ if (e.key === "Escape") closeLightbox();
+ });
+
+ // Focus trap for lightbox
+ document.getElementById("lightbox").addEventListener("keydown", function (e) {
+ if (e.key !== "Tab") return;
+
+ var focusable = this.querySelectorAll("button, [href], input, select, textarea, [tabindex]:not([tabindex='-1'])");
+ focusable = Array.from(focusable).filter(function (el) { return el.offsetParent !== null; });
+ if (focusable.length === 0) return;
+
+ var first = focusable[0];
+ var last = focusable[focusable.length - 1];
+
+ if (e.shiftKey) {
+ if (document.activeElement === first) {
+ e.preventDefault();
+ last.focus();
+ }
+ } else {
+ if (document.activeElement === last) {
+ e.preventDefault();
+ first.focus();
+ }
+ }
+ });
+
+ // Form submit is handled server-side by PHP – no JS intervention needed.
+ // Form submit – opens email client with pre-filled mailto: link
+ document.getElementById("contactForm").addEventListener("submit", function (e) {
+ e.preventDefault();
+
+ var fname = document.getElementById("fname").value.trim();
+ var lname = document.getElementById("lname").value.trim();
+ var email = document.getElementById("email").value.trim();
+ var phone = document.getElementById("phone").value.trim();
+ var interest = document.getElementById("interest").value;
+ var message = document.getElementById("message").value.trim();
+
+ var subject = "Kontaktanfrage: " + interest;
+ var body = "Von: " + fname + " " + lname + "\n";
+ body += "E-Mail: " + email + "\n";
+ if (phone) body += "Telefon: " + phone + "\n";
+ body += "Anliegen: " + interest + "\n\n";
+ body += message;
+
+ var mailto =
+ "mailto:mki@kies-media.de" +
+ "?subject=" + encodeURIComponent(subject) +
+ "&body=" + encodeURIComponent(body);
+
+ window.location.href = mailto;
+
+ // Show success message
+ this.style.display = "none";
+ var success = document.getElementById("formSuccess");
+ success.style.display = "block";
+ success.style.opacity = "0";
+ success.style.transition = "opacity 0.4s ease";
+ requestAnimationFrame(function () {
+ success.style.opacity = "1";
+ });
+ });
+});
+
+// Mobile hamburger menu (vanilla JS)
+(function () {
+ var hamburger = document.querySelector(".nav-hamburger");
+ var nav = document.getElementById("navbar");
+ var overlay = document.querySelector(".nav-mobile-overlay");
+ var links = nav ? nav.querySelectorAll(".nav-links a") : [];
+
+ function toggleMenu() {
+ var isOpen = hamburger.classList.toggle("active");
+ nav.classList.toggle("mobile-open", isOpen);
+ if (overlay) overlay.classList.toggle("active", isOpen);
+ hamburger.setAttribute("aria-expanded", isOpen);
+ document.body.style.overflow = isOpen ? "hidden" : "";
+ }
+
+ function closeMenu() {
+ hamburger.classList.remove("active");
+ nav.classList.remove("mobile-open");
+ if (overlay) overlay.classList.remove("active");
+ hamburger.setAttribute("aria-expanded", "false");
+ document.body.style.overflow = "";
+ }
+
+ if (hamburger) {
+ hamburger.addEventListener("click", toggleMenu);
+ }
+
+ if (overlay) {
+ overlay.addEventListener("click", closeMenu);
+ }
+
+ links.forEach(function (link) {
+ link.addEventListener("click", closeMenu);
+ });
+
+ document.addEventListener("keydown", function (e) {
+ if (e.key === "Escape") closeMenu();
+ });
+
+ // Close on resize to desktop
+ window.addEventListener("resize", function () {
+ if (window.innerWidth > 900) closeMenu();
+ });
+})();
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100755
index 0000000..516c855
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,3 @@
+User-agent: *
+Allow: /
+Sitemap: https://haus-schleusingen.de/haus-schleusingen.html
diff --git a/robots.txt b/robots.txt
old mode 100644
new mode 100755
diff --git a/screenshot-landingpage-thumb.png b/screenshot-landingpage-thumb.png
old mode 100644
new mode 100755
diff --git a/screenshot-landingpage.png b/screenshot-landingpage.png
old mode 100644
new mode 100755