feat: photo landing page with favicon set

This commit is contained in:
Jonas Miederer 2026-09-13 12:52:46 +02:00
parent b533549e45
commit 672faf3af8
19 changed files with 297 additions and 45 deletions

View file

@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
import { MantineProvider } from "@mantine/core";
import "@mantine/core/styles.css";
import "./photos.css";
import App from "./App";
import { colorSchemeManager, theme } from "./theme";

View file

@ -3,9 +3,10 @@ import {
Accordion,
Anchor,
Badge,
Box,
Button,
Card,
Grid,
Flex,
Group,
Image,
List,
@ -40,17 +41,23 @@ const benefits = [
{
icon: <IconFileText size={28} />,
title: "In eigenen Worten",
text: "Mitarbeitende beschreiben Anliegen wie gewohnt — das System führt in verständlichen Schritten durch den Vorgang.",
text: "Mitarbeitende beschreiben Anliegen wie gewohnt — das System führt in verständlichen Schritten durch den Vorgang. Schluss mit Stempeln und Durchschlägen.",
photo: "/photos/paperwork-desk.webp",
alt: "Schreibtisch mit Stempeln und Formularen — so sieht es heute oft aus",
},
{
icon: <IconSparkles size={28} />,
title: "Regeln automatisch geprüft",
text: "Schwellen, Zuständigkeiten und Abläufe prüft die Software selbst. Routine läuft durch, ohne dass jemand klicken muss.",
photo: "/photos/team-laptop.webp",
alt: "Team arbeitet gemeinsam am Laptop",
},
{
icon: <IconUserCheck size={28} />,
title: "Mensch nur wo nötig",
text: "Echte Entscheidungen landen mit allen Fakten bei der zuständigen Person — Freigabe oder Ablehnung inklusive Notiz.",
text: "Echte Entscheidungen landen mit allen Fakten bei der zuständigen Person — Freigabe oder Ablehnung inklusive Notiz. Für alles andere bleibt Zeit.",
photo: "/photos/church-pews.webp",
alt: "Ruhiger Kirchenraum mit leeren Bänken",
},
];
@ -96,30 +103,48 @@ export default function StartPage() {
const { me } = useAuth();
return (
<Stack gap="xl">
{/* Hero */}
{/* Hero: text left, parish photo right */}
<Card withBorder padding="xl" radius="lg">
<Badge mb="sm">Für Pfarrbüros und Kirchengemeinden</Badge>
<Title order={1} mb="sm">
Weniger Verwaltung. Mehr Zeit für Menschen.
</Title>
<Text size="lg" c="dimmed" mb="lg" maw={640}>
Kirche Einfach führt Mitarbeitende in eigenen Worten durch Vorgänge,
prüft Regeln automatisch und fragt Menschen nur, wo Urteil nötig
ist — lückenlos protokolliert.
</Text>
<Group>
<Button component={Link} to="/access" size="lg">
{CTA_LABEL}
</Button>
<Button component={Link} to="/login" size="lg" variant="light">
Zum Login
</Button>
</Group>
{me && (
<Text mt="md">
Angemeldet als <strong>{me.email}</strong> ({me.tenant_id}).
</Text>
)}
<Flex
direction={{ base: "column", md: "row" }}
gap="xl"
align="center"
>
<Box style={{ flex: 7 }}>
<Badge mb="sm">Für Pfarrbüros und Kirchengemeinden</Badge>
<Title order={1} mb="sm">
Weniger Verwaltung. Mehr Zeit für Menschen.
</Title>
<Text size="lg" c="dimmed" mb="lg">
Kirche Einfach führt Mitarbeitende in eigenen Worten durch
Vorgänge, prüft Regeln automatisch und fragt Menschen nur, wo
Urteil nötig ist — lückenlos protokolliert.
</Text>
<Group>
<Button component={Link} to="/access" size="lg">
{CTA_LABEL}
</Button>
<Button component={Link} to="/login" size="lg" variant="light">
Zum Login
</Button>
</Group>
{me && (
<Text mt="md">
Angemeldet als <strong>{me.email}</strong> ({me.tenant_id}).
</Text>
)}
</Box>
<Box style={{ flex: 5 }} w={{ base: "100%", md: "auto" }}>
<Image
src="/photos/hero-church.webp"
alt="Steinerner Kirchturm unter blauem Himmel"
radius="lg"
className="photo"
h={380}
fit="cover"
/>
</Box>
</Flex>
</Card>
{/* Facts */}
@ -136,24 +161,53 @@ export default function StartPage() {
))}
</Group>
{/* Benefits */}
<Grid>
{benefits.map((b) => (
<Grid.Col key={b.title} span={{ base: 12, sm: 4 }}>
<Card withBorder padding="lg" h="100%">
<ThemeIcon size={48} radius="md" variant="light" mb="sm">
{b.icon}
</ThemeIcon>
<Title order={4} mb="xs">
{b.title}
</Title>
<Text size="sm" c="dimmed">
{b.text}
</Text>
</Card>
</Grid.Col>
))}
</Grid>
{/* Benefits: alternating photo/text rows */}
{benefits.map((b, i) => {
const photo = (
<Box style={{ flex: 5 }} w={{ base: "100%", md: "auto" }}>
<Image
src={b.photo}
alt={b.alt}
radius="lg"
className="photo"
loading="lazy"
h={300}
fit="cover"
/>
</Box>
);
const text = (
<Box style={{ flex: 7 }}>
<ThemeIcon size={48} radius="md" variant="light" mb="sm">
{b.icon}
</ThemeIcon>
<Title order={3} mb="xs">
{b.title}
</Title>
<Text c="dimmed">{b.text}</Text>
</Box>
);
return (
<Flex
key={b.title}
direction={{ base: "column", md: "row" }}
gap="xl"
align="center"
>
{i % 2 === 0 ? (
<>
{photo}
{text}
</>
) : (
<>
{text}
{photo}
</>
)}
</Flex>
);
})}
{/* Product visual */}
<Card withBorder padding="lg" radius="lg">
@ -168,6 +222,8 @@ export default function StartPage() {
src="/screenshots/workflows-light.png"
alt="Workflow-Formular in Kirche Einfach"
radius="md"
className="photo"
loading="lazy"
bd="1px solid var(--mantine-color-default-border)"
/>
</Card>

5
frontend/src/photos.css Normal file
View file

@ -0,0 +1,5 @@
/* Photo treatment: gentle dimming in dark mode so bright photos do not
* glare against the slate surfaces. Applied via className="photo". */
[data-mantine-color-scheme="dark"] .photo {
filter: brightness(0.9) saturate(0.95);
}