Markenseiten Rotpunkt und Febrü aus der Zulieferung vom 31.08.

Umsetzung der beiden finalen Webseitentexte aus dev/2026-08-28/.
Die angekündigte Aufgabenliste umsetzung-03-09-2026.md war leer, Grundlage
sind daher die Umsetzungshinweise in den PDFs selbst.

- Neue Felder in cms_brands: sections, widget_label, cta_title, cta_text.
  Einleitung und Leistungskacheln reichten für diese Texte nicht aus.
- Die als Vertiefung gelieferten Blöcke werden aufklappbar dargestellt,
  mit aria-expanded und aria-controls. Bewusst x-transition statt
  x-collapse, weil das Collapse-Plugin hier nicht registriert ist.
- Kein zweiter CTA-Button: das schwebende Terminanfrage-Widget trägt auf
  der Markenseite die Beschriftung der Marke und liest den Kontext aus
  der Route, da es im Layout und nicht je Seite eingebunden ist.
- Footer bekommt unten Platz und lange Beschriftungen brechen um, damit
  das Widget auf dem Smartphone nichts verdeckt.
- Abschnitte sind im Admin vollständig pflegbar, inklusive Sortierung.
- Interne Quellenhinweise der PDFs sind nicht übernommen; ein Test prüft,
  dass sie nicht im Ausgabe-HTML stehen.
- BrandPageTextSeeder fasst nur die Textfelder an, Bilder, Galerie und
  Leistungen bleiben unangetastet.

Vor der Veröffentlichung: Herstellerfreigabe einholen und die Aussage zur
erhaltenen Waldfläche nur mit ausdrücklicher Bestätigung verwenden. Beides
steht in der Deployment-Checkliste.

854 Tests grün (3150 Assertions), Pint sauber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-03 08:52:36 +00:00
parent 6d53243ea6
commit 4ef3a4d8f1
13 changed files with 1143 additions and 69 deletions

View file

@ -58,6 +58,71 @@
</section>
@endif
{{-- 2b. Abschnitte der Markenseite. Die als Vertiefung gelieferten Blöcke sind aufklappbar,
damit die Seite schnell erfassbar bleibt, ohne dass Inhalt verloren geht. --}}
@if (!empty($brand['sections']))
<section class="pb-16">
<div class="container-narrow space-y-14">
@foreach ($brand['sections'] as $index => $section)
<div wire:key="section-{{ $index }}">
@if (!empty($section['title']))
<h2 class="text-2xl lg:text-3xl font-medium">{{ $section['title'] }}</h2>
@endif
@foreach ($section['paragraphs'] as $i => $paragraph)
<p class="text-muted-foreground leading-relaxed mt-4">{{ $paragraph }}</p>
@endforeach
@if (!empty($section['quote']))
<figure class="mt-8 border-l-4 border-secondary pl-6">
<blockquote class="text-large leading-relaxed italic">„{{ $section['quote'] }}"</blockquote>
@if (!empty($section['quote_author']))
<figcaption class="text-sm text-muted-foreground mt-3">{{ $section['quote_author'] }}</figcaption>
@endif
</figure>
@endif
@if (!empty($section['detail']))
<div class="mt-6 card-elevated rounded-2xl overflow-hidden"
x-data="{ open: false }">
<button type="button" x-on:click="open = !open"
x-bind:aria-expanded="open.toString()"
aria-controls="detail-{{ $index }}"
class="flex w-full items-center justify-between gap-4 p-5 text-left cursor-pointer hover:bg-muted/30 transition-colors">
<span class="font-medium">{{ $section['detail']['title'] }}</span>
<svg class="h-5 w-5 shrink-0 text-secondary transition-transform duration-200"
x-bind:class="open ? 'rotate-180' : ''" fill="none" stroke="currentColor"
stroke-width="2" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7" />
</svg>
</button>
{{-- x-transition statt x-collapse: das Collapse-Plugin ist in diesem
Projekt nicht registriert, dieselbe Bewegung wie im FAQ-Akkordeon. --}}
<div id="detail-{{ $index }}" x-show="open" x-cloak
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 -translate-y-2"
x-transition:enter-end="opacity-100 translate-y-0"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-end="opacity-0 -translate-y-2">
<div class="px-5 pb-5 border-t border-border/60 pt-4">
@foreach ($section['detail']['paragraphs'] as $paragraph)
<p class="text-muted-foreground leading-relaxed mt-3 first:mt-0">{{ $paragraph }}</p>
@endforeach
</div>
</div>
</div>
@endif
@if (!empty($section['claim']))
<p class="text-large text-secondary font-medium mt-6">{{ $section['claim'] }}</p>
@endif
</div>
@endforeach
</div>
</section>
@endif
{{-- 3. Leistungen --}}
@if (!empty($brand['services']))
<section class="section-padding bg-accent">
@ -215,8 +280,12 @@
{{-- 5. CTA --}}
<section class="section-padding bg-secondary text-secondary-foreground">
<div class="container-narrow text-center spacing-section">
<h2 class="text-section-title text-white">{{ __('ui.brand_cta_title', ['name' => $brand['name']]) }}</h2>
<p class="text-large max-w-2xl mx-auto leading-relaxed">{{ __('ui.brand_cta_text') }}</p>
<h2 class="text-section-title text-white">
{{ $brand['cta_title'] ?: __('ui.brand_cta_title', ['name' => $brand['name']]) }}
</h2>
<p class="text-large max-w-2xl mx-auto leading-relaxed">
{{ $brand['cta_text'] ?: __('ui.brand_cta_text') }}
</p>
<div class="pt-4 flex flex-wrap items-center justify-center gap-4">
<a href="{{ route('contact') }}" class="btn-primary-accent px-8 py-4 text-lg">
@ -229,7 +298,6 @@
</a>
@endif
</div>
{{-- Der WhatsApp-Button kommt mit AP3, sobald die Nummer bestätigt ist. --}}
</div>
</section>

View file

@ -162,8 +162,9 @@
<div class="flex items-center gap-3">
<button type="button" x-on:click="open = !open" x-bind:aria-expanded="open.toString()"
aria-controls="appointment-widget-title"
class="btn-secondary-accent flex items-center gap-2 rounded-full px-5 py-3 shadow-lg cursor-pointer">
<svg class="h-5 w-5" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"
class="btn-secondary-accent flex items-center gap-2 rounded-full px-5 py-3 shadow-lg cursor-pointer
max-w-[calc(100vw-2rem)] text-left leading-tight">
<svg class="h-5 w-5 shrink-0" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"
aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5" />

View file

@ -1,4 +1,6 @@
<footer class="section-dark section-padding">
{{-- pb-28 auf kleinen Schirmen: darunter schwebt das Terminanfrage-Widget, das sonst die
letzten Zeilen des Footers verdecken wuerde. --}}
<footer class="section-dark section-padding pb-28 sm:pb-16">
<div class="container-padding">
{{-- Main Content --}}
<div class="text-center spacing-section">