Compare commits
15 Commits
5167634ee6
...
feature/is
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b73603293 | ||
| d609175b3c | |||
|
|
1fcdca95b7 | ||
| 88ef7aa6ac | |||
| bf53da13be | |||
| 2307c379dc | |||
| 2c6ed749d5 | |||
| c2f2709790 | |||
| 69ca8efa47 | |||
| 40001adbce | |||
| 158f07e374 | |||
| 76b1ec58c2 | |||
| 565c8b304d | |||
| 51d4f96b20 | |||
| 7706f11106 |
@@ -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 {
|
:root {
|
||||||
--cream: #f5f0e8;
|
--cream: #f5f0e8;
|
||||||
--warm: #e8dfd0;
|
--warm: #e8dfd0;
|
||||||
--stone: #9e9485;
|
--stone: #7a7062;
|
||||||
--dark: #1c1a17;
|
--dark: #1c1a17;
|
||||||
--charcoal: #2e2b26;
|
--charcoal: #2e2b26;
|
||||||
--accent: #8b6914;
|
--accent: #8b6914;
|
||||||
@@ -92,23 +140,105 @@ nav.scrolled .nav-links a:hover {
|
|||||||
|
|
||||||
.nav-cta {
|
.nav-cta {
|
||||||
font-family: "DM Sans", sans-serif;
|
font-family: "DM Sans", sans-serif;
|
||||||
font-size: 0.78rem;
|
font-size: 0.82rem;
|
||||||
font-weight: 500;
|
font-weight: 600;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.12em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
background: var(--accent);
|
background: var(--accent);
|
||||||
color: var(--white);
|
color: var(--white);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 0.65rem 1.5rem;
|
padding: 0.8rem 2rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
border-radius: 2px;
|
||||||
|
box-shadow: 0 2px 12px rgb(139 105 20 / 35%);
|
||||||
transition:
|
transition:
|
||||||
background 0.3s,
|
background 0.3s,
|
||||||
transform 0.2s;
|
transform 0.2s,
|
||||||
|
box-shadow 0.3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-cta:hover {
|
.nav-cta:hover {
|
||||||
background: var(--accent-light);
|
background: var(--accent-light);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 20px rgb(139 105 20 / 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HAMBURGER */
|
||||||
|
.nav-hamburger {
|
||||||
|
display: none;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
z-index: 110;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger span,
|
||||||
|
.nav-hamburger span::before,
|
||||||
|
.nav-hamburger span::after {
|
||||||
|
display: block;
|
||||||
|
width: 22px;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 1px;
|
||||||
|
transition:
|
||||||
|
transform 0.3s ease,
|
||||||
|
opacity 0.3s ease,
|
||||||
|
background 0.4s;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger span::before,
|
||||||
|
.nav-hamburger span::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger span::before {
|
||||||
|
transform: translateY(-7px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger span::after {
|
||||||
|
transform: translateY(7px);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.scrolled .nav-hamburger span,
|
||||||
|
nav.scrolled .nav-hamburger span::before,
|
||||||
|
nav.scrolled .nav-hamburger span::after {
|
||||||
|
background: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger.active span {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger.active span::before {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
background: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-hamburger.active span::after {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
background: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-mobile-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgb(28 26 23 / 50%);
|
||||||
|
z-index: 90;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-mobile-overlay.active {
|
||||||
|
display: block;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HERO */
|
/* HERO */
|
||||||
@@ -909,6 +1039,36 @@ nav.scrolled .nav-links a:hover {
|
|||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-errors {
|
||||||
|
background: #fdf2f2;
|
||||||
|
border: 1px solid #e8a0a0;
|
||||||
|
padding: 1rem 1.2rem;
|
||||||
|
margin-bottom: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-errors ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0 0 0 1.2rem;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-errors li {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #9e2c2c;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hp-field {
|
||||||
|
position: absolute;
|
||||||
|
left: -9999px;
|
||||||
|
top: -9999px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.form-success {
|
.form-success {
|
||||||
display: none;
|
display: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -1038,6 +1198,58 @@ footer {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-hamburger {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile slide-down nav */
|
||||||
|
nav.mobile-open .nav-links {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: rgb(253 252 250 / 98%);
|
||||||
|
backdrop-filter: blur(12px);
|
||||||
|
padding: 5rem 1.5rem 2rem;
|
||||||
|
gap: 0;
|
||||||
|
z-index: 95;
|
||||||
|
border-bottom: 1px solid var(--warm);
|
||||||
|
animation: slideDown 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.mobile-open .nav-links a {
|
||||||
|
color: var(--charcoal);
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 1rem 0;
|
||||||
|
border-bottom: 1px solid var(--warm);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.mobile-open .nav-hamburger span,
|
||||||
|
nav.mobile-open .nav-hamburger span::before,
|
||||||
|
nav.mobile-open .nav-hamburger span::after {
|
||||||
|
background: var(--dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav.mobile-open .nav-hamburger.active span {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hero-content {
|
.hero-content {
|
||||||
padding: 0 1.5rem 4rem;
|
padding: 0 1.5rem 4rem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ module.exports = [
|
|||||||
sourceType: "script",
|
sourceType: "script",
|
||||||
globals: {
|
globals: {
|
||||||
...globals.browser,
|
...globals.browser,
|
||||||
...globals.jquery,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
|
|||||||
@@ -1,3 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// --- Helper functions ---
|
||||||
|
function normalizeContactValue(string $value): string
|
||||||
|
{
|
||||||
|
return trim($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeContactValue(string $value): string
|
||||||
|
{
|
||||||
|
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsHeaderInjection(string $value): bool
|
||||||
|
{
|
||||||
|
return (bool) preg_match('/[\r\n]/', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// --- Form processing ---
|
||||||
|
$formErrors = [];
|
||||||
|
$formSuccess = false;
|
||||||
|
if (!empty($_SESSION['form_success'])) {
|
||||||
|
$formSuccess = true;
|
||||||
|
unset($_SESSION['form_success']);
|
||||||
|
}
|
||||||
|
if (!empty($_SESSION['form_errors'])) {
|
||||||
|
$formErrors = $_SESSION['form_errors'];
|
||||||
|
unset($_SESSION['form_errors']);
|
||||||
|
}
|
||||||
|
if (!empty($_SESSION['form_data'])) {
|
||||||
|
$formData = $_SESSION['form_data'];
|
||||||
|
unset($_SESSION['form_data']);
|
||||||
|
} else {
|
||||||
|
$formData = ['fname' => '', '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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="de">
|
<html lang="de">
|
||||||
<head>
|
<head>
|
||||||
@@ -58,12 +182,15 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="fonts/fonts.css" />
|
<link rel="stylesheet" href="fonts/fonts.css" />
|
||||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="css/haus-schleusingen.css" />
|
<link rel="stylesheet" href="css/haus-schleusingen.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav id="navbar">
|
<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>
|
<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">
|
<ul class="nav-links">
|
||||||
<li><a href="#galerie">Galerie</a></li>
|
<li><a href="#galerie">Galerie</a></li>
|
||||||
<li><a href="#grundriss">Grundriss</a></li>
|
<li><a href="#grundriss">Grundriss</a></li>
|
||||||
@@ -77,6 +204,7 @@
|
|||||||
Jetzt anfragen
|
Jetzt anfragen
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="nav-mobile-overlay" aria-hidden="true"></div>
|
||||||
|
|
||||||
<section class="hero" id="hero">
|
<section class="hero" id="hero">
|
||||||
<div
|
<div
|
||||||
@@ -107,6 +235,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<main id="main-content">
|
||||||
<div class="facts-strip">
|
<div class="facts-strip">
|
||||||
<div class="fact">
|
<div class="fact">
|
||||||
<div class="fact-val">227</div>
|
<div class="fact-val">227</div>
|
||||||
@@ -160,7 +289,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="galerie" class="gallery-section">
|
<section id="galerie" class="gallery-section" aria-label="Fotogalerie">
|
||||||
<div class="gallery-header">
|
<div class="gallery-header">
|
||||||
<div>
|
<div>
|
||||||
<div class="section-eyebrow">Fotogalerie</div>
|
<div class="section-eyebrow">Fotogalerie</div>
|
||||||
@@ -170,54 +299,54 @@
|
|||||||
<div class="masonry-grid">
|
<div class="masonry-grid">
|
||||||
<div class="grid-sizer"></div>
|
<div class="grid-sizer"></div>
|
||||||
|
|
||||||
<div class="grid-item" data-img="bilder/Außenansicht-2.png">
|
<div class="grid-item" data-img="bilder/Außenansicht-2.png" role="button" tabindex="0" aria-label="Außenansicht – Großansicht öffnen">
|
||||||
<img src="bilder/Außenansicht-2-small.png" alt="Außenansicht" />
|
<img src="bilder/Außenansicht-2-small.png" alt="Außenansicht des Einfamilienhauses" />
|
||||||
<span class="grid-item-label">Außenansicht</span>
|
<span class="grid-item-label">Außenansicht</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/wohnzimmer2.png">
|
<div class="grid-item" data-img="bilder/wohnzimmer2.png" role="button" tabindex="0" aria-label="Wohnzimmer – Großansicht öffnen">
|
||||||
<img src="bilder/wohnzimmer2-small.png" alt="Wohnzimmer" />
|
<img src="bilder/wohnzimmer2-small.png" alt="Wohnzimmer mit 42,6 m² Wohnfläche" />
|
||||||
<span class="grid-item-label">Wohnzimmer · 42,6 m²</span>
|
<span class="grid-item-label">Wohnzimmer · 42,6 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/Küche 1.jpg">
|
<div class="grid-item" data-img="bilder/Küche 1.jpg" role="button" tabindex="0" aria-label="Küche – Großansicht öffnen">
|
||||||
<img src="bilder/Küche 1.jpg" alt="Küche" />
|
<img src="bilder/Küche 1.jpg" alt="Küche mit 18,4 m²" />
|
||||||
<span class="grid-item-label">Küche · 18,4 m²</span>
|
<span class="grid-item-label">Küche · 18,4 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/schlafzimmer.png">
|
<div class="grid-item" data-img="bilder/schlafzimmer.png" role="button" tabindex="0" aria-label="Schlafzimmer – Großansicht öffnen">
|
||||||
<img src="bilder/schlafzimmer-small.png" alt="Schlafzimmer" />
|
<img src="bilder/schlafzimmer-small.png" alt="Schlafzimmer mit 18 m²" />
|
||||||
<span class="grid-item-label">Schlafzimmer · 18 m²</span>
|
<span class="grid-item-label">Schlafzimmer · 18 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/Bad.jpg">
|
<div class="grid-item" data-img="bilder/Bad.jpg" role="button" tabindex="0" aria-label="Badezimmer – Großansicht öffnen">
|
||||||
<img src="bilder/Bad.jpg" alt="Badezimmer" />
|
<img src="bilder/Bad.jpg" alt="Badezimmer mit 9,8 m²" />
|
||||||
<span class="grid-item-label">Badezimmer · 9,8 m²</span>
|
<span class="grid-item-label">Badezimmer · 9,8 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/Kinderzimmer.png">
|
<div class="grid-item" data-img="bilder/Kinderzimmer.png" role="button" tabindex="0" aria-label="Kinderzimmer 1 – Großansicht öffnen">
|
||||||
<img src="bilder/Kinderzimmer-small.png" alt="Kinderzimmer 1" />
|
<img src="bilder/Kinderzimmer-small.png" alt="Kinderzimmer 1 mit 21,7 m²" />
|
||||||
<span class="grid-item-label">Kinderzimmer 1 · 21,7 m²</span>
|
<span class="grid-item-label">Kinderzimmer 1 · 21,7 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/Kinderzimmer 2.jpg">
|
<div class="grid-item" data-img="bilder/Kinderzimmer 2.jpg" role="button" tabindex="0" aria-label="Kinderzimmer 2 – Großansicht öffnen">
|
||||||
<img src="bilder/Kinderzimmer 2-small.png" alt="Kinderzimmer 2" />
|
<img src="bilder/Kinderzimmer 2-small.png" alt="Kinderzimmer 2 mit 15,7 m²" />
|
||||||
<span class="grid-item-label">Kinderzimmer 2 · 15,7 m²</span>
|
<span class="grid-item-label">Kinderzimmer 2 · 15,7 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/kinderzimmer 2 2.jpeg">
|
<div class="grid-item" data-img="bilder/kinderzimmer 2 2.jpeg" role="button" tabindex="0" aria-label="Kinderzimmer Detail – Großansicht öffnen">
|
||||||
<img src="bilder/kinderzimmer 2 2-small.png" alt="Kinderzimmer Detail" />
|
<img src="bilder/kinderzimmer 2 2-small.png" alt="Detailansicht Kinderzimmer" />
|
||||||
<span class="grid-item-label">Kinderzimmer Detail</span>
|
<span class="grid-item-label">Kinderzimmer Detail</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/Kinderzimmer 3.jpg">
|
<div class="grid-item" data-img="bilder/Kinderzimmer 3.jpg" role="button" tabindex="0" aria-label="Gästezimmer – Großansicht öffnen">
|
||||||
<img src="bilder/Kinderzimmer 3-small.png" alt="Kinderzimmer 3" />
|
<img src="bilder/Kinderzimmer 3-small.png" alt="Gästezimmer mit 11,5 m²" />
|
||||||
<span class="grid-item-label">Gästezimmer · 11,5 m²</span>
|
<span class="grid-item-label">Gästezimmer · 11,5 m²</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/Bad-2.jpg">
|
<div class="grid-item" data-img="bilder/Bad-2.jpg" role="button" tabindex="0" aria-label="Zweites Bad – Großansicht öffnen">
|
||||||
<img src="bilder/Bad-2-small.jpg" alt="Wohnbereich Detail 1" />
|
<img src="bilder/Bad-2-small.jpg" alt="Zweites Badezimmer im Haus" />
|
||||||
<span class="grid-item-label">Wohnbereich</span>
|
<span class="grid-item-label">Wohnbereich</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/bad3.jpg">
|
<div class="grid-item" data-img="bilder/bad3.jpg" role="button" tabindex="0" aria-label="Drittes Bad – Großansicht öffnen">
|
||||||
<img src="bilder/Bad-3-small.jpg" alt="Wohnbereich Detail 2" />
|
<img src="bilder/Bad-3-small.jpg" alt="Drittes Badezimmer im Haus" />
|
||||||
<span class="grid-item-label">Wohnbereich Detail</span>
|
<span class="grid-item-label">Wohnbereich Detail</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-item" data-img="bilder/WhatsApp Image 2026-03-30 at 07.50.42 (2).jpeg">
|
<div class="grid-item" data-img="bilder/WhatsApp Image 2026-03-30 at 07.50.42 (2).jpeg" role="button" tabindex="0" aria-label="Hausansicht – Großansicht öffnen">
|
||||||
<img
|
<img
|
||||||
src="bilder/WhatsApp Image 2026-03-30 at 07.50.42 (2).jpeg"
|
src="bilder/WhatsApp Image 2026-03-30 at 07.50.42 (2).jpeg"
|
||||||
alt="Wohnbereich Detail 3"
|
alt="Weitere Außenansicht des Einfamilienhauses"
|
||||||
/>
|
/>
|
||||||
<span class="grid-item-label">Hausansicht</span>
|
<span class="grid-item-label">Hausansicht</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -229,14 +358,14 @@
|
|||||||
<h2>Großzügig auf allen Etagen</h2>
|
<h2>Großzügig auf allen Etagen</h2>
|
||||||
<div class="floor-accordion">
|
<div class="floor-accordion">
|
||||||
<div class="floor-item">
|
<div class="floor-item">
|
||||||
<div class="floor-header">
|
<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>
|
<span class="floor-title">Erdgeschoss</span>
|
||||||
<div class="floor-size">
|
<div class="floor-size">
|
||||||
<span>99,5 m²</span>
|
<span>99,5 m²</span>
|
||||||
<div class="floor-icon">+</div>
|
<div class="floor-icon">+</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-body">
|
<div class="floor-body" id="floor-body-0" role="region" aria-labelledby="floor-title-0">
|
||||||
<div class="floor-rooms-grid">
|
<div class="floor-rooms-grid">
|
||||||
<div class="room-chip">
|
<div class="room-chip">
|
||||||
Flur
|
Flur
|
||||||
@@ -278,14 +407,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-item">
|
<div class="floor-item">
|
||||||
<div class="floor-header">
|
<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>
|
<span class="floor-title">1. Obergeschoss</span>
|
||||||
<div class="floor-size">
|
<div class="floor-size">
|
||||||
<span>120,4 m²</span>
|
<span>120,4 m²</span>
|
||||||
<div class="floor-icon">+</div>
|
<div class="floor-icon">+</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-body">
|
<div class="floor-body" id="floor-body-1" role="region" aria-labelledby="floor-title-1">
|
||||||
<div class="floor-rooms-grid">
|
<div class="floor-rooms-grid">
|
||||||
<div class="room-chip">
|
<div class="room-chip">
|
||||||
Flur
|
Flur
|
||||||
@@ -327,14 +456,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-item">
|
<div class="floor-item">
|
||||||
<div class="floor-header">
|
<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>
|
<span class="floor-title">2. Obergeschoss</span>
|
||||||
<div class="floor-size">
|
<div class="floor-size">
|
||||||
<span>68 m²</span>
|
<span>68 m²</span>
|
||||||
<div class="floor-icon">+</div>
|
<div class="floor-icon">+</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-body">
|
<div class="floor-body" id="floor-body-2" role="region" aria-labelledby="floor-title-2">
|
||||||
<div class="floor-rooms-grid">
|
<div class="floor-rooms-grid">
|
||||||
<div class="room-chip">
|
<div class="room-chip">
|
||||||
Flur
|
Flur
|
||||||
@@ -376,14 +505,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-item">
|
<div class="floor-item">
|
||||||
<div class="floor-header">
|
<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>
|
<span class="floor-title">Dachboden</span>
|
||||||
<div class="floor-size">
|
<div class="floor-size">
|
||||||
<span>94 m² Nutzfläche</span>
|
<span>94 m² Nutzfläche</span>
|
||||||
<div class="floor-icon">+</div>
|
<div class="floor-icon">+</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="floor-body">
|
<div class="floor-body" id="floor-body-3" role="region" aria-labelledby="floor-title-3">
|
||||||
<div class="floor-rooms-grid">
|
<div class="floor-rooms-grid">
|
||||||
<div class="room-chip">
|
<div class="room-chip">
|
||||||
Dachboden unten (ungeheizt)
|
Dachboden unten (ungeheizt)
|
||||||
@@ -415,7 +544,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="pricing-section" id="miete">
|
<section class="pricing-section" id="miete" aria-label="Mietkonditionen">
|
||||||
<div class="pricing-inner">
|
<div class="pricing-inner">
|
||||||
<div class="section-eyebrow">Mietkonditionen</div>
|
<div class="section-eyebrow">Mietkonditionen</div>
|
||||||
<h2>Transparente Preisgestaltung</h2>
|
<h2>Transparente Preisgestaltung</h2>
|
||||||
@@ -513,7 +642,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="contact-section" id="kontakt">
|
<section class="contact-section" id="kontakt" aria-label="Kontaktformular">
|
||||||
<div class="contact-inner">
|
<div class="contact-inner">
|
||||||
<div class="section-eyebrow">Kontakt</div>
|
<div class="section-eyebrow">Kontakt</div>
|
||||||
<h2>
|
<h2>
|
||||||
@@ -527,15 +656,31 @@
|
|||||||
paar Terminvorschläge an.
|
paar Terminvorschläge an.
|
||||||
</p>
|
</p>
|
||||||
<div class="contact-form">
|
<div class="contact-form">
|
||||||
<form id="contactForm">
|
<?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-result" 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-row">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="fname">Vorname</label>
|
<label for="fname">Vorname</label>
|
||||||
<input type="text" id="fname" name="fname" placeholder="Max" required />
|
<input type="text" id="fname" name="fname" placeholder="Max" required value="<?= escapeContactValue($formData['fname']) ?>" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="lname">Nachname</label>
|
<label for="lname">Nachname</label>
|
||||||
<input type="text" id="lname" name="lname" placeholder="Mustermann" required />
|
<input type="text" id="lname" name="lname" placeholder="Mustermann" required value="<?= escapeContactValue($formData['lname']) ?>" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
@@ -547,20 +692,25 @@
|
|||||||
name="email"
|
name="email"
|
||||||
placeholder="max@beispiel.de"
|
placeholder="max@beispiel.de"
|
||||||
required
|
required
|
||||||
|
value="<?= escapeContactValue($formData['email']) ?>"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="phone">Telefon</label>
|
<label for="phone">Telefon</label>
|
||||||
<input type="tel" id="phone" name="phone" placeholder="+49 ..." />
|
<input type="tel" id="phone" name="phone" placeholder="+49 ..." value="<?= escapeContactValue($formData['phone']) ?>" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-field full">
|
<div class="form-field full">
|
||||||
<label for="interest">Anliegen</label>
|
<label for="interest">Anliegen</label>
|
||||||
<select id="interest" name="interest">
|
<select id="interest" name="interest">
|
||||||
<option>Besichtigung anfragen</option>
|
<?php
|
||||||
<option>Allgemeine Informationen</option>
|
$interestOptions = ['Besichtigung anfragen', 'Allgemeine Informationen', 'Mietbewerbung einreichen'];
|
||||||
<option>Mietbewerbung einreichen</option>
|
foreach ($interestOptions as $opt):
|
||||||
|
$selected = ($formData['interest'] === $opt) ? ' selected' : '';
|
||||||
|
?>
|
||||||
|
<option<?= $selected ?>><?= escapeContactValue($opt) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -572,16 +722,20 @@
|
|||||||
name="message"
|
name="message"
|
||||||
rows="4"
|
rows="4"
|
||||||
placeholder="Ihre Nachricht ..."
|
placeholder="Ihre Nachricht ..."
|
||||||
></textarea>
|
required
|
||||||
|
><?= escapeContactValue($formData['message']) ?></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Honeypot: hidden field for spam bots -->
|
||||||
|
<div class="hp-field" aria-hidden="true">
|
||||||
|
<label for="website">Website</label>
|
||||||
|
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off" />
|
||||||
|
</div>
|
||||||
|
<!-- Form load timestamp for minimum-submit-time check -->
|
||||||
|
<input type="hidden" name="form_time" value="<?= time() ?>" />
|
||||||
<button type="submit" class="btn-submit">Anfrage absenden</button>
|
<button type="submit" class="btn-submit">Anfrage absenden</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="form-success" id="formSuccess">
|
<?php endif; ?>
|
||||||
<p>Vielen Dank für Ihre Anfrage!</p>
|
|
||||||
<br />
|
|
||||||
<small>Ihr E-Mail-Programm wurde geöffnet. Bitte senden Sie die E-Mail ab, damit Ihre Anfrage bei uns eingeht.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="contact-details">
|
<div class="contact-details">
|
||||||
<p>Oder schreiben Sie uns direkt: <a href="mailto:mki@kies-media.de">mki@kies-media.de</a></p>
|
<p>Oder schreiben Sie uns direkt: <a href="mailto:mki@kies-media.de">mki@kies-media.de</a></p>
|
||||||
@@ -589,7 +743,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
</main>
|
||||||
|
<footer role="contentinfo">
|
||||||
<div class="footer-logo">Bahnhofstraße 10 · Schleusingen</div>
|
<div class="footer-logo">Bahnhofstraße 10 · Schleusingen</div>
|
||||||
<div class="footer-links">
|
<div class="footer-links">
|
||||||
<a href="impressum.html">Impressum</a>
|
<a href="impressum.html">Impressum</a>
|
||||||
@@ -597,9 +752,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<div class="lightbox" id="lightbox">
|
<div class="lightbox" id="lightbox" role="dialog" aria-modal="true" aria-label="Bildansicht">
|
||||||
<button class="lightbox-close" id="lightboxClose">×</button>
|
<button class="lightbox-close" id="lightboxClose" aria-label="Bildansicht schließen">×</button>
|
||||||
<img src="" id="lightboxImg" alt="Vollbild" />
|
<img src="" id="lightboxImg" alt="" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/haus-schleusingen.js"></script>
|
<script src="js/haus-schleusingen.js"></script>
|
||||||
@@ -1,86 +1,208 @@
|
|||||||
$(function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
// Navbar scroll
|
// Navbar scroll
|
||||||
$(window).on("scroll", function () {
|
var navbar = document.getElementById("navbar");
|
||||||
if ($(this).scrollTop() > 60) $("#navbar").addClass("scrolled");
|
window.addEventListener("scroll", function () {
|
||||||
else $("#navbar").removeClass("scrolled");
|
if (window.scrollY > 60) navbar.classList.add("scrolled");
|
||||||
|
else navbar.classList.remove("scrolled");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Hero animation on load
|
// Hero animation on load
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
$("#heroContent").addClass("visible");
|
document.getElementById("heroContent").classList.add("visible");
|
||||||
$("#heroBg").addClass("loaded");
|
document.getElementById("heroBg").classList.add("loaded");
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
// Scroll animations
|
// Scroll animations via IntersectionObserver
|
||||||
function checkVisible() {
|
var animElements = document.querySelectorAll(".fact, [data-animate]");
|
||||||
$(".fact, [data-animate]").each(function () {
|
animElements.forEach(function (el) {
|
||||||
var el = $(this);
|
el.style.opacity = "0";
|
||||||
var top = el.offset().top;
|
el.style.transform = "translateY(30px)";
|
||||||
var windowBottom = $(window).scrollTop() + $(window).height();
|
el.style.transition = "opacity 0.8s ease, transform 0.8s ease";
|
||||||
if (windowBottom > top + 60) {
|
});
|
||||||
el.addClass("visible");
|
|
||||||
el.css({ opacity: 1, transform: "translateY(0)" });
|
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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
$("[data-animate]").css({
|
{ rootMargin: "0px 0px -60px 0px" }
|
||||||
opacity: 0,
|
);
|
||||||
transform: "translateY(30px)",
|
animElements.forEach(function (el) {
|
||||||
transition: "opacity 0.8s ease, transform 0.8s ease",
|
observer.observe(el);
|
||||||
});
|
});
|
||||||
$(window).on("scroll", checkVisible);
|
} else {
|
||||||
checkVisible();
|
// 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
|
||||||
$(".floor-header").on("click", function () {
|
// Floor accordion (vanilla JS + a11y)
|
||||||
var item = $(this).closest(".floor-item");
|
document.querySelectorAll(".floor-header").forEach(function (header) {
|
||||||
var isOpen = item.hasClass("open");
|
header.addEventListener("click", function () {
|
||||||
$(".floor-item").removeClass("open");
|
var item = this.closest(".floor-item");
|
||||||
$(".floor-body").slideUp(300);
|
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) {
|
if (!isOpen) {
|
||||||
item.addClass("open");
|
item.classList.add("open");
|
||||||
item.find(".floor-body").slideDown(300);
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (e.key === "Enter" || e.key === " ") {
|
||||||
|
e.preventDefault();
|
||||||
|
$(this).trigger("click");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lightbox – gallery grid items
|
// Lightbox – track last focused element for focus return
|
||||||
$(document).on("click", ".grid-item", function () {
|
var lightboxTrigger = null;
|
||||||
var src = $(this).data("img") || $(this).find("img").attr("src");
|
|
||||||
$("#lightboxImg").attr("src", src);
|
function openLightbox(src) {
|
||||||
|
lightboxTrigger = document.activeElement;
|
||||||
|
$("#lightboxImg").attr("src", src).attr("alt", "");
|
||||||
$("#lightbox").addClass("open");
|
$("#lightbox").addClass("open");
|
||||||
$("body").css("overflow", "hidden");
|
$("body").css("overflow", "hidden");
|
||||||
|
// Set focus to close button
|
||||||
|
setTimeout(function () {
|
||||||
|
$("#lightboxClose").focus();
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeLightbox() {
|
||||||
|
$("#lightbox").removeClass("open");
|
||||||
|
$("body").css("overflow", "");
|
||||||
|
// Return focus to trigger
|
||||||
|
if (lightboxTrigger) {
|
||||||
|
$(lightboxTrigger).focus();
|
||||||
|
lightboxTrigger = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lightbox – gallery grid items
|
||||||
|
|
||||||
|
// 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
|
// Lightbox – floor plan images in Raumaufteilung
|
||||||
$(document).on("click", ".floor-plan img[data-img]", function () {
|
document.querySelectorAll(".floor-plan img[data-img]").forEach(function (img) {
|
||||||
var src = $(this).data("img");
|
img.addEventListener("click", function () {
|
||||||
$("#lightboxImg").attr("src", src);
|
openLightbox(this.dataset.img);
|
||||||
$("#lightbox").addClass("open");
|
|
||||||
$("body").css("overflow", "hidden");
|
|
||||||
});
|
});
|
||||||
$("#lightboxClose, #lightbox").on("click", function (e) {
|
});
|
||||||
if (e.target === this) {
|
|
||||||
$("#lightbox").removeClass("open");
|
function openLightbox(src) {
|
||||||
$("body").css("overflow", "");
|
lightboxTrigger = document.activeElement;
|
||||||
|
document.getElementById("lightboxImg").setAttribute("src", src);
|
||||||
|
document.getElementById("lightbox").classList.add("open");
|
||||||
|
document.body.style.overflow = "hidden";
|
||||||
|
// Focus close button
|
||||||
|
setTimeout(function () {
|
||||||
|
document.getElementById("lightboxClose").focus();
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeLightbox() {
|
||||||
|
document.getElementById("lightbox").classList.remove("open");
|
||||||
|
document.body.style.overflow = "";
|
||||||
|
// Return focus to trigger
|
||||||
|
if (lightboxTrigger) {
|
||||||
|
lightboxTrigger.focus();
|
||||||
|
lightboxTrigger = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById("lightboxClose").addEventListener("click", closeLightbox);
|
||||||
|
document.getElementById("lightbox").addEventListener("click", function (e) {
|
||||||
|
if (e.target === this) closeLightbox();
|
||||||
});
|
});
|
||||||
$(document).on("keydown", function (e) {
|
document.addEventListener("keydown", function (e) {
|
||||||
if (e.key === "Escape") {
|
if (e.key === "Escape") closeLightbox();
|
||||||
$("#lightbox").removeClass("open");
|
});
|
||||||
$("body").css("overflow", "");
|
|
||||||
|
// 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
|
// Form submit – opens email client with pre-filled mailto: link
|
||||||
$("#contactForm").on("submit", function (e) {
|
document.getElementById("contactForm").addEventListener("submit", function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
var fname = $("#fname").val().trim();
|
var fname = document.getElementById("fname").value.trim();
|
||||||
var lname = $("#lname").val().trim();
|
var lname = document.getElementById("lname").value.trim();
|
||||||
var email = $("#email").val().trim();
|
var email = document.getElementById("email").value.trim();
|
||||||
var phone = $("#phone").val().trim();
|
var phone = document.getElementById("phone").value.trim();
|
||||||
var interest = $("#interest").val();
|
var interest = document.getElementById("interest").value;
|
||||||
var message = $("#message").val().trim();
|
var message = document.getElementById("message").value.trim();
|
||||||
|
|
||||||
var subject = "Kontaktanfrage: " + interest;
|
var subject = "Kontaktanfrage: " + interest;
|
||||||
var body = "Von: " + fname + " " + lname + "\n";
|
var body = "Von: " + fname + " " + lname + "\n";
|
||||||
@@ -97,7 +219,58 @@ $(function () {
|
|||||||
window.location.href = mailto;
|
window.location.href = mailto;
|
||||||
|
|
||||||
// Show success message
|
// Show success message
|
||||||
$("#contactForm").hide();
|
this.style.display = "none";
|
||||||
$("#formSuccess").fadeIn(400);
|
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();
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|||||||
9
js/masonry.pkgd.min.js
vendored
9
js/masonry.pkgd.min.js
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user