Files
KantSites/scripts/seed-kantarci-immobilien.php
kantarciandClaude Opus 5 45d1141951 Show the own site icon to Google under every address, and the KANTARCI landing page
- Google kept showing the KantSites K for Elektro Bernhard after the site
  icon was changed. The own icon only existed at 32 and 180 px, and Google
  takes multiples of 48 only; /favicon.svg was a file in public/, so the web
  server always delivered the bundled K.
- The icon is now also cut at 48 and 192 px. The head links /favicon-192.png,
  /favicon.ico serves the 48 px PNG, and /favicon.svg is answered by the
  application: the bundled mark without an own icon (now in
  public/assets/favicon.svg), otherwise a 302 to the 192 px PNG. Sites that
  set their icon earlier get the new sizes on first request. A removed icon
  no longer counts even though its files remain.
- The archive update only adds files to public/, so it now deletes the old
  public/favicon.svg explicitly.
- Seventh local reference site: the KANTARCI Immobilien landing page with a
  three-step rental request, own database, seed and reset scripts.

Tests: 582 PHPUnit on PHP 8.2 and 8.4, 35 node, test:editor and
test:browser green (test:browser reports only the known editor colour
contrast on /grundlagen).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 07:37:04 +02:00

734 lines
50 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Baut die Landingpage „KANTARCI Immobilien – Mietanfrage“ als Inhalt dieses CMS auf.
*
* Vorlage ist das gelieferte Paket „KANTARCI-Immobilien-Landingpage.zip“ (18.09.2026): eine
* Next.js-Seite (app/page.tsx, app/globals.css) mit Kopf, dunklem Hero, Werteleiste, Mietanfrage
* und Fuß. Wortlaut, Farben (Navy #102333, Gold #b49a66), Georgia für Überschriften und Arial
* für den Text sind daraus übernommen. Gebaut ist alles aus gewöhnlichen Bausteinen – kein
* eigener Baustein, kein Plugin, keine externe Schrift.
*
* Bewusste Abweichungen:
* - Die Mietanfrage ist das serverseitige Formular „mietanfrage“ dieses CMS statt der
* Cloudflare-D1-Anbindung: Eingänge landen in der Verwaltung und per E-Mail. Auf Umuts Wunsch
* in drei Schritten mit Bildkarten (Haushalt, Zimmer, Ausstattung) und Zusammenfassung statt
* einer langen Seite. Der Einzug ist eine Auswahl statt eines Datumsfelds (der Baukasten
* kennt kein Datum).
* - E-Mail überall info@kantarci-immo.de: die Domain der Vorlage gibt es nicht.
* - Neu sind Impressum und Datenschutz, beide Pflicht: Die Seite ist ein geschäftlicher
* Auftritt (§ 5 DDG) und das Formular verarbeitet personenbezogene Daten (Art. 13 DSGVO).
* Anschrift, Geschäftsführer und E-Mail hat Umut am 18.09.2026 genannt; was offen ist
* (Register, Telefon, § 34c), steht als „NOCH ZU PRÜFEN“ im Text.
* - Logo und Hausmotiv sind Zuschnitte aus dem Banner (scripts/build-kantarci-assets.php).
* - Die Symbole der Werteleiste kommen aus dem Symbolsatz des Kerns (kein Blatt, kein Herz).
*
* 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-kantarci-immobilien.sh).
* Erstes Argument: der Ordner mit logo-kantarci.png, haus.jpg und symbol-kantarci.png.
*/
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\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, 'kantarci-immobilien')) {
fwrite(STDERR, "Sicherung: DATABASE_URL muss auf die KANTARCI-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);
$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);
$firma = 'KANTARCI Immobilien GmbH';
// Die Vorlage nennt immo@kantarci-immobilien.de; die Domain gibt es nicht (18.09.2026, kein
// DNS-Eintrag). Umut hat info@kantarci-immo.de für Impressum und Datenschutz genannt, die Domain
// hat Mailserver - deshalb steht sie überall, auch als Empfänger der Mietanfrage.
$mail = 'info@kantarci-immo.de';
$gf = 'Ridvan Kantarci';
$strasse = 'Ooser Hauptstraße 51';
$ort = '76532 Baden-Baden';
$SCHEMA = 24;
// ---------------------------------------------------------------------------------------------
// 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'];
$nurUnten = static fn (string $w): array => ['top' => '0px', 'right' => '0px', 'bottom' => $w, 'left' => '0px'];
$section = static function (string $id, array $children, array $style = [], ?string $anchor = null): array {
$block = ['id' => $id, 'type' => 'section', 'children' => $children, 'style' => $style];
if (null !== $anchor) {
$block['anchor'] = $anchor;
}
return $block;
};
$row = static function (string $id, array $children, array $design = [], array $style = []): array {
$block = ['id' => $id, 'type' => 'row', 'children' => $children];
if ([] !== $design) {
$block['layout'] = ['design' => $design];
}
if ([] !== $style) {
$block['style'] = $style;
}
return $block;
};
/** Eine Zeile, die nebeneinander stellt statt zu rastern – Kleinzeile, Werteleiste, Kopf, Fuß. */
$zeile = static function (string $id, array $children, string $gap, array $extra = [], array $mobil = [], array $tablet = []) use ($row): array {
$style = ['desktop' => ['display' => 'flex', 'alignItems' => 'center', 'gap' => $gap, 'margin' => ['top' => '0px'], ...$extra]];
if ([] !== $tablet) {
$style['tablet'] = $tablet;
}
if ([] !== $mobil) {
$style['mobile'] = $mobil;
}
return $row($id, $children, [], $style);
};
$column = static function (string $id, array $children, array $style = [], string $sichtbarkeit = 'all', string $css = ''): array {
$block = ['id' => $id, 'type' => 'column', 'children' => $children];
if ([] !== $style) {
$block['style'] = $style;
}
if ('all' !== $sichtbarkeit) {
$block['layout'] = ['visibility' => $sichtbarkeit];
}
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;
};
/** Eine reine Fläche: der kurze Goldstrich vor der Kleinzeile, der Verlauf über dem Hausbild. */
$flaeche = static function (string $id, array $style, string $css = ''): array {
$block = ['id' => $id, 'type' => 'spacer', 'props' => ['size' => 'small'], 'style' => $style];
if ('' !== $css) {
$block['css'] = $css;
}
return $block;
};
/** Ein Button ohne den Pfeil des Bausteins: die Vorlage kommt ohne Pfeile aus. */
$button = static function (string $id, string $text, string $href, array $style = []): array {
return ['id' => $id, 'type' => 'button', 'props' => ['text' => $text, 'href' => $href], 'style' => $style, 'css' => '& span{display:none}'];
};
$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 function (string $id, string $name, int $groesse, array $style = []): array {
return ['id' => $id, 'type' => 'icon', 'props' => ['icon' => $name, 'size' => $groesse, 'decorative' => true, 'label' => ''], 'style' => $style];
};
/**
* Setzt die Einblendungen nach einer Liste, statt sie über das ganze Dokument zu streuen.
*
* @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;
};
// Wiederkehrende Gestaltungswerte der Vorlage.
$kleinStil = ['desktop' => ['fontSize' => '12px', 'fontWeight' => '700', 'letterSpacing' => '0.17em', 'lineHeight' => 1.6, 'margin' => ['bottom' => '20px']], 'mobile' => ['fontSize' => '11px']];
$linkCss = "& a{color:#102333;text-decoration:underline;text-underline-offset:3px}\n& a:hover{color:#88703f}";
// Die Mietanfrage in drei Schritten: volle Breite statt der 640 px des Kerns, zwei Spalten dort,
// wo zwei kurze Felder nebeneinander passen. Eigenes CSS kennt kein @media: die Raster füllen
// deshalb mit auto-fill, am Telefon bleibt so eine Spalte. Felder über `data-label`, weil es kein
// :nth-child kennt; Deckkraft der Nebentexte auf 1, sonst fallen sie unter 4,5:1.
$formularCss = '& .block-form{max-width:none}
& .form-step{grid-template-columns:repeat(auto-fill,minmax(190px,1fr));column-gap:16px;row-gap:18px}
& .form-step>.form-field{grid-column:1/-1}
& .form-step>.form-step-title{grid-column:1/-1}
& .form-step>.form-step-intro{grid-column:1/-1}
& .form-step>[data-label="Vorname"]{grid-column:span 1}
& .form-step>[data-label="Nachname"]{grid-column:span 1}
& .form-step>[data-label="Einzugstermin"]{grid-column:span 1}
& .form-step>[data-label="Max. Warmmiete pro Monat"]{grid-column:span 1}
& .form-step-title{font-family:Georgia,serif;font-size:22px;font-weight:400;color:#142535;margin-bottom:4px}
& .form-step-intro{font-size:14px;color:#5f6b74;opacity:1}
& .form-progress-step{opacity:1;color:#5f6b74}
& .form-progress-step[aria-current]{color:#142535}
& .form-field{align-content:start}
& .form-field label{font-size:14px;font-weight:600;color:#142535}
& .form-field input{background:#fcfcfc;border:1px solid #dce0e2;border-radius:3px;padding:12px 13px;font-size:16px;color:#142535}
& .form-field select{background:#fcfcfc;border:1px solid #dce0e2;border-radius:3px;padding:12px 13px;font-size:16px;color:#142535}
& .form-field textarea{background:#fcfcfc;border:1px solid #dce0e2;border-radius:3px;padding:12px 13px;font-size:16px;color:#142535;min-height:110px}
& .form-choice legend{font-size:14px;font-weight:600;color:#142535}
& .form-choice-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}
& [data-label="Ausstattung"] .form-choice-grid{grid-template-columns:repeat(auto-fill,minmax(125px,1fr));gap:10px}
& .form-step>.form-summary-body{grid-column:1/-1}
& .form-summary-title{font-family:Arial,sans-serif;font-size:15px;font-weight:700;letter-spacing:0}
& .form-summary-step{border-color:#dce0e2;border-radius:6px}
& .form-choice-card label{border-color:#dce0e2;border-radius:6px;font-size:14px}
& .field-note{font-size:13px;color:#5f6b74;opacity:1}
& .required-mark{color:#88703f}
& .check-row label{font-weight:400;font-size:14px;line-height:1.65;color:#576570}
& .cms-button{justify-content:center;background:#142535;color:#fff;border-radius:3px}
& .cms-button:hover{background:#234256;color:#fff}';
/** Die Kleinzeile in Versalien, wahlweise mit dem kurzen Goldstrich davor. */
$kleinzeile = static function (string $id, string $inhalt, string $farbe = '@gold-text', bool $strich = false) use ($text, $zeile, $flaeche, $kleinStil): array {
$zeilenText = $text($id.'-text', $inhalt, ['desktop' => [...$kleinStil['desktop'], 'color' => $farbe, 'margin' => ['bottom' => '0px']], 'mobile' => $kleinStil['mobile']]);
if (!$strich) {
return [...$zeilenText, 'id' => $id, 'style' => ['desktop' => [...$kleinStil['desktop'], 'color' => $farbe], 'mobile' => $kleinStil['mobile']]];
}
return $zeile($id, [
$flaeche($id.'-strich', ['desktop' => ['width' => '27px', 'height' => '1px', 'backgroundColor' => '@gold', 'flexShrink' => 0]], 'min-height:0'),
$zeilenText,
], '12px', ['margin' => ['top' => '0px', 'bottom' => '20px']]);
};
// ---------------------------------------------------------------------------------------------
// Website, Gestaltung, Medien, Formular
// ---------------------------------------------------------------------------------------------
$db->insert('website', ['id' => 'default', 'name' => 'KANTARCI Immobilien', 'default_locale' => 'de', 'theme' => '{}']);
$theme->save(
[
['key' => 'navy', 'name' => 'Navy', 'value' => '#102333'],
['key' => 'tinte', 'name' => 'Schrift', 'value' => '#142535'],
['key' => 'gold', 'name' => 'Gold (Linien, Flächen)', 'value' => '#b49a66'],
['key' => 'gold-knopf', 'name' => 'Gold als Knopf', 'value' => '#bda16a'],
['key' => 'gold-hell', 'name' => 'Gold auf Navy', 'value' => '#cbb17b'],
['key' => 'gold-text', 'name' => 'Gold als Schrift auf Weiß', 'value' => '#88703f'],
['key' => 'text', 'name' => 'Fließtext', 'value' => '#5f6b74'],
['key' => 'text-navy', 'name' => 'Fließtext auf Navy', 'value' => '#d1d8de'],
['key' => 'hell', 'name' => 'Helle Fläche', 'value' => '#f1f3f3'],
['key' => 'linie', 'name' => 'Trennlinie', 'value' => '#e1e4e5'],
['key' => 'papier', 'name' => 'Papier', 'value' => '#ffffff'],
],
[
// Die Vorlage lädt keine Webschrift: Arial für Text, Georgia für Überschriften.
['key' => 'body', 'name' => 'Fließtext', 'family' => 'Arial', 'source' => 'system', 'weights' => [400, 600, 700], 'fallback' => 'sans-serif'],
['key' => 'heading', 'name' => 'Überschriften', 'family' => 'Georgia', 'source' => 'system', 'weights' => [400], 'fallback' => 'serif'],
],
['fontFamily' => 'body', 'headingFontFamily' => 'heading', 'fontSize' => '16px', 'lineHeight' => 1.6, 'color' => '@tinte', 'backgroundColor' => '@papier', 'linkColor' => '@tinte', 'containerWidth' => '1140px', 'headingScale' => 1.25, 'linkUnderline' => false, 'imageRadius' => '0px'],
['shape' => 'square', 'appearance' => 'solid', 'size' => 'normal', 'fontFamily' => 'inherit', 'fontSize' => 15, 'fontWeight' => 700, 'letterSpacing' => 0, 'textTransform' => 'none', 'backgroundColor' => '@gold-knopf', 'color' => '@navy', 'hoverBackgroundColor' => '#d0b582', 'hoverColor' => '@navy'],
);
echo "Farben, Schriften und Buttons gesetzt\n";
$medien = [];
foreach ([
'logo' => ['logo-kantarci.png', 'KANTARCI Immobilien GmbH'],
'haus' => ['haus.jpg', 'Modernes Wohnhaus mit begrünten Balkonen'],
'symbol' => ['symbol-kantarci.png', 'Bildmarke von KANTARCI Immobilien'],
// Die Bildkarten der Mietanfrage (scripts/build-kantarci-options.mjs).
'haushalt-allein' => ['options/haushalt-allein.png', 'Symbol: eine Person'],
'haushalt-zu-zweit' => ['options/haushalt-zu-zweit.png', 'Symbol: zwei Personen'],
'haushalt-familie' => ['options/haushalt-familie.png', 'Symbol: zwei Erwachsene und ein Kind'],
'haushalt-wg' => ['options/haushalt-wg.png', 'Symbol: drei Personen'],
'zimmer-1' => ['options/zimmer-1.png', 'Grundriss mit einem Zimmer'],
'zimmer-2' => ['options/zimmer-2.png', 'Grundriss mit zwei Zimmern'],
'zimmer-3' => ['options/zimmer-3.png', 'Grundriss mit drei Zimmern'],
'zimmer-4' => ['options/zimmer-4.png', 'Grundriss mit vier Zimmern'],
'ausstattung-balkon' => ['options/ausstattung-balkon.png', 'Symbol: Balkon'],
'ausstattung-aufzug' => ['options/ausstattung-aufzug.png', 'Symbol: Aufzug'],
'ausstattung-stellplatz' => ['options/ausstattung-stellplatz.png', 'Symbol: Parkplatz'],
'ausstattung-kueche' => ['options/ausstattung-kueche.png', 'Symbol: Küche'],
'ausstattung-barrierefrei' => ['options/ausstattung-barrierefrei.png', 'Symbol: Rollstuhl'],
'ausstattung-haustier' => ['options/ausstattung-haustier.png', 'Symbol: Pfote'],
] 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";
}
// Drei Schritte: wer einzieht, wie gewohnt werden soll, wie wir Sie erreichen. Haushalt, Zimmer
// und Ausstattung sind Bildkarten; die Personenzahl fragt das Formular nur, wo sie offen ist.
$karten = static fn (array $zuordnung): array => array_map(static fn (string $schluessel): string => $medien[$schluessel]['id'], $zuordnung);
$forms->save('mietanfrage', 'Mietanfrage', [
['name' => 'schritt_haushalt', 'type' => 'step', 'label' => 'Ihr Haushalt', 'help' => 'Wer zieht in die neue Wohnung ein?'],
['name' => 'haushalt', 'type' => 'radio', 'label' => 'Wer zieht ein?', 'required' => true, 'options' => ['Allein', 'Zu zweit', 'Familie mit Kindern', 'Wohngemeinschaft'],
'images' => $karten(['Allein' => 'haushalt-allein', 'Zu zweit' => 'haushalt-zu-zweit', 'Familie mit Kindern' => 'haushalt-familie', 'Wohngemeinschaft' => 'haushalt-wg'])],
['name' => 'personen', 'type' => 'number', 'label' => 'Personen im Haushalt', 'required' => true, 'min' => 2, 'max' => 30,
'showIf' => ['field' => 'haushalt', 'values' => ['Familie mit Kindern', 'Wohngemeinschaft']]],
['name' => 'schritt_wohnen', 'type' => 'step', 'label' => 'Ihre Wohnwünsche', 'help' => 'Alles hier ist freiwillig – je mehr wir wissen, desto besser passt unser Vorschlag.'],
['name' => 'zimmer', 'type' => 'radio', 'label' => 'Wie viele Zimmer?', 'required' => false, 'options' => ['1 Zimmer', '2 Zimmer', '3 Zimmer', '4 und mehr'],
'images' => $karten(['1 Zimmer' => 'zimmer-1', '2 Zimmer' => 'zimmer-2', '3 Zimmer' => 'zimmer-3', '4 und mehr' => 'zimmer-4'])],
['name' => 'ausstattung', 'type' => 'checkboxes', 'label' => 'Ausstattung', 'required' => false, 'help' => 'Was ist Ihnen wichtig? Mehrfachauswahl möglich.',
'options' => ['Balkon oder Terrasse', 'Aufzug', 'Stellplatz', 'Einbauküche', 'Barrierefrei', 'Haustier erlaubt'],
'images' => $karten(['Balkon oder Terrasse' => 'ausstattung-balkon', 'Aufzug' => 'ausstattung-aufzug', 'Stellplatz' => 'ausstattung-stellplatz', 'Einbauküche' => 'ausstattung-kueche', 'Barrierefrei' => 'ausstattung-barrierefrei', 'Haustier erlaubt' => 'ausstattung-haustier'])],
['name' => 'einzug', 'type' => 'select', 'label' => 'Einzugstermin', 'required' => false, 'options' => ['Ab sofort', 'In 1 bis 3 Monaten', 'In 3 bis 6 Monaten', 'Später oder flexibel']],
['name' => 'warmmiete', 'type' => 'number', 'label' => 'Max. Warmmiete pro Monat', 'required' => false, 'min' => 1, 'max' => 100000, 'unit' => '€'],
['name' => 'objekt', 'type' => 'text', 'label' => 'Gewünschte Wohnung / Objektadresse', 'required' => false, 'help' => 'Noch kein bestimmtes Objekt? Lassen Sie das Feld einfach frei.'],
['name' => 'schritt_kontakt', 'type' => 'step', 'label' => 'Ihre Kontaktdaten', 'help' => 'Damit wir uns persönlich bei Ihnen melden können.'],
['name' => 'vorname', 'type' => 'text', 'label' => 'Vorname', 'required' => true],
['name' => 'nachname', 'type' => 'text', 'label' => 'Nachname', 'required' => true],
['name' => 'email', 'type' => 'email', 'label' => 'E-Mail-Adresse', 'required' => true],
['name' => 'telefon', 'type' => 'phone', 'label' => 'Telefonnummer', 'required' => false, 'help' => 'Für eine persönliche Rückmeldung'],
['name' => 'anschrift', 'type' => 'text', 'label' => 'Aktuelle Anschrift', 'required' => false, 'help' => 'Straße, Hausnummer, PLZ und Ort'],
['name' => 'nachricht', 'type' => 'textarea', 'label' => 'Ihre Nachricht', 'required' => false, 'help' => 'Was ist Ihnen beim Wohnen sonst noch wichtig?'],
['name' => 'datenschutz', 'type' => 'consent', 'label' => 'Ich bin einverstanden, dass KANTARCI meine Angaben zur Bearbeitung meiner Mietanfrage speichert und mich kontaktiert.'],
], ['recipient' => $mail, 'subject' => 'Neue Mietanfrage über die Landingpage', 'success' => 'Vielen Dank für Ihr Interesse. Ihre Mietanfrage ist angekommen – wir melden uns persönlich bei Ihnen.', 'replyToField' => 'email', 'buttonLabel' => 'Mietanfrage absenden', 'summary' => true], 0);
echo "Formular angelegt: mietanfrage\n";
// ---------------------------------------------------------------------------------------------
// Kopf- und Fußbereich
// ---------------------------------------------------------------------------------------------
$kopf = [
'schemaVersion' => $SCHEMA, 'title' => 'Kopfbereich', 'description' => 'Erscheint auf jeder Seite über dem Inhalt.',
'blocks' => [
$section('kopf', [
$zeile('kopf-zeile', [
$column('kopf-marke', [
$bild('kopf-logo', $medien['logo'], 'KANTARCI Immobilien GmbH – zur Startseite', [
'desktop' => ['maxWidth' => '300px', 'margin' => $seiten('0px')],
'mobile' => ['maxWidth' => '220px'],
], '/'),
], ['desktop' => ['flexShrink' => 1, 'minWidth' => '0']]),
$column('kopf-kontakt', [
$zeile('kopf-kontakt-zeile', [
$symbol('kopf-kontakt-symbol', 'mail', 20, ['desktop' => ['color' => '@navy', 'flexShrink' => 0]]),
$text('kopf-kontakt-link', '<a href="mailto:'.$mail.'">Kontakt aufnehmen</a>', ['desktop' => ['fontSize' => '14px', 'fontWeight' => '600', 'margin' => ['bottom' => '0px']]], "& a{color:#102333}\n& a:hover{color:#88703f}"),
], '10px'),
], ['desktop' => ['flexShrink' => 0]], 'hide-mobile'),
], '24px', ['justifyContent' => 'space-between']),
], [
'desktop' => ['backgroundColor' => '@papier', 'padding' => ['top' => '22px', 'bottom' => '22px']],
'mobile' => ['padding' => ['top' => '16px', 'bottom' => '16px']],
]),
],
];
$fuss = [
'schemaVersion' => $SCHEMA, 'title' => 'Fußbereich', 'description' => 'Erscheint auf jeder Seite unter dem Inhalt.',
'blocks' => [
$section('fuss', [
$zeile('fuss-oben', [
$column('fuss-marke', [
$text('fuss-name', 'KANTARCI', ['desktop' => ['fontFamily' => 'heading', 'fontSize' => '22px', 'letterSpacing' => '0.14em', 'lineHeight' => 1.3, 'color' => '@papier', 'margin' => ['bottom' => '0px']]]),
$text('fuss-zusatz', 'IMMOBILIEN GMBH', ['desktop' => ['fontSize' => '10px', 'letterSpacing' => '0.22em', 'color' => '@gold-hell', 'margin' => ['bottom' => '0px']]]),
]),
$column('fuss-spruch', [
$text('fuss-spruch-text', 'Lebensraum schaffen. Zukunft bauen.', ['desktop' => ['fontSize' => '13px', 'color' => '#bac4cd', 'margin' => ['bottom' => '0px']]]),
]),
$column('fuss-mail', [
$text('fuss-mail-text', '<a href="mailto:'.$mail.'">'.$mail.'</a>', ['desktop' => ['fontSize' => '13px', 'margin' => ['bottom' => '0px']]], "& a{color:#bac4cd;overflow-wrap:anywhere}\n& a:hover{color:#fff;text-decoration:underline}"),
]),
], '24px', ['justifyContent' => 'space-between', 'flexWrap' => 'wrap', 'padding' => ['top' => '34px', 'bottom' => '26px'], 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#243a4c'],
['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '12px']),
$zeile('fuss-unten', [
$text('fuss-copyright', '© 2026 '.$firma, ['desktop' => ['fontSize' => '12px', 'color' => '#bac4cd', 'margin' => ['bottom' => '0px']]]),
$zeile('fuss-links', [
// Impressum und Datenschutz kommen aus den Einstellungen, nicht aus einem Menü.
['id' => 'fuss-rechtliches', 'type' => 'legal', 'props' => ['label' => 'Rechtliches'],
'style' => ['desktop' => ['fontSize' => '12px', 'gap' => '4px', 'color' => '#bac4cd']],
'css' => "& a{color:#bac4cd}\n& a:hover{color:#fff}"],
$text('fuss-agentur', '<a href="https://kant-it.de">Website by Kant-IT Solutions</a>', ['desktop' => ['fontSize' => '12px', 'margin' => ['bottom' => '0px']]], "& a{color:#bac4cd}\n& a:hover{color:#fff}"),
], '22px', ['flexWrap' => 'wrap'], ['rowGap' => '10px', 'columnGap' => '18px']),
], '24px', ['justifyContent' => 'space-between', 'flexWrap' => 'wrap', 'padding' => ['top' => '20px', 'bottom' => '26px']],
['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '12px']),
], [
'desktop' => ['backgroundColor' => '@navy', 'color' => '#bac4cd', 'padding' => ['top' => '0px', 'bottom' => '0px']],
]),
],
];
$partials->create('header', 'default', 'Kopfbereich', $kopf);
$partials->create('footer', 'default', 'Fußbereich', $fuss);
echo "Kopf- und Fußbereich angelegt\n";
/** Eine Rechtsseite: Kleinzeile, Titel und Vorspann auf Navy, dann Absätze mit Zwischenüberschriften. */
$rechtsseite = static function (string $praefix, string $label, string $titel, string $vorspann, array $absaetze) use ($section, $row, $column, $kleinzeile, $heading, $text, $nurOben, $linkCss): array {
$kopf = $section($praefix.'-kopf', [
$kleinzeile($praefix.'-label', $label, '@gold-hell', true),
$heading($praefix.'-h1', $titel, 1, [
'desktop' => ['fontSize' => '52px', 'lineHeight' => 1.1, 'letterSpacing' => '-0.025em', 'color' => '@papier', 'margin' => ['bottom' => '20px']],
'mobile' => ['fontSize' => '38px'],
]),
$text($praefix.'-vorspann', $vorspann, [
'desktop' => ['maxWidth' => '640px', 'fontSize' => '17px', 'lineHeight' => 1.75, 'color' => '@text-navy', 'margin' => ['bottom' => '0px']],
'mobile' => ['fontSize' => '16px'],
]),
], [
'desktop' => ['backgroundColor' => '@navy', 'color' => '@papier', 'padding' => ['top' => '72px', 'bottom' => '64px']],
'mobile' => ['padding' => ['top' => '44px', 'bottom' => '40px']],
]);
$punkte = [];
foreach ($absaetze as $i => [$zwischentitel, $inhalt]) {
$punkte[] = $row($praefix.'-punkt-'.$i, [
$column($praefix.'-punkt-'.$i.'-titel', '' === $zwischentitel ? [] : [
$heading($praefix.'-h2-'.$i, $zwischentitel, 2, [
'desktop' => ['fontFamily' => 'body', 'fontSize' => '16px', 'fontWeight' => '700', 'lineHeight' => 1.5, 'letterSpacing' => '0', 'color' => '@tinte', 'margin' => ['bottom' => '0px']],
]),
], [
'desktop' => ['flexBasis' => '260px', 'flexShrink' => 0],
'tablet' => ['flexBasis' => '200px'],
'mobile' => ['flexBasis' => 'auto'],
]),
$column($praefix.'-punkt-'.$i.'-text', [
$text($praefix.'-p-'.$i, $inhalt, ['desktop' => ['fontSize' => '16px', 'lineHeight' => 1.75, 'color' => '@text', 'margin' => ['bottom' => '0px']]], $linkCss),
], ['desktop' => ['flexGrow' => 1, 'flexBasis' => '0', 'minWidth' => '0'], 'mobile' => ['flexBasis' => 'auto']]),
], [], [
'desktop' => ['display' => 'flex', 'alignItems' => 'flex-start', 'gap' => '44px', 'margin' => ['top' => '0px'],
'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '26px', 'bottom' => '26px']],
'tablet' => ['gap' => '28px'],
'mobile' => ['flexDirection' => 'column', 'gap' => '8px', 'padding' => ['top' => '20px', 'bottom' => '20px']],
]);
}
$inhalt = $section($praefix, [
$column($praefix.'-inhalt', $punkte, ['desktop' => ['maxWidth' => '920px']]),
], ['desktop' => ['padding' => ['top' => '48px', 'bottom' => '90px']], 'mobile' => ['padding' => ['top' => '24px', 'bottom' => '56px']]]);
return [$kopf, $inhalt];
};
// ---------------------------------------------------------------------------------------------
// Startseite
// ---------------------------------------------------------------------------------------------
$wertStil = ['desktop' => ['fontSize' => '14px', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '12px', 'textAlign' => 'center', 'lineHeight' => 1.5]];
$wert = static fn (string $id, string $icon, string $label): array => $zeile('wert-'.$id, [
$symbol('wert-'.$id.'-symbol', $icon, 24, ['desktop' => ['color' => '@gold-text', 'flexShrink' => 0]]),
$text('wert-'.$id.'-text', $label, $wertStil),
], '13px', [], ['flexDirection' => 'column', 'gap' => '9px', 'flexGrow' => 1, 'flexBasis' => '0']);
$schritt = static fn (string $n, string $titel, string $inhalt): array => $zeile('schritt-'.$n, [
$text('schritt-'.$n.'-nummer', $n, ['desktop' => ['fontFamily' => 'heading', 'fontSize' => '22px', 'lineHeight' => 1.2, 'color' => '@gold-text', 'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '#c9b58e', 'padding' => ['top' => '8px'], 'margin' => ['bottom' => '0px'], 'flexShrink' => 0]]),
$text('schritt-'.$n.'-text', '<strong>'.$titel.'</strong>'.$inhalt, ['desktop' => ['fontSize' => '14px', 'lineHeight' => 1.65, 'color' => '@text', 'maxWidth' => '290px', 'margin' => ['top' => '5px', 'bottom' => '0px']]],
'& strong{display:block;color:#102333;font-size:15px;margin-bottom:4px}'),
], '17px', ['alignItems' => 'flex-start', 'margin' => ['top' => '22px']]);
$startseite = [
'schemaVersion' => $SCHEMA,
'title' => 'Mietanfrage',
'description' => 'Ihr neues Zuhause beginnt hier. Melden Sie Ihr Interesse an einer Mietwohnung bei KANTARCI Immobilien an – persönlich und unverbindlich.',
'blocks' => [
// 1 – Hero: Navy, Text links, das Haus rechts mit Verlauf und Bildzeile.
$section('hero', [
$row('hero-zeile', [
$column('hero-text', [
$kleinzeile('hero-label', 'KANTARCI IMMOBILIEN', '#d1bb90', true),
$heading('hero-titel', 'Hier beginnt Ihr<br><em>neues Zuhause.</em>', 1, [
'desktop' => ['fontSize' => '62px', 'lineHeight' => 1.09, 'letterSpacing' => '-0.025em', 'color' => '@papier', 'margin' => ['bottom' => '25px']],
'tablet' => ['fontSize' => '45px'],
'mobile' => ['fontSize' => '42px'],
], '& em{font-style:normal;color:#cbb17b}'),
$text('hero-intro', 'Ein Ort zum Ankommen. Raum für Ihr Leben. Entdecken Sie neue Mietwohnungen und teilen Sie uns mit, wie Sie wohnen möchten.', [
'desktop' => ['maxWidth' => '450px', 'fontSize' => '16px', 'lineHeight' => 1.85, 'color' => '@text-navy', 'margin' => ['bottom' => '30px']],
'mobile' => ['fontSize' => '15px'],
]),
$button('hero-knopf', 'Jetzt Mietinteresse anmelden', '#mietanfrage', ['desktop' => ['margin' => ['top' => '0px']]]),
$text('hero-notiz', 'Unverbindlich. Persönlich. In wenigen Minuten.', ['desktop' => ['fontSize' => '12px', 'color' => '#b1bec7', 'margin' => ['top' => '14px', 'bottom' => '0px']]]),
], [
'desktop' => ['display' => 'flex', 'flexDirection' => 'column', 'alignItems' => 'flex-start', 'justifyContent' => 'center', 'padding' => ['top' => '64px', 'bottom' => '60px']],
'tablet' => ['padding' => ['top' => '44px', 'bottom' => '44px']],
'mobile' => ['padding' => ['top' => '40px', 'bottom' => '8px']],
]),
$column('hero-visual', [
$bild('hero-bild', $medien['haus'], 'Modernes Wohnhaus mit begrünten Balkonen', [
'desktop' => ['imageHeight' => '480px', 'objectFit' => 'cover', 'objectPosition' => 'top left', 'margin' => $seiten('0px')],
'tablet' => ['imageHeight' => '460px'],
'mobile' => ['imageHeight' => '280px'],
], null, '& img{display:block;width:100%}'),
$flaeche('hero-verlauf', ['desktop' => ['position' => 'absolute', 'left' => '0px', 'right' => '0px', 'bottom' => '0px', 'width' => 'auto', 'height' => 'auto', 'zIndex' => 1]], 'top:45%;min-height:0;background:linear-gradient(transparent,rgba(16,35,51,.88));pointer-events:none'),
$column('hero-bildzeile', [
$text('hero-bildzeile-klein', 'RAUM FÜR MORGEN', ['desktop' => ['fontSize' => '11px', 'letterSpacing' => '0.18em', 'color' => '#f2e4c6', 'margin' => ['bottom' => '4px']]]),
$text('hero-bildzeile-satz', 'Wohnen. Wohlfühlen. Ankommen.', ['desktop' => ['fontFamily' => 'heading', 'fontSize' => '25px', 'lineHeight' => 1.3, 'color' => '@papier', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '21px']]),
], ['desktop' => ['position' => 'absolute', 'left' => '32px', 'right' => '32px', 'bottom' => '28px', 'zIndex' => 2], 'mobile' => ['left' => '20px', 'bottom' => '18px']]),
], [
'desktop' => ['position' => 'relative', 'overflow' => 'hidden', 'backgroundColor' => '#d9dde0', 'minHeight' => '480px'],
'tablet' => ['minHeight' => '460px'],
'mobile' => ['minHeight' => '280px'],
]),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '56px', 'alignItems' => 'stretch', 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '28px'],
'mobile' => ['gap' => '24px'],
]),
], [
'desktop' => ['backgroundColor' => '@navy', 'color' => '@papier', 'padding' => ['top' => '0px', 'bottom' => '0px']],
'mobile' => ['padding' => ['top' => '0px', 'bottom' => '24px']],
], 'start'),
// 2 – Die Werteleiste aus dem Banner.
$section('werte', [
$zeile('werte-zeile', [
$wert('modern', 'star', 'Modern & nachhaltig'),
$wert('komfort', 'home', 'Zeitgemäßer Wohnkomfort'),
$wert('leben', 'people', 'Zuhause für jedes Leben'),
], '20px', ['justifyContent' => 'space-between', 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#e4e6e7', 'padding' => ['top' => '26px', 'bottom' => '26px']],
['alignItems' => 'flex-start', 'gap' => '14px', 'padding' => ['top' => '22px', 'bottom' => '22px']]),
], ['desktop' => ['padding' => ['top' => '0px', 'bottom' => '0px']]]),
// 3 – Mietanfrage: Einleitung links, Formularkarte rechts.
$section('anfrage', [
$row('anfrage-zeile', [
$column('anfrage-text', [
$kleinzeile('anfrage-label', 'IHRE MIETANFRAGE'),
$heading('anfrage-h2', 'Ein paar Angaben.<br>Viele Möglichkeiten.', 2, [
'desktop' => ['fontSize' => '38px', 'lineHeight' => 1.2, 'letterSpacing' => '-0.02em', 'margin' => ['bottom' => '22px']],
'tablet' => ['fontSize' => '33px'],
'mobile' => ['fontSize' => '34px'],
]),
$text('anfrage-intro', 'Sie interessieren sich für eine bestimmte Wohnung oder suchen noch das passende Zuhause? Erzählen Sie uns kurz von Ihren Wünschen.', [
'desktop' => ['maxWidth' => '350px', 'fontSize' => '16px', 'lineHeight' => 1.8, 'color' => '@text', 'margin' => ['bottom' => '12px']],
'mobile' => ['maxWidth' => '100%'],
]),
$schritt('01', 'Anfrage ausfüllen', 'Haushalt, Wohnwünsche und Kontaktdaten – in drei kurzen Schritten.'),
$schritt('02', 'Persönlich ins Gespräch kommen', 'Wir melden uns bei Ihnen, um die Möglichkeiten zu besprechen.'),
$zeile('anfrage-direkt', [
$symbol('anfrage-direkt-symbol', 'mail', 24, ['desktop' => ['color' => '@gold-text', 'flexShrink' => 0]]),
$text('anfrage-direkt-text', '<strong>Lieber direkt schreiben?</strong><a href="mailto:'.$mail.'">'.$mail.'</a>', ['desktop' => ['fontSize' => '14px', 'lineHeight' => 1.6, 'margin' => ['bottom' => '0px']]],
"& strong{display:block;color:#102333}\n& a{color:#5f6b74;overflow-wrap:anywhere}\n& a:hover{color:#88703f;text-decoration:underline}"),
], '15px', ['borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '26px'], 'margin' => ['top' => '38px']], ['margin' => ['top' => '26px'], 'padding' => ['top' => '20px']]),
], ['desktop' => ['padding' => ['top' => '14px']], 'mobile' => ['padding' => ['top' => '0px']]]),
$column('anfrage-karte', [
$zeile('anfrage-karte-kopf', [
$column('anfrage-karte-titel', [
$heading('anfrage-karte-h3', 'Ihr Weg zum neuen Zuhause', 3, ['desktop' => ['fontFamily' => 'body', 'fontSize' => '20px', 'fontWeight' => '600', 'letterSpacing' => '-0.015em', 'lineHeight' => 1.35, 'margin' => ['bottom' => '5px']]]),
$text('anfrage-karte-pflicht', 'In drei kurzen Schritten. Pflichtfelder sind mit * markiert.', ['desktop' => ['fontSize' => '13px', 'color' => '@text', 'margin' => ['bottom' => '0px']]]),
]),
$symbol('anfrage-karte-symbol', 'home', 32, ['desktop' => ['color' => '@gold-text', 'flexShrink' => 0]]),
], '16px', ['justifyContent' => 'space-between', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px', 'bottom' => '28px'], 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#edf0f0', 'padding' => ['bottom' => '22px']]),
['id' => 'anfrage-formular', 'type' => 'form', 'props' => ['form' => 'mietanfrage'], 'css' => $formularCss],
$text('anfrage-hinweis', 'Ihre Angaben werden nur für Ihre Mietanfrage verwendet – mehr dazu in den <a href="/datenschutz">Datenschutzhinweisen</a>. Bitte senden Sie hier keine Ausweiskopien oder anderen vertraulichen Unterlagen.', [
'desktop' => ['fontSize' => '13px', 'lineHeight' => 1.65, 'color' => '@text', 'margin' => ['top' => '18px', 'bottom' => '0px']],
], $linkCss),
$text('anfrage-unverbindlich', '✓ Unverbindliche Anfrage – kein Mietvertragsabschluss.', ['desktop' => ['fontSize' => '12px', 'color' => '@text', 'textAlign' => 'center', 'margin' => ['top' => '12px', 'bottom' => '0px']]]),
], [
'desktop' => ['backgroundColor' => '@papier', 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#e2e5e6', 'borderRadius' => ['topLeft' => '7px', 'topRight' => '7px', 'bottomRight' => '7px', 'bottomLeft' => '7px'], 'padding' => ['top' => '32px', 'right' => '34px', 'bottom' => '32px', 'left' => '34px'], 'shadowY' => '12px', 'shadowBlur' => '40px', 'shadowColor' => '#14253510'],
'tablet' => ['padding' => ['top' => '26px', 'right' => '24px', 'bottom' => '26px', 'left' => '24px']],
'mobile' => ['padding' => ['top' => '25px', 'right' => '20px', 'bottom' => '25px', 'left' => '20px']],
]),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '65px', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '32px'],
'mobile' => ['gap' => '26px'],
]),
], [
'desktop' => ['padding' => ['top' => '74px', 'bottom' => '84px']],
'tablet' => ['padding' => ['top' => '55px', 'bottom' => '60px']],
'mobile' => ['padding' => ['top' => '44px', 'bottom' => '48px']],
], 'mietanfrage'),
],
];
// ---------------------------------------------------------------------------------------------
// Seiten anlegen und veröffentlichen
// ---------------------------------------------------------------------------------------------
$erstellt = [];
$anlegen = static function (string $slug, array $dokument, string $seoTitel = '', string $teilbild = '') use ($pages, $db, &$erstellt): 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']);
$erstellt[$slug] = $id;
echo 'Seite angelegt: /'.$db->fetchOne('SELECT path FROM page WHERE id = ?', [$id])."\n";
return $id;
};
// Der Text des Hero spielt beim Laden, das Hausbild bleibt ohne Einblendung (LCP).
$startseite = $einblenden($startseite, [
'hero-label' => ['fade'],
'hero-titel' => ['rise', 'short'],
'hero-intro' => ['rise', 'medium'],
'hero-knopf' => ['rise', 'long'],
'hero-notiz' => ['fade', 'long'],
'anfrage-text' => ['rise'],
'anfrage-karte' => ['rise', 'short'],
]);
$anlegen('home', $startseite, 'Mietanfrage | KANTARCI Immobilien GmbH', $medien['haus']['id']);
$pruefen = static fn (string $was): string => '<strong>NOCH ZU PRÜFEN:</strong> '.$was;
$anlegen('impressum', [
'schemaVersion' => $SCHEMA, 'title' => 'Impressum',
'description' => 'Anbieterangaben der KANTARCI Immobilien GmbH.',
'blocks' => [
...$rechtsseite('impressum', 'RECHTLICHE ANGABEN', 'Impressum', 'Angaben gemäß § 5 DDG und § 18 Abs. 2 MStV.', [
['Anbieterin', $firma.'<br>'.$strasse.'<br>'.$ort],
['Vertreten durch', $gf.', Geschäftsführer'],
['Registereintrag', $pruefen('Handelsregisternummer (HRB …) und Registergericht (Amtsgericht …).')],
['Kontakt', "E-Mail: <a href=\"mailto:$mail\">$mail</a><br>".$pruefen('Telefonnummer ergänzen – das Gesetz verlangt neben der E-Mail einen zweiten schnellen Kontaktweg.')],
['Umsatzsteuer', $pruefen('Umsatzsteuer-Identifikationsnummer nach § 27a UStG, falls vorhanden.')],
['Aufsichtsbehörde', $pruefen('Wer Wohnungen für Dritte vermittelt oder verwaltet, braucht eine Erlaubnis nach § 34c GewO; dann die zuständige Erlaubnisbehörde hier eintragen. Vermietet die GmbH nur eigene Wohnungen, entfällt die Angabe.')],
['Verantwortlich für den Inhalt', $gf.', Anschrift wie oben.'],
['Verbraucherstreitbeilegung', 'Wir sind nicht bereit und nicht verpflichtet, an einem Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.'],
['Haftung für Inhalte', 'Als Diensteanbieter sind wir für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Wir sind jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Bei Bekanntwerden von Rechtsverletzungen entfernen wir diese Inhalte umgehend.'],
['Urheberrecht', 'Die von uns erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechts bedürfen der schriftlichen Zustimmung.'],
['Bildnachweis', 'Logo und Hausmotiv: '.$firma.'. '.$pruefen('Urheber und Lizenz des Hausfotos eintragen, falls es nicht von KANTARCI selbst stammt.')],
]),
],
], 'Impressum | '.$firma);
$anlegen('datenschutz', [
'schemaVersion' => $SCHEMA, 'title' => 'Datenschutz',
'description' => 'Wie die KANTARCI Immobilien GmbH Daten beim Besuch der Website und bei einer Mietanfrage verarbeitet.',
'blocks' => [
...$rechtsseite('datenschutz', 'IHRE DATEN', 'Datenschutzhinweise', 'Hier erfahren Sie, welche Daten beim Besuch dieser Website und bei Ihrer Mietanfrage verarbeitet werden – und welche Rechte Sie haben.', [
['1. Verantwortliche', "$firma, $strasse, $ort, vertreten durch den Geschäftsführer $gf. E-Mail <a href=\"mailto:$mail\">$mail</a>."],
['2. Besuch dieser Website', 'Beim Aufruf verarbeitet der Hostingdienst 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.<br><br>'.$pruefen('Hostinganbieter benennen, Auftragsverarbeitungsvertrag abschließen und die Speicherdauer der Serverprotokolle eintragen.')],
['3. Cookies und externe Dienste', 'Diese Website setzt keine Analyse- oder Werbecookies. Bilder, Schriften und Gestaltung kommen vom eigenen Server; es werden keine Karten, Videos oder Schriften von Dritten nachgeladen. Deshalb gibt es auch keinen Einwilligungsdialog.'],
['4. Mietanfrage', 'Wenn Sie das Formular absenden, verarbeiten wir Ihre Kontaktdaten (Name, E-Mail, auf Wunsch Telefon und Anschrift) und Ihre Wohnwünsche (etwa Haushaltsgröße, Zimmerzahl, Einzug, Mietobergrenze und Ihre Nachricht), um Ihre Anfrage zu bearbeiten und Ihnen passende Wohnungen anzubieten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Anbahnung eines Mietvertrags). Pflichtangaben sind nur Name, E-Mail und Haushaltsgröße; ohne sie können wir die Anfrage nicht bearbeiten. Das Formular schützt sich gegen automatisierte Eingaben, ohne dafür Dienste Dritter zu laden.'],
['5. Wie lange wir speichern', 'Anfragedaten löschen wir, sobald sie für die Bearbeitung nicht mehr nötig sind – spätestens sechs Monate nach der letzten Kontaktaufnahme, wenn kein Mietverhältnis entsteht. Entsteht ein Mietverhältnis, gelten für die dann anfallenden Unterlagen die gesetzlichen Aufbewahrungsfristen.<br><br>'.$pruefen('Frist mit der Praxis abgleichen.')],
['6. Empfänger', 'Ihre Angaben erreichen uns per E-Mail und liegen in der Verwaltung dieser Website. Darüber hinaus geben wir sie nicht weiter, außer wir sind dazu gesetzlich verpflichtet.<br><br>'.$pruefen('E-Mail-Anbieter und Hostingdienst als Auftragsverarbeiter benennen.')],
['7. 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. Eine erteilte Einwilligung können Sie jederzeit mit Wirkung für die Zukunft widerrufen.'],
['8. 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.'],
['', "Fragen zum Datenschutz beantworten wir gern unter <a href=\"mailto:$mail\">$mail</a>."],
]),
],
], 'Datenschutz | '.$firma);
$fehlerId = $anlegen('404', [
'schemaVersion' => $SCHEMA, 'title' => 'Seite nicht gefunden',
'description' => 'Die gesuchte Seite gibt es nicht oder sie wurde verschoben.',
'blocks' => [
$section('fehler', [
$kleinzeile('fehler-label', 'FEHLER 404', '@gold-hell', true),
$heading('fehler-titel', 'Diese Seite<br><em>gibt es nicht.</em>', 1, [
'desktop' => ['fontSize' => '52px', 'lineHeight' => 1.1, 'letterSpacing' => '-0.025em', 'color' => '@papier', 'margin' => ['bottom' => '22px']],
'mobile' => ['fontSize' => '38px'],
], '& em{font-style:normal;color:#cbb17b}'),
$text('fehler-text', 'Vielleicht hat sich ein Tippfehler in die Adresse geschlichen. Über die Startseite finden Sie zurück.', [
'desktop' => ['maxWidth' => '460px', 'fontSize' => '17px', 'lineHeight' => 1.8, 'color' => '@text-navy', 'margin' => ['bottom' => '28px']],
]),
$button('fehler-knopf', 'Zur Startseite', '/', ['desktop' => ['margin' => ['top' => '0px']]]),
], [
'desktop' => ['backgroundColor' => '@navy', 'color' => '@papier', 'padding' => ['top' => '110px', 'bottom' => '130px']],
'mobile' => ['padding' => ['top' => '64px', 'bottom' => '80px']],
]),
],
]);
$db->executeStatement('UPDATE page SET robots = ? WHERE id = ?', ['noindex', $fehlerId]);
// ---------------------------------------------------------------------------------------------
// Einstellungen, Konto
// ---------------------------------------------------------------------------------------------
$settings->assignNotFoundPage($fehlerId, '');
$settings->save([
'legalName' => $firma,
'description' => 'KANTARCI Immobilien GmbH: neue Mietwohnungen – modern, nachhaltig, mit zeitgemäßem Wohnkomfort. Mietinteresse unverbindlich anmelden.',
'email' => $mail,
'street' => $strasse, 'postalCode' => '76532', 'city' => 'Baden-Baden', 'country' => 'DE',
'localBusiness' => true, 'llmsTxt' => true,
'faviconMediaId' => $medien['symbol']['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' => false, '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"]']);
echo "\nFertig.\nVerwaltung: Benutzer „umut“, Passwort: $passwort\n";