b2in/resources/views/livewire/web/components/sections/brand-detail.blade.php
Kevin Adametz a52347845e Abstimmungsrunde 03.09.: CABINET-Seite, korrigierte Markentexte, Galerie-Hinweise, Abschluss-CTA
Setzt die vier Punkte aus dev/2026-08-28/umsetzung-03-09-2026.md um.

1. Markenseiten
   - Rotpunkt und Febrü auf die korrigierten Fassungen (*_FINAL.md) gebracht:
     bei Rotpunkt sind die beiden Nachhaltigkeitsabschnitte zusammengefuehrt,
     bei Febrü behaelt der Blaue-Engel-Satz seinen Produktbezug ohne den
     erklaerenden Zusatz.
   - CABINET nach demselben Muster neu verfasst: 8 Abschnitte, 3 Vertiefungen,
     eigener Abschlussblock und eigene Widget-Beschriftung. Bewusst ohne Zahlen,
     Zertifizierungen oder Herstelleraussagen – dafuer gibt es keine Quelle.
     Braucht Marcels Freigabe.
   - Febrü-Claim korrigiert: „Arbeiten, wo man gerne bleibt.“

2. „BEISPIEL“-Vermerk aus den Bildunterschriften entfernt. Weil die Demobilder
   auf Vorschau und Live bereits in der Datenbank stehen, entfernt
   BrandGalleryCaptionNormalizerSeeder ihn dort in-place – nur der Text, Bild,
   Kategorie und Reihenfolge bleiben unberuehrt.

3. Hinweise zu Visualisierungen neu formuliert (Markengalerie und /referenzen).
   Die Trennung bleibt, sie laeuft nur nicht mehr ueber eine Verneinung.

4. Abschluss-CTA der Markenseiten ueberarbeitet: weiche Lichtflaechen statt
   Vollton, gesetzter Hauptbutton mit Pfeil, Herstellerseite als gleichwertiger
   Outline-Button, Showroom als Fusszeile.

Ein Pruefskript hat jeden veroeffentlichten Satz gegen die abgestimmten
Markdown-Fassungen gehalten: keine Abweichung.

Tests: 859 gruen (3180 Assertions), Pint sauber, npm run build:b2in gebaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 09:17:16 +00:00

357 lines
22 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{{-- Detailseiten-Vorlage laut umsetzung-inhalte.md §5:
Header (Logo + Claim + Stimmungsbild) → Einleitung → Leistungen → Galerie → CTA --}}
<article>
{{-- 1. Header --}}
<section class="section-padding">
<div class="container-padding">
<a href="{{ route('marken') }}"
class="inline-flex items-center gap-2 text-muted-foreground hover:text-foreground transition-colors mb-8">
<span aria-hidden="true">&larr;</span> {{ __('ui.all_brands') }}
</a>
<div class="grid lg:grid-cols-2 gap-12 items-center">
<div>
@if (!empty($brand['logo']))
<img src="{{ theme_image_url($brand['logo']) }}" alt="{{ $brand['name'] }}"
class="h-10 w-auto mb-6 object-contain object-left" />
@endif
<h1 class="text-section-title">{{ $brand['name'] }}</h1>
<p class="text-large text-secondary font-medium mt-2">{{ $brand['tagline'] }}</p>
@if (!empty($brand['showroom']))
<p class="text-muted-foreground mt-4 inline-flex items-center gap-2">
<svg class="w-5 h-5 text-secondary" fill="none" stroke="currentColor" stroke-width="1.5"
viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round"
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
</svg>
{{ $brand['showroom'] }}
</p>
@endif
</div>
<div>
@if (!empty($brand['image']))
<img src="{{ theme_image_url($brand['image']) }}" alt="{{ $brand['name'] }}"
class="w-full rounded-2xl object-cover" />
@else
<div class="w-full aspect-[4/3] rounded-2xl bg-muted flex items-center justify-center">
<span class="text-muted-foreground text-sm">{{ $brand['name'] }}</span>
</div>
@endif
</div>
</div>
</div>
</section>
{{-- 2. Einleitung --}}
@if (!empty($brand['description']))
<section class="pb-16">
<div class="container-narrow">
<div class="prose-content text-large text-muted-foreground leading-relaxed">
{!! $brand['description'] !!}
</div>
</div>
</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">
<div class="container-padding">
<h2 class="text-section-title text-center mb-12">{{ __('ui.brand_services') }}</h2>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-8">
@foreach ($brand['services'] as $index => $service)
<div class="card-elevated rounded-2xl p-6" wire:key="service-{{ $index }}">
<h3 class="text-lg font-medium">{{ $service['title'] ?? '' }}</h3>
<p class="text-muted-foreground leading-relaxed mt-2">{{ $service['description'] ?? '' }}</p>
</div>
@endforeach
</div>
</div>
</section>
@endif
{{-- 4. Galerie: realisierte Projekte und Inspirationen strikt getrennt --}}
@php
$galleryProjects = $gallery['projects'] ?? [];
$galleryInspirations = $gallery['inspirations'] ?? [];
$hasBothGalleryGroups = $galleryProjects !== [] && $galleryInspirations !== [];
$initialGalleryTab = $galleryProjects !== [] ? 'projects' : 'inspirations';
$galleryLightbox = collect(['projects' => $galleryProjects, 'inspirations' => $galleryInspirations])
->map(fn (array $items) => collect($items)->map(fn (array $item) => [
'src' => theme_image_url($item['image']),
'caption' => $item['caption'] ?? '',
'badge' => $item['category'] === \App\Models\CmsBrand::GALLERY_PROJECT
? __('ui.brand_gallery_badge_project')
: __('ui.brand_gallery_badge_inspiration'),
])->values()->all())
->all();
@endphp
@if ($galleryProjects || $galleryInspirations)
<section id="galerie" class="section-padding"
x-data="{
tab: '{{ $initialGalleryTab }}',
groups: @js($galleryLightbox),
lightbox: false,
current: 0,
get items() { return this.groups[this.tab] ?? [] },
open(index) { this.current = index; this.lightbox = true },
next() { this.current = (this.current + 1) % this.items.length },
prev() { this.current = (this.current - 1 + this.items.length) % this.items.length },
}">
<div class="container-padding">
<div class="text-center max-w-2xl mx-auto">
<h2 class="text-section-title">{{ __('ui.brand_gallery_title') }}</h2>
<p class="text-muted-foreground leading-relaxed mt-4">{{ __('ui.brand_gallery_intro') }}</p>
</div>
@if ($hasBothGalleryGroups)
<div class="flex justify-center mt-10" role="tablist" aria-label="{{ __('ui.brand_gallery_title') }}">
<div class="inline-flex rounded-full bg-accent p-1 gap-1">
@foreach (['projects' => __('ui.brand_gallery_tab_projects'), 'inspirations' => __('ui.brand_gallery_tab_inspirations')] as $tabKey => $tabLabel)
<button type="button" role="tab" wire:key="gallery-tab-{{ $tabKey }}"
x-on:click="tab = '{{ $tabKey }}'"
x-bind:aria-selected="tab === '{{ $tabKey }}' ? 'true' : 'false'"
x-bind:class="tab === '{{ $tabKey }}'
? 'bg-background text-foreground shadow-sm'
: 'text-muted-foreground hover:text-foreground'"
class="px-6 py-2.5 rounded-full text-sm font-medium transition-colors cursor-pointer">
{{ $tabLabel }}
</button>
@endforeach
</div>
</div>
@endif
@foreach (['projects' => $galleryProjects, 'inspirations' => $galleryInspirations] as $groupKey => $groupItems)
@if ($groupItems)
<div wire:key="gallery-group-{{ $groupKey }}"
@if ($hasBothGalleryGroups) x-show="tab === '{{ $groupKey }}'" x-cloak @endif
class="mt-10">
@unless ($hasBothGalleryGroups)
<h3 class="text-xl font-medium text-center mb-8">
{{ $groupKey === 'projects' ? __('ui.brand_gallery_tab_projects') : __('ui.brand_gallery_tab_inspirations') }}
</h3>
@endunless
@if ($groupKey === 'inspirations')
<p class="text-sm text-muted-foreground text-center max-w-2xl mx-auto mb-8">
{{ __('ui.brand_gallery_inspiration_note') }}
</p>
@endif
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
@foreach ($groupItems as $index => $item)
<figure wire:key="gallery-{{ $groupKey }}-{{ $index }}" class="group">
<button type="button" x-on:click="open({{ $index }})"
class="relative block w-full overflow-hidden rounded-2xl cursor-pointer">
<img src="{{ theme_image_url($item['image']) }}"
alt="{{ $item['caption'] ?: $brand['name'] }}" loading="lazy"
class="w-full aspect-[4/3] object-cover transition-transform duration-300 group-hover:scale-105" />
<span class="absolute top-3 left-3 rounded-full bg-black/60 px-3 py-1 text-xs font-medium text-white backdrop-blur-sm">
{{ $groupKey === 'projects' ? __('ui.brand_gallery_badge_project') : __('ui.brand_gallery_badge_inspiration') }}
</span>
</button>
@if (!empty($item['caption']))
<figcaption class="text-sm text-muted-foreground mt-3">{{ $item['caption'] }}</figcaption>
@endif
</figure>
@endforeach
</div>
</div>
@endif
@endforeach
</div>
{{-- Lightbox: ins body geteleportet, damit keine Eltern-Constraints stören --}}
<template x-teleport="body">
<div x-show="lightbox" x-cloak class="fixed inset-0 z-50 bg-black/90"
x-transition:enter="transition ease-out duration-300" x-transition:enter-start="opacity-0"
x-transition:leave="transition ease-in duration-200" x-transition:leave-end="opacity-0"
x-on:keydown.escape.window="lightbox = false"
x-on:keydown.arrow-right.window="lightbox && next()"
x-on:keydown.arrow-left.window="lightbox && prev()">
<div class="absolute inset-0 flex flex-col items-center justify-center gap-4 p-6">
<img x-bind:src="items[current]?.src" x-bind:alt="items[current]?.caption"
class="max-h-[80vh] max-w-[90vw] rounded-lg object-contain" />
<p class="text-center text-sm text-white/80">
<span class="rounded-full border border-white/30 px-3 py-1" x-text="items[current]?.badge"></span>
<span class="ml-3" x-text="items[current]?.caption"></span>
</p>
</div>
<button type="button" x-on:click="lightbox = false" aria-label="{{ __('ui.close') }}"
class="absolute top-6 right-6 flex h-12 w-12 cursor-pointer items-center justify-center rounded-full border border-white/20 bg-white/10 text-white/90 transition-all hover:bg-white/20 hover:text-white">
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<button type="button" x-show="items.length > 1" x-on:click="prev()" aria-label="{{ __('ui.previous') }}"
class="absolute left-4 top-1/2 flex h-12 w-12 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border border-white/20 bg-white/10 text-white/90 transition-all hover:bg-white/20">
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</button>
<button type="button" x-show="items.length > 1" x-on:click="next()" aria-label="{{ __('ui.next') }}"
class="absolute right-4 top-1/2 flex h-12 w-12 -translate-y-1/2 cursor-pointer items-center justify-center rounded-full border border-white/20 bg-white/10 text-white/90 transition-all hover:bg-white/20">
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</button>
</div>
</template>
</section>
@endif
{{-- 5. Abschluss. Der Block schliesst die Seite ab und traegt den Claim aus der Zulieferung –
deshalb weiche Lichtflaechen statt Volltonhintergrund und ein gesetzter Hauptbutton
statt eines Textlinks. --}}
<section class="section-padding bg-secondary text-secondary-foreground relative overflow-hidden">
{{-- Rein dekorativ: zwei weiche Lichtpunkte nehmen der Flaeche die Haerte. --}}
<div aria-hidden="true" class="pointer-events-none absolute inset-0">
<div class="absolute -top-40 left-1/2 h-96 w-[42rem] -translate-x-1/2 rounded-full bg-white/10 blur-3xl"></div>
<div class="absolute -bottom-48 -right-24 h-96 w-96 rounded-full bg-black/20 blur-3xl"></div>
</div>
<div class="container-narrow relative text-center">
<span aria-hidden="true" class="mx-auto block h-px w-16 bg-white/40"></span>
<h2 class="text-section-title text-white mt-8 text-balance">
{{ $brand['cta_title'] ?: __('ui.brand_cta_title', ['name' => $brand['name']]) }}
</h2>
<p class="text-large text-white/85 max-w-2xl mx-auto leading-relaxed mt-5">
{{ $brand['cta_text'] ?: __('ui.brand_cta_text') }}
</p>
<div class="mt-10 flex flex-col sm:flex-row flex-wrap items-center justify-center gap-4">
<a href="{{ route('contact') }}"
class="group inline-flex items-center justify-center gap-2 rounded-lg bg-white px-8 py-4 text-lg font-medium text-secondary shadow-lg transition-all duration-300 hover:-translate-y-0.5 hover:shadow-xl">
{{ __('ui.request_appointment') }}
<svg class="h-5 w-5 transition-transform duration-300 group-hover:translate-x-1" fill="none"
stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M13 7l5 5-5 5M6 12h12" />
</svg>
</a>
@if (!empty($brand['website_url']))
<a href="{{ $brand['website_url'] }}" target="_blank" rel="noopener"
class="inline-flex items-center justify-center gap-2 rounded-lg border border-white/40 px-8 py-4 text-lg text-white transition-colors duration-300 hover:bg-white/10">
{{ __('ui.brand_website') }}
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"
aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round"
d="M13.5 6H18v4.5M17.5 6.5L10 14M15 14.5V18H6V9h3.5" />
</svg>
</a>
@endif
</div>
@if (!empty($brand['showroom']))
<p class="mt-10 inline-flex items-center gap-2 text-sm text-white/70">
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"
aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round"
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z" />
</svg>
{{ $brand['showroom'] }}
</p>
@endif
</div>
</section>
{{-- Weitere Marken --}}
@if (!empty($otherBrands))
<section class="section-padding bg-accent">
<div class="container-padding">
<h2 class="text-section-title text-center mb-12">{{ __('ui.other_brands') }}</h2>
<div class="grid sm:grid-cols-2 gap-8 max-w-4xl mx-auto">
@foreach ($otherBrands as $other)
<a href="{{ route('marken.show', ['slug' => $other['slug']]) }}"
wire:key="other-{{ $other['slug'] }}"
class="card-elevated rounded-2xl p-8 block hover:shadow-lg transition-shadow duration-300">
<h3 class="text-xl font-medium">{{ $other['name'] }}</h3>
<p class="text-secondary font-medium mt-1">{{ $other['tagline'] }}</p>
<p class="text-muted-foreground leading-relaxed mt-3">{!! $other['teaser'] !!}</p>
</a>
@endforeach
</div>
</div>
</section>
@endif
</article>