b2in/resources/views/livewire/web/components/sections/brand-detail.blade.php
Kevin Adametz 4b07bbdcae Startseite ruhiger, Otterpohl mit Montage, Marken-Bilder und Farbwelt-Entwurf
- Hinweisleiste per B2IN_ANNOUNCEMENT_BAR ausgeblendet (Standard aus)
- Hero: „Räume aus einer Hand.“ als neue Überschrift, zweiter Button entfernt
- Projekttext mit zentralem deutschen Vertragspartner (Punkt statt Gedankenstrich)
- Otterpohl als vierte Kernmarke: Text v2, Abschnitt „Montage“, Titelbild getauscht
- Markenabschnitte: Absätze mit „- “ als Liste mit fetter Einleitung
- Titelbilder und Galerien für CABINET, Rotpunkt, Febrü und Otterpohl (WebP)
- Feedback 17.09. in den Markentexten, Demobilder entfernt, WhatsApp-Nummer
- Palette „Modern“ fest ohne Schalter, Entwurf /entwurf mit Hero-Slideshow
- Seeder-Liste fürs Deployment im Entwicklungsplan

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 10:34:21 +00:00

363 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>
{{-- Liegt ein Logo vor, steht es anstelle des Schriftzugs. Es sitzt im <h1>,
damit die Ueberschrift erhalten bleibt – den Text liefert das alt-Attribut.
Feste Box statt fester Hoehe: die Logos haben sehr unterschiedliche
Seitenverhaeltnisse (Februe breit, Rotpunkt fast quadratisch). --}}
@if (!empty($brand['logo']))
<h1>
<img src="{{ theme_image_url($brand['logo']) }}" alt="{{ $brand['name'] }}"
class="h-24 sm:h-[7.2rem] max-w-[17rem] w-auto object-contain object-left" />
</h1>
@else
<h1 class="text-section-title">{{ $brand['name'] }}</h1>
@endif
<p class="text-large text-secondary font-medium mt-4">{{ $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['anchor'])) id="{{ $section['anchor'] }}" @endif>
@if (!empty($section['title']))
<h2 class="text-2xl lg:text-3xl font-medium">{{ $section['title'] }}</h2>
@endif
@foreach (\App\Models\CmsBrand::paragraphBlocks($section['paragraphs']) as $block)
@if ($block['type'] === 'list')
<ul class="mt-6 space-y-4">
@foreach ($block['items'] as $item)
<li class="flex gap-3 text-muted-foreground leading-relaxed">
<span class="mt-2.5 h-1.5 w-1.5 shrink-0 rounded-full bg-secondary" aria-hidden="true"></span>
<span>
@if ($item['lead'] !== '')
<strong class="font-medium text-foreground">{{ $item['lead'] }}:</strong>
@endif
{{ $item['text'] }}
</span>
</li>
@endforeach
</ul>
@else
<p class="text-muted-foreground leading-relaxed mt-4">{{ $block['text'] }}</p>
@endif
@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="group flex w-full items-center justify-between gap-4 p-5 text-left cursor-pointer">
<span class="font-medium transition-colors duration-300 group-hover:text-secondary">{{ $section['detail']['title'] }}</span>
<svg class="h-5 w-5 shrink-0 text-secondary transition-transform duration-500 ease-[cubic-bezier(0.33,1,0.68,1)]"
x-bind:class="{ 'rotate-180': open }" 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>
{{-- Weiches Auf- und Zuklappen ueber grid-template-rows (0fr → 1fr):
das animiert die tatsaechliche Hoehe, waehrend x-show den Inhalt
schlagartig einblendet. Das Alpine-Collapse-Plugin ist in diesem
Projekt nicht registriert, dies kommt ohne Abhaengigkeit aus.
Der zugeklappte Zustand steht auch im statischen class-Attribut,
damit vor dem Start von Alpine nichts aufgeklappt aufblitzt – die
Objekt-Schreibweise von x-bind:class entfernt ihn beim Oeffnen. --}}
<div id="detail-{{ $index }}"
class="grid grid-rows-[0fr] opacity-0 transition-all duration-500 ease-[cubic-bezier(0.33,1,0.68,1)]"
x-bind:class="{ 'grid-rows-[1fr] opacity-100': open, 'grid-rows-[0fr] opacity-0': !open }"
x-bind:aria-hidden="(!open).toString()">
<div class="overflow-hidden">
<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>
</div>
@endif
@if (!empty($section['claim']))
<p class="text-large text-secondary font-medium mt-6">{{ $section['claim'] }}</p>
@endif
@if (!empty($section['link']) && !empty($section['link_text']))
<a href="{{ $section['link'] }}"
class="text-secondary font-medium mt-4 inline-flex items-center gap-1 hover:underline">
{{ $section['link_text'] }}
<span aria-hidden="true">&rarr;</span>
</a>
@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, Einblicke in die Werkstatt und Visualisierungen stehen in
einem Raster, unterschieden nur durch das Badge – der Rubrikwechsel ist ausgesetzt
(Abstimmung 22.09., Nachtrag 17). Reihenfolge: Projekte, Werkstatt, Visualisierungen.
Überschrift und Zeile darunter richten sich nach dem, was die Galerie enthält (Nachtrag 19):
nur Visualisierungen → „Inspirationen und Entwürfe“, nur Projekte → „Referenzen“, Projekte
und Werkstatt → „Referenzen und Einblicke“, mit Visualisierungen → „Projekte und Entwürfe“. --}}
@php
$galleryItems = array_merge($gallery['projects'] ?? [], $gallery['company'] ?? [], $gallery['inspirations'] ?? []);
$galleryHasReal = ! empty($gallery['projects']) || ! empty($gallery['company']);
$galleryHasDrafts = ! empty($gallery['inspirations']);
$galleryVariant = match (true) {
! $galleryHasReal => 'inspirations',
$galleryHasDrafts => 'mixed',
! empty($gallery['company']) => 'projects_company',
default => 'projects',
};
$galleryLightbox = collect($galleryItems)->map(fn (array $item) => [
'src' => theme_image_url($item['image']),
'caption' => $item['caption'] ?? '',
'badge' => \App\Models\CmsBrand::galleryBadge($item['category']),
])->values()->all();
@endphp
@if ($galleryItems)
<section id="galerie" class="section-padding"
x-data="{
items: @js($galleryLightbox),
lightbox: false,
current: 0,
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_'.$galleryVariant) }}</h2>
<p class="text-muted-foreground leading-relaxed mt-4">{{ __('ui.brand_gallery_intro_'.$galleryVariant) }}</p>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6 mt-12">
@foreach ($galleryItems as $index => $item)
<figure wire:key="gallery-{{ $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">
{{ \App\Models\CmsBrand::galleryBadge($item['category']) }}
</span>
</button>
@if (!empty($item['caption']))
<figcaption class="text-sm text-muted-foreground mt-3">{{ $item['caption'] }}</figcaption>
@endif
</figure>
@endforeach
</div>
</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.
Der durchgehende Verlauf steckt in `.cta-band` (shared-styles.css) – als Utility-Klassen
waere er nicht ueber alle Themes hinweg zu halten. --}}
<section class="section-padding cta-band text-secondary-foreground">
<div class="container-narrow relative text-center">
{{-- Haarlinie als Auftakt, laeuft zu beiden Seiten aus. --}}
<span aria-hidden="true"
class="mx-auto block h-px w-24 bg-gradient-to-r from-transparent via-white/50 to-transparent"></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">
{{-- Oeffnet das Widget mit dieser Marke als Anliegen. --}}
<x-appointment-link :href="route('contact')" :topic="$brand['slug']"
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-[0_12px_30px_-14px_rgba(0,0,0,0.65)] transition-all duration-300 hover:-translate-y-0.5 hover:shadow-[0_18px_38px_-14px_rgba(0,0,0,0.7)]">
{{ __('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>
</x-appointment-link>
@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/25 px-8 py-4 text-lg text-white/90 transition-colors duration-300 hover:border-white/50 hover:text-white">
{{ __('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/65">
<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>
{{-- Drei weitere Marken: nebeneinander ab lg, darunter zweispaltig. --}}
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-8 max-w-6xl 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">
@if (!empty($other['logo']))
<img src="{{ theme_image_url($other['logo']) }}" alt="{{ $other['name'] }}"
class="h-[3.3rem] max-w-[10rem] w-auto mb-4 object-contain object-left" loading="lazy" />
@endif
<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>