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); $posts = new PostRepository($db, $media, $settings); $theme = new Theme($db); $firma = 'Elektro Bernhard Regenerative Energie GmbH'; $mail = 'pv@elektro-bernhard.de'; $mailInfo = 'info@elektro-bernhard.de'; $telefon = '+49 7221 3944909'; $telHref = 'tel:+4972213944909'; $instagram = 'https://www.instagram.com/elektrobernhardenergie/'; $SCHEMA = 24; // --------------------------------------------------------------------------------------------- // Bausteine // --------------------------------------------------------------------------------------------- $seiten = static fn (string $w): array => ['top' => $w, 'right' => $w, 'bottom' => $w, 'left' => $w]; $ecken = static fn (string $r): array => ['topLeft' => $r, 'topRight' => $r, 'bottomRight' => $r, 'bottomLeft' => $r]; $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']; $obenUnten = static fn (string $w): array => ['top' => $w, '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; }; /** Der Rhythmus der Vorlage: 81/78 px am Schreibtisch, 58 auf dem Tablet, 47 auf dem Telefon. */ $abschnitt = static function (string $id, array $children, array $style = [], ?string $anchor = null) use ($section): array { $basis = [ 'desktop' => ['padding' => ['top' => '81px', 'bottom' => '78px']], 'tablet' => ['padding' => ['top' => '58px', 'bottom' => '58px']], 'mobile' => ['padding' => ['top' => '47px', 'bottom' => '47px']], ]; 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 – Kopfleiste, Aktionen, 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 $anchor = null, string $css = ''): array { $block = ['id' => $id, 'type' => 'column', 'children' => $children]; if ([] !== $style) { $block['style'] = $style; } if ('all' !== $sichtbarkeit) { $block['layout'] = ['visibility' => $sichtbarkeit]; } if (null !== $anchor) { $block['anchor'] = $anchor; } 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 rote Strich vor der Kleinzeile. */ $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. Der Baustein hängt das Symbol „arrow-up-right“ an – das passt hier, die Vorlage * setzt genau diesen Pfeil hinter jeden Knopf. Wo sie keinen will, nimmt der Span ihn weg. */ $button = static function (string $id, string $text, string $href, array $style = [], bool $pfeil = true): array { $block = ['id' => $id, 'type' => 'button', 'props' => ['text' => $text, 'href' => $href], 'style' => $style]; if (!$pfeil) { $block['css'] = '& span{display:none}'; } return $block; }; $bild = static function (string $id, array $eintrag, string $alt, array $style = [], ?string $href = null, string $css = '', bool $vergroessern = false): 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'; } elseif ($vergroessern) { // Der Bildbetrachter der Galerie: ohne Skript bleibt der Verweis auf das große Bild. $props['click'] = 'vergroessern'; } $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 $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. $h1Stil = ['desktop' => ['fontWeight' => '600', 'fontSize' => '64px', 'lineHeight' => 1.075, 'letterSpacing' => '-0.045em', 'margin' => ['bottom' => '0px']], 'tablet' => ['fontSize' => '45px'], 'mobile' => ['fontSize' => '40px']]; $h2Stil = ['desktop' => ['fontWeight' => '600', 'fontSize' => '40px', 'lineHeight' => 1.16, 'letterSpacing' => '-0.035em', 'margin' => ['bottom' => '0px']], 'tablet' => ['fontSize' => '32px'], 'mobile' => ['fontSize' => '30px']]; $h3Stil = ['desktop' => ['fontWeight' => '600', 'fontSize' => '20px', 'lineHeight' => 1.16, 'letterSpacing' => '-0.02em', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '19px']]; $kleinStil = ['desktop' => ['fontSize' => '12px', 'fontWeight' => '600', 'letterSpacing' => '0.1em', 'textTransform' => 'uppercase', 'lineHeight' => 1.5, 'color' => '@grau', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '11px']]; $fliess = ['desktop' => ['color' => '@grau', 'fontSize' => '16px', 'lineHeight' => 1.8, 'margin' => ['bottom' => '0px']]]; $linkCss = "& a{color:#b52c2c;text-decoration:underline;text-underline-offset:3px}\n& a:hover{color:#952222}"; /** Der Textlink der Vorlage: 14 px, halbfett, Pfeil daneben, rot beim Zeigen. */ $textLinkCss = "& a{font-weight:500;font-size:14px;text-decoration:none}\n& a:hover{color:#b52c2c}"; /** * Ein Textlink mit Pfeil. Der Pfeil ist ein Symbolbaustein neben dem Text, kein Schriftzeichen * im Text: ein "→" ist ein Buchstabe und sieht in jeder Schrift anders aus. Text und Pfeil * zeigen auf dieselbe Adresse; der Pfeil ist Schmuck und deshalb von Vorlesesoftware und * Tastatur ausgenommen - das erledigt der Symbolbaustein fuer ein dekoratives Symbol selbst. * * @param string $pfeil "arrow-right", "arrow-up-right" oder "arrow-left" * @param bool $vorn Pfeil vor dem Text - fuer den Rueckweg */ $textlink = static function (string $id, string $label, string $href, string $pfeil = 'arrow-right', array $style = [], bool $vorn = false, string $farbe = '@tinte') use ($row, $text): array { $wort = $text($id.'-text', ''.$label.'', [ 'desktop' => ['fontSize' => '14px', 'fontWeight' => '500', 'color' => $farbe, 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']], ], '& a{text-decoration:none}'); $symbol = ['id' => $id.'-pfeil', 'type' => 'icon', 'props' => ['icon' => $pfeil, 'size' => 20, 'decorative' => true, 'label' => '', 'href' => $href], 'style' => ['desktop' => ['flexShrink' => 0, 'color' => $farbe]]]; return [...$row($id, $vorn ? [$symbol, $wort] : [$wort, $symbol], [], [ 'desktop' => ['display' => 'flex', 'alignItems' => 'center', 'gap' => '10px', 'margin' => ['top' => '0px'], ...($style['desktop'] ?? [])], ...array_diff_key($style, ['desktop' => null]), ]), 'css' => '&:hover a{color:#b52c2c} &:hover .block-icon{color:#b52c2c}']; }; $kleinzeile = static fn (string $id, string $inhalt, string $farbe = '@grau', string $abstand = '21px'): array => $text($id, $inhalt, [ 'desktop' => [...$kleinStil['desktop'], 'color' => $farbe, 'margin' => ['bottom' => $abstand]], 'mobile' => $kleinStil['mobile'], ]); // --------------------------------------------------------------------------------------------- // Website, Gestaltung, Medien, Formulare // --------------------------------------------------------------------------------------------- $db->insert('website', ['id' => 'default', 'name' => 'EB Solar', 'default_locale' => 'de', 'theme' => '{}']); $theme->save( [ ['key' => 'rot', 'name' => 'Signalrot', 'value' => '#b52c2c'], ['key' => 'rot-dunkel', 'name' => 'Rot beim Zeigen', 'value' => '#952222'], ['key' => 'tinte', 'name' => 'Tinte', 'value' => '#252b30'], ['key' => 'grau', 'name' => 'Fließtext', 'value' => '#666d72'], ['key' => 'grau-tief', 'name' => 'Nebentext dunkel', 'value' => '#51585c'], ['key' => 'linie', 'name' => 'Trennlinie', 'value' => '#e6e8e8'], ['key' => 'hell', 'name' => 'Helle Fläche', 'value' => '#f5f6f6'], ['key' => 'papier', 'name' => 'Papier', 'value' => '#ffffff'], ], [ // Die Vorlage lädt bewusst keine Webschrift; Arial steht auf jedem Gerät bereit. ['key' => 'body', 'name' => 'Fließtext', 'family' => 'Arial', 'source' => 'system', 'weights' => [400, 500, 600], 'fallback' => 'sans-serif'], ['key' => 'heading', 'name' => 'Überschriften', 'family' => 'Arial', 'source' => 'system', 'weights' => [400, 600], 'fallback' => 'sans-serif'], ], ['fontFamily' => 'body', 'headingFontFamily' => 'heading', 'fontSize' => '16px', 'lineHeight' => 1.65, 'color' => '@tinte', 'backgroundColor' => '@papier', 'linkColor' => '@tinte', 'containerWidth' => '1280px', 'headingScale' => 1.25, 'linkUnderline' => false, 'imageRadius' => '0px'], ['shape' => 'rounded', 'appearance' => 'solid', 'size' => 'normal', 'fontFamily' => 'inherit', 'fontSize' => 15, 'fontWeight' => 600, 'letterSpacing' => 0, 'textTransform' => 'none', 'backgroundColor' => '@rot', 'color' => '@papier', 'hoverBackgroundColor' => '@rot-dunkel', 'hoverColor' => '@papier'], ); echo "Farben, Schriften und Buttons gesetzt\n"; $medien = []; $hochladen = static function (string $datei, string $alt, string $titel = '') use ($assets, $media, &$medien): array { if (isset($medien[$datei])) { return $medien[$datei]; } $pfad = $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' => '' !== $titel ? $titel : pathinfo($datei, PATHINFO_FILENAME), 'alt' => $alt, 'archived' => false, 'version' => $eintrag['version']]); $medien[$datei] = $media->find($eintrag['id']); echo "Bild übernommen: $datei\n"; return $medien[$datei]; }; $logo = $hochladen('logo.png', $firma, 'Elektro Bernhard – Markenzeichen'); $favicon = $hochladen('favicon.jpg', $firma, 'Elektro Bernhard – Favicon'); $hero = $hochladen('hero.jpg', 'Photovoltaikanlage von Elektro Bernhard auf dem Dach eines Wohnhauses', 'Photovoltaik auf dem Wohnhausdach'); $hersteller = [ ['sma.png', 'SMA'], ['solaredge.png', 'SolarEdge'], ['byd.png', 'BYD'], ['solarfabrik.png', 'Solar Fabrik'], ['axitec.png', 'AXITEC'], ['meyerburger.png', 'Meyer Burger'], ]; foreach ($hersteller as [$datei, $name]) { $hochladen($datei, $name, $name); } // Die Bildkarten des Solarchecks: Symbole in Tinte und Rot auf heller Fläche, gezeichnet von // scripts/build-ebsolar-options.mjs (SVG-Quellen liegen daneben). Sobald eine Option ein Bild hat, // zeigt der Formularbaustein die Auswahl als Karten statt als Optionsfelder. $karte = static fn (string $name, string $alt): string => $hochladen('options/'.$name.'.png', $alt, 'Solarcheck: '.$name)['id']; $karten = [ 'personen' => [ '1 Person' => $karte('personen-1', 'Eine Person'), '2 Personen' => $karte('personen-2', 'Zwei Personen'), '3 – 4 Personen' => $karte('personen-3-4', 'Drei Personen'), '4+ Personen' => $karte('personen-4-plus', 'Vier Personen und ein Plus'), ], 'dachform' => [ 'Flachdach' => $karte('dach-flach', 'Haus mit flachem Dach'), 'Satteldach' => $karte('dach-sattel', 'Haus mit Giebeldach'), 'Pultdach' => $karte('dach-pult', 'Haus mit einseitig geneigtem Dach'), 'erweitert/versetzt' => $karte('dach-versetzt', 'Zwei Gebäudeteile mit versetzten Dächern'), 'sonstiges' => $karte('dach-sonstiges', 'Haus mit Fragezeichen'), ], 'dachflaeche' => [ 'bis 50 m²' => $karte('flaeche-50', 'Dachfläche mit zwei Modulen'), '50 m² – 100 m²' => $karte('flaeche-100', 'Dachfläche mit sechs Modulen'), '100 m² – 200 m²' => $karte('flaeche-200', 'Dachfläche mit fünfzehn Modulen'), 'über 200 m²' => $karte('flaeche-200-plus', 'Dachfläche mit achtundzwanzig Modulen'), 'nicht sicher' => $karte('flaeche-unsicher', 'Dachfläche mit Fragezeichen'), ], 'eindeckung' => [ 'Ziegel' => $karte('eindeckung-ziegel', 'Dachziegel in Reihen'), 'Bitumen' => $karte('eindeckung-bitumen', 'Bitumenbahn wird auf ein Flachdach gerollt'), 'Blech' => $karte('eindeckung-blech', 'Trapezblech im Schnitt'), 'Anderes' => $karte('eindeckung-anderes', 'Fläche mit Fragezeichen'), ], 'speicher' => [ 'Ja' => $karte('speicher-ja', 'Akku mit Blitz'), 'Nein' => $karte('speicher-nein', 'Durchgestrichener Akku'), ], ]; $einwilligung = 'Ich habe die Datenschutzhinweise gelesen. Meine Angaben werden verwendet, um meine Anfrage zu beantworten.'; $forms->save('kontakt', 'Kontaktanfrage', [ ['name' => 'name', 'type' => 'text', 'label' => 'Ihr Name', 'required' => true, 'help' => 'Vor- und Nachname'], ['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' => 'Was haben Sie vor? Erzählen Sie uns kurz von Ihrem Projekt.'], ['name' => 'datenschutz', 'type' => 'consent', 'label' => $einwilligung], ], ['recipient' => $mail, 'subject' => 'Neue Nachricht über ebsolar.de', 'success' => 'Vielen Dank. Ihre Nachricht ist angekommen – wir melden uns bei Ihnen.', 'replyToField' => 'email', 'buttonLabel' => 'Nachricht senden'], 0); echo "Formular angelegt: kontakt\n"; // Der Solarcheck: dieselben acht Schritte und Fragen wie das Original auf ebsolar.de // (abgerufen 16.09.2026). Der Jahresverbrauch ist dort ein Schieberegler; hier ein Zahlenfeld. $forms->save('solarcheck', 'Solarcheck', [ ['name' => 'schritt_haushalt', 'type' => 'step', 'label' => 'Ihr Haushalt', 'help' => 'Damit wir einschätzen, wie viel Strom Sie brauchen.'], ['name' => 'personen', 'type' => 'radio', 'label' => 'Wie viele Personen leben im Haushalt?', 'required' => true, 'options' => ['1 Person', '2 Personen', '3 – 4 Personen', '4+ Personen'], 'images' => $karten['personen']], ['name' => 'schritt_verbrauch', 'type' => 'step', 'label' => 'Verbrauch und Baujahr', 'help' => 'Schätzwerte genügen – die letzte Stromrechnung hilft.'], ['name' => 'jahresverbrauch', 'type' => 'number', 'label' => 'Jahresverbrauch in kWh ca.', 'required' => true, 'min' => 500, 'max' => 50000, 'unit' => 'kWh'], ['name' => 'baujahr_haus', 'type' => 'number', 'label' => 'Baujahr Haus', 'required' => true, 'min' => 1800, 'max' => 2026], ['name' => 'baujahr_elektro', 'type' => 'number', 'label' => 'Baujahr Elektroinstallation', 'required' => true, 'min' => 1800, 'max' => 2026], ['name' => 'schritt_dachform', 'type' => 'step', 'label' => 'Ihr Dach', 'help' => 'Die Form entscheidet über Montage und Ertrag.'], ['name' => 'dachform', 'type' => 'radio', 'label' => 'Welche Form hat Ihr Dach?', 'required' => true, 'options' => ['Flachdach', 'Satteldach', 'Pultdach', 'erweitert/versetzt', 'sonstiges'], 'images' => $karten['dachform']], ['name' => 'schritt_flaeche', 'type' => 'step', 'label' => 'Dachfläche'], ['name' => 'dachflaeche', 'type' => 'radio', 'label' => 'Wie groß ist Ihre Dachfläche ca.?', 'required' => true, 'options' => ['bis 50 m²', '50 m² – 100 m²', '100 m² – 200 m²', 'über 200 m²', 'nicht sicher'], 'images' => $karten['dachflaeche']], ['name' => 'schritt_eindeckung', 'type' => 'step', 'label' => 'Dacheindeckung'], ['name' => 'eindeckung', 'type' => 'radio', 'label' => 'Aus welchem Material besteht die Dacheindeckung?', 'required' => true, 'options' => ['Ziegel', 'Bitumen', 'Blech', 'Anderes'], 'images' => $karten['eindeckung']], ['name' => 'schritt_speicher', 'type' => 'step', 'label' => 'Stromspeicher'], ['name' => 'speicher', 'type' => 'radio', 'label' => 'Wünschen Sie einen Stromspeicher?', 'required' => true, 'options' => ['Ja', 'Nein'], 'images' => $karten['speicher']], ['name' => 'schritt_ort', 'type' => 'step', 'label' => 'Wo soll die Anlage verbaut werden?'], ['name' => 'strasse', 'type' => 'text', 'label' => 'Straße & Hausnummer', 'required' => true], ['name' => 'plz', 'type' => 'text', 'label' => 'PLZ', 'required' => true], ['name' => 'stadt', 'type' => 'text', 'label' => 'Stadt', 'required' => true], ['name' => 'schritt_kontakt', 'type' => 'step', 'label' => 'Wie können wir Sie kontaktieren?'], ['name' => 'name', 'type' => 'text', 'label' => 'Name', 'required' => true, 'help' => 'Vor- und Nachname'], ['name' => 'email', 'type' => 'email', 'label' => 'E-Mail', 'required' => true], ['name' => 'telefon', 'type' => 'phone', 'label' => 'Telefon', 'required' => true], ['name' => 'datenschutz', 'type' => 'consent', 'label' => 'Ich willige ein, dass meine Angaben gespeichert werden, damit meine Anfrage beantwortet werden kann.'], ], ['recipient' => $mail, 'subject' => 'Neuer Solarcheck über ebsolar.de', 'success' => 'Vielen Dank. Ihr Solarcheck ist angekommen – wir melden uns mit Ihrer persönlichen Kalkulation.', 'replyToField' => 'email', 'buttonLabel' => 'Solarcheck absenden', 'summary' => true], 0); echo "Formular angelegt: solarcheck\n"; // --------------------------------------------------------------------------------------------- // Kopfbereich: helle Leiste, darunter der weiße Kopf, der beim Scrollen oben bleibt // --------------------------------------------------------------------------------------------- $marke = static fn (string $id, string $breite, string $mobil) => $bild($id, $logo, $firma, [ 'desktop' => ['maxWidth' => $breite, 'margin' => $seiten('0px')], 'tablet' => ['maxWidth' => '225px'], 'mobile' => ['maxWidth' => $mobil], ], '/'); $kopf = [ 'schemaVersion' => $SCHEMA, 'title' => 'Kopfbereich', 'description' => 'Erscheint auf jeder Seite über dem Inhalt.', 'blocks' => [ $section('kopf-leiste', [ $zeile('kopf-leiste-zeile', [ $column('kopf-leiste-firma', [ $text('kopf-leiste-firma-text', 'Elektro Bernhard · Regenerative Energie GmbH', ['desktop' => ['fontSize' => '12px', 'letterSpacing' => '0.025em', 'lineHeight' => 1.6, 'color' => '#5f6568', 'margin' => ['bottom' => '0px']]]), ], [], 'hide-mobile'), $column('kopf-leiste-ort', [ $text('kopf-leiste-ort-text', 'Baden-Baden & Umgebung', [ 'desktop' => ['fontSize' => '12px', 'letterSpacing' => '0.025em', 'lineHeight' => 1.6, 'color' => '#5f6568', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '11px', 'margin' => ['left' => 'auto']], ]), ]), ], '20px', ['justifyContent' => 'space-between'], ['justifyContent' => 'flex-end']), ], [ 'desktop' => ['backgroundColor' => '@hell', 'color' => '#646a6d', 'padding' => ['top' => '7px', 'bottom' => '7px']], ]), $section('kopf-haupt', [ $row('kopf-haupt-zeile', [ $column('kopf-marke', [$marke('kopf-logo', '270px', '210px')], ['desktop' => ['flexShrink' => 0]]), $column('kopf-navigation', [ ['id' => 'kopf-menue', 'type' => 'menu', 'props' => ['slot' => 'header', 'layout' => 'horizontal', 'collapse' => 'mobile', 'burgerLabel' => 'both'], 'style' => ['desktop' => [ 'fontSize' => '14px', 'fontWeight' => '500', 'justifyContent' => 'flex-end', 'display' => 'flex', // Der Menü-Knopf im Zuschnitt der Vorlage: feiner Rahmen, kleine Rundung. 'burgerColor' => '@tinte', 'burgerBorderColor' => '@linie', 'burgerBorderRadius' => '5px', 'burgerFontSize' => '14px', ]], // Der letzte Eintrag ist der rote Knopf „Zum Solarcheck“. 'css' => "& .menu-list{gap:4px 29px;justify-content:flex-end;align-items:center}\n& .menu-list a{transition:color .18s}\n& .menu-list a:hover{color:#b52c2c;text-decoration:none}\n& .menu-list>li:last-child>a{background:#b52c2c;color:#ffffff;border-radius:6px;padding:12px 19px;font-weight:600}\n& .menu-list>li:last-child>a:hover{background:#952222;color:#ffffff}", ], ], ['desktop' => ['display' => 'flex', 'justifyContent' => 'flex-end', 'alignItems' => 'center']]), ], [], [ 'desktop' => ['display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'space-between', 'gap' => '28px', 'minHeight' => '99px', 'margin' => ['top' => '0px']], 'tablet' => ['minHeight' => '84px'], 'mobile' => ['minHeight' => '76px', 'gap' => '16px'], ]), ], [ 'desktop' => ['backgroundColor' => '#fffffffa', 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', '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', [$marke('fuss-logo', '235px', '235px')], ['desktop' => ['flexShrink' => 0]]), $column('fuss-spruch', [ $text('fuss-spruch-text', 'Solarenergie aus Baden-Baden.
Für die Region. Für morgen.', [ 'desktop' => ['fontSize' => '13px', 'color' => '@grau', 'lineHeight' => 1.7, 'margin' => ['bottom' => '0px', 'left' => '15px']], 'mobile' => ['margin' => ['left' => '0px']], ]), ], ['desktop' => ['margin' => ['right' => 'auto'], 'flexGrow' => 1], 'mobile' => ['order' => 2, 'flexBasis' => '100%', 'flexGrow' => 0]]), $column('fuss-sozial', [ ['id' => 'fuss-instagram', 'type' => 'social', 'props' => ['size' => 20, 'shape' => 'plain', 'layout' => 'horizontal', 'labels' => true, 'networks' => ['instagram']], 'style' => ['desktop' => ['fontSize' => '14px', 'fontWeight' => '500']], 'css' => '& a:hover{color:#b52c2c}'], ], ['mobile' => ['order' => 3]]), ], '30px', ['justifyContent' => 'space-between', 'padding' => ['top' => '54px', 'bottom' => '39px']], ['flexWrap' => 'wrap', 'alignItems' => 'flex-start', 'gap' => '22px', 'padding' => ['top' => '38px', 'bottom' => '29px']]), $zeile('fuss-unten', [ $column('fuss-recht', [ $text('fuss-copyright', '© 2026 '.$firma, ['desktop' => ['fontSize' => '12px', 'color' => '@grau', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '11px']]), ]), $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' => '24px']], 'css' => '& a:hover{color:#b52c2c}'], $text('fuss-agentur', 'Website by Kant-IT Solutions', ['desktop' => ['fontSize' => '12px', 'color' => '@grau', 'margin' => ['bottom' => '0px']]], '& a:hover{color:#b52c2c}'), ], '24px', ['flexWrap' => 'wrap'], ['rowGap' => '10px', 'columnGap' => '18px', 'justifyContent' => 'flex-start']), ], '30px', ['justifyContent' => 'space-between', 'alignItems' => 'center', 'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '21px', 'bottom' => '21px'], 'color' => '@grau'], ['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '12px', 'padding' => ['top' => '21px', 'bottom' => '21px']]), ], ['desktop' => ['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"; // --------------------------------------------------------------------------------------------- // Der Kontaktbereich als wiederverwendbare Sektion: Startseite, Projektübersicht und jeder // Projektbeitrag verweisen darauf. Eine Änderung erreicht so alle Seiten auf einmal. // --------------------------------------------------------------------------------------------- // Formulare im Zuschnitt der Vorlage: volle Breite statt der 640 px des Kerns, und zwei // Spalten dort, wo zwei kurze Felder nebeneinander passen (Name/E-Mail, PLZ/Stadt). Angesprochen // werden die Felder über ihr `data-label`, weil eigenes CSS kein :nth-child kennt. $formularCss = '& .block-form{max-width:none;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:20px;row-gap:18px} & .block-form>.form-field{grid-column:1/-1} & .block-form>.form-step{grid-column:1/-1} & .block-form>.cms-button{grid-column:1/-1;justify-self:start} & .block-form>.form-error-summary{grid-column:1/-1} & .block-form>.form-success{grid-column:1/-1} & .block-form>.form-missing{grid-column:1/-1} & .block-form>.form-progress{grid-column:1/-1} & .block-form>.form-step-nav{grid-column:1/-1} & .block-form>.form-summary{grid-column:1/-1} & .form-step{grid-template-columns:repeat(2,minmax(0,1fr));column-gap:20px;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} & .block-form>[data-label="Ihr Name"]{grid-column:span 1} & .block-form>[data-label="E-Mail-Adresse"]{grid-column:span 1} & .block-form>[data-label="Telefon"]{grid-column:span 1} & .form-step>[data-label="PLZ"]{grid-column:span 1} & .form-step>[data-label="Stadt"]{grid-column:span 1} & .form-step>[data-label="E-Mail"]{grid-column:span 1} & .form-step>[data-label="Telefon"]{grid-column:span 1} & .form-step>[data-label="Baujahr Haus"]{grid-column:span 1} & .form-step>[data-label="Baujahr Elektroinstallation"]{grid-column:span 1} & .form-field{align-content:start} & .form-field label{font-size:14px;font-weight:600;color:#252b30} & .form-field input{background:#ffffff;border:1px solid #dfe2e2;border-radius:6px;padding:13px 14px;font-size:16px;color:#252b30} & .form-field select{background:#ffffff;border:1px solid #dfe2e2;border-radius:6px;padding:13px 14px;font-size:16px;color:#252b30} & .form-field textarea{background:#ffffff;border:1px solid #dfe2e2;border-radius:6px;padding:13px 14px;font-size:16px;color:#252b30;min-height:130px} & .form-choice legend{font-size:14px;font-weight:600;color:#252b30} & .form-choice-grid{grid-template-columns:repeat(auto-fill,minmax(170px,1fr));gap:14px} & .form-choice-card label{border-color:#dfe2e2;border-radius:10px} & .field-note{font-size:13px;color:#5b6266;opacity:1} & .form-step-intro{color:#5b6266;opacity:1} & .form-progress-step{opacity:1;color:#5b6266} & .form-progress-step[aria-current]{color:#252b30} & .check-row label{font-weight:400;font-size:14px;line-height:1.6}'; $kontaktbereich = [ 'schemaVersion' => $SCHEMA, 'title' => 'Kontaktbereich', 'description' => 'Der Abschnitt „Was kann Ihr Dach?“ mit Solarcheck und Kontaktformular.', 'blocks' => [ $section('kontakt-abschnitt', [ $column('kontakt-karte', [ $zeile('kontakt-kopf', [ $column('kontakt-text', [ $kleinzeile('kontakt-label', 'Der nächste Schritt', '@rot', '13px'), $heading('kontakt-titel', 'Was kann Ihr Dach?', 2, $h2Stil), $text('kontakt-intro', 'Erzählen Sie uns von Ihrem Vorhaben. Über unseren Solarcheck erhalten wir die Grundlage für Ihre persönliche Kalkulation.', [ 'desktop' => ['color' => '@grau', 'fontSize' => '15px', 'lineHeight' => 1.7, 'maxWidth' => '520px', 'margin' => ['top' => '18px', 'bottom' => '0px']], 'mobile' => ['fontSize' => '16px'], ]), ], ['desktop' => ['flexGrow' => 1]]), $column('kontakt-aktionen', [ $button('kontakt-knopf', 'Zum Solarcheck', '/solarcheck', [ 'desktop' => ['minWidth' => '212px', 'justifyContent' => 'space-between', 'margin' => ['top' => '0px']], 'mobile' => ['width' => '100%', 'minWidth' => '0px'], ]), $textlink('kontakt-sprung', 'Direkt Kontakt aufnehmen', '#kontaktformular', 'arrow-right', ['desktop' => ['margin' => ['top' => '18px']]]), ], ['desktop' => ['flexShrink' => 0, 'display' => 'flex', 'flexDirection' => 'column', 'alignItems' => 'flex-start'], 'mobile' => ['margin' => ['top' => '25px']]]), ], '50px', ['alignItems' => 'center'], ['flexDirection' => 'column', 'alignItems' => 'stretch', 'gap' => '0px'], ['gap' => '30px']), $column('kontakt-formular', [ $zeile('kontakt-formular-kopf', [ $heading('kontakt-formular-titel', 'Kontakt aufnehmen', 3, $h3Stil), $text('kontakt-formular-pflicht', 'Felder mit * sind Pflichtfelder', ['desktop' => ['fontSize' => '13px', 'color' => '@grau', 'margin' => ['bottom' => '0px', 'left' => 'auto']]]), ], '20px', ['justifyContent' => 'space-between', 'margin' => ['bottom' => '24px']], ['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '6px']), ['id' => 'kontakt-formular-block', 'type' => 'form', 'props' => ['form' => 'kontakt'], 'css' => $formularCss], ], [ 'desktop' => ['backgroundColor' => '@papier', 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'borderRadius' => $ecken('10px'), 'padding' => $seiten('34px'), 'margin' => ['top' => '38px']], 'mobile' => ['padding' => $seiten('20px'), 'margin' => ['top' => '28px']], ], 'all', 'kontaktformular'), ], [ 'desktop' => ['backgroundColor' => '@hell', 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'borderRadius' => $ecken('12px'), 'padding' => ['top' => '47px', 'right' => '50px', 'bottom' => '47px', 'left' => '50px']], 'tablet' => ['padding' => $seiten('40px')], 'mobile' => ['padding' => ['top' => '29px', 'right' => '25px', 'bottom' => '29px', 'left' => '25px'], 'borderRadius' => $ecken('9px')], ]), ], [ 'desktop' => ['padding' => ['top' => '0px', 'bottom' => '78px']], 'mobile' => ['padding' => ['top' => '0px', 'bottom' => '47px']], ], 'kontakt'), ], ]; $partials->create('section', 'kontakt', 'Kontaktbereich', $kontaktbereich); echo "Wiederverwendbare Sektion angelegt: kontakt\n"; $kontaktref = ['id' => 'kontakt-sektion', 'type' => 'partialref', 'props' => ['partial' => 'kontakt']]; // --------------------------------------------------------------------------------------------- // Startseite // --------------------------------------------------------------------------------------------- $startseite = [ 'schemaVersion' => $SCHEMA, 'title' => 'EB Solar · Sonnenenergie aus Ihrer Region', 'description' => 'Photovoltaik von Elektro Bernhard in Baden-Baden. Individuelle Planung und professionelle Umsetzung Ihrer Solaranlage.', 'blocks' => [ // Hero: links der Text, rechts das Bild mit der Bildunterschrift darüber. $section('hero', [ $row('hero-zeile', [ $column('hero-text', [ $zeile('hero-label', [ $flaeche('hero-strich', ['desktop' => ['width' => '24px', 'height' => '2px', 'minHeight' => '2px', 'backgroundColor' => '@rot', 'margin' => $seiten('0px'), 'flexShrink' => 0]]), $text('hero-label-text', 'Solarenergie. Persönlich. Regional.', [ 'desktop' => ['fontSize' => '11px', 'fontWeight' => '600', 'letterSpacing' => '0.11em', 'textTransform' => 'uppercase', 'color' => '#5f656a', 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '10px'], ]), ], '10px', ['margin' => ['bottom' => '21px']], ['margin' => ['bottom' => '18px']]), $heading('hero-titel', 'Ihr Dach.
Ihr Strom.
Unsere Expertise.', 1, $h1Stil, '& em{color:#b52c2c;font-style:normal}'), $text('hero-intro', 'Machen Sie mehr aus der Sonne. Wir planen und installieren Ihre Photovoltaikanlage – passend zu Ihrem Zuhause und Ihrem Alltag.', [ 'desktop' => ['color' => '@grau', 'fontSize' => '16px', 'lineHeight' => 1.8, 'maxWidth' => '440px', 'margin' => ['top' => '25px', 'bottom' => '0px']], 'mobile' => ['margin' => ['top' => '21px', 'bottom' => '0px'], 'lineHeight' => 1.75], ]), $zeile('hero-aktionen', [ $button('hero-knopf', 'Solarprojekt starten', '/solarcheck', ['desktop' => ['margin' => ['top' => '0px']]]), $textlink('hero-sprung', 'Unsere Projekte ansehen', '#projekte'), ], '25px', ['flexWrap' => 'wrap', 'margin' => ['top' => '31px']], ['gap' => '16px', 'margin' => ['top' => '24px']]), $text('hero-notiz', 'In wenigen Schritten zu Ihrer individuellen Anfrage.', [ 'desktop' => ['fontSize' => '12px', 'color' => '@grau', 'margin' => ['top' => '14px', 'bottom' => '0px']], 'mobile' => ['fontSize' => '11px', 'margin' => ['top' => '12px', 'bottom' => '0px']], ]), ], ['desktop' => ['flexGrow' => 1, 'flexBasis' => '0', 'minWidth' => '0'], 'mobile' => ['flexGrow' => 0, 'flexBasis' => 'auto']]), $column('hero-bild', [ $bild('hero-foto', $hero, 'Photovoltaikanlage von Elektro Bernhard auf dem Dach eines Wohnhauses', [ 'desktop' => ['imageHeight' => '492px', 'objectFit' => 'cover', 'width' => '100%', 'margin' => $seiten('0px')], 'tablet' => ['imageHeight' => '465px'], 'mobile' => ['imageHeight' => '340px'], ]), // Die Bildunterschrift liegt als Spalte über dem Foto: eine Zeile darf im // Kern keine Fläche tragen (.block-row setzt background:transparent). $column('hero-unterschrift', [ $zeile('hero-unterschrift-zeile', [ $column('hero-marke-kreis', [ $symbol('hero-marke-pfeil', 'arrow-up-right', 20, ['desktop' => ['color' => '@rot']]), ], [ 'desktop' => ['width' => '36px', 'height' => '36px', 'flexShrink' => 0, 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'borderRadius' => $ecken('50%'), 'display' => 'flex', 'alignItems' => 'center', 'justifyContent' => 'center'], ], 'hide-tablet'), $column('hero-unterschrift-text', [ $text('hero-unterschrift-titel', 'Aus Sonnenlicht wird Unabhängigkeit.', ['desktop' => ['fontSize' => '14px', 'fontWeight' => '600', 'lineHeight' => 1.4, 'margin' => ['bottom' => '0px']], 'tablet' => ['fontSize' => '12px'], 'mobile' => ['fontSize' => '13px']]), $text('hero-unterschrift-klein', 'Photovoltaik von Elektro Bernhard', ['desktop' => ['fontSize' => '12px', 'color' => '@grau', 'lineHeight' => 1.4, 'margin' => ['top' => '3px', 'bottom' => '0px']]]), ]), ], '16px', [], ['gap' => '12px'], ['gap' => '10px']), ], [ 'desktop' => ['position' => 'absolute', 'bottom' => '20px', 'left' => '20px', 'right' => '20px', 'backgroundColor' => '#fffffff5', 'borderRadius' => $ecken('8px'), 'padding' => ['top' => '16px', 'right' => '19px', 'bottom' => '16px', 'left' => '19px']], 'tablet' => ['bottom' => '12px', 'left' => '12px', 'right' => '12px', 'padding' => $seiten('13px')], 'mobile' => ['bottom' => '15px', 'left' => '15px', 'right' => '15px', 'padding' => $seiten('14px')], ]), ], [ 'desktop' => ['position' => 'relative', 'overflow' => 'hidden', 'borderRadius' => $ecken('12px'), 'backgroundColor' => '#e4e8e9', 'flexGrow' => 1.06, 'flexBasis' => '0', 'minWidth' => '0'], 'mobile' => ['borderRadius' => $ecken('9px'), 'flexGrow' => 0, 'flexBasis' => 'auto'], ]), ], [], [ 'desktop' => ['display' => 'flex', 'alignItems' => 'center', 'gap' => '65px', 'margin' => ['top' => '0px']], 'tablet' => ['gap' => '35px'], 'mobile' => ['flexDirection' => 'column', 'gap' => '30px'], ]), ], [ 'desktop' => ['padding' => ['top' => '54px', 'bottom' => '47px']], 'tablet' => ['padding' => ['top' => '40px', 'bottom' => '40px']], 'mobile' => ['padding' => ['top' => '36px', 'bottom' => '28px']], ]), // Die vier Zusicherungen zwischen zwei Linien. $section('vorteile', [ $zeile('vorteile-zeile', array_map( static fn (array $paar): array => $zeile('vorteil-'.$paar[0], [ $symbol('vorteil-'.$paar[0].'-haken', 'check', 16, ['desktop' => ['color' => '@rot', 'flexShrink' => 0]]), $text('vorteil-'.$paar[0].'-text', $paar[1], ['desktop' => ['fontSize' => '14px', 'lineHeight' => 1.5, 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '12px']]), ], '11px', [], ['gap' => '7px']), [['1', 'Elektro-Meisterbetrieb'], ['2', 'Individuell geplant'], ['3', 'Professionell installiert'], ['4', 'Persönlich vor Ort']], ), '25px', ['justifyContent' => 'space-between', 'flexWrap' => 'wrap', 'color' => '@grau-tief'], ['display' => 'grid', 'gridColumns' => 2, 'gap' => '14px', 'alignItems' => 'center']), ], [ 'desktop' => ['borderWidth' => $obenUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '23px', 'bottom' => '23px']], 'mobile' => ['padding' => ['top' => '19px', 'bottom' => '19px']], ]), // Drei Schritte von der Idee bis aufs Dach. $abschnitt('leistungen-abschnitt', [ $zeile('leistungen-kopf', [ $column('leistungen-kopf-text', [ $kleinzeile('leistungen-label', 'Von der Idee bis aufs Dach', '@grau', '13px'), $heading('leistungen-titel', 'Eine Solarlösung, die zu Ihnen passt.', 2, $h2Stil), ], ['desktop' => ['flexGrow' => 1]]), $column('leistungen-kopf-notiz', [ $text('leistungen-notiz', 'Durchdacht geplant. Sorgfältig umgesetzt.
Mit einem Ansprechpartner an Ihrer Seite.', [ 'desktop' => ['fontSize' => '15px', 'color' => '@grau', 'lineHeight' => 1.7, 'margin' => ['bottom' => '0px']], 'tablet' => ['maxWidth' => '260px', 'margin' => ['top' => '16px', 'bottom' => '0px']], ]), ], ['desktop' => ['flexShrink' => 0]]), ], '32px', ['alignItems' => 'flex-end', 'justifyContent' => 'space-between', 'margin' => ['bottom' => '39px']], ['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '0px', 'margin' => ['bottom' => '28px']], ['alignItems' => 'flex-start', 'flexDirection' => 'column', 'gap' => '0px']), $row('leistungen-raster', array_map( static fn (array $l): array => $column('leistung-'.$l[0], [ $text('leistung-'.$l[0].'-nummer', $l[0], ['desktop' => ['fontSize' => '12px', 'fontWeight' => '600', 'color' => '@rot', 'lineHeight' => 1.5, 'margin' => ['bottom' => '23px']], 'mobile' => ['margin' => ['bottom' => '6px']]]), $heading('leistung-'.$l[0].'-titel', $l[1], 3, $h3Stil), $text('leistung-'.$l[0].'-text', $l[2], [ 'desktop' => ['fontSize' => '15px', 'color' => '@grau', 'lineHeight' => 1.8, 'maxWidth' => '345px', 'margin' => ['top' => '14px', 'bottom' => '0px']], 'mobile' => ['fontSize' => '16px', 'lineHeight' => 1.7, 'maxWidth' => 'none', 'margin' => ['top' => '12px', 'bottom' => '0px']], ]), ], [ 'desktop' => ['borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '25px']], 'mobile' => ['padding' => ['top' => '22px']], ]), [ ['01', 'Gut beraten & geplant', 'Wir betrachten Ihr Dach, Ihren Strombedarf und Ihre Wünsche. Daraus entsteht die Grundlage für Ihre Solaranlage.'], ['02', 'Individuell abgestimmt', 'Die passende Technik für Ihr Zuhause: Wir stimmen die Komponenten aufeinander und auf Ihre Anforderungen ab.'], ['03', 'Fachgerecht umgesetzt', 'Unser erfahrenes Team übernimmt die Installation. Sorgfältig, zuverlässig und mit Blick auf jedes Detail.'], ], ), [], [ 'desktop' => ['display' => 'grid', 'gridColumns' => 3, 'gap' => '40px', 'margin' => ['top' => '0px']], 'tablet' => ['gap' => '23px'], 'mobile' => ['gridColumns' => 1, 'gap' => '25px'], ]), ], [], 'leistungen'), // Über uns auf heller Fläche. $section('ueber-uns-abschnitt', [ $row('ueber-uns-zeile', [ $column('ueber-uns-kopf', [ $kleinzeile('ueber-uns-label', 'Elektro Bernhard', '@grau', '17px'), $heading('ueber-uns-titel', 'Gutes Handwerk.
Neue Energie.', 2, [ 'desktop' => [...$h2Stil['desktop'], 'fontSize' => '43px', 'lineHeight' => 1.16], 'tablet' => ['fontSize' => '35px'], 'mobile' => ['fontSize' => '35px'], ]), ], ['desktop' => ['flexGrow' => 1, 'flexBasis' => '0', 'minWidth' => '0'], 'mobile' => ['flexGrow' => 0, 'flexBasis' => 'auto']]), $column('ueber-uns-text', [ $text('ueber-uns-absatz-1', 'Wir kommen aus dem Elektrohandwerk. Mit der Elektro Bernhard Regenerative Energie GmbH haben wir unsere Leidenschaft für nachhaltige Energie zu unserem Schwerpunkt gemacht.', [ 'desktop' => ['fontSize' => '17px', 'lineHeight' => 1.8, 'maxWidth' => '600px', 'margin' => ['bottom' => '0px']], 'mobile' => ['fontSize' => '16px'], ]), $text('ueber-uns-absatz-2', 'Als Elektro-Meisterbetrieb in Baden-Baden verbinden wir fachliches Know-how mit persönlicher Beratung – für Solarprojekte hier in der Region.', [ 'desktop' => ['fontSize' => '15px', 'color' => '@grau', 'lineHeight' => 1.8, 'maxWidth' => '600px', 'margin' => ['top' => '14px', 'bottom' => '0px']], 'mobile' => ['fontSize' => '16px'], ]), $textlink('ueber-uns-sprung', 'Lernen wir uns kennen', '#kontaktformular', 'arrow-right', ['desktop' => ['margin' => ['top' => '23px']]]), ], ['desktop' => ['flexGrow' => 1.2, 'flexBasis' => '0', 'minWidth' => '0'], 'mobile' => ['flexGrow' => 0, 'flexBasis' => 'auto']]), ], [], [ 'desktop' => ['display' => 'flex', 'alignItems' => 'flex-start', 'gap' => '90px', 'margin' => ['top' => '0px']], 'tablet' => ['gap' => '40px'], 'mobile' => ['flexDirection' => 'column', 'gap' => '23px'], ]), ], [ 'desktop' => ['backgroundColor' => '@hell', 'padding' => ['top' => '68px', 'bottom' => '68px']], 'tablet' => ['padding' => ['top' => '50px', 'bottom' => '50px']], 'mobile' => ['padding' => ['top' => '42px', 'bottom' => '42px']], ], 'ueber-uns'), // Projekte: drei Beiträge aus der Kategorie „Projekte“ und die Herstellerleiste. $abschnitt('projekte-abschnitt', [ $zeile('projekte-kopf', [ $column('projekte-kopf-text', [ $kleinzeile('projekte-label', 'Einblicke in unsere Arbeit', '@grau', '13px'), $heading('projekte-titel', 'Sonnenenergie in der Praxis.', 2, $h2Stil), ], ['desktop' => ['flexGrow' => 1]]), $column('projekte-alle', [ $textlink('projekte-alle-link', 'Alle Projekte', '/projekte', 'arrow-up-right'), ], ['desktop' => ['flexShrink' => 0], 'mobile' => ['margin' => ['top' => '18px']]]), ], '32px', ['alignItems' => 'flex-end', 'justifyContent' => 'space-between', 'margin' => ['bottom' => '31px']], ['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '0px', 'margin' => ['bottom' => '28px']], ['alignItems' => 'flex-start', 'flexDirection' => 'column', 'gap' => '0px']), ['id' => 'projekte-liste', 'type' => 'postlist', 'props' => ['count' => 3, 'category' => 'projekte', 'layout' => 'karten', 'paginate' => false, 'showMeta' => false, 'showExcerpt' => false, 'showTerms' => false], 'style' => ['desktop' => ['margin' => ['top' => '0px']]], // Die Karte der Vorlage: randloses Bild mit Rundung, darunter der Titel. // Die Vorlage setzt über den Titel noch die Projektart. Die steht hier als // Schlagwort am Beitrag und nicht als Kategorie: „vorheriges/nächstes // Projekt“ folgt der ersten Kategorie, und die muss bei allen acht dieselbe // sein. Die Beitragsliste zeigt nur Kategorien, deshalb bleibt die Zeile weg. 'css' => "& .post-list{grid-template-columns:repeat(3,minmax(0,1fr));gap:25px}\n& .post-card{background:none;border:0;border-radius:0;overflow:visible;transform:none;transition:none;box-shadow:none} & .post-card:hover{transform:none;box-shadow:none}\n& .post-image{border-radius:8px;background:#f5f6f6}\n& .post-image img{aspect-ratio:1.5;transition:transform .35s}\n& .post-card:hover .post-image img{transform:scale(1.035)}\n& .post-view{display:none}\n& .post-card>.post-body{display:flex;flex-direction:column;gap:6px;padding:17px 0 0}\n& .post-terms{order:-1;margin:0;font-size:12px}\n& .post-terms a{color:#666d72;text-decoration:none}\n& .post-card h3{font-size:18px;font-weight:600;line-height:1.4;letter-spacing:-0.02em}\n& .post-card h3 a:hover{color:#b52c2c;text-decoration:none}", ], $column('hersteller', [ $text('hersteller-text', 'Wir setzen auf bewährte Technik.', ['desktop' => ['fontSize' => '13px', 'color' => '@grau', 'textAlign' => 'center', 'margin' => ['bottom' => '15px']]]), ['id' => 'hersteller-logos', 'type' => 'logos', 'children' => array_map( static fn (array $h): array => $bild('hersteller-'.pathinfo($h[0], PATHINFO_FILENAME), $medien[$h[0]], $h[1], ['desktop' => ['margin' => $seiten('0px')]]), $hersteller, ), 'style' => ['desktop' => ['gap' => '24px', 'margin' => ['top' => '0px']], 'mobile' => ['gap' => '16px']], 'css' => "& img{max-height:76px;width:auto;filter:grayscale(1);opacity:.66}\n& li{display:flex;justify-content:center}"], ], [ 'desktop' => ['borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '29px'], 'margin' => ['top' => '66px']], 'mobile' => ['padding' => ['top' => '24px'], 'margin' => ['top' => '40px']], ]), ], [], 'projekte'), $kontaktref, ], ]; // --------------------------------------------------------------------------------------------- // Seiten anlegen // --------------------------------------------------------------------------------------------- $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; }; $startseite = $einblenden($startseite, [ 'hero-label' => ['fade'], 'hero-titel' => ['rise', 'short'], 'hero-intro' => ['rise', 'medium'], 'hero-aktionen' => ['rise', 'long'], 'hero-notiz' => ['fade', 'long'], // Das Startbild blendet bewusst nicht ein: es ist das groesste Element ueber der Falz und // damit der LCP. Eine Einblendung schiebt seinen ersten Anstrich um fast eine Sekunde. 'vorteile-zeile' => ['fade'], 'leistungen-kopf' => ['rise'], 'leistung-01' => ['rise'], 'leistung-02' => ['rise', 'short'], 'leistung-03' => ['rise', 'medium'], 'ueber-uns-kopf' => ['rise'], 'ueber-uns-text' => ['rise', 'short'], 'projekte-kopf' => ['rise'], 'projekte-liste' => ['rise', 'short'], 'hersteller' => ['fade'], ]); $anlegen('home', $startseite, 'EB Solar · Sonnenenergie aus Ihrer Region', $hero['id']); // Die Projektübersicht. $uebersicht = [ 'schemaVersion' => $SCHEMA, 'title' => 'Unsere Projekte', 'description' => 'Photovoltaikprojekte von Elektro Bernhard: Bilder und Einblicke in unsere Installationen in der Region.', 'blocks' => [ $section('projektuebersicht', [ $column('projektuebersicht-kopf', [ $kleinzeile('projektuebersicht-label', 'Elektro Bernhard · Referenzen', '@grau', '15px'), $heading('projektuebersicht-titel', 'Unsere Projekte.', 1, $h1Stil), $text('projektuebersicht-intro', 'Von der Dachanlage bis zur Terrassenmontage: Einblicke in unsere Arbeit und die Solarlösungen unserer Kunden.', [ 'desktop' => ['fontSize' => '16px', 'color' => '@grau', 'lineHeight' => 1.8, 'maxWidth' => '580px', 'margin' => ['top' => '23px', 'bottom' => '0px']], 'mobile' => ['margin' => ['top' => '18px', 'bottom' => '0px']], ]), ], [ 'desktop' => ['padding' => ['top' => '20px', 'bottom' => '40px']], 'mobile' => ['padding' => ['top' => '12px', 'bottom' => '28px']], ]), $heading('projektuebersicht-raster-titel', 'Alle Projekte', 2, [], 'position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap'), ['id' => 'projektuebersicht-liste', 'type' => 'postlist', 'props' => ['count' => 12, 'category' => 'projekte', 'layout' => 'karten', 'paginate' => true, 'showMeta' => false, 'showExcerpt' => false, 'showTerms' => false], 'css' => "& .post-list{grid-template-columns:repeat(2,minmax(0,1fr));gap:44px 32px}\n& .post-card{background:none;border:0;border-radius:0;overflow:visible;transform:none;transition:none;box-shadow:none} & .post-card:hover{transform:none;box-shadow:none}\n& .post-image{border-radius:8px;background:#f5f6f6}\n& .post-image img{aspect-ratio:1.65;transition:transform .35s}\n& .post-card:hover .post-image img{transform:scale(1.035)}\n& .post-view{display:none}\n& .post-card>.post-body{display:flex;flex-direction:column;gap:7px;padding:19px 0 0}\n& .post-terms{order:-1;margin:0;font-size:12px}\n& .post-terms a{color:#666d72;text-decoration:none}\n& .post-card h3{font-size:22px;font-weight:600;line-height:1.35;letter-spacing:-0.02em}\n& .post-card h3 a:hover{color:#b52c2c;text-decoration:none}", ], ], [ 'desktop' => ['padding' => ['top' => '45px', 'bottom' => '70px']], 'mobile' => ['padding' => ['top' => '28px', 'bottom' => '42px']], ]), $kontaktref, ], ]; $anlegen('projekte', $uebersicht, 'Unsere Projekte · EB Solar'); // Der Solarcheck als eigene Seite mit dem achtschrittigen Formular. $solarcheck = [ 'schemaVersion' => $SCHEMA, 'title' => 'Solarcheck', 'description' => 'In wenigen Minuten zur persönlichen Kalkulation: Beantworten Sie acht kurze Fragen zu Haushalt, Dach und Speicher – wir melden uns bei Ihnen.', 'blocks' => [ $section('solarcheck-kopf', [ $column('solarcheck-kopf-inhalt', [ $kleinzeile('solarcheck-label', 'In etwa drei Minuten', '@rot', '15px'), $heading('solarcheck-titel', 'Solarcheck', 1, $h1Stil), $text('solarcheck-intro', 'Ihr eigenes Kraftwerk auf dem Dach: Sie produzieren Ihren eigenen Strom und sparen Kosten. Beantworten Sie ein paar Fragen zu Ihrem Haushalt und Ihrem Dach – anschließend melden wir uns bei Ihnen.', [ 'desktop' => ['fontSize' => '17px', 'color' => '@grau', 'lineHeight' => 1.8, 'maxWidth' => '620px', 'margin' => ['top' => '23px', 'bottom' => '0px']], 'mobile' => ['fontSize' => '16px', 'margin' => ['top' => '18px', 'bottom' => '0px']], ]), ]), ], [ 'desktop' => ['backgroundColor' => '@hell', 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '64px', 'bottom' => '60px']], 'mobile' => ['padding' => ['top' => '40px', 'bottom' => '36px']], ]), $abschnitt('solarcheck-formular', [ $column('solarcheck-karte', [ ['id' => 'solarcheck-block', 'type' => 'form', 'props' => ['form' => 'solarcheck'], 'css' => $formularCss], ], [ // Volle Breite: die Karte nimmt den ganzen Inhaltsbereich ein. 'desktop' => ['backgroundColor' => '@papier', 'borderWidth' => $seiten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'borderRadius' => $ecken('12px'), 'padding' => ['top' => '42px', 'right' => '50px', 'bottom' => '42px', 'left' => '50px']], 'tablet' => ['padding' => $seiten('34px')], 'mobile' => ['padding' => $seiten('20px'), 'borderRadius' => $ecken('9px')], ]), ]), ], ]; $anlegen('solarcheck', $solarcheck, 'Solarcheck · EB Solar'); /** * Eine Rechtsseite: Kleinzeile, Titel, Vorspann auf heller Fläche, dann Absätze mit * Zwischenüberschriften in zwei Spalten. */ $rechtsseite = static function (string $praefix, string $label, string $titel, string $vorspann, array $absaetze) use ($section, $abschnitt, $row, $column, $kleinzeile, $heading, $text, $nurOben, $nurUnten, $h1Stil, $linkCss): array { $kopf = $section($praefix.'-kopf', [ $kleinzeile($praefix.'-label', $label, '@grau', '15px'), $heading($praefix.'-h1', $titel, 1, [ 'desktop' => [...$h1Stil['desktop'], 'fontSize' => '52px'], 'tablet' => ['fontSize' => '42px'], 'mobile' => ['fontSize' => '36px'], ]), $text($praefix.'-vorspann', $vorspann, [ 'desktop' => ['maxWidth' => '640px', 'fontSize' => '17px', 'lineHeight' => 1.7, 'color' => '@grau', 'margin' => ['top' => '22px', 'bottom' => '0px']], 'mobile' => ['fontSize' => '16px', 'margin' => ['top' => '16px', 'bottom' => '0px']], ]), ], [ 'desktop' => ['backgroundColor' => '@hell', 'borderWidth' => $nurUnten('1px'), 'borderStyle' => 'solid', 'borderColor' => '@linie', 'padding' => ['top' => '64px', 'bottom' => '60px']], 'tablet' => ['padding' => ['top' => '52px', 'bottom' => '48px']], 'mobile' => ['padding' => ['top' => '40px', 'bottom' => '36px']], ]); $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' => ['fontSize' => '16px', 'fontWeight' => '600', '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' => '15px', 'lineHeight' => 1.8, 'color' => '@grau', 'margin' => ['bottom' => '0px']], ], $linkCss), ], ['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' => '26px', 'bottom' => '26px']], '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' => '50px', 'bottom' => '90px']], 'mobile' => ['padding' => ['top' => '28px', 'bottom' => '56px']]]); return [$kopf, $inhalt]; }; $anlegen('impressum', [ 'schemaVersion' => $SCHEMA, 'title' => 'Impressum', 'description' => 'Anbieterangaben der Elektro Bernhard Regenerative Energie GmbH in Baden-Baden.', 'blocks' => $rechtsseite('impressum', 'Rechtliche Angaben', 'Impressum', 'Angaben gemäß § 5 DDG und § 18 Abs. 2 MStV.', [ ['Anbieterin', $firma.'
(unabhängig von der Elektro Bernhard GmbH)
Ooser Hauptstraße 51
76532 Baden-Baden'], ['Vertreten durch', 'Ridvan Kantarci, Geschäftsführer'], ['Registereintrag', 'Handelsregister: HRB 741275
NOCH ZU PRÜFEN: Registergericht eintragen (im Impressum der laufenden Website nicht genannt).'], ['Kontakt', "Telefon: $telefon
E-Mail: $mailInfo
Photovoltaik: $mail"], ['Umsatzsteuer', 'Umsatzsteuer-Identifikationsnummer nach § 27a UStG: DE 815 618 082'], ['Verantwortlich für den Inhalt', 'Ridvan Kantarci, 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.'], ['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. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Bei Bekanntwerden von Rechtsverletzungen entfernen wir solche Links 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. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet.'], ['Bildnachweis', 'Logo, Herstellerlogos, Titelbild und Projektfotos stammen von der bisherigen Website www.ebsolar.de und werden hier für den Redesign-Entwurf verwendet. NOCH ZU PRÜFEN: Urheber der Fotos und die jeweiligen Nutzungsrechte eintragen.'], ]), ], 'Impressum | '.$firma); $anlegen('datenschutz', [ 'schemaVersion' => $SCHEMA, 'title' => 'Datenschutz', 'description' => 'Wie die Elektro Bernhard Regenerative Energie GmbH Daten beim Besuch dieser Website und bei einer Anfrage verarbeitet.', 'blocks' => $rechtsseite('datenschutz', 'Ihre Daten', 'Datenschutzhinweise', 'Hier erfahren Sie, welche Daten beim Besuch dieser Website und bei Ihrer Anfrage verarbeitet werden – und welche Rechte Sie haben.', [ ['1. Verantwortliche', "$firma, Ooser Hauptstraße 51, 76532 Baden-Baden, vertreten durch Ridvan Kantarci. Telefon $telefon, E-Mail $mailInfo.

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.

NOCH ZU PRÜFEN: Hostinganbieter benennen, Auftragsverarbeitungsvertrag abschließen, Verarbeitungsländer und die 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. Der Link zu unserem Instagram-Profil führt zu Meta Platforms Ireland Ltd.; erst mit dem Klick verlassen Sie unsere Website.'], ['4. Solarcheck und Kontaktanfrage', 'Wenn Sie den Solarcheck ausfüllen oder uns über das Kontaktformular schreiben, verarbeiten wir Ihre Angaben zu Haushalt, Gebäude, Dach und Kontakt, um Ihre Anfrage zu beantworten und Ihnen ein Angebot zu unterbreiten. Rechtsgrundlage ist Art. 6 Abs. 1 lit. b DSGVO (Anbahnung eines Vertrags), sonst Art. 6 Abs. 1 lit. f DSGVO. Die Formulare speichern den Zeitpunkt Ihrer Einwilligung und schützen 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 Beantwortung nicht mehr nötig sind und keine gesetzliche Aufbewahrungspflicht entgegensteht. Entsteht aus Ihrer Anfrage ein Auftrag, gelten für die dann anfallenden Unterlagen die handels- und steuerrechtlichen Fristen.'], ['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 oder es ist für die Ausführung Ihres Auftrags nötig, etwa gegenüber dem Netzbetreiber bei der Anmeldung Ihrer Anlage.

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 $mailInfo."], ]), ], 'Datenschutz | '.$firma); $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', '@grau', '21px'), $heading('fehler-titel', 'Diese Seite
haben wir nicht.', 1, $h1Stil, '& em{color:#b52c2c;font-style:normal}'), $text('fehler-text', 'Vielleicht hat sich ein Tippfehler in die Adresse geschlichen. Über die Startseite finden Sie zurück – oder sehen Sie sich unsere Projekte an.', [ 'desktop' => ['maxWidth' => '480px', 'color' => '@grau', 'fontSize' => '17px', 'lineHeight' => 1.8, 'margin' => ['top' => '25px', 'bottom' => '28px']], ]), $zeile('fehler-aktionen', [ $button('fehler-knopf', 'Zur Startseite', '/', ['desktop' => ['margin' => ['top' => '0px']]], false), $textlink('fehler-sprung', 'Unsere Projekte ansehen', '/projekte'), ], '25px', ['flexWrap' => 'wrap'], ['gap' => '16px']), ], ['desktop' => ['padding' => ['top' => '110px', 'bottom' => '130px']], 'mobile' => ['padding' => ['top' => '60px', 'bottom' => '70px']]]), ], ]); $db->executeStatement('UPDATE page SET robots = ? WHERE id = ?', ['noindex', $fehlerId]); // --------------------------------------------------------------------------------------------- // Projektbeiträge: Kopf, Galerie mit Bildbetrachter, Nachbarn, Kontaktbereich // --------------------------------------------------------------------------------------------- $projektDaten = json_decode((string) file_get_contents($assets.'/projects.json'), true, 512, JSON_THROW_ON_ERROR); // Die Reihenfolge der Übersicht der Vorlage. Der jüngste Beitrag steht oben, deshalb zählen die // Daten rückwärts: die Beitragsliste sortiert nach Datum. $reihenfolge = ['mayr-melnhof-karton-ag', 'rheinstetten', 'michelbach', 'au-am-rhein', 'loffenau', 'ruelzheim', 'kuppenheim', 'rheinstetten-dachanlage']; $titel = [ 'mayr-melnhof-karton-ag' => 'Mayr-Melnhof Karton AG', 'rheinstetten' => 'Rheinstetten', 'michelbach' => 'Michelbach', 'au-am-rhein' => 'Au am Rhein', 'loffenau' => 'Loffenau', 'ruelzheim' => 'Rülzheim', 'kuppenheim' => 'Kuppenheim', 'rheinstetten-dachanlage' => 'Rheinstetten Dachanlage', ]; $kategorieLabel = ['rheinstetten' => 'Terrassenmontage · SolarEdge']; $nachSlug = []; foreach ($projektDaten as $projekt) { $nachSlug[$projekt['slug']] = $projekt; } $nummer = 0; foreach ($reihenfolge as $slug) { if (!isset($nachSlug[$slug])) { fwrite(STDERR, "Projekt fehlt in projects.json: $slug\n"); exit(1); } $projekt = $nachSlug[$slug]; ++$nummer; $p = 'projekt-'.$nummer; $name = $titel[$slug]; $label = $kategorieLabel[$slug] ?? 'Photovoltaik'; $bilder = []; foreach ($projekt['images'] as $i => $eintrag) { $datei = 'projects/'.basename((string) $eintrag['src']); $bilder[] = $hochladen($datei, $name.' – Projektansicht '.($i + 1), $name.' '.($i + 1)); } $anzahl = count($bilder); $beschreibung = sprintf('%s: %s von Elektro Bernhard – %d %s aus der Umsetzung.', $name, $label, $anzahl, 1 === $anzahl ? 'Projektbild' : 'Projektbilder'); $bloecke = [ $section($p.'-kopf', [ $textlink($p.'-zurueck', 'Alle Projekte', '/projekte', 'arrow-left', ['desktop' => ['margin' => ['bottom' => '30px']]], true, '@grau'), $zeile($p.'-titelzeile', [ $column($p.'-titel-text', [ $kleinzeile($p.'-label', $label, '@grau', '15px'), $heading($p.'-h1', $name, 1, [ 'desktop' => [...$h1Stil['desktop'], 'fontSize' => '56px'], 'tablet' => ['fontSize' => '42px'], 'mobile' => ['fontSize' => '36px'], ]), ], ['desktop' => ['flexGrow' => 1]]), $column($p.'-anzahl', [ $text($p.'-anzahl-text', $anzahl.' '.(1 === $anzahl ? 'Projektbild' : 'Projektbilder'), [ 'desktop' => ['fontSize' => '14px', 'color' => '@grau', 'margin' => ['bottom' => '6px']], 'mobile' => ['margin' => ['top' => '17px', 'bottom' => '0px']], ]), ], ['desktop' => ['flexShrink' => 0]]), ], '24px', ['alignItems' => 'flex-end', 'justifyContent' => 'space-between', 'margin' => ['bottom' => '40px']], ['flexDirection' => 'column', 'alignItems' => 'flex-start', 'gap' => '0px', 'margin' => ['bottom' => '28px']], ['alignItems' => 'flex-start']), ['id' => $p.'-galerie', 'type' => 'gallery', 'props' => ['lightbox' => true], 'children' => array_map( static fn (array $foto, int $i): array => $bild($p.'-bild-'.($i + 1), $foto, $name.' – Projektansicht '.($i + 1), [ 'desktop' => ['width' => '100%', 'imageRatio' => '16/9', 'objectFit' => 'cover', 'imageRadius' => $ecken('10px'), 'margin' => $seiten('0px')], ], null, '', true), $bilder, array_keys($bilder), ), 'style' => [ 'desktop' => ['gridColumns' => 2, 'rowGap' => '33px', 'columnGap' => '28px', 'margin' => ['top' => '0px']], 'mobile' => ['gridColumns' => 1, 'gap' => '23px'], ], 'css' => '& > :first-child{grid-column:1/-1}'], ], [ 'desktop' => ['padding' => ['top' => '45px', 'bottom' => '55px']], 'mobile' => ['padding' => ['top' => '28px', 'bottom' => '34px']], ]), $section($p.'-nachbarn', [ ['id' => $p.'-navigation', 'type' => 'postnav', 'props' => ['previousLabel' => 'Vorheriges Projekt', 'nextLabel' => 'Nächstes Projekt'], 'css' => "& .post-nav{border-top:1px solid #e6e8e8;border-bottom:1px solid #e6e8e8;padding:27px 0;gap:35px}\n& .post-nav-link{border:0;border-radius:0;background:none;padding:0}\n& .post-nav-link:hover{color:#b52c2c}\n& .post-nav-label{font-size:12px;color:#666d72}\n& .post-nav-title{font-size:16px;font-weight:500}"], ], [ 'desktop' => ['padding' => ['top' => '0px', 'bottom' => '70px']], 'mobile' => ['padding' => ['top' => '0px', 'bottom' => '42px']], ]), $kontaktref, ]; $id = $pages->createPage($slug, null, 'post', [ 'schemaVersion' => $SCHEMA, 'title' => $name, 'description' => $beschreibung, 'blocks' => $bloecke, ]); $seite = $db->fetchAssociative('SELECT version FROM page WHERE id = ?', [$id]); // Die Daten zählen rückwärts, damit die Beitragsliste dieselbe Reihenfolge zeigt wie die // Übersicht der Vorlage. Echte Ausführungsdaten liegen dem Paket nicht bei. $posts->saveMetadata($id, [ 'excerpt' => '', 'author' => '', 'publishedAt' => sprintf('2026-%02d-01', 9 - $nummer + 1 > 0 ? 9 - $nummer + 1 : 1), 'featuredMediaId' => $bilder[0]['id'], 'ogMediaId' => $bilder[0]['id'], 'seoTitle' => $name.' · Projekt von EB Solar', 'robots' => 'index', ], ['Projekte'], [$label], (int) $seite['version']); $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 'Projekt angelegt: /'.$slug.' ('.$anzahl." Bilder)\n"; } // --------------------------------------------------------------------------------------------- // Menü, Einstellungen, Konto // --------------------------------------------------------------------------------------------- $sprung = static fn (string $label, string $anchor): array => ['label' => $label, 'type' => 'page', 'pageId' => $erstellt['home'], 'anchor' => $anchor, 'children' => []]; $seite = static fn (string $label, string $slug): array => ['label' => $label, 'type' => 'page', 'pageId' => $erstellt[$slug], 'anchor' => '', 'children' => []]; $menus->save('header', 'Hauptnavigation', [ $sprung('Leistungen', 'leistungen'), $sprung('Über uns', 'ueber-uns'), $seite('Projekte', 'projekte'), $sprung('Kontakt', 'kontakt'), $seite('Zum Solarcheck', 'solarcheck'), ], 0); echo "Menü angelegt\n"; $settings->assignNotFoundPage($fehlerId, ''); $settings->save([ 'legalName' => $firma, 'description' => 'Photovoltaik von Elektro Bernhard in Baden-Baden: individuelle Planung und professionelle Umsetzung Ihrer Solaranlage. Elektro-Meisterbetrieb für Solarprojekte in der Region.', 'phone' => $telefon, 'email' => $mail, 'street' => 'Ooser Hauptstraße 51', 'postalCode' => '76532', 'city' => 'Baden-Baden', 'country' => 'DE', 'social' => ['instagram' => $instagram], 'areaServed' => implode("\n", ['Baden-Baden', 'Rastatt', 'Gaggenau', 'Kuppenheim', 'Rheinstetten', 'Bühl', 'Karlsruhe', 'Mittelbaden']), 'localBusiness' => true, 'llmsTxt' => true, 'faviconMediaId' => $favicon['id'], 'stickyHeader' => true, 'headerOverlay' => false, 'motion' => 'always', 'homePath' => 'home', 'imprintPath' => 'impressum', 'privacyPath' => 'datenschutz', // Die Vorlage 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";