diff --git a/src/app/globals.css b/src/app/globals.css
index 3dc2f39..e3c366f 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -41,7 +41,15 @@
--fl-accent: var(--fl-amber); /* als Textfarbe nutzbar: 11,3:1 */
--fl-accent-alt: var(--fl-magenta); /* als Textfarbe nutzbar: 6,0:1 */
--fl-on-accent: #1a1024; /* Text auf dem Gradient: min. 5,5:1 */
+ /* Markenstops fuer SVG-Verlaeufe (Logo). Als Variablen, damit das Logo im
+ Light Mode dunkler wird und lesbar bleibt, ohne die Identitaet zu aendern. */
+ --fl-brand-1: #ffc46b;
+ --fl-brand-2: #ffb454;
+ --fl-brand-3: #ff4d9d;
+
--fl-gradient: linear-gradient(115deg, var(--fl-amber-soft) 0%, var(--fl-amber) 38%, var(--fl-magenta) 100%);
+ /* Fuer Text: im Dunkeln identisch, im Hellen deutlich abgedunkelt (AA). */
+ --fl-gradient-ink: var(--fl-gradient);
--fl-gradient-soft: linear-gradient(115deg, #ffb45426 0%, #ff4d9d26 100%);
--fl-glow: 0 0 0 1px #ffb4541f, 0 8px 34px -10px #ff4d9d59, 0 2px 12px -4px #ffb45440;
--fl-glow-strong: 0 0 0 1px #ffb4543d, 0 14px 48px -12px #ff4d9d80, 0 4px 18px -6px #ffb4546b;
@@ -80,7 +88,15 @@
--fl-accent: #8a4b00; /* als Textfarbe: 6,4:1 */
--fl-accent-alt: #b01262; /* als Textfarbe: 6,4:1 */
--fl-on-accent: #23122c; /* Text auf dem Gradient: min. 6,1:1 */
+ /* Logo im Hellen: dieselbe Bernstein-zu-Magenta-Bewegung, nur tiefer
+ angesetzt — sonst verschwindet die Wortmarke auf dem warmen Weiss. */
+ --fl-brand-1: #c26a00;
+ --fl-brand-2: #b35d00;
+ --fl-brand-3: #c9186f;
+
--fl-gradient: linear-gradient(115deg, var(--fl-amber-soft) 0%, var(--fl-amber) 38%, var(--fl-magenta) 100%);
+ /* Verlaufstext im Hellen: beide Enden erfuellen AA auf --fl-bg. */
+ --fl-gradient-ink: linear-gradient(115deg, #8a4b00 0%, #a33a2a 45%, #b01262 100%);
--fl-gradient-soft: linear-gradient(115deg, #ffb4542e 0%, #ff4d9d2e 100%);
--fl-glow: 0 0 0 1px #d8a15c33, 0 8px 26px -12px #ff4d9d4d, 0 2px 10px -4px #ffb4544d;
--fl-glow-strong: 0 0 0 1px #d8a15c59, 0 14px 40px -14px #ff4d9d66, 0 4px 16px -6px #ffb45473;
@@ -181,7 +197,7 @@
-------------------------------------------------------------------------- */
@utility fl-gradient-text {
- background-image: var(--fl-gradient);
+ background-image: var(--fl-gradient-ink);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..1fe4283
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,80 @@
+import type { Metadata, Viewport } from "next";
+import { Outfit } from "next/font/google";
+import { BrandDefs } from "@/components/brand/FlimmerLogo";
+import { env } from "@/lib/env";
+import "./globals.css";
+
+/**
+ * Outfit wird beim Bauen heruntergeladen und mit ausgeliefert — die laufende
+ * Instanz fragt also niemals bei Google an.
+ */
+const outfit = Outfit({
+ subsets: ["latin"],
+ display: "swap",
+ variable: "--font-outfit",
+});
+
+export const metadata: Metadata = {
+ metadataBase: new URL(env.appUrl),
+ title: {
+ default: "Flimmer",
+ template: "%s · Flimmer",
+ },
+ description: "Serien und Filme tracken — selbst gehostet, ohne fremde Konten.",
+ applicationName: "Flimmer",
+ manifest: "/manifest.webmanifest",
+ icons: {
+ icon: [
+ { url: "/favicon.svg", type: "image/svg+xml" },
+ { url: "/favicon.ico", sizes: "48x48" },
+ ],
+ apple: "/icons/apple-touch-icon.png",
+ },
+ openGraph: {
+ title: "Flimmer",
+ description: "Serien und Filme tracken — selbst gehostet, ohne fremde Konten.",
+ images: ["/og-image.png"],
+ type: "website",
+ },
+};
+
+export const viewport: Viewport = {
+ themeColor: [
+ { media: "(prefers-color-scheme: dark)", color: "#0c0a14" },
+ { media: "(prefers-color-scheme: light)", color: "#faf7f5" },
+ ],
+ width: "device-width",
+ initialScale: 1,
+ viewportFit: "cover",
+};
+
+/**
+ * Setzt data-theme, bevor der erste Pixel gemalt wird — sonst blitzt beim
+ * Laden kurz das falsche Farbschema auf.
+ */
+const THEME_BOOTSTRAP = `(function(){try{
+var stored=localStorage.getItem("flimmer-theme");
+var mode=stored==="light"||stored==="dark"?stored:
+ (stored==="system"||!stored)&&window.matchMedia("(prefers-color-scheme: light)").matches?"light":"dark";
+document.documentElement.setAttribute("data-theme",mode);
+}catch(e){document.documentElement.setAttribute("data-theme","dark");}})();`;
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+
+
+
+
+
+ Zum Inhalt springen
+
+ {children}
+
+
+ );
+}
diff --git a/src/app/manifest.ts b/src/app/manifest.ts
new file mode 100644
index 0000000..f40ebd1
--- /dev/null
+++ b/src/app/manifest.ts
@@ -0,0 +1,33 @@
+import type { MetadataRoute } from "next";
+
+/**
+ * PWA-Manifest. Wird von Next unter /manifest.webmanifest ausgeliefert.
+ * Die Icons stammen aus scripts/generate-icons.ts.
+ */
+export default function manifest(): MetadataRoute.Manifest {
+ return {
+ name: "Flimmer — Serien- und Film-Tracker",
+ short_name: "Flimmer",
+ description: "Serien und Filme tracken — selbst gehostet, ohne fremde Konten.",
+ start_url: "/",
+ scope: "/",
+ display: "standalone",
+ orientation: "portrait-primary",
+ background_color: "#0c0a14",
+ theme_color: "#0c0a14",
+ lang: "de",
+ dir: "ltr",
+ categories: ["entertainment", "productivity"],
+ icons: [
+ { src: "/icons/icon-64.png", sizes: "64x64", type: "image/png", purpose: "any" },
+ { src: "/icons/icon-96.png", sizes: "96x96", type: "image/png", purpose: "any" },
+ { src: "/icons/icon-128.png", sizes: "128x128", type: "image/png", purpose: "any" },
+ { src: "/icons/icon-192.png", sizes: "192x192", type: "image/png", purpose: "any" },
+ { src: "/icons/icon-256.png", sizes: "256x256", type: "image/png", purpose: "any" },
+ { src: "/icons/icon-384.png", sizes: "384x384", type: "image/png", purpose: "any" },
+ { src: "/icons/icon-512.png", sizes: "512x512", type: "image/png", purpose: "any" },
+ { src: "/icons/maskable-192.png", sizes: "192x192", type: "image/png", purpose: "maskable" },
+ { src: "/icons/maskable-512.png", sizes: "512x512", type: "image/png", purpose: "maskable" },
+ ],
+ };
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..f94bffb
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,83 @@
+import Link from "next/link";
+import { requireUser } from "@/lib/auth";
+import { getSettings } from "@/lib/settings";
+import { env } from "@/lib/env";
+import { AppShell } from "@/components/AppShell";
+import { FlimmerMark } from "@/components/brand/FlimmerLogo";
+
+/**
+ * Übersichtsseite.
+ *
+ * In dieser Etappe steht hier die Begrüßung und der Hinweis auf die nächsten
+ * Schritte; ab Etappe 5 ziehen „Weiterschauen", Kalender und Neuzugänge ein.
+ */
+export default async function DashboardPage() {
+ const user = await requireUser();
+ const settings = await getSettings();
+
+ return (
+
+
+
+
+
+
+ Hallo {user.name.split(" ")[0]}.
+
+
+ Schön, dass du da bist. Sobald du deinen ersten Titel hinzufügst, findest du hier
+ deine laufenden Serien, die nächsten Episoden und was auf deiner Watchlist gerade
+ verfügbar geworden ist.
+
+
+
+
+ Titel suchen und hinzufügen
+
+
+ Zur Bibliothek
+
+
+
+ {!env.tmdb.isConfigured && (
+
+ Es ist noch kein TMDB-Schlüssel hinterlegt. Ohne ihn findet die Suche keine
+ Metadaten — trage TMDB_API_KEY in der .env ein und starte
+ die Instanz neu.
+