Files
KantSites/scripts/seed-tagespflege-moldovan.php
T
kantarciandClaude Opus 5 860bed9199
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Drop the draft notice and give the Moldovan legal pages proper margins
The legal pages put their width limit on the section itself, which shrank the
section and its container along with it: the text sat 32 px from the window
edge while the header's brand stands at 100 px. The section now stays full
width and only the content column is limited, so title and text share the
header's edge on every device.

Both pages open with a pale band - label, title, lead - in the colour of the
offers section, followed by one row per point: heading on the left, text on
the right, separated by fine rules, stacked on a phone.

The footer no longer carries "Gestaltungsentwurf · Beispieltexte &
illustratives KI-Bild".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 06:40:29 +02:00

910 lines
63 KiB
PHP
Raw 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 Website „Tagespflege Jacqueline Moldovan“ als Inhalt dieses CMS auf.
*
* Vorlage ist das gelieferte Paket „Tagespflege_Moldovan_Website.zip“ (09.09.2026): ein Onepager
* aus index.html, style.css und script.js. Wortlaut, Farben, Schriftgrößen, Abstände und die
* Reihenfolge der Abschnitte sind daraus übernommen. Gebaut ist alles aus gewöhnlichen
* Bausteinen – kein eigener Baustein, kein eigenes Skript, keine externe Schrift.
*
* Zwei Stellen weichen bewusst ab:
* - Das Kontaktformular verschickt serverseitig, statt im Mailprogramm des Besuchers eine
* vorbereitete E-Mail zu öffnen. Der Hinweistext darunter sagt das entsprechend.
* - Das Markenzeichen liegt in der Vorlage als SVG vor; die Medienverwaltung nimmt JPEG, PNG und
* WebP. Verwendet wird deshalb marke.png, die verlustfreie Umsetzung derselben Zeichnung.
*
* 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-tagespflege-moldovan.sh).
* Erstes Argument: der Ordner mit gemeinsam.webp, jacqueline-moldovan.png und marke.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, 'tagespflege-moldovan')) {
fwrite(STDERR, "Sicherung: DATABASE_URL muss auf die Moldovan-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);
$mail = 'info@tagespflege-moldovan.com';
$telefon = '07221 / 922 7961';
$telHref = 'tel:+4972219227961';
$SCHEMA = 23;
// ---------------------------------------------------------------------------------------------
// Bausteine
// ---------------------------------------------------------------------------------------------
$ecken = static fn (string $r): array => ['topLeft' => $r, 'topRight' => $r, 'bottomRight' => $r, 'bottomLeft' => $r];
$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'];
$nurLinks = static fn (string $w): array => ['top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => $w];
$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;
};
/** Der Rhythmus der Vorlage: 100 px am Schreibtisch, 75 auf dem Tablet, 61 auf dem Telefon. */
$abschnitt = static function (string $id, array $children, array $style = [], ?string $anchor = null) use ($section): array {
$basis = [
'desktop' => ['padding' => ['top' => '100px', 'bottom' => '100px']],
'tablet' => ['padding' => ['top' => '75px', 'bottom' => '75px']],
'mobile' => ['padding' => ['top' => '61px', 'bottom' => '61px']],
];
foreach ($style as $device => $values) {
$basis[$device] = [...($basis[$device] ?? []), ...$values];
}
return $section($id, $children, $basis, $anchor);
};
$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 – Marke, Signatur, Wertepunkt, Zeitleiste. */
$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'): array {
$block = ['id' => $id, 'type' => 'column', 'children' => $children];
if ([] !== $style) {
$block['style'] = $style;
}
if ('all' !== $sichtbarkeit) {
$block['layout'] = ['visibility' => $sichtbarkeit];
}
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 Strich vor einer Kleinzeile, die Linie der Zeitleiste, die grüne Form. */
$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;
};
$button = static function (string $id, string $text, string $href, array $style = []): array {
return ['id' => $id, 'type' => 'button', 'props' => ['text' => $text, 'href' => $href], 'style' => $style];
};
$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.
*
* Ein Block mit Einblendung wartet unsichtbar, bis er ins Bild scrollt; was beim Laden schon
* sichtbar ist, spielt sofort. Ohne JavaScript steht alles einfach da – die Seite bleibt
* vollständig, nur ohne Bewegung.
*
* @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.
$serif = ['fontFamily' => 'heading', 'fontWeight' => '400', 'letterSpacing' => '-0.045em', 'lineHeight' => 1.1];
$h2Stil = ['desktop' => [...$serif, 'fontSize' => '53px', 'margin' => ['bottom' => '0px']], 'tablet' => ['fontSize' => '42px'], 'mobile' => ['fontSize' => '38px']];
$emGruen = '& em{color:#718f3d;font-weight:400}';
$fliess = ['desktop' => ['color' => '@gedaempft', 'fontSize' => '16px', 'lineHeight' => 1.65, 'margin' => ['bottom' => '20px']]];
$klein = ['desktop' => ['fontSize' => '11px', 'fontWeight' => '700', 'letterSpacing' => '0.13em', 'color' => '#6a795e', 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']]];
$h3Stil = ['desktop' => ['fontFamily' => 'body', 'fontSize' => '19px', 'fontWeight' => '500', 'letterSpacing' => '-0.025em', 'lineHeight' => 1.35, 'margin' => ['bottom' => '13px']]];
/** Die Kleinzeile über einer Überschrift: ein kurzer Strich, dann Versalien. */
$kleinzeile = static function (string $id, string $inhalt, string $farbe = '#527236', string $strich = '@gruen', string $abstand = '25px') use ($zeile, $flaeche, $text): array {
return $zeile($id, [
$flaeche($id.'-strich', ['desktop' => ['width' => '24px', 'height' => '3px', 'backgroundColor' => $strich, 'flexShrink' => 0]]),
$text($id.'-text', $inhalt, [
'desktop' => ['fontSize' => '12px', 'fontWeight' => '700', 'letterSpacing' => '0.16em', 'textTransform' => 'uppercase', 'lineHeight' => 1.7, 'color' => $farbe, 'margin' => ['bottom' => '0px']],
'mobile' => ['fontSize' => '11px'],
]),
], '11px', ['margin' => ['top' => '0px', 'bottom' => $abstand]], ['margin' => ['top' => '0px', 'bottom' => '18px']]);
};
// ---------------------------------------------------------------------------------------------
// Website, Gestaltung, Medien, Formular
// ---------------------------------------------------------------------------------------------
$db->insert('website', ['id' => 'default', 'name' => 'Tagespflege Jacqueline Moldovan', 'default_locale' => 'de', 'theme' => '{}']);
$theme->save(
[
['key' => 'tinte', 'name' => 'Tinte', 'value' => '#294655'],
['key' => 'blau', 'name' => 'Blau', 'value' => '#2e7595'],
['key' => 'blau-dunkel', 'name' => 'Blau dunkel', 'value' => '#245e79'],
['key' => 'dunkelblau', 'name' => 'Kontaktbereich', 'value' => '#284f63'],
['key' => 'gruen', 'name' => 'Grün', 'value' => '#9abf56'],
['key' => 'limette', 'name' => 'Limette', 'value' => '#c0d348'],
['key' => 'oliv', 'name' => 'Oliv', 'value' => '#718f3d'],
['key' => 'marke', 'name' => 'Markengrün', 'value' => '#83a944'],
['key' => 'gedaempft', 'name' => 'Gedämpft', 'value' => '#647078'],
['key' => 'blass', 'name' => 'Blass', 'value' => '#f3f6ef'],
['key' => 'leiste', 'name' => 'Kopfleiste', 'value' => '#f1f4ee'],
['key' => 'sand', 'name' => 'Werteband', 'value' => '#fafbf8'],
['key' => 'linie', 'name' => 'Trennlinie', 'value' => '#dce3dd'],
['key' => 'papier', 'name' => 'Papier', 'value' => '#ffffff'],
],
[
// Beide Schriften stehen auf jedem Gerät bereit; die Vorlage lädt bewusst keine Webschrift.
['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.65, 'color' => '@tinte', 'backgroundColor' => '@papier', 'linkColor' => '@tinte', 'containerWidth' => '1240px', 'headingScale' => 1.25, 'linkUnderline' => false],
['shape' => 'square', 'appearance' => 'solid', 'size' => 'normal', 'fontFamily' => 'inherit', 'fontSize' => 15, 'fontWeight' => 600, 'letterSpacing' => 0, 'textTransform' => 'none', 'backgroundColor' => '@blau', 'color' => '@papier', 'hoverBackgroundColor' => '@blau-dunkel', 'hoverColor' => '@papier'],
);
echo "Farben, Schriften und Buttons gesetzt\n";
$medien = [];
foreach ([
'marke' => ['marke.png', 'Markenzeichen der Tagespflege Jacqueline Moldovan: ein Schmetterling in Grün und Blau'],
'gemeinsam' => ['gemeinsam.webp', 'Illustrative Szene: Eine Seniorin und eine Begleiterin lachen gemeinsam an einem Tisch.'],
'portraet' => ['jacqueline-moldovan.png', 'Jacqueline Moldovan'],
] 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, $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";
}
$forms->save('kontakt', 'Kontaktanfrage', [
['name' => 'name', 'type' => 'text', 'label' => 'Ihr Name', 'required' => true],
['name' => 'email', 'type' => 'email', 'label' => 'E-Mail-Adresse', 'required' => true],
['name' => 'telefon', 'type' => 'phone', 'label' => 'Telefon', 'required' => false, 'help' => 'Für einen persönlichen Rückruf'],
['name' => 'nachricht', 'type' => 'textarea', 'label' => 'Ihre Nachricht', 'required' => true, 'help' => 'Erzählen Sie uns, was Ihnen wichtig ist. Bitte keine Angaben zu Gesundheit oder Pflegegrad – das besprechen wir lieber persönlich.'],
['name' => 'datenschutz', 'type' => 'consent', 'label' => 'Ich habe die Datenschutzhinweise gelesen. Meine Angaben werden verwendet, um meine Anfrage zu beantworten.'],
], ['recipient' => $mail, 'subject' => 'Neue Nachricht über tagespflege-moldovan.com', 'success' => 'Vielen Dank. Ihre Nachricht ist angekommen – wir melden uns bei Ihnen.', 'replyToField' => 'email'], 0);
echo "Formular angelegt: kontakt\n";
// ---------------------------------------------------------------------------------------------
// Kopfbereich: schmale Leiste, darunter der weiße Kopf, der beim Scrollen oben bleibt
// ---------------------------------------------------------------------------------------------
/** Die Wortmarke: Schmetterling, darüber „Tagespflege“, darunter der Name. */
$wortmarke = static function (string $praefix, string $bildhoehe, string $bildbreite, string $gross, string $klein) use ($zeile, $column, $bild, $text, $medien, $seiten): array {
return $zeile($praefix.'-zeile', [
$column($praefix.'-bild', [
$bild($praefix.'-logo', $medien['marke'], 'Tagespflege Jacqueline Moldovan', [
'desktop' => ['maxWidth' => $bildbreite, 'imageHeight' => $bildhoehe, 'objectFit' => 'contain', 'margin' => $seiten('0px')],
], '/'),
], ['desktop' => ['flexShrink' => 0]]),
$column($praefix.'-schrift', [
$text($praefix.'-name', 'Tagespflege', ['desktop' => ['fontSize' => $gross, 'fontWeight' => '700', 'letterSpacing' => '-0.04em', 'lineHeight' => 1.3, 'color' => '@marke', 'margin' => ['bottom' => '0px']]]),
$text($praefix.'-zusatz', 'Jacqueline Moldovan', ['desktop' => ['fontSize' => $klein, 'color' => '#62676b', 'lineHeight' => 1.3, 'margin' => ['top' => '3px', 'bottom' => '0px']]]),
]),
], '11px', ['flexShrink' => 0]);
};
$kopf = [
'schemaVersion' => $SCHEMA, 'title' => 'Kopfbereich', 'description' => 'Erscheint auf jeder Seite über dem Inhalt.',
'blocks' => [
$section('kopf-leiste', [
$row('kopf-leiste-zeile', [
$column('kopf-leiste-ort', [
$text('kopf-leiste-ort-text', 'Mitten in Baden-Baden. Mitten im Leben.', ['desktop' => ['fontSize' => '14px', 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']]]),
], [], 'hide-mobile'),
$column('kopf-leiste-kontakt', [
$text('kopf-leiste-telefon', '<a href="'.$telHref.'">Ihr direkter Kontakt: '.$telefon.'</a>', [
'desktop' => ['fontSize' => '14px', 'lineHeight' => 1.5, 'textAlign' => 'right', 'margin' => ['bottom' => '0px']],
'mobile' => ['fontSize' => '12px', 'textAlign' => 'center'],
], '& a:hover{text-decoration:underline}'),
]),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], ['desktop' => ['alignItems' => 'center', 'margin' => ['top' => '0px']]]),
], [
'desktop' => ['backgroundColor' => '@leiste', 'color' => '#506054', 'padding' => ['top' => '8px', 'bottom' => '8px']],
]),
$section('kopf-haupt', [
$row('kopf-haupt-zeile', [
$column('kopf-marke', [$wortmarke('kopf-marke', '60px', '40px', '24px', '15px')], ['desktop' => ['flexShrink' => 0]]),
$column('kopf-navigation', [
['id' => 'kopf-menue', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'horizontal', 'collapse' => 'mobile', 'burgerLabel' => 'both'],
'style' => ['desktop' => [
'fontSize' => '15px', 'justifyContent' => 'flex-end', 'display' => 'flex',
// Der Menü-Knopf im Zuschnitt der Vorlage: heller Rahmen, kaum gerundet.
'burgerColor' => '@tinte', 'burgerBorderColor' => '#d8e0d1', 'burgerBorderRadius' => '3px', 'burgerFontSize' => '14px',
]],
'css' => "& .menu-list{gap:4px 29px;justify-content:flex-end}\n& .menu-list a{transition:color .2s}\n& .menu-list a:hover{color:#688f30;text-decoration:none}\n& .menu-list>li:last-child>a{border:1px solid #cfd9ce;border-radius:4px;padding:12px 19px}",
],
], ['desktop' => ['display' => 'flex', 'justifyContent' => 'flex-end', 'alignItems' => 'center']]),
], [], [
'desktop' => ['display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'space-between', 'gap' => '25px', 'minHeight' => '100px', 'margin' => ['top' => '0px']],
'tablet' => ['minHeight' => '86px'],
'mobile' => ['minHeight' => '80px'],
]),
], [
'desktop' => ['backgroundColor' => '#fffffff7', 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#e7ece7', 'padding' => ['top' => '0px', 'bottom' => '0px']],
]),
],
];
// ---------------------------------------------------------------------------------------------
// Fußbereich
// ---------------------------------------------------------------------------------------------
$fuss = [
'schemaVersion' => $SCHEMA, 'title' => 'Fußbereich', 'description' => 'Erscheint auf jeder Seite unter dem Inhalt.',
'blocks' => [
$section('fuss', [
$zeile('fuss-oben', [
$column('fuss-marke', [$wortmarke('fuss-marke', '51px', '34px', '19px', '13px')], ['desktop' => ['flexShrink' => 0]]),
$column('fuss-spruch', [
$text('fuss-spruch-text', 'Mit Aufmerksamkeit. Mit Freude. Mit Herz.', [
'desktop' => ['fontFamily' => 'heading', 'fontStyle' => 'italic', 'fontSize' => '17px', 'color' => '#737b78', 'textAlign' => 'center', 'margin' => ['bottom' => '0px']],
'tablet' => ['maxWidth' => '210px'],
'mobile' => ['fontSize' => '15px', 'textAlign' => 'left', 'maxWidth' => '100%'],
]),
], ['mobile' => ['order' => 3, 'flexBasis' => '100%']]),
$column('fuss-nachoben', [
$text('fuss-nachoben-text', '<a href="#start">Nach oben ↑</a>', [
'desktop' => ['fontSize' => '13px', 'textAlign' => 'right', 'margin' => ['bottom' => '0px']],
'mobile' => ['margin' => ['left' => 'auto']],
], '& a:hover{text-decoration:underline}'),
]),
], '25px', ['justifyContent' => 'space-between', 'padding' => ['top' => '37px', 'bottom' => '37px']], ['flexWrap' => 'wrap', 'gap' => '22px', 'padding' => ['top' => '30px', 'bottom' => '30px']]),
$zeile('fuss-unten', [
$column('fuss-recht', [
$text('fuss-copyright', '© 2026 Tagespflege Jacqueline Moldovan', ['desktop' => ['fontSize' => '12px', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '11px']]),
]),
$column('fuss-hinweis', [
// Impressum und Datenschutz kommen aus den Einstellungen, nicht aus einem Menü.
['id' => 'fuss-rechtliches', 'type' => 'legal', 'props' => ['label' => 'Rechtliches'],
'style' => ['desktop' => ['fontSize' => '12px', 'justifyContent' => 'flex-end', 'gap' => '4px'], 'mobile' => ['justifyContent' => 'flex-start']],
'css' => "& a:hover{text-decoration:underline}"],
]),
], '20px', ['justifyContent' => 'space-between', 'alignItems' => 'flex-start', 'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '#e7ebe4', 'padding' => ['top' => '18px', 'bottom' => '18px'], 'color' => '#78817b'], ['flexDirection' => 'column', 'gap' => '7px']),
], [
'desktop' => ['borderWidth' => $nurUnten('7px'), 'borderStyle' => 'solid', 'borderColor' => '@limette', '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, Vorspann, dann Absätze mit Zwischenüberschriften.
*
* Stellen, die nur die Tagespflege selbst beantworten kann, stehen als „NOCH ZU PRÜFEN“ im Text,
* statt erfunden zu werden – eine falsche Registerangabe ist schlimmer als eine fehlende.
*/
$rechtsseite = static function (string $praefix, string $label, string $titel, string $vorspann, array $absaetze) use ($section, $abschnitt, $row, $column, $kleinzeile, $heading, $text, $serif, $emGruen, $nurOben, $nurUnten): array {
// Oben ein ruhiges Band in der Farbe des Angebotsbereichs: Kleinzeile, Titel, Vorspann.
// Die Sektion bleibt voll breit; die Breite begrenzt erst der Inhalt darin. So steht der
// Text auf derselben Kante wie Marke und Menü im Kopf, statt am Fensterrand zu kleben.
$kopf = $section($praefix.'-kopf', [
[...$kleinzeile($praefix.'-label', $label), 'layout' => ['effects' => ['entrance' => 'fade', 'delay' => 'none']]],
[...$heading($praefix.'-h1', $titel, 1, [
'desktop' => [...$serif, 'fontSize' => '59px', 'margin' => ['bottom' => '22px']],
'tablet' => ['fontSize' => '46px'],
'mobile' => ['fontSize' => '40px', 'margin' => ['bottom' => '16px']],
], $emGruen), 'layout' => ['effects' => ['entrance' => 'rise', 'delay' => 'short']]],
[...$text($praefix.'-vorspann', $vorspann, [
'desktop' => ['maxWidth' => '640px', 'fontSize' => '19px', 'lineHeight' => 1.7, 'color' => '@gedaempft', 'margin' => ['bottom' => '0px']],
'mobile' => ['fontSize' => '17px'],
]), 'layout' => ['effects' => ['entrance' => 'rise', 'delay' => 'medium']]],
], [
'desktop' => ['backgroundColor' => '@blass', 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '84px', 'bottom' => '72px']],
'tablet' => ['padding' => ['top' => '64px', 'bottom' => '56px']],
'mobile' => ['padding' => ['top' => '48px', 'bottom' => '40px']],
]);
// Darunter jeder Punkt als eigene Zeile: Zwischentitel links, Text rechts, getrennt durch
// eine feine Linie. Auf dem Telefon untereinander.
$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' => '17px', 'fontWeight' => '700', 'letterSpacing' => '-0.01em', 'lineHeight' => 1.5, 'color' => '@tinte', 'margin' => ['bottom' => '0px']],
]),
], [
'desktop' => ['flexBasis' => '280px', 'flexShrink' => 0],
'tablet' => ['flexBasis' => '220px'],
'mobile' => ['flexBasis' => 'auto'],
]),
$column($praefix.'-punkt-'.$i.'-text', [
$text($praefix.'-p-'.$i, $inhalt, [
'desktop' => ['fontSize' => '16px', 'lineHeight' => 1.75, 'color' => '@gedaempft', 'margin' => ['bottom' => '0px']],
], "& a{color:#2e7595;text-decoration:underline;text-underline-offset:3px}\n& a:hover{color:#245e79}"),
], ['desktop' => ['flexGrow' => 1, 'flexBasis' => '0', 'minWidth' => '0']]),
], [], [
'desktop' => ['display' => 'flex', 'alignItems' => 'flex-start', 'gap' => '48px', 'margin' => ['top' => '0px'],
'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '28px', 'bottom' => '28px']],
'tablet' => ['gap' => '32px'],
'mobile' => ['flexDirection' => 'column', 'gap' => '8px', 'padding' => ['top' => '22px', 'bottom' => '22px']],
]);
}
$inhalt = $abschnitt($praefix, [
$column($praefix.'-inhalt', $punkte, ['desktop' => ['maxWidth' => '960px']]),
], ['desktop' => ['padding' => ['top' => '56px', 'bottom' => '100px']], 'mobile' => ['padding' => ['top' => '28px', 'bottom' => '61px']]]);
return [$kopf, $inhalt];
};
// ---------------------------------------------------------------------------------------------
// Startseite
// ---------------------------------------------------------------------------------------------
$werte = ['Persönlich begleitet', 'Gemeinsam aktiv', 'Mit Freude durch den Tag'];
// Nummer, Symbol, Farbe des oberen Rands, Titel, Text – die drei Karten aus „Unser Angebot“.
$angebote = [
['01 / MITEINANDER', 'people', '#a9c563', '#f0f5e7', '#6b8f36', 'Gesellschaft genießen', 'Erzählen, zuhören, gemeinsam lachen. Hier können aus Begegnungen vertraute Beziehungen werden.'],
['02 / LEBENSFREUDE', 'music', '#4285a2', '#edf5f8', '#347895', 'Mit Freude aktiv sein', 'Musik, Bewegung und kreative Momente bringen Abwechslung in den Tag. Jeder macht mit, wie es sich gut anfühlt.'],
['03 / GEBORGENHEIT', 'home', '#bdce62', '#f0f5e7', '#6b8f36', 'Sich aufgehoben fühlen', 'Ein liebevoll gedeckter Tisch, Zeit zum Durchatmen und aufmerksame Begleitung geben dem Tag einen vertrauten Rahmen.'],
];
$tagesablauf = [
['01', 'ANKOMMEN', 'In Ruhe in den Tag starten', 'Ein freundliches Willkommen, ein gemeinsames Frühstück und Zeit für die ersten Gespräche.'],
['02', 'GEMEINSAM ERLEBEN', 'Den Vormittag gestalten', 'Ein Spiel, etwas Bewegung oder gemeinsam kreativ werden – passend zu Lust und Möglichkeiten.'],
['03', 'GENIESSEN & AUSRUHEN', 'Zeit für eine Pause', 'Zusammen Mittag essen und anschließend entspannen. Jeder darf seinen eigenen Rhythmus finden.'],
['04', 'DEN TAG AUSKLINGEN LASSEN', 'Mit einem Lächeln nach Hause', 'Kaffee, eine gemütliche Runde und schöne Erinnerungen, die mit nach Hause gehen.'],
];
$startseite = [
'schemaVersion' => $SCHEMA,
'title' => 'Tagespflege Moldovan',
'description' => 'Tagespflege Jacqueline Moldovan in Baden-Baden. Ein Ort für Begegnung, Geborgenheit und schöne gemeinsame Tage.',
'blocks' => [
// 1 – Hero: Text links, Foto rechts auf einer limettenen Form, Notizkarte darüber.
$section('hero', [
$row('hero-zeile', [
$column('hero-text', [
$kleinzeile('hero-label', 'Tagespflege in Baden-Baden'),
$heading('hero-titel', 'Ein schöner Tag.<br>Ein gutes Gefühl.<br><em>Ein Stück Zuhause.</em>', 1, [
'desktop' => [...$serif, 'fontSize' => '68px', 'margin' => ['bottom' => '25px']],
'tablet' => ['fontSize' => '42px'],
'mobile' => ['fontSize' => '43px', 'lineHeight' => 1.12, 'margin' => ['bottom' => '21px']],
], $emGruen),
$text('hero-intro', 'Gemeinsam lachen, Neues erleben und sich gut aufgehoben fühlen. Wir geben dem Tag Leben – und Angehörigen ein wenig Freiraum.', [
'desktop' => ['maxWidth' => '465px', 'color' => '@gedaempft', 'fontSize' => '17px', 'lineHeight' => 1.8, 'margin' => ['bottom' => '28px']],
'mobile' => ['fontSize' => '16px', 'maxWidth' => '520px'],
]),
$zeile('hero-aktionen', [
$column('hero-aktion-knopf', [
$button('hero-knopf', 'Lernen wir uns kennen', '#kontakt', ['desktop' => ['borderRadius' => $ecken('4px'), 'margin' => ['top' => '0px']]]),
], ['desktop' => ['flexShrink' => 0]]),
$column('hero-aktion-link', [
$text('hero-textlink', '<a href="#bei-uns">Unsere Tagespflege entdecken ↓</a>', [
'desktop' => ['fontSize' => '14px', 'padding' => ['top' => '15px', 'bottom' => '15px'], 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#cad4c8', 'margin' => ['bottom' => '0px']],
]),
], ['desktop' => ['flexShrink' => 0]]),
], '18px', ['alignItems' => 'flex-start', 'flexWrap' => 'wrap'], ['rowGap' => '8px', 'columnGap' => '20px']),
$text('hero-notiz', "Tagsüber in guter Gesellschaft.\nAbends im vertrauten Zuhause.", [
'desktop' => ['fontSize' => '14px', 'lineHeight' => 1.6, 'color' => '#737a7a', 'borderWidth' => $nurLinks('3px'), 'borderStyle' => 'solid', 'borderColor' => '@limette', 'padding' => ['left' => '15px'], 'margin' => ['top' => '29px', 'bottom' => '0px']],
'mobile' => ['margin' => ['top' => '20px', 'bottom' => '0px']],
]),
]),
$column('hero-visual', [
// Die limettene Form hinter dem Foto – dieselbe Schnittkante wie in der Vorlage.
$flaeche('hero-form', [
'desktop' => ['position' => 'absolute', 'top' => '-19px', 'right' => '-17px', 'bottom' => '35px', 'left' => '35px', 'height' => 'auto', 'width' => 'auto', 'backgroundColor' => '@limette', 'zIndex' => 0],
'mobile' => ['top' => '-14px', 'right' => '-9px', 'bottom' => '28px', 'left' => '30px'],
], 'clip-path:polygon(18% 0,100% 0,81% 100%,0 100%)'),
$bild('hero-bild', $medien['gemeinsam'], 'Illustrative Szene: Eine Seniorin und eine Begleiterin lachen gemeinsam an einem Tisch.', [
'desktop' => ['position' => 'relative', 'zIndex' => 1, 'imageHeight' => '465px', 'objectFit' => 'cover', 'objectPosition' => 'center', 'imageRadius' => ['topLeft' => '110px', 'topRight' => '6px', 'bottomRight' => '6px', 'bottomLeft' => '6px'], 'margin' => $seiten('0px')],
'tablet' => ['imageHeight' => '390px'],
'mobile' => ['imageHeight' => '380px', 'imageRadius' => ['topLeft' => '85px', 'topRight' => '6px', 'bottomRight' => '6px', 'bottomLeft' => '6px']],
], null, '& img{object-position:54% 45%}'),
$column('hero-karte', [
$text('hero-karte-label', 'WAS WIR UNS WÜNSCHEN', $klein),
$text('hero-karte-text', "Dass Sie gerne\n<em>wiederkommen.</em>", [
'desktop' => [...$serif, 'fontSize' => '30px', 'lineHeight' => 1.15, 'letterSpacing' => '-0.02em', 'margin' => ['top' => '11px', 'bottom' => '15px']],
'mobile' => ['fontSize' => '27px'],
], '& em{color:#73933d}'),
$flaeche('hero-karte-strich', ['desktop' => ['width' => '36px', 'height' => '3px', 'backgroundColor' => '@limette']]),
], [
'desktop' => ['position' => 'absolute', 'left' => '-25px', 'bottom' => '-23px', 'zIndex' => 2, 'backgroundColor' => '@papier', 'minWidth' => '245px', 'padding' => ['top' => '22px', 'right' => '28px', 'bottom' => '17px', 'left' => '28px'], 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#edf0e9', 'shadowY' => '8px', 'shadowBlur' => '35px', 'shadowColor' => '#29465513'],
'tablet' => ['left' => '-12px', 'minWidth' => '190px', 'padding' => ['top' => '18px', 'right' => '22px', 'bottom' => '14px', 'left' => '22px']],
'mobile' => ['left' => '-12px', 'bottom' => '-25px', 'minWidth' => '190px', 'padding' => ['top' => '20px', 'right' => '25px', 'bottom' => '16px', 'left' => '25px']],
]),
$text('hero-caption', 'Gemeinschaft, die guttut.', [
'desktop' => ['position' => 'absolute', 'right' => '0px', 'bottom' => '-31px', 'fontSize' => '12px', 'color' => '#737b79', 'margin' => ['bottom' => '0px']],
]),
], [
'desktop' => ['position' => 'relative', 'margin' => ['left' => '10px', 'bottom' => '25px']],
'tablet' => ['margin' => ['left' => '0px', 'bottom' => '25px']],
'mobile' => ['margin' => ['top' => '0px', 'right' => '6px', 'bottom' => '22px', 'left' => '12px']],
]),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '45px', 'alignItems' => 'center', 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '25px'],
'mobile' => ['gap' => '45px'],
]),
], [
'desktop' => ['minHeight' => '650px', 'display' => 'flex', 'alignItems' => 'center', 'padding' => ['top' => '67px', 'bottom' => '75px']],
'tablet' => ['minHeight' => 'auto', 'padding' => ['top' => '50px', 'bottom' => '75px']],
'mobile' => ['minHeight' => 'auto', 'padding' => ['top' => '42px', 'bottom' => '57px']],
], 'start'),
// 2 – Das Band mit den drei Versprechen.
$section('werte', [
$row('werte-zeile', array_map(
static fn (string $wert, int $i): array => $column('wert-'.($i + 1), [
$symbol('wert-'.($i + 1).'-haken', 'check', 16, ['desktop' => ['color' => '#789d3e', 'flexShrink' => 0]]),
$text('wert-'.($i + 1).'-text', $wert, ['desktop' => ['fontSize' => '15px', 'letterSpacing' => '0.015em', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '14px']]),
], [
'desktop' => ['display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'center', 'gap' => '13px']
+ (0 === $i ? [] : ['borderWidth' => ['top' => '0px', 'right' => '0px', 'bottom' => '0px', 'left' => '1px'], 'borderStyle' => 'solid', 'borderColor' => '#dbe2d6']),
'mobile' => ['justifyContent' => 'flex-start', 'gap' => '16px', 'borderStyle' => 'none'],
]),
$werte, array_keys($werte),
), ['desktop' => ['columns' => 'three'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['margin' => ['top' => '0px']],
'mobile' => ['gap' => '12px'],
]),
], [
'desktop' => ['backgroundColor' => '@sand', 'borderWidth' => ['top' => '1px', 'right' => '0px', 'bottom' => '1px', 'left' => '0px'], 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '22px', 'bottom' => '22px']],
'mobile' => ['padding' => ['top' => '20px', 'bottom' => '20px']],
]),
// 3 – Willkommen: Überschrift links, Text und Signatur rechts.
$abschnitt('vorstellung', [
$row('vorstellung-zeile', [
$column('vorstellung-titel', [
$kleinzeile('vorstellung-label', 'Willkommen bei uns'),
$heading('vorstellung-h2', 'Hier zählt der Mensch.<br><em>Und das Miteinander.</em>', 2, $h2Stil, $emGruen),
]),
$column('vorstellung-text', [
$text('vorstellung-lead', 'Manchmal braucht es nur ein vertrautes Gesicht, ein gutes Gespräch und das Gefühl, dazuzugehören.', [
'desktop' => ['fontSize' => '20px', 'lineHeight' => 1.7, 'color' => '@tinte', 'margin' => ['bottom' => '20px']],
'mobile' => ['fontSize' => '18px'],
]),
$text('vorstellung-p1', 'Unsere Tagespflege ist ein Ort für ältere Menschen, die ihren Tag in Gesellschaft verbringen möchten. Dabei ist uns wichtig, Raum für persönliche Gewohnheiten zu lassen: für Aktivität ebenso wie für eine ruhige Pause.', $fliess),
$text('vorstellung-p2', 'Mit Aufmerksamkeit und einem offenen Ohr möchten wir den Alltag unserer Gäste bereichern und ihre Angehörigen entlasten.', $fliess),
$zeile('vorstellung-signatur', [
$column('signatur-bild', [
$bild('signatur-portraet', $medien['portraet'], 'Jacqueline Moldovan', [
'desktop' => ['maxWidth' => '104px', 'imageHeight' => '104px', 'objectFit' => 'cover', 'objectPosition' => 'top', 'imageRadius' => $ecken('50%'), 'imageBorderWidth' => $seiten('4px'), 'imageBorderStyle' => 'solid', 'imageBorderColor' => '@papier', 'margin' => $seiten('0px')],
'tablet' => ['maxWidth' => '86px', 'imageHeight' => '86px'],
'mobile' => ['maxWidth' => '86px', 'imageHeight' => '86px'],
], null),
], ['desktop' => ['flexShrink' => 0]]),
$flaeche('signatur-strich', ['desktop' => ['width' => '28px', 'height' => '2px', 'backgroundColor' => '@gruen', 'flexShrink' => 0], 'tablet' => ['width' => '20px']]),
$column('signatur-name', [
$text('signatur-person', '<strong>Jacqueline Moldovan</strong>', ['desktop' => ['fontSize' => '15px', 'fontWeight' => '600', 'color' => '#2e6682', 'margin' => ['bottom' => '0px']]]),
$text('signatur-rolle', 'Geschäftsführerin', ['desktop' => ['fontSize' => '13px', 'color' => '@gedaempft', 'margin' => ['top' => '4px', 'bottom' => '0px']]]),
]),
], '18px', ['flexWrap' => 'wrap', 'margin' => ['top' => '29px']], ['gap' => '14px']),
]),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '80px', 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '50px'],
'mobile' => ['gap' => '28px'],
]),
], [], 'bei-uns'),
// 4 – Unser Angebot: drei Karten auf blasser Fläche.
$abschnitt('angebot-abschnitt', [
$zeile('angebot-kopf', [
$column('angebot-kopf-titel', [
$kleinzeile('angebot-label', 'Raum für einen guten Tag'),
$heading('angebot-h2', 'Was den Alltag<br><em>schöner macht.</em>', 2, $h2Stil, $emGruen),
]),
$column('angebot-kopf-text', [
$text('angebot-intro', "Die kleinen Dinge machen den Unterschied.\nUnd Menschen, die sich Zeit nehmen.", [
'desktop' => ['fontSize' => '15px', 'color' => '@gedaempft', 'margin' => ['bottom' => '5px']],
'tablet' => ['maxWidth' => '260px'],
'mobile' => ['margin' => ['top' => '23px', 'bottom' => '0px']],
]),
]),
], '35px', ['alignItems' => 'flex-end', 'justifyContent' => 'space-between', 'margin' => ['top' => '0px', 'bottom' => '46px']], ['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '0px', 'margin' => ['top' => '0px', 'bottom' => '30px']]),
$row('angebot-raster', array_map(
static function (array $a, int $i) use ($column, $text, $symbol, $heading, $h3Stil, $seiten, $ecken): array {
$n = $i + 1;
return $column('angebot-'.$n, [
$text('angebot-'.$n.'-nummer', $a[0], ['desktop' => ['fontSize' => '11px', 'fontWeight' => '600', 'letterSpacing' => '0.14em', 'color' => '#788574', 'margin' => ['bottom' => '0px']]]),
$symbol('angebot-'.$n.'-symbol', $a[1], 32, [
'desktop' => ['width' => '55px', 'height' => '55px', 'display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'center', 'borderRadius' => $ecken('50%'), 'backgroundColor' => $a[3], 'color' => $a[4], 'margin' => ['top' => '31px', 'bottom' => '23px']],
'mobile' => ['width' => '48px', 'height' => '48px'],
]),
$heading('angebot-'.$n.'-titel', $a[5], 3, $h3Stil),
$text('angebot-'.$n.'-text', $a[6], ['desktop' => ['fontSize' => '15px', 'color' => '@gedaempft', 'lineHeight' => 1.8, 'margin' => ['bottom' => '0px']]]),
], [
'desktop' => ['backgroundColor' => '@papier', 'borderWidth' => ['top' => '3px', 'right' => '0px', 'bottom' => '0px', 'left' => '0px'], 'borderStyle' => 'solid', 'borderColor' => $a[2], 'padding' => ['top' => '30px', 'right' => '29px', 'bottom' => '28px', 'left' => '29px']],
'tablet' => ['padding' => ['top' => '23px', 'right' => '19px', 'bottom' => '23px', 'left' => '19px']],
'mobile' => ['padding' => ['top' => '25px', 'right' => '27px', 'bottom' => '25px', 'left' => '27px']],
]);
},
$angebote, array_keys($angebote),
), ['desktop' => ['columns' => 'three'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '25px', 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '15px'],
'mobile' => ['gap' => '17px'],
]),
], ['desktop' => ['backgroundColor' => '@blass']], 'angebot'),
// 5 – Ein Tag bei uns: Einleitung links, die Zeitleiste rechts.
$abschnitt('tagesablauf-abschnitt', [
$row('tag-zeile', [
$column('tag-intro', [
$kleinzeile('tag-label', 'Ein Tag bei uns'),
$heading('tag-h2', 'Ein guter Rhythmus.<br><em>Ganz ohne Eile.</em>', 2, $h2Stil, $emGruen),
$text('tag-intro-text', 'So könnte ein gemeinsamer Tag aussehen. Mit vertrauten Ritualen, neuen Impulsen und Zeit für das, was Ihnen Freude macht.', [
'desktop' => ['maxWidth' => '375px', 'color' => '@gedaempft', 'margin' => ['top' => '27px', 'bottom' => '0px']],
'mobile' => ['maxWidth' => '100%'],
]),
$bild('tag-marke', $medien['marke'], '', [
'desktop' => ['maxWidth' => '110px', 'opacity' => 0.65, 'margin' => ['top' => '36px']],
]) + ['layout' => ['visibility' => 'hide-mobile']],
]),
$column('tag-liste', array_map(
static function (array $s, int $i) use ($zeile, $column, $text, $heading, $flaeche, $klein, $h3Stil, $ecken, $seiten, $tagesablauf): array {
$n = $i + 1;
$letzte = $n === count($tagesablauf);
$links = [
$text('tag-'.$n.'-zahl', $s[0], ['desktop' => [
'width' => '42px', 'height' => '42px', 'flexShrink' => 0, 'display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'center',
'backgroundColor' => '#f2f6e9', 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '#dbe7cc', 'borderRadius' => $ecken('50%'),
'fontSize' => '12px', 'color' => '#72943d', 'margin' => ['bottom' => '0px'],
]]),
];
if (!$letzte) {
$links[] = $flaeche('tag-'.$n.'-linie', ['desktop' => ['width' => '1px', 'height' => 'auto', 'flexGrow' => 1, 'backgroundColor' => '#dbe3d4']]);
}
return $zeile('tag-schritt-'.$n, [
$column('tag-'.$n.'-spur', $links, ['desktop' => ['width' => '43px', 'flexShrink' => 0, 'display' => 'flex', 'flexDirection' => 'column', 'alignItems' => 'center']]),
$column('tag-'.$n.'-inhalt', [
$text('tag-'.$n.'-label', $s[1], $klein),
$heading('tag-'.$n.'-titel', $s[2], 3, ['desktop' => [...$h3Stil['desktop'], 'margin' => ['top' => '7px', 'bottom' => '9px']]]),
$text('tag-'.$n.'-text', $s[3], ['desktop' => ['fontSize' => '15px', 'color' => '@gedaempft', 'lineHeight' => 1.75, 'margin' => ['bottom' => '0px']]]),
]),
], '24px', ['alignItems' => 'stretch', 'padding' => ['bottom' => $letzte ? '0px' : '34px']], ['gap' => '19px']);
},
$tagesablauf, array_keys($tagesablauf),
)),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '90px', 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '50px'],
'mobile' => ['gap' => '28px'],
]),
], [], 'tagesablauf'),
// 6 – Kontakt: dunkelblaue Fläche, rechts die weiße Formularkarte.
$section('kontakt-abschnitt', [
// Die schräge hellere Fläche am rechten Rand, mit dem limettenen Streifen davor.
$flaeche('kontakt-form', [
'desktop' => ['position' => 'absolute', 'top' => '0px', 'bottom' => '0px', 'right' => '-80px', 'width' => '180px', 'height' => 'auto', 'backgroundColor' => '#315d71', 'borderWidth' => $nurLinks('12px'), 'borderStyle' => 'solid', 'borderColor' => '#a1bb59', 'zIndex' => 0],
'mobile' => ['right' => '-150px'],
], 'transform:skewX(-16deg);pointer-events:none'),
$row('kontakt-zeile', [
$column('kontakt-text', [
$kleinzeile('kontakt-label', 'Der erste Schritt ist ein Gespräch', '#c4d998', '@limette', '22px'),
$heading('kontakt-h2', 'Ein gutes Gefühl<br>beginnt mit einem<br><em>persönlichen Hallo.</em>', 2, [
'desktop' => [...$serif, 'fontSize' => '59px', 'color' => '@papier', 'margin' => ['bottom' => '24px']],
'tablet' => ['fontSize' => '45px'],
'mobile' => ['fontSize' => '43px'],
], '& em{color:#c1d786;font-weight:400}'),
$text('kontakt-intro', 'Sie möchten unsere Tagespflege kennenlernen oder haben Fragen? Erzählen Sie uns, was Ihnen wichtig ist. Wir freuen uns auf Sie.', [
'desktop' => ['maxWidth' => '430px', 'color' => '#d5e0e4', 'fontSize' => '15px', 'lineHeight' => 1.8, 'margin' => ['bottom' => '28px']],
]),
// Ein Telefonlink, kein Button-Baustein: Buttons verweisen auf Seiten, nicht auf tel:.
$text('kontakt-anrufen', '<a href="'.$telHref.'">Rufen Sie uns an ↗</a>', ['desktop' => ['margin' => ['bottom' => '0px']]],
"& a{display:inline-flex;align-items:center;gap:25px;background:#c3d886;color:#27434a;border-radius:4px;padding:15px 22px;font-size:15px;font-weight:600;transition:background .2s,transform .2s}\n& a:hover{background:#d1e3a5;transform:translateY(-2px)}"),
['id' => 'kontakt-daten', 'type' => 'contact',
'props' => ['phone' => $telefon, 'email' => $mail, 'address' => "Fremersbergstraße 75\n76530 Baden-Baden"],
'style' => ['desktop' => ['fontSize' => '15px', 'color' => '#dce7eb', 'gap' => '10px', 'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '#ffffff30', 'padding' => ['top' => '26px'], 'margin' => ['top' => '30px']]],
// Die Vorlage führt die Angaben ohne Symbole; die Adresse behält ihren Umbruch.
'css' => "& svg{display:none}\n& span{display:block}\n& a{overflow-wrap:anywhere}\n& a:hover{text-decoration:underline}\n& span:first-child a{font-size:22px;color:#ffffff;letter-spacing:-0.025em}\n& span:last-child{white-space:pre-line;line-height:1.65;margin-top:5px}",
],
], ['desktop' => ['alignSelf' => 'center']]),
$column('kontakt-karte', [
$text('kontakt-karte-label', 'WIR FREUEN UNS AUF IHRE NACHRICHT', ['desktop' => [...$klein['desktop'], 'margin' => ['bottom' => '18px']]]),
$heading('kontakt-karte-titel', 'Was können wir<br>für Sie tun?', 3, [
'desktop' => ['fontFamily' => 'heading', 'fontWeight' => '400', 'fontSize' => '30px', 'lineHeight' => 1.25, 'letterSpacing' => '-0.02em', 'margin' => ['bottom' => '25px']],
]),
['id' => 'kontakt-formular', 'type' => 'form', 'props' => ['form' => 'kontakt'],
// Felder und Knopf im Zuschnitt der Vorlage; der Wortlaut des Knopfes kommt vom Baustein.
'css' => "& .form-field label{font-size:14px;font-weight:600;color:#294655}
& .form-field input{background:#fafcf8;border:1px solid #cfd9ce;border-radius:4px;padding:12px 13px;font-size:16px;color:#294655}
& .form-field textarea{background:#fafcf8;border:1px solid #cfd9ce;border-radius:4px;padding:12px 13px;font-size:16px;color:#294655;min-height:120px}
& .field-note{font-size:12px;color:#748078}
& .cms-button{width:100%;justify-content:center;border-radius:4px;margin-top:4px}",
],
$text('kontakt-formular-hinweis', 'Ihre Nachricht geht direkt an die Tagespflege. Wir verwenden Ihre Angaben ausschließlich, um Ihre Anfrage zu beantworten – mehr dazu in den <a href="/datenschutz">Datenschutzhinweisen</a>.', [
'desktop' => ['fontSize' => '13px', 'lineHeight' => 1.65, 'color' => '#64736b', 'margin' => ['top' => '19px', 'bottom' => '0px']],
]),
], [
'desktop' => ['alignSelf' => 'center', 'backgroundColor' => '@papier', 'color' => '@tinte', 'borderRadius' => $ecken('3px'), 'borderWidth' => $nurOben('4px'), 'borderStyle' => 'solid', 'borderColor' => '#bdd26f', 'padding' => ['top' => '32px', 'right' => '35px', 'bottom' => '32px', 'left' => '35px']],
'tablet' => ['padding' => ['top' => '28px', 'right' => '25px', 'bottom' => '28px', 'left' => '25px']],
'mobile' => ['padding' => ['top' => '28px', 'right' => '25px', 'bottom' => '28px', 'left' => '25px']],
]),
], ['desktop' => ['columns' => 'two'], 'mobile' => ['columns' => 'one']], [
'desktop' => ['gap' => '100px', 'position' => 'relative', 'zIndex' => 1, 'margin' => ['top' => '0px']],
'tablet' => ['gap' => '35px'],
'mobile' => ['gap' => '35px'],
]),
], [
'desktop' => ['backgroundColor' => '@dunkelblau', 'color' => '@papier', 'position' => 'relative', 'overflow' => 'hidden', 'padding' => ['top' => '83px', 'bottom' => '83px']],
'mobile' => ['padding' => ['top' => '60px', 'bottom' => '60px']],
], 'kontakt'),
],
];
// ---------------------------------------------------------------------------------------------
// 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 Hero spielt beim Laden, alles Weitere beim Hereinscrollen. Gestaffelt, damit eine Zeile
// nacheinander erscheint statt auf einen Schlag.
$startseite = $einblenden($startseite, [
'hero-label' => ['fade'],
'hero-titel' => ['rise', 'short'],
'hero-intro' => ['rise', 'medium'],
'hero-aktionen' => ['rise', 'long'],
'hero-notiz' => ['fade', 'long'],
'hero-visual' => ['right', 'short'],
'werte-zeile' => ['fade'],
'vorstellung-titel' => ['rise'],
'vorstellung-text' => ['rise', 'short'],
'angebot-kopf' => ['rise'],
'angebot-1' => ['rise'],
'angebot-2' => ['rise', 'short'],
'angebot-3' => ['rise', 'medium'],
'tag-intro' => ['rise'],
'tag-schritt-1' => ['rise'],
'tag-schritt-2' => ['rise', 'short'],
'tag-schritt-3' => ['rise', 'medium'],
'tag-schritt-4' => ['rise', 'long'],
'kontakt-text' => ['rise'],
'kontakt-karte' => ['rise', 'short'],
]);
$anlegen('home', $startseite, 'Tagespflege Moldovan · Gemeinsam den Tag genießen', $medien['gemeinsam']['id']);
$anlegen('impressum', [
'schemaVersion' => $SCHEMA, 'title' => 'Impressum',
'description' => 'Anbieterangaben der Tagespflege Jacqueline Moldovan in Baden-Baden.',
'blocks' => [
...$rechtsseite('impressum', 'Rechtliche Angaben', 'Impressum', 'Angaben gemäß § 5 DDG und § 18 Abs. 2 MStV.', [
['Anbieterin', "Tagespflege Jacqueline Moldovan<br>Fremersbergstraße 75<br>76530 Baden-Baden<br><br>Vertreten durch Jacqueline Moldovan."],
['Kontakt', "Telefon: <a href=\"$telHref\">$telefon</a><br>E-Mail: <a href=\"mailto:$mail\">$mail</a>"],
['Rechtsform und Register', 'NOCH ZU PRÜFEN: Rechtsform, Registergericht und Registernummer ergänzen, sofern eine Eintragung besteht.'],
['Umsatzsteuer', 'NOCH ZU PRÜFEN: Umsatzsteuer-Identifikationsnummer eintragen oder auf die Steuerbefreiung für Pflegeleistungen nach § 4 Nr. 16 UStG hinweisen.'],
['Aufsichtsbehörde', 'NOCH ZU PRÜFEN: zuständige Aufsicht für die Tagespflege eintragen, in Baden-Baden üblicherweise die Heimaufsicht der Stadt. Ebenfalls zu ergänzen: Berufsbezeichnung und zuständige Kammer, sofern einschlägig, sowie ein bestehender Versorgungsvertrag nach § 72 SGB XI.'],
['Verantwortlich für den Inhalt', "Jacqueline Moldovan, Anschrift wie oben."],
['Verbraucherstreitbeilegung', 'Wir sind nicht bereit und nicht verpflichtet, an einem Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teilzunehmen.'],
['Bildnachweis', 'Porträt: Jacqueline Moldovan. Einstiegsbild: illustrative KI-Aufnahme, keine Abbildung tatsächlicher Gäste. Markenzeichen: eigene Gestaltung.'],
['Haftung für Links', 'Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen Einfluss haben. Für diese Inhalte ist stets die jeweilige Anbieterin oder der jeweilige Anbieter verantwortlich. Bei Bekanntwerden von Rechtsverletzungen entfernen wir solche Links umgehend.'],
]),
],
], 'Impressum | Tagespflege Jacqueline Moldovan Baden-Baden');
$anlegen('datenschutz', [
'schemaVersion' => $SCHEMA, 'title' => 'Datenschutz',
'description' => 'Wie die Tagespflege Jacqueline Moldovan Daten beim Besuch der Website und bei einer Kontaktaufnahme verarbeitet.',
'blocks' => [
...$rechtsseite('datenschutz', 'Ihre Daten', 'Datenschutzhinweise', 'Hier erfahren Sie, welche Daten beim Besuch dieser Website und bei Ihrer Kontaktaufnahme verarbeitet werden – und welche Rechte Sie haben.', [
['1. Verantwortliche', "Tagespflege Jacqueline Moldovan, Fremersbergstraße 75, 76530 Baden-Baden. Telefon <a href=\"$telHref\">$telefon</a>, E-Mail <a href=\"mailto:$mail\">$mail</a>.<br><br>NOCH ZU PRÜFEN: Ob eine Datenschutzbeauftragte oder ein Datenschutzbeauftragter zu benennen ist, hängt von der Zahl der ständig mit der Verarbeitung beschäftigten Personen ab; gegebenenfalls hier die Kontaktdaten ergänzen."],
['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>NOCH ZU PRÜFEN: Hostinganbieter benennen, Auftragsverarbeitungsvertrag abschließen, Verarbeitungsländer und die konkrete Speicherdauer der Serverprotokolle eintragen.'],
['3. Cookies, Schriften, 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. Ihre Nachricht über das Kontaktformular', 'Wenn Sie uns schreiben, verarbeiten wir die von Ihnen angegebenen Kontakt- und Inhaltsdaten, um Ihre Anfrage zu beantworten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO, wenn es um die Anbahnung einer Betreuung geht, sonst Art. 6 Abs. 1 lit. f DSGVO.<br><br>Bitte schildern Sie im Formular keine Gesundheitsangaben, keinen Pflegegrad und keine Diagnosen. Solche Angaben sind besonders geschützt (Art. 9 DSGVO); wir besprechen sie lieber persönlich oder am Telefon.'],
['5. Wie lange wir speichern', 'Anfragedaten löschen wir, sobald sie für die Beantwortung nicht mehr nötig sind und keine gesetzliche Aufbewahrungspflicht entgegensteht. Entsteht aus Ihrer Anfrage ein Betreuungsverhältnis, gelten für die dann anfallenden Unterlagen die gesetzlichen Fristen.'],
['6. Empfänger', 'Ihre Angaben erreichen die Tagespflege per E-Mail. Darüber hinaus geben wir sie nicht weiter, außer wir sind dazu gesetzlich verpflichtet.<br><br>NOCH ZU PRÜFEN: 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 | Tagespflege Jacqueline Moldovan');
$fehlerId = $anlegen('404', [
'schemaVersion' => $SCHEMA, 'title' => 'Seite nicht gefunden',
'description' => 'Die gesuchte Seite gibt es nicht oder sie wurde verschoben.',
'blocks' => [
$abschnitt('fehler', [
[...$kleinzeile('fehler-label', 'Fehler 404'), 'layout' => ['effects' => ['entrance' => 'fade', 'delay' => 'none']]],
$heading('fehler-titel', 'Diese Seite<br><em>haben wir nicht.</em>', 1, [
'desktop' => [...$serif, 'fontSize' => '59px', 'margin' => ['bottom' => '25px']],
'mobile' => ['fontSize' => '40px'],
], $emGruen),
$text('fehler-text', 'Vielleicht hat sich ein Tippfehler in die Adresse geschlichen. Über die Startseite finden Sie zurück.', [
'desktop' => ['maxWidth' => '465px', 'color' => '@gedaempft', 'fontSize' => '17px', 'lineHeight' => 1.8, 'margin' => ['bottom' => '28px']],
]),
$button('fehler-knopf', 'Zur Startseite', '/', ['desktop' => ['borderRadius' => $ecken('4px'), 'margin' => ['top' => '0px']]]),
], ['desktop' => ['padding' => ['top' => '120px', 'bottom' => '140px']]]),
],
]);
$db->executeStatement('UPDATE page SET robots = ? WHERE id = ?', ['noindex', $fehlerId]);
// ---------------------------------------------------------------------------------------------
// Menü, Einstellungen, Konto
// ---------------------------------------------------------------------------------------------
$sprung = static fn (string $label, string $anchor): array => ['label' => $label, 'type' => 'page', 'pageId' => $erstellt['home'], 'anchor' => $anchor, 'children' => []];
$menus->save('header', 'Hauptnavigation', [
$sprung('Bei uns', 'bei-uns'),
$sprung('Unser Angebot', 'angebot'),
$sprung('Ein Tag bei uns', 'tagesablauf'),
$sprung('Kontakt aufnehmen ↗', 'kontakt'),
], 0);
echo "Menü angelegt\n";
$settings->assignNotFoundPage($fehlerId, '');
$settings->save([
'legalName' => 'Tagespflege Jacqueline Moldovan',
'description' => 'Tagespflege in Baden-Baden: ein Ort für ältere Menschen, die ihren Tag in Gesellschaft verbringen möchten. Persönliche Begleitung, gemeinsame Aktivitäten und Zeit für Ruhe – abends wieder im vertrauten Zuhause.',
'phone' => $telefon, 'email' => $mail,
'street' => 'Fremersbergstraße 75', 'postalCode' => '76530', 'city' => 'Baden-Baden', 'country' => 'DE',
'localBusiness' => true, 'llmsTxt' => true,
'stickyHeader' => true, 'headerOverlay' => false,
'motion' => 'always',
'faviconMediaId' => $medien['marke']['id'],
'homePath' => 'home', 'imprintPath' => 'impressum', 'privacyPath' => 'datenschutz',
'consentBanner' => false, 'scrollTop' => false, 'postMetaHeader' => true,
]);
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";