356 lines
18 KiB
PHP
356 lines
18 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Home page — page body only (nav/footer/lightbox live in layouts/main.php).
|
|
*
|
|
* @var string $locale
|
|
* @var array<string,mixed> $formData
|
|
* @var list<string> $formErrors Translation keys, resolved via t()
|
|
* @var bool $formSuccess
|
|
* @var array<string,string> $interestKeys ['visit' => 'form.interest.visit', ...]
|
|
* @var callable(string):string $escapeContactValue
|
|
* @var callable(string,array,string=):string $t
|
|
*/
|
|
|
|
$gridItems = [
|
|
// NOTE: image filenames reflect the actual files in public/bilder/ on the server.
|
|
// 3 items were removed (gästezimmer / wohnbereich / wohnbereich-detail)
|
|
// because no matching files exist in the image inventory.
|
|
['img' => 'bilder/Außenansicht-2.png', 'key' => 'gallery.exterior', 'alt' => 'gallery.alt.exterior', 'class' => 'span-2 row-2'],
|
|
['img' => 'bilder/wohnzimmer2.png', 'key' => 'gallery.living', 'alt' => 'gallery.alt.living', 'class' => 'span-2 row-1'],
|
|
['img' => 'bilder/Küche 1.jpg', 'key' => 'gallery.kitchen', 'alt' => 'gallery.alt.kitchen', 'class' => ''],
|
|
['img' => 'bilder/schlafzimmer.png', 'key' => 'gallery.bedroom', 'alt' => 'gallery.alt.bedroom', 'class' => ''],
|
|
['img' => 'bilder/Bad.jpg', 'key' => 'gallery.bath', 'alt' => 'gallery.alt.bath', 'class' => ''],
|
|
['img' => 'bilder/Kinderzimmer 2.jpg', 'key' => 'gallery.kid1', 'alt' => 'gallery.alt.kid1', 'class' => ''],
|
|
['img' => 'bilder/Kinderzimmer 3.jpg', 'key' => 'gallery.kid2', 'alt' => 'gallery.alt.kid2', 'class' => ''],
|
|
['img' => 'bilder/kinderzimmer 2 2.webp', 'key' => 'gallery.kid_detail', 'alt' => 'gallery.alt.kid_detail', 'class' => 'span-2 row-1'],
|
|
['img' => 'bilder/Außenansicht-2.png', 'key' => 'gallery.area3', 'alt' => 'gallery.alt.exterior', 'class' => 'span-2 row-1'],
|
|
];
|
|
?>
|
|
<header class="hero" id="hero">
|
|
<img src="/bilder/Außenansicht-2.webp" alt="" class="hero-bg" id="heroBg" loading="eager" decoding="async" fetchpriority="high">
|
|
<div class="hero-overlay" aria-hidden="true"></div>
|
|
<div class="hero-content" id="heroContent">
|
|
<span class="hero-tag"><?= htmlspecialchars($t('hero.tag'), ENT_QUOTES) ?></span>
|
|
<h1 class="hero-h1">
|
|
<span class="hero-line"><?= htmlspecialchars($t('hero.h1.line1'), ENT_QUOTES) ?></span>
|
|
<span class="hero-line accent"><?= htmlspecialchars($t('hero.h1.line2'), ENT_QUOTES) ?></span>
|
|
<span class="hero-line"><?= htmlspecialchars($t('hero.h1.line3'), ENT_QUOTES) ?></span>
|
|
</h1>
|
|
<ul class="hero-meta" aria-label="<?= htmlspecialchars($t('hero.address'), ENT_QUOTES) ?>">
|
|
<li class="hero-meta-item"><?= htmlspecialchars($t('hero.address'), ENT_QUOTES) ?></li>
|
|
<li class="hero-meta-item"><?= htmlspecialchars($t('hero.area'), ENT_QUOTES) ?></li>
|
|
<li class="hero-meta-item"><?= htmlspecialchars($t('hero.rooms'), ENT_QUOTES) ?></li>
|
|
<li class="hero-meta-item"><?= htmlspecialchars($t('hero.floors'), ENT_QUOTES) ?></li>
|
|
</ul>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="facts-strip" aria-label="<?= htmlspecialchars($t('intro.stats.area'), ENT_QUOTES) ?>">
|
|
<div class="fact"><span class="fact-value">227</span><span class="fact-unit"><?= htmlspecialchars($t('facts.area'), ENT_QUOTES) ?></span></div>
|
|
<div class="fact"><span class="fact-value">6</span><span class="fact-unit"><?= htmlspecialchars($t('facts.rooms'), ENT_QUOTES) ?></span></div>
|
|
<div class="fact"><span class="fact-value">3</span><span class="fact-unit"><?= htmlspecialchars($t('facts.floors'), ENT_QUOTES) ?></span></div>
|
|
<div class="fact"><span class="fact-value">1.300</span><span class="fact-unit"><?= htmlspecialchars($t('facts.rent'), ENT_QUOTES) ?></span></div>
|
|
</section>
|
|
|
|
<section class="intro" id="intro">
|
|
<div class="intro-grid">
|
|
<div class="intro-text">
|
|
<span class="section-eyebrow"><?= htmlspecialchars($t('intro.eyebrow'), ENT_QUOTES) ?></span>
|
|
<h2><?= htmlspecialchars($t('intro.h2'), ENT_QUOTES) ?></h2>
|
|
<p><?= htmlspecialchars($t('intro.p1'), ENT_QUOTES) ?></p>
|
|
<p><?= htmlspecialchars($t('intro.p2'), ENT_QUOTES) ?></p>
|
|
</div>
|
|
<aside class="intro-stats">
|
|
<div class="stat">
|
|
<span class="stat-label"><?= htmlspecialchars($t('intro.stats.area'), ENT_QUOTES) ?></span>
|
|
<span class="stat-value">196,5 m²</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="stat-label"><?= htmlspecialchars($t('intro.stats.terrace'), ENT_QUOTES) ?></span>
|
|
<span class="stat-value">35,8 m²</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="stat-label"><?= htmlspecialchars($t('intro.stats.garage'), ENT_QUOTES) ?></span>
|
|
<span class="stat-value">2 PKW</span>
|
|
</div>
|
|
<span class="intro-badge"><?= htmlspecialchars($t('intro.badge'), ENT_QUOTES) ?></span>
|
|
</aside>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="gallery-section" id="galerie" aria-label="<?= htmlspecialchars($t('gallery.aria'), ENT_QUOTES) ?>">
|
|
<div class="section-head">
|
|
<span class="section-eyebrow"><?= htmlspecialchars($t('gallery.eyebrow'), ENT_QUOTES) ?></span>
|
|
<h2><?= htmlspecialchars($t('gallery.h2'), ENT_QUOTES) ?></h2>
|
|
</div>
|
|
<div class="masonry-grid">
|
|
<?php foreach ($gridItems as $item): ?>
|
|
<button type="button" class="grid-item"
|
|
data-img="<?= htmlspecialchars($item['img'], ENT_QUOTES) ?>"
|
|
aria-label="<?= htmlspecialchars($t($item['key']) . $t('gallery.zoom'), ENT_QUOTES) ?>">
|
|
<img src="/<?= htmlspecialchars($item['img'], ENT_QUOTES) ?>" alt="<?= htmlspecialchars($t($item['alt']), ENT_QUOTES) ?>" loading="lazy" decoding="async">
|
|
<span class="grid-item-label"><?= htmlspecialchars($t($item['key']), ENT_QUOTES) ?></span>
|
|
</button>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="floors-section" id="grundriss" aria-label="<?= htmlspecialchars($t('floors.eyebrow'), ENT_QUOTES) ?>">
|
|
<div class="section-head">
|
|
<span class="section-eyebrow"><?= htmlspecialchars($t('floors.eyebrow'), ENT_QUOTES) ?></span>
|
|
<h2><?= htmlspecialchars($t('floors.h2'), ENT_QUOTES) ?></h2>
|
|
</div>
|
|
|
|
<?php
|
|
$floorImageMap = [
|
|
'eg' => 'bilder/grundrisse/EG.png',
|
|
'og1' => 'bilder/grundrisse/OG 1 2.png',
|
|
'og2' => 'bilder/grundrisse/OG 2 grundriss.png',
|
|
'attic' => 'bilder/grundrisse/Dachboden unten.png',
|
|
];
|
|
|
|
$floors = [
|
|
['id' => 'eg', 'titleKey' => 'floors.eg.title', 'areaKey' => 'floors.eg.area', 'altKey' => 'floors.alt.eg',
|
|
'rooms' => [
|
|
['key' => 'floors.room.hall', 'size' => '21,0'],
|
|
['key' => 'floors.room.wc', 'size' => '1,7'],
|
|
['key' => 'floors.room.garage', 'size' => '23,4'],
|
|
['key' => 'floors.room.storage1', 'size' => '5,5'],
|
|
['key' => 'floors.room.heating', 'size' => '11,2'],
|
|
['key' => 'floors.room.storage2', 'size' => '6,4'],
|
|
]],
|
|
['id' => 'og1', 'titleKey' => 'floors.og1.title', 'areaKey' => 'floors.og1.area', 'altKey' => 'floors.alt.og1',
|
|
'rooms' => [
|
|
['key' => 'floors.room.living', 'size' => '42,6'],
|
|
['key' => 'floors.room.kitchen', 'size' => '18,4'],
|
|
['key' => 'floors.room.guest', 'size' => '11,5'],
|
|
['key' => 'floors.room.bath', 'size' => '9,8'],
|
|
['key' => 'floors.room.storage1','size' => '3,4'],
|
|
['key' => 'floors.room.heating', 'size' => '8,0'],
|
|
]],
|
|
['id' => 'og2', 'titleKey' => 'floors.og2.title', 'areaKey' => 'floors.og2.area', 'altKey' => 'floors.alt.og2',
|
|
'rooms' => [
|
|
['key' => 'floors.room.bedroom', 'size' => '18,0'],
|
|
['key' => 'floors.room.kid1', 'size' => '21,7'],
|
|
['key' => 'floors.room.kid2', 'size' => '15,7'],
|
|
['key' => 'floors.room.bath', 'size' => '6,4'],
|
|
]],
|
|
['id' => 'attic','titleKey' => 'floors.attic.title', 'areaKey' => 'floors.attic.area', 'altKey' => 'floors.alt.attic',
|
|
'rooms' => [
|
|
['key' => 'floors.room.attic_low', 'size' => ''],
|
|
['key' => 'floors.room.attic_mid', 'size' => ''],
|
|
['key' => 'floors.room.attic_high', 'size' => ''],
|
|
]],
|
|
];
|
|
?>
|
|
|
|
<div class="floors-accordion">
|
|
<?php foreach ($floors as $floor): ?>
|
|
<details class="floor-item" id="floor-<?= htmlspecialchars($floor['id'], ENT_QUOTES) ?>">
|
|
<summary class="floor-header">
|
|
<span class="floor-title"><?= htmlspecialchars($t($floor['titleKey']), ENT_QUOTES) ?></span>
|
|
<span class="floor-area"><?= htmlspecialchars($t($floor['areaKey']), ENT_QUOTES) ?></span>
|
|
</summary>
|
|
<div class="floor-body">
|
|
<img src="/<?= htmlspecialchars($floorImageMap[$floor['id']] ?? 'bilder/grundrisse/EG.png', ENT_QUOTES) ?>"
|
|
alt="<?= htmlspecialchars($t($floor['altKey']), ENT_QUOTES) ?>"
|
|
loading="lazy" decoding="async"
|
|
class="floor-plan-img">
|
|
<ul class="room-list">
|
|
<?php foreach ($floor['rooms'] as $room): ?>
|
|
<li>
|
|
<span class="room-name"><?= htmlspecialchars($t($room['key']), ENT_QUOTES) ?></span>
|
|
<?php if ($room['size'] !== ''): ?>
|
|
<span class="room-size"><?= htmlspecialchars($room['size'], ENT_QUOTES) ?> m²</span>
|
|
<?php endif; ?>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
</details>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="pricing-section" id="miete" aria-label="<?= htmlspecialchars($t('rent.aria'), ENT_QUOTES) ?>">
|
|
<div class="section-head">
|
|
<span class="section-eyebrow"><?= htmlspecialchars($t('rent.eyebrow'), ENT_QUOTES) ?></span>
|
|
<h2><?= htmlspecialchars($t('rent.h2'), ENT_QUOTES) ?></h2>
|
|
</div>
|
|
|
|
<div class="pricing-grid">
|
|
<div class="price-card">
|
|
<span class="price-label"><?= htmlspecialchars($t('rent.cold'), ENT_QUOTES) ?></span>
|
|
<span class="price-value">1.300 €</span>
|
|
<span class="price-unit"><?= htmlspecialchars($t('rent.per_month'), ENT_QUOTES) ?></span>
|
|
</div>
|
|
<div class="price-card">
|
|
<span class="price-label"><?= htmlspecialchars($t('rent.warm'), ENT_QUOTES) ?></span>
|
|
<span class="price-value">1.600 €</span>
|
|
<span class="price-unit"><?= htmlspecialchars($t('rent.warm_includes'), ENT_QUOTES) ?></span>
|
|
</div>
|
|
<div class="price-card">
|
|
<span class="price-label"><?= htmlspecialchars($t('rent.deposit'), ENT_QUOTES) ?></span>
|
|
<span class="price-value">2.600 €</span>
|
|
<span class="price-unit"><?= htmlspecialchars($t('rent.deposit_months'), ENT_QUOTES) ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<dl class="rent-notes">
|
|
<dt><?= htmlspecialchars($t('rent.note.available'), ENT_QUOTES) ?></dt>
|
|
<dd><?= htmlspecialchars($t('rent.note.available_val'), ENT_QUOTES) ?></dd>
|
|
<dt><?= htmlspecialchars($t('rent.note.costs'), ENT_QUOTES) ?></dt>
|
|
<dd><?= htmlspecialchars($t('rent.note.costs_val'), ENT_QUOTES) ?></dd>
|
|
<dt><?= htmlspecialchars($t('rent.note.energy'), ENT_QUOTES) ?></dt>
|
|
<dd><?= htmlspecialchars($t('rent.note.energy_val'), ENT_QUOTES) ?></dd>
|
|
<dt><?= htmlspecialchars($t('rent.note.pets'), ENT_QUOTES) ?></dt>
|
|
<dd><?= htmlspecialchars($t('rent.note.pets_val'), ENT_QUOTES) ?></dd>
|
|
</dl>
|
|
</section>
|
|
|
|
<section class="lage-section" id="lage" aria-label="<?= htmlspecialchars($t('loc.eyebrow'), ENT_QUOTES) ?>">
|
|
<div class="section-head">
|
|
<span class="section-eyebrow"><?= htmlspecialchars($t('loc.eyebrow'), ENT_QUOTES) ?></span>
|
|
<h2><?= htmlspecialchars($t('loc.h2'), ENT_QUOTES) ?></h2>
|
|
</div>
|
|
|
|
<div class="lage-grid">
|
|
<ul class="lage-features">
|
|
<li>
|
|
<span class="lage-feature-title"><?= htmlspecialchars($t('loc.shopping'), ENT_QUOTES) ?></span>
|
|
<span class="lage-feature-desc"><?= htmlspecialchars($t('loc.shopping_desc'), ENT_QUOTES) ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="lage-feature-title"><?= htmlspecialchars($t('loc.transport'), ENT_QUOTES) ?></span>
|
|
<span class="lage-feature-desc"><?= htmlspecialchars($t('loc.transport_desc'), ENT_QUOTES) ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="lage-feature-title"><?= htmlspecialchars($t('loc.center'), ENT_QUOTES) ?></span>
|
|
<span class="lage-feature-desc"><?= htmlspecialchars($t('loc.center_desc'), ENT_QUOTES) ?></span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="lage-map-wrapper">
|
|
<iframe
|
|
title="<?= htmlspecialchars($t('loc.map_title'), ENT_QUOTES) ?>"
|
|
src="https://www.openstreetmap.org/export/embed.html?bbox=10.7535%2C50.5095%2C10.7705%2C50.5185&layer=mapnik&marker=50.5140%2C10.7620"
|
|
loading="lazy" referrerpolicy="no-referrer-when-downgrade"></iframe>
|
|
<p class="lage-address">
|
|
<strong><?= htmlspecialchars($t('loc.address'), ENT_QUOTES) ?>:</strong><br>
|
|
<?= /* address HTML is XSS-safe — composed of trusted translations */ $t('loc.address_val') ?>
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="contact-section" id="kontakt" aria-label="<?= htmlspecialchars($t('contact.aria'), ENT_QUOTES) ?>">
|
|
<div class="section-head">
|
|
<span class="section-eyebrow"><?= htmlspecialchars($t('contact.eyebrow'), ENT_QUOTES) ?></span>
|
|
<h2><?= htmlspecialchars($t('contact.h2'), ENT_QUOTES) ?> <em><?= htmlspecialchars($t('contact.h2_em'), ENT_QUOTES) ?></em></h2>
|
|
<p class="contact-intro"><?= htmlspecialchars($t('contact.intro'), ENT_QUOTES) ?></p>
|
|
</div>
|
|
|
|
<div id="form-result" class="form-result" role="status" aria-live="polite">
|
|
<?php if ($formSuccess): ?>
|
|
<div class="form-success">
|
|
<strong><?= htmlspecialchars($t('contact.success'), ENT_QUOTES) ?></strong>
|
|
<p><?= htmlspecialchars($t('contact.success_sub'), ENT_QUOTES) ?></p>
|
|
</div>
|
|
<?php elseif (!empty($formErrors)): ?>
|
|
<div class="form-errors" role="alert">
|
|
<ul>
|
|
<?php foreach ($formErrors as $errKey): ?>
|
|
<li><?= htmlspecialchars($t($errKey), ENT_QUOTES) ?></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<form class="contact-form" method="post" action="/#kontakt" novalidate>
|
|
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($_SESSION['csrf_token'] ?? '', ENT_QUOTES) ?>">
|
|
<input type="hidden" name="form_time" value="<?= htmlspecialchars((string) time(), ENT_QUOTES) ?>">
|
|
<div class="hp-field" aria-hidden="true">
|
|
<label for="website-hp"><?= htmlspecialchars($t('contact.hp_label'), ENT_QUOTES) ?></label>
|
|
<input type="text" id="website-hp" name="website" tabindex="-1" autocomplete="off">
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-field">
|
|
<label for="fname"><?= htmlspecialchars($t('contact.fname'), ENT_QUOTES) ?></label>
|
|
<input type="text" id="fname" name="fname" required maxlength="80" autocomplete="given-name"
|
|
value="<?= $escapeContactValue($formData['fname'] ?? '') ?>"
|
|
<?= !empty($formFieldErrors['fname']) ? 'aria-invalid="true" aria-describedby="err-fname"' : '' ?>>
|
|
<?php if (!empty($formFieldErrors['fname'])): ?>
|
|
<p id="err-fname" class="form-field-error"><?= htmlspecialchars($t($formFieldErrors['fname'][0]), ENT_QUOTES) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="form-field">
|
|
<label for="lname"><?= htmlspecialchars($t('contact.lname'), ENT_QUOTES) ?></label>
|
|
<input type="text" id="lname" name="lname" required maxlength="80" autocomplete="family-name"
|
|
value="<?= $escapeContactValue($formData['lname'] ?? '') ?>"
|
|
<?= !empty($formFieldErrors['lname']) ? 'aria-invalid="true" aria-describedby="err-lname"' : '' ?>>
|
|
<?php if (!empty($formFieldErrors['lname'])): ?>
|
|
<p id="err-lname" class="form-field-error"><?= htmlspecialchars($t($formFieldErrors['lname'][0]), ENT_QUOTES) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-field">
|
|
<label for="email"><?= htmlspecialchars($t('contact.email'), ENT_QUOTES) ?></label>
|
|
<input type="email" id="email" name="email" required maxlength="120" autocomplete="email"
|
|
value="<?= $escapeContactValue($formData['email'] ?? '') ?>"
|
|
<?= !empty($formFieldErrors['email']) ? 'aria-invalid="true" aria-describedby="err-email"' : '' ?>>
|
|
<?php if (!empty($formFieldErrors['email'])): ?>
|
|
<p id="err-email" class="form-field-error"><?= htmlspecialchars($t($formFieldErrors['email'][0]), ENT_QUOTES) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
<div class="form-field">
|
|
<label for="phone"><?= htmlspecialchars($t('contact.phone'), ENT_QUOTES) ?></label>
|
|
<input type="tel" id="phone" name="phone" maxlength="40" autocomplete="tel"
|
|
value="<?= $escapeContactValue($formData['phone'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-field">
|
|
<label for="interest"><?= htmlspecialchars($t('contact.interest'), ENT_QUOTES) ?></label>
|
|
<select id="interest" name="interest" required>
|
|
<?php
|
|
$currentInterest = $formData['interest'] ?? 'visit';
|
|
$interestLabels = [
|
|
'visit' => 'contact.interest_visit',
|
|
'info' => 'contact.interest_info',
|
|
'apply' => 'contact.interest_apply',
|
|
];
|
|
foreach ($interestLabels as $value => $labelKey): ?>
|
|
<option value="<?= htmlspecialchars($value, ENT_QUOTES) ?>"
|
|
<?= $currentInterest === $value ? 'selected' : '' ?>>
|
|
<?= htmlspecialchars($t($labelKey), ENT_QUOTES) ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-field">
|
|
<label for="message"><?= htmlspecialchars($t('contact.message'), ENT_QUOTES) ?></label>
|
|
<textarea id="message" name="message" required rows="6" maxlength="2000"
|
|
placeholder="<?= htmlspecialchars($t('contact.message'), ENT_QUOTES) ?>"
|
|
<?= !empty($formFieldErrors['message']) ? 'aria-invalid="true" aria-describedby="err-message"' : ''
|
|
?>><?= $escapeContactValue($formData['message'] ?? '') ?></textarea>
|
|
<?php if (!empty($formFieldErrors['message'])): ?>
|
|
<p id="err-message" class="form-field-error"><?= htmlspecialchars($t($formFieldErrors['message'][0]), ENT_QUOTES) ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<button type="submit" class="form-submit"><?= htmlspecialchars($t('contact.submit'), ENT_QUOTES) ?></button>
|
|
|
|
<p class="contact-direct"><?= htmlspecialchars($t('contact.direct'), ENT_QUOTES) ?>
|
|
<a href="mailto:mki@kies-media.de">mki@kies-media.de</a>
|
|
</p>
|
|
</form>
|
|
</section>
|