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>
This commit is contained in:
parent
3211569fee
commit
4b07bbdcae
124 changed files with 3618 additions and 496 deletions
|
|
@ -151,6 +151,102 @@
|
|||
|
||||
--font-secondary: 'IBM Plex Sans', 'Inter', system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
/* ── Interior, Variante „Modern“ (Nachtrag 18) ──────────────────────
|
||||
Marcels Rückmeldung vom 23.09.: die Creme-Flächen wirken altbacken. Diese
|
||||
Variante lässt Tanne und Ocker aus dem Logo unangetastet und hellt nur die
|
||||
Flächen auf: Grund fast weiß, Karten weiß, Sektionswechsel in hellem Grau-
|
||||
Beige, Footer in Tanne. Aktiv, sobald der Farb-Schalter (palette-switch)
|
||||
`palette-modern` an <html> setzt; die Wahl liegt im localStorage. */
|
||||
.palette-modern .world-interior {
|
||||
--background: 40 25% 98%; /* #fbfaf8 fast weiß, leicht warm */
|
||||
--card: 0 0% 100%; /* Karten reinweiß */
|
||||
--popover: 0 0% 100%;
|
||||
|
||||
--muted: 40 12% 94%; /* #f2f0ed */
|
||||
--muted-foreground: 24 8% 38%;
|
||||
|
||||
--accent: 36 14% 93%; /* #efece7 Sektionswechsel, klar statt Sand */
|
||||
--accent-dark: 36 14% 88%;
|
||||
|
||||
--border: 36 12% 88%; /* #e6e2dc */
|
||||
--input: 0 0% 100%;
|
||||
|
||||
--hero-container: 40 14% 95%;
|
||||
--hero-container-dark: 40 12% 90%;
|
||||
--hero-container-light: 40 25% 98%;
|
||||
|
||||
/* Footer dunkel in Tanne – gibt der hellen Seite unten einen klaren Abschluss */
|
||||
--footer-bg: 175 16% 15%;
|
||||
--footer-fg: 38 33% 95%;
|
||||
--footer-muted: 38 20% 76%;
|
||||
--footer-line: 38 20% 76%;
|
||||
|
||||
/* Karten sind jetzt weiß auf fast weiß: etwas mehr Schatten, damit sie sich lösen */
|
||||
--shadow-card: 0 6px 24px -10px hsl(var(--foreground) / 0.14);
|
||||
--shadow-elevated: 0 24px 48px -20px hsl(var(--foreground) / 0.2);
|
||||
}
|
||||
|
||||
/* ── Interior, Entwurf „Greige“ (Nachtrag 19, Briefing Marcel 24.09.) ─────
|
||||
Heller, leicht kühler Grundton (Greige statt Creme), deutlich präsenteres
|
||||
Tannengrün, Gold nur als feine Linie. Liegt als Klasse am <body> der
|
||||
Entwurfsseite /entwurf – nicht am <html> wie der Browser-Schalter, damit
|
||||
sich beide nicht überlagern. */
|
||||
.world-interior.palette-greige {
|
||||
--background: 45 14% 93%; /* #f0eeeb Greige, Off-White */
|
||||
--foreground: 166 25% 13%; /* Tannengrau für Text */
|
||||
|
||||
--card: 40 20% 97%; /* #f8f7f4 – hell, aber kein steriles Weiß */
|
||||
--card-foreground: 166 25% 13%;
|
||||
--popover: 40 20% 97%;
|
||||
--popover-foreground: 166 25% 13%;
|
||||
|
||||
--primary: 165 44% 14%; /* #14342c Tannengrün, klar und dunkel */
|
||||
--primary-light: 165 32% 22%;
|
||||
--primary-lighter: 165 12% 82%;
|
||||
--primary-dark: 165 50% 10%;
|
||||
--primary-foreground: 45 20% 95%;
|
||||
|
||||
--secondary: 38 45% 46%; /* #aa8341 gedecktes Gold */
|
||||
--secondary-light: 38 45% 56%;
|
||||
--secondary-lighter: 40 40% 80%;
|
||||
--secondary-dark: 38 48% 38%;
|
||||
--secondary-deep: 38 55% 34%; /* Gold als Textfarbe auf hellem Grund */
|
||||
--cta-band-line: 38 45% 46%;
|
||||
--secondary-foreground: 45 20% 95%;
|
||||
|
||||
--clay: 30 8% 50%;
|
||||
|
||||
--muted: 45 12% 90%;
|
||||
--muted-foreground: 160 8% 36%;
|
||||
|
||||
--accent: 45 12% 89%; /* #e8e6e0 Sektionswechsel */
|
||||
--accent-dark: 45 12% 84%;
|
||||
--accent-foreground: 166 25% 13%;
|
||||
|
||||
--dark-bg: 165 44% 14%;
|
||||
--dark-text: 45 20% 95%;
|
||||
--dark-muted: 45 15% 78%;
|
||||
|
||||
/* Footer in Tannengrün, Haarlinie in Gold */
|
||||
--footer-bg: 165 44% 14%;
|
||||
--footer-fg: 45 20% 95%;
|
||||
--footer-muted: 45 15% 78%;
|
||||
--footer-line: 38 45% 46%;
|
||||
|
||||
--cta-band-end: 165 30% 22%;
|
||||
|
||||
--border: 40 10% 84%;
|
||||
--input: 40 20% 98%;
|
||||
--ring: 165 44% 14%;
|
||||
|
||||
--hero-container: 40 20% 97%;
|
||||
--hero-container-dark: 45 12% 90%;
|
||||
--hero-container-light: 40 25% 98%;
|
||||
|
||||
--shadow-card: 0 6px 24px -10px hsl(var(--foreground) / 0.14);
|
||||
--shadow-elevated: 0 30px 60px -28px hsl(var(--primary) / 0.35);
|
||||
}
|
||||
}
|
||||
|
||||
/* Interior: Ocker ist Akzent, kein Flächenton unter Text – Creme auf Ocker erreicht
|
||||
|
|
@ -173,4 +269,202 @@
|
|||
color: hsl(var(--secondary-deep));
|
||||
}
|
||||
|
||||
/* Variante „Modern“: auf dem dunklen Footer trägt Ocker in voller Stärke. */
|
||||
.palette-modern .world-interior .section-footer .text-secondary {
|
||||
color: hsl(var(--secondary));
|
||||
}
|
||||
|
||||
/* Variante „Modern“: Abschlussband und Footer sind beide dunkel – eine helle Haarlinie trennt sie. */
|
||||
.palette-modern .world-interior .section-footer {
|
||||
border-top: 1px solid hsl(var(--footer-muted) / 0.35);
|
||||
}
|
||||
|
||||
/* Der schwebende Termin-Button liegt über Bildern und weißen Karten: er bekommt eine
|
||||
Fläche in Kartenfarbe, damit die Kontur nicht im Hintergrund verschwindet. */
|
||||
.world-interior .appointment-toggle.btn-secondary-accent {
|
||||
background: hsl(var(--card)) !important;
|
||||
}
|
||||
|
||||
.world-interior .appointment-toggle.btn-secondary-accent:hover {
|
||||
background: hsl(var(--primary)) !important;
|
||||
}
|
||||
|
||||
/* Cookie-Manager (acme/cookie-consent) in der Interior-Welt: die Paketfarben aus
|
||||
config/cookie-consent.php sind das Blau der Immobilien-Welt. Hier folgt der schwebende
|
||||
Button dem Akzent-Button der Seite (Tanne, Hover heller), Icon, Schalter und Link-Hover
|
||||
tragen Ocker, „Speichern“ liegt in Tanne. Die Immobilien-Welt behält ihr Blau. */
|
||||
.world-interior .cookie-consent-wrapper {
|
||||
--cc-primary: hsl(var(--secondary));
|
||||
--cc-primary-hover: hsl(var(--secondary-dark));
|
||||
--cc-save: hsl(var(--primary));
|
||||
--cc-save-hover: hsl(var(--primary-light));
|
||||
--cc-button-bg: hsl(var(--primary));
|
||||
--cc-button-hover: hsl(var(--primary-light));
|
||||
}
|
||||
|
||||
.world-interior .cookie-consent-wrapper .cc-floating-btn {
|
||||
--tw-ring-color: hsl(var(--secondary)) !important;
|
||||
color: hsl(var(--primary-foreground));
|
||||
background: linear-gradient(145deg, hsl(var(--primary)) 0%, hsl(var(--primary-light)) 100%);
|
||||
}
|
||||
|
||||
.world-interior .cookie-consent-wrapper .cc-floating-btn:hover {
|
||||
background: linear-gradient(145deg, hsl(var(--primary-light)) 0%, hsl(var(--primary)) 100%);
|
||||
}
|
||||
|
||||
/* Variante „Modern“: der Footer ist dunkel, also das negative Logo statt des positiven. */
|
||||
.footer-logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.palette-modern .world-interior .footer-logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.palette-modern .world-interior .footer-logo-dark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@import "./shared-styles.css";
|
||||
|
||||
/* ── Entwurf „Greige“: Footer dunkel mit Goldlinie und negativem Logo ─────── */
|
||||
.world-interior.palette-greige .section-footer {
|
||||
border-top: 1px solid hsl(var(--secondary) / 0.6);
|
||||
}
|
||||
|
||||
.world-interior.palette-greige .section-footer .text-secondary {
|
||||
color: hsl(var(--secondary));
|
||||
}
|
||||
|
||||
.world-interior.palette-greige .footer-logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.world-interior.palette-greige .footer-logo-dark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* ── Hero des Entwurfs: Rahmen mit Goldkante, grüne Flanken, Slider ────────
|
||||
Goldkanten als Verlaufs-Border: transparenter Rand, Fläche auf padding-box,
|
||||
Goldverlauf auf border-box. Kein Pseudo-Rand mit Maske mehr – der ließ an
|
||||
der gerundeten Kante eine helle Linie durchscheinen. Die grünen Flanken sind
|
||||
Inline-SVGs (components/web/hero-flank-*), skaliert über die Rahmenhöhe. */
|
||||
.hero-draft .hero-frame {
|
||||
border: 1.5px solid transparent;
|
||||
background:
|
||||
linear-gradient(hsl(var(--hero-container)), hsl(var(--hero-container))) padding-box,
|
||||
linear-gradient(
|
||||
125deg,
|
||||
hsl(var(--secondary-light)) 0%,
|
||||
hsl(var(--secondary)) 28%,
|
||||
hsl(var(--secondary) / 0.35) 62%,
|
||||
hsl(var(--secondary-light)) 100%
|
||||
) border-box;
|
||||
box-shadow: var(--shadow-elevated);
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.hero-draft .hero-flank {
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.hero-draft .hero-flank-right {
|
||||
top: 0;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hero-draft .hero-flank-left {
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
height: 46%;
|
||||
}
|
||||
|
||||
.hero-draft .hero-image {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Goldkante um das Bild, gleiche Technik wie am Rahmen */
|
||||
.hero-draft .hero-image-frame {
|
||||
border: 1.5px solid transparent;
|
||||
border-radius: 1.25rem;
|
||||
background:
|
||||
linear-gradient(hsl(var(--card)), hsl(var(--card))) padding-box,
|
||||
linear-gradient(
|
||||
135deg,
|
||||
hsl(var(--secondary-light)) 0%,
|
||||
hsl(var(--secondary)) 40%,
|
||||
hsl(var(--secondary) / 0.4) 100%
|
||||
) border-box;
|
||||
box-shadow: var(--shadow-elevated);
|
||||
}
|
||||
|
||||
.hero-draft .hero-eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.28em;
|
||||
text-transform: uppercase;
|
||||
color: hsl(var(--muted-foreground));
|
||||
}
|
||||
|
||||
.hero-draft .hero-eyebrow::after {
|
||||
content: "";
|
||||
width: 3.5rem;
|
||||
height: 1px;
|
||||
background: hsl(var(--secondary));
|
||||
}
|
||||
|
||||
.hero-draft .hero-title {
|
||||
font-family: var(--font-secondary);
|
||||
font-size: clamp(2rem, 3.1vw, 3.3rem);
|
||||
line-height: 1.12;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.01em;
|
||||
color: hsl(var(--primary));
|
||||
}
|
||||
|
||||
.hero-draft .hero-title em {
|
||||
font-style: normal;
|
||||
color: hsl(var(--secondary-deep));
|
||||
}
|
||||
|
||||
/* Button: Goldrand rundum, unten kräftiger, Pfeil in Gold */
|
||||
.hero-draft .btn-hero {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem 1.75rem;
|
||||
border-radius: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: hsl(var(--primary-foreground));
|
||||
background: linear-gradient(145deg, hsl(var(--primary)) 0%, hsl(var(--primary-light)) 100%);
|
||||
border: 1px solid hsl(var(--secondary));
|
||||
border-bottom-width: 3px;
|
||||
box-shadow: 0 14px 30px -14px hsl(var(--primary) / 0.6);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.hero-draft .btn-hero svg {
|
||||
color: hsl(var(--secondary-light));
|
||||
}
|
||||
|
||||
.hero-draft .btn-hero:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 18px 34px -14px hsl(var(--primary) / 0.7);
|
||||
}
|
||||
|
||||
.hero-draft .hero-slide {
|
||||
transition: opacity 1.4s ease-in-out;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.hero-draft .hero-slide {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -80,14 +80,12 @@ return [
|
|||
],
|
||||
],
|
||||
'hero' => [
|
||||
'title' => '<span class="text-secondary">B2in Interior</span> – Premium-Einrichtung von starken Marken. Für Ihr Zuhause. Für Ihr Projekt.',
|
||||
'title' => '<span class="text-secondary">Räume aus einer Hand.</span><br>Mit starken Marken und persönlichen Lösungen.',
|
||||
'subtitle' => 'Von der einzelnen Maßanfertigung bis zur vollständigen Ausstattung – mit CABINET, Rotpunkt Küchen, Febrü und einem Netzwerk regionaler Fachexperten.',
|
||||
'image' => 'b2in/hero-room.jpg',
|
||||
'image_alt' => 'B2in Interior – Premium-Einrichtung von starken Marken',
|
||||
'cta1_text' => 'Unsere Marken entdecken',
|
||||
'cta1_link' => '/marken',
|
||||
'cta2_text' => 'Termin anfragen',
|
||||
'cta2_link' => '/contact',
|
||||
'stats' => [
|
||||
'Premium-Marken',
|
||||
'Maßgefertigte Lösungen',
|
||||
|
|
@ -96,6 +94,15 @@ return [
|
|||
'card_title' => 'B2in Interior',
|
||||
'card_text' => 'Einrichtung, <span class="text-secondary">die bleibt.</span>',
|
||||
],
|
||||
'hero_slider' => [
|
||||
'eyebrow' => 'B2in Interior',
|
||||
'title' => '<em>Räume aus einer Hand.</em> Mit starken Marken und persönlichen Lösungen.',
|
||||
'subtitle' => 'Wir vereinen CABINET, Rotpunkt Küchen, Febrü und ein Netzwerk regionaler Experten, um individuelle Interior-Lösungen für Ihr Zuhause oder Ihr Projekt zu realisieren.',
|
||||
'cta_text' => 'Unsere Marken entdecken',
|
||||
'cta_link' => '/marken',
|
||||
'interval' => 7,
|
||||
'slides' => [],
|
||||
],
|
||||
'founder_bar' => [
|
||||
'image' => 'b2in/marcel-scheibe.jpg',
|
||||
'name' => 'Marcel Scheibe',
|
||||
|
|
@ -203,7 +210,7 @@ return [
|
|||
[
|
||||
'icon' => 'building-office-2',
|
||||
'title' => 'Ausstattung für Ihr Projekt',
|
||||
'description' => 'Vollständige Einrichtung für Objekte, Büros und Projektentwicklungen – koordiniert aus einer Hand, von der Küche bis zur letzten Blume. Mit deutscher Vertragssicherheit und einem eingespielten Herstellernetzwerk.',
|
||||
'description' => 'Vollständige Einrichtung für Objekte, Büros und Projektentwicklungen. Koordiniert aus einer Hand, von der Küche bis zur letzten Blume. Mit einem zentralen deutschen Vertragspartner und einem eingespielten Herstellernetzwerk.',
|
||||
'button_text' => 'Projekt besprechen',
|
||||
'button_link' => '/contact',
|
||||
],
|
||||
|
|
@ -251,15 +258,15 @@ return [
|
|||
],
|
||||
[
|
||||
'slug' => 'tischlerei',
|
||||
'name' => 'Tischlerei',
|
||||
'tagline' => 'Maßarbeit, die den Raum vollendet.',
|
||||
'teaser' => 'Aufbau, Sonderlösungen und Veredelung aus einer Tischlerei in Ostwestfalen – dort, wo Serienmöbel aufhören und Ihr Raum anfängt.',
|
||||
'name' => 'Otterpohl Möbelservice',
|
||||
'tagline' => 'Nicht easy click & buy. Aber dennoch easy to get.',
|
||||
'teaser' => 'Individuelle Möbel, Türen, Treppen, Küchen, Wandverkleidungen und Objektbau – bis hin zum kompletten Innenausbau. Tischlerhandwerk aus Rheda-Wiedenbrück.',
|
||||
'image' => '',
|
||||
'logo' => '',
|
||||
'link_text' => 'Mehr zur Tischlerei',
|
||||
'logo' => 'marken/otterpohl.png',
|
||||
'link_text' => 'Mehr zu Otterpohl',
|
||||
'origin_city' => 'Rheda-Wiedenbrück',
|
||||
'origin_distance' => 'rund 35 km',
|
||||
'origin_role' => 'Unsere Werkstatt für Aufbau, Sonderlösungen und Veredelung.',
|
||||
'origin_role' => 'Tischlerei und Innenausbau aus Rheda-Wiedenbrück – vom einzelnen Möbel bis zum ganzen Raum, inklusive Montage.',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
@ -292,10 +299,30 @@ return [
|
|||
],
|
||||
[
|
||||
'title' => 'Aufbau vor Ort, Sonderwünsche inklusive',
|
||||
'description' => 'Unsere Tischlerei baut auf und ergänzt, was der Raum zusätzlich braucht: Blenden, Anschlüsse, Sonderlösungen.',
|
||||
'description' => 'Otterpohl baut auf und ergänzt, was der Raum zusätzlich braucht: Blenden, Anschlüsse, Sonderlösungen.',
|
||||
],
|
||||
],
|
||||
],
|
||||
// Gemeinsamer Handwerk-Abschluss jeder Markenseite (Feedback 17.09., Nachtrag 15). Erscheint auf
|
||||
// allen Markenseiten außer bei Otterpohl selbst; der Link führt zur Otterpohl-Seite.
|
||||
'brand_craft' => [
|
||||
'title' => 'Planung plus Handwerk',
|
||||
'paragraphs' => [
|
||||
'CABINET, Rotpunkt und Febrü decken bereits eine enorme planerische und gestalterische Bandbreite ab. Es geht deshalb ausdrücklich nicht um Sonderanfertigungen um jeden Preis.',
|
||||
'Natürlich gibt es trotzdem diesen einen Fall, bei dem selbst ein hervorragendes System an seine Grenze kommt: der Übergang zur Architektur, die besondere Verkleidung, die Wand, die noch fehlt, oder der letzte Millimeter. Dann greift unsere Zusammenarbeit mit Otterpohl aus Rheda-Wiedenbrück – Tischlerhandwerk, Innenausbau und Montage aus einer Hand.',
|
||||
'Wir wägen gestalterischen Mehrwert, technische Notwendigkeit und wirtschaftliche Sinnhaftigkeit ehrlich miteinander ab. Am Ende entscheiden aber Ihre Wünsche – nicht unser Drang, Ihnen noch eine Sonderlösung zu verkaufen.',
|
||||
],
|
||||
'detail' => [
|
||||
'title' => 'Wenn das Handwerk den letzten Millimeter übernimmt',
|
||||
'paragraphs' => [
|
||||
'Möglich sind beispielsweise passgenaue Nischenlösungen, besondere Verkleidungen, raumhohe Einbauten, Übergänge zu angrenzenden Möbeln, individuelle Lackierungen und Echtholzfurniere oder Anpassungen an die vorhandene Architektur.',
|
||||
'Bei Bedarf endet das Konzept nicht am Möbel. Auch Trockenbau, Tapeten, Fußböden, Wandgestaltung, Beleuchtung und weitere Details können einbezogen werden.',
|
||||
],
|
||||
],
|
||||
'claim' => 'Die Möglichkeiten eines Maßsystems. Die Flexibilität des Handwerks.',
|
||||
'link_text' => 'Mehr zu Otterpohl',
|
||||
'brand_slug' => 'tischlerei',
|
||||
],
|
||||
'sponsoring' => [
|
||||
'title' => 'Regional <span class="text-secondary">verbunden</span>',
|
||||
'subtitle' => 'Wir unterstützen Vereine und Organisationen aus der Region – weil Verwurzelung mehr ist als eine Adresse.',
|
||||
|
|
|
|||
|
|
@ -17,6 +17,20 @@ return [
|
|||
'menu_open' => 'Menü öffnen',
|
||||
|
||||
// Welten-Wechsler (Interior / Immobilien)
|
||||
// Farb-Schalter für die Abstimmung der Palette (Nachtrag 18)
|
||||
'palette' => [
|
||||
'label' => 'Farbwelt',
|
||||
'classic' => 'Bisher',
|
||||
'modern' => 'Neu',
|
||||
'draft' => 'Entwurf',
|
||||
],
|
||||
// Slideshow im Hero des Entwurfs (Nachtrag 19)
|
||||
'hero_slider' => [
|
||||
'label' => 'Einblicke in unsere Marken',
|
||||
'dots' => 'Bild wählen',
|
||||
'dot' => 'Bild :n von :total: :label',
|
||||
'empty' => 'Noch keine Bilder hinterlegt.',
|
||||
],
|
||||
'worlds_label' => 'Bereich wechseln',
|
||||
'world_interior' => 'Interior',
|
||||
'world_immobilien' => 'Immobilien',
|
||||
|
|
@ -39,7 +53,8 @@ return [
|
|||
'success' => 'Vielen Dank! Wir melden uns zeitnah bei Ihnen und stimmen den Termin ab.',
|
||||
'new_request' => 'Weitere Anfrage',
|
||||
'date_in_past' => 'Bitte einen Termin ab heute wählen.',
|
||||
'whatsapp_intro' => 'Lieber direkt schreiben? Wir bereiten die Nachricht mit Ihren Angaben vor.',
|
||||
'whatsapp_intro' => 'Am schnellsten per WhatsApp – wir bereiten die Nachricht mit Ihren Angaben vor.',
|
||||
'or_form' => 'oder per Formular',
|
||||
'whatsapp_text' => 'Hallo B2in Interior, ich hätte gern einen Beratungstermin.',
|
||||
'whatsapp_note' => 'Öffnet WhatsApp mit vorbereitetem Text. Abgeschickt wird die Nachricht erst von Ihnen.',
|
||||
],
|
||||
|
|
@ -58,12 +73,22 @@ return [
|
|||
'more_about' => 'Mehr zu :name',
|
||||
|
||||
// Markengalerie – realisierte Projekte vs. Entwuerfe
|
||||
'brand_gallery_title' => 'Referenzen und Inspirationen',
|
||||
'brand_gallery_intro' => 'Ausgeführte Arbeiten aus unseren Aufträgen – und Visualisierungen, die zeigen, was in einem Raum möglich ist.',
|
||||
'brand_gallery_title' => 'Inspirationen und Entwürfe',
|
||||
'brand_gallery_intro' => 'Visualisierungen, die zeigen, was in einem Raum möglich ist – noch bevor das erste Möbel steht.',
|
||||
// Varianten je Galerie-Inhalt (Nachtrag 19): nur Entwürfe / nur Projekte und Werkstatt / gemischt
|
||||
'brand_gallery_title_inspirations' => 'Inspirationen und Entwürfe',
|
||||
'brand_gallery_intro_inspirations' => 'Visualisierungen, die zeigen, was in einem Raum möglich ist – noch bevor das erste Möbel steht.',
|
||||
'brand_gallery_title_projects' => 'Referenzen',
|
||||
'brand_gallery_intro_projects' => 'Realisierte Projekte – so sieht es aus, wenn Planung und Handwerk fertig sind.',
|
||||
'brand_gallery_title_projects_company' => 'Referenzen und Einblicke',
|
||||
'brand_gallery_intro_projects_company' => 'Realisierte Projekte und ein Blick in die Werkstatt, in der sie entstehen.',
|
||||
'brand_gallery_title_mixed' => 'Projekte und Entwürfe',
|
||||
'brand_gallery_intro_mixed' => 'Umgesetzte Projekte und Visualisierungen, die zeigen, was in einem Raum möglich ist.',
|
||||
'brand_gallery_tab_projects' => 'Realisierte Projekte',
|
||||
'brand_gallery_tab_inspirations' => 'Inspirationen und Entwürfe',
|
||||
'brand_gallery_badge_project' => 'Realisiertes Projekt',
|
||||
'brand_gallery_badge_inspiration' => 'Visualisierung',
|
||||
'brand_gallery_badge_company' => 'Aus der Werkstatt',
|
||||
'brand_gallery_inspiration_note' => 'Entwürfe und Visualisierungen. Sie zeigen gestalterische Möglichkeiten, noch bevor das erste Möbel steht.',
|
||||
'previous' => 'Vorheriges Bild',
|
||||
'next' => 'Nächstes Bild',
|
||||
|
|
|
|||
|
|
@ -77,14 +77,12 @@ return [
|
|||
],
|
||||
],
|
||||
'hero' => [
|
||||
'title' => '<span class="text-secondary">B2in Interior</span> – Premium furnishing from strong brands. For your home. For your project.',
|
||||
'title' => '<span class="text-secondary">Spaces from a single source.</span><br>With strong brands and personal solutions.',
|
||||
'subtitle' => 'From a single bespoke piece to a complete fit-out – with CABINET, Rotpunkt Küchen, Febrü and a network of regional specialists.',
|
||||
'image' => 'b2in/hero-room.jpg',
|
||||
'image_alt' => 'B2in Interior – Premium furnishing from strong brands',
|
||||
'cta1_text' => 'Discover our brands',
|
||||
'cta1_link' => '/marken',
|
||||
'cta2_text' => 'Request an appointment',
|
||||
'cta2_link' => '/contact',
|
||||
'stats' => [
|
||||
'Premium brands',
|
||||
'Bespoke solutions',
|
||||
|
|
@ -93,6 +91,15 @@ return [
|
|||
'card_title' => 'B2in Interior',
|
||||
'card_text' => 'Interiors <span class="text-secondary">that last.</span>',
|
||||
],
|
||||
'hero_slider' => [
|
||||
'eyebrow' => 'B2in Interior',
|
||||
'title' => '<em>Rooms from a single source.</em> With strong brands and personal solutions.',
|
||||
'subtitle' => 'We bring together CABINET, Rotpunkt Küchen, Febrü and a network of regional experts to create individual interior solutions for your home or your project.',
|
||||
'cta_text' => 'Discover our brands',
|
||||
'cta_link' => '/marken',
|
||||
'interval' => 7,
|
||||
'slides' => [],
|
||||
],
|
||||
'founder_bar' => [
|
||||
'image' => 'b2in/marcel-scheibe.jpg',
|
||||
'name' => 'Marcel Scheibe',
|
||||
|
|
@ -200,7 +207,7 @@ return [
|
|||
[
|
||||
'icon' => 'building-office-2',
|
||||
'title' => 'Fit-out for your project',
|
||||
'description' => 'Complete furnishing for properties, offices and developments – coordinated from a single source, from the kitchen to the last flower. With German contractual reliability and an established manufacturer network.',
|
||||
'description' => 'Complete furnishing for properties, offices and developments. Coordinated from a single source, from the kitchen to the last flower. With a single German contract partner and an established manufacturer network.',
|
||||
'button_text' => 'Discuss your project',
|
||||
'button_link' => '/contact',
|
||||
],
|
||||
|
|
@ -248,15 +255,15 @@ return [
|
|||
],
|
||||
[
|
||||
'slug' => 'tischlerei',
|
||||
'name' => 'Joinery',
|
||||
'tagline' => 'Craftsmanship that completes the room.',
|
||||
'teaser' => 'Assembly, bespoke solutions and finishing from a joinery in East Westphalia – where series furniture ends and your room begins.',
|
||||
'name' => 'Otterpohl Möbelservice',
|
||||
'tagline' => 'Not easy click & buy. But still easy to get.',
|
||||
'teaser' => 'Bespoke furniture, doors, stairs, kitchens, wall cladding and contract work – up to complete interior fit-outs. Joinery from Rheda-Wiedenbrück.',
|
||||
'image' => '',
|
||||
'logo' => '',
|
||||
'link_text' => 'More about the joinery',
|
||||
'logo' => 'marken/otterpohl.png',
|
||||
'link_text' => 'More about Otterpohl',
|
||||
'origin_city' => 'Rheda-Wiedenbrück',
|
||||
'origin_distance' => 'about 35 km',
|
||||
'origin_role' => 'Our workshop for assembly, custom solutions and finishing.',
|
||||
'origin_role' => 'Joinery and interior fit-out from Rheda-Wiedenbrück – from a single piece to the whole room, installation included.',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
|
@ -286,10 +293,28 @@ return [
|
|||
],
|
||||
[
|
||||
'title' => 'Assembly on site, special requests included',
|
||||
'description' => 'Our joinery assembles and adds what the room needs beyond that: panels, connections, custom solutions.',
|
||||
'description' => 'Otterpohl assembles and adds what the room needs beyond that: panels, connections, custom solutions.',
|
||||
],
|
||||
],
|
||||
],
|
||||
'brand_craft' => [
|
||||
'title' => 'Planning plus craftsmanship',
|
||||
'paragraphs' => [
|
||||
'CABINET, Rotpunkt and Febrü already cover an enormous range of planning and design options. So this is explicitly not about custom-made pieces at any price.',
|
||||
'Still, there is always that one case where even an excellent system reaches its limit: the transition to the architecture, a special cladding, the wall that is still missing, or the last millimetre. That is where our collaboration with Otterpohl from Rheda-Wiedenbrück comes in – joinery, interior fit-out and installation from a single source.',
|
||||
'We weigh design value, technical necessity and economic sense honestly. In the end, your wishes decide – not our urge to sell you yet another special solution.',
|
||||
],
|
||||
'detail' => [
|
||||
'title' => 'When craftsmanship takes over the last millimetre',
|
||||
'paragraphs' => [
|
||||
'Possible are, for example, precisely fitted alcove solutions, special claddings, full-height built-ins, transitions to adjoining furniture, individual lacquer finishes and real-wood veneers, or adaptations to the existing architecture.',
|
||||
'If needed, the concept does not end at the furniture. Drywall, wallpaper, flooring, wall design, lighting and further details can be included.',
|
||||
],
|
||||
],
|
||||
'claim' => 'The possibilities of a made-to-measure system. The flexibility of craftsmanship.',
|
||||
'link_text' => 'More about Otterpohl',
|
||||
'brand_slug' => 'tischlerei',
|
||||
],
|
||||
'sponsoring' => [
|
||||
'title' => 'Rooted <span class="text-secondary">in the region</span>',
|
||||
'subtitle' => 'We support clubs and organisations from our region – because being rooted is more than an address.',
|
||||
|
|
|
|||
|
|
@ -17,6 +17,20 @@ return [
|
|||
'menu_open' => 'Open menu',
|
||||
|
||||
// World switcher (Interior / Real estate)
|
||||
// Farb-Schalter für die Abstimmung der Palette (Nachtrag 18)
|
||||
'palette' => [
|
||||
'label' => 'Colour scheme',
|
||||
'classic' => 'Current',
|
||||
'modern' => 'New',
|
||||
'draft' => 'Draft',
|
||||
],
|
||||
// Slideshow in the draft hero (Nachtrag 19)
|
||||
'hero_slider' => [
|
||||
'label' => 'Glimpses of our brands',
|
||||
'dots' => 'Choose image',
|
||||
'dot' => 'Image :n of :total: :label',
|
||||
'empty' => 'No images yet.',
|
||||
],
|
||||
'worlds_label' => 'Switch area',
|
||||
'world_interior' => 'Interior',
|
||||
'world_immobilien' => 'Real estate',
|
||||
|
|
@ -39,7 +53,8 @@ return [
|
|||
'success' => 'Thank you! We will get back to you shortly to arrange the appointment.',
|
||||
'new_request' => 'Another request',
|
||||
'date_in_past' => 'Please choose a date from today onwards.',
|
||||
'whatsapp_intro' => 'Prefer to write directly? We prepare the message with your details.',
|
||||
'whatsapp_intro' => 'Fastest via WhatsApp – we prepare the message with your details.',
|
||||
'or_form' => 'or use the form',
|
||||
'whatsapp_text' => 'Hello B2in Interior, I would like to arrange an appointment.',
|
||||
'whatsapp_note' => 'Opens WhatsApp with a prepared text. You send the message yourself.',
|
||||
],
|
||||
|
|
@ -58,12 +73,21 @@ return [
|
|||
'more_about' => 'More about :name',
|
||||
|
||||
// Brand gallery – realised projects vs. drafts
|
||||
'brand_gallery_title' => 'References and inspiration',
|
||||
'brand_gallery_intro' => 'Completed work from our projects – and visualisations showing what is possible in a room.',
|
||||
'brand_gallery_title' => 'Inspiration and drafts',
|
||||
'brand_gallery_intro' => 'Visualisations that show what is possible in a room – before the first piece of furniture is in place.',
|
||||
'brand_gallery_title_inspirations' => 'Inspiration and drafts',
|
||||
'brand_gallery_intro_inspirations' => 'Visualisations that show what is possible in a room – before the first piece of furniture is in place.',
|
||||
'brand_gallery_title_projects' => 'References',
|
||||
'brand_gallery_intro_projects' => 'Completed projects – this is what it looks like once planning and craft are done.',
|
||||
'brand_gallery_title_projects_company' => 'References and insights',
|
||||
'brand_gallery_intro_projects_company' => 'Completed projects and a look into the workshop where they are made.',
|
||||
'brand_gallery_title_mixed' => 'Projects and drafts',
|
||||
'brand_gallery_intro_mixed' => 'Completed projects and visualisations that show what is possible in a room.',
|
||||
'brand_gallery_tab_projects' => 'Completed projects',
|
||||
'brand_gallery_tab_inspirations' => 'Inspiration and drafts',
|
||||
'brand_gallery_badge_project' => 'Completed project',
|
||||
'brand_gallery_badge_inspiration' => 'Visualisation',
|
||||
'brand_gallery_badge_company' => 'From the workshop',
|
||||
'brand_gallery_inspiration_note' => 'Drafts and visualisations. They show design possibilities before the first piece of furniture is in place.',
|
||||
'previous' => 'Previous image',
|
||||
'next' => 'Next image',
|
||||
|
|
|
|||
55
resources/views/components/palette-switch.blade.php
Normal file
55
resources/views/components/palette-switch.blade.php
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
{{-- Farb-Schalter für die Abstimmung (Nachtrag 18 + 19): bisherige Creme-Palette, die hellere
|
||||
Variante „Neu“ und der Entwurf vom 24.09. Schwimmt am linken Rand in der Mitte – unten links
|
||||
sitzt der Cookie-Button, unten rechts die Terminanfrage.
|
||||
„Bisher“ und „Neu“ schalten im Browser um (localStorage b2in-palette, im <head> gesetzt,
|
||||
?palette=modern|classic per Link). „Entwurf“ ist eine eigene Seite (/entwurf) mit fester
|
||||
Palette; von dort führen die beiden anderen Einträge zurück auf die Startseite. --}}
|
||||
@props(['draft' => false])
|
||||
|
||||
<div class="fixed left-3 top-1/2 z-40 -translate-y-1/2 print:hidden" data-palette-switch
|
||||
@unless ($draft)
|
||||
x-data="{
|
||||
palette: document.documentElement.classList.contains('palette-modern') ? 'modern' : 'classic',
|
||||
choose(value) {
|
||||
this.palette = value;
|
||||
document.documentElement.classList.toggle('palette-modern', value === 'modern');
|
||||
try { localStorage.setItem('b2in-palette', value); } catch (e) {}
|
||||
},
|
||||
}"
|
||||
@endunless>
|
||||
<div class="flex flex-col items-stretch gap-1 rounded-full border border-border bg-card/95 p-1 text-[11px] font-medium shadow-lg backdrop-blur-sm"
|
||||
role="group" aria-label="{{ __('ui.palette.label') }}">
|
||||
<span class="px-2 pt-1 text-center text-[10px] uppercase tracking-wide text-muted-foreground" aria-hidden="true">
|
||||
{{ __('ui.palette.label') }}
|
||||
</span>
|
||||
|
||||
@if ($draft)
|
||||
<a href="{{ url('/?palette=classic') }}"
|
||||
class="rounded-full px-3 py-1.5 text-center text-muted-foreground transition-colors duration-200 hover:text-foreground">
|
||||
{{ __('ui.palette.classic') }}
|
||||
</a>
|
||||
<a href="{{ url('/?palette=modern') }}"
|
||||
class="rounded-full px-3 py-1.5 text-center text-muted-foreground transition-colors duration-200 hover:text-foreground">
|
||||
{{ __('ui.palette.modern') }}
|
||||
</a>
|
||||
<span aria-current="page" class="rounded-full bg-primary px-3 py-1.5 text-center text-primary-foreground">
|
||||
{{ __('ui.palette.draft') }}
|
||||
</span>
|
||||
@else
|
||||
<button type="button" x-on:click="choose('classic')" x-bind:aria-pressed="(palette === 'classic').toString()"
|
||||
x-bind:class="palette === 'classic' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'"
|
||||
class="cursor-pointer rounded-full px-3 py-1.5 transition-colors duration-200">
|
||||
{{ __('ui.palette.classic') }}
|
||||
</button>
|
||||
<button type="button" x-on:click="choose('modern')" x-bind:aria-pressed="(palette === 'modern').toString()"
|
||||
x-bind:class="palette === 'modern' ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:text-foreground'"
|
||||
class="cursor-pointer rounded-full px-3 py-1.5 transition-colors duration-200">
|
||||
{{ __('ui.palette.modern') }}
|
||||
</button>
|
||||
<a href="{{ route('home.draft') }}"
|
||||
class="rounded-full px-3 py-1.5 text-center text-muted-foreground transition-colors duration-200 hover:text-foreground">
|
||||
{{ __('ui.palette.draft') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
18
resources/views/components/web/hero-flank-left.blade.php
Normal file
18
resources/views/components/web/hero-flank-left.blade.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{{-- Linke Flanke des Entwurfs-Heros (Nachtrag 19): schmaler Haken unten links, oben gerundet,
|
||||
mit Goldlinie. Höhe rund 46 % des Rahmens, Breite aus dem viewBox-Verhältnis. --}}
|
||||
<svg class="hero-flank hero-flank-left hidden lg:block" viewBox="0 0 80 480" preserveAspectRatio="xMinYMax meet"
|
||||
aria-hidden="true" focusable="false" data-hero-flank="left">
|
||||
<defs>
|
||||
<linearGradient id="hero-flank-left-green" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" style="stop-color: hsl(var(--primary-light))" />
|
||||
<stop offset="1" style="stop-color: hsl(var(--primary-dark))" />
|
||||
</linearGradient>
|
||||
<linearGradient id="hero-flank-left-gold" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" style="stop-color: hsl(var(--secondary-light))" />
|
||||
<stop offset="1" style="stop-color: hsl(var(--secondary)); stop-opacity: 0.5" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M0 480V56C0 25 25 0 56 0H80V480Z" fill="url(#hero-flank-left-green)" />
|
||||
<path d="M14 480V60C14 35 35 14 60 14H80" fill="none" stroke="url(#hero-flank-left-gold)" stroke-width="1.5"
|
||||
vector-effect="non-scaling-stroke" />
|
||||
</svg>
|
||||
24
resources/views/components/web/hero-flank-right.blade.php
Normal file
24
resources/views/components/web/hero-flank-right.blade.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{{-- Rechte Flanke des Entwurfs-Heros (Nachtrag 19): tannengrüner Block mit gerundeter Innenkante,
|
||||
Goldlinie mit Verlauf entlang der Rundung und einer kurzen Zierlinie wie in der Visualisierung.
|
||||
Skaliert über die Rahmenhöhe (height: 100 %, Breite aus dem viewBox-Verhältnis). Farben
|
||||
kommen aus den Tokens, damit die Flanke jede Palette mitmacht. Kann später 1:1 gegen eine
|
||||
gelieferte SVG-Datei getauscht werden. --}}
|
||||
<svg class="hero-flank hero-flank-right hidden lg:block" viewBox="0 0 140 1000" preserveAspectRatio="xMaxYMid meet"
|
||||
aria-hidden="true" focusable="false" data-hero-flank="right">
|
||||
<defs>
|
||||
<linearGradient id="hero-flank-right-green" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" style="stop-color: hsl(var(--primary-light))" />
|
||||
<stop offset="1" style="stop-color: hsl(var(--primary-dark))" />
|
||||
</linearGradient>
|
||||
<linearGradient id="hero-flank-right-gold" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop offset="0" style="stop-color: hsl(var(--secondary-light))" />
|
||||
<stop offset="0.5" style="stop-color: hsl(var(--secondary)); stop-opacity: 0.45" />
|
||||
<stop offset="1" style="stop-color: hsl(var(--secondary-light))" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M140 0H72C32 0 0 32 0 72V928C0 968 32 1000 72 1000H140Z" fill="url(#hero-flank-right-green)" />
|
||||
<path d="M140 14H74C41 14 14 41 14 74V926C14 959 41 986 74 986H140" fill="none"
|
||||
stroke="url(#hero-flank-right-gold)" stroke-width="1.5" vector-effect="non-scaling-stroke" />
|
||||
<path d="M124 330V600" fill="none" stroke="url(#hero-flank-right-gold)" stroke-width="1.5"
|
||||
vector-effect="non-scaling-stroke" />
|
||||
</svg>
|
||||
|
|
@ -565,13 +565,16 @@ $cancelForm = function () {
|
|||
wire:click="addGalleryImage('{{ CmsBrand::GALLERY_PROJECT }}')">Projekt</flux:button>
|
||||
<flux:button size="xs" variant="ghost" icon="plus"
|
||||
wire:click="addGalleryImage('{{ CmsBrand::GALLERY_INSPIRATION }}')">Inspiration</flux:button>
|
||||
<flux:button size="xs" variant="ghost" icon="plus"
|
||||
wire:click="addGalleryImage('{{ CmsBrand::GALLERY_COMPANY }}')">Werkstatt</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 rounded-lg border border-amber-200 bg-amber-50 p-3 text-sm text-amber-800 dark:border-amber-800 dark:bg-amber-900/20 dark:text-amber-200">
|
||||
<strong>Projekt</strong> = tatsächlich ausgeführter Auftrag.
|
||||
<strong>Projekt</strong> = tatsächlich ausgeführter Auftrag (erscheint auch unter „Referenzen“, solange dort nichts gepflegt ist).
|
||||
<strong>Werkstatt</strong> = Einblick in Fertigung oder Montage des Herstellers.
|
||||
<strong>Inspiration</strong> = Entwurf, Rendering oder KI-Darstellung.
|
||||
Auf der Seite werden beide getrennt gezeigt und beschriftet – bitte nichts vermischen.
|
||||
Auf der Seite trägt jedes Bild seine Beschriftung – bitte nichts vermischen.
|
||||
</div>
|
||||
|
||||
@if (empty($gallery))
|
||||
|
|
@ -589,6 +592,7 @@ $cancelForm = function () {
|
|||
<flux:select wire:model="gallery.{{ $i }}.category" size="sm" class="mb-2">
|
||||
<flux:select.option value="{{ CmsBrand::GALLERY_PROJECT }}">Realisiertes Projekt</flux:select.option>
|
||||
<flux:select.option value="{{ CmsBrand::GALLERY_INSPIRATION }}">Inspiration / Entwurf</flux:select.option>
|
||||
<flux:select.option value="{{ CmsBrand::GALLERY_COMPANY }}">Aus der Werkstatt</flux:select.option>
|
||||
</flux:select>
|
||||
|
||||
<flux:input wire:model="gallery.{{ $i }}.caption" size="sm" class="mb-2"
|
||||
|
|
|
|||
|
|
@ -71,13 +71,29 @@
|
|||
<section class="pb-16">
|
||||
<div class="container-narrow space-y-14">
|
||||
@foreach ($brand['sections'] as $index => $section)
|
||||
<div wire:key="section-{{ $index }}">
|
||||
<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 ($section['paragraphs'] as $i => $paragraph)
|
||||
<p class="text-muted-foreground leading-relaxed mt-4">{{ $paragraph }}</p>
|
||||
@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']))
|
||||
|
|
@ -129,6 +145,14 @@
|
|||
@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">→</span>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
|
@ -153,98 +177,65 @@
|
|||
</section>
|
||||
@endif
|
||||
|
||||
{{-- 4. Galerie: realisierte Projekte und Inspirationen strikt getrennt --}}
|
||||
{{-- 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
|
||||
$galleryProjects = $gallery['projects'] ?? [];
|
||||
$galleryInspirations = $gallery['inspirations'] ?? [];
|
||||
$hasBothGalleryGroups = $galleryProjects !== [] && $galleryInspirations !== [];
|
||||
$initialGalleryTab = $galleryProjects !== [] ? 'projects' : 'inspirations';
|
||||
$galleryItems = array_merge($gallery['projects'] ?? [], $gallery['company'] ?? [], $gallery['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();
|
||||
$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 ($galleryProjects || $galleryInspirations)
|
||||
@if ($galleryItems)
|
||||
<section id="galerie" class="section-padding"
|
||||
x-data="{
|
||||
tab: '{{ $initialGalleryTab }}',
|
||||
groups: @js($galleryLightbox),
|
||||
items: @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>
|
||||
<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>
|
||||
|
||||
@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>
|
||||
<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
|
||||
|
||||
<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
|
||||
</figure>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Lightbox: ins body geteleportet, damit keine Eltern-Constraints stören --}}
|
||||
|
|
|
|||
|
|
@ -1,139 +1,102 @@
|
|||
{{-- Hero des Entwurfs (Nachtrag 19): Rahmen mit feiner Goldkante und grünen Flanken, links
|
||||
Text mit einem Button, rechts die Slideshow. Das Bild hat ein festes Seitenverhältnis –
|
||||
das Motiv wird auf großen Bildschirmen nicht mehr unten beschnitten (Briefing Punkt 2).
|
||||
Der Wechsel läuft langsam (Überblendung 1,4 s, Standard alle 7 s), pausiert bei Hover und
|
||||
bleibt bei „reduzierter Bewegung“ stehen. --}}
|
||||
<section id="hero" class="hero-draft section-padding relative">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
|
||||
x-data="{
|
||||
active: 0,
|
||||
count: {{ count($slides) }},
|
||||
interval: {{ $this->intervalMs }},
|
||||
timer: null,
|
||||
paused: false,
|
||||
reduced: window.matchMedia('(prefers-reduced-motion: reduce)').matches,
|
||||
start() {
|
||||
if (this.reduced || this.count < 2) { return; }
|
||||
this.stop();
|
||||
this.timer = setInterval(() => { if (!this.paused && !document.hidden) { this.next(); } }, this.interval);
|
||||
},
|
||||
stop() { if (this.timer) { clearInterval(this.timer); this.timer = null; } },
|
||||
go(i) { this.active = (i + this.count) % this.count; this.start(); },
|
||||
next() { this.go(this.active + 1); },
|
||||
prev() { this.go(this.active - 1); },
|
||||
}"
|
||||
x-init="start()" x-on:keydown.arrow-right.prevent="next()" x-on:keydown.arrow-left.prevent="prev()">
|
||||
|
||||
<div x-data="heroSlider()" x-init="init()">
|
||||
<section class="relative mb-10">
|
||||
<!-- Slider Container -->
|
||||
<div class="absolute inset-0 overflow-hidden">
|
||||
@if(!empty($content['slides']))
|
||||
@foreach($content['slides'] as $index => $slide)
|
||||
<div
|
||||
class="absolute inset-0 transition-opacity duration-1000 ease-in-out"
|
||||
:class="currentSlide === {{ $index }} ? 'opacity-100' : 'opacity-0'"
|
||||
>
|
||||
<img
|
||||
src="{{ theme_image_url($slide['image']) }}"
|
||||
alt="{{ $slide['image_alt'] }}"
|
||||
class="size-full object-cover"
|
||||
/>
|
||||
<div class="absolute inset-0 bg-gray-900 opacity-20"></div>
|
||||
</div>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
<div class="hero-frame relative overflow-hidden rounded-[28px]">
|
||||
<x-web.hero-flank-right />
|
||||
<x-web.hero-flank-left />
|
||||
|
||||
<!-- Content -->
|
||||
<div class="relative mx-auto flex max-w-3xl flex-col items-center px-6 py-32 text-center sm:py-64 lg:px-0">
|
||||
<h1 class="text-hero text-white">
|
||||
{!! $content['title'] !!}
|
||||
</h1>
|
||||
<p class="mt-4 text-xl text-white">{!! $content['subtitle'] !!}</p>
|
||||
<a href="{{ $content['cta1_link'] }}" class="mt-8 inline-block btn-secondary-accent">{{ $content['cta1_text'] }}</a>
|
||||
</div>
|
||||
{{-- Rechts wenig Innenabstand: das Bild liegt über der grünen Flanke, wie in der Visualisierung. --}}
|
||||
<div class="relative grid items-center gap-10 p-6 sm:p-10 lg:grid-cols-12 lg:gap-10 lg:py-5 lg:pl-16 lg:pr-12">
|
||||
{{-- Text --}}
|
||||
<div class="space-y-6 lg:col-span-5 lg:py-10 lg:pr-4">
|
||||
@if (!empty($content['eyebrow']))
|
||||
<p class="hero-eyebrow">{{ $content['eyebrow'] }}</p>
|
||||
@endif
|
||||
|
||||
<!-- Navigation Dots -->
|
||||
@if(!empty($content['slides']) && count($content['slides']) > 1)
|
||||
<div class="absolute bottom-8 left-1/2 transform -translate-x-1/2">
|
||||
<div class="flex space-x-3">
|
||||
@foreach($content['slides'] as $index => $slide)
|
||||
<button
|
||||
@click="setSlide({{ $index }})"
|
||||
class="w-3 h-3 rounded-full border-2 border-white/50 transition-all duration-300"
|
||||
:class="currentSlide === {{ $index }} ? 'bg-white border-white' : 'bg-transparent hover:border-white/80'"
|
||||
aria-label="{{ __('ui.show_slide', ['number' => $index + 1]) }}"
|
||||
></button>
|
||||
@endforeach
|
||||
<h1 class="hero-title">{!! $content['title'] ?? '' !!}</h1>
|
||||
|
||||
@if (!empty($content['subtitle']))
|
||||
<p class="max-w-md text-base leading-relaxed text-muted-foreground">{!! $content['subtitle'] !!}</p>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
<a href="{{ $content['cta_link'] ?? '/marken' }}" class="btn-hero">
|
||||
{{ $content['cta_text'] ?? __('ui.all_brands_cta') }}
|
||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" stroke-width="1.8" viewBox="0 0 24 24"
|
||||
aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M13.5 4.5L21 12m0 0l-7.5 7.5M21 12H3" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Slideshow --}}
|
||||
<div class="hero-image lg:col-span-7">
|
||||
<div class="hero-image-frame">
|
||||
<div class="relative aspect-[4/3] overflow-hidden rounded-[calc(1.25rem-1.5px)] bg-muted lg:aspect-[5/4]"
|
||||
role="group" aria-roledescription="carousel" aria-label="{{ __('ui.hero_slider.label') }}"
|
||||
x-on:mouseenter="paused = true" x-on:mouseleave="paused = false"
|
||||
x-on:focusin="paused = true" x-on:focusout="paused = false" tabindex="0">
|
||||
@forelse ($slides as $index => $slide)
|
||||
<figure wire:key="hero-slide-{{ $index }}" data-hero-slide="{{ $index }}"
|
||||
class="hero-slide absolute inset-0 m-0 {{ $index === 0 ? 'opacity-100' : 'opacity-0' }}"
|
||||
x-bind:class="active === {{ $index }} ? 'opacity-100' : 'opacity-0 pointer-events-none'"
|
||||
x-bind:aria-hidden="(active !== {{ $index }}).toString()">
|
||||
<img src="{{ theme_image_url($slide['image']) }}" alt="{{ $slide['alt'] }}"
|
||||
class="h-full w-full object-cover" @if ($index > 0) loading="lazy" @endif />
|
||||
@if ($slide['label'] !== '')
|
||||
<figcaption
|
||||
class="absolute bottom-5 left-5 rounded-full bg-card/85 px-3 py-1 text-[10px] font-medium uppercase tracking-[0.18em] text-foreground/80 backdrop-blur-sm">
|
||||
{{ $slide['label'] }}
|
||||
</figcaption>
|
||||
@endif
|
||||
</figure>
|
||||
@empty
|
||||
<div class="flex h-full items-center justify-center text-sm text-muted-foreground">
|
||||
{{ __('ui.hero_slider.empty') }}
|
||||
</div>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Arrow Navigation (Optional) -->
|
||||
@if(!empty($content['slides']) && count($content['slides']) > 1)
|
||||
<button
|
||||
@click="previousSlide()"
|
||||
class="absolute left-6 top-1/2 transform -translate-y-1/2 text-white/70 hover:text-white transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-white/50 rounded-full p-2"
|
||||
aria-label="{{ __('ui.previous_image') }}"
|
||||
>
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
@click="nextSlide()"
|
||||
class="absolute right-6 top-1/2 transform -translate-y-1/2 text-white/70 hover:text-white transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-white/50 rounded-full p-2"
|
||||
aria-label="{{ __('ui.next_image') }}"
|
||||
>
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||
</svg>
|
||||
</button>
|
||||
@endif
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function heroSlider() {
|
||||
return {
|
||||
currentSlide: 0,
|
||||
totalSlides: {{ !empty($content['slides']) ? count($content['slides']) : 0 }},
|
||||
autoplayInterval: null,
|
||||
autoplayTimeout: null,
|
||||
|
||||
init() {
|
||||
if (this.totalSlides > 1) {
|
||||
this.startAutoplay();
|
||||
}
|
||||
},
|
||||
|
||||
// Automatischer Wechsel (nur für Intervall)
|
||||
autoNextSlide() {
|
||||
this.currentSlide = (this.currentSlide + 1) % this.totalSlides;
|
||||
},
|
||||
|
||||
// Manuelle Navigation
|
||||
nextSlide() {
|
||||
this.currentSlide = (this.currentSlide + 1) % this.totalSlides;
|
||||
this.resetAutoplay();
|
||||
},
|
||||
|
||||
previousSlide() {
|
||||
this.currentSlide = (this.currentSlide - 1 + this.totalSlides) % this.totalSlides;
|
||||
this.resetAutoplay();
|
||||
},
|
||||
|
||||
setSlide(index) {
|
||||
this.currentSlide = index;
|
||||
this.resetAutoplay();
|
||||
},
|
||||
|
||||
startAutoplay() {
|
||||
// Zuerst alle bestehenden Timer löschen
|
||||
this.stopAutoplay();
|
||||
|
||||
this.autoplayInterval = setInterval(() => {
|
||||
this.autoNextSlide();
|
||||
}, 5000); // Wechsel alle 5 Sekunden
|
||||
},
|
||||
|
||||
stopAutoplay() {
|
||||
if (this.autoplayInterval) {
|
||||
clearInterval(this.autoplayInterval);
|
||||
this.autoplayInterval = null;
|
||||
}
|
||||
if (this.autoplayTimeout) {
|
||||
clearTimeout(this.autoplayTimeout);
|
||||
this.autoplayTimeout = null;
|
||||
}
|
||||
},
|
||||
|
||||
resetAutoplay() {
|
||||
// Alle Timer stoppen
|
||||
this.stopAutoplay();
|
||||
|
||||
// Nach 5 Sekunden Pause wieder starten
|
||||
this.autoplayTimeout = setTimeout(() => {
|
||||
this.startAutoplay();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{{-- Punkte unter dem Rahmen --}}
|
||||
@if (count($slides) > 1)
|
||||
<div class="mt-6 flex items-center justify-center gap-3" role="tablist" aria-label="{{ __('ui.hero_slider.dots') }}">
|
||||
@foreach ($slides as $index => $slide)
|
||||
<button type="button" role="tab" wire:key="hero-dot-{{ $index }}" x-on:click="go({{ $index }})"
|
||||
x-bind:aria-selected="(active === {{ $index }}).toString()"
|
||||
x-bind:class="active === {{ $index }} ? 'bg-primary w-14' : 'bg-border w-10 hover:bg-muted-foreground/40'"
|
||||
class="h-1 cursor-pointer rounded-full transition-all duration-500 {{ $index === 0 ? 'bg-primary w-14' : 'bg-border w-10' }}"
|
||||
aria-label="{{ __('ui.hero_slider.dot', ['n' => $index + 1, 'total' => count($slides), 'label' => $slide['label']]) }}">
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -16,9 +16,12 @@
|
|||
<a href="{{ $content['cta1_link'] }}" class="btn-primary-accent">
|
||||
{{ $content['cta1_text'] }}
|
||||
</a>
|
||||
<x-appointment-link :href="$content['cta2_link']" class="btn-secondary-accent">
|
||||
{{ $content['cta2_text'] }}
|
||||
</x-appointment-link>
|
||||
{{-- Zweiter Button nur, wenn gepflegt – seit 01.10.2026 leer, „Termin anfragen“ bleibt im Widget. --}}
|
||||
@if (filled($content['cta2_text'] ?? null))
|
||||
<x-appointment-link :href="$content['cta2_link'] ?? '/contact'" class="btn-secondary-accent">
|
||||
{{ $content['cta2_text'] }}
|
||||
</x-appointment-link>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@if(isset($content['stats']))
|
||||
|
|
|
|||
|
|
@ -63,7 +63,32 @@
|
|||
<p class="text-sm text-muted-foreground leading-relaxed mt-2">{{ $content['intro'] }}</p>
|
||||
@endif
|
||||
|
||||
<form wire:submit="submit" class="space-y-4 mt-6">
|
||||
{{-- WhatsApp zuerst (Abstimmung 23.09.): der schnellste Weg steht oben, das Formular
|
||||
darunter. Nur sichtbar, wenn im CMS eine Nummer hinterlegt ist. Der Link ist ein
|
||||
normaler Klick-zum-Chat-Link – auf dem Handy öffnet die installierte App, sonst
|
||||
WhatsApp Web. Der Text ist aus dem Formularstand vorbereitet, abgeschickt wird er
|
||||
vom Besucher selbst. --}}
|
||||
@if ($this->whatsappUrl !== '')
|
||||
<div class="mt-5" data-appointment-whatsapp>
|
||||
<p class="text-sm text-muted-foreground">{{ __('ui.appointment.whatsapp_intro') }}</p>
|
||||
<a x-bind:href="whatsappHref" target="_blank" rel="noopener"
|
||||
class="mt-3 flex w-full items-center justify-center gap-2 rounded-lg bg-[#25D366] px-6 py-3 font-medium text-white transition-transform hover:scale-[1.02]">
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.872.118.571-.085 1.758-.719 2.006-1.413.247-.694.247-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884a9.82 9.82 0 016.988 2.896 9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.885-9.885 9.885M20.52 3.449C18.24 1.245 15.24 0 12.045 0 5.463 0 .104 5.359.101 11.945c0 2.096.549 4.142 1.595 5.945L0 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.582 0 11.941-5.359 11.944-11.945a11.87 11.87 0 00-3.417-8.4" />
|
||||
</svg>
|
||||
{{ __('ui.whatsapp') }}
|
||||
</a>
|
||||
<p class="mt-2 text-xs text-muted-foreground">{{ __('ui.appointment.whatsapp_note') }}</p>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3 text-xs uppercase tracking-wide text-muted-foreground" aria-hidden="true">
|
||||
<span class="h-px flex-1 bg-border"></span>
|
||||
<span>{{ __('ui.appointment.or_form') }}</span>
|
||||
<span class="h-px flex-1 bg-border"></span>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form wire:submit="submit" class="space-y-4 mt-5">
|
||||
<div>
|
||||
<label for="appointment-name" class="text-foreground mb-2 block text-sm font-medium">
|
||||
{{ __('ui.appointment.name') }} <span aria-hidden="true">*</span>
|
||||
|
|
@ -150,23 +175,6 @@
|
|||
</button>
|
||||
</form>
|
||||
|
||||
{{-- Alternative: WhatsApp. Nur sichtbar, wenn im CMS eine Nummer hinterlegt ist.
|
||||
Der Link ist ein normaler Klick-zum-Chat-Link – auf dem Handy öffnet die
|
||||
installierte App, sonst WhatsApp Web. Der Text ist vorbereitet, abgeschickt
|
||||
wird er vom Besucher selbst. --}}
|
||||
@if ($this->whatsappUrl !== '')
|
||||
<div class="mt-6 pt-6 border-t border-border/60">
|
||||
<p class="text-sm text-muted-foreground">{{ __('ui.appointment.whatsapp_intro') }}</p>
|
||||
<a x-bind:href="whatsappHref" target="_blank" rel="noopener"
|
||||
class="mt-3 flex w-full items-center justify-center gap-2 rounded-lg bg-[#25D366] px-6 py-3 font-medium text-white transition-transform hover:scale-[1.02]">
|
||||
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.372-.025-.521-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51l-.57-.01c-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.872.118.571-.085 1.758-.719 2.006-1.413.247-.694.247-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884a9.82 9.82 0 016.988 2.896 9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.885-9.885 9.885M20.52 3.449C18.24 1.245 15.24 0 12.045 0 5.463 0 .104 5.359.101 11.945c0 2.096.549 4.142 1.595 5.945L0 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.582 0 11.941-5.359 11.944-11.945a11.87 11.87 0 00-3.417-8.4" />
|
||||
</svg>
|
||||
{{ __('ui.whatsapp') }}
|
||||
</a>
|
||||
<p class="mt-2 text-xs text-muted-foreground">{{ __('ui.appointment.whatsapp_note') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
|
@ -174,7 +182,7 @@
|
|||
<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
|
||||
max-w-[calc(100vw-2rem)] text-left leading-tight">
|
||||
max-w-[calc(100vw-2rem)] text-left leading-tight appointment-toggle">
|
||||
<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"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{{-- Der Footer folgt der Farbwelt der Seite: Interior auf Sand mit Tanne und positivem Logo,
|
||||
Immobilien dunkel mit negativem Logo (Tokens `--footer-*` in theme-b2in.css, Logo in mount()).
|
||||
In der Farbvariante „Modern“ liegt auch der Interior-Footer dunkel und zeigt das negative Logo.
|
||||
pb-28 auf kleinen Schirmen: darunter schwebt das Terminanfrage-Widget, das sonst die
|
||||
letzten Zeilen des Footers verdecken wuerde. --}}
|
||||
<footer id="footer" class="section-footer section-padding pb-28 sm:pb-16">
|
||||
|
|
@ -9,9 +10,16 @@
|
|||
<div class="spacing-section">
|
||||
<div class="flex items-center justify-center">
|
||||
<img src="{{ asset($logoPath) }}"
|
||||
alt="{{ $domainName ?? 'B2in Interior' }} Logo" class="h-16 w-auto"
|
||||
alt="{{ $domainName ?? 'B2in Interior' }} Logo" class="footer-logo-light h-16 w-auto"
|
||||
width="81" height="64"
|
||||
loading="lazy" />
|
||||
@if ($logoPathDark !== '')
|
||||
{{-- Farbvariante „Modern“: dunkler Footer, negatives Logo (Umschaltung per CSS). --}}
|
||||
<img src="{{ asset($logoPathDark) }}"
|
||||
alt="{{ $domainName ?? 'B2in Interior' }} Logo" class="footer-logo-dark h-16 w-auto"
|
||||
width="81" height="64"
|
||||
loading="lazy" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="container-narrow spacing-content">
|
||||
|
|
|
|||
33
resources/views/web/home-draft.blade.php
Normal file
33
resources/views/web/home-draft.blade.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{{-- Entwurf der Startseite in der Farbwelt vom 24.09.2026 (Nachtrag 19). Nur zur Abstimmung:
|
||||
Palette „greige“ am Body, Gold-Logo, neuer Hero mit Slideshow, keine Ankündigungsleiste.
|
||||
Der Rest der Seite ist die reguläre Startseite – die Sektionen färben sich über die Tokens. --}}
|
||||
@extends('web.layouts.web-master')
|
||||
|
||||
@section('title', 'Entwurf – B2in Interior')
|
||||
@section('meta_description', 'Entwurf der Startseite in der neuen Farbwelt – nur zur Abstimmung.')
|
||||
@section('announcement_bar', 'off')
|
||||
|
||||
@push('meta')
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
@endpush
|
||||
|
||||
@section('content')
|
||||
<div class="bg-background">
|
||||
<livewire:web.components.ui.header />
|
||||
|
||||
<main class="variante-glass-flow">
|
||||
<livewire:web.components.sections.hero-slider />
|
||||
<livewire:web.components.sections.founder-bar />
|
||||
<livewire:web.components.sections.audience-switch />
|
||||
<livewire:web.components.sections.core-brands bg="bg-accent" />
|
||||
<livewire:web.components.sections.origin-section />
|
||||
<livewire:web.components.sections.vision-section />
|
||||
<livewire:web.components.sections.sponsoring bg="bg-accent" />
|
||||
<livewire:web.components.sections.display-feed />
|
||||
<livewire:web.components.sections.immobilien-teaser />
|
||||
<livewire:web.components.sections.c-t-a-section />
|
||||
</main>
|
||||
|
||||
<livewire:web.components.ui.footer />
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -1,5 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
@php
|
||||
// Farbpalette der Interior-Welt (Nachtrag 18): ohne Schalter setzt der Server die Klasse aus
|
||||
// config('app.palette'); mit Schalter entscheidet das Skript im <head> anhand des localStorage.
|
||||
$paletteFixed = config('app.theme', 'b2in') === 'b2in'
|
||||
&& b2in_world() === 'interior'
|
||||
&& ! config('app.palette_switch')
|
||||
&& b2in_palette() === ''
|
||||
&& config('app.palette') === 'modern';
|
||||
@endphp
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}"{!! $paletteFixed ? ' class="palette-modern"' : '' !!}>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
|
@ -51,8 +60,40 @@
|
|||
|
||||
@php
|
||||
$theme = config('app.theme', 'b2in');
|
||||
// Farb-Schalter nur auf den Interior-Seiten von b2in, solange die Palette abgestimmt wird.
|
||||
$showPaletteSwitch = $theme === 'b2in' && b2in_world() === 'interior' && config('app.palette_switch');
|
||||
// Entwurf /entwurf (Nachtrag 19): feste Palette „greige“ am Body, kein Browser-Schalter.
|
||||
$paletteDraft = b2in_palette() === 'greige';
|
||||
// Global per B2IN_ANNOUNCEMENT_BAR, einzeln per @section('announcement_bar', 'off').
|
||||
$hideAnnouncementBar = ! config('app.announcement_bar') || trim($__env->yieldContent('announcement_bar', 'on')) === 'off';
|
||||
@endphp
|
||||
|
||||
@if ($showPaletteSwitch && ! $paletteDraft)
|
||||
{{-- Läuft vor dem Stylesheet: setzt die gemerkte Palette, bevor die Seite gezeichnet wird.
|
||||
?palette=modern|classic in der URL überschreibt und merkt sich die Wahl; ohne
|
||||
gemerkte Wahl gilt config('app.palette'). --}}
|
||||
<script>
|
||||
(function () {
|
||||
var fromUrl = new URLSearchParams(window.location.search).get('palette');
|
||||
var palette = null;
|
||||
try {
|
||||
if (fromUrl === 'modern' || fromUrl === 'classic') {
|
||||
localStorage.setItem('b2in-palette', fromUrl);
|
||||
}
|
||||
palette = localStorage.getItem('b2in-palette');
|
||||
} catch (e) {
|
||||
palette = fromUrl;
|
||||
}
|
||||
if (palette === null) {
|
||||
palette = @js(config('app.palette'));
|
||||
}
|
||||
if (palette === 'modern') {
|
||||
document.documentElement.classList.add('palette-modern');
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@endif
|
||||
|
||||
@vite([
|
||||
\App\Helpers\ThemeHelper::getThemeCssPath(),
|
||||
'resources/js/app.js'
|
||||
|
|
@ -113,13 +154,15 @@
|
|||
@stack('styles')
|
||||
</head>
|
||||
|
||||
<body class="antialiased bg-background text-foreground world-{{ b2in_world() }}">
|
||||
<body class="antialiased bg-background text-foreground world-{{ b2in_world() }}{{ $paletteDraft ? ' palette-greige' : '' }}">
|
||||
|
||||
{{-- GTM noscript (nur bei GTM-Nutzung) --}}
|
||||
<x-cookie-consent::gtm-noscript />
|
||||
|
||||
<!-- Announcement Bar -->
|
||||
<livewire:web.components.ui.announcement-bar />
|
||||
{{-- Ankündigungsleiste – global per B2IN_ANNOUNCEMENT_BAR, je Seite mit @section('announcement_bar', 'off') abschaltbar --}}
|
||||
@unless ($hideAnnouncementBar)
|
||||
<livewire:web.components.ui.announcement-bar />
|
||||
@endunless
|
||||
|
||||
{{-- TopBar (Backup: Sprachwechsel & Social Icons – folgt später) --}}
|
||||
{{-- <livewire:web.components.ui.top-bar /> --}}
|
||||
|
|
@ -129,6 +172,10 @@
|
|||
{{-- Terminanfrage: schwebend unten rechts, auf allen Seiten --}}
|
||||
<livewire:web.components.ui.appointment-widget />
|
||||
|
||||
@if ($showPaletteSwitch)
|
||||
<x-palette-switch :draft="$paletteDraft" />
|
||||
@endif
|
||||
|
||||
{{-- Cookie Consent Manager (vor Livewire-Scripts) --}}
|
||||
<x-cookie-consent::manager />
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
@extends('web.layouts.web-master')
|
||||
|
||||
@section('title', 'Unsere Marken – CABINET, Rotpunkt Küchen, Febrü & Tischlerei | B2in Interior')
|
||||
@section('title', 'Unsere Marken – CABINET, Rotpunkt Küchen, Febrü & Otterpohl | B2in Interior')
|
||||
@section('meta_description', 'Ausgewählte Hersteller statt beliebiger Kataloge: maßgefertigte Einbauschränke, Premium-Küchen und Möbel fürs Arbeiten – mit persönlicher Beratung in der Region.')
|
||||
|
||||
@section('content')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue