This commit is contained in:
Lars Nolden
2026-08-13 15:46:04 +02:00
parent 920b1da4b0
commit 2c1a8ae9b5
178 changed files with 10289 additions and 0 deletions
@@ -0,0 +1,115 @@
import * as React from "react"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import { PageBreadcrumb } from "@/components/gebos/page-breadcrumb"
import { PageHero } from "@/components/gebos/page-hero"
import { FeatureTile } from "@/components/gebos/icon-tile"
type AudienceFeature = {
icon?: React.ReactNode
/** self-contained visual (asset-kit icon/vector) rendered without IconBadge */
media?: React.ReactNode
title: React.ReactNode
sub?: React.ReactNode
}
function AudienceBreadcrumb({ current }: { current: string }) {
return (
<PageBreadcrumb
items={[{ label: "Für wen?", to: "/fuer-wen" }, { label: current }]}
/>
)
}
/**
* Shared audience page template (tiles 58):
* split PageHero with breadcrumb, feature tile row, closing slot
* (teal CtaBanner or slim repeat-CTA section).
*/
function AudiencePage({
breadcrumb,
title,
tagline,
lead,
actions,
visual,
features,
children,
closing,
}: {
breadcrumb: string
title: React.ReactNode
/** short bold subline under the H1 (e.g. "Bestellen. Montieren. Fertig.") */
tagline?: React.ReactNode
lead?: React.ReactNode
actions?: React.ReactNode
visual?: React.ReactNode
features: AudienceFeature[]
/** additional full-width spec sections between feature row and closing */
children?: React.ReactNode
closing?: React.ReactNode
}) {
return (
<>
<PageHero
breadcrumb={<AudienceBreadcrumb current={breadcrumb} />}
title={title}
lead={
<>
{tagline ? (
<span className="mb-2 block text-lg font-bold text-navy sm:text-xl">
{tagline}
</span>
) : null}
{lead}
</>
}
actions={actions}
visual={visual}
/>
<Container>
<Section className="pt-2 sm:pt-4 lg:pt-6">
<div className="grid grid-cols-2 gap-x-6 gap-y-10 sm:grid-cols-4">
{features.map((feature, i) => (
<FeatureTile
key={i}
icon={feature.icon}
media={feature.media}
title={feature.title}
sub={feature.sub}
/>
))}
</div>
</Section>
{children}
{closing}
</Container>
</>
)
}
/**
* Slim closing section repeating the hero CTAs
* (tiles 7 and 8 close without a gradient band).
*/
function AudienceClosing({
title,
lead,
actions,
}: {
title: React.ReactNode
lead?: React.ReactNode
actions: React.ReactNode
}) {
return (
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading align="center" title={title} lead={lead} />
<div className="mt-7 flex flex-wrap items-center justify-center gap-3">
{actions}
</div>
</Section>
)
}
export { AudiencePage, AudienceClosing }
export type { AudienceFeature }
@@ -0,0 +1,291 @@
import { Link } from "react-router-dom"
import { Euro, Handshake, ReceiptText, ShieldCheck, Wrench } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import { Section, SectionHeading } from "@/components/gebos/section"
import { CtaBanner } from "@/components/gebos/cta-banner"
import { NumberedList } from "@/components/gebos/process-steps"
import { AssetRender, renders } from "@/components/gebos/site-assets"
import { AudiencePage } from "@/pages/audiences/audience-page"
/**
* Page-local hero composition (05-homeowners kit):
* building render on the right, smoke alarm top-left,
* detached check orb as accent between them.
*/
function HomeownerArt() {
return (
<div className="relative mx-auto aspect-[1.28] w-full max-w-[560px]">
<AssetRender
render={renders.buildingIso}
className="absolute top-[4%] right-0 w-[74%] z-50"
imgClassName="w-full"
loading="eager"
/>
<AssetRender
render={renders.smokeAlarm}
className="absolute top-[8%] left-[2%] w-[26%]"
imgClassName="w-full"
/>
<AssetRender
render={renders.barChart}
alt=""
className="absolute top-[52%] left-[10%] w-[16%] scale-200"
/>
<AssetRender
render={renders.barChart}
alt=""
className="absolute top-[40%] right-[0%] w-[16%] scale-200 z-30"
/>
<AssetRender
render={renders.cubesRound}
alt=""
className="absolute top-[50%] right-[30%] w-[16%] scale-200 z-30"
/>
</div>
)
}
const SELF_INSTALL_STEPS = [
{
title: "Gebäude angeben",
description:
"Sie teilen uns die grundlegenden Informationen zum Gebäude und den Wohnungen mit.",
},
{
title: "Passende Ausstattung zusammenstellen",
description:
"Die benötigten Zähler, Kommunikationskomponenten und gegebenenfalls Rauchwarnmelder werden zusammengestellt.",
},
{
title: "Vorkonfiguriert erhalten",
description:
"Die Geräte werden bereits für Ihr Gebäude vorbereitet geliefert.",
},
{
title: "Montieren",
description:
"Sie selbst, Ihr Hausmeister oder Ihr Fachbetrieb installiert die Geräte.",
},
{
title: "GebOS übernimmt",
description:
"Nach der Inbetriebnahme laufen Datenerfassung und die gebuchten Dienstleistungen über GebOS.",
},
]
const WHY_GEBOS = [
{
title: "Kosten kontrollieren",
text: "Entscheiden Sie selbst, welche Arbeiten Sie übernehmen und welche Sie abgeben möchten.",
},
{
title: "Keine unnötige technische Komplexität",
text: "Die Hardware wird für das jeweilige Gebäude vorbereitet.",
},
{
title: "Bestehenden Handwerker nutzen",
text: "Sie benötigen keinen speziellen GebOS-Installateur.",
},
{
title: "Alles an einem Ort",
text: "UVI, Abrechnung, Geräte und weitere Gebäudedienstleistungen werden zentral verwaltet.",
},
{
title: "Weniger laufender Aufwand",
text: "Nach der Einrichtung werden Verbrauchsdaten automatisch erfasst und verarbeitet.",
},
]
const VARIANT_ROWS = [
["Planung", "gemeinsam mit GebOS", "GebOS"],
["Hardware", "GebOS", "GebOS"],
["Installation", "Kunde / eigener Fachbetrieb", "organisiert durch GebOS"],
["Softwareeinrichtung", "vorbereitet durch GebOS", "GebOS"],
["Laufender Betrieb", "GebOS", "GebOS"],
["Kosten", "möglichst niedrig", "höhere Planungssicherheit und Komfort"],
]
export default function HauseigentuemerPage() {
return (
<AudiencePage
breadcrumb="Hauseigentümer"
title={
<span className="block text-[2.25rem] text-pretty sm:text-[2.75rem]">
Messdienstleistungen für Ihr Mehrfamilienhaus. Einfach organisiert.
</span>
}
lead="UVI, Heizkostenabrechnung und Rauchwarnmelder mit vorkonfigurierter Technik selbst installieren oder als Komplettlösung beauftragen."
actions={
<Button asChild size="lg">
<Link to="/konfigurator">Preis für mein Gebäude berechnen</Link>
</Button>
}
visual={<HomeownerArt />}
features={[
{
icon: <Wrench />,
title: "Selbst installieren",
sub: "Kosten sparen, flexibel bleiben",
},
{
icon: <ShieldCheck />,
title: "Komplettlösung",
sub: "Wir kümmern uns um alles",
},
{
icon: <ReceiptText />,
title: "Transparente Preise",
sub: "Keine versteckten Kosten",
},
{
icon: <Handshake />,
title: "Bestehende Partner",
sub: "Ihre Handwerker bleiben",
},
]}
closing={
<Section className="pt-0 sm:pt-0 lg:pt-0">
<CtaBanner
icon={<Euro />}
title="Welche Lösung passt zu Ihrem Gebäude?"
description="Mit wenigen Angaben zur ersten Preisindikation oder direkt die Komplettlösung anfragen."
actions={
<>
<Button asChild variant="inverse" size="lg">
<Link to="/konfigurator">
Preis für mein Gebäude berechnen
</Link>
</Button>
<Button asChild variant="inverse" size="lg">
<Link to="/kontakt">Komplettlösung anfragen</Link>
</Button>
</>
}
/>
</Section>
}
>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Ihre Entscheidung"
title="Zwei Wege zu GebOS"
/>
<div className="mt-10 grid gap-6 md:grid-cols-2">
<Card className="relative gap-0 overflow-hidden p-7">
<AssetRender
render={renders.drillTool}
className="pointer-events-none absolute -top-2 -right-3 w-28 opacity-90"
/>
<h3 className="pr-24 text-lg font-extrabold tracking-tight text-navy">
Selbst installieren
</h3>
<p className="mt-3 text-sm leading-relaxed text-pretty text-muted-foreground">
Sie möchten Kosten niedrig halten und können die Installation
selbst oder über einen vertrauten Heizungsbauer, Hausmeister
oder anderen Fachbetrieb organisieren? GebOS liefert die
benötigte Technik passend zu Ihrem Gebäude vorkonfiguriert.
</p>
<p className="mt-3 text-sm font-bold leading-relaxed text-navy">
Sie organisieren die Montage. GebOS übernimmt den digitalen
Betrieb.
</p>
<div className="mt-6">
<Button asChild size="sm">
<Link to="/konfigurator">Preis berechnen</Link>
</Button>
</div>
</Card>
<Card className="relative gap-0 overflow-hidden p-7">
<AssetRender
render={renders.shippingBoxMeters}
className="pointer-events-none absolute -top-1 -right-4 w-32"
/>
<h3 className="pr-28 text-lg font-extrabold tracking-tight text-navy">
Komplett installieren lassen
</h3>
<p className="mt-3 text-sm leading-relaxed text-pretty text-muted-foreground">
Sie möchten sich möglichst wenig mit Planung, Beschaffung und
Installation beschäftigen? GebOS organisiert die komplette
Umsetzung für Sie. Von der Ermittlung der benötigten Geräte
über die Installation bis zum laufenden Betrieb erhalten Sie
eine durchgängige Lösung.
</p>
<div className="mt-6">
<Button asChild size="sm" variant="outline">
<Link to="/kontakt">Komplettlösung anfragen</Link>
</Button>
</div>
</Card>
</div>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Schritt für Schritt"
title="So funktioniert die Selbstinstallation"
/>
<NumberedList steps={SELF_INSTALL_STEPS} className="mt-10 max-w-2xl" />
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Ihre Vorteile"
title="Warum GebOS für Hauseigentümer?"
/>
<div className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{WHY_GEBOS.map((item) => (
<Card key={item.title} className="gap-0 p-6">
<h3 className="text-sm font-bold text-navy">{item.title}</h3>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
{item.text}
</p>
</Card>
))}
</div>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Im Vergleich"
title="Welche Variante passt zu mir?"
/>
<Card className="mt-10 gap-0 overflow-hidden p-2 sm:p-4">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[30%]" />
<TableHead>Selbst installieren</TableHead>
<TableHead>Komplettlösung</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{VARIANT_ROWS.map(([label, self, full]) => (
<TableRow key={label}>
<TableCell className="font-bold text-navy">
{label}
</TableCell>
<TableCell className="text-muted-foreground">
{self}
</TableCell>
<TableCell className="text-muted-foreground">
{full}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Card>
</Section>
</AudiencePage>
)
}
@@ -0,0 +1,348 @@
import { Link } from "react-router-dom"
import {
Ban,
Euro,
LayoutDashboard,
Package,
Timer,
TrendingUp,
Unlock,
X,
} from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import { Section, SectionHeading } from "@/components/gebos/section"
import { CtaBanner } from "@/components/gebos/cta-banner"
import { IconBadge } from "@/components/gebos/icon-tile"
import { NumberedList } from "@/components/gebos/process-steps"
import {
AssetRender,
renders,
vectors,
} from "@/components/gebos/site-assets"
import { AudiencePage } from "@/pages/audiences/audience-page"
const deviceExamples = [
"Wärmemengenzähler",
"Warmwasserzähler",
"Kaltwasserzähler",
"Kommunikationsinfrastruktur",
"Rauchwarnmelder",
]
const rolloutSteps = [
{
title: "Portfolio und Objekte erfassen",
description:
"Bei der Auftragserstellung werden die betreffenden Gebäude, Wohnungen und die vorhandene beziehungsweise benötigte Ausstattung erfasst.",
},
{
title: "Gerätebedarf bestimmen",
description: (
<>
Für jedes Objekt wird festgelegt, welche Geräte benötigt werden.
Beispielsweise:
<span className="mt-2 flex flex-wrap gap-1.5">
{deviceExamples.map((device) => (
<Badge key={device} variant="muted">
{device}
</Badge>
))}
</span>
</>
),
},
{
title: "GebOS bereitet den Rollout vor",
description:
"Gebäude und Wohnungen werden im System angelegt und die Hardware wird objektbezogen vorbereitet.",
},
{
title: "Installationsfertig liefern",
description:
"Die Geräte werden passend zum jeweiligen Gebäude beziehungsweise zur jeweiligen Nutzungseinheit ausgeliefert.",
},
{
title: "Bestehende Monteure installieren",
description:
"Eigene Mitarbeiter, Hausmeister oder bestehende Installationspartner können die Montage übernehmen. Eine umfangreiche Softwarekonfiguration vor Ort ist nicht erforderlich.",
},
{
title: "Bewohner einladen",
description:
"Nach der Einrichtung müssen im Wesentlichen noch die relevanten Bewohnerdaten ergänzt beziehungsweise Einladungen zum Mieterportal versendet werden.",
},
]
const noEffortItems = [
{ icon: <Ban />, title: "Keine aufwendige Gerätekonfiguration." },
{ icon: <X />, title: "Keine manuelle Anlage jedes einzelnen Gerätes." },
{
icon: <Unlock />,
title: "Keine spezielle GebOS-Zertifizierung für den Installateur.",
},
]
const portfolioStats = [
{ value: "48", label: "Gebäude" },
{ value: "384", label: "Wohnungen" },
{ value: "1.746", label: "Geräte" },
]
const statusInfos = [
"Auftrag angelegt",
"Hardware vorbereitet",
"Versendet",
"Installation begonnen",
"Installation vollständig",
"Geräte aktiv",
"Bewohner eingeladen",
"Handlungsbedarf",
]
const whyGebos = [
{
title: "Schneller Rollout",
sub: "Standardisierte Abläufe ermöglichen die parallele Umsetzung vieler Objekte.",
},
{
title: "Weniger Administration",
sub: "Gebäudedaten werden möglichst nur einmal erfasst und anschließend weiterverwendet.",
},
{
title: "Vorkonfigurierte Hardware",
sub: "Geräte werden bereits passend zu den jeweiligen Objekten vorbereitet.",
},
{
title: "Bestehende Installationspartner",
sub: "Eigene Hausmeister, technische Mitarbeiter oder externe Fachbetriebe können weiter eingesetzt werden.",
},
{
title: "Zentrale Portfolioübersicht",
sub: "Gebäude, Wohnungen und Geräte lassen sich gemeinsam betrachten.",
},
{
title: "Ein Ansprechpartner",
sub: "Technik und laufende digitale Dienstleistungen greifen ineinander.",
},
]
/** Portfolio/rollout hero per asset kit 06: building, teal bars, consumption ring. */
function PortfolioHeroArt() {
return (
<div className="relative aspect-[1.28] w-full">
<img
src={vectors.tealBars}
alt=""
className="absolute top-[22%] right-0 w-[44%]"
/>
<AssetRender
render={renders.building}
className="absolute inset-x-[8%] top-[4%]"
imgClassName="w-full"
loading="eager"
/>
<img
src={vectors.consumptionRing}
alt=""
className="absolute bottom-[3%] left-[2%] w-[28%]"
/>
</div>
)
}
export default function HausverwaltungenPage() {
return (
<AudiencePage
breadcrumb="Hausverwaltungen"
title="Für Hausverwaltungen"
tagline="Skalieren Sie Ihre Bestände einfach."
lead="Rollout über viele Gebäude mit minimalem Aufwand von der Planung bis zum laufenden Betrieb."
actions={
<>
<Button asChild size="lg">
<Link to="/konfigurator">Portfolio kalkulieren</Link>
</Button>
<Button asChild size="lg" variant="outline">
<Link to="/kontakt">Rollout besprechen</Link>
</Button>
</>
}
visual={<PortfolioHeroArt />}
features={[
{
icon: <Package />,
title: "Vorkonfigurierte Hardware",
},
{
icon: <Timer />,
title: "Wenig Aufwand vor Ort",
},
{
icon: <LayoutDashboard />,
title: "Zentrale Übersicht",
},
{
icon: <TrendingUp />,
title: "Skalierbar für wachsende Portfolios",
},
]}
closing={
<Section className="pt-0 sm:pt-0 lg:pt-0">
<CtaBanner
icon={<Euro />}
title="Wie sieht Ihr Portfolio aus?"
description="Erhalten Sie zunächst mit wenigen Angaben eine Preisindikation für mehrere Gebäude oder sprechen Sie mit uns über vorhandene Messtechnik und den geplanten Rollout."
actions={
<>
<Button asChild variant="inverse" size="lg">
<Link to="/konfigurator">Portfolio kalkulieren</Link>
</Button>
<Button asChild variant="inverse" size="lg">
<Link to="/kontakt">Rollout besprechen</Link>
</Button>
</>
}
/>
</Section>
}
>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
eyebrow="Skalierung"
title="Weniger Projektaufwand pro Gebäude"
lead="Bei einem einzelnen Gebäude sind manuelle Arbeitsschritte überschaubar. Bei dutzenden Gebäuden werden sie zum Problem. GebOS reduziert genau diese wiederkehrenden Aufgaben."
/>
<Card className="mx-auto mt-8 max-w-xl border-l-4 border-l-primary">
<CardContent className="py-6 text-center">
<p className="text-lg font-bold text-balance text-navy">
Objekte einmal erfassen. Geräte vorbereitet erhalten. Montieren.
Betreiben.
</p>
</CardContent>
</Card>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Ablauf"
title="Bestellung und Installation greifen ineinander"
/>
<NumberedList className="mt-10 max-w-2xl" steps={rolloutSteps} />
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
title="Der Monteur montiert. GebOS kümmert sich um den Rest."
lead="Die Installation soll nicht davon abhängen, dass vor Ort ein speziell geschulter GebOS-Techniker verfügbar ist."
/>
<div className="mt-10 grid gap-6 sm:grid-cols-3">
{noEffortItems.map((item, i) => (
<Card key={i}>
<CardContent className="flex flex-col items-center gap-4 py-6 text-center">
<IconBadge variant="soft" shape="squircle" size="lg">
{item.icon}
</IconBadge>
<p className="text-sm font-bold text-balance text-navy">
{item.title}
</p>
</CardContent>
</Card>
))}
</div>
<p className="mt-8 text-center text-base text-muted-foreground">
Damit können bestehende Installationsstrukturen weiter genutzt
werden.
</p>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
eyebrow="Übersicht"
title="Rollout zentral überblicken"
lead="Bei größeren Portfolios reicht es nicht, Geräte lediglich zu installieren. Die Verwaltung muss erkennen können, wo der Rollout steht und wo Handlungsbedarf besteht."
/>
<Card className="mx-auto mt-10 max-w-3xl shadow-card-lg">
<CardContent className="py-6">
<div className="flex flex-wrap items-start justify-between gap-6">
<div className="min-w-0 flex-1">
<p className="text-xs font-bold tracking-[0.14em] text-primary uppercase">
Beispiel
</p>
<h3 className="mt-1 text-xl font-extrabold tracking-tight text-navy">
Portfolio
</h3>
<div className="mt-5 flex flex-wrap gap-x-10 gap-y-4">
{portfolioStats.map((stat) => (
<div key={stat.label}>
<div className="text-3xl font-extrabold tracking-tight text-navy">
{stat.value}
</div>
<div className="text-xs font-medium text-muted-foreground">
{stat.label}
</div>
</div>
))}
</div>
<div className="mt-6 flex flex-wrap gap-2">
<Badge>43 Gebäude aktiv</Badge>
<Badge variant="muted">3 in Installation</Badge>
<Badge variant="destructive">2 mit Handlungsbedarf</Badge>
</div>
</div>
<img
src={vectors.consumptionRing}
alt=""
className="w-36 shrink-0 max-sm:hidden"
/>
</div>
<Separator className="my-6" />
<p className="text-xs font-bold text-muted-foreground">
Mögliche Statusinformationen
</p>
<div className="mt-2.5 flex flex-wrap gap-1.5">
{statusInfos.map((status) => (
<Badge key={status} variant="outline">
{status}
</Badge>
))}
</div>
</CardContent>
</Card>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
title="Warum GebOS für Hausverwaltungen?"
/>
<div className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{whyGebos.map((item) => (
<Card key={item.title}>
<CardContent className="py-5">
<h3 className="text-sm font-bold text-navy">{item.title}</h3>
<p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">
{item.sub}
</p>
</CardContent>
</Card>
))}
</div>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
title="Vom kleinen Bestand bis zum wachsenden Portfolio"
lead="GebOS soll nicht verlangen, dass eine Hausverwaltung zunächst eine eigene Messdienst-Infrastruktur aufbaut. Neue Gebäude können schrittweise ergänzt und nach demselben standardisierten Prozess ausgerollt werden."
/>
</Section>
</AudiencePage>
)
}
@@ -0,0 +1,327 @@
import { Link } from "react-router-dom"
import {
CircleCheck,
GraduationCap,
Package,
PackageX,
Unlock,
Users,
Wrench,
} from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Checklist } from "@/components/gebos/checklist"
import { IconBadge } from "@/components/gebos/icon-tile"
import { ProcessSteps } from "@/components/gebos/process-steps"
import { Section, SectionHeading } from "@/components/gebos/section"
import { AssetRender, renders, vectors } from "@/components/gebos/site-assets"
import { AudiencePage, AudienceClosing } from "@/pages/audiences/audience-page"
const KUNDEN_BEDARFE = [
"UVI",
"Heizkostenabrechnung",
"fernauslesbare Messtechnik",
"Rauchwarnmelder",
]
const KEINE_HUERDEN = [
{
icon: <Unlock />,
title: "Keine verpflichtende Partnerschaft",
},
{
icon: <PackageX />,
title: "Keine Mindestabnahme",
},
{
icon: <GraduationCap />,
title: "Keine verpflichtende GebOS-Vertriebsschulung",
},
{
icon: <Wrench />,
title: "Keine komplizierte Softwareeinrichtung auf der Baustelle",
},
]
const GEBOS_BEREITET_VOR = [
"Gebäudestruktur",
"Wohnungen beziehungsweise Nutzungseinheiten",
"Gerätezuordnung",
"benötigte Hardware",
"Kommunikationsinfrastruktur",
"Softwarestruktur",
]
const SIE_UEBERNEHMEN = [
"fachgerechte Montage",
"gegebenenfalls Austausch vorhandener Geräte",
"Einbau der vorgesehenen Messtechnik",
"Abschluss der Installation",
]
export default function InstallateurePage() {
return (
<AudiencePage
breadcrumb="Installateure & Heizungsbauer"
title={
<>
Für Installateure &amp;
<br />
Heizungsbauer
</>
}
tagline="Bestellen. Montieren. Fertig."
lead="GebOS für Ihre Kunden installieren ohne Partnerprogramm, verpflichtende Zertifizierung oder aufwendige Softwarekonfiguration."
actions={
<Button asChild size="lg">
<Link to="/konfigurator">Gebäude für meinen Kunden konfigurieren</Link>
</Button>
}
visual={
<div className="relative aspect-[1.28] w-full">
<img
src={vectors.checkOrb}
alt=""
className="absolute top-[6%] right-[8%] w-[16%]"
/>
<AssetRender
render={renders.heatMeter}
className="absolute top-[30%] left-[2%] w-[38%]"
imgClassName="w-full"
/>
<AssetRender
render={renders.gateway}
className="absolute top-[24%] right-[16%] w-[46%]"
imgClassName="w-full"
/>
<img
src={vectors.glassCubes}
alt=""
className="absolute right-[1%] bottom-[10%] w-[26%]"
/>
</div>
}
features={[
{
icon: <Unlock />,
title: "Keine Partnerpflichten",
sub: "Keine Mindestmengen",
},
{
icon: <Package />,
title: "Vorkonfigurierte Hardware",
},
{
icon: <Wrench />,
title: "Einfache Installation",
sub: "Keine Softwarekonfiguration",
},
{
icon: <Users />,
title: "Ihre Kunden",
sub: "Bleiben Ihre Kunden",
},
]}
closing={
<AudienceClosing
title="GebOS bei einem Kunden einsetzen?"
lead="Stellen Sie mit wenigen Angaben zunächst die ungefähre Ausstattung und den Preis für das Gebäude zusammen."
actions={
<Button asChild size="lg">
<Link to="/konfigurator">
Gebäude für meinen Kunden konfigurieren
</Link>
</Button>
}
/>
}
>
{/* 1. Ihr Kunde braucht eine Messdienstlösung? */}
<Section className="pt-4 sm:pt-6 lg:pt-8">
<SectionHeading
eyebrow="Ausgangslage"
title="Ihr Kunde braucht eine Messdienstlösung?"
lead="Sie betreuen ein Mehrfamilienhaus und Ihr Kunde benötigt beispielsweise:"
/>
<div className="mt-6 flex flex-wrap gap-2.5">
{KUNDEN_BEDARFE.map((bedarf) => (
<Badge
key={bedarf}
variant="outline"
className="px-4 py-1.5 text-sm font-semibold text-navy"
>
<CircleCheck className="text-primary" />
{bedarf}
</Badge>
))}
</div>
<p className="mt-6 max-w-2xl text-base leading-relaxed text-muted-foreground">
Dann können Sie GebOS einfach für das betreffende Gebäude einsetzen.{" "}
<strong className="font-semibold text-navy">
Sie müssen dafür nicht zunächst Teil eines Partnerprogramms werden.
</strong>
</p>
</Section>
{/* 2. So einfach soll die Installation sein */}
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Ablauf"
title="So einfach soll die Installation sein"
align="center"
/>
<ProcessSteps
className="mt-12"
steps={[
{
media: (
<AssetRender
render={renders.building}
className="flex h-16 items-center justify-center"
imgClassName="h-16 w-auto"
/>
),
title: "Gebäude erfassen",
description:
"Die benötigte Ausstattung wird für das konkrete Objekt zusammengestellt.",
},
{
media: (
<img
src={vectors.packageBox}
alt=""
className="h-16 w-16 object-contain"
/>
),
title: "Geräte bestellen",
description:
"GebOS liefert die zum Gebäude passenden Geräte.",
},
{
media: (
<AssetRender
render={renders.gateway}
className="flex h-16 items-center justify-center"
imgClassName="h-16 w-auto"
/>
),
title: "Vorkonfiguriert erhalten",
description:
"Die Hardware wird bereits objektbezogen vorbereitet.",
},
{
media: (
<AssetRender
render={renders.heatMeter}
className="flex h-16 items-center justify-center"
imgClassName="h-16 w-auto"
/>
),
title: "Montieren",
description: "Sie übernehmen die fachgerechte Installation.",
},
{
media: (
<img
src={vectors.checkOrb}
alt=""
className="h-16 w-16 object-contain"
/>
),
title: "Fertig",
description:
"Die digitale Einrichtung und der laufende Betrieb werden von GebOS übernommen.",
},
]}
/>
</Section>
{/* 3. Kein klassisches Partnerprogramm erforderlich */}
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Ohne Einstiegshürden"
title="Kein klassisches Partnerprogramm erforderlich"
lead="Für die normale Installation von GebOS sollen keine unnötigen Einstiegshürden entstehen."
align="center"
/>
<div className="mt-10 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{KEINE_HUERDEN.map((item, i) => (
<Card key={i} className="items-start gap-4 p-6">
<IconBadge variant="soft" shape="squircle">
{item.icon}
</IconBadge>
<p className="text-sm font-bold leading-snug text-navy">
{item.title}
</p>
</Card>
))}
</div>
<p className="mx-auto mt-8 max-w-2xl text-center text-base leading-relaxed text-muted-foreground">
<strong className="font-semibold text-navy">
Der Fachbetrieb bleibt der Fachbetrieb des Kunden.
</strong>{" "}
GebOS stellt das Produkt und die digitale Infrastruktur bereit.
</p>
</Section>
{/* 4. Sie können bei Ihren bestehenden Kunden bleiben */}
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
title="Sie können bei Ihren bestehenden Kunden bleiben"
lead={
<>
GebOS ist besonders interessant, wenn ein Kunde Sie ohnehin mit
Arbeiten an Heizungs-, Wasser- oder Gebäudetechnik beauftragt und
zusätzlich eine Lösung für Messdienstleistungen benötigt. Sie
können die Installation übernehmen, ohne selbst einen
vollständigen Messdienst aufbauen zu müssen.
</>
}
/>
</Section>
{/* 5. Was GebOS vorbereitet / Was Sie übernehmen */}
<Section className="pt-0 sm:pt-0 lg:pt-0">
<div className="grid gap-6 lg:grid-cols-2">
<Card className="gap-0 p-6 sm:p-8">
<h3 className="text-xl font-extrabold tracking-tight text-navy">
Was GebOS vorbereitet
</h3>
<p className="mt-1 text-sm text-muted-foreground">Je nach Auftrag:</p>
<Checklist className="mt-5" items={GEBOS_BEREITET_VOR} />
<p className="mt-5 text-sm leading-relaxed text-muted-foreground">
Damit soll vor Ort möglichst wenig zusätzliche digitale Arbeit
entstehen.
</p>
</Card>
<Card className="gap-0 p-6 sm:p-8">
<h3 className="text-xl font-extrabold tracking-tight text-navy">
Was Sie übernehmen
</h3>
<p className="mt-1 text-sm text-muted-foreground">
Das Handwerk bleibt bei Ihnen:
</p>
<Checklist className="mt-5" items={SIE_UEBERNEHMEN} />
</Card>
</div>
</Section>
{/* 6. Und wenn Sie regelmäßig GebOS installieren? */}
<Section className="pt-0 pb-10 sm:pt-0 sm:pb-12 lg:pt-0 lg:pb-14">
<div className="mx-auto max-w-2xl text-center">
<h2 className="text-2xl font-extrabold tracking-tight text-balance text-navy sm:text-3xl">
Und wenn Sie regelmäßig GebOS installieren?
</h2>
<p className="mt-4 text-base leading-relaxed text-muted-foreground">
Auch dann soll der Einstieg freiwillig bleiben. Für Fachbetriebe mit
größerem Volumen können später zusätzliche Konditionen oder
Leistungen angeboten werden. Das normale Installieren von GebOS
bleibt davon unabhängig.
</p>
</div>
</Section>
</AudiencePage>
)
}
@@ -0,0 +1,271 @@
import { Link } from "react-router-dom"
import { ArrowDown, Code2, Layers, ShieldCheck, Tag } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Section, SectionHeading } from "@/components/gebos/section"
import { Checklist } from "@/components/gebos/checklist"
import {
AssetRender,
renders,
vectors,
} from "@/components/gebos/site-assets"
import { AudiencePage, AudienceClosing } from "@/pages/audiences/audience-page"
const platformRequirements = [
"kompatible Messtechnik",
"Kommunikationsinfrastruktur",
"Gateways",
"sichere Datenerfassung",
"Geräteverwaltung",
"Monitoring",
"Verbrauchsdatenverarbeitung",
"Nutzerportale",
"Schnittstellen",
"laufender technischer Betrieb",
]
const whiteLabelItems = [
"eigenes Branding",
"eigene Kundenbeziehung",
"mandantenfähige Verwaltung",
"Endkundenportale",
"APIs und Datenexporte",
"Geräteverwaltung",
"technischer Support",
]
const platformCapabilities = [
{
title: "Gerätekompatibilität",
sub: "Einbindung unterschiedlicher Zähler- und Gerätetypen auf Basis geeigneter Standards.",
},
{
title: "Gateway-Infrastruktur",
sub: "Zentrale Erfassung und sichere Übertragung der Gerätedaten.",
},
{
title: "Gerätemanagement",
sub: "Verwaltung großer Gerätebestände über Gebäude und Kunden hinweg.",
},
{
title: "Monitoring",
sub: "Probleme und Ausfälle erkennen, bevor daraus manueller Aufwand beim Kunden entsteht.",
},
{
title: "APIs und Integrationen",
sub: "Anbindung an eigene Systeme und bestehende Geschäftsprozesse.",
},
{
title: "Mandantenfähigkeit",
sub: "Kunden und Bestände getrennt innerhalb einer gemeinsamen Plattform verwalten.",
},
{
title: "White Label",
sub: "GebOS-Infrastruktur unter der Marke des Messdienstleisters einsetzen.",
},
]
const businessComponents = [
"Einkaufspreise für Hardware",
"Plattformgebühren",
"Mengenstaffeln",
"wiederkehrende Gebühren",
"White-Label-Konditionen",
"Support- und Servicelevel",
"mögliche Mindestmengen",
]
const stackFlow = ["Ihre Kunden", "Ihr Messdienst", "GebOS Plattform"]
/** Platform hero per asset kit 07: laptop dashboard, glass cubes, cloud support. */
function PlatformHeroArt() {
return (
<div className="relative flex aspect-[1.45] w-full items-center">
<img
src={vectors.cloudSupport}
alt=""
className="absolute top-0 right-0 w-[26%]"
/>
<AssetRender
render={renders.laptopDashboard}
className="mx-auto w-full"
imgClassName="w-full"
loading="eager"
/>
<img
src={vectors.glassCubes}
alt=""
className="absolute bottom-[2%] left-0 w-[24%]"
/>
</div>
)
}
export default function MessdienstleisterPage() {
return (
<AudiencePage
breadcrumb="Messdienstleister"
title="Für Messdienstleister"
tagline="Ihre Services. Unsere Plattform."
lead="Nutzen Sie GebOS als technische Grundlage für Ihren eigenen Messdienst skalierbar und zuverlässig."
actions={
<>
<Button asChild size="lg">
<Link to="/kontakt">Plattform kennenlernen</Link>
</Button>
<Button asChild size="lg" variant="outline">
<Link to="/kontakt">Konditionen anfragen</Link>
</Button>
</>
}
visual={<PlatformHeroArt />}
features={[
{
icon: <Tag />,
title: "White Label",
sub: "Ihr Branding, Ihre Kunden",
},
{
icon: <Code2 />,
title: "Offene APIs",
sub: "Einfach integrieren",
},
{
icon: <Layers />,
title: "Skalierbare Plattform",
sub: "Für jedes Geschäftsmodell",
},
{
icon: <ShieldCheck />,
title: "Zuverlässiger Betrieb",
sub: "Sicher und performant",
},
]}
closing={
<AudienceClosing
title="Möchten Sie GebOS für Ihren Messdienst einsetzen?"
lead="Wir zeigen Ihnen Plattform, technische Möglichkeiten und mögliche Geschäftsmodelle."
actions={
<>
<Button asChild size="lg">
<Link to="/kontakt">Plattform kennenlernen</Link>
</Button>
<Button asChild size="lg" variant="outline">
<Link to="/kontakt">Konditionen anfragen</Link>
</Button>
</>
}
/>
}
>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Infrastruktur"
title="Messdienstleistungen anbieten, ohne die gesamte Plattform selbst zu entwickeln"
lead="Moderne Messdienstleistungen benötigen mehr als einzelne Zähler. Erforderlich sind unter anderem:"
/>
<div className="mt-8 grid max-w-3xl gap-x-10 gap-y-2.5 sm:grid-cols-2">
<Checklist items={platformRequirements.slice(0, 5)} />
<Checklist items={platformRequirements.slice(5)} />
</div>
<p className="mt-8 text-base font-bold text-navy">
GebOS stellt diese Infrastruktur als Plattform bereit.
</p>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
title="Konzentrieren Sie sich auf Ihre Kunden"
lead="Der Messdienstleister betreut seine Kunden und verkauft seine Dienstleistungen. GebOS stellt die technische Infrastruktur im Hintergrund bereit."
/>
<Card className="mx-auto mt-10 max-w-md">
<CardContent className="flex flex-col items-center gap-2.5 py-8">
{stackFlow.map((label) => (
<div
key={label}
className="flex flex-col items-center gap-2.5"
>
<div className="rounded-full border border-border bg-card px-6 py-2.5 text-sm font-bold text-navy shadow-pill">
{label}
</div>
<ArrowDown className="size-4 text-primary" />
</div>
))}
<div className="rounded-full bg-brand-gradient px-6 py-2.5 text-center text-sm font-bold text-white shadow-band">
Hardware · Datenerfassung · Software · Betrieb
</div>
</CardContent>
</Card>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
eyebrow="Ihre Marke"
title="White Label"
lead="GebOS kann als technische Grundlage für Dienstleistungen unter Ihrer eigenen Marke eingesetzt werden. Je nach Geschäftsmodell können unter anderem relevant sein:"
/>
<div className="mt-8 grid max-w-3xl gap-x-10 gap-y-2.5 sm:grid-cols-2">
<Checklist items={whiteLabelItems.slice(0, 4)} />
<Checklist items={whiteLabelItems.slice(4)} />
</div>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
eyebrow="Technik"
title="Technische Plattform"
lead="Für Messdienstleister steht die technische Leistungsfähigkeit deutlich stärker im Vordergrund."
/>
<div className="mt-10 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{platformCapabilities.map((item) => (
<Card key={item.title}>
<CardContent className="py-5">
<h3 className="text-sm font-bold text-navy">{item.title}</h3>
<p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">
{item.sub}
</p>
</CardContent>
</Card>
))}
</div>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
eyebrow="Konditionen"
title="Das Geschäftsmodell muss funktionieren"
lead="Eine Plattform für Messdienstleister ist nur interessant, wenn auch der wirtschaftliche Rahmen stimmt. Deshalb werden kommerzielle Konditionen individuell beziehungsweise mengenabhängig vereinbart mit anderen Geschäftsmodellen, Mengenstaffeln und gegebenenfalls White-Label-Konditionen als im öffentlichen Preiskonfigurator."
/>
<div className="mx-auto mt-8 max-w-2xl text-center">
<p className="text-xs font-bold text-muted-foreground">
Mögliche Bestandteile
</p>
<div className="mt-2.5 flex flex-wrap justify-center gap-1.5">
{businessComponents.map((component) => (
<Badge key={component} variant="outline">
{component}
</Badge>
))}
</div>
<p className="mt-6 text-sm text-muted-foreground italic">
Die konkreten Konditionen werden nicht öffentlich auf der Website
dargestellt.
</p>
</div>
</Section>
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
align="center"
title="Gemeinsam skalieren"
lead="GebOS soll insbesondere Messdienstleistern ermöglichen, neue digitale Dienstleistungen anzubieten, ohne dafür eine vollständige eigene Hardware- und Softwareentwicklung aufbauen zu müssen. Der Messdienstleister behält dabei seine Kundenbeziehung und sein eigenes Geschäftsmodell."
/>
</Section>
</AudiencePage>
)
}
@@ -0,0 +1,553 @@
import * as React from "react"
import { Link } from "react-router-dom"
import { ArrowRight, Euro, Plus, X } from "lucide-react"
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Checkbox } from "@/components/ui/checkbox"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
import { Checklist } from "@/components/gebos/checklist"
import { DonutIllustration } from "@/components/gebos/illustrations"
import { IconBadge } from "@/components/gebos/icon-tile"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import { PageBreadcrumb } from "@/components/gebos/page-breadcrumb"
import { icons, vectors } from "@/components/gebos/site-assets"
type Heat = "heizkoerper" | "fussbodenheizung" | "gemischt" | "unbekannt"
type Building = {
id: number
adresse: string
heat: Heat
wohnungen: number
zimmer: number
}
const HEAT_OPTIONS: { value: Heat; label: string }[] = [
{ value: "heizkoerper", label: "Heizkörper" },
{ value: "fussbodenheizung", label: "Fußbodenheizung" },
{ value: "gemischt", label: "Gemischt" },
{ value: "unbekannt", label: "Weiß ich nicht" },
]
const INITIAL_BUILDINGS: Building[] = [
{ id: 1, adresse: "", heat: "heizkoerper", wohnungen: 12, zimmer: 3 },
{ id: 2, adresse: "", heat: "heizkoerper", wohnungen: 8, zimmer: 3 },
{ id: 3, adresse: "", heat: "heizkoerper", wohnungen: 6, zimmer: 3 },
]
const UVI_MONTHLY = 10.5
const INSPECTION_MONTHLY = 2.2
const RWM_ONE_TIME = 42
function formatEuro(value: number) {
return value.toLocaleString("de-DE", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
})
}
function StepCard({
step,
title,
children,
}: {
step: number
title: string
children: React.ReactNode
}) {
return (
<Card className="gap-0 p-6 sm:p-7">
<div className="flex items-center gap-3">
<span className="flex size-8 shrink-0 items-center justify-center rounded-full bg-primary text-sm font-extrabold text-primary-foreground">
{step}
</span>
<h3 className="text-lg font-extrabold tracking-tight text-navy">
{title}
</h3>
</div>
<div className="mt-5">{children}</div>
</Card>
)
}
export default function ConfiguratorPage() {
const [uvi, setUvi] = React.useState(true)
const [rwm, setRwm] = React.useState(true)
const [buildings, setBuildings] = React.useState<Building[]>(INITIAL_BUILDINGS)
const [email, setEmail] = React.useState("")
const nextId = React.useRef(INITIAL_BUILDINGS.length + 1)
const updateBuilding = (id: number, patch: Partial<Building>) => {
setBuildings((prev) => prev.map((b) => (b.id === id ? { ...b, ...patch } : b)))
}
const addBuilding = () => {
setBuildings((prev) => [
...prev,
{ id: nextId.current++, adresse: "", heat: "heizkoerper", wohnungen: 8, zimmer: 3 },
])
}
const removeBuilding = (id: number) => {
setBuildings((prev) => (prev.length > 1 ? prev.filter((b) => b.id !== id) : prev))
}
const totalBuildings = buildings.length
const totalUnits = buildings.reduce((sum, b) => sum + (b.wohnungen || 0), 0)
const sensors = buildings.reduce(
(sum, b) => sum + Math.round((b.wohnungen || 0) * (b.zimmer || 0) * 0.92),
0
)
const smokeDetectors = buildings.reduce(
(sum, b) => sum + Math.round((b.wohnungen || 0) * ((b.zimmer || 0) + 0.7)),
0
)
const monthly =
(uvi ? UVI_MONTHLY : 0) + (uvi || rwm ? INSPECTION_MONTHLY : 0)
return (
<Container>
<Section className="pt-6 sm:pt-7 lg:pt-8">
<div className="relative">
<PageBreadcrumb
className="mb-4"
items={[
{ label: "Startseite", to: "/" },
{ label: "Konfigurator" },
]}
/>
<SectionHeading
title="Konfigurator"
lead="In 4 Schritten zur Preisindikation."
/>
<div
aria-hidden="true"
className="pointer-events-none absolute -top-6 right-0 hidden items-end lg:flex"
>
<img src={vectors.euroOrb} alt="" className="w-40" />
<img
src={vectors.consumptionRing}
alt=""
className="-mb-3 -ml-6 w-44"
/>
</div>
</div>
<div className="mt-10 grid items-start gap-8 lg:grid-cols-[minmax(0,1fr)_380px]">
{/* Left column: steps */}
<div className="flex flex-col gap-6">
<StepCard step={1} title="Leistungen wählen">
<div className="flex flex-col gap-4">
<label className="flex cursor-pointer items-start gap-3">
<Checkbox
checked={uvi}
onCheckedChange={(v) => setUvi(v === true)}
className="mt-0.5"
/>
<span>
<span className="block text-sm font-bold text-navy">UVI</span>
<span className="block text-sm text-muted-foreground">
Unterjährige Verbrauchsinformation inklusive Messtechnik
</span>
</span>
</label>
<label className="flex cursor-pointer items-start gap-3">
<Checkbox
checked={rwm}
onCheckedChange={(v) => setRwm(v === true)}
className="mt-0.5"
/>
<span>
<span className="block text-sm font-bold text-navy">
Rauchwarnmelder
</span>
<span className="block text-sm text-muted-foreground">
Rauchwarnmelder inklusive digitaler Dienstleistungen
</span>
</span>
</label>
</div>
</StepCard>
<StepCard step={2} title="Gebäude angeben">
<div className="flex flex-col gap-5">
{buildings.map((b, i) => (
<div
key={b.id}
className="rounded-xl border border-border bg-brand-50/40 p-4 sm:p-5"
>
<div className="flex items-center justify-between">
<div className="text-sm font-bold text-navy">
Gebäude {i + 1}
</div>
{buildings.length > 1 ? (
<Button
type="button"
variant="ghost"
size="icon"
className="size-7 text-muted-foreground"
aria-label={`Gebäude ${i + 1} entfernen`}
onClick={() => removeBuilding(b.id)}
>
<X />
</Button>
) : null}
</div>
<div className="mt-3 grid gap-3 sm:grid-cols-2">
<div className="flex flex-col gap-1.5 sm:col-span-2">
<Label htmlFor={`adresse-${b.id}`}>
Adresse{" "}
<span className="font-normal text-muted-foreground">
(optional)
</span>
</Label>
<Input
id={`adresse-${b.id}`}
placeholder="Venloer Straße 123, 50823 Köln"
value={b.adresse}
onChange={(e) =>
updateBuilding(b.id, { adresse: e.target.value })
}
/>
</div>
<div className="flex flex-col gap-1.5 sm:col-span-2">
<Label htmlFor={`heat-${b.id}`}>Wärmeverteilung</Label>
<Select
value={b.heat}
onValueChange={(v) =>
updateBuilding(b.id, { heat: v as Heat })
}
>
<SelectTrigger id={`heat-${b.id}`} className="w-full">
<SelectValue />
</SelectTrigger>
<SelectContent>
{HEAT_OPTIONS.map((opt) => (
<SelectItem key={opt.value} value={opt.value}>
{opt.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-1.5">
<Label htmlFor={`wohnungen-${b.id}`}>Wohnungen</Label>
<Input
id={`wohnungen-${b.id}`}
type="number"
min={1}
value={b.wohnungen}
onChange={(e) =>
updateBuilding(b.id, {
wohnungen: Math.max(
0,
Math.round(Number(e.target.value) || 0)
),
})
}
/>
</div>
<div className="flex flex-col gap-1.5">
<Label htmlFor={`zimmer-${b.id}`}>Ø Zimmer</Label>
<Input
id={`zimmer-${b.id}`}
type="number"
min={1}
value={b.zimmer}
onChange={(e) =>
updateBuilding(b.id, {
zimmer: Math.max(0, Number(e.target.value) || 0),
})
}
/>
</div>
</div>
</div>
))}
<Button
type="button"
variant="outline"
className="self-start"
onClick={addBuilding}
>
<Plus /> Gebäude hinzufügen
</Button>
</div>
</StepCard>
<StepCard step={3} title="Preis sehen">
<p className="text-sm text-muted-foreground">
Sofortige erste Preisindikation.
</p>
<Accordion type="single" collapsible className="mt-3">
<AccordionItem value="annahmen" className="border-b-0">
<AccordionTrigger className="text-sm font-bold text-navy">
Wie wurde dieser Preis berechnet?
</AccordionTrigger>
<AccordionContent className="text-sm text-muted-foreground">
<p>
Für diese Schätzung gehen wir aufgrund Ihrer Angaben von
ungefähr{" "}
<strong className="text-navy">
{sensors} Heizsensoren und {smokeDetectors}{" "}
Rauchwarnmeldern
</strong>{" "}
aus.
</p>
<p className="mt-2">
Bei Gebäuden mit durchschnittlich drei Zimmern verwenden
wir eine typische Geräteausstattung als
Berechnungsgrundlage.
</p>
<p className="mt-2">
Die endgültige Anzahl wird bei der detaillierten
Konfiguration der Wohnungen bestimmt und ersetzt diese
Schätzwerte.
</p>
</AccordionContent>
</AccordionItem>
</Accordion>
</StepCard>
<StepCard step={4} title="Konfiguration speichern">
<form
onSubmit={(e) => e.preventDefault()}
className="flex flex-col gap-3"
>
<div className="flex flex-col gap-1.5">
<Label htmlFor="config-email">E-Mail-Adresse</Label>
<Input
id="config-email"
type="email"
placeholder="name@unternehmen.de"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<Button type="submit" className="self-start">
Konfiguration speichern &amp; fortsetzen
</Button>
<p className="text-xs text-muted-foreground">
Sie erhalten einen persönlichen Link (gebos.de/konfiguration/)
zu Ihrer Konfiguration sofort weitermachen, später
zurückkehren oder den Link intern weitergeben.
</p>
</form>
</StepCard>
<div className="flex flex-wrap gap-3">
<Button type="button">Jetzt Preis berechnen</Button>
<Button variant="outline" asChild>
<Link to="/so-funktionierts">So funktioniert der Konfigurator</Link>
</Button>
</div>
</div>
{/* Right column: sticky summary */}
<div className="lg:sticky lg:top-24">
<Card className="gap-0 p-6 shadow-card-lg sm:p-7">
<h3 className="text-base font-extrabold tracking-tight text-navy">
Ihre vorläufige Konfiguration
</h3>
<div className="mt-4 flex flex-col gap-1">
<div className="text-2xl font-extrabold tracking-tight text-navy">
{totalBuildings}{" "}
{totalBuildings === 1 ? "Gebäude" : "Gebäude"}
</div>
<div className="text-2xl font-extrabold tracking-tight text-navy">
{totalUnits} {totalUnits === 1 ? "Wohnung" : "Wohnungen"}
</div>
</div>
{uvi || rwm ? (
<>
<Separator className="my-4" />
<p className="text-xs font-bold tracking-[0.14em] text-muted-foreground uppercase">
Voraussichtlich benötigt
</p>
<ul className="mt-2 flex flex-col gap-1.5 text-sm text-navy">
{uvi ? (
<li className="font-semibold">
ca. {sensors} Heizsensoren
</li>
) : null}
{rwm ? (
<li className="font-semibold">
ca. {smokeDetectors} Rauchwarnmelder
</li>
) : null}
</ul>
</>
) : null}
<div className="mt-5 rounded-xl bg-brand-gradient p-4 text-white shadow-band">
<div className="flex items-center justify-between gap-3">
<div>
<div className="text-2xl font-extrabold tracking-tight">
ca. {formatEuro(monthly)}{"\u00A0"}
</div>
<div className="text-sm text-white/80">
je Wohnung / Monat
</div>
</div>
<IconBadge variant="glass" size="lg">
<Euro />
</IconBadge>
</div>
{rwm ? (
<div className="mt-3 border-t border-white/20 pt-2 text-xs text-white/80">
+ {formatEuro(RWM_ONE_TIME)}{"\u00A0"} je Wohnung einmalig
(RWM-Hardware)
</div>
) : null}
</div>
<div className="mt-5 flex items-start gap-4">
<DonutIllustration className="w-16 shrink-0" />
<p className="text-xs leading-relaxed text-muted-foreground">
Die erste Berechnung ist eine unverbindliche Preisindikation.
Der genaue Preis ergibt sich aus der vollständigen
Objektkonfiguration.
</p>
</div>
</Card>
</div>
</div>
</Section>
{/* Was ist in der Schätzung enthalten? */}
<Section className="pt-0 sm:pt-0 lg:pt-0">
<SectionHeading
title="Was ist in der Schätzung enthalten?"
lead="Je nach gewähltem Paket beispielsweise:"
/>
<div className="mt-8 grid gap-6 lg:grid-cols-2">
<Card
className={
"gap-0 p-6 transition-opacity sm:p-8" +
(uvi ? "" : " opacity-50")
}
>
<div className="flex items-center gap-4">
<img src={icons.uvi} alt="" className="size-14" />
<h3 className="text-xl font-extrabold tracking-tight text-navy">
UVI
</h3>
</div>
<Checklist
className="mt-5"
items={[
"geschätzte benötigte Messtechnik",
"GebOS Plattform",
"Datenerfassung",
"Unterjährige Verbrauchsinformation",
"laufender digitaler Betrieb",
]}
/>
</Card>
<Card
className={
"gap-0 p-6 transition-opacity sm:p-8" +
(rwm ? "" : " opacity-50")
}
>
<div className="flex items-center gap-4">
<img src={icons.smokeAlarm} alt="" className="size-14" />
<h3 className="text-xl font-extrabold tracking-tight text-navy">
Rauchwarnmelder
</h3>
</div>
<Checklist
className="mt-5"
items={[
"geschätzte Anzahl Rauchwarnmelder",
"Verwaltung der Geräte",
"gebuchte digitale Dienstleistungen",
]}
/>
</Card>
</div>
</Section>
{/* Stufe 2 So geht es weiter */}
<Section className="pt-0 pb-16 sm:pt-0 sm:pb-20 lg:pt-0 lg:pb-24">
<SectionHeading
eyebrow="So geht es weiter"
title="Erst schätzen. Dann genau konfigurieren."
lead="Der vollständige Konfigurator beginnt nicht wieder bei null. Die bereits erfassten Informationen werden übernommen und die bisherigen Annahmen schrittweise durch echte Daten ersetzt."
/>
<div className="mt-8 flex flex-col gap-3">
<div className="flex flex-wrap items-center gap-2.5">
<Badge variant="muted" className="px-3.5 py-1.5 text-sm">
Geschätzt: 72 Heizsensoren
</Badge>
<ArrowRight className="size-4 text-primary" aria-hidden="true" />
<Badge className="px-3.5 py-1.5 text-sm">
Konfiguriert: 68 Heizsensoren
</Badge>
</div>
<div className="flex flex-wrap items-center gap-2.5">
<Badge variant="muted" className="px-3.5 py-1.5 text-sm">
Geschätzt: 96 Rauchwarnmelder
</Badge>
<ArrowRight className="size-4 text-primary" aria-hidden="true" />
<Badge className="px-3.5 py-1.5 text-sm">
Konfiguriert: 103 Rauchwarnmelder
</Badge>
</div>
</div>
<Card className="mt-10 max-w-2xl gap-0 p-6 sm:p-8">
<p className="text-xs font-bold tracking-[0.14em] text-primary uppercase">
Finales Ergebnis
</p>
<h3 className="mt-2 text-xl font-extrabold tracking-tight text-navy">
Ihre GebOS-Konfiguration
</h3>
<p className="mt-1 text-sm font-semibold text-navy">
3 Gebäude · 26 Wohnungen
</p>
<Separator className="my-4" />
<dl className="flex flex-col gap-3 text-sm">
<div>
<dt className="font-bold text-navy">UVI</dt>
<dd className="text-muted-foreground">
68 Heizsensoren / Messgeräte, weitere benötigte Messtechnik
</dd>
</div>
<div>
<dt className="font-bold text-navy">Rauchwarnmelder</dt>
<dd className="text-muted-foreground">103 Rauchwarnmelder</dd>
</div>
<div>
<dt className="font-bold text-navy">Infrastruktur</dt>
<dd className="text-muted-foreground">
technisch ausgelegte Gateway- und Kommunikationsinfrastruktur
</dd>
</div>
</dl>
<Separator className="my-4" />
<p className="text-sm leading-relaxed text-muted-foreground">
Ihr Preis ist dann keine grobe Hochrechnung auf Basis von
Durchschnittswerten mehr, sondern ein Preis auf Grundlage der
tatsächlichen Objektkonfiguration.
</p>
<div className="mt-5">
<Button type="button">Angebot anfordern</Button>
</div>
</Card>
<p className="mt-8 max-w-2xl text-sm leading-relaxed text-muted-foreground">
Ihre Daten werden im gesamten Prozess weiterverwendet:
Preisschätzung Konfiguration Bestellung Gerätevorbereitung
Installation laufender Betrieb.
</p>
</Section>
</Container>
)
}
+223
View File
@@ -0,0 +1,223 @@
import * as React from "react"
import { Link } from "react-router-dom"
import { Building2, House, LineChart, Wrench } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Checklist } from "@/components/gebos/checklist"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import { PageBreadcrumb } from "@/components/gebos/page-breadcrumb"
import {
AssetRender,
renders,
vectors,
} from "@/components/gebos/site-assets"
type AudienceKey =
| "hauseigentuemer"
| "hausverwaltung"
| "messdienstleister"
| "installateur"
const AUDIENCES: Record<
AudienceKey,
{
select: string
tab: string
tabIcon: React.ReactNode
headline: string
text: string
bullets: string[]
cta: string
href: string
}
> = {
hauseigentuemer: {
select: "Hauseigentümer",
tab: "Hauseigentümer",
tabIcon: <House />,
headline: "Messdienstleistungen für Ihr Mehrfamilienhaus",
text: "Installieren Sie vorkonfigurierte Technik selbst beziehungsweise mit Ihrem bestehenden Fachbetrieb oder überlassen Sie GebOS die komplette Umsetzung.",
bullets: [
"einfache Installation",
"transparente Kosten",
"zuverlässiger laufender Betrieb",
"eigene Fachbetriebe weiter nutzen",
"wahlweise Selbstinstallation oder Komplettlösung",
],
cta: "GebOS für Hauseigentümer",
href: "/fuer-wen/hauseigentuemer",
},
hausverwaltung: {
select: "Hausverwaltung",
tab: "Hausverwaltungen",
tabIcon: <Building2 />,
headline: "Viele Gebäude. Ein einfacher Rollout.",
text: "Erfassen Sie Ihre Objekte einmal, erhalten Sie vorkonfigurierte Hardware und nutzen Sie bestehende Mitarbeiter oder Installationspartner für die Montage.",
bullets: [
"standardisierter Rollout",
"vorkonfigurierte Geräte",
"wenig Softwarearbeit vor Ort",
"zentrale Portfolioverwaltung",
"geringer administrativer Aufwand",
"schnelle Erweiterung um weitere Gebäude",
],
cta: "GebOS für Hausverwaltungen",
href: "/fuer-wen/hausverwaltungen",
},
messdienstleister: {
select: "Messdienstleister",
tab: "Messdienstleister",
tabIcon: <LineChart />,
headline: "Ihre Messdienstleistung. Unsere Infrastruktur.",
text: "Nutzen Sie GebOS als technische Grundlage für Ihre eigenen Dienstleistungen mit Hardware, Plattform, Gerätemanagement und optionalem White Label.",
bullets: [
"technische Plattform",
"Gerätekompatibilität",
"Skalierbarkeit",
"APIs und Integrationen",
"White Label",
"individuelles Geschäftsmodell",
],
cta: "GebOS für Messdienstleister",
href: "/fuer-wen/messdienstleister",
},
installateur: {
select: "Installateur & Heizungsbauer",
tab: "Installateure",
tabIcon: <Wrench />,
headline: "Bestellen. Montieren. Fertig.",
text: "Setzen Sie GebOS bei Ihren bestehenden Kunden ein, ohne zunächst ein Partnerprogramm durchlaufen zu müssen.",
bullets: [
"kein verpflichtender Partnerstatus",
"keine Mindestabnahme",
"vorkonfigurierte Hardware",
"keine aufwendige Softwarekonfiguration",
"bestehende Kundenbeziehung bleibt erhalten",
],
cta: "GebOS für Installateure",
href: "/fuer-wen/installateure",
},
}
const AUDIENCE_KEYS = Object.keys(AUDIENCES) as AudienceKey[]
/**
* Page-local hero composition (04-for-whom kit):
* building portfolio render, teal growth bars behind right,
* consumption ring in front at the lower left.
*/
function ForWhomArt() {
return (
<div className="relative mx-auto aspect-[1.28] w-full max-w-[560px]">
<img
src={vectors.tealBars}
alt=""
className="absolute top-[16%] right-0 w-[38%]"
/>
<AssetRender
render={renders.building}
className="absolute inset-x-[6%] top-[6%]"
imgClassName="w-full"
loading="eager"
/>
<img
src={vectors.consumptionRing}
alt=""
className="absolute bottom-[2%] left-[2%] w-[30%] drop-shadow-lg"
/>
</div>
)
}
export default function ForWhomPage() {
const [audience, setAudience] = React.useState<AudienceKey>("hausverwaltung")
const active = AUDIENCES[audience]
return (
<div className="bg-hero-glow">
<Container>
<Section className="pt-6 sm:pt-7 lg:pt-8">
<PageBreadcrumb
className="mb-4"
items={[
{ label: "Startseite", to: "/" },
{ label: "Für wen?" },
]}
/>
<SectionHeading
eyebrow="Finden Sie die passende Lösung"
title="Für wen ist GebOS?"
lead="GebOS wird von unterschiedlichen Kundengruppen eingesetzt. Wählen Sie aus, was am besten zu Ihnen passt."
/>
<div className="mt-8 flex flex-wrap items-center gap-3">
<Label htmlFor="audience-select" className="text-muted-foreground">
Ich bin
</Label>
<Select
value={audience}
onValueChange={(value) => setAudience(value as AudienceKey)}
>
<SelectTrigger id="audience-select" className="w-64">
<SelectValue />
</SelectTrigger>
<SelectContent>
{AUDIENCE_KEYS.map((key) => (
<SelectItem key={key} value={key}>
{AUDIENCES[key].select}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="mt-6 grid items-center gap-10 lg:grid-cols-[1.05fr_0.95fr] lg:gap-6">
<Card className="max-w-xl gap-0 p-7">
<h2 className="text-xl font-extrabold tracking-tight text-balance text-navy sm:text-2xl">
{active.headline}
</h2>
<p className="mt-3 text-sm leading-relaxed text-pretty text-muted-foreground">
{active.text}
</p>
<p className="mt-5 text-sm font-bold text-navy">
Besonders wichtig für Sie:
</p>
<Checklist items={active.bullets} className="mt-3" />
<div className="mt-6">
<Button asChild>
<Link to={active.href}>{active.cta}</Link>
</Button>
</div>
</Card>
<ForWhomArt />
</div>
<Tabs
value={audience}
onValueChange={(value) => setAudience(value as AudienceKey)}
className="mt-12"
>
<TabsList>
{AUDIENCE_KEYS.map((key) => (
<TabsTrigger key={key} value={key}>
{AUDIENCES[key].tabIcon}
{AUDIENCES[key].tab}
</TabsTrigger>
))}
</TabsList>
</Tabs>
</Section>
</Container>
</div>
)
}
+590
View File
@@ -0,0 +1,590 @@
import { Link } from "react-router-dom"
import {
ArrowRight,
Building2,
Calculator,
HardHat,
House,
Network,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import { PageHero } from "@/components/gebos/page-hero"
import { HeroArt } from "@/components/gebos/hero-art"
import { FeatureTile, IconBadge } from "@/components/gebos/icon-tile"
import { ProcessSteps, NumberedList } from "@/components/gebos/process-steps"
import { Checklist } from "@/components/gebos/checklist"
import { CtaBanner } from "@/components/gebos/cta-banner"
import {
AssetRender,
icons,
renders,
vectors,
} from "@/components/gebos/site-assets"
const features = [
{
media: <img src={icons.uvi} alt="" className="size-14" loading="lazy" />,
title: "UVI",
sub: "Monatliche Verbrauchsinformation",
},
{
media: (
<img src={icons.billing} alt="" className="size-14" loading="lazy" />
),
title: "Heizkosten\u00adabrechnung",
sub: "Jährlich korrekt abrechnen",
},
{
media: (
<img src={icons.smokeAlarm} alt="" className="size-14" loading="lazy" />
),
title: "Rauchwarnmelder",
sub: "Sicher überwacht, zentral verwaltet",
},
{
media: <img src={icons.radio} alt="" className="size-14" loading="lazy" />,
title: "Fernauslesbare Messtechnik",
sub: "Automatisch erfasst, sicher übertragen",
},
{
media: (
<img src={icons.portal} alt="" className="size-14" loading="lazy" />
),
title: "Portale & Verwaltung",
sub: "Alle Daten und Geräte im Blick",
},
]
const operationsScope = [
{
media: <img src={icons.uvi} alt="" className="size-12" loading="lazy" />,
title: "Unterjährige Verbrauchsinformation",
text: "Verbrauchsdaten werden automatisch erfasst und die regelmäßigen Verbrauchsinformationen für die Bewohner bereitgestellt.",
},
{
media: (
<img src={icons.billing} alt="" className="size-12" loading="lazy" />
),
title: "Heizkostenabrechnung",
text: "Die erfassten Verbrauchsdaten bilden die Grundlage für die jährliche Heizkostenabrechnung.",
},
{
media: (
<img src={icons.smokeAlarm} alt="" className="size-12" loading="lazy" />
),
title: "Rauchwarnmelder",
text: "Rauchwarnmelder können gemeinsam mit der übrigen Gebäudetechnik geplant, installiert und zentral verwaltet werden.",
},
{
media: <img src={icons.radio} alt="" className="size-12" loading="lazy" />,
title: "Fernauslesbare Messtechnik",
text: "Wärme- und Wasserverbrauch werden über kompatible Geräte automatisch erfasst.",
},
{
media: (
<AssetRender
render={renders.gateway}
className="flex size-12 items-center justify-center"
imgClassName="w-12"
/>
),
title: "Gateway und Datenübertragung",
text: "Die Messdaten werden zentral gesammelt und an GebOS übertragen.",
},
{
media: (
<img src={icons.portal} alt="" className="size-12" loading="lazy" />
),
title: "Portale und Verwaltung",
text: "Gebäude, Wohnungen, Geräte und Verbrauchsdaten werden zentral verwaltet. Bewohner erhalten Zugriff auf die für sie vorgesehenen Informationen.",
},
]
function StepMedia({ src }: { src: string }) {
return (
<span className="flex size-16 items-center justify-center">
<img src={src} alt="" className="size-16" loading="lazy" />
</span>
)
}
const processSteps = [
{
media: <StepMedia src={vectors.uviBadge} />,
title: "Gebäude erfassen",
description: "Daten zum Gebäude und den Wohnungen erfassen.",
},
{
media: (
<AssetRender
render={renders.building}
className="flex size-16 items-center justify-center"
imgClassName="w-16"
/>
),
title: "Gerätebedarf bestimmen",
description: "GebOS ermittelt die benötigte Ausstattung automatisch.",
},
{
media: <StepMedia src={vectors.packageBox} />,
title: "Vorkonfiguriert liefern",
description: "Hardware wird für Ihr Objekt vorbereitet geliefert.",
},
{
media: (
<AssetRender
render={renders.heatMeter}
className="flex size-16 items-center justify-center"
imgClassName="w-16"
/>
),
title: "Geräte montieren",
description: "Montage vor Ort ohne aufwendige Softwarekonfiguration.",
},
{
media: <StepMedia src={vectors.wirelessOrb} />,
title: "Automatisch betreiben",
description: "GebOS übernimmt Daten, Services und laufenden Betrieb.",
},
]
const lessEffort = [
{
vector: vectors.packageBox,
title: "Vorkonfigurierte Hardware",
text: "Geräte werden passend zum jeweiligen Gebäude und zur vorgesehenen Nutzung vorbereitet.",
},
{
vector: vectors.checkOrb,
title: "Keine aufwendige Einrichtung auf der Baustelle",
text: "Der Installateur kann sich auf die fachgerechte Montage konzentrieren.",
},
{
vector: vectors.checkOrb,
title: "Bestehende Fachbetriebe nutzen",
text: "Kein spezieller GebOS-Partner erforderlich eigene Hausmeister, Heizungsbauer oder bestehende Installationspartner können weiterhin eingesetzt werden.",
},
{
vector: vectors.checkOrb,
title: "Weniger doppelte Dateneingabe",
text: "Informationen aus Bestellung und Konfiguration werden für den späteren Betrieb weiterverwendet.",
},
]
const systemTasks = [
{
media: <img src={icons.uvi} alt="" className="size-10" loading="lazy" />,
title: "UVI",
text: "Regelmäßige Verbrauchsinformation für Bewohner.",
},
{
media: (
<img src={icons.billing} alt="" className="size-10" loading="lazy" />
),
title: "Heizkostenabrechnung",
text: "Verbrauchsdaten strukturiert erfassen und für die jährliche Abrechnung nutzen.",
},
{
media: (
<img src={icons.smokeAlarm} alt="" className="size-10" loading="lazy" />
),
title: "Rauchwarnmelder",
text: "Rauchwarnmelder gemeinsam mit der übrigen Gebäudetechnik planen und verwalten.",
},
{
media: <img src={icons.radio} alt="" className="size-10" loading="lazy" />,
title: "Messtechnik und Infrastruktur",
text: "Fernauslesbare Zähler, Sensoren und Kommunikationsinfrastruktur für den laufenden Betrieb.",
},
]
const audiences = [
{
icon: <House />,
name: "Hauseigentümer",
headline: "Messdienstleistungen für Ihr Mehrfamilienhaus",
text: "Installieren Sie vorkonfigurierte Technik selbst beziehungsweise mit Ihrem bestehenden Fachbetrieb oder überlassen Sie GebOS die komplette Umsetzung.",
points: [
"einfache Installation",
"transparente Kosten",
"zuverlässiger laufender Betrieb",
],
cta: "GebOS für Hauseigentümer",
to: "/fuer-wen/hauseigentuemer",
},
{
icon: <Building2 />,
name: "Hausverwaltungen",
headline: "Viele Gebäude. Ein einfacher Rollout.",
text: "Erfassen Sie Ihre Objekte einmal, erhalten Sie vorkonfigurierte Hardware und nutzen Sie bestehende Mitarbeiter oder Installationspartner für die Montage.",
points: [
"standardisierter Rollout",
"vorkonfigurierte Geräte",
"zentrale Portfolioverwaltung",
],
cta: "GebOS für Hausverwaltungen",
to: "/fuer-wen/hausverwaltungen",
},
{
icon: <Network />,
name: "Messdienstleister",
headline: "Ihre Messdienstleistung. Unsere Infrastruktur.",
text: "Nutzen Sie GebOS als technische Grundlage für Ihre eigenen Dienstleistungen mit Hardware, Plattform, Gerätemanagement und optionalem White Label.",
points: ["technische Plattform", "Skalierbarkeit", "APIs und White Label"],
cta: "GebOS für Messdienstleister",
to: "/fuer-wen/messdienstleister",
},
{
icon: <HardHat />,
name: "Installateure & Heizungsbauer",
headline: "Bestellen. Montieren. Fertig.",
text: "Setzen Sie GebOS bei Ihren bestehenden Kunden ein, ohne zunächst ein Partnerprogramm durchlaufen zu müssen.",
points: [
"kein verpflichtender Partnerstatus",
"keine Mindestabnahme",
"vorkonfigurierte Hardware",
],
cta: "GebOS für Installateure",
to: "/fuer-wen/installateure",
},
]
const scaleFlows = [
{
vector: vectors.tealBars,
label: "1 Gebäude",
sub: "Ein Eigentümer kann ein Mehrfamilienhaus konfigurieren.",
steps: [
{ title: "GebOS konfigurieren" },
{ title: "Installieren" },
{ title: "Betreiben" },
],
},
{
vector: vectors.consumptionRing,
label: "50 Gebäude",
sub: "Eine Hausverwaltung kann denselben Prozess auf viele Gebäude übertragen.",
steps: [
{ title: "Nach demselben Prozess konfigurieren" },
{ title: "Rollout organisieren" },
{ title: "Zentral betreiben" },
],
},
]
const priceFlow = [
{ title: "Wenige Angaben" },
{ title: "Erste Preisindikation" },
{ title: "Konfiguration speichern" },
{ title: "Gebäude vollständig konfigurieren" },
{ title: "Genauer Preis" },
]
export default function HomePage() {
return (
<>
<PageHero
title={
<>
Messdienstleistungen
<br className="hidden sm:block" />{" "}
<span className="text-primary">einfach</span> gemacht.
</>
}
lead="Unterjährige Verbrauchsinformation (UVI), Heizkostenabrechnung und Rauchwarnmelder von der vorkonfigurierten Hardware bis zum laufenden Betrieb."
actions={
<>
<Button asChild size="lg">
<Link to="/konfigurator">Preis berechnen</Link>
</Button>
<Button asChild variant="outline" size="lg">
<Link to="/so-funktionierts">So funktioniert GebOS</Link>
</Button>
</>
}
visual={<HeroArt />}
/>
<Container>
<Section className="pt-0">
<div className="rounded-2xl border border-border bg-card p-6 shadow-card sm:p-8">
<div className="grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 lg:grid-cols-5">
{features.map((feature) => (
<FeatureTile key={feature.sub} {...feature} />
))}
</div>
</div>
</Section>
<Section>
<SectionHeading
align="center"
eyebrow="Das System"
title="Alles, was für den laufenden Messbetrieb benötigt wird"
lead="GebOS verbindet Messtechnik, Datenerfassung und Software zu einem durchgängigen System. Statt einzelne Komponenten selbst zusammenzustellen und miteinander zu verbinden, erhalten Sie eine Lösung, bei der Bestellung, Hardware, Konfiguration und laufender Betrieb zusammenspielen."
/>
<div className="mt-12 grid gap-5 sm:grid-cols-2 lg:grid-cols-3">
{operationsScope.map((item) => (
<Card key={item.title} className="p-6 gap-0">
<CardContent className="flex h-full flex-col items-start gap-3 p-0">
{item.media}
<h3 className="text-base font-bold text-navy">
{item.title}
</h3>
<p className="text-sm leading-relaxed text-muted-foreground">
{item.text}
</p>
</CardContent>
</Card>
))}
</div>
</Section>
<Section>
<SectionHeading
align="center"
eyebrow="Der Prozess"
title="So funktioniert GebOS"
lead="Von der Bestellung bis zum laufenden Betrieb GebOS verbindet Bestellung, Konfiguration und Installation miteinander."
/>
<ProcessSteps steps={processSteps} className="mt-12" />
<p className="mt-10 text-center text-base font-semibold italic text-navy">
Bestellung, Konfiguration und Installation greifen ineinander.
</p>
<div className="mt-6 flex justify-center">
<Button asChild size="lg">
<Link to="/konfigurator">Preis für mein Gebäude berechnen</Link>
</Button>
</div>
</Section>
<Section>
<SectionHeading
align="center"
eyebrow="Installation"
title="Weniger Aufwand vor Ort"
lead="Die Installation eines Messsystems sollte nicht daran scheitern, dass vor Ort komplizierte Software eingerichtet oder jedes Gerät manuell zugeordnet werden muss. GebOS bereitet die digitale Struktur bereits vor der Installation vor."
/>
<div className="mt-12 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
{lessEffort.map((item) => (
<Card key={item.title} className="p-6 gap-0">
<CardContent className="flex h-full flex-col items-start gap-3 p-0">
<img
src={item.vector}
alt=""
className="size-12"
loading="lazy"
/>
<h3 className="text-base font-bold text-navy">
{item.title}
</h3>
<p className="text-sm leading-relaxed text-muted-foreground">
{item.text}
</p>
</CardContent>
</Card>
))}
</div>
</Section>
<Section>
<SectionHeading
align="center"
eyebrow="Leistungen"
title="Ein System für mehrere Aufgaben"
lead="GebOS bündelt wiederkehrende Mess- und Gebäudedienstleistungen auf einer gemeinsamen technischen Grundlage."
/>
<Card className="mt-12 divide-y divide-border p-0 gap-0">
{systemTasks.map((task) => (
<div
key={task.title}
className="flex flex-col gap-3 p-5 sm:flex-row sm:items-center sm:gap-5 sm:px-7"
>
{task.media}
<div className="min-w-0 flex-1">
<h3 className="text-sm font-bold text-navy">{task.title}</h3>
<p className="mt-0.5 text-sm leading-relaxed text-muted-foreground">
{task.text}
</p>
</div>
<Button
asChild
variant="link"
className="h-auto shrink-0 self-start p-0 sm:self-center"
>
<Link to="/loesungen">
Mehr erfahren
<ArrowRight />
</Link>
</Button>
</div>
))}
</Card>
</Section>
<Section>
<SectionHeading
align="center"
eyebrow="Skalierung"
title="Vom einzelnen Mehrfamilienhaus bis zum Portfolio"
lead="GebOS ist so aufgebaut, dass derselbe grundlegende Prozess sowohl für ein einzelnes Gebäude als auch für größere Bestände funktioniert. Neue Objekte werden nach einem einheitlichen Schema ergänzt und ausgerollt."
/>
<Card className="mt-12 p-0 gap-0 overflow-hidden">
<div className="grid divide-y divide-border sm:grid-cols-2 sm:divide-x sm:divide-y-0">
{scaleFlows.map((flow) => (
<div key={flow.label} className="flex flex-col gap-5 p-6 sm:p-8">
<div className="flex items-center gap-4">
<img
src={flow.vector}
alt=""
className="size-14"
loading="lazy"
/>
<div>
<h3 className="text-xl font-extrabold tracking-tight text-navy">
{flow.label}
</h3>
<p className="mt-0.5 text-sm text-muted-foreground">
{flow.sub}
</p>
</div>
</div>
<NumberedList steps={flow.steps} className="mt-1" />
</div>
))}
</div>
</Card>
</Section>
<Section>
<SectionHeading
eyebrow="Preis"
title="Was kostet GebOS?"
lead="Mit dem GebOS-Konfigurator erhalten Sie bereits mit wenigen Angaben eine erste Preisindikation."
/>
<div className="mt-10 grid items-start gap-10 lg:grid-cols-2 lg:gap-14">
<div>
<p className="text-sm leading-relaxed text-muted-foreground">
Dafür werden zunächst nur grundlegende Informationen benötigt,
beispielsweise:
</p>
<Checklist
className="mt-4"
items={[
"gewünschte Leistungen",
"Anzahl der Gebäude",
"Anzahl der Wohnungen",
"Art der Wärmeverteilung",
"durchschnittliche Anzahl der Zimmer",
]}
/>
<p className="mt-4 text-sm leading-relaxed text-muted-foreground">
Auf Basis dieser Angaben schätzt GebOS die voraussichtlich
benötigte Geräteanzahl und berechnet einen ungefähren Preis.
</p>
<h3 className="mt-8 text-lg font-extrabold tracking-tight text-navy">
Erst schätzen. Dann genau konfigurieren.
</h3>
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
Der Schnellkonfigurator arbeitet bewusst mit Annahmen. Für
einen genauen Preis können Sie die Konfiguration anschließend
speichern und Ihre Gebäude detailliert erfassen. Die bereits
eingegebenen Daten werden übernommen.
</p>
<Button asChild size="lg" className="mt-6">
<Link to="/konfigurator">Preis berechnen</Link>
</Button>
</div>
<div>
<Card className="p-6 gap-0 sm:p-8">
<CardContent className="p-0">
<div className="flex items-center gap-4">
<img
src={vectors.euroOrb}
alt=""
className="size-14"
loading="lazy"
/>
<h3 className="text-base font-bold text-navy">
In fünf Schritten zum genauen Preis
</h3>
</div>
<NumberedList steps={priceFlow} className="mt-6" />
</CardContent>
</Card>
<p className="mt-5 text-xs leading-relaxed text-muted-foreground italic">
Die erste Berechnung ist eine unverbindliche Preisindikation.
Die tatsächliche Ausstattung und der genaue Preis ergeben sich
aus der vollständigen Objektkonfiguration.
</p>
</div>
</div>
</Section>
<Section>
<SectionHeading
align="center"
eyebrow="Zielgruppen"
title="Für wen ist GebOS?"
lead="GebOS wird von unterschiedlichen Kundengruppen eingesetzt. Wählen Sie aus, was am besten zu Ihnen passt."
/>
<div className="mt-12 grid gap-5 sm:grid-cols-2">
{audiences.map((audience) => (
<Card key={audience.to} className="p-6 gap-0 sm:p-7">
<CardContent className="flex h-full flex-col items-start gap-3 p-0">
<div className="flex items-center gap-3">
<IconBadge variant="soft" shape="squircle" size="lg">
{audience.icon}
</IconBadge>
<span className="text-xs font-bold tracking-wide text-primary uppercase">
{audience.name}
</span>
</div>
<h3 className="text-lg font-extrabold tracking-tight text-navy">
{audience.headline}
</h3>
<p className="text-sm leading-relaxed text-muted-foreground">
{audience.text}
</p>
<p className="mt-1 text-xs font-bold tracking-wide text-navy uppercase">
Besonders wichtig für Sie
</p>
<Checklist items={audience.points} className="mb-2" />
<Button asChild variant="outline" className="mt-auto">
<Link to={audience.to}>{audience.cta}</Link>
</Button>
</CardContent>
</Card>
))}
</div>
</Section>
<Section>
<CtaBanner
icon={<Calculator />}
title="Messdienstleistungen müssen nicht kompliziert sein."
description="Gebäude erfassen, passende Technik erhalten, montieren und anschließend zentral betreiben."
actions={
<>
<Button asChild variant="inverse" size="lg">
<Link to="/konfigurator">Preis berechnen</Link>
</Button>
<Button
asChild
variant="outline"
size="lg"
className="border-white/40 bg-transparent text-white hover:bg-white/10 hover:text-white"
>
<Link to="/so-funktionierts">So funktioniert GebOS</Link>
</Button>
</>
}
/>
</Section>
</Container>
</>
)
}
@@ -0,0 +1,181 @@
import type { ReactNode } from "react"
import { Link } from "react-router-dom"
import { Calculator, ChevronRight } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { CtaBanner } from "@/components/gebos/cta-banner"
import { ProcessSteps } from "@/components/gebos/process-steps"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import { PageBreadcrumb } from "@/components/gebos/page-breadcrumb"
import {
AssetRender,
renders,
vectors,
} from "@/components/gebos/site-assets"
function StepMedia({ children }: { children: ReactNode }) {
return (
<div className="flex h-16 items-end justify-center gap-1">{children}</div>
)
}
const steps = [
{
media: (
<StepMedia>
<img src={vectors.uviBadge} alt="" className="w-16" />
</StepMedia>
),
title: "Gebäude erfassen",
description:
"Bei der Konfiguration werden Gebäude, Wohnungen und die benötigten Leistungen erfasst.",
},
{
media: (
<StepMedia>
<AssetRender render={renders.building} imgClassName="w-16" />
</StepMedia>
),
title: "Gerätebedarf bestimmen",
description:
"Auf Basis der Gebäudedaten wird ermittelt, welche Messtechnik, Rauchwarnmelder und weiteren Komponenten benötigt werden.",
},
{
media: (
<StepMedia>
<img src={vectors.packageBox} alt="" className="w-16" />
</StepMedia>
),
title: "Vorkonfiguriert liefern",
description:
"Gebäude, Wohnungen und Geräte werden bereits vorbereitet. Die Hardware wird objektbezogen und installationsfertig geliefert.",
},
{
media: (
<StepMedia>
<AssetRender render={renders.heatMeter} imgClassName="w-12" />
<AssetRender render={renders.gateway} imgClassName="w-9" />
</StepMedia>
),
title: "Geräte montieren",
description: (
<>
Die Geräte müssen vor Ort im Wesentlichen nur noch fachgerecht montiert
werden.{" "}
<strong className="font-bold text-navy">
Keine aufwendige Softwarekonfiguration bei der Installation.
</strong>
</>
),
},
{
media: (
<StepMedia>
<img src={vectors.wirelessOrb} alt="" className="w-16" />
</StepMedia>
),
title: "Automatisch betreiben",
description:
"Nach der Installation übernimmt GebOS die laufende Datenerfassung und die gebuchten digitalen Dienstleistungen.",
},
]
const journey = [
"Besucher versteht GebOS",
"identifiziert seinen Anwendungsfall",
"berechnet einen ungefähren Preis",
"speichert seine Konfiguration",
"ergänzt die tatsächlichen Gebäudedaten",
"erhält den genauen Preis",
"erteilt den Auftrag",
"GebOS bereitet Gebäude und Geräte vor",
"vorkonfigurierte Hardware wird geliefert",
"Montage vor Ort",
"automatischer laufender Betrieb",
]
export default function HowItWorksPage() {
return (
<>
<div className="bg-hero-glow">
<Container>
<Section className="pt-6 pb-10 sm:pt-7 sm:pb-12 lg:pt-8 lg:pb-14">
<PageBreadcrumb
className="mb-4 justify-center"
items={[
{ label: "Startseite", to: "/" },
{ label: "So funktionierts" },
]}
/>
<SectionHeading
align="center"
title="So funktioniert GebOS"
lead="Von der Bestellung bis zum laufenden Betrieb. GebOS verbindet Bestellung, Konfiguration und Installation miteinander."
/>
<Card className="mt-10 gap-0 p-8 sm:mt-12 sm:p-10">
<ProcessSteps steps={steps} />
</Card>
<p className="mx-auto mt-10 max-w-xl text-center text-sm font-medium text-muted-foreground italic">
Bestellung, Konfiguration und Installation greifen ineinander.
</p>
</Section>
</Container>
</div>
<Container>
<Section>
<SectionHeading
align="center"
eyebrow="Gesamtprozess"
title="Der Gesamtprozess aus Kundensicht"
lead="Website, Konfigurator und operativer Rollout sind keine getrennten Systeme sie bauen aufeinander auf."
/>
<div className="mx-auto mt-10 flex max-w-4xl flex-wrap items-center justify-center gap-x-1.5 gap-y-3">
{journey.map((station, i) => (
<span key={station} className="flex items-center gap-1.5">
<Badge variant="outline" className="bg-card">
{station}
</Badge>
{i < journey.length - 1 ? (
<ChevronRight
aria-hidden="true"
className="size-4 shrink-0 text-brand-400"
/>
) : null}
</span>
))}
</div>
<div className="mx-auto mt-10 max-w-2xl text-center">
<p className="text-base font-bold text-navy sm:text-lg">
Der Konfigurator ist der Beginn des
GebOS-Bestell- und Rolloutprozesses.
</p>
<p className="mt-3 text-sm leading-relaxed text-muted-foreground">
Die einmal eingegebenen Gebäudedaten werden nicht erneut erfasst,
sondern durch den gesamten Prozess weiterverwendet.
</p>
<p className="mt-6 font-mono text-xs tracking-wide text-muted-foreground uppercase">
Preisschätzung Konfiguration Bestellung Gerätevorbereitung
Installation Betrieb
</p>
</div>
</Section>
</Container>
<Container>
<Section className="pt-0">
<CtaBanner
icon={<Calculator />}
title="Was kostet GebOS für Ihr Gebäude?"
description="Mit wenigen Angaben erhalten Sie eine erste Preisindikation."
actions={
<Button variant="inverse" size="lg" asChild>
<Link to="/konfigurator">Preis berechnen</Link>
</Button>
}
/>
</Section>
</Container>
</>
)
}
+154
View File
@@ -0,0 +1,154 @@
import { ClipboardCheck, Euro, House, ShieldCheck, Tag, Users2, Wifi } from "lucide-react"
import { Separator } from "@/components/ui/separator"
import { Container, Section } from "@/components/gebos/section"
import { PageHero } from "@/components/gebos/page-hero"
import { PageBreadcrumb } from "@/components/gebos/page-breadcrumb"
import { HeroVisual } from "@/components/gebos/hero-visual"
import { IconBadge } from "@/components/gebos/icon-tile"
import { PriceCard } from "@/components/gebos/price-card"
import {
SensorCubesIllustration,
SmokeDetectorIllustration,
} from "@/components/gebos/illustrations"
const trustItems = [
{ icon: ClipboardCheck, label: ["Klare", "Vertragsstruktur"] },
{ icon: ShieldCheck, label: ["Keine versteckten", "Gebühren"] },
{ icon: Users2, label: ["Rabatte für", "große Objekte"] },
] as const
export default function KlarpreisPage() {
return (
<>
<PageHero
breadcrumb={
<PageBreadcrumb
items={[
{ label: "Startseite", to: "/" },
{ label: "GebOS Klarpreis" },
]}
/>
}
title={
<>
GebOS <span className="text-primary">Klarpreis</span>
</>
}
lead="Drei sauber getrennte Leistungen. Einfache Preise. Keine Überraschungen."
extra={
<div className="inline-flex items-center gap-3 rounded-full border border-border bg-card px-4 py-2 text-sm text-muted-foreground shadow-pill">
<IconBadge variant="soft" size="sm">
<House />
</IconBadge>
<span>Ideal für Wohngebäude, Hausverwaltungen und Bestandshalter.</span>
</div>
}
visual={
<HeroVisual
media={<SmokeDetectorIllustration className="mx-auto w-48" />}
chipTitle="Inspektion OK"
chipSub="Letzte Prüfung 06.05.2024"
/>
}
/>
<Container>
<Section className="pt-0">
<div className="grid gap-6 lg:grid-cols-3">
<PriceCard
title="RWM-Hardware"
cadence="Einmalig"
price="42,00 €"
unit="je Wohnung einmalig"
allocation={{ label: "Nicht umlagefähig", tone: "negative" }}
description="Rauchwarnmelder inklusive Montage und Eigentumsübertragung."
media={<SmokeDetectorIllustration />}
/>
<PriceCard
title="Ferninspektion"
cadence="Monatlich"
price="2,20 €"
unit="je Wohnung / Monat"
allocation={{ label: "Grundsätzlich umlagefähig", tone: "positive" }}
description="Regelmäßige Prüfung, Nachweise, Meldungen und Support."
media={
<div className="relative">
<SmokeDetectorIllustration />
<Wifi className="absolute -right-1 top-1 size-5 text-primary" />
</div>
}
/>
<PriceCard
title={
<span className="text-[1.15rem] whitespace-nowrap">
UVI-Komplettbetrieb
</span>
}
cadence="Monatlich"
price="10,50 €"
unit="je Wohnung / Monat"
allocation={{ label: "Grundsätzlich umlagefähig", tone: "positive" }}
description="Sensoren, Daten, Portal und Verbrauchsinformation inklusive."
media={<SensorCubesIllustration />}
/>
</div>
<div className="mt-6 grid gap-6 lg:grid-cols-[1.4fr_1fr]">
{/* total band */}
<div className="flex flex-wrap items-center gap-6 rounded-2xl bg-brand-gradient p-6 shadow-band sm:flex-nowrap sm:p-7">
<IconBadge variant="glass" size="xl">
<Euro />
</IconBadge>
<div>
<div className="font-semibold text-white/80">
Gesamtkosten laufend
</div>
<div className="mt-1 flex flex-wrap items-baseline gap-x-3">
<span className="text-4xl font-extrabold tracking-tight text-white">
12,70
</span>
<span className="text-sm text-white/75">
je Wohnung / Monat
</span>
</div>
</div>
<Separator
orientation="vertical"
className="hidden self-stretch bg-white/25 sm:block"
/>
<div className="flex items-center gap-3">
<IconBadge variant="glass" size="sm">
<Tag />
</IconBadge>
<p className="max-w-56 text-sm leading-snug text-white/80">
Alle laufenden Leistungen klar gebündelt ohne versteckte
Zusatzkosten.
</p>
</div>
</div>
{/* trust card */}
<div className="grid grid-cols-3 divide-x divide-border rounded-2xl bg-card p-6 shadow-card">
{trustItems.map(({ icon: Icon, label }) => (
<div
key={label[1]}
className="flex flex-col items-center justify-center gap-2.5 px-2 text-center"
>
<IconBadge variant="soft" size="lg" shape="circle">
<Icon />
</IconBadge>
<div className="text-sm leading-snug font-bold text-navy">
{label[0]}
<br />
{label[1]}
</div>
</div>
))}
</div>
</div>
</Section>
</Container>
</>
)
}
@@ -0,0 +1,39 @@
import type * as React from "react"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import {
PageBreadcrumb,
type Crumb,
} from "@/components/gebos/page-breadcrumb"
/**
* Minimal content page (Unternehmen / Kontakt) and stand-in while
* full pages are being built.
*/
function SimplePage({
title,
lead,
breadcrumb,
children,
}: {
title: React.ReactNode
lead?: React.ReactNode
breadcrumb?: Crumb[]
children?: React.ReactNode
}) {
return (
<div className="bg-hero-glow">
<Container>
<Section className={breadcrumb ? "pt-6 sm:pt-7 lg:pt-8" : undefined}>
{breadcrumb ? (
<PageBreadcrumb className="mb-4" items={breadcrumb} />
) : null}
<SectionHeading title={title} lead={lead} />
{children}
</Section>
</Container>
</div>
)
}
export { SimplePage }
+132
View File
@@ -0,0 +1,132 @@
import { Link } from "react-router-dom"
import { ArrowRight, Euro } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Card } from "@/components/ui/card"
import { CtaBanner } from "@/components/gebos/cta-banner"
import { Container, Section, SectionHeading } from "@/components/gebos/section"
import { PageBreadcrumb } from "@/components/gebos/page-breadcrumb"
import {
AssetRender,
renders,
vectors,
} from "@/components/gebos/site-assets"
const solutions = [
{
title: "UVI",
sub: "Monatliche Verbrauchsinformation",
description:
"Verbrauchsdaten werden automatisch erfasst und die regelmäßigen Verbrauchsinformationen für die Bewohner bereitgestellt.",
illustration: (
<div className="relative">
<AssetRender render={renders.gateway} imgClassName="w-28" />
<img
src={vectors.glassCubes}
alt=""
className="absolute -top-3 -right-8 w-14"
/>
</div>
),
},
{
title: "Heizkostenabrechnung",
sub: "Verbrauchsdaten korrekt abrechnen",
description:
"Verbrauchsdaten strukturiert erfassen und für die jährliche Abrechnung nutzen.",
illustration: <img src={vectors.consumptionRing} alt="" className="w-28" />,
},
{
title: "Rauchwarnmelder",
sub: "Sicher überwacht, zentral verwaltet",
description:
"Rauchwarnmelder gemeinsam mit der übrigen Gebäudetechnik planen und verwalten.",
illustration: <AssetRender render={renders.smokeAlarm} imgClassName="w-28" />,
},
]
export default function SolutionsPage() {
return (
<>
<div className="bg-hero-glow">
<Container>
<Section className="pt-6 pb-10 sm:pt-7 sm:pb-12 lg:pt-8 lg:pb-14">
<PageBreadcrumb
className="mb-4"
items={[
{ label: "Startseite", to: "/" },
{ label: "Lösungen" },
]}
/>
<SectionHeading
title="Unsere Lösungen"
lead="Drei sauber getrennte Leistungen. Perfekt zusammenspielend."
/>
<div className="mt-10 grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{solutions.map((s) => (
<Card key={s.title} className="gap-0 p-6 sm:p-7">
<h3 className="text-xl font-extrabold tracking-tight text-navy">
{s.title}
</h3>
<p className="mt-1 text-sm text-muted-foreground">{s.sub}</p>
<div className="my-6 flex h-32 flex-1 items-center justify-center">
{s.illustration}
</div>
<p className="text-sm leading-relaxed text-muted-foreground">
{s.description}
</p>
<Button
variant="link"
className="mt-3 self-start px-0"
asChild
>
<Link to="/loesungen">
Mehr erfahren
<ArrowRight />
</Link>
</Button>
</Card>
))}
</div>
<Card className="mt-6 flex-col items-start gap-4 p-5 sm:flex-row sm:items-center sm:p-6">
<AssetRender
render={renders.gateway}
className="shrink-0"
imgClassName="w-14"
/>
<div className="min-w-0 flex-1">
<h3 className="text-sm font-bold text-navy">
Messtechnik und Infrastruktur
</h3>
<p className="mt-0.5 text-sm leading-relaxed text-muted-foreground">
Fernauslesbare Zähler, Sensoren und
Kommunikationsinfrastruktur für den laufenden Betrieb.
</p>
</div>
<Button variant="link" className="shrink-0 px-0 sm:px-4" asChild>
<Link to="/loesungen">
Mehr erfahren
<ArrowRight />
</Link>
</Button>
</Card>
</Section>
</Container>
</div>
<Container>
<Section className="pt-0">
<CtaBanner
icon={<Euro />}
title="Jetzt Preis für Ihr Gebäude berechnen"
description="In wenigen Minuten zur ersten Preisindikation."
actions={
<Button variant="inverse" size="lg" asChild>
<Link to="/konfigurator">Preis berechnen</Link>
</Button>
}
/>
</Section>
</Container>
</>
)
}