mirror of
https://github.com/Since-3/Kumasy-Marketing.git
synced 2026-10-12 19:51:17 +00:00
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import type { Metadata, Viewport } from "next";
|
|
import { Manrope, Plus_Jakarta_Sans } from "next/font/google";
|
|
import "./globals.css";
|
|
import { SITE_NAME, SITE_URL } from "./lib/site";
|
|
import { buildStructuredData } from "./lib/structured-data";
|
|
|
|
// DESIGN.md names Switzer / Basier Circle; neither ships on Google Fonts.
|
|
// These are the documented substitutes — Manrope for UI, Plus Jakarta Sans for
|
|
// display, which holds the tight negative tracking the brand depends on.
|
|
const switzer = Manrope({
|
|
variable: "--font-switzer-local",
|
|
subsets: ["latin"],
|
|
weight: ["400", "500", "600", "700"],
|
|
display: "swap",
|
|
});
|
|
|
|
const display = Plus_Jakarta_Sans({
|
|
variable: "--font-display-local",
|
|
subsets: ["latin"],
|
|
weight: ["500", "600"],
|
|
style: ["normal", "italic"],
|
|
display: "swap",
|
|
});
|
|
|
|
export const metadata: Metadata = {
|
|
metadataBase: new URL(SITE_URL),
|
|
title: {
|
|
// Under 60 characters so Google does not truncate it in the SERP,
|
|
// with the primary keyword ahead of the brand name.
|
|
default: "Kursverwaltung & Buchungssystem für Kursbetriebe · Kumasy",
|
|
template: "%s · Kumasy",
|
|
},
|
|
description:
|
|
"Kumasy bündelt Kurse, Räume, Trainer, Buchungen und Zahlungen in einer Oberfläche. Deine Teilnehmer:innen buchen und bezahlen selbst — mit Stripe, Warteliste und GoBD-konformen Rechnungen.",
|
|
applicationName: SITE_NAME,
|
|
category: "Business Software",
|
|
authors: [{ name: SITE_NAME, url: SITE_URL }],
|
|
creator: SITE_NAME,
|
|
publisher: SITE_NAME,
|
|
alternates: { canonical: "/" },
|
|
openGraph: {
|
|
type: "website",
|
|
locale: "de_DE",
|
|
url: SITE_URL,
|
|
siteName: SITE_NAME,
|
|
title: "Deine Kurse füllen und bezahlen sich von selbst.",
|
|
description:
|
|
"Kurse, Räume, Trainer, Buchungen und Zahlungen in einer Oberfläche. Jetzt auf die Warteliste.",
|
|
},
|
|
twitter: {
|
|
card: "summary_large_image",
|
|
title: "Kumasy — Kursverwaltung und Buchungssystem",
|
|
description:
|
|
"Kurse, Räume, Trainer, Buchungen und Zahlungen in einer Oberfläche.",
|
|
},
|
|
// No `icons` key: app/favicon.ico, app/icon.svg and app/apple-icon.png
|
|
// are picked up by the file convention. Declaring them here as well
|
|
// emitted a duplicate <link rel="icon">.
|
|
manifest: "/manifest.webmanifest",
|
|
formatDetection: { telephone: false, address: false, email: false },
|
|
robots: {
|
|
index: true,
|
|
follow: true,
|
|
googleBot: {
|
|
index: true,
|
|
follow: true,
|
|
// Let Google use full-length snippets, large image previews and
|
|
// full video previews rather than its conservative defaults.
|
|
"max-snippet": -1,
|
|
"max-image-preview": "large",
|
|
"max-video-preview": -1,
|
|
},
|
|
},
|
|
};
|
|
|
|
export const viewport: Viewport = {
|
|
// Matches --color-canvas (#f9fafb), the actual body background.
|
|
themeColor: "#f9fafb",
|
|
colorScheme: "light",
|
|
};
|
|
|
|
export default function RootLayout({ children }: LayoutProps<"/">) {
|
|
return (
|
|
<html
|
|
lang="de"
|
|
className={`${switzer.variable} ${display.variable} h-full antialiased`}
|
|
>
|
|
<head>
|
|
{/* Structured data. Rendered on the server into the initial HTML,
|
|
so crawlers see it without executing any JavaScript. */}
|
|
<script
|
|
type="application/ld+json"
|
|
// The payload is built from our own constants, never from user
|
|
// input, so there is nothing here to escape.
|
|
dangerouslySetInnerHTML={{
|
|
__html: JSON.stringify(buildStructuredData()),
|
|
}}
|
|
/>
|
|
</head>
|
|
<body className="min-h-full flex flex-col bg-canvas text-ink">
|
|
<a
|
|
href="#hauptinhalt"
|
|
className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-[100] focus:rounded-2xl focus:bg-canvas focus:px-5 focus:py-3 focus:text-body focus:font-medium focus:shadow-elevated"
|
|
>
|
|
Zum Hauptinhalt springen
|
|
</a>
|
|
{children}
|
|
</body>
|
|
</html>
|
|
);
|
|
}
|