diff --git a/app/agb/page.tsx b/app/agb/page.tsx
new file mode 100644
index 0000000..33acfd9
--- /dev/null
+++ b/app/agb/page.tsx
@@ -0,0 +1,37 @@
+import type { Metadata } from "next";
+import { LegalPage, Todo } from "../components/legal";
+
+export const metadata: Metadata = {
+ title: "Allgemeine Geschäftsbedingungen",
+ description: "Allgemeine Geschäftsbedingungen für die Nutzung von Kumasy.",
+ alternates: { canonical: "/agb" },
+ robots: { index: false, follow: true },
+};
+
+export default function AgbPage() {
+ return (
+
+
+
+
+ Die AGB sind noch nicht verfasst. Sie sollten von einer
+ Rechtsanwältin oder einem Rechtsanwalt erstellt werden — ein
+ Vorlagentext führt bei einem SaaS-Vertrag mit Zahlungsabwicklung
+ und Auftragsverarbeitung erfahrungsgemäß zu unwirksamen Klauseln.
+
+
+
+ Folgende Punkte sind typischerweise zu regeln: Vertragsgegenstand
+ und Leistungsumfang, Zustandekommen des Vertrags, Preise und
+ Zahlungsbedingungen, Laufzeit und Kündigung, Verfügbarkeit und
+ Wartungsfenster, Pflichten der Nutzerin, Haftung und
+ Haftungsbeschränkung, Datenschutz und Auftragsverarbeitung,
+ Änderungen der AGB sowie Schlussbestimmungen und Gerichtsstand.
+
+
+
+ );
+}
diff --git a/app/apple-icon.png b/app/apple-icon.png
new file mode 100644
index 0000000..dbea313
Binary files /dev/null and b/app/apple-icon.png differ
diff --git a/app/components/hero.tsx b/app/components/hero.tsx
index e9bfce5..57b501b 100644
--- a/app/components/hero.tsx
+++ b/app/components/hero.tsx
@@ -137,7 +137,11 @@ export function Hero() {
{/* Per-character entrance. The
carries the readable text for
assistive tech; the animated spans are aria-hidden. */}
-
+ {/* The floor is 2.1rem, not 2.75rem: the longest line ("und
+ bezahlen sich") has to fit 321px of usable width on a 353px
+ Samsung viewport. 10.2vw carries it from there up to the
+ 5.25rem ceiling. */}
+
Deine Kurse füllen und bezahlen sich von selbst.
@@ -171,7 +175,7 @@ export function Hero() {
-
+
diff --git a/app/components/legal.tsx b/app/components/legal.tsx
new file mode 100644
index 0000000..618f2de
--- /dev/null
+++ b/app/components/legal.tsx
@@ -0,0 +1,60 @@
+import type { ReactNode } from "react";
+import { Footer } from "./sections";
+import { Nav } from "./ui";
+
+/**
+ * Shared shell for the legal pages. The root layout renders only
+ * {children}, so each page has to bring its own chrome — same reasoning
+ * as not-found.tsx.
+ */
+export function LegalPage({
+ title,
+ intro,
+ children,
+}: {
+ title: string;
+ intro?: string;
+ children: ReactNode;
+}) {
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
+
+/**
+ * Marks copy that still has to be supplied. Visible on purpose — a
+ * placeholder that blends in is a placeholder that ships.
+ */
+export function Todo({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/app/components/mockup.tsx b/app/components/mockup.tsx
index 441251b..9395641 100644
--- a/app/components/mockup.tsx
+++ b/app/components/mockup.tsx
@@ -50,7 +50,15 @@ export function BrowserFrame({
}
/* ------------------------------------------------------------------ *
- * Floating glass stat chip
+ * Floating stat chip
+ *
+ * Deliberately opaque, unlike the rest of the glass family. These chips
+ * sit directly on top of the dashboard screenshot, so a translucent
+ * surface lets the sidebar labels and the chart line read straight
+ * through the chip's own text — backdrop-blur cannot fix that, because
+ * what is behind is small sharp type rather than a soft wash. Same call
+ * the app makes for dialogs: darken behind, never blur, so the content
+ * in front stays legible.
* ------------------------------------------------------------------ */
function GlassStat({
@@ -65,7 +73,7 @@ function GlassStat({
tone: "mint" | "amber";
}) {
return (
-
+
- {Array.from(text).map((char, index) => (
-
- {char === " " ? " " : char}
-
+ {/* Grouped per word, not per character: a bare list of inline-block
+ letters lets the browser break inside a word ("fülle/n"), which it
+ does on narrow phones. Each word is whitespace-nowrap, so it travels
+ as one unit while the stagger still runs across the whole line. */}
+ {splitWords(text).map((word, wordIndex) => (
+
+ {Array.from(word).map((char, index) => (
+
+ {char === " " ? " " : char}
+
+ ))}
+
))}
);
diff --git a/app/components/sections.tsx b/app/components/sections.tsx
index 585baeb..9586054 100644
--- a/app/components/sections.tsx
+++ b/app/components/sections.tsx
@@ -1,6 +1,7 @@
"use client";
import Image from "next/image";
+import Link from "next/link";
import type { ComponentType, ReactNode } from "react";
import {
ArrowRight,
@@ -30,6 +31,8 @@ import {
import { BrowserFrame, ParallaxLayer } from "./mockup";
import { BrandButton, Eyebrow, Faq, GhostLink, WaitlistForm } from "./ui";
import { Logo } from "./logo";
+import { FAQ_ENTRIES } from "../lib/faq";
+import { CONTACT_EMAIL } from "../lib/site";
/* ------------------------------------------------------------------ *
* Shared section scaffolding
@@ -497,7 +500,15 @@ export function Pricing() {
Platz auf der Warteliste sichern
- [Mindestlaufzeit und Kündigungsfrist hier ergänzen]
+ Monatlich abrechenbar. Die Konditionen zu Laufzeit und
+ Kündigung stehen in den{" "}
+
+ AGB
+
+ .
@@ -620,15 +631,15 @@ export function Pilot() {
-
-
- „[Hier das Zitat von 120gradStudio einsetzen — ein bis zwei
- Sätze, am besten mit konkreter Zahl.]“
-
-
-
+ {/* Das Testimonial folgt, sobald 120gradStudio ein freigegebenes
+ Zitat geliefert hat. Bis dahin steht hier die belegbare
+ Aussage statt eines erfundenen Zitats — und im Markup kein
+ Review-Schema, das Google als Manipulation werten würde. */}
+
+ Seit dem Start laufen dort Kursplanung, Buchungen und Zahlungen
+ vollständig über Kumasy — im laufenden Betrieb, nicht in einer
+ Testumgebung.
+
@@ -685,33 +696,26 @@ export function FaqSection() {
+ {/* Driven by FAQ_ENTRIES so the rendered text and the FAQPage
+ structured data cannot drift apart — Google requires them to
+ match exactly. */}
-
- Nein. Gastbuchung ohne Registrierung ist Standard — genau da
- springen sonst die meisten ab. Wer will, legt sich trotzdem ein
- Konto an und sieht seine Buchungshistorie.
-
-
- Unser Migrationstool pflegt Kurse, Kundinnen und Termine aus
- deinem alten System automatisch in Kumasy ein. Du musst nichts
- abtippen und kannst vorher prüfen, was übernommen wird.
-
-
- Deine Kundin, beim Checkout. Dein monatlicher Betrag bleibt bei
- 150 € — unabhängig davon, ob du 20 oder 2.000 Buchungen im Monat
- hast. Dazu kommen nur die Transaktionsgebühren von Stripe bzw.
- PayPal.
-
-
- Ja. Gehostet wird über Vercel in der Region Frankfurt, Zahlungen
- laufen über Stripe, Rechnungen werden GoBD-konform erstellt.
- [Auftragsverarbeitungsvertrag und Datenschutzerklärung hier
- verlinken.]
-
-
- Wir sind in der Pilotphase und geben nach und nach Zugänge frei.
- Du bekommst eine E-Mail, sobald du dran bist.
-
+ {FAQ_ENTRIES.map(({ question, answer, link }) => (
+
+ {answer}
+ {link ? (
+ <>
+ {" "}
+
+ {link.label}
+
+ >
+ ) : null}
+
+ ))}
@@ -765,7 +769,7 @@ export function ClosingCta() {