refactor: Umstellung auf Mini-MVC-Architektur (Issue #46)
All checks were successful
Deploy Feature Branch to Test / deploy (push) Successful in 24s

- Front Controller Pattern mit public/index.php als Einstiegspunkt
- Eigenes Routing (App\Core\Router) ohne externes Framework
- Controller: HomeController, ImpressumController, DatenschutzController
- Views mit gemeinsamem Layout (app/views/layouts/main.php)
- PSR-4 Autoloading
- Statische Assets nach public/ verschoben
- Alte Dateien (index.php, impressum.html, datenschutz.html) geloescht
- 301-Redirects fuer alte URLs
- PHP 8.5 kompatibel
- Apache DocumentRoot auf public/ gesetzt
This commit is contained in:
2026-05-19 14:38:38 +00:00
parent 73635a5f03
commit 1aedcaf314
130 changed files with 1303 additions and 1179 deletions

504
app/views/home/index.php Normal file
View File

@@ -0,0 +1,504 @@
<a href="#main-content" class="skip-link">Zum Inhalt springen</a>
<nav id="navbar" role="navigation" aria-label="Hauptnavigation">
<div class="nav-logo">Bahnhofstraße 10</div>
<button class="nav-hamburger" aria-label="Navigation öffnen" aria-expanded="false">
<span></span>
</button>
<ul class="nav-links">
<li><a href="#galerie">Galerie</a></li>
<li><a href="#grundriss">Grundriss</a></li>
<li><a href="#miete">Miete</a></li>
<li><a href="#lage">Lage</a></li>
</ul>
<button
class="nav-cta"
onclick="$('html').animate({ scrollTop: $('#kontakt').offset().top }, 700)"
>
Jetzt anfragen
</button>
</nav>
<div class="nav-mobile-overlay" aria-hidden="true"></div>
<section class="hero" id="hero">
<div
class="hero-bg"
id="heroBg"
style="background-image: url(/bilder/Außenansicht-2.webp)"
></div>
<div class="hero-overlay"></div>
<div class="hero-content" id="heroContent">
<div class="hero-tag">Zur Langzeitmiete · Ab sofort verfügbar</div>
<h1>
Großzügiges
<br />
<em>Einfamilienhaus</em>
<br />
in Schleusingen
</h1>
<div class="hero-meta">
<span><strong>Schleusinger Bahnhofstraße 10</strong></span>
<span>227 Wohnfläche</span>
<span>6 Zimmer</span>
<span>3 Etagen + Dachterrasse</span>
</div>
</div>
<div class="hero-scroll">
<span>Entdecken</span>
<div class="scroll-line"></div>
</div>
</section>
<main id="main-content">
<div class="facts-strip">
<div class="fact">
<div class="fact-val">227</div>
<div class="fact-label"> Wohnfläche</div>
</div>
<div class="fact">
<div class="fact-val">6</div>
<div class="fact-label">Zimmer</div>
</div>
<div class="fact">
<div class="fact-val">3</div>
<div class="fact-label">Etagen</div>
</div>
<div class="fact">
<div class="fact-val">1.300</div>
<div class="fact-label"> Kaltmiete</div>
</div>
</div>
<section class="intro" id="intro">
<div class="intro-text" data-animate>
<div class="section-eyebrow">Das Objekt</div>
<h2>Wohnen mit Charakter und viel Raum</h2>
<p>
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.
</p>
<p>
Garage für zwei Fahrzeuge, großzügige Dachterrasse mit 35,8 , vollausgestattete Küche,
Vollbad sowie Abstell- und Nutzräume machen das Haus zu einem außergewöhnlichen
Mietobjekt.
</p>
<div class="intro-stats">
<div>
<div class="istat-val">154,9 </div>
<div class="istat-label">Nutzfläche</div>
</div>
<div>
<div class="istat-val">35,8 </div>
<div class="istat-label">Dachterrasse</div>
</div>
<div>
<div class="istat-val">2 Stpl.</div>
<div class="istat-label">Garage</div>
</div>
</div>
</div>
<div class="intro-img" data-animate>
<picture>
<source srcset="/bilder/wohnzimmer2.webp" type="image/webp">
<img src="/bilder/wohnzimmer2.png" alt="Wohnzimmer" loading="lazy" />
</picture>
<div class="intro-img-badge">Wohnzimmer · 42,6 </div>
</div>
</section>
<section id="galerie" class="gallery-section" aria-label="Fotogalerie">
<div class="gallery-header">
<div>
<div class="section-eyebrow">Fotogalerie</div>
<h2>Einblicke ins Haus</h2>
</div>
</div>
<div class="masonry-grid">
<div class="grid-sizer"></div>
<div class="grid-item" data-img="/bilder/Außenansicht-2.webp" role="button" tabindex="0" aria-label="Außenansicht Großansicht öffnen">
<picture>
<source srcset="/bilder/Außenansicht-2-small.webp" type="image/webp">
<img src="/bilder/Außenansicht-2-small.png" alt="Außenansicht des Einfamilienhauses" loading="lazy" />
</picture>
<span class="grid-item-label">Außenansicht</span>
</div>
<div class="grid-item" data-img="/bilder/wohnzimmer2.webp" role="button" tabindex="0" aria-label="Wohnzimmer Großansicht öffnen">
<picture>
<source srcset="/bilder/wohnzimmer2-small.webp" type="image/webp">
<img src="/bilder/wohnzimmer2-small.png" alt="Wohnzimmer mit 42,6 m² Wohnfläche" loading="lazy" />
</picture>
<span class="grid-item-label">Wohnzimmer · 42,6 </span>
</div>
<div class="grid-item" data-img="/bilder/Küche 1.webp" role="button" tabindex="0" aria-label="Küche Großansicht öffnen">
<picture>
<source srcset="/bilder/Küche 1-small.webp" type="image/webp">
<img src="/bilder/Küche 1.jpg" alt="Küche mit 18,4 m²" loading="lazy" />
</picture>
<span class="grid-item-label">Küche · 18,4 </span>
</div>
<div class="grid-item" data-img="/bilder/schlafzimmer.webp" role="button" tabindex="0" aria-label="Schlafzimmer Großansicht öffnen">
<picture>
<source srcset="/bilder/schlafzimmer-small.webp" type="image/webp">
<img src="/bilder/schlafzimmer-small.png" alt="Schlafzimmer mit 18 m²" loading="lazy" />
</picture>
<span class="grid-item-label">Schlafzimmer · 18 </span>
</div>
<div class="grid-item" data-img="/bilder/Bad.webp" role="button" tabindex="0" aria-label="Badezimmer Großansicht öffnen">
<picture>
<source srcset="/bilder/Bad-small.webp" type="image/webp">
<img src="/bilder/Bad.jpg" alt="Badezimmer mit 9,8 m²" loading="lazy" />
</picture>
<span class="grid-item-label">Badezimmer · 9,8 </span>
</div>
<div class="grid-item" data-img="/bilder/Kinderzimmer.webp" role="button" tabindex="0" aria-label="Kinderzimmer 1 Großansicht öffnen">
<picture>
<source srcset="/bilder/Kinderzimmer-small.webp" type="image/webp">
<img src="/bilder/Kinderzimmer-small.png" alt="Kinderzimmer 1 mit 21,7 m²" loading="lazy" />
</picture>
<span class="grid-item-label">Kinderzimmer 1 · 21,7 </span>
</div>
<div class="grid-item" data-img="/bilder/Kinderzimmer 2.webp" role="button" tabindex="0" aria-label="Kinderzimmer 2 Großansicht öffnen">
<picture>
<source srcset="/bilder/Kinderzimmer 2-small.webp" type="image/webp">
<img src="/bilder/Kinderzimmer 2-small.png" alt="Kinderzimmer 2 mit 15,7 m²" loading="lazy" />
</picture>
<span class="grid-item-label">Kinderzimmer 2 · 15,7 </span>
</div>
<div class="grid-item" data-img="/bilder/kinderzimmer 2 2.webp" role="button" tabindex="0" aria-label="Kinderzimmer Detail Großansicht öffnen">
<picture>
<source srcset="/bilder/kinderzimmer 2 2-small.webp" type="image/webp">
<img src="/bilder/kinderzimmer 2 2-small.png" alt="Detailansicht Kinderzimmer" loading="lazy" />
</picture>
<span class="grid-item-label">Kinderzimmer Detail</span>
</div>
<div class="grid-item" data-img="/bilder/Kinderzimmer 3.webp" role="button" tabindex="0" aria-label="Gästezimmer Großansicht öffnen">
<picture>
<source srcset="/bilder/Kinderzimmer 3-small.webp" type="image/webp">
<img src="/bilder/Kinderzimmer 3-small.png" alt="Gästezimmer mit 11,5 m²" loading="lazy" />
</picture>
<span class="grid-item-label">Gästezimmer · 11,5 </span>
</div>
<div class="grid-item" data-img="/bilder/Bad-2.webp" role="button" tabindex="0" aria-label="Zweites Bad Großansicht öffnen">
<picture>
<source srcset="/bilder/Bad-2-small.webp" type="image/webp">
<img src="/bilder/Bad-2-small.jpg" alt="Zweites Badezimmer im Haus" loading="lazy" />
</picture>
<span class="grid-item-label">Wohnbereich</span>
</div>
<div class="grid-item" data-img="/bilder/Bad-3.webp" role="button" tabindex="0" aria-label="Drittes Bad Großansicht öffnen">
<picture>
<source srcset="/bilder/Bad-3-small.webp" type="image/webp">
<img src="/bilder/Bad-3-small.jpg" alt="Drittes Badezimmer im Haus" loading="lazy" />
</picture>
<span class="grid-item-label">Wohnbereich Detail</span>
</div>
<div class="grid-item" data-img="/bilder/Bad-4.webp" role="button" tabindex="0" aria-label="Wohnbereich Detail Großansicht öffnen">
<picture>
<source srcset="/bilder/Bad-4-small.webp" type="image/webp">
<img src="/bilder/Bad-4-small.jpg" alt="Wohnbereich Detail 3" loading="lazy" />
</picture>
<span class="grid-item-label">Hausansicht</span>
</div>
</div>
</section>
<section class="floors-section" id="grundriss">
<div class="section-eyebrow">Raumaufteilung</div>
<h2>Großzügig auf allen Etagen</h2>
<div class="floor-accordion">
<div class="floor-item">
<div class="floor-header" role="button" tabindex="0" aria-expanded="false" aria-controls="floor-body-0" id="floor-title-0">
<span class="floor-title">Erdgeschoss</span>
<div class="floor-size">
<span>99,5 </span>
<div class="floor-icon">+</div>
</div>
</div>
<div class="floor-body" id="floor-body-0" role="region" aria-labelledby="floor-title-0">
<div class="floor-rooms-grid">
<div class="room-chip">Flur<span class="room-chip-area">20,1 </span></div>
<div class="room-chip">WC<span class="room-chip-area">0,8 </span></div>
<div class="room-chip">Garage / Partykeller<span class="room-chip-area">42,6 </span></div>
<div class="room-chip">Abstellraum 1<span class="room-chip-area">9,9 </span></div>
<div class="room-chip">Abstellraum 2<span class="room-chip-area">7,8 </span></div>
<div class="room-chip">Heizungskeller<span class="room-chip-area">18,3 </span></div>
</div>
<div class="floor-plan floor-plan-multi">
<picture>
<source srcset="/bilder/grundrisse/EG-small.webp" type="image/webp">
<img src="/bilder/grundrisse/EG-small.jpg" alt="Grundriss Erdgeschoss" loading="lazy" data-img="/bilder/grundrisse/EG.webp" />
</picture>
<picture>
<source srcset="/bilder/grundrisse/EG 3D-small.webp" type="image/webp">
<img src="/bilder/grundrisse/EG 3D-small.jpg" alt="Grundriss Erdgeschoss" loading="lazy" data-img="/bilder/grundrisse/EG 3D.webp" />
</picture>
</div>
</div>
</div>
<div class="floor-item">
<div class="floor-header" role="button" tabindex="0" aria-expanded="false" aria-controls="floor-body-1" id="floor-title-1">
<span class="floor-title">1. Obergeschoss</span>
<div class="floor-size">
<span>120,4 </span>
<div class="floor-icon">+</div>
</div>
</div>
<div class="floor-body" id="floor-body-1" role="region" aria-labelledby="floor-title-1">
<div class="floor-rooms-grid">
<div class="room-chip">Flur<span class="room-chip-area">20,1 </span></div>
<div class="room-chip">Wohnzimmer<span class="room-chip-area">42,6 </span></div>
<div class="room-chip">Gästezimmer<span class="room-chip-area">11,5 </span></div>
<div class="room-chip">Badezimmer<span class="room-chip-area">9,8 </span></div>
<div class="room-chip">Küche<span class="room-chip-area">18,4 </span></div>
<div class="room-chip">Schlafzimmer<span class="room-chip-area">18,0 </span></div>
</div>
<div class="floor-plan floor-plan-multi">
<picture>
<source srcset="/bilder/grundrisse/OG 1 2-small.webp" type="image/webp">
<img src="/bilder/grundrisse/OG 1 2-small.jpg" alt="Grundriss 1. Obergeschoss" loading="lazy" data-img="/bilder/grundrisse/OG 1 2.webp" />
</picture>
<picture>
<source srcset="/bilder/grundrisse/OG 1 3D-small.webp" type="image/webp">
<img src="/bilder/grundrisse/OG 1 3D-small.jpg" alt="Grundriss 1. Obergeschoss" loading="lazy" data-img="/bilder/grundrisse/OG 1 3D.webp" />
</picture>
</div>
</div>
</div>
<div class="floor-item">
<div class="floor-header" role="button" tabindex="0" aria-expanded="false" aria-controls="floor-body-2" id="floor-title-2">
<span class="floor-title">2. Obergeschoss</span>
<div class="floor-size">
<span>68 </span>
<div class="floor-icon">+</div>
</div>
</div>
<div class="floor-body" id="floor-body-2" role="region" aria-labelledby="floor-title-2">
<div class="floor-rooms-grid">
<div class="room-chip">Flur<span class="room-chip-area">13,9 </span></div>
<div class="room-chip">Kinderzimmer 1<span class="room-chip-area">21,7 </span></div>
<div class="room-chip">Kinderzimmer 2<span class="room-chip-area">15,7 </span></div>
<div class="room-chip">Spielzimmer<span class="room-chip-area">6,3 </span></div>
<div class="room-chip">Ankleidezimmer<span class="room-chip-area">1,4 </span></div>
<div class="room-chip">Dachterrasse<span class="room-chip-area">9,0 </span> <small>(25% von 35,8 )</small></div>
</div>
<div class="floor-plan floor-plan-multi">
<picture>
<source srcset="/bilder/grundrisse/OG 2 grundriss-small.webp" type="image/webp">
<img src="/bilder/grundrisse/OG 2 grundriss-small.jpg" alt="Grundriss 2. Obergeschoss" loading="lazy" data-img="/bilder/grundrisse/OG 2 grundriss.webp" />
</picture>
<picture>
<source srcset="/bilder/grundrisse/OG 2 3D-small.webp" type="image/webp">
<img src="/bilder/grundrisse/OG 2 3D-small.jpg" alt="Grundriss 2. Obergeschoss" loading="lazy" data-img="/bilder/grundrisse/OG 2 3D.webp" />
</picture>
</div>
</div>
</div>
<div class="floor-item">
<div class="floor-header" role="button" tabindex="0" aria-expanded="false" aria-controls="floor-body-3" id="floor-title-3">
<span class="floor-title">Dachboden</span>
<div class="floor-size">
<span>94 Nutzfläche</span>
<div class="floor-icon">+</div>
</div>
</div>
<div class="floor-body" id="floor-body-3" role="region" aria-labelledby="floor-title-3">
<div class="floor-rooms-grid">
<div class="room-chip">Dachboden unten (ungeheizt)<span class="room-chip-area">52 </span></div>
<div class="room-chip">Dachboden Mitte (ungeheizt)<span class="room-chip-area">31 </span></div>
<div class="room-chip">Dachboden oben (ungeheizt)<span class="room-chip-area">11 </span></div>
</div>
<div class="floor-plan floor-plan-multi">
<picture>
<source srcset="/bilder/grundrisse/Dachboden unten 2-small.webp" type="image/webp">
<img src="/bilder/grundrisse/Dachboden unten 2-small.jpg" alt="Grundriss Dachboden" loading="lazy" data-img="/bilder/grundrisse/Dachboden unten 2.webp" />
</picture>
<picture>
<source srcset="/bilder/grundrisse/Dachboden unten-small.webp" type="image/webp">
<img src="/bilder/grundrisse/Dachboden unten-small.jpg" alt="Grundriss Dachboden" loading="lazy" data-img="/bilder/grundrisse/Dachboden unten.webp" />
</picture>
</div>
</div>
</div>
</div>
</section>
<section class="pricing-section" id="miete" aria-label="Mietkonditionen">
<div class="pricing-inner">
<div class="section-eyebrow">Mietkonditionen</div>
<h2>Transparente Preisgestaltung</h2>
<div class="price-cards">
<div class="price-card">
<div class="pc-label">Kaltmiete</div>
<div class="pc-val">1.300 </div>
<div class="pc-sub">pro Monat</div>
</div>
<div class="price-card highlight">
<div class="pc-label">Gesamtmiete warm</div>
<div class="pc-val">1.600 </div>
<div class="pc-sub">inkl. 300 Nebenkosten</div>
</div>
<div class="price-card">
<div class="pc-label">Kaution</div>
<div class="pc-val">2.600 </div>
<div class="pc-sub">2 Nettokaltmieten</div>
</div>
</div>
<div class="price-note">
<div class="pn-item">
<strong>Verfügbarkeit</strong>
Ab sofort · unbefristete Laufzeit
</div>
<div class="pn-item">
<strong>Nebenkosten</strong>
Vorauszahlung 300 /Monat, jährliche Abrechnung
</div>
<div class="pn-item">
<strong>Energieausweis</strong>
Wird bei Mietbeginn übergeben · Erdgasheizung
</div>
<div class="pn-item">
<strong>Haustiere</strong>
Auf Anfrage
</div>
</div>
</div>
</section>
<section class="lage-section" id="lage">
<div class="section-eyebrow">Standort</div>
<h2>Zentral und ruhig zugleich</h2>
<div class="lage-grid">
<div class="lage-item">
<div class="lage-icon">🛒</div>
<div>
<div class="lage-title">Einkaufen & Versorgung</div>
<div class="lage-desc">Supermärkte, Ärzte, Apotheken und Schulen sind fußläufig erreichbar</div>
</div>
</div>
<div class="lage-item">
<div class="lage-icon">🚌</div>
<div>
<div class="lage-title">Öffentlicher Nahverkehr</div>
<div class="lage-desc">Zentrale Bushaltestelle ca. 200 m entfernt direkte Verbindungen in die Region</div>
</div>
</div>
<div class="lage-item">
<div class="lage-icon">🏛</div>
<div>
<div class="lage-title">Innenstadt Schleusingen</div>
<div class="lage-desc">Wochenmarkt und Stadtmitte nur ca. 500 m entfernt</div>
</div>
</div>
<div class="lage-item">
<div class="lage-icon">📍</div>
<div>
<div class="lage-title">Genaue Adresse</div>
<div class="lage-desc">Schleusinger Bahnhofstraße 10<br />98533 Schleusingen, Thüringen</div>
</div>
</div>
</div>
<div class="lage-map-wrapper">
<iframe
src="https://maps.google.com/maps?q=50.5090045,10.7473859&t=&z=16&ie=UTF8&iwloc=&output=embed"
width="100%" height="450" style="border: 0" allowfullscreen="" loading="lazy"
referrerpolicy="no-referrer-when-downgrade"
title="Standort Bahnhofstraße 10, Schleusingen"
></iframe>
</div>
</section>
<section class="contact-section" id="kontakt" aria-label="Kontaktformular">
<div class="contact-inner">
<div class="section-eyebrow">Kontakt</div>
<h2>Interesse?<br /><em>Schreiben Sie uns.</em></h2>
<p>
Wir freuen uns über Ihre Anfrage und melden uns innerhalb von 24 Stunden.
Besichtigungstermine sind nach Absprache möglich. Bitte geben Sie bei Ihrer Anfrage ein
paar Terminvorschläge an.
</p>
<div class="contact-form">
<?php if ($formSuccess): ?>
<div id="form-result" class="form-success" style="display: block">
<p>Vielen Dank für Ihre Anfrage!</p>
<br />
<small>Wir haben Ihre Nachricht erhalten und melden uns innerhalb von 24 Stunden bei Ihnen.</small>
</div>
<?php else: ?>
<?php if (!empty($formErrors)): ?>
<div id="form-errors" class="form-errors">
<ul>
<?php foreach ($formErrors as $error): ?>
<li><?= $escapeContactValue($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form id="contactForm" method="post">
<div class="form-row">
<div class="form-field">
<label for="fname">Vorname</label>
<input type="text" id="fname" name="fname" placeholder="Max" required value="<?= $escapeContactValue($formData['fname']) ?>" />
</div>
<div class="form-field">
<label for="lname">Nachname</label>
<input type="text" id="lname" name="lname" placeholder="Mustermann" required value="<?= $escapeContactValue($formData['lname']) ?>" />
</div>
</div>
<div class="form-row">
<div class="form-field">
<label for="email">E-Mail</label>
<input type="email" id="email" name="email" placeholder="max@beispiel.de" required value="<?= $escapeContactValue($formData['email']) ?>" />
</div>
<div class="form-field">
<label for="phone">Telefon</label>
<input type="tel" id="phone" name="phone" placeholder="+49 ..." value="<?= $escapeContactValue($formData['phone']) ?>" />
</div>
</div>
<div class="form-row">
<div class="form-field full">
<label for="interest">Anliegen</label>
<select id="interest" name="interest">
<?php
$interestOptions = ['Besichtigung anfragen', 'Allgemeine Informationen', 'Mietbewerbung einreichen'];
foreach ($interestOptions as $opt):
$selected = ($formData['interest'] === $opt) ? ' selected' : '';
?>
<option<?= $selected ?>><?= $escapeContactValue($opt) ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-row">
<div class="form-field full">
<label for="message">Nachricht</label>
<textarea id="message" name="message" rows="4" placeholder="Ihre Nachricht ..." required><?= $escapeContactValue($formData['message']) ?></textarea>
</div>
</div>
<div class="hp-field" aria-hidden="true">
<label for="website">Website</label>
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off" />
</div>
<input type="hidden" name="form_time" value="<?= time() ?>" />
<button type="submit" class="btn-submit">Anfrage absenden</button>
</form>
<?php endif; ?>
</div>
<div class="contact-details">
<p>Oder schreiben Sie uns direkt: <a href="mailto:mki@kies-media.de">mki@kies-media.de</a></p>
</div>
</div>
</section>
</main>
<footer role="contentinfo">
<div class="footer-logo">Bahnhofstraße 10 · Schleusingen</div>
<div class="footer-links">
<a href="/impressum">Impressum</a>
<a href="/datenschutz">Datenschutz</a>
</div>
</footer>
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-label="Bildansicht">
<button class="lightbox-close" id="lightboxClose" aria-label="Bildansicht schließen">&times;</button>
<img src="" id="lightboxImg" alt="" />
</div>