diff --git a/README.md b/README.md index c0a9d82..1798974 100644 --- a/README.md +++ b/README.md @@ -387,7 +387,7 @@ Die Landingpage „KANTARCI Immobilien – Mietanfrage“ (KANTARCI Immobilien G ### Achte lokale Referenzwebsite: Kantarci Markt (Vorab-Seite zur Neueröffnung) -Die Seite „Kantarci Markt – Neueröffnung“ (Wilhelm-Drapp-Straße, Baden-Baden) liegt in `var/kantarci-markt.db` mit Medien in `var/kantarci-markt-media`; die Startkonfiguration `kantarci-markt` in `.claude/launch.json` nutzt Port 8100, `kantarci-markt-prod` denselben Bestand im Produktionsmodus auf Port 8101 zum Messen. `sh scripts/reset-kantarci-markt.sh [Bilderordner]` (Standard `var/kantarci-markt-assets` mit `kantarci-markt-logo.png`, `market.jpg` und `vegetables.jpg` aus dem gelieferten Paket) schneidet das Logo zu und rastert Seitensymbol und Stempel (`node scripts/build-kantarci-markt-assets.mjs`), wirft die Installation weg, baut mit `scripts/seed-kantarci-markt.php` Gestaltung, Kopf- und Fußbereich, Startseite, Impressum, Datenschutz, 404 und Einstellungen auf und holt DM Sans auf den eigenen Server (`cms:fonts:fetch`, ersetzt in `fonts.css` nur die Regeln für DM Sans). Gemessen mit `MSYS_NO_PATHCONV=1 CMS_URL=http://127.0.0.1:8101 CMS_PATHS=/,/impressum,/datenschutz CMS_AUDIT_DIR=reports/lighthouse-kantarci-markt node scripts/audit.mjs` (in Git Bash verhindert `MSYS_NO_PATHCONV`, dass `/impressum` zu einem Windows-Pfad wird). Die bewussten Abweichungen vom Paket stehen im Kopf des Skripts. +Die Seite „Kantarci Markt – Neueröffnung“ (Wilhelm-Drapp-Straße, Baden-Baden) liegt in `var/kantarci-markt.db` mit Medien in `var/kantarci-markt-media`; die Startkonfiguration `kantarci-markt` in `.claude/launch.json` nutzt Port 8100, `kantarci-markt-prod` denselben Bestand im Produktionsmodus auf Port 8101 zum Messen. `sh scripts/reset-kantarci-markt.sh [Bilderordner]` (Standard `var/kantarci-markt-assets` mit `kantarci-markt-logo.png`, `market.jpg` und `vegetables.jpg` aus dem gelieferten Paket) schneidet das Logo zu und rastert Seitensymbol und Stempel (`node scripts/build-kantarci-markt-assets.mjs`), wirft die Installation weg, baut mit `scripts/seed-kantarci-markt.php` Gestaltung, Kopf- und Fußbereich, Startseite, Impressum, Datenschutz, 404 und Einstellungen auf und holt DM Sans auf den eigenen Server (`cms:fonts:fetch`, ersetzt in `fonts.css` nur die Regeln für DM Sans). Gemessen mit `MSYS_NO_PATHCONV=1 CMS_URL=http://127.0.0.1:8101 CMS_PATHS=/,/impressum,/datenschutz CMS_AUDIT_DIR=reports/lighthouse-kantarci-markt node scripts/audit.mjs` (in Git Bash verhindert `MSYS_NO_PATHCONV`, dass `/impressum` zu einem Windows-Pfad wird). Die bewussten Abweichungen vom Paket stehen im Kopf des Skripts. Die Demo der Eröffnungsseite liegt unter `/eroeffnung` (noindex) und bettet die Angebote aus `app.kantarci-markt.de` ein; die App erlaubt das nur von `localhost`, deshalb misst man sie mit `CMS_HOST_RULES="MAP localhost 127.0.0.1" CMS_URL=http://localhost:8101 CMS_PATHS=/eroeffnung`. Zusätzliche Prüfung mit den lokalen Kant-IT-Inhalten: `node scripts/admin-quality-check.mjs` (PHP über `PHP_BINARY` wählbar). Das Skript benötigt `var/kantit.db` und `var/kantit-media`, erstellt eine eigene Datenbankkopie und ein zufälliges Testkonto darin; es ändert die Pilotdatenbank nicht. Screenshots und Bericht landen in `reports/admin-quality/`. diff --git a/docs/STATUS.md b/docs/STATUS.md index 67a3e5f..2eed8b4 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,5 +1,23 @@ # Projektstatus +## Kantarci Markt: Demo der Eröffnungsseite mit Angeboten aus der App (24.09.2026) + +**Anlass:** Umut: „Setze mal die demoseite um wie du es dir vorstellst ich überlasse dir alles. Links und logo zu playstore und ios app, wilhelm drapp str. in baden baden, Rest Platzhalter“. Die Angebote kommen aus der Web-Ansicht seiner App: `https://app.kantarci-markt.de/embed/angebote.php?ton=dunkel`, dazu sein Skript für die Höhe. + +**Neu (nur Inhalt, kein Kern):** `/eroeffnung` im Seed-Skript, veröffentlicht, aber `noindex` und nirgends verlinkt. Aufbau: Einstieg „Jetzt geöffnet“ mit zwei Wegen (Angebote, Anfahrt) und Öffnungszeiten, Angebote als eingebettete App-Ansicht, App-Bereich mit Knöpfen für App Store und Google Play, das Versprechen, Besuch (Adresse mit „Route planen“, Öffnungszeiten, Kontakt), Abschluss. Zur Eröffnung soll dieser Inhalt als geplanter Entwurf auf die Startseite, `/eroeffnung` fällt dann weg. + +- **Einwilligung:** keine nötig. `Consent::granted()` lässt jede Gruppe durch, solange die Website keine Einwilligungsleiste hat (bei Kantarci Markt aus). Meine frühere Aussage, der Baustein verlange immer eine Einwilligung, war falsch. Eine Option „Eigener Dienst“ braucht es erst, wenn die Seite einmal eine Leiste bekommt. +- **Höhe:** Umuts Skript hängt als eigenes Skript am Baustein. Es lief zu spät: Die App meldet ihre Höhe beim Laden (gemessen 332 ms), das Skript lief als `defer`-Datei erst kurz vor DOMContentLoaded (402 ms), danach meldet die App nur noch Änderungen. Jetzt fragt das Skript nach dem Start per `km-angebote-height-request` nach und lädt den Rahmen als Rückfall nach 600 ms einmal neu, falls keine Meldung kam. Die App kennt die Nachfrage noch nicht, deshalb greift derzeit der Rückfall (zweiter Abruf der Web-Ansicht). +- **Hintergrund:** Die Web-Ansicht ist für hellen Grund gebaut (dunkle Überschrift, weiße Karten), `ton=dunkel` und `ton=hell` liefern dieselbe Seite. Der Angebotsbereich steht deshalb auf Sand statt auf Waldgrün. `& iframe{border-radius:0;background:transparent}` am Baustein nimmt Rundung und Tönung aus `site.css`. +- **Platzhalter:** Öffnungszeiten (Mo–Sa 8–20 Uhr, So geschlossen), „Telefon folgt“, Store-Links auf die Suche nach „Kantarci Markt“ im jeweiligen Store, Store-Knöpfe als eigene schwarze Knöpfe ohne die Logos von Apple und Google. Alles oben im Abschnitt des Seed-Skripts. +- **`scripts/audit.mjs`** kennt `CMS_HOST_RULES` (etwa `MAP localhost 127.0.0.1`): Die App erlaubt das Einbetten nur von `kantarci-markt.de` und `http://localhost:*`, der Server lauscht auf 127.0.0.1. + +**Geprüft:** Lighthouse im Produktionsmodus über `localhost:8101`, Rahmen geladen: `/eroeffnung` mobil 96 / Desktop 100, Barrierefreiheit, Best Practices und agentisches Browsing 100. SEO 69 nur wegen des gewollten `noindex` (`is-crawlable`), keine Konsolenfehler. Die Startseite blieb bei 98 / 100 und überall sonst 100. Im Browser: Rahmen 448 px (Desktop) bzw. 971 px (375 px) hoch, kein Scrollbalken, kein waagerechtes Scrollen. Sicherheitsrichtlinie der Seite: `frame-src https://app.kantarci-markt.de`, `script-src 'self'`. + +**Nachtrag, gleicher Tag – Popup statt Link (App-Repository `C:\Django Project\Supermarkt`):** Umut: Angebote ohne Bild wirkten durchsichtig, „Unsere Angebote“ war weiß, und statt eines Links soll ein Popup die Einzelheiten hier zeigen. Die App-Ansicht hatte inzwischen `ton`, `titel`, `app`, `bg` bekommen; `ton=dunkel` machte die Überschrift weiß. Eingebunden ist sie jetzt mit `?titel=0&app=0` (eigene Überschrift der Seite, kein Knopf nach außen). In der App (`public/embed/angebote.php`, noch nicht committet, nicht ausgeliefert): Karten sind `
` mit einem Knopf im Namen statt ``, ein natives `` zeigt Bild, Kategorie, Name, Gültigkeit, Beschreibung und Preise auf Höhe der Karte (das iframe ist so hoch wie sein Inhalt) und meldet per `km-angebote-focus`, wo es steht. Die Ansicht beantwortet `km-angebote-height-request`, Angebote ohne Bild haben zartes Grün statt Creme, und ohne Datenbank gibt es die leere Ansicht statt eines Fatal Errors (`count(null)`). `embed/angebote.js` und das Skript im Seed kennen Nachfrage und Hinscrollen. Geprüft mit der lokalen App-Fassung (Anfragen im Browser umgeleitet, Beispieldaten im lokalen Cache): keine Links mehr, Popup im sichtbaren Bereich (auch nach Scrollen), Fokus auf „Schließen“ und zurück zur Karte, Esc und Klick auf den Rand schließen, axe ohne Befund, nur ein Abruf der Ansicht (kein Rückfall-Neuladen). Live wirkt es erst nach Push und App-Update auf dem Server. + +**Offen, von Umut:** echte Store-Adressen (und auf Wunsch die offiziellen Badges), Öffnungszeiten, Telefon, Eröffnungsdatum, Impressumsangaben. App-Änderungen committen, pushen und auf dem Server aktualisieren. + ## Fassung 1.23.0: achte Installation „Kantarci Markt – Neueröffnung“, content im eigenen CSS, Schriften ergänzt statt überschrieben (23.09.2026) **Anlass:** Umut: „erstellst du bitte bald verfügbar site nach dem prinzip kannst gerne dazu implementieren“ – dazu das Paket `Kantarci-Markt-Landingpage.zip` (statische Seite: Logo-Kopf, Hero „Frische bekommt eine neue Adresse.“ mit Marktfoto, Tomatenkarte und Stempel, Versprechen auf Waldgrün, Abschluss „Frische. Qualität. Faire Preise.“, Fuß). diff --git a/scripts/audit.mjs b/scripts/audit.mjs index 242f163..5c77d2e 100644 --- a/scripts/audit.mjs +++ b/scripts/audit.mjs @@ -18,7 +18,9 @@ const portProbe = net.createServer(); await new Promise(resolve => portProbe.listen(0, '127.0.0.1', resolve)); const port = portProbe.address().port; await new Promise(resolve => portProbe.close(resolve)); -const chrome = await chromium.launch({ args: [`--remote-debugging-port=${port}`] }); +// CMS_HOST_RULES, etwa "MAP localhost 127.0.0.1": für eingebettete Seiten, die das Einbetten nur +// von localhost erlauben, während der Server auf 127.0.0.1 lauscht (Kantarci-Markt-App). +const chrome = await chromium.launch({ args: [`--remote-debugging-port=${port}`, ...(process.env.CMS_HOST_RULES ? [`--host-resolver-rules=${process.env.CMS_HOST_RULES}`] : [])] }); const summary = []; try { for (const path of paths) { diff --git a/scripts/seed-kantarci-markt.php b/scripts/seed-kantarci-markt.php index db4cd55..1b1d1f6 100644 --- a/scripts/seed-kantarci-markt.php +++ b/scripts/seed-kantarci-markt.php @@ -518,6 +518,274 @@ $startseite = $einblenden($startseite, [ $anlegen('home', $startseite, 'Kantarci Markt – Neueröffnung in Baden-Baden', $medien['markt']['id']); +// --------------------------------------------------------------------------------------------- +// Eröffnungsseite (Demo, 24.09.2026) +// --------------------------------------------------------------------------------------------- +// +// So sieht die Startseite aus, wenn der Markt geöffnet hat. Sie liegt vorerst unter +// /eroeffnung, ist nicht verlinkt und steht auf noindex: Umut soll sie ansehen können, ohne dass +// die Vorab-Seite verschwindet. Zur Eröffnung wandert ihr Inhalt als Entwurf auf die Startseite +// (geplante Veröffentlichung), /eroeffnung wird danach gelöscht. +// +// Die Angebote kommen aus der Web-Ansicht der eigenen App (app.kantarci-markt.de). Die erlaubt +// das Einbetten selbst (frame-ancestors) und meldet ihre Höhe per postMessage. Das Skript am +// Baustein ist Umuts Gegenstück dazu (24.09.2026). Eine Einwilligung braucht es nicht, solange die +// Website keine Einwilligungsleiste hat: Consent::granted() lässt dann jede Gruppe durch. +// +// Platzhalter, bis Umut die echten Werte schickt: Öffnungszeiten, Telefon, Store-Adressen (die +// führen vorerst auf die Suche im jeweiligen Store) und die Store-Badges (eigene Knöpfe statt der +// offiziellen Grafiken von Apple und Google). + +// Ohne eigene Überschrift und ohne Knopf zur App: Die Seite hat ihre Überschrift, und Umut will keine Links nach +// außen (24.09.2026). Ein Klick auf ein Angebot öffnet ein Popup in der Ansicht. Heller Ton, weil der Abschnitt Sand ist. +$angeboteUrl = 'https://app.kantarci-markt.de/embed/angebote.php?titel=0&app=0'; +$appStoreUrl = 'https://apps.apple.com/de/search?term=Kantarci%20Markt'; +$playStoreUrl = 'https://play.google.com/store/search?q=Kantarci%20Markt&c=apps'; +$oeffnungszeiten = [['Montag bis Freitag', '8:00 – 20:00 Uhr'], ['Samstag', '8:00 – 20:00 Uhr'], ['Sonntag und Feiertage', 'geschlossen']]; +$routeUrl = 'https://www.google.com/maps/dir/?api=1&destination='.rawurlencode($strasse.', '.$plz.' '.$ort); + +// Umuts Gegenstück aus der App, um zwei Dinge ergänzt. Erstens läuft es als eigene Datei (defer) +// und damit oft erst, nachdem die App ihre Höhe schon einmal gemeldet hat - danach meldet sie nur +// noch Änderungen. Deshalb fragt es nach dem Start nach ('km-angebote-height-request'); antwortet +// die App nicht, lädt es den Rahmen als Rückfall einmal neu. Zweitens meldet die App beim Öffnen +// eines Angebots, wo ihr Popup steht ('km-angebote-focus'); liegt das außerhalb des Sichtbaren, +// scrollt die Seite dorthin. +$hoehenSkript = <<<'JS' +(function () { + var gemeldet = false; + function passend(f) { + try { + var src = new URL(f.src, location.href); + return src.pathname.indexOf('/embed/angebote') !== -1 ? src : null; + } catch (e) { return null; } + } + window.addEventListener('message', function (ev) { + if (!ev.data || (ev.data.type !== 'km-angebote-height' && ev.data.type !== 'km-angebote-focus')) return; + var frames = document.querySelectorAll('iframe'); + for (var i = 0; i < frames.length; i++) { + var f = frames[i]; + if (f.contentWindow !== ev.source) continue; + var src = passend(f); + if (!src || src.origin !== ev.origin) return; + if (ev.data.type === 'km-angebote-focus') { + var oben = f.getBoundingClientRect().top + Math.max(0, Math.min(20000, Number(ev.data.top) || 0)); + var hoehe = Math.max(0, Math.min(20000, Number(ev.data.height) || 0)); + if (oben < 16 || oben + Math.min(hoehe, window.innerHeight - 32) > window.innerHeight - 16) { + var still = window.matchMedia('(prefers-reduced-motion: reduce)').matches; + window.scrollTo({ top: window.scrollY + oben - 24, behavior: still ? 'auto' : 'smooth' }); + } + return; + } + var h = Math.max(150, Math.min(20000, Number(ev.data.height) || 0)); + f.style.height = (h + 2) + 'px'; + f.style.minHeight = '0'; + f.style.background = 'transparent'; + f.setAttribute('scrolling', 'no'); + gemeldet = true; + return; + } + }); + var frames = document.querySelectorAll('iframe'); + for (var i = 0; i < frames.length; i++) { + (function (f) { + var src = passend(f); + if (!src) return; + f.contentWindow.postMessage({ type: 'km-angebote-height-request' }, src.origin); + setTimeout(function () { if (!gemeldet) f.src = f.src; }, 600); + })(frames[i]); + } +})(); +JS; + +/** Ändert Bausteine eines Baums nach ID, ohne ihn neu zu schreiben. */ +$aendern = static function (array $block, array $plan) use (&$aendern): array { + if (isset($plan[$block['id'] ?? ''])) { + $block = $plan[$block['id']]($block); + } + foreach ($block['children'] ?? [] as $i => $kind) { + $block['children'][$i] = $aendern($kind, $plan); + } + + return $block; +}; +$setzeText = static fn (string $inhalt): Closure => static function (array $b) use ($inhalt): array { + $b['props']['text'] = $inhalt; + + return $b; +}; + +/** Ein Store-Knopf: schwarz, zweizeilig, wie die Badges der Stores, aber ohne deren Logos. */ +$storeKnopf = static fn (string $id, string $oben, string $unten, string $href): array => $text($id, + ''.$oben.'
'.$unten.'
', + ['desktop' => ['margin' => ['bottom' => '0px']]], + "& a{display:inline-block;min-width:176px;padding:9px 20px 10px;border-radius:9px;background:#000;border:1px solid #a6a6a6;color:#fff;font-size:12px;line-height:1.25;text-decoration:none}\n" + ."& a:hover{background:#123b26;border-color:#d2b978}\n" + ."& a:focus-visible{outline:3px solid #ad893b;outline-offset:3px}\n" + .'& strong{display:block;font-size:21px;font-weight:500;letter-spacing:-0.01em}'); + +$abschnittTitel = static fn (string $id, string $klein, string $titel, string $farbeKlein, string $farbeTitel, string $strich = '@gold', string $farbeBetont = '#8c6b27') => [ + $kleinzeile($id.'-label', $klein, $farbeKlein, $strich), + $heading($id.'-h2', $titel, 2, [ + 'desktop' => ['fontSize' => '64px', 'fontWeight' => '400', 'lineHeight' => 1.06, 'letterSpacing' => '-0.035em', 'color' => $farbeTitel, 'margin' => ['bottom' => '22px']], + 'tablet' => ['fontSize' => '52px'], + 'mobile' => ['fontSize' => '40px'], + ], '& em{font-style:italic;color:'.$farbeBetont.'}'), +]; + +$eroeffnung = [ + 'schemaVersion' => $SCHEMA, + 'title' => 'Jetzt geöffnet', + 'description' => 'Kantarci Markt in der Wilhelm-Drapp-Straße in Baden-Baden: frische Produkte, echte Vielfalt, faire Preise und jede Woche neue Angebote.', + 'blocks' => [ + // 1 – Hero: wie die Vorab-Seite, aber mit Öffnungszeiten und zwei Wegen weiter. + $aendern($startseite['blocks'][0], [ + 'hero-label-text' => $setzeText('Jetzt geöffnet'), + 'hero-titel' => $setzeText('Frische hat jetzt
eine Adresse.'), + 'hero-intro' => $setzeText('Sorgfältig ausgewählte Produkte, echte Vielfalt und faire Preise – ab sofort in der Wilhelm-Drapp-Straße in Baden-Baden.'), + 'hero-termin-icon' => static function (array $b): array { + $b['props']['icon'] = 'clock'; + + return $b; + }, + 'hero-termin-klein' => $setzeText('Heute geöffnet'), + 'hero-termin-satz' => $setzeText($oeffnungszeiten[0][1]), + 'hero-adresse' => $setzeText('So finden Sie uns
'.$strasse.'
'.$plz.' '.$ort), + 'hero-markt' => static function (array $b): array { + $b['props']['caption'] = 'Frische Vielfalt, jeden Tag.'; + + return $b; + }, + 'hero-text' => static function (array $b) use ($zeile, $button): array { + // Die beiden Wege stehen zwischen Einleitung und Öffnungszeiten. + array_splice($b['children'], 3, 0, [$zeile('hero-wege', [ + $button('hero-angebote', 'Aktuelle Angebote', '#angebote'), + ['id' => 'hero-besuch', 'type' => 'button', 'props' => ['text' => 'Anfahrt & Öffnungszeiten', 'href' => '#besuch'], + 'css' => "background:transparent;color:#123b26;box-shadow:inset 0 0 0 1px #123b26\n& span{display:none}\n&:hover{background:#123b26;color:#fff}"], + ], '14px', ['flexWrap' => 'wrap', 'margin' => ['top' => '0px', 'bottom' => '30px']])]); + + return $b; + }, + ]), + + // 2 – Angebote aus der App, auf Sand: Die Web-Ansicht ist für hellen Grund gestaltet + // (dunkle Überschrift, weiße Karten); `ton` ändert daran derzeit nichts (24.09.2026). + $section('angebote-bereich', [ + $row('angebote-kopf', [ + $column('angebote-kopf-titel', $abschnittTitel('angebote', 'Diese Woche', 'Aktuelle
Angebote.', '@gold-text', '@forst')), + $column('angebote-kopf-text', [ + $text('angebote-intro', 'Direkt aus unserer App – jede Woche neu. Die Angebote gelten im Markt, solange der Vorrat reicht.', [ + 'desktop' => ['maxWidth' => '420px', 'fontSize' => '17px', 'lineHeight' => 1.65, 'color' => '@text', 'margin' => ['bottom' => '0px']], + ]), + ], ['desktop' => ['alignSelf' => 'flex-end']]), + ], ['desktop' => ['columns' => 'two'], 'tablet' => ['columns' => 'one'], 'mobile' => ['columns' => 'one']], [ + 'desktop' => ['gap' => '40px', 'alignItems' => 'flex-end', 'margin' => ['top' => '0px', 'bottom' => '44px']], + 'mobile' => ['gap' => '8px', 'margin' => ['bottom' => '32px']], + ]), + ['id' => 'angebote-app', 'type' => 'embed', + 'props' => ['group' => 'external', 'title' => 'Aktuelle Angebote von Kantarci Markt', 'provider' => 'Kantarci-Markt-App', 'url' => $angeboteUrl, 'hint' => '', 'height' => 900], + // Die Höhe setzt das Skript, sobald die App sie meldet; bis dahin gelten die 900 px. + 'css' => '& iframe{border-radius:0;background:transparent}', + 'script' => $hoehenSkript], + ], [ + 'desktop' => ['backgroundColor' => '@sand', 'padding' => ['top' => '110px', 'bottom' => '110px'], 'borderWidth' => ['top' => '1px', 'right' => '0px', 'bottom' => '1px', 'left' => '0px'], 'borderStyle' => 'solid', 'borderColor' => '@gold'], + 'tablet' => ['padding' => ['top' => '84px', 'bottom' => '84px']], + 'mobile' => ['padding' => ['top' => '64px', 'bottom' => '64px']], + ], 'angebote'), + + // 3 – Die App: Text und Store-Knöpfe links, die Bildmarke als App-Symbol rechts. + $section('app-bereich', [ + $row('app-zeile', [ + $column('app-text', [ + ...$abschnittTitel('app', 'Die Kantarci-Markt-App', 'Angebote immer
in der Tasche.', '@gold-text', '@forst'), + $text('app-intro', 'Mit unserer App haben Sie die aktuellen Angebote jederzeit dabei – auch unterwegs und vor dem Einkauf.', [ + 'desktop' => ['maxWidth' => '520px', 'fontSize' => '18px', 'lineHeight' => 1.65, 'color' => '@text', 'margin' => ['bottom' => '30px']], + 'mobile' => ['fontSize' => '17px'], + ]), + $zeile('app-stores', [ + $storeKnopf('app-ios', 'Laden im', 'App Store', $appStoreUrl), + $storeKnopf('app-android', 'Jetzt bei', 'Google Play', $playStoreUrl), + ], '14px', ['flexWrap' => 'wrap']), + ]), + $column('app-symbol', [ + $bild('app-symbol-bild', $medien['symbol'], 'App-Symbol von Kantarci Markt', [ + 'desktop' => ['maxWidth' => '200px', 'width' => '200px', 'margin' => $seiten('0px'), 'rotate' => -6, + 'shadowY' => '24px', 'shadowBlur' => '50px', 'shadowColor' => '#062f2440', 'borderRadius' => $rund('44px'), 'overflow' => 'hidden'], + 'mobile' => ['maxWidth' => '140px', 'width' => '140px', 'borderRadius' => $rund('31px')], + ], null, '& img{display:block;width:100%;height:auto}'), + ], ['desktop' => ['display' => 'flex', 'justifyContent' => 'center', 'alignItems' => 'center']]), + ], ['desktop' => ['columns' => 'two'], 'tablet' => ['columns' => 'one'], 'mobile' => ['columns' => 'one']], [ + 'desktop' => ['gap' => '60px', 'alignItems' => 'center', 'margin' => ['top' => '0px'], 'columnWidths' => '3 2'], + 'mobile' => ['gap' => '44px'], + ]), + ], [ + 'desktop' => ['backgroundColor' => '@creme', 'padding' => ['top' => '110px', 'bottom' => '110px']], + 'tablet' => ['padding' => ['top' => '84px', 'bottom' => '84px']], + 'mobile' => ['padding' => ['top' => '64px', 'bottom' => '72px']], + ], 'app'), + + // 4 – Das Versprechen, wie auf der Vorab-Seite. + $startseite['blocks'][1], + + // 5 – Besuch: Adresse mit Route, Öffnungszeiten, Kontakt. + $section('besuch-bereich', [ + $row('besuch-kopf', [ + $column('besuch-titel', $abschnittTitel('besuch', 'Besuchen Sie uns', 'Wir freuen uns
auf Sie.', '@gold-text', '@forst')), + ], [], ['desktop' => ['margin' => ['top' => '0px', 'bottom' => '40px']]]), + $row('besuch-spalten', [ + $column('besuch-adresse', [ + $heading('besuch-adresse-h3', 'Adresse', 3, ['desktop' => ['fontFamily' => 'body', 'fontSize' => '14px', 'fontWeight' => '700', 'letterSpacing' => '0.18em', 'textTransform' => 'uppercase', 'color' => '@gold-text', 'margin' => ['bottom' => '14px']]]), + $text('besuch-adresse-text', $name.'
'.$strasse.'
'.$plz.' '.$ort, ['desktop' => ['fontSize' => '18px', 'lineHeight' => 1.6, 'color' => '@forst', 'margin' => ['bottom' => '22px']]]), + ['id' => 'besuch-route', 'type' => 'button', 'props' => ['text' => 'Route planen', 'href' => $routeUrl, 'newTab' => true], 'css' => '& span{display:none}'], + ]), + $column('besuch-zeiten', [ + $heading('besuch-zeiten-h3', 'Öffnungszeiten', 3, ['desktop' => ['fontFamily' => 'body', 'fontSize' => '14px', 'fontWeight' => '700', 'letterSpacing' => '0.18em', 'textTransform' => 'uppercase', 'color' => '@gold-text', 'margin' => ['bottom' => '14px']]]), + $text('besuch-zeiten-text', implode('
', array_map(static fn (array $z): string => ''.$z[0].''.$z[1], $oeffnungszeiten)), [ + 'desktop' => ['fontSize' => '17px', 'lineHeight' => 1.6, 'color' => '@text', 'margin' => ['bottom' => '0px']], + ], "& strong{display:block;margin-top:12px;color:#123b26;font-weight:700}\n& strong:first-child{margin-top:0}"), + ]), + $column('besuch-kontakt', [ + $heading('besuch-kontakt-h3', 'Kontakt', 3, ['desktop' => ['fontFamily' => 'body', 'fontSize' => '14px', 'fontWeight' => '700', 'letterSpacing' => '0.18em', 'textTransform' => 'uppercase', 'color' => '@gold-text', 'margin' => ['bottom' => '14px']]]), + $text('besuch-kontakt-text', 'Telefon folgt
E-Mail: '.$mail.'', [ + 'desktop' => ['fontSize' => '17px', 'lineHeight' => 1.9, 'color' => '@text', 'margin' => ['bottom' => '0px']], + ], $linkCss), + ]), + ], ['desktop' => ['columns' => 'three'], 'tablet' => ['columns' => 'one'], 'mobile' => ['columns' => 'one']], [ + 'desktop' => ['gap' => '48px', 'alignItems' => 'flex-start', 'margin' => ['top' => '0px'], 'borderWidth' => $nurOben('1px'), 'borderStyle' => 'solid', 'borderColor' => '#e4dcc8', 'padding' => ['top' => '36px']], + 'mobile' => ['gap' => '36px'], + ]), + ], [ + 'desktop' => ['backgroundColor' => '@creme', 'padding' => ['top' => '110px', 'bottom' => '110px']], + 'tablet' => ['padding' => ['top' => '84px', 'bottom' => '84px']], + 'mobile' => ['padding' => ['top' => '64px', 'bottom' => '72px']], + ], 'besuch'), + + // 6 – Abschluss wie auf der Vorab-Seite, mit neuem Gruß. + $aendern($startseite['blocks'][2], [ + 'abschluss-gruss-text' => $setzeText('Danke, dass Sie da sind.'), + 'abschluss-gruss-satz' => $setzeText('Bis gleich im Kantarci Markt.'), + ]), + ], +]; + +$eroeffnung = $einblenden($eroeffnung, [ + 'hero-label' => ['fade'], + 'hero-titel' => ['rise', 'short'], + 'hero-intro' => ['rise', 'medium'], + 'hero-wege' => ['rise', 'long'], + 'hero-termin' => ['rise', 'long'], + 'hero-adresse' => ['fade', 'long'], + 'app-text' => ['rise'], + 'app-symbol' => ['rise', 'short'], + 'versprechen-intro' => ['rise'], + 'versprechen-liste' => ['rise', 'short'], + 'abschluss-spruch' => ['rise'], + 'abschluss-gruss' => ['rise', 'short'], +]); + +$eroeffnungId = $anlegen('eroeffnung', $eroeffnung, 'Kantarci Markt – frisch, vielfältig, fair in Baden-Baden', $medien['markt']['id']); +// Nur zum Ansehen: nicht in Suchmaschinen, nicht in der Sitemap, nirgends verlinkt. +$db->executeStatement('UPDATE page SET robots = ? WHERE id = ?', ['noindex', $eroeffnungId]); + /** Eine Rechtsseite: Kleinzeile, Titel und Vorspann auf Waldgrün, dann Absätze mit Zwischenüberschriften. */ $rechtsseite = static function (string $praefix, string $label, string $titel, string $vorspann, array $absaetze) use ($section, $row, $column, $kleinzeile, $heading, $text, $nurOben, $linkCss): array { $kopf = $section($praefix.'-kopf', [