1126 lines
86 KiB
PHP
1126 lines
86 KiB
PHP
<?php
|
||
|
||
/**
|
||
* Baut die Website „LUXIMMO Hausverwaltung“ (Luximmo UG, Gaggenau) als Inhalt dieses CMS auf.
|
||
*
|
||
* Vorlage ist der Onepager aus luximmo-website.zip (06.11.2026): Kopf mit Logo in der Mitte,
|
||
* Hero mit Video und großer Schlagzeile, Intro mit drei Kärtchen, drei Leistungen mit Foto,
|
||
* „Über uns“ mit Eslem Kurt, Kontakt mit Formular, schlichter Fuß. Farben (Tinte, Gold, Creme),
|
||
* Schrift (Instrument Sans) und Texte sind übernommen.
|
||
*
|
||
* Ausgebaut gegenüber dem Paket:
|
||
* - eigene Seite /verwaltungsanfrage mit einem Formular in drei Schritten nach dem Vorbild des
|
||
* DEEG-Preisfinders: Verwaltungsart als Bildkarten (die drei Leistungsfotos), Eckdaten zum
|
||
* Objekt mit Feldern je Verwaltungsart, Lage und Kontakt, dann die Zusammenfassung,
|
||
* - je Leistung eine aufklappbare Liste „Was dazugehört“,
|
||
* - „So läuft der Wechsel“ in vier Schritten, häufige Fragen (als FAQ ausgezeichnet) und ein
|
||
* Aufrufband zur Verwaltungsanfrage,
|
||
* - Impressum, Datenschutz, 404, Fuß mit Logo, Anschrift und Seitenlinks.
|
||
*
|
||
* Bewusste Abweichungen:
|
||
* - Das Portrait von Eslem Kurt fehlt im Paket (dort ein grauer Platzhalter). Statt eines leeren
|
||
* Kastens steht eine dunkle Karte mit Monogramm und Zitat; ein Foto lässt sich im Editor als
|
||
* Hintergrundbild der Spalte setzen.
|
||
* - Telefon ist im Paket ein Platzhalter und steht deshalb nicht auf der Seite. E-Mail und
|
||
* Domain hat Umut nachgereicht (info@lux-immo.net, lux-immo.net); dorthin gehen beide
|
||
* Formulare (abweichend per LUXIMMO_MAIL), jede Einsendung liegt zusätzlich in der Verwaltung.
|
||
* - Das Video gibt es nur als WebM (kein ffmpeg zum Umwandeln). Bis es läuft, und bei
|
||
* „Bewegung reduzieren“, steht ein Standbild daraus (build-luximmo-poster.mjs).
|
||
*
|
||
* Das Skript schreibt ausschließlich in die Datenbank aus DATABASE_URL und in MEDIA_DIRECTORY,
|
||
* beide müssen auf eine frische, migrierte Installation zeigen (siehe reset-luximmo.sh).
|
||
* Erstes Argument: der Ordner aus build-luximmo-assets.php.
|
||
*/
|
||
|
||
require dirname(__DIR__).'/config/bootstrap.php';
|
||
|
||
use App\Content\DocumentValidator;
|
||
use App\Content\FormRepository;
|
||
use App\Content\MediaLibrary;
|
||
use App\Content\MenuRepository;
|
||
use App\Content\PageRepository;
|
||
use App\Content\PartialRepository;
|
||
use App\Content\SiteSettings;
|
||
use App\Content\Theme;
|
||
use App\Content\VideoLibrary;
|
||
use App\Infrastructure\ConnectionFactory;
|
||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||
|
||
$root = dirname(__DIR__);
|
||
$assets = $argv[1] ?? null;
|
||
if (null === $assets || !is_dir($assets)) {
|
||
fwrite(STDERR, "Bitte den Ordner mit den Bildern als Argument angeben.\n");
|
||
exit(1);
|
||
}
|
||
$url = str_replace('%kernel.project_dir%', str_replace('\\', '/', $root), (string) $_SERVER['DATABASE_URL']);
|
||
$mediaDirectory = str_replace('%kernel.project_dir%', $root, (string) $_SERVER['MEDIA_DIRECTORY']);
|
||
if (!str_contains($url, 'luximmo')) {
|
||
fwrite(STDERR, "Sicherung: DATABASE_URL muss auf die Luximmo-Datenbank zeigen.\n");
|
||
exit(1);
|
||
}
|
||
|
||
$db = ConnectionFactory::create($url);
|
||
if (false !== $db->fetchOne('SELECT id FROM page LIMIT 1')) {
|
||
fwrite(STDERR, "Diese Datenbank enthält bereits Seiten. Abbruch, damit nichts überschrieben wird.\n");
|
||
exit(1);
|
||
}
|
||
|
||
$media = new MediaLibrary($db, $mediaDirectory);
|
||
$videos = new VideoLibrary($db, $mediaDirectory);
|
||
$settings = new SiteSettings($db, $media);
|
||
$pages = new PageRepository($db, new DocumentValidator(), $settings);
|
||
$partials = new PartialRepository($db, new DocumentValidator());
|
||
$menus = new MenuRepository($db, $settings);
|
||
$forms = new FormRepository($db);
|
||
$theme = new Theme($db);
|
||
|
||
// Angaben aus dem Paket (Kontaktbereich). Telefon und E-Mail fehlen dort noch.
|
||
$firma = 'Luximmo UG';
|
||
$marke = 'LUXIMMO';
|
||
$person = 'Eslem Kurt';
|
||
$strasse = 'Hauptstraße 169';
|
||
$telefon = '07225 5080756';
|
||
$tel = 'tel:+4972255080756';
|
||
$plz = '76571';
|
||
$ort = 'Gaggenau';
|
||
$mail = getenv('LUXIMMO_MAIL') ?: 'info@lux-immo.net';
|
||
$domain = 'https://lux-immo.net';
|
||
$SCHEMA = DocumentValidator::SCHEMA_VERSION;
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Bausteine
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$seiten = static fn (string $w): array => ['top' => $w, 'right' => $w, 'bottom' => $w, 'left' => $w];
|
||
$nurOben = static fn (string $w): array => ['top' => $w, 'right' => '0px', 'bottom' => '0px', 'left' => '0px'];
|
||
$rund = static fn (string $w): array => ['topLeft' => $w, 'topRight' => $w, 'bottomRight' => $w, 'bottomLeft' => $w];
|
||
$quer = static fn (string $w): array => ['left' => $w, 'right' => $w];
|
||
|
||
$section = static function (string $id, array $children, array $style = [], ?string $anchor = null, string $css = '', string $breite = 'normal', array $props = []): array {
|
||
$block = ['id' => $id, 'type' => 'section', 'children' => $children, 'style' => $style];
|
||
if ([] !== $props) {
|
||
$block['props'] = $props;
|
||
}
|
||
if (null !== $anchor) {
|
||
$block['anchor'] = $anchor;
|
||
}
|
||
if ('' !== $css) {
|
||
$block['css'] = $css;
|
||
}
|
||
if ('normal' !== $breite) {
|
||
$block['layout'] = ['width' => $breite];
|
||
}
|
||
|
||
return $block;
|
||
};
|
||
|
||
$row = static function (string $id, array $children, array $design = [], array $style = [], string $css = ''): array {
|
||
$block = ['id' => $id, 'type' => 'row', 'children' => $children];
|
||
if ([] !== $design) {
|
||
$block['layout'] = ['design' => $design];
|
||
}
|
||
if ([] !== $style) {
|
||
$block['style'] = $style;
|
||
}
|
||
if ('' !== $css) {
|
||
$block['css'] = $css;
|
||
}
|
||
|
||
return $block;
|
||
};
|
||
|
||
$zweiSpalten = ['desktop' => ['columns' => 'two'], 'tablet' => ['columns' => 'one'], 'mobile' => ['columns' => 'one']];
|
||
|
||
/** Eine Zeile, die nebeneinander stellt statt zu rastern. */
|
||
$zeile = static function (string $id, array $children, string $gap, array $extra = [], array $mobil = [], string $css = '') use ($row): array {
|
||
$style = ['desktop' => ['display' => 'flex', 'alignItems' => 'center', 'gap' => $gap, ...$extra, 'margin' => ['top' => '0px', ...($extra['margin'] ?? [])]]];
|
||
if ([] !== $mobil) {
|
||
$style['mobile'] = $mobil;
|
||
}
|
||
|
||
return $row($id, $children, [], $style, $css);
|
||
};
|
||
|
||
$column = static function (string $id, array $children, array $style = [], string $css = ''): array {
|
||
$block = ['id' => $id, 'type' => 'column', 'children' => $children];
|
||
if ([] !== $style) {
|
||
$block['style'] = $style;
|
||
}
|
||
if ('' !== $css) {
|
||
$block['css'] = $css;
|
||
}
|
||
|
||
return $block;
|
||
};
|
||
|
||
$heading = static function (string $id, string $text, int $level, array $style = [], string $css = ''): array {
|
||
$block = ['id' => $id, 'type' => 'heading', 'props' => ['text' => $text, 'level' => $level]];
|
||
if ([] !== $style) {
|
||
$block['style'] = $style;
|
||
}
|
||
if ('' !== $css) {
|
||
$block['css'] = $css;
|
||
}
|
||
|
||
return $block;
|
||
};
|
||
|
||
$text = static function (string $id, string $inhalt, array $style = [], string $css = ''): array {
|
||
$block = ['id' => $id, 'type' => 'text', 'props' => ['text' => $inhalt]];
|
||
if ([] !== $style) {
|
||
$block['style'] = $style;
|
||
}
|
||
if ('' !== $css) {
|
||
$block['css'] = $css;
|
||
}
|
||
|
||
return $block;
|
||
};
|
||
|
||
$bild = static function (string $id, array $eintrag, string $alt, array $style = [], ?string $href = null, string $css = ''): array {
|
||
$gross = end($eintrag['variants']);
|
||
$props = [
|
||
'src' => $gross['src'], 'alt' => $alt, 'decorative' => '' === $alt, 'width' => $gross['width'], 'height' => $gross['height'],
|
||
'mediaId' => $eintrag['id'],
|
||
'sources' => array_map(static fn (array $v): array => ['src' => $v['src'], 'width' => $v['width']], $eintrag['variants']),
|
||
];
|
||
if (null !== $href) {
|
||
$props['href'] = $href;
|
||
$props['click'] = 'link';
|
||
}
|
||
$block = ['id' => $id, 'type' => 'image', 'props' => $props, 'style' => $style];
|
||
if ('' !== $css) {
|
||
$block['css'] = $css;
|
||
}
|
||
|
||
return $block;
|
||
};
|
||
|
||
$symbol = static fn (string $id, string $name, int $groesse, array $style = []): array => ['id' => $id, 'type' => 'icon', 'props' => ['icon' => $name, 'size' => $groesse, 'decorative' => true, 'label' => ''], 'style' => $style];
|
||
|
||
/**
|
||
* Ein Knopf als Pille wie in der Vorlage.
|
||
*
|
||
* @param string $art tinte (schwarz), gold, rahmen (Rahmen auf hell), hell (Rahmen auf dunkel)
|
||
*/
|
||
$knopf = static function (string $id, string $inhalt, string $href, string $art = 'tinte', string $icon = 'arrow-right', array $style = []): array {
|
||
$farbe = [
|
||
'tinte' => '',
|
||
'gold' => "background:#BDA076;color:#141414;\n&:hover{background:#d3bc97;color:#141414}\n",
|
||
'rahmen' => "background:transparent;color:#141414;box-shadow:inset 0 0 0 1.5px #141414;\n&:hover{background:#141414;color:#fff}\n",
|
||
'hell' => "background:transparent;color:#fff;box-shadow:inset 0 0 0 1.5px rgba(255,255,255,.55);\n&:hover{background:rgba(255,255,255,.12);color:#fff}\n",
|
||
][$art];
|
||
$props = ['text' => $inhalt, 'href' => $href, 'icon' => $icon];
|
||
if ('none' !== $icon) {
|
||
$props['iconPosition'] = 'end';
|
||
$props['iconMotion'] = 'slide';
|
||
}
|
||
|
||
return ['id' => $id, 'type' => 'button', 'props' => $props, 'css' => "min-height:50px;padding:0 26px;margin-top:0;transition:transform .2s ease,background-color .2s ease,color .2s ease;\n".$farbe.'&:hover{transform:translateY(-2px)}']
|
||
+ ([] === $style ? [] : ['style' => $style]);
|
||
};
|
||
|
||
/**
|
||
* Setzt die Einblendungen nach einer Liste.
|
||
*
|
||
* @param array<string, array{0: string, 1?: string}> $plan Block-ID => [Art, Verzögerung]
|
||
*/
|
||
$einblenden = static function (array $dokument, array $plan): array {
|
||
$gehen = function (array $bloecke) use (&$gehen, $plan): array {
|
||
foreach ($bloecke as $i => $block) {
|
||
if (isset($plan[$block['id'] ?? ''])) {
|
||
[$art, $verzug] = [...$plan[$block['id']], 'none'];
|
||
$bloecke[$i]['layout'] = [...($block['layout'] ?? []), 'effects' => ['entrance' => $art, 'delay' => $verzug]];
|
||
}
|
||
if (isset($block['children'])) {
|
||
$bloecke[$i]['children'] = $gehen($block['children']);
|
||
}
|
||
}
|
||
|
||
return $bloecke;
|
||
};
|
||
$dokument['blocks'] = $gehen($dokument['blocks']);
|
||
|
||
return $dokument;
|
||
};
|
||
|
||
$linkCss = "& a{color:#141414;text-decoration:underline;text-underline-offset:3px;text-decoration-color:#BDA076}\n& a:hover{color:#7A6235}";
|
||
|
||
/** Das Etikett der Vorlage: Creme-Pille mit goldenem Punkt. */
|
||
$etikett = static fn (string $id, string $inhalt, string $unten = '28px') => $text($id, $inhalt, ['desktop' => [
|
||
'fontSize' => '13px', 'fontWeight' => '500', 'letterSpacing' => '0.06em', 'lineHeight' => 1.4, 'color' => '@gold-text', 'margin' => ['top' => '0px', 'bottom' => $unten],
|
||
]], "display:inline-flex;align-items:center;gap:10px;padding:8px 18px;border-radius:999px;background:#F5EFE4\n&::before{content:'';width:6px;height:6px;border-radius:50%;background:#BDA076;flex-shrink:0}");
|
||
|
||
/** Die Kleinzeile: Versalien in Gold, wie „Adresse“ und „Direkt“ in der Vorlage. */
|
||
$kleinzeile = static fn (string $id, string $inhalt, string $farbe = '@gold-dunkel', string $unten = '10px') => $text($id, $inhalt, ['desktop' => [
|
||
'fontSize' => '13px', 'fontWeight' => '500', 'letterSpacing' => '0.08em', 'lineHeight' => 1.5, 'textTransform' => 'uppercase', 'color' => $farbe, 'margin' => ['top' => '0px', 'bottom' => $unten],
|
||
]]);
|
||
|
||
$h2Stil = [
|
||
'desktop' => ['fontSize' => '48px', 'fontWeight' => '500', 'lineHeight' => 1.05, 'letterSpacing' => '-0.03em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '0px']],
|
||
'tablet' => ['fontSize' => '40px'],
|
||
'mobile' => ['fontSize' => '32px'],
|
||
];
|
||
$fliess = static fn (string $farbe = '@text', string $unten = '0px', string $breite = 'none', string $groesse = '17px') => [
|
||
'desktop' => ['fontSize' => $groesse, 'lineHeight' => 1.6, 'color' => $farbe, 'maxWidth' => $breite, 'margin' => ['top' => '0px', 'bottom' => $unten]],
|
||
'mobile' => ['maxWidth' => 'none', 'fontSize' => '16px'],
|
||
];
|
||
|
||
/** Ein Haken vor einer Zeile. */
|
||
$haken = static fn (string $id, string $inhalt, string $farbe = '@text', string $hakenfarbe = '@gold-dunkel') => $zeile($id, [
|
||
$symbol($id.'-icon', 'check-circle', 20, ['desktop' => ['color' => $hakenfarbe, 'flexShrink' => 0, 'margin' => ['top' => '2px']]]),
|
||
$text($id.'-text', $inhalt, ['desktop' => ['fontSize' => '16px', 'lineHeight' => 1.55, 'color' => $farbe, 'margin' => ['bottom' => '0px']]], "& strong{font-weight:600;color:#141414}\n".$linkCss),
|
||
], '12px', ['alignItems' => 'flex-start', 'margin' => ['bottom' => '12px']]);
|
||
|
||
// Abschnittsränder wie in der Vorlage: clamp(24px, 5vw, 80px) seitlich, als Gerätewerte.
|
||
$rand = ['desktop' => '80px', 'tablet' => '40px', 'mobile' => '20px'];
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Website, Gestaltung, Medien
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$db->insert('website', ['id' => 'default', 'name' => $marke, 'default_locale' => 'de', 'theme' => '{}']);
|
||
|
||
$theme->save(
|
||
[
|
||
['key' => 'tinte', 'name' => 'Tinte (Schrift, Knöpfe)', 'value' => '#141414'],
|
||
['key' => 'gold', 'name' => 'Gold (Logo, Akzente)', 'value' => '#BDA076'],
|
||
['key' => 'gold-dunkel', 'name' => 'Gold dunkel (Kleinzeilen, Symbole)', 'value' => '#7A6235'],
|
||
['key' => 'gold-text', 'name' => 'Gold Text (Etikett)', 'value' => '#6E5830'],
|
||
['key' => 'creme', 'name' => 'Creme (Karten)', 'value' => '#F7F4EF'],
|
||
['key' => 'creme-hell', 'name' => 'Creme hell (Etikett)', 'value' => '#F5EFE4'],
|
||
['key' => 'text', 'name' => 'Fließtext', 'value' => '#5A554D'],
|
||
['key' => 'grau', 'name' => 'Grau (zweite Satzhälfte)', 'value' => '#8A8278'],
|
||
['key' => 'leise', 'name' => 'Leise (Fuß, Hinweise)', 'value' => '#6B655C'],
|
||
['key' => 'linie', 'name' => 'Linien und Feldränder', 'value' => '#E6DED2'],
|
||
['key' => 'auf-dunkel', 'name' => 'Schrift auf dunkel', 'value' => '#E8E1D6'],
|
||
['key' => 'papier', 'name' => 'Weiß', 'value' => '#ffffff'],
|
||
],
|
||
[
|
||
['key' => 'body', 'name' => 'Text und Überschriften', 'family' => 'Instrument Sans', 'source' => 'google', 'weights' => [400, 500, 600], 'fallback' => 'sans-serif'],
|
||
],
|
||
['fontFamily' => 'body', 'headingFontFamily' => 'body', 'fontSize' => '17px', 'lineHeight' => 1.55, 'color' => '@tinte', 'backgroundColor' => '@papier', 'linkColor' => '@tinte', 'containerWidth' => '1200px', 'headingScale' => 1.25, 'linkUnderline' => false, 'imageRadius' => '24px'],
|
||
['shape' => 'pill', 'appearance' => 'solid', 'size' => 'normal', 'fontFamily' => 'body', 'fontSize' => 16, 'fontWeight' => 500, 'letterSpacing' => 0, 'textTransform' => 'none', 'backgroundColor' => '@tinte', 'color' => '@papier', 'hoverBackgroundColor' => '@gold-dunkel', 'hoverColor' => '@papier'],
|
||
);
|
||
echo "Farben, Schrift und Buttons gesetzt\n";
|
||
|
||
$medien = [];
|
||
foreach ([
|
||
'favicon' => ['favicon.png', 'Zeichen von LUXIMMO'],
|
||
// Nur für die strukturierten Daten; sichtbar ist das eingebettete SVG.
|
||
'logo' => ['logo.png', 'LUXIMMO Logo'],
|
||
'poster' => ['hero-poster.webp', 'Offener Wohn- und Essbereich mit Treppe, Holzlamellen und großer Glasfront'],
|
||
'miet' => ['mietverwaltung.webp', 'Helles Mehrfamilienhaus mit Balkonen und gepflegtem Vorgarten im Abendlicht'],
|
||
'weg' => ['weg.webp', 'Wohnanlage mit mehreren Gebäuden und grünem Innenhof'],
|
||
'sonder' => ['sonder.webp', 'Helles Wohnzimmer mit Sofa, Esstisch und Blick auf den Balkon'],
|
||
'opt-wohnung' => ['option-wohnung.webp', ''],
|
||
'opt-haus' => ['option-haus.webp', ''],
|
||
'opt-mfh' => ['option-mehrfamilienhaus.webp', ''],
|
||
'opt-grund' => ['option-grundstueck.webp', ''],
|
||
] as $schluessel => [$datei, $alt]) {
|
||
$pfad = rtrim($assets, '/\\').'/'.$datei;
|
||
if (!is_file($pfad)) {
|
||
fwrite(STDERR, "Bild fehlt: $pfad\n");
|
||
exit(1);
|
||
}
|
||
$eintrag = $media->upload(new UploadedFile($pfad, basename($datei), null, null, true));
|
||
$media->update($eintrag['id'], ['title' => pathinfo($datei, PATHINFO_FILENAME), 'alt' => $alt, 'archived' => false, 'version' => $eintrag['version']]);
|
||
$medien[$schluessel] = $media->find($eintrag['id']);
|
||
echo "Bild übernommen: $datei\n";
|
||
}
|
||
$film = $videos->upload(new UploadedFile(rtrim($assets, '/\\').'/hero.webm', 'Hero Luximmo.webm', null, null, true));
|
||
echo "Video übernommen: hero.webm\n";
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Formulare
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$einwilligung = ['name' => 'datenschutz', 'type' => 'consent', 'label' => 'Ich stimme zu, dass meine Angaben zur Bearbeitung der Anfrage verwendet werden. Mehr in der Datenschutzerklärung.'];
|
||
|
||
// Wie im Paket: Name, Telefon, E-Mail, Anliegen, Nachricht, Einwilligung.
|
||
$forms->save('kontakt', 'Kontakt', [
|
||
['name' => 'name', 'type' => 'text', 'label' => 'Name', 'required' => true, 'help' => 'Vor- und Nachname'],
|
||
['name' => 'telefon', 'type' => 'phone', 'label' => 'Telefon', 'required' => false, 'help' => 'Freiwillig'],
|
||
['name' => 'email', 'type' => 'email', 'label' => 'E-Mail', 'required' => true],
|
||
['name' => 'anliegen', 'type' => 'select', 'label' => 'Worum geht es?', 'required' => true, 'options' => ['Immobilienverkauf', 'WEG-Verwaltung', 'Mietverwaltung', 'Sondereigentum', 'Sonstiges']],
|
||
['name' => 'nachricht', 'type' => 'textarea', 'label' => 'Nachricht', 'required' => true, 'help' => 'Objekt, Anzahl Einheiten, Ihr Anliegen …'],
|
||
$einwilligung,
|
||
], ['recipient' => $mail, 'subject' => 'Neue Anfrage über die Website', 'success' => 'Vielen Dank! Ihre Anfrage ist angekommen – wir melden uns in der Regel innerhalb eines Werktags.', 'replyToField' => 'email', 'buttonLabel' => 'Anfrage senden'], 0);
|
||
echo "Formular angelegt: kontakt\n";
|
||
|
||
// Die Verwaltungsanfrage nach dem Vorbild des DEEG-Preisfinders: erst die Verwaltungsart als
|
||
// Bildkarten, dann die Eckdaten je Art (Felder hinter Bedingungen), dann Lage und Kontakt, zum
|
||
// Schluss die Zusammenfassung.
|
||
$art = static fn (array $feld, array $werte): array => [...$feld, 'showIf' => ['field' => 'verwaltung', 'values' => $werte]];
|
||
$haus = ['Mietverwaltung', 'WEG-Verwaltung'];
|
||
$forms->save('verwaltungsanfrage', 'Verwaltungsanfrage', [
|
||
['name' => 'schritt_verwaltung', 'type' => 'step', 'label' => 'Ihre Verwaltung', 'help' => 'Welche Verwaltung suchen Sie? Ein Klick genügt.'],
|
||
['name' => 'verwaltung', 'type' => 'radio', 'label' => 'Welche Verwaltung suchen Sie?', 'required' => true, 'options' => ['Mietverwaltung', 'WEG-Verwaltung', 'Sondereigentum'],
|
||
'images' => ['Mietverwaltung' => $medien['miet']['id'], 'WEG-Verwaltung' => $medien['weg']['id'], 'Sondereigentum' => $medien['sonder']['id']]],
|
||
['name' => 'situation', 'type' => 'radio', 'label' => 'Wie wird das Objekt heute verwaltet?', 'required' => true, 'options' => ['Von einer anderen Verwaltung', 'Von mir selbst', 'Neubau / Erstbezug', 'Ich möchte mich erst informieren']],
|
||
|
||
['name' => 'schritt_objekt', 'type' => 'step', 'label' => 'Das Objekt', 'help' => 'Ungefähre Angaben genügen für ein erstes Angebot.'],
|
||
$art(['name' => 'objektart', 'type' => 'select', 'label' => 'Art des Objekts', 'required' => true, 'options' => ['Mehrfamilienhaus', 'Wohn- und Geschäftshaus', 'Wohnanlage mit mehreren Häusern', 'Gewerbeobjekt']], $haus),
|
||
$art(['name' => 'einheiten', 'type' => 'number', 'label' => 'Anzahl Einheiten', 'required' => true, 'min' => 1, 'max' => 999, 'help' => 'Wohnungen und Gewerbeeinheiten zusammen'], $haus),
|
||
$art(['name' => 'stellplaetze', 'type' => 'number', 'label' => 'Stellplätze und Garagen', 'required' => false, 'min' => 0, 'max' => 999], $haus),
|
||
$art(['name' => 'wohnungen', 'type' => 'number', 'label' => 'Wie viele Wohnungen sollen wir betreuen?', 'required' => true, 'min' => 1, 'max' => 99], ['Sondereigentum']),
|
||
$art(['name' => 'vermietet', 'type' => 'radio', 'label' => 'Ist die Wohnung vermietet?', 'required' => true, 'options' => ['Ja – vermietet', 'Nein – steht leer', 'Teilweise']], ['Sondereigentum']),
|
||
$art(['name' => 'beschluss', 'type' => 'radio', 'label' => 'Hat die Eigentümergemeinschaft über den Wechsel beschlossen?', 'required' => false, 'options' => ['Ja', 'Noch nicht', 'Ist in Vorbereitung']], ['WEG-Verwaltung']),
|
||
['name' => 'wohnflaeche', 'type' => 'number', 'label' => 'Wohnfläche insgesamt', 'required' => false, 'min' => 10, 'max' => 100000, 'unit' => 'm²'],
|
||
['name' => 'baujahr', 'type' => 'number', 'label' => 'Baujahr', 'required' => false, 'min' => 1800, 'max' => 2030],
|
||
['name' => 'ausstattung', 'type' => 'checkboxes', 'label' => 'Was gehört dazu?', 'required' => false, 'options' => ['Aufzug', 'Tiefgarage', 'Zentralheizung', 'Garten / Außenanlagen', 'Gewerbeeinheiten']],
|
||
['name' => 'beginn', 'type' => 'select', 'label' => 'Ab wann soll die Verwaltung starten?', 'required' => true, 'options' => ['So bald wie möglich', 'In den nächsten drei Monaten', 'Zum Jahreswechsel', 'Noch offen']],
|
||
|
||
['name' => 'schritt_kontakt', 'type' => 'step', 'label' => 'Lage und Kontakt', 'help' => 'Wir melden uns in der Regel innerhalb eines Werktags mit einem Angebot oder Rückfragen.'],
|
||
['name' => 'lage', 'type' => 'text', 'label' => 'Straße und Ort des Objekts', 'required' => true, 'help' => 'z. B. Hauptstraße 12, Gaggenau'],
|
||
['name' => 'rolle', 'type' => 'radio', 'label' => 'Sie schreiben als', 'required' => true, 'options' => ['Eigentümer/in', 'Verwaltungsbeirat', 'Sonstiges']],
|
||
['name' => 'name', 'type' => 'text', 'label' => 'Vor- und Nachname', 'required' => true],
|
||
['name' => 'email', 'type' => 'email', 'label' => 'E-Mail', 'required' => true],
|
||
['name' => 'telefon', 'type' => 'phone', 'label' => 'Telefon', 'required' => false, 'help' => 'Freiwillig – wenn wir Sie lieber anrufen sollen'],
|
||
['name' => 'erreichbar', 'type' => 'select', 'label' => 'Wann erreichen wir Sie am besten?', 'required' => false, 'options' => ['Vormittags', 'Nachmittags', 'Abends', 'Egal']],
|
||
['name' => 'nachricht', 'type' => 'textarea', 'label' => 'Was sollten wir noch wissen?', 'required' => false, 'help' => 'Anstehende Arbeiten, offene Abrechnungen, Wünsche …'],
|
||
$einwilligung,
|
||
], ['recipient' => $mail, 'subject' => 'Neue Verwaltungsanfrage über die Website', 'success' => 'Vielen Dank! Ihre Anfrage ist bei uns angekommen. Wir melden uns in der Regel innerhalb eines Werktags mit einem Angebot oder Rückfragen.', 'replyToField' => 'email', 'buttonLabel' => 'Anfrage senden', 'summary' => true], 0);
|
||
echo "Formular angelegt: verwaltungsanfrage\n";
|
||
|
||
// „Was ist Ihre Immobilie wert?“ – die Bewertungsanfrage nach dem DEEG-Preisfinder: Objektart
|
||
// als Bildkarten (Piktogramme aus build-luximmo-assets.php) und Anlass, Eckdaten je Objektart,
|
||
// Lage und Kontakt, Zusammenfassung.
|
||
$objekt = static fn (array $feld, array $werte): array => [...$feld, 'showIf' => ['field' => 'immobilienart', 'values' => $werte]];
|
||
$gebaeude = ['Wohnung', 'Haus', 'Mehrfamilienhaus'];
|
||
$forms->save('bewertung', 'Immobilienbewertung', [
|
||
['name' => 'schritt_objekt', 'type' => 'step', 'label' => 'Ihre Immobilie', 'help' => 'Um welche Immobilie geht es, und was haben Sie vor?'],
|
||
['name' => 'immobilienart', 'type' => 'radio', 'label' => 'Um welche Immobilie geht es?', 'required' => true, 'options' => ['Wohnung', 'Haus', 'Mehrfamilienhaus', 'Grundstück'],
|
||
'images' => ['Wohnung' => $medien['opt-wohnung']['id'], 'Haus' => $medien['opt-haus']['id'], 'Mehrfamilienhaus' => $medien['opt-mfh']['id'], 'Grundstück' => $medien['opt-grund']['id']]],
|
||
['name' => 'anlass', 'type' => 'radio', 'label' => 'Was haben Sie vor?', 'required' => true, 'options' => ['Verkaufen', 'Vermieten', 'Nur den Wert erfahren']],
|
||
|
||
['name' => 'schritt_eckdaten', 'type' => 'step', 'label' => 'Eckdaten', 'help' => 'Was wir über die Immobilie wissen sollten. Schätzwerte genügen.'],
|
||
$objekt(['name' => 'wohnflaeche', 'type' => 'number', 'label' => 'Wohnfläche', 'required' => true, 'min' => 10, 'max' => 5000, 'unit' => 'm²'], $gebaeude),
|
||
$objekt(['name' => 'grundstueck', 'type' => 'number', 'label' => 'Grundstücksfläche', 'required' => false, 'min' => 10, 'max' => 100000, 'unit' => 'm²'], ['Haus', 'Mehrfamilienhaus']),
|
||
$objekt(['name' => 'flaeche_grundstueck', 'type' => 'number', 'label' => 'Grundstücksfläche', 'required' => true, 'min' => 10, 'max' => 100000, 'unit' => 'm²'], ['Grundstück']),
|
||
$objekt(['name' => 'bebauung', 'type' => 'select', 'label' => 'Wie lässt sich das Grundstück nutzen?', 'required' => false, 'options' => ['Bauland', 'Bereits bebaut', 'Bauerwartungsland', 'Garten- oder Freizeitland', 'Weiß ich nicht']], ['Grundstück']),
|
||
$objekt(['name' => 'zimmer', 'type' => 'select', 'label' => 'Zimmer', 'required' => false, 'options' => ['1', '2', '3', '4', '5', '6', '7', '8 oder mehr']], ['Wohnung', 'Haus']),
|
||
$objekt(['name' => 'etage', 'type' => 'select', 'label' => 'Etage', 'required' => false, 'options' => ['Erdgeschoss', '1. Obergeschoss', '2. Obergeschoss', '3. Obergeschoss oder höher', 'Dachgeschoss']], ['Wohnung']),
|
||
$objekt(['name' => 'einheiten', 'type' => 'number', 'label' => 'Wohneinheiten', 'required' => true, 'min' => 2, 'max' => 500], ['Mehrfamilienhaus']),
|
||
$objekt(['name' => 'mieteinnahmen', 'type' => 'number', 'label' => 'Jahresnettokaltmiete (ungefähr)', 'required' => false, 'min' => 0, 'max' => 10000000, 'unit' => '€'], ['Mehrfamilienhaus']),
|
||
$objekt(['name' => 'baujahr', 'type' => 'number', 'label' => 'Baujahr', 'required' => false, 'min' => 1800, 'max' => 2030], $gebaeude),
|
||
$objekt(['name' => 'zustand', 'type' => 'select', 'label' => 'Zustand', 'required' => false, 'options' => ['Neuwertig', 'Gepflegt', 'Modernisiert', 'Renovierungsbedürftig', 'Sanierungsbedürftig']], $gebaeude),
|
||
$objekt(['name' => 'ausstattung', 'type' => 'checkboxes', 'label' => 'Was ist vorhanden?', 'required' => false, 'options' => ['Einbauküche', 'Balkon / Terrasse', 'Garten', 'Aufzug', 'Garage / Stellplatz', 'Keller']], $gebaeude),
|
||
$objekt(['name' => 'nutzung', 'type' => 'radio', 'label' => 'Wie wird die Immobilie heute genutzt?', 'required' => false, 'options' => ['Selbst bewohnt', 'Vermietet', 'Leerstand']], ['Wohnung', 'Haus']),
|
||
|
||
['name' => 'schritt_kontakt', 'type' => 'step', 'label' => 'Lage und Kontakt', 'help' => 'Wohin dürfen wir Ihre Einschätzung schicken?'],
|
||
['name' => 'lage', 'type' => 'text', 'label' => 'Straße und Ort der Immobilie', 'required' => true, 'help' => 'z. B. Hauptstraße 12, Gaggenau'],
|
||
['name' => 'zeitraum', 'type' => 'select', 'label' => 'Wann möchten Sie verkaufen oder vermieten?', 'required' => false, 'options' => ['So bald wie möglich', 'In den nächsten sechs Monaten', 'In einem Jahr oder später', 'Noch offen']],
|
||
['name' => 'name', 'type' => 'text', 'label' => 'Vor- und Nachname', 'required' => true],
|
||
['name' => 'email', 'type' => 'email', 'label' => 'E-Mail', 'required' => true],
|
||
['name' => 'telefon', 'type' => 'phone', 'label' => 'Telefon', 'required' => false, 'help' => 'Freiwillig – wenn wir Sie lieber anrufen sollen'],
|
||
['name' => 'nachricht', 'type' => 'textarea', 'label' => 'Was sollten wir noch wissen?', 'required' => false, 'help' => 'Modernisierungen, Besonderheiten, Ihre Fragen …'],
|
||
$einwilligung,
|
||
], ['recipient' => $mail, 'subject' => 'Neue Bewertungsanfrage über die Website', 'success' => 'Vielen Dank! Ihre Bewertungsanfrage ist bei uns angekommen. Wir melden uns in der Regel innerhalb eines Werktags mit einer ersten Einschätzung.', 'replyToField' => 'email', 'buttonLabel' => 'Bewertung anfragen', 'summary' => true], 0);
|
||
echo "Formular angelegt: bewertung\n";
|
||
|
||
// Das Logo als Vektor (LUXIMMO_Logo.svg vom Kunden), direkt im HTML: scharf auf jedem Display und
|
||
// ohne eigene Anfrage. Die Mediathek nimmt keine SVGs an. Die weiße Fläche fällt weg, der Ausschnitt
|
||
// sitzt auf Zeichen und Schriftzug; ohne IDs, weil das Logo in Kopf und Fuß zugleich steht.
|
||
$logoDatei = rtrim($assets, '/\\').'/logo-original.svg';
|
||
if (!is_file($logoDatei)) {
|
||
fwrite(STDERR, "Logo fehlt: $logoDatei\n");
|
||
exit(1);
|
||
}
|
||
preg_match_all('/<path id="[^"]+" (fill="[^"]+" fill-rule="[^"]+" d="[^"]+")\/>/', (string) file_get_contents($logoDatei), $logoPfade);
|
||
if (2 !== count($logoPfade[1])) {
|
||
fwrite(STDERR, "Logo: Zeichen und Schriftzug nicht gefunden.\n");
|
||
exit(1);
|
||
}
|
||
$logoSvg = static fn (string $auszeichnung): string => '<svg xmlns="http://www.w3.org/2000/svg" viewBox="305 256 649 570" width="649" height="570" '.$auszeichnung.'><path '.implode('/><path ', $logoPfade[1]).'/></svg>';
|
||
$logoCss = "& a{display:block}\n& svg{display:block;width:100%;height:auto}";
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Kopfbereich: Menü links, Logo in der Mitte, Knopf rechts – wie die Vorlage
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$kopf = [
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Kopfbereich', 'description' => 'Erscheint auf jeder Seite über dem Inhalt.',
|
||
'blocks' => [
|
||
$section('kopf', [
|
||
$zeile('kopf-zeile', [
|
||
$column('kopf-links', [
|
||
['id' => 'kopf-menue', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'horizontal', 'collapse' => 'tablet', 'burgerLabel' => 'icon'],
|
||
'style' => ['desktop' => [
|
||
'fontSize' => '15px', 'fontWeight' => '500', 'color' => '@tinte',
|
||
'burgerColor' => '@tinte', 'burgerBackgroundColor' => '@creme', 'burgerBorderColor' => '@linie', 'burgerBorderRadius' => '999px',
|
||
]],
|
||
// Goldene Linie, die beim Überfahren wächst; das Klappmenü liegt als Fläche
|
||
// unter dem ganzen Kopf.
|
||
'css' => "& .menu-list{gap:4px 28px;align-items:center}\n"
|
||
."& .menu-list a{position:relative;padding:12px 0;text-decoration:none}\n"
|
||
."& .menu-list a::after{content:'';position:absolute;left:0;right:100%;bottom:6px;height:1.5px;background:#BDA076;transition:right .25s ease}\n"
|
||
."& .menu-list a:hover::after{right:0}\n"
|
||
."& .menu-burger summary{width:46px;height:46px;display:flex;align-items:center;justify-content:center;padding:0}\n"
|
||
."& .menu-burger-body{position:absolute;left:0;right:0;top:100%;background:#fff;padding:4px 20px 20px;border-top:1px solid #E6DED2;box-shadow:0 18px 30px rgba(20,20,20,.08);font-size:18px;z-index:30}\n"
|
||
."& .menu-burger-body .menu-list{flex-direction:column;align-items:stretch;gap:0}\n"
|
||
."& .menu-burger-body .menu-list a{display:block;padding:14px 0;border-bottom:1px solid #EFE8DC}\n"
|
||
.'& .menu-burger-body .menu-list a::after{display:none}'],
|
||
], ['desktop' => ['flexGrow' => 1, 'flexBasis' => '0px']]),
|
||
['id' => 'kopf-logo', 'type' => 'html', 'props' => ['html' => '<a href="/" aria-label="'.$marke.' – zur Startseite">'.$logoSvg('aria-hidden="true" focusable="false"').'</a>'], 'style' => [
|
||
'desktop' => ['width' => '119px', 'maxWidth' => '119px', 'flexShrink' => 0, 'margin' => $seiten('0px')],
|
||
'mobile' => ['width' => '80px', 'maxWidth' => '80px'],
|
||
], 'css' => $logoCss],
|
||
$column('kopf-rechts', [
|
||
['css' => 'white-space:nowrap;
|
||
'.$knopf('kopf-knopf', 'Kontakt', '/#kontakt', 'tinte', 'none')['css']] + $knopf('kopf-knopf', 'Kontakt', '/#kontakt', 'tinte', 'none', ['mobile' => ['fontSize' => '14px', 'padding' => ['left' => '18px', 'right' => '18px']]]),
|
||
], ['desktop' => ['flexGrow' => 1, 'flexBasis' => '0px', 'display' => 'flex', 'justifyContent' => 'flex-end']]),
|
||
], '24px', ['justifyContent' => 'space-between', 'minHeight' => '152px'], ['minHeight' => '104px', 'gap' => '12px'], 'position:relative'),
|
||
], [
|
||
'desktop' => ['backgroundColor' => '@papier', 'padding' => ['top' => '0px', 'bottom' => '0px', 'left' => $rand['desktop'], 'right' => $rand['desktop']]],
|
||
'tablet' => ['padding' => ['left' => $rand['tablet'], 'right' => $rand['tablet']]],
|
||
'mobile' => ['padding' => ['left' => $rand['mobile'], 'right' => $rand['mobile']]],
|
||
], null, '', 'wide'),
|
||
],
|
||
];
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Fußbereich: Logo, Anschrift, Seiten; darunter die Zeile der Vorlage
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$fussLink = "& a{color:#5A554D;text-decoration:none;transition:color .2s}\n& a:hover{color:#141414}";
|
||
$fussText = static fn (string $id, string $inhalt) => $text($id, $inhalt, ['desktop' => ['fontSize' => '15px', 'lineHeight' => 1.9, 'color' => '@text', 'margin' => ['bottom' => '0px']]], $fussLink);
|
||
|
||
$fuss = [
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Fußbereich', 'description' => 'Erscheint auf jeder Seite unter dem Inhalt.',
|
||
'blocks' => [
|
||
$section('fuss', [
|
||
$column('fuss-raster', [
|
||
$column('fuss-marke', [
|
||
['id' => 'fuss-logo', 'type' => 'html', 'props' => ['html' => $logoSvg('role="img" aria-label="'.$marke.'"')], 'style' => ['desktop' => ['width' => '96px', 'maxWidth' => '96px', 'margin' => $seiten('0px')]], 'css' => $logoCss],
|
||
$text('fuss-spruch', 'Ihr neuer Immobilienpartner vor Ort. Ein Lichtblick für Ihre Immobilie.', ['desktop' => ['fontSize' => '15px', 'lineHeight' => 1.6, 'color' => '@text', 'maxWidth' => '280px', 'margin' => ['top' => '20px', 'bottom' => '0px']], 'mobile' => ['maxWidth' => 'none']]),
|
||
]),
|
||
$column('fuss-anschrift', [
|
||
$kleinzeile('fuss-anschrift-titel', 'Adresse', '@gold-dunkel', '8px'),
|
||
$fussText('fuss-anschrift-text', $firma.'<br>'.$person.'<br>'.$strasse.'<br>'.$plz.' '.$ort.'<br><a href="'.$tel.'">'.$telefon.'</a><br><a href="mailto:'.$mail.'">'.$mail.'</a>'),
|
||
]),
|
||
$column('fuss-seiten', [
|
||
$kleinzeile('fuss-seiten-titel', 'Leistungen', '@gold-dunkel', '8px'),
|
||
$fussText('fuss-seiten-text', '<a href="/#leistungen">Immobilienverkauf</a><br><a href="/#leistungen">WEG-Verwaltung</a><br><a href="/#leistungen">Mietverwaltung</a><br><a href="/#leistungen">Sondereigentum</a><br><a href="/verwaltungsanfrage">Verwaltungsanfrage</a><br><a href="/immobilienbewertung">Immobilienbewertung</a>'),
|
||
]),
|
||
], [
|
||
'desktop' => ['display' => 'grid', 'gridColumns' => 3, 'gap' => '40px', 'padding' => ['top' => '56px', 'bottom' => '40px']],
|
||
'tablet' => ['gridColumns' => 2],
|
||
'mobile' => ['gridColumns' => 1, 'gap' => '28px', 'padding' => ['top' => '40px', 'bottom' => '28px']],
|
||
]),
|
||
$zeile('fuss-unten', [
|
||
$text('fuss-copyright', '© 2026 '.$firma.' · '.$ort, ['desktop' => ['fontSize' => '14px', 'color' => '@leise', 'margin' => ['bottom' => '0px']]]),
|
||
$zeile('fuss-links', [
|
||
['id' => 'fuss-rechtliches', 'type' => 'legal', 'props' => ['label' => 'Rechtliches'],
|
||
'style' => ['desktop' => ['fontSize' => '14px', 'gap' => '24px', 'color' => '@leise']],
|
||
'css' => "& a{color:#6B655C;text-decoration:underline;text-underline-offset:3px}\n& a:hover{color:#141414}"],
|
||
$text('fuss-agentur', '<a href="https://kant-it.de">Webdesign by Kant-IT Solutions</a>', ['desktop' => ['fontSize' => '14px', 'margin' => ['bottom' => '0px']]], "& a{color:#6B655C;text-decoration:none}\n& a:hover{color:#141414}"),
|
||
], '24px', ['flexWrap' => 'wrap'], ['rowGap' => '10px', 'columnGap' => '18px']),
|
||
], '24px', ['justifyContent' => 'space-between', 'flexWrap' => 'wrap', 'rowGap' => '12px', 'padding' => ['top' => '22px', 'bottom' => '32px'], 'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie'],
|
||
['flexDirection' => 'column', 'alignItems' => 'flex-start']),
|
||
], ['desktop' => ['backgroundColor' => '@papier', 'padding' => ['top' => '0px', 'bottom' => '0px', 'left' => $rand['desktop'], 'right' => $rand['desktop']]], 'tablet' => ['padding' => ['left' => $rand['tablet'], 'right' => $rand['tablet']]], 'mobile' => ['padding' => ['left' => $rand['mobile'], 'right' => $rand['mobile']]]], null, '', 'wide'),
|
||
],
|
||
];
|
||
|
||
// Die Linie unter dem Kopf zeichnet der Kern an .site-header; die weiße Sektion liegt darüber.
|
||
$kopf['blocks'][0]['css'] = 'position:relative;margin-bottom:-1px';
|
||
$partials->create('header', 'default', 'Kopfbereich', $kopf);
|
||
$partials->create('footer', 'default', 'Fußbereich', $fuss);
|
||
echo "Kopf- und Fußbereich angelegt\n";
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Startseite
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
/** Ein Kärtchen des Intros: Symbol im weißen Kreis, Titel, Satz. */
|
||
$vorteil = static fn (int $n, string $icon, string $titel, string $satz) => $column('vorteil-'.$n, [
|
||
$column('vorteil-'.$n.'-kreis', [$symbol('vorteil-'.$n.'-icon', $icon, 24, ['desktop' => ['color' => '@gold-dunkel']])], ['desktop' => [
|
||
'display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'center', 'width' => '52px', 'height' => '52px', 'backgroundColor' => '@papier', 'borderRadius' => $rund('50%'), 'margin' => ['bottom' => '22px'],
|
||
]]),
|
||
$heading('vorteil-'.$n.'-titel', $titel, 2, ['desktop' => ['fontSize' => '20px', 'fontWeight' => '500', 'letterSpacing' => '-0.01em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '10px']]]),
|
||
$text('vorteil-'.$n.'-satz', $satz, ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.55, 'color' => '@text', 'margin' => ['bottom' => '0px']]]),
|
||
], ['desktop' => ['backgroundColor' => '@creme', 'borderRadius' => $rund('24px'), 'padding' => ['top' => '36px', 'right' => '32px', 'bottom' => '36px', 'left' => '32px']]],
|
||
"transition:transform .3s ease,background-color .3s ease\n&:hover{transform:translateY(-4px);background-color:#F2ECE2}");
|
||
|
||
/** Eine Leistung: Foto, Titel, Satz, aufklappbar „Was dazugehört“. */
|
||
$leistung = static function (string $id, array $foto, string $alt, string $titel, string $satz, array $punkte) use ($column, $bild, $heading, $text, $nurOben): array {
|
||
return $column($id, [
|
||
$bild($id.'-bild', $foto, $alt, [
|
||
'desktop' => ['margin' => ['top' => '0px', 'right' => '0px', 'bottom' => '22px', 'left' => '0px'], 'imageHeight' => '280px', 'objectFit' => 'cover'],
|
||
'tablet' => ['imageHeight' => '320px'],
|
||
'mobile' => ['imageHeight' => '260px'],
|
||
], null, "overflow:hidden;border-radius:24px\n& img{display:block;width:100%;transition:transform .6s ease}\n&:hover img{transform:scale(1.04)}"),
|
||
$heading($id.'-titel', $titel, 3, ['desktop' => ['fontSize' => '24px', 'fontWeight' => '500', 'letterSpacing' => '-0.02em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '6px']]]),
|
||
$text($id.'-satz', $satz, ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.55, 'color' => '@text', 'margin' => ['bottom' => '0px']]]),
|
||
['id' => $id.'-mehr', 'type' => 'accordion', 'props' => ['faq' => false],
|
||
'style' => ['desktop' => ['borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'margin' => ['top' => '20px']]],
|
||
'children' => [
|
||
['id' => $id.'-punkt', 'type' => 'accordionitem', 'props' => ['title' => 'Was dazugehört', 'open' => false],
|
||
'css' => "& summary{border:0;border-radius:0;background:transparent;padding:16px 0;font-size:15px;font-weight:500;color:#141414}\n& .accordion-body{padding:0 0 10px}\nborder:0;border-radius:0;padding:0",
|
||
'children' => [
|
||
$text($id.'-liste', '<ul>'.implode('', array_map(static fn (string $p): string => '<li>'.$p.'</li>', $punkte)).'</ul>', ['desktop' => ['fontSize' => '15px', 'lineHeight' => 1.6, 'color' => '@text', 'margin' => ['bottom' => '0px']]],
|
||
"& ul{padding-left:18px;margin:0}\n& li{margin:6px 0}\n& li::marker{color:#BDA076}"),
|
||
],
|
||
],
|
||
],
|
||
],
|
||
]);
|
||
};
|
||
|
||
/** Ein Schritt des Wechsels: große Ziffer in Gold, Titel, Satz. */
|
||
$schritt = static fn (int $n, string $titel, string $satz) => $column('schritt-'.$n, [
|
||
$text('schritt-'.$n.'-nummer', '0'.$n, ['desktop' => ['fontSize' => '15px', 'fontWeight' => '500', 'letterSpacing' => '0.08em', 'color' => '@gold-dunkel', 'margin' => ['bottom' => '28px']]],
|
||
"display:flex;align-items:center;gap:12px\n&::after{content:'';flex:1;height:1px;background:#E6DED2}"),
|
||
$heading('schritt-'.$n.'-titel', $titel, 3, ['desktop' => ['fontSize' => '22px', 'fontWeight' => '500', 'letterSpacing' => '-0.015em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '10px']]]),
|
||
$text('schritt-'.$n.'-satz', $satz, ['desktop' => ['fontSize' => '16px', 'lineHeight' => 1.6, 'color' => '@text', 'margin' => ['bottom' => '0px']]]),
|
||
]);
|
||
|
||
/** Eine häufige Frage. */
|
||
$frage = static fn (int $n, string $titel, string $antwort) => ['id' => 'frage-'.$n, 'type' => 'accordionitem', 'props' => ['title' => $titel, 'open' => false],
|
||
'css' => "& summary{border:0;border-radius:0;background:transparent;padding:24px 0;font-size:19px;font-weight:500;color:#141414;letter-spacing:-0.01em}\n& .accordion-body{padding:0 0 24px}\nborder:0;border-bottom:1px solid #E6DED2;border-radius:0;padding:0",
|
||
'children' => [$text('frage-'.$n.'-antwort', $antwort, ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.65, 'color' => '@text', 'maxWidth' => '760px', 'margin' => ['bottom' => '0px']]], $linkCss)],
|
||
];
|
||
|
||
$abschnitt = static fn (string $oben, string $unten) => [
|
||
'desktop' => ['padding' => ['top' => $oben, 'bottom' => $unten, 'left' => $rand['desktop'], 'right' => $rand['desktop']]],
|
||
'tablet' => ['padding' => ['left' => $rand['tablet'], 'right' => $rand['tablet']]],
|
||
'mobile' => ['padding' => ['top' => (int) $oben > 0 ? '64px' : '0px', 'bottom' => (int) $unten > 0 ? '64px' : '0px', 'left' => $rand['mobile'], 'right' => $rand['mobile']]],
|
||
];
|
||
|
||
$startseite = [
|
||
'schemaVersion' => $SCHEMA,
|
||
'title' => 'Ein Lichtblick für Ihre Immobilie.',
|
||
'description' => 'Luximmo – Ihr neuer Immobilienpartner vor Ort: Immobilienverkauf und WEG-Verwaltung in Gaggenau und Umgebung, Baden-Baden und im Schwarzwald.',
|
||
'blocks' => [
|
||
// 1 – Hero: Video in einer gerundeten Fläche, Schlagzeile unten links, Satz und Knopf rechts.
|
||
$section('hero', [
|
||
$row('hero-zeile', [
|
||
$heading('hero-titel', 'Ein Lichtblick<br>für Ihre Immobilie.', 1, [
|
||
'desktop' => ['fontSize' => '104px', 'fontWeight' => '500', 'lineHeight' => 0.95, 'letterSpacing' => '-0.035em', 'color' => '@papier', 'maxWidth' => '900px', 'flexGrow' => 1, 'flexBasis' => '560px', 'margin' => ['top' => '0px', 'bottom' => '0px']],
|
||
'tablet' => ['fontSize' => '72px', 'flexBasis' => '100%'],
|
||
'mobile' => ['fontSize' => '36px'],
|
||
]),
|
||
$column('hero-seite', [
|
||
$text('hero-satz', 'Immobilienverkauf und WEG-Verwaltung in Gaggenau und Umgebung, Baden-Baden und im Schwarzwald.', ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.55, 'color' => '@auf-dunkel', 'margin' => ['bottom' => '20px']]]),
|
||
$knopf('hero-knopf', 'Anfrage stellen', '/verwaltungsanfrage', 'gold'),
|
||
], ['desktop' => ['width' => '320px', 'maxWidth' => '100%', 'flexShrink' => 0]]),
|
||
], [], [
|
||
'desktop' => ['display' => 'flex', 'flexWrap' => 'wrap', 'justifyContent' => 'space-between', 'alignItems' => 'flex-end', 'gap' => '32px', 'margin' => ['top' => '0px']],
|
||
]),
|
||
], [
|
||
'desktop' => ['backgroundImage' => $medien['poster']['id'], 'backgroundSize' => 'cover', 'backgroundPosition' => 'center', 'backgroundColor' => '#2F2A24',
|
||
'color' => '@papier', 'minHeight' => '736px', 'display' => 'flex', 'alignItems' => 'flex-end',
|
||
'margin' => $quer($rand['desktop']), 'borderRadius' => $rund('24px'), 'overflow' => 'hidden',
|
||
'padding' => ['top' => '56px', 'bottom' => '56px', 'left' => '56px', 'right' => '56px']],
|
||
'tablet' => ['minHeight' => '620px', 'margin' => $quer($rand['tablet']), 'padding' => ['top' => '40px', 'bottom' => '40px', 'left' => '40px', 'right' => '40px']],
|
||
'mobile' => ['minHeight' => '560px', 'margin' => $quer($rand['mobile']), 'padding' => ['top' => '28px', 'bottom' => '28px', 'left' => '24px', 'right' => '24px']],
|
||
], null,
|
||
// Der Verlauf der Vorlage über Video und Standbild; der Knopf zum Anhalten oben rechts,
|
||
// damit er nicht auf dem Aufruf liegt.
|
||
"position:relative\n&::before{content:'';position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;background:linear-gradient(to top,rgba(20,17,13,.72) 0%,rgba(20,17,13,.35) 38%,rgba(20,17,13,0) 65%);pointer-events:none}\n& .section-inner{position:relative;z-index:2;width:100%}\n& .video-toggle{top:20px;bottom:auto;right:20px;z-index:3}",
|
||
'full', ['backgroundVideoWebm' => $film['src']]),
|
||
|
||
// 2 – Intro: Etikett, große Aussage, drei Kärtchen.
|
||
$section('intro', [
|
||
$column('intro-kopf', [
|
||
$etikett('intro-etikett', 'Ihr neuer Immobilienpartner vor Ort', '32px'),
|
||
$text('intro-aussage', 'Wir kümmern uns um Mieter, Abrechnungen und Instandhaltung – <em>damit Ihre Immobilie läuft und ihren Wert behält.</em>', [
|
||
'desktop' => ['fontSize' => '52px', 'fontWeight' => '400', 'lineHeight' => 1.15, 'letterSpacing' => '-0.025em', 'color' => '@tinte', 'maxWidth' => '980px', 'margin' => ['bottom' => '0px']],
|
||
'tablet' => ['fontSize' => '40px'],
|
||
'mobile' => ['fontSize' => '30px'],
|
||
], "text-wrap:balance\n& em{font-style:normal;color:#8A8278}"),
|
||
], ['desktop' => ['display' => 'flex', 'flexDirection' => 'column', 'alignItems' => 'center', 'textAlign' => 'center']]),
|
||
$column('intro-raster', [
|
||
$vorteil(1, 'user', 'Persönlich', 'Eine feste Ansprechpartnerin statt wechselnder Sachbearbeiter.'),
|
||
$vorteil(2, 'chat', 'Erreichbar', 'Kurze Wege und schnelle Antworten – ohne Warteschleife.'),
|
||
$vorteil(3, 'pin', 'Regional', 'Vor Ort in Gaggenau und Umgebung, Baden-Baden und im Schwarzwald.'),
|
||
], [
|
||
'desktop' => ['display' => 'grid', 'gridColumns' => 3, 'gap' => '24px', 'margin' => ['top' => '96px']],
|
||
'tablet' => ['gridColumns' => 1, 'gap' => '16px'],
|
||
'mobile' => ['margin' => ['top' => '56px']],
|
||
]),
|
||
$text('intro-region', 'Gaggenau & Umgebung · Baden-Baden · Schwarzwald', [
|
||
'desktop' => ['fontSize' => '24px', 'lineHeight' => 1.4, 'letterSpacing' => '-0.01em', 'textAlign' => 'center', 'color' => '@tinte', 'backgroundColor' => '#EADFD0', 'borderRadius' => $rund('999px'), 'padding' => ['top' => '18px', 'bottom' => '18px', 'left' => '24px', 'right' => '24px'], 'margin' => ['top' => '24px', 'bottom' => '0px']],
|
||
'mobile' => ['fontSize' => '18px', 'borderRadius' => $rund('24px')],
|
||
], 'font-style:italic'),
|
||
], $abschnitt('112px', '112px'), null, '', 'wide'),
|
||
|
||
// 3 – Leistungen: drei Fotos mit Titel, Satz und „Was dazugehört“.
|
||
$section('leistungen-bereich', [
|
||
$zeile('leistungen-kopf', [
|
||
$heading('leistungen-titel', 'Leistungen', 2, $h2Stil),
|
||
$text('leistungen-link', '<a href="/verwaltungsanfrage">Unverbindlich anfragen →</a>', ['desktop' => ['fontSize' => '15px', 'fontWeight' => '500', 'margin' => ['bottom' => '0px']]], $linkCss),
|
||
], '16px', ['justifyContent' => 'space-between', 'alignItems' => 'baseline', 'flexWrap' => 'wrap', 'margin' => ['bottom' => '56px']], ['margin' => ['bottom' => '36px']]),
|
||
$column('leistungen-raster', [
|
||
$leistung('leistung-verkauf', $medien['poster'], 'Offener Wohn- und Essbereich mit Treppe, Holzlamellen und großer Glasfront', 'Immobilienverkauf', 'Professionelle Vermarktung, marktgerechte Bewertung und persönliche Begleitung bis zum erfolgreichen Verkauf.', [
|
||
'Professionelle Vermarktung Ihrer Immobilie',
|
||
'Marktgerechte Bewertung',
|
||
'Persönliche Begleitung bis zum erfolgreichen Verkauf',
|
||
]),
|
||
$leistung('leistung-weg', $medien['weg'], 'Wohnanlage mit mehreren Gebäuden und grünem Innenhof', 'WEG-Verwaltung', 'Strukturierte, transparente und zuverlässige Verwaltung von Wohnungseigentümergemeinschaften.', [
|
||
'Eigentümerversammlung vorbereiten, leiten, protokollieren',
|
||
'Beschlüsse umsetzen und Beschlusssammlung führen',
|
||
'Wirtschaftsplan und Jahresabrechnung',
|
||
'Erhaltungsrücklage und Hausgeld verwalten',
|
||
'Instandhaltung planen und begleiten',
|
||
]),
|
||
$leistung('leistung-miet', $medien['miet'], 'Helles Mehrfamilienhaus mit Balkonen und gepflegtem Vorgarten im Abendlicht', 'Mietverwaltung', 'Mieterbetreuung, Mietinkasso und Nebenkostenabrechnung.', [
|
||
'Mietersuche, Besichtigung und Mietvertrag',
|
||
'Mietinkasso und Mahnwesen',
|
||
'Betriebs- und Nebenkostenabrechnung',
|
||
'Ansprechpartnerin für Mieter bei Schäden',
|
||
'Handwerker beauftragen und Rechnungen prüfen',
|
||
]),
|
||
$leistung('leistung-sonder', $medien['sonder'], 'Helles Wohnzimmer mit Sofa, Esstisch und Blick auf den Balkon', 'Sondereigentum', 'Betreuung einzelner Wohnungen – von Vermietung bis Instandhaltung.', [
|
||
'Vermietung und Mieterwechsel Ihrer Wohnung',
|
||
'Miete, Kaution und Nebenkosten im Blick',
|
||
'Ihre Stimme bei der Eigentümerversammlung',
|
||
'Abstimmung mit der WEG-Verwaltung',
|
||
'Übergaben und Wohnungsabnahmen',
|
||
]),
|
||
], [
|
||
'desktop' => ['display' => 'grid', 'gridColumns' => 4, 'columnGap' => '28px', 'rowGap' => '56px', 'alignItems' => 'flex-start'],
|
||
'tablet' => ['gridColumns' => 2],
|
||
'mobile' => ['gridColumns' => 1],
|
||
]),
|
||
], $abschnitt('0px', '0px'), 'leistungen', '', 'wide'),
|
||
|
||
// 4 – So läuft der Wechsel: vier Schritte auf Creme.
|
||
$section('wechsel', [
|
||
$column('wechsel-karte', [
|
||
$row('wechsel-kopf', [
|
||
$heading('wechsel-titel', 'So einfach wechseln Sie zu uns.', 2, $h2Stil),
|
||
$text('wechsel-satz', 'Den Verwalterwechsel organisieren wir – von der Übergabe der Unterlagen bis zur ersten Abrechnung.', $fliess('@text', '0px', '420px')),
|
||
], [], [
|
||
'desktop' => ['display' => 'flex', 'flexWrap' => 'wrap', 'justifyContent' => 'space-between', 'alignItems' => 'flex-end', 'rowGap' => '20px', 'columnGap' => '48px', 'margin' => ['top' => '0px', 'bottom' => '64px']],
|
||
'mobile' => ['margin' => ['bottom' => '40px']],
|
||
]),
|
||
$column('wechsel-raster', [
|
||
$schritt(1, 'Anfrage', 'Sie schildern uns kurz Ihr Objekt – online in drei Schritten oder im Gespräch.'),
|
||
$schritt(2, 'Kennenlernen', 'Wir sehen uns das Objekt vor Ort an und klären, was Ihnen wichtig ist.'),
|
||
$schritt(3, 'Angebot', 'Sie erhalten ein klares Angebot mit allen Leistungen – ohne Kleingedrucktes.'),
|
||
$schritt(4, 'Übergabe', 'Wir übernehmen Unterlagen, Konten und Verträge und informieren Mieter und Eigentümer.'),
|
||
], [
|
||
'desktop' => ['display' => 'grid', 'gridColumns' => 4, 'gap' => '40px'],
|
||
'tablet' => ['gridColumns' => 2, 'rowGap' => '48px'],
|
||
'mobile' => ['gridColumns' => 1, 'rowGap' => '36px'],
|
||
]),
|
||
$zeile('wechsel-knoepfe', [
|
||
$knopf('wechsel-knopf', 'Verwaltungsanfrage starten', '/verwaltungsanfrage'),
|
||
], '14px', ['margin' => ['top' => '64px']], ['margin' => ['top' => '44px']]),
|
||
], [
|
||
'desktop' => ['backgroundColor' => '@creme', 'borderRadius' => $rund('24px'), 'padding' => ['top' => '80px', 'bottom' => '80px', 'left' => '64px', 'right' => '64px']],
|
||
'tablet' => ['padding' => ['left' => '40px', 'right' => '40px']],
|
||
'mobile' => ['padding' => ['top' => '52px', 'bottom' => '52px', 'left' => '22px', 'right' => '22px']],
|
||
]),
|
||
], $abschnitt('112px', '0px'), 'ablauf', '', 'wide'),
|
||
|
||
// 5 – Über uns: dunkle Karte mit Monogramm (Portrait fehlt), Text rechts.
|
||
$section('ueber-bereich', [
|
||
$row('ueber-zeile', [
|
||
$column('ueber-karte', [
|
||
$text('ueber-monogramm', 'EK', ['desktop' => ['fontSize' => '160px', 'fontWeight' => '500', 'lineHeight' => 1, 'letterSpacing' => '-0.04em', 'color' => '@gold', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '110px']], 'opacity:.9'),
|
||
$text('ueber-zitat', 'Ein Lichtblick für Ihre Immobilie.', ['desktop' => ['fontSize' => '22px', 'lineHeight' => 1.4, 'letterSpacing' => '-0.01em', 'color' => '@auf-dunkel', 'maxWidth' => '460px', 'margin' => ['top' => 'auto', 'bottom' => '0px']], 'mobile' => ['fontSize' => '19px']]),
|
||
], [
|
||
'desktop' => ['display' => 'flex', 'flexDirection' => 'column', 'justifyContent' => 'space-between', 'gap' => '40px', 'minHeight' => '480px', 'backgroundColor' => '#3C352D', 'borderRadius' => $rund('24px'), 'padding' => $seiten('48px')],
|
||
'mobile' => ['minHeight' => '380px', 'padding' => $seiten('28px')],
|
||
], "position:relative;overflow:hidden\n&::after{content:'';position:absolute;right:-90px;bottom:-90px;width:300px;height:300px;border:1.5px solid rgba(189,160,118,.35);border-radius:50%;pointer-events:none}"),
|
||
$column('ueber-text', [
|
||
$heading('ueber-titel', 'Eine Ansprechpartnerin. Für alles.', 2, [...$h2Stil, 'desktop' => [...$h2Stil['desktop'], 'margin' => ['top' => '0px', 'bottom' => '24px']]]),
|
||
$text('ueber-satz', 'Bei Luximmo landen Sie nicht in einer Warteschleife. '.$person.' kennt Ihr Objekt, Ihre Mieter und Ihre Unterlagen – und ist direkt für Sie erreichbar.', $fliess('@text', '24px')),
|
||
$haken('ueber-punkt-1', '<strong>Feste Zuständigkeit</strong> – keine Weiterleitung, kein Ticketsystem'),
|
||
$haken('ueber-punkt-2', '<strong>Transparente Abrechnungen</strong> – nachvollziehbar und pünktlich'),
|
||
$haken('ueber-punkt-3', '<strong>Vor Ort</strong> – Begehungen, Übergaben, Versammlungen in Gaggenau und Umgebung'),
|
||
$text('ueber-name', $person.'<br><em>'.$firma.'</em>', ['desktop' => ['fontSize' => '17px', 'fontWeight' => '500', 'lineHeight' => 1.55, 'margin' => ['top' => '20px', 'bottom' => '0px']]], '& em{font-style:normal;font-weight:400;color:#8A8278}'),
|
||
], ['desktop' => ['padding' => ['bottom' => '8px']]]),
|
||
], $zweiSpalten, [
|
||
'desktop' => ['gap' => '64px', 'alignItems' => 'flex-end', 'margin' => ['top' => '0px'], 'columnWidths' => '7 5'],
|
||
'tablet' => ['gap' => '40px'],
|
||
]),
|
||
], $abschnitt('112px', '0px'), 'ueber-uns', '', 'wide'),
|
||
|
||
// 6 – Bewertung: dunkle Fläche, links Text und Knopf, rechts die vier Objektarten.
|
||
$section('bewertung-bereich', [
|
||
$column('bewertung-band', [
|
||
$row('bewertung-zeile', [
|
||
$column('bewertung-text', [
|
||
$kleinzeile('bewertung-klein', 'Immobilienbewertung', '@gold', '18px'),
|
||
$heading('bewertung-titel', 'Was ist Ihre Immobilie wert?', 2, [...$h2Stil, 'desktop' => [...$h2Stil['desktop'], 'color' => '@papier', 'margin' => ['top' => '0px', 'bottom' => '20px']]]),
|
||
$text('bewertung-satz', 'Ob Verkauf, Vermietung oder einfach zur Orientierung: Wir schätzen den Wert Ihrer Wohnung, Ihres Hauses oder Grundstücks in Gaggenau und Umgebung – kostenlos und unverbindlich.', $fliess('@auf-dunkel', '28px', '480px')),
|
||
$haken('bewertung-punkt-1', 'Drei kurze Schritte, Schätzwerte genügen', '@auf-dunkel', '@gold'),
|
||
$haken('bewertung-punkt-2', 'Persönliche Einschätzung statt Rechner-Ergebnis', '@auf-dunkel', '@gold'),
|
||
$zeile('bewertung-knoepfe', [
|
||
$knopf('bewertung-knopf', 'Jetzt Wert ermitteln', '/immobilienbewertung', 'gold'),
|
||
], '14px', ['margin' => ['top' => '24px']]),
|
||
]),
|
||
$column('bewertung-arten', array_map(
|
||
static fn (string $schluessel, string $name) => $column('bewertung-art-'.$schluessel, [
|
||
$bild('bewertung-art-'.$schluessel.'-bild', $medien['opt-'.$schluessel], '', ['desktop' => ['margin' => ['top' => '0px', 'right' => '0px', 'bottom' => '10px', 'left' => '0px']]], null, '& img{display:block;width:100%;border-radius:16px}'),
|
||
$text('bewertung-art-'.$schluessel.'-text', '<a href="/immobilienbewertung">'.$name.'</a>', ['desktop' => ['fontSize' => '16px', 'fontWeight' => '500', 'textAlign' => 'center', 'margin' => ['bottom' => '0px']]], "& a{color:#fff;text-decoration:none}\n& a:hover{color:#BDA076}"),
|
||
], ['desktop' => ['padding' => $seiten('10px'), 'borderRadius' => $rund('22px'), 'backgroundColor' => '#ffffff0f']],
|
||
"position:relative;transition:background-color .25s ease,transform .25s ease\n& a::after{content:'';position:absolute;top:0;right:0;bottom:0;left:0}\n&:hover{background-color:#ffffff1f;transform:translateY(-3px)}\n&:focus-within{outline:2px solid #BDA076;outline-offset:3px}"),
|
||
['wohnung', 'haus', 'mfh', 'grund'],
|
||
['Wohnung', 'Haus', 'Mehrfamilienhaus', 'Grundstück'],
|
||
), [
|
||
'desktop' => ['display' => 'grid', 'gridColumns' => 2, 'gap' => '16px'],
|
||
]),
|
||
], $zweiSpalten, [
|
||
'desktop' => ['gap' => '64px', 'alignItems' => 'center', 'margin' => ['top' => '0px'], 'columnWidths' => '1 1'],
|
||
'tablet' => ['gap' => '40px'],
|
||
]),
|
||
], [
|
||
'desktop' => ['backgroundColor' => '#3C352D', 'color' => '@papier', 'borderRadius' => $rund('24px'), 'padding' => ['top' => '80px', 'bottom' => '80px', 'left' => '64px', 'right' => '64px']],
|
||
'tablet' => ['padding' => ['left' => '40px', 'right' => '40px']],
|
||
'mobile' => ['padding' => ['top' => '52px', 'bottom' => '52px', 'left' => '22px', 'right' => '22px']],
|
||
]),
|
||
], $abschnitt('112px', '0px'), 'wert', '', 'wide'),
|
||
|
||
// 7 – Häufige Fragen.
|
||
$section('fragen-bereich', [
|
||
$row('fragen-zeile', [
|
||
$column('fragen-kopf', [
|
||
$heading('fragen-titel', 'Häufige Fragen', 2, [...$h2Stil, 'desktop' => [...$h2Stil['desktop'], 'margin' => ['top' => '0px', 'bottom' => '16px']]]),
|
||
$text('fragen-satz', 'Ihre Frage ist nicht dabei? <a href="#kontakt">Schreiben Sie uns.</a>', $fliess('@text'), $linkCss),
|
||
]),
|
||
['id' => 'fragen-liste', 'type' => 'accordion', 'props' => ['faq' => true], 'css' => 'gap:0',
|
||
'style' => ['desktop' => ['borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie']],
|
||
'children' => [
|
||
$frage(1, 'Was kostet eine Hausverwaltung bei Luximmo?', 'Das hängt von der Art der Verwaltung, der Zahl der Einheiten und dem Zustand des Objekts ab. Nach einer kurzen <a href="/verwaltungsanfrage">Verwaltungsanfrage</a> erhalten Sie ein Angebot mit allen Leistungen und Preisen.'),
|
||
$frage(2, 'Wie läuft ein Wechsel von der bisherigen Verwaltung ab?', 'Sie kündigen den bisherigen Vertrag fristgerecht, bei einer WEG beschließt die Eigentümerversammlung die neue Verwaltung. Die Übergabe von Unterlagen, Konten und Verträgen organisieren wir mit der bisherigen Verwaltung.'),
|
||
$frage(3, 'Welche Objekte verwalten Sie?', 'Mehrfamilienhäuser, Wohn- und Geschäftshäuser, Eigentümergemeinschaften und einzelne Eigentumswohnungen – in Gaggenau und Umgebung, Baden-Baden und im Schwarzwald.'),
|
||
$frage(4, 'Wie erreichen Mieter und Eigentümer Sie?', 'Direkt bei '.$person.' – telefonisch, per E-Mail oder über das Kontaktformular. Anfragen beantworten wir in der Regel innerhalb eines Werktags.'),
|
||
$frage(6, 'Unterstützen Sie mich auch beim Verkauf?', 'Ja. Wir übernehmen die professionelle Vermarktung, ermitteln einen marktgerechten Preis und begleiten Sie persönlich bis zum erfolgreichen Verkauf. Den ersten Schritt machen Sie mit der <a href="/immobilienbewertung">Immobilienbewertung</a>.'),
|
||
$frage(5, 'Übernehmen Sie auch einzelne Wohnungen?', 'Ja. Mit der Sondereigentumsverwaltung betreuen wir einzelne Wohnungen: Vermietung, Miete und Nebenkosten, Abstimmung mit der WEG-Verwaltung und Ihre Vertretung in der Versammlung.'),
|
||
],
|
||
],
|
||
], $zweiSpalten, [
|
||
'desktop' => ['gap' => '64px', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px'], 'columnWidths' => '4 7'],
|
||
'tablet' => ['gap' => '32px'],
|
||
]),
|
||
], $abschnitt('112px', '0px'), 'fragen', '', 'wide'),
|
||
|
||
// 7 – Kontakt: Linie, große Schlagzeile, Anschrift, Formular in Creme.
|
||
$section('kontakt-bereich', [
|
||
$row('kontakt-zeile', [
|
||
$column('kontakt-text', [
|
||
$heading('kontakt-titel', 'Sprechen wir über Ihre Immobilie.', 2, [
|
||
'desktop' => ['fontSize' => '76px', 'fontWeight' => '500', 'lineHeight' => 0.98, 'letterSpacing' => '-0.035em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '40px']],
|
||
'tablet' => ['fontSize' => '56px'],
|
||
'mobile' => ['fontSize' => '40px', 'margin' => ['bottom' => '28px']],
|
||
]),
|
||
$text('kontakt-satz', 'Schreiben Sie uns kurz, worum es geht – wir melden uns in der Regel innerhalb eines Werktags.', $fliess('@text', '40px', '420px')),
|
||
$zeile('kontakt-angaben', [
|
||
$column('kontakt-adresse', [
|
||
$kleinzeile('kontakt-adresse-titel', 'Adresse', '@gold-dunkel', '8px'),
|
||
$text('kontakt-adresse-text', $firma.'<br>'.$person.'<br>'.$strasse.'<br>'.$plz.' '.$ort, ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.6, 'margin' => ['bottom' => '0px']]]),
|
||
]),
|
||
$column('kontakt-mehr', [
|
||
$kleinzeile('kontakt-mehr-titel', 'Direkt', '@gold-dunkel', '8px'),
|
||
$text('kontakt-mehr-text', '<a href="'.$tel.'">'.$telefon.'</a><br><a href="mailto:'.$mail.'">'.$mail.'</a><br>Für ein Angebot:<br><a href="/verwaltungsanfrage">Verwaltungsanfrage →</a>', ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.6, 'margin' => ['bottom' => '0px']]], $linkCss),
|
||
]),
|
||
], '56px', ['alignItems' => 'flex-start', 'flexWrap' => 'wrap', 'rowGap' => '32px']),
|
||
]),
|
||
$column('kontakt-karte', [
|
||
['id' => 'kontakt-formular', 'type' => 'form', 'props' => ['form' => 'kontakt'], 'css' => '__FORMULAR__'],
|
||
], [
|
||
'desktop' => ['backgroundColor' => '@creme', 'borderRadius' => $rund('24px'), 'padding' => $seiten('56px')],
|
||
'tablet' => ['padding' => $seiten('36px')],
|
||
'mobile' => ['padding' => ['top' => '28px', 'right' => '20px', 'bottom' => '28px', 'left' => '20px']],
|
||
]),
|
||
], $zweiSpalten, [
|
||
'desktop' => ['gap' => '96px', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px'], 'columnWidths' => '1 1',
|
||
'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@tinte', 'padding' => ['top' => '56px']],
|
||
'tablet' => ['gap' => '48px'],
|
||
]),
|
||
], $abschnitt('112px', '112px'), 'kontakt', '', 'wide'),
|
||
],
|
||
];
|
||
|
||
// Die Formulare im Stil der Vorlage: weiße Felder auf Creme, runde Ecken, goldener Fokus.
|
||
$formularCss = '& .block-form{max-width:none}
|
||
& .form-field label{font-size:14px;font-weight:500;color:#141414}
|
||
& .form-field input{background:#fff;border:1px solid #E6DED2;border-radius:14px;padding:15px 18px;font-size:16px;color:#141414;min-height:54px}
|
||
& .form-field select{background:#fff;border:1px solid #E6DED2;border-radius:14px;padding:15px 18px;font-size:16px;color:#141414;min-height:54px}
|
||
& .form-field textarea{background:#fff;border:1px solid #E6DED2;border-radius:14px;padding:15px 18px;font-size:16px;color:#141414;min-height:140px}
|
||
& .form-field input:focus{border-color:#BDA076;outline:4px solid rgba(189,160,118,.25);outline-offset:0}
|
||
& .form-field select:focus{border-color:#BDA076;outline:4px solid rgba(189,160,118,.25);outline-offset:0}
|
||
& .form-field textarea:focus{border-color:#BDA076;outline:4px solid rgba(189,160,118,.25);outline-offset:0}
|
||
& .field-note{font-size:13px;color:#5A554D;opacity:1}
|
||
& .form-step-title{font-size:24px;font-weight:500;letter-spacing:-0.015em;margin-bottom:12px}
|
||
& .form-step-intro{color:#5A554D;opacity:1;margin:0}
|
||
& .form-progress-step{color:#5A554D;opacity:1}
|
||
& .form-choice{display:flex;flex-wrap:wrap;gap:10px}
|
||
& .form-choice .check-row{gap:10px;align-items:center;background:#fff;border:1px solid #E6DED2;border-radius:999px;padding:11px 18px;transition:border-color .2s}
|
||
& .form-choice .check-row:hover{border-color:#BDA076}
|
||
& .check-row label{font-weight:400;font-size:15px;color:#141414}
|
||
& .check-row input{accent-color:#141414;min-height:0;width:18px;height:18px;padding:0;margin:0}
|
||
& .cms-button{min-height:54px;font-size:16px;padding:0 32px;transition:transform .2s ease,background-color .2s ease}
|
||
& .cms-button:hover{transform:translateY(-2px)}';
|
||
|
||
$ersetzeFormularCss = static function (array $bloecke) use (&$ersetzeFormularCss, $formularCss): array {
|
||
foreach ($bloecke as $i => $block) {
|
||
if ('__FORMULAR__' === ($block['css'] ?? null)) {
|
||
$bloecke[$i]['css'] = $formularCss;
|
||
}
|
||
if (isset($block['children'])) {
|
||
$bloecke[$i]['children'] = $ersetzeFormularCss($block['children']);
|
||
}
|
||
}
|
||
|
||
return $bloecke;
|
||
};
|
||
$startseite['blocks'] = $ersetzeFormularCss($startseite['blocks']);
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Seiten anlegen und veröffentlichen
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$anlegen = static function (string $slug, array $dokument, string $seoTitel = '', string $teilbild = '') use ($pages, $db): string {
|
||
$id = $pages->createPage($slug, null, 'page', $dokument);
|
||
if ('' !== $seoTitel) {
|
||
$db->executeStatement('UPDATE page SET seo_title = ? WHERE id = ?', [$seoTitel, $id]);
|
||
}
|
||
if ('' !== $teilbild) {
|
||
$db->executeStatement('UPDATE page SET og_media_id = ? WHERE id = ?', [$teilbild, $id]);
|
||
}
|
||
$seite = $db->fetchAssociative('SELECT draft_revision_id, version FROM page WHERE id = ?', [$id]);
|
||
$pages->publish($id, $seite['draft_revision_id'], (int) $seite['version']);
|
||
echo 'Seite angelegt: /'.$db->fetchOne('SELECT path FROM page WHERE id = ?', [$id])."\n";
|
||
|
||
return $id;
|
||
};
|
||
|
||
// Der Hero steht sofort (LCP); alles darunter steigt beim Scrollen auf.
|
||
$startseite = $einblenden($startseite, [
|
||
'intro-kopf' => ['rise'],
|
||
'vorteil-1' => ['rise'],
|
||
'vorteil-2' => ['rise', 'short'],
|
||
'vorteil-3' => ['rise', 'medium'],
|
||
'leistungen-kopf' => ['rise'],
|
||
'leistung-verkauf' => ['rise'],
|
||
'leistung-weg' => ['rise', 'short'],
|
||
'leistung-miet' => ['rise', 'medium'],
|
||
'leistung-sonder' => ['rise', 'long'],
|
||
'wechsel-kopf' => ['rise'],
|
||
'schritt-1' => ['rise'],
|
||
'schritt-2' => ['rise', 'short'],
|
||
'schritt-3' => ['rise', 'medium'],
|
||
'schritt-4' => ['rise', 'long'],
|
||
'ueber-karte' => ['left'],
|
||
'ueber-text' => ['rise', 'short'],
|
||
'fragen-zeile' => ['rise'],
|
||
'kontakt-text' => ['rise'],
|
||
'kontakt-karte' => ['rise', 'short'],
|
||
]);
|
||
|
||
$homeId = $anlegen('home', $startseite, 'Hausverwaltung & Immobilienverkauf in Gaggenau | LUXIMMO', $medien['poster']['id']);
|
||
|
||
/** Der Seitenkopf der Unterseiten: Etikett, große Überschrift, Vorspann. */
|
||
$seitenkopf = static fn (string $praefix, string $klein, string $titel, string $vorspann) => $section($praefix.'-kopf', [
|
||
$etikett($praefix.'-etikett', $klein, '28px'),
|
||
$heading($praefix.'-h1', $titel, 1, [
|
||
'desktop' => ['fontSize' => '84px', 'fontWeight' => '500', 'lineHeight' => 0.98, 'letterSpacing' => '-0.035em', 'color' => '@tinte', 'maxWidth' => '980px', 'margin' => ['top' => '0px', 'bottom' => '28px']],
|
||
'tablet' => ['fontSize' => '60px'],
|
||
'mobile' => ['fontSize' => '42px', 'margin' => ['bottom' => '20px']],
|
||
]),
|
||
$text($praefix.'-vorspann', $vorspann, $fliess('@text', '0px', '620px', '19px')),
|
||
], $abschnitt('64px', '64px'), null, '', 'wide');
|
||
|
||
$karte = [
|
||
'desktop' => ['backgroundColor' => '@creme', 'borderRadius' => $rund('24px'), 'padding' => $seiten('56px')],
|
||
'tablet' => ['padding' => $seiten('36px')],
|
||
'mobile' => ['padding' => ['top' => '28px', 'right' => '20px', 'bottom' => '28px', 'left' => '20px']],
|
||
];
|
||
|
||
/** Die dunkle Karte der Seitenspalte: Ansprechpartnerin und Anschrift. */
|
||
$personKarte = static fn (string $praefix) => $column($praefix.'-person', [
|
||
$text($praefix.'-person-monogramm', 'EK', ['desktop' => ['fontSize' => '56px', 'fontWeight' => '500', 'lineHeight' => 1, 'letterSpacing' => '-0.04em', 'color' => '@gold', 'margin' => ['bottom' => '20px']]]),
|
||
$text($praefix.'-person-text', 'Ihre Anfrage landet direkt bei <strong>'.$person.'</strong>. Sie meldet sich in der Regel innerhalb eines Werktags.', ['desktop' => ['fontSize' => '16px', 'lineHeight' => 1.6, 'color' => '@auf-dunkel', 'margin' => ['bottom' => '20px']]], '& strong{color:#fff;font-weight:500}'),
|
||
$zeile($praefix.'-person-ort', [
|
||
$symbol($praefix.'-person-ort-icon', 'pin', 20, ['desktop' => ['color' => '@gold', 'flexShrink' => 0]]),
|
||
$text($praefix.'-person-ort-text', $firma.', '.$strasse.', '.$plz.' '.$ort, ['desktop' => ['fontSize' => '15px', 'lineHeight' => 1.5, 'color' => '@auf-dunkel', 'margin' => ['bottom' => '0px']]]),
|
||
], '12px', ['alignItems' => 'flex-start', 'margin' => ['bottom' => '10px']]),
|
||
$zeile($praefix.'-person-telefon', [
|
||
$symbol($praefix.'-person-telefon-icon', 'phone', 20, ['desktop' => ['color' => '@gold', 'flexShrink' => 0]]),
|
||
$text($praefix.'-person-telefon-text', '<a href="'.$tel.'">'.$telefon.'</a>', ['desktop' => ['fontSize' => '15px', 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']]], "& a{color:#fff;text-decoration:none}\n& a:hover{color:#BDA076}"),
|
||
], '12px', ['alignItems' => 'flex-start', 'margin' => ['bottom' => '10px']]),
|
||
$zeile($praefix.'-person-mail', [
|
||
$symbol($praefix.'-person-mail-icon', 'mail', 20, ['desktop' => ['color' => '@gold', 'flexShrink' => 0]]),
|
||
$text($praefix.'-person-mail-text', '<a href="mailto:'.$mail.'">'.$mail.'</a>', ['desktop' => ['fontSize' => '15px', 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']]], "& a{color:#fff;text-decoration:none;overflow-wrap:anywhere}\n& a:hover{color:#BDA076}"),
|
||
], '12px', ['alignItems' => 'flex-start']),
|
||
], ['desktop' => ['backgroundColor' => '#3C352D', 'borderRadius' => $rund('24px'), 'padding' => $seiten('32px')]]);
|
||
|
||
/** Hinweiskarte der Seitenspalte: Titel und Haken. */
|
||
$hinweis = static fn (string $id, string $titel, array $punkte) => $column($id, [
|
||
$heading($id.'-titel', $titel, 2, ['desktop' => ['fontSize' => '22px', 'fontWeight' => '500', 'letterSpacing' => '-0.015em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '18px']]]),
|
||
...array_map(static fn (int $i, string $p) => $haken($id.'-punkt-'.$i, $p), array_keys($punkte), $punkte),
|
||
], ['desktop' => ['borderRadius' => $rund('24px'), 'padding' => $seiten('32px'), 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie']]);
|
||
|
||
// Die Bildkarten einer Auswahl: Bild oben, gerundet, dunkler Rand bei Wahl.
|
||
$kartenCss = "\n& .form-choice-grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:14px}\n& .form-choice-card label{border:1.5px solid #E6DED2;border-radius:20px;background:#fff;padding:10px 10px 14px;font-weight:500;font-size:15px;color:#141414}\n& .form-choice-card label:hover{border-color:#BDA076}\n& .form-choice-card input:checked+label{border-color:#141414;box-shadow:0 0 0 1.5px #141414}\n& .form-choice-card input{accent-color:#141414;top:20px;left:20px;width:20px;height:20px;min-height:0;padding:0;margin:0}\n& .form-choice-card input:focus{outline:none}\n& .form-choice-picture{border-radius:12px}";
|
||
|
||
$anlegen('immobilienbewertung', $einblenden([
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Immobilienbewertung',
|
||
'description' => 'Was ist Ihre Immobilie wert? Luximmo schätzt Wohnungen, Häuser, Mehrfamilienhäuser und Grundstücke in Gaggenau und Umgebung – kostenlos und unverbindlich.',
|
||
'blocks' => [
|
||
$seitenkopf('bewertung', 'Immobilienbewertung', 'Was ist Ihre Immobilie wert?', 'Ob Verkauf, Vermietung oder einfach zur Orientierung: Beantworten Sie ein paar kurze Fragen, und wir melden uns mit einer persönlichen Einschätzung – kostenlos und unverbindlich.'),
|
||
$section('bewertung', [
|
||
$row('bewertung-seite-zeile', [
|
||
$column('bewertung-karte', [
|
||
['id' => 'bewertung-formular', 'type' => 'form', 'props' => ['form' => 'bewertung'], 'css' => $formularCss.$kartenCss],
|
||
$text('bewertung-hinweis', 'Ihre Angaben verwenden wir nur für Ihre Anfrage. Mehr dazu in der <a href="/datenschutz">Datenschutzerklärung</a>.', ['desktop' => ['fontSize' => '13px', 'lineHeight' => 1.6, 'color' => '@text', 'margin' => ['top' => '20px', 'bottom' => '0px']]], $linkCss),
|
||
], $karte),
|
||
$column('bewertung-seite', [
|
||
$hinweis('bewertung-ablauf', 'Gut zu wissen', [
|
||
'Drei kurze Schritte, Schätzwerte genügen.',
|
||
'Für Wohnungen, Häuser, Mehrfamilienhäuser und Grundstücke.',
|
||
'Eine persönliche Einschätzung, kein automatischer Rechner.',
|
||
'Kostenlos und ohne Verpflichtung.',
|
||
]),
|
||
$personKarte('bewertung'),
|
||
], ['desktop' => ['display' => 'flex', 'flexDirection' => 'column', 'gap' => '24px']]),
|
||
], $zweiSpalten, [
|
||
'desktop' => ['gap' => '40px', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px'], 'columnWidths' => '2 1'],
|
||
'tablet' => ['gap' => '28px'],
|
||
]),
|
||
], $abschnitt('0px', '160px'), null, '', 'wide'),
|
||
],
|
||
], ['bewertung-karte' => ['rise'], 'bewertung-seite' => ['rise', 'short']]), 'Immobilienbewertung Gaggenau – Was ist Ihre Immobilie wert? | LUXIMMO', $medien['miet']['id']);
|
||
|
||
$anlegen('verwaltungsanfrage', $einblenden([
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Verwaltungsanfrage',
|
||
'description' => 'Verwaltungsangebot in drei Schritten: Mietverwaltung, WEG-Verwaltung oder Sondereigentum in Gaggenau und Umgebung bei Luximmo anfragen.',
|
||
'blocks' => [
|
||
$seitenkopf('anfrage', 'Verwaltungsanfrage', 'Ihr Angebot in drei Schritten.', 'Sagen Sie uns, welche Verwaltung Sie suchen und um welches Objekt es geht. Ungefähre Angaben genügen – wir melden uns mit einem Angebot oder Rückfragen.'),
|
||
$section('anfrage', [
|
||
$row('anfrage-zeile', [
|
||
$column('anfrage-karte', [
|
||
['id' => 'anfrage-formular', 'type' => 'form', 'props' => ['form' => 'verwaltungsanfrage'], 'css' => $formularCss.$kartenCss],
|
||
$text('anfrage-hinweis', 'Ihre Angaben verwenden wir nur für Ihre Anfrage. Mehr dazu in der <a href="/datenschutz">Datenschutzerklärung</a>.', ['desktop' => ['fontSize' => '13px', 'lineHeight' => 1.6, 'color' => '@text', 'margin' => ['top' => '20px', 'bottom' => '0px']]], $linkCss),
|
||
], $karte),
|
||
$column('anfrage-seite', [
|
||
$hinweis('anfrage-ablauf', 'So geht es weiter', [
|
||
'Wir sehen uns Ihre Angaben persönlich an.',
|
||
'Bei Bedarf besichtigen wir das Objekt gemeinsam.',
|
||
'Sie erhalten ein klares Angebot – unverbindlich.',
|
||
]),
|
||
$personKarte('anfrage'),
|
||
], ['desktop' => ['display' => 'flex', 'flexDirection' => 'column', 'gap' => '24px']]),
|
||
], $zweiSpalten, [
|
||
'desktop' => ['gap' => '40px', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px'], 'columnWidths' => '2 1'],
|
||
'tablet' => ['gap' => '28px'],
|
||
]),
|
||
], $abschnitt('0px', '160px'), null, '', 'wide'),
|
||
],
|
||
], ['anfrage-karte' => ['rise'], 'anfrage-seite' => ['rise', 'short']]), 'Verwaltungsanfrage – Angebot für Ihre Hausverwaltung | LUXIMMO', $medien['weg']['id']);
|
||
|
||
/** Eine Rechtsseite: Seitenkopf, dann Absätze mit Zwischenüberschriften. */
|
||
$rechtsseite = static function (string $praefix, string $titel, string $vorspann, array $absaetze) use ($seitenkopf, $section, $column, $heading, $text, $linkCss, $abschnitt): array {
|
||
$punkte = [];
|
||
foreach ($absaetze as $i => [$zwischentitel, $inhalt]) {
|
||
if ('' !== $zwischentitel) {
|
||
$punkte[] = $heading($praefix.'-h2-'.$i, $zwischentitel, 2, ['desktop' => ['fontSize' => '24px', 'fontWeight' => '500', 'lineHeight' => 1.3, 'letterSpacing' => '-0.015em', 'color' => '@tinte', 'margin' => ['top' => '40px', 'bottom' => '10px']]]);
|
||
}
|
||
$punkte[] = $text($praefix.'-p-'.$i, $inhalt, ['desktop' => ['fontSize' => '17px', 'lineHeight' => 1.7, 'color' => '@text', 'margin' => ['bottom' => '0px']]], $linkCss);
|
||
}
|
||
|
||
return [
|
||
$seitenkopf($praefix, 'Rechtliches', $titel, $vorspann),
|
||
$section($praefix, [
|
||
$column($praefix.'-inhalt', $punkte, ['desktop' => ['maxWidth' => '820px', 'borderWidth' => ['top' => '1px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'], 'borderStyle' => 'solid', 'borderColor' => '@linie']]),
|
||
], $abschnitt('0px', '160px'), null, '', 'wide'),
|
||
];
|
||
};
|
||
|
||
$offen = '<strong>NOCH ZU ERGÄNZEN</strong>';
|
||
$anlegen('impressum', [
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Impressum',
|
||
'description' => 'Anbieterangaben der Luximmo UG (haftungsbeschränkt), Hauptstraße 169, 76571 Gaggenau.',
|
||
'blocks' => $rechtsseite('impressum', 'Impressum', 'Angaben gemäß § 5 DDG.', [
|
||
['Anbieter', $firma.' (haftungsbeschränkt)<br>'.$strasse.'<br>'.$plz.' '.$ort],
|
||
['Vertreten durch', 'Geschäftsführung: '.$offen],
|
||
['Kontakt', 'Telefon: <a href="'.$tel.'">'.$telefon.'</a><br>E-Mail: <a href="mailto:'.$mail.'">'.$mail.'</a><br>Website: <a href="'.$domain.'">lux-immo.net</a>'],
|
||
['Registereintrag', 'Handelsregister, Registergericht und Registernummer: '.$offen],
|
||
['Umsatzsteuer-ID', 'Umsatzsteuer-Identifikationsnummer nach § 27a UStG: '.$offen],
|
||
['Erlaubnis nach § 34c GewO', 'Wohnimmobilienverwalter mit Erlaubnis nach § 34c Abs. 1 Satz 1 Nr. 4 GewO. Zuständige Aufsichtsbehörde: '.$offen],
|
||
['Verbraucherstreitbeilegung', 'Wir sind nicht bereit und nicht verpflichtet, an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.'],
|
||
['Haftung für Inhalte und Links', 'Die Inhalte dieser Website wurden mit größtmöglicher Sorgfalt erstellt. Für Inhalte verlinkter Websites Dritter ist der jeweilige Betreiber verantwortlich; bei Bekanntwerden von Rechtsverstößen entfernen wir solche Links umgehend.'],
|
||
['Urheberrecht', 'Die Inhalte dieser Website unterliegen dem deutschen Urheberrecht. Jede Verwertung außerhalb der Grenzen des Urheberrechts bedarf der vorherigen schriftlichen Zustimmung.'],
|
||
['Bildnachweis', 'Fotos und Video: '.$offen],
|
||
]),
|
||
], 'Impressum | LUXIMMO');
|
||
|
||
$anlegen('datenschutz', [
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Datenschutz',
|
||
'description' => 'Welche Daten beim Besuch der Website der Luximmo UG und bei einer Anfrage verarbeitet werden.',
|
||
'blocks' => $rechtsseite('datenschutz', 'Datenschutzerklärung', 'Hier erfahren Sie, welche Daten beim Besuch dieser Website und bei einer Anfrage verarbeitet werden – und welche Rechte Sie haben.', [
|
||
['1. Verantwortlicher', $firma.' (haftungsbeschränkt), '.$person.', '.$strasse.', '.$plz.' '.$ort.'. Telefon <a href="'.$tel.'">'.$telefon.'</a>, E-Mail <a href="mailto:'.$mail.'">'.$mail.'</a>.'],
|
||
['2. Besuch dieser Website', 'Beim Aufruf verarbeitet unser Hostingdienstleister technisch erforderliche Verbindungsdaten: IP-Adresse, Zeitpunkt, angeforderte Adresse, Browserangaben und HTTP-Status. Das dient der sicheren Bereitstellung und der Fehlersuche. Rechtsgrundlage ist unser berechtigtes Interesse an einem störungsfreien Betrieb, Art. 6 Abs. 1 lit. f DSGVO. Mit dem Hostingdienstleister besteht ein Vertrag über die Auftragsverarbeitung nach Art. 28 DSGVO. Die Serverprotokolle werden nach spätestens sieben Tagen gelöscht oder anonymisiert, sofern sie nicht zur Aufklärung eines Sicherheitsvorfalls länger gebraucht werden.'],
|
||
['3. Cookies und externe Dienste', 'Diese Website setzt keine Analyse- oder Werbecookies. Bilder, Video, Schrift und Gestaltung kommen vom eigenen Server; es werden keine Karten, Videos oder Schriften von Dritten nachgeladen. Deshalb gibt es auch keinen Einwilligungsdialog.'],
|
||
['4. Kontaktformular, Verwaltungsanfrage und Immobilienbewertung', 'Wenn Sie uns über ein Formular schreiben, verarbeiten wir Ihre Angaben – etwa Name, E-Mail-Adresse, Telefonnummer und die Angaben zu Ihrem Objekt –, um Ihre Anfrage zu bearbeiten und Ihnen ein Angebot oder eine Werteinschätzung zu machen (Art. 6 Abs. 1 lit. b DSGVO). Die Anfrage wird in unserem System gespeichert und per E-Mail an uns weitergeleitet. Wir löschen sie, wenn sie erledigt ist und keine gesetzliche Aufbewahrungspflicht besteht.'],
|
||
['5. Kontakt per E-Mail oder Telefon', 'Wenn Sie uns schreiben oder anrufen, verarbeiten wir Ihre Angaben, um Ihr Anliegen zu bearbeiten (Art. 6 Abs. 1 lit. b oder f DSGVO).'],
|
||
['6. Ihre Rechte', 'Sie haben unter den gesetzlichen Voraussetzungen das Recht auf Auskunft, Berichtigung, Löschung, Einschränkung der Verarbeitung und Datenübertragbarkeit. Einer Verarbeitung auf Grundlage von Art. 6 Abs. 1 lit. f DSGVO können Sie widersprechen.'],
|
||
['7. Beschwerderecht', 'Sie können sich bei einer Datenschutz-Aufsichtsbehörde beschweren, für uns beim Landesbeauftragten für den Datenschutz und die Informationsfreiheit Baden-Württemberg.'],
|
||
]),
|
||
], 'Datenschutz | LUXIMMO');
|
||
|
||
$fehlerId = $anlegen('404', [
|
||
'schemaVersion' => $SCHEMA, 'title' => 'Seite nicht gefunden',
|
||
'description' => 'Die gesuchte Seite gibt es nicht oder sie wurde verschoben.',
|
||
'blocks' => [
|
||
$section('fehler', [
|
||
$etikett('fehler-etikett', 'Fehler 404'),
|
||
$heading('fehler-titel', 'Diese Seite gibt es leider nicht.', 1, [
|
||
'desktop' => ['fontSize' => '76px', 'fontWeight' => '500', 'lineHeight' => 1, 'letterSpacing' => '-0.035em', 'color' => '@tinte', 'margin' => ['top' => '0px', 'bottom' => '24px']],
|
||
'mobile' => ['fontSize' => '40px'],
|
||
]),
|
||
$text('fehler-text', 'Vielleicht wurde sie verschoben. Von der Startseite aus finden Sie alles Wichtige – oder Sie fragen direkt ein Verwaltungsangebot an.', $fliess('@text', '36px', '520px')),
|
||
$zeile('fehler-knoepfe', [
|
||
$knopf('fehler-knopf', 'Zur Startseite', '/'),
|
||
$knopf('fehler-anfrage', 'Verwaltungsanfrage', '/verwaltungsanfrage', 'rahmen', 'none'),
|
||
], '14px', ['flexWrap' => 'wrap']),
|
||
], $abschnitt('120px', '160px'), null, '', 'wide'),
|
||
],
|
||
]);
|
||
$db->executeStatement('UPDATE page SET robots = ? WHERE id = ?', ['noindex', $fehlerId]);
|
||
|
||
// ---------------------------------------------------------------------------------------------
|
||
// Menü, Einstellungen, Konto
|
||
// ---------------------------------------------------------------------------------------------
|
||
|
||
$seiteId = static fn (string $pfad): string => (string) $db->fetchOne('SELECT id FROM page WHERE path = ?', [$pfad]);
|
||
$anker = static fn (string $label, string $anker): array => ['label' => $label, 'type' => 'page', 'pageId' => $homeId, 'anchor' => $anker, 'children' => []];
|
||
$menus->save('header', 'Hauptnavigation', [
|
||
$anker('Leistungen', 'leistungen'),
|
||
$anker('Über uns', 'ueber-uns'),
|
||
['label' => 'Bewertung', 'type' => 'page', 'pageId' => $seiteId('immobilienbewertung'), 'children' => []],
|
||
['label' => 'Anfrage', 'type' => 'page', 'pageId' => $seiteId('verwaltungsanfrage'), 'children' => []],
|
||
], 0);
|
||
echo "Menü angelegt\n";
|
||
|
||
$settings->assignNotFoundPage($fehlerId, '');
|
||
$settings->save([
|
||
'legalName' => $firma,
|
||
'description' => 'Luximmo – Ihr neuer Immobilienpartner vor Ort: Immobilienverkauf und WEG-Verwaltung in Gaggenau und Umgebung, Baden-Baden und im Schwarzwald.',
|
||
'email' => $mail, 'phone' => '+49 7225 5080756',
|
||
'street' => $strasse, 'postalCode' => $plz, 'city' => $ort, 'country' => 'DE',
|
||
'areaServed' => 'Gaggenau, Baden-Baden, Schwarzwald',
|
||
'localBusiness' => true, 'llmsTxt' => true,
|
||
// Koordinaten aus OpenStreetMap, Gebäude Hauptstraße 169 (09.11.2026).
|
||
'businessType' => 'RealEstateAgent', 'latitude' => '48.79381', 'longitude' => '8.33237',
|
||
'organisationLogoMediaId' => $medien['logo']['id'],
|
||
'faviconMediaId' => $medien['favicon']['id'],
|
||
'stickyHeader' => false, 'headerOverlay' => false,
|
||
'motion' => 'always',
|
||
'homePath' => 'home', 'imprintPath' => 'impressum', 'privacyPath' => 'datenschutz',
|
||
// Die Seite lädt nichts von Dritten; deshalb keine Einwilligungsleiste.
|
||
'consentBanner' => false, 'scrollTop' => true, 'postMetaHeader' => false,
|
||
]);
|
||
echo "Einstellungen gespeichert\n";
|
||
|
||
$passwort = bin2hex(random_bytes(12));
|
||
$db->insert('cms_user', ['id' => bin2hex(random_bytes(16)), 'username' => 'umut', 'password_hash' => password_hash($passwort, PASSWORD_BCRYPT), 'roles' => '["ROLE_ADMIN"]']);
|
||
file_put_contents($root.'/var/luximmo-zugang.txt', "Verwaltung: Benutzer „umut“, Passwort: $passwort\n");
|
||
|
||
echo "\nFertig.\nZugang zur Verwaltung steht in var/luximmo-zugang.txt\n";
|