@import 'tailwindcss'; /* * perinco CRM - Corporate Identity * Farben direkt aus dem perinco Logo gesampelt: * Gruen #6BA04A (Akzent #68A33F) * Grau #6E6E6E * Schrift: perinco.de nutzt Avenir LT (Light 35 / Heavy 85). Ist Avenir lokal * installiert, wird es verwendet, sonst Nunito Sans als freies Aequivalent. */ /* * Sieben Farbwelten, jede in hell und dunkel. Sie haengen an einer Klasse am * (theme-pixel, theme-outdoor, theme-cyberpunk, theme-rainy, theme-snow, theme-aura); ohne Klasse gilt * perinco. Der Modus bleibt die vorhandene Klasse "dark". * * Warum das ohne Aenderung an den Komponenten geht: im Markup steht nirgends * eine feste Farbe, sondern durchgaengig ink-*, brand-*, canvas und surface - * und Tailwind v4 loest auch Eckenradien (--radius-*), Schatten und Schriften * ueber Variablen auf. Ein Theme tauscht also nur Werte. * * Die Rollen der Skalen sind ueber die ganze App gleich (nachgezaehlt, damit * ein Theme nichts unlesbar macht): * ink-50/100 Flaechen und Hover * ink-200 Rahmen (224 von 225 Verwendungen) * ink-300-900 Text, 400 traegt die Hinweistexte * ink-950 liegt nur unter Dialog-Overlays, bleibt immer dunkel * brand-50/100 getoente Flaechen * brand-200-400 Rahmen * brand-500/600 gefuellte Knoepfe - hier steht WEISSE Schrift drauf, jedes * Theme braucht dort also einen ausreichend dunklen Ton * brand-600-800 Text auf getoenten Flaechen */ @theme { --font-sans: 'Avenir Next', 'Avenir', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; /* * Zweite Schrift fuer Ueberschriften und Feldbeschriftungen. Meist ist sie mit * der Grundschrift identisch; nur "Pixel Art" setzt hier eine andere ein - die * Pixelschrift bleibt damit aus dem Fliesstext heraus, ohne dass es dafuer * eigene Selektoren braucht. */ --font-display: 'Avenir Next', 'Avenir', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; /* * Flaechen sind bewusst semantisch benannt statt "weiss": im Dark Mode werden * unten nur diese Variablen umgesetzt, die Utility-Klassen in den Komponenten * bleiben unveraendert. * canvas = Seitenhintergrund * surface = Karten, Leisten, Dialoge */ --color-canvas: #f7f7f7; --color-surface: #ffffff; --color-brand-50: #f2f8ee; --color-brand-100: #e2efd9; --color-brand-200: #c6e0b6; --color-brand-300: #a3cd8a; --color-brand-400: #85ba66; --color-brand-500: #6ba04a; --color-brand-600: #58873b; --color-brand-700: #456a2f; --color-brand-800: #375427; --color-brand-900: #2d4421; --color-brand-accent: #68a33f; --color-ink-50: #f7f7f7; --color-ink-100: #ededed; --color-ink-200: #dcdcdc; --color-ink-300: #c2c2c2; --color-ink-400: #9a9a9a; --color-ink-500: #6e6e6e; --color-ink-600: #5a5a5a; --color-ink-700: #464646; --color-ink-800: #333333; --color-ink-900: #1f1f1f; --color-ink-950: #141414; --shadow-card: 0 1px 2px rgb(31 31 31 / 0.04), 0 4px 16px rgb(31 31 31 / 0.06); --shadow-lift: 0 8px 28px rgb(31 31 31 / 0.1); } /* * Dark Mode. Die Komponenten benutzen durchgaengig die Skalen ink-* und brand-* * sowie canvas/surface - hier werden nur deren Werte getauscht, deshalb braucht * es keine dark:-Varianten im Markup. * * Zwei Ausnahmen bleiben absichtlich in beiden Modi gleich: * ink-950 - liegt nur unter Dialog-Overlays, muss also dunkel bleiben * text-white - steht immer auf farbigen Flaechen (Buttons, Termin-Chips) */ .dark { --color-canvas: #141414; --color-surface: #1c1c1c; /* Neutrale Skala gedreht: 50 = dunkelste Flaeche, 900 = hellster Text */ --color-ink-50: #1f1f1f; --color-ink-100: #292929; --color-ink-200: #383838; /* Nicht die exakte Umkehrung von #c2c2c2: dunkler waeren Hinweistexte (text-ink-300) auf der Flaeche kaum noch zu lesen. */ --color-ink-300: #6e6e6e; --color-ink-400: #909090; --color-ink-500: #a8a8a8; --color-ink-600: #bdbdbd; --color-ink-700: #d2d2d2; --color-ink-800: #e6e6e6; --color-ink-900: #f5f5f5; --color-ink-950: #000000; /* * brand-500 bleibt das CI-Gruen (Buttons). Die hellen Stufen werden zu * dunklen Tints (Hintergruende), die dunklen zu hellen Toenen (Text) - 600 * dient beidem: als hellerer Button-Hover und als lesbare Textfarbe. */ --color-brand-50: #1d2b15; --color-brand-100: #26381c; --color-brand-200: #3a5a29; --color-brand-300: #4e7739; --color-brand-400: #6b9f4f; --color-brand-600: #7fb75c; --color-brand-700: #a3cd8a; --color-brand-800: #c6e0b6; --color-brand-900: #e2efd9; /* * Statusfarben der Kontakte und Toasts sind getoente Pillen: helle Flaeche mit * dunklem Text. Im Dark Mode wird das gedreht. Die Stufen 500/600 bleiben * unberuehrt - dort steht weisse Schrift drauf (Loeschen-Button). */ --color-sky-50: #0d2537; --color-sky-200: #1d4a6b; --color-sky-700: #7cc4ef; --color-amber-50: #2e2008; --color-amber-200: #5c4210; --color-amber-700: #f0b356; --color-violet-50: #241a3d; --color-violet-200: #3f2f66; --color-violet-700: #b49bf2; --color-red-50: #2e1414; --color-red-200: #5e2626; --color-red-700: #f28b8b; } /* =========================================================================== * Outdoor * * Wanderkarte und Bergsee: Sandpapier als Flaeche, Seeblau-Gruen als Akzent, * warme Lehmgrautoene fuer Text. Bewusst kein zweites Blattgruen - das haette * neben perinco wie ein Versehen gewirkt. Der Unterschied faellt an drei * Stellen auf: die Flaeche ist warm statt neutral, die Ecken sind runder und * die Schatten weicher. * * Der helle Block ist mit :not(.dark) abgegrenzt. Ohne das gewinnt er gegen * .dark (eine Klasse mehr in der Spezifitaet) und der Dunkelmodus waere * kaputt - genau das ist der Grund fuer die Schreibweise in allen Themes. * =========================================================================== */ html.theme-outdoor:not(.dark) { /* Blauer Himmel oben, Wiese links. Heller Schleier aus der Flaechenfarbe. */ --theme-header-image: linear-gradient( 90deg, rgb(253 251 246 / 0.94) 0%, rgb(253 251 246 / 0.38) 30%, rgb(253 251 246 / 0.3) 55%, rgb(253 251 246 / 0.94) 100% ), url('/themes/outdoor-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(253 251 246 / 0.97) 0%, rgb(253 251 246 / 0.88) 45%, rgb(253 251 246 / 0.44) 78%, rgb(253 251 246 / 0.58) 100% ), url('/themes/outdoor-sidebar.svg'); --color-canvas: #f2eee3; --color-surface: #fdfbf6; --color-brand-50: #e8f2f1; --color-brand-100: #d2e6e3; --color-brand-200: #a8cfca; --color-brand-300: #74b3ac; --color-brand-400: #3f938b; --color-brand-500: #12736c; --color-brand-600: #0d5d57; --color-brand-700: #0a4a45; --color-brand-800: #073833; --color-brand-900: #052b27; --color-brand-accent: #12736c; /* Lehmgrau: dieselbe Helligkeitstreppe wie perinco, nur mit warmem Stich. */ --color-ink-50: #f4f0e6; --color-ink-100: #e9e3d4; --color-ink-200: #d6cdb8; --color-ink-300: #a1977f; /* Hinweistexte (text-ink-400, 241 Stellen) muessen auf dem Sand lesbar bleiben - deshalb dunkler als die reine Umkehrung. */ --color-ink-400: #6f6650; --color-ink-500: #5c5344; --color-ink-600: #4b4436; --color-ink-700: #3b352a; --color-ink-800: #2b271e; --color-ink-900: #1d1a14; --color-ink-950: #100e0a; /* Runder und weicher als perinco - der zweite Wiedererkennungswert. */ --radius-sm: 0.3rem; --radius-md: 0.45rem; --radius-lg: 0.65rem; --radius-xl: 0.9rem; --radius-2xl: 1.25rem; --shadow-card: 0 2px 4px rgb(61 53 42 / 0.06), 0 8px 24px rgb(61 53 42 / 0.1); --shadow-lift: 0 12px 32px rgb(61 53 42 / 0.16); --font-sans: 'Cabin', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; --font-display: 'Cabin', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; } /* * Outdoor, dunkel: Waldnacht. Die Flaechen sind nicht neutral schwarz, sondern * ins Tannengruen gezogen; der Text bekommt einen warmen Stich, damit es nicht * nach kaltem Terminal aussieht. */ html.theme-outdoor.dark { /* Dieselben Grafiken, dunkler Schleier: aus dem Himmel wird Nacht, aus der Wiese eine Silhouette. */ --theme-header-image: linear-gradient( 90deg, rgb(26 32 22 / 0.95) 0%, rgb(26 32 22 / 0.68) 30%, rgb(26 32 22 / 0.6) 55%, rgb(26 32 22 / 0.95) 100% ), url('/themes/outdoor-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(26 32 22 / 0.97) 0%, rgb(26 32 22 / 0.9) 45%, rgb(26 32 22 / 0.62) 78%, rgb(26 32 22 / 0.72) 100% ), url('/themes/outdoor-sidebar.svg'); --color-canvas: #12170f; --color-surface: #1a2016; --color-brand-50: #0c2b28; --color-brand-100: #103833; --color-brand-200: #17544d; --color-brand-300: #1d6d65; --color-brand-400: #2f9187; --color-brand-500: #14837a; --color-brand-600: #1f9c91; --color-brand-700: #74b3ac; --color-brand-800: #a8cfca; --color-brand-900: #d2e6e3; --color-brand-accent: #1f9c91; --color-ink-50: #1d2419; --color-ink-100: #26301f; --color-ink-200: #35412c; --color-ink-300: #6f7a63; --color-ink-400: #8e9a81; --color-ink-500: #a7b39a; --color-ink-600: #bdc7b2; --color-ink-700: #d3dbca; --color-ink-800: #e6ebe0; --color-ink-900: #f4f7f0; --color-ink-950: #000000; --radius-sm: 0.3rem; --radius-md: 0.45rem; --radius-lg: 0.65rem; --radius-xl: 0.9rem; --radius-2xl: 1.25rem; --shadow-card: 0 2px 4px rgb(0 0 0 / 0.4), 0 8px 24px rgb(0 0 0 / 0.45); --shadow-lift: 0 12px 32px rgb(0 0 0 / 0.6); --font-sans: 'Cabin', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; --font-display: 'Cabin', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; /* Die Statusfarben kommen aus .dark und passen dort schon - hier nur die Pillen leicht ins Warme gezogen, damit sie nicht herausfallen. */ --color-amber-50: #2c2109; --color-amber-200: #574112; } /* =========================================================================== * Pixel Art * * 8-bit: keine runden Ecken, harte Schatten ohne Weichzeichnung, Rahmen als * schwarze Outlines, gesaettigtes Arcade-Gruen. * * Die Pixelschrift steckt nur in --font-display (Ueberschriften und * Feldbeschriftungen). Der Fliesstext bleibt bewusst Nunito Sans: die * Akquise-Tabelle hat acht Spalten bei 11-13 px, und eine Pixelschrift waere * darin nicht mehr zu lesen - ein Design, das die Arbeit behindert, waere ein * schlechtes Design. * =========================================================================== */ html.theme-pixel:not(.dark) { /* 8-bit-Himmel oben, Steinmauer links. */ --theme-header-image: linear-gradient( 90deg, rgb(246 248 241 / 0.95) 0%, rgb(246 248 241 / 0.45) 30%, rgb(246 248 241 / 0.38) 55%, rgb(246 248 241 / 0.95) 100% ), url('/themes/pixel-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(246 248 241 / 0.97) 0%, rgb(246 248 241 / 0.92) 48%, rgb(246 248 241 / 0.72) 80%, rgb(246 248 241 / 0.84) 100% ), url('/themes/pixel-sidebar.svg'); /* Deutlich dunkler als die Karten: Pixelgrafik lebt von harten Kanten, und bei zu aehnlichen Toenen verschwimmen Karte und Grund. */ --color-canvas: #cdd8c2; --color-surface: #f6f8f1; --color-brand-50: #e0f2d4; --color-brand-100: #c8e8b4; /* Rahmen sind hier keine Andeutung, sondern Kontur - deshalb dunkel. */ --color-brand-200: #2b7a1c; --color-brand-300: #256b18; --color-brand-400: #35931f; --color-brand-500: #2e8b22; --color-brand-600: #24701a; --color-brand-700: #1b5613; --color-brand-800: #143f0e; --color-brand-900: #0e2f0a; --color-brand-accent: #2e8b22; --color-ink-50: #eef2e8; --color-ink-100: #dde4d4; /* Der Kniff des Themes: ink-200 ist fast ausschliesslich Rahmen, wird hier also zur schwarzen Outline - 224 Stellen ohne eine Zeile im Markup. */ --color-ink-200: #1f2a1a; --color-ink-300: #5a6653; --color-ink-400: #47523f; --color-ink-500: #38412f; --color-ink-600: #2c3425; --color-ink-700: #22291c; --color-ink-800: #182015; --color-ink-900: #0e140c; --color-ink-950: #000000; --radius-sm: 0; --radius-md: 0; --radius-lg: 0; --radius-xl: 0; --radius-2xl: 0; /* Versetzt statt weichgezeichnet - so sehen Schatten in Pixelgrafik aus. */ --shadow-card: 4px 4px 0 0 #1f2a1a; --shadow-lift: 6px 6px 0 0 #1f2a1a; --font-display: 'Silkscreen', 'Courier New', ui-monospace, monospace; } /* * Pixel Art, dunkel: Arcade-Automat im dunklen Raum. Flaechen fast schwarz mit * blauem Stich, Konturen in gedecktem Gruen (schwarze Outlines waeren auf * schwarzem Grund unsichtbar), das Gruen der Knoepfe leuchtet kraeftiger. */ html.theme-pixel.dark { /* Nachtvariante: derselbe Himmel unter dunklem Schleier. */ --theme-header-image: linear-gradient( 90deg, rgb(21 26 34 / 0.96) 0%, rgb(21 26 34 / 0.7) 30%, rgb(21 26 34 / 0.62) 55%, rgb(21 26 34 / 0.96) 100% ), url('/themes/pixel-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(21 26 34 / 0.97) 0%, rgb(21 26 34 / 0.92) 48%, rgb(21 26 34 / 0.78) 80%, rgb(21 26 34 / 0.88) 100% ), url('/themes/pixel-sidebar.svg'); --color-canvas: #0d0f14; --color-surface: #151a22; --color-brand-50: #0f2a0c; --color-brand-100: #163c11; --color-brand-200: #2c7a1f; --color-brand-300: #35931f; --color-brand-400: #3aa524; --color-brand-500: #3aa524; --color-brand-600: #49bf30; --color-brand-700: #7fdc63; --color-brand-800: #b0ec9c; --color-brand-900: #d8f7cd; --color-brand-accent: #49bf30; --color-ink-50: #1a2029; --color-ink-100: #232b36; --color-ink-200: #33734a; --color-ink-300: #6f7f92; --color-ink-400: #93a3b5; --color-ink-500: #adbccb; --color-ink-600: #c2cfdb; --color-ink-700: #d6e0ea; --color-ink-800: #e9eff5; --color-ink-900: #f6f9fc; --color-ink-950: #000000; --radius-sm: 0; --radius-md: 0; --radius-lg: 0; --radius-xl: 0; --radius-2xl: 0; --shadow-card: 4px 4px 0 0 #000000; --shadow-lift: 6px 6px 0 0 #000000; --font-display: 'Silkscreen', 'Courier New', ui-monospace, monospace; } /* =========================================================================== * Cyberpunk * * Neon in der Nacht: fast schwarze Flaechen mit blauem Stich, violettes * Leuchten, Kanten so scharf, dass die Karten wie HUD-Elemente wirken (2 px * statt 12 px Radius - nicht ganz eckig, das ist schon Pixel Art). * * Der Akzent ist bewusst Violett und nicht Neon-Cyan: auf brand-500 und * brand-600 steht 69 Mal weisse Schrift, und auf Cyan waere die nicht zu lesen. * Cyan sitzt deshalb nur in --color-brand-accent und in den Statusfarben, wo * dunkler Text drauf steht. * * Schatten sind hier kein Schatten, sondern ein Schein: eine farbige Kontur * plus weicher Halo in der Markenfarbe. * =========================================================================== */ html.theme-cyberpunk.dark { /* Neonroehren oben, regnerische Gasse links - hier ist die Grafik zu Hause, der Schleier darf duenner sein. */ --theme-header-image: linear-gradient( 90deg, rgb(15 17 32 / 0.94) 0%, rgb(15 17 32 / 0.5) 28%, rgb(15 17 32 / 0.42) 55%, rgb(15 17 32 / 0.94) 100% ), url('/themes/cyberpunk-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(15 17 32 / 0.96) 0%, rgb(15 17 32 / 0.86) 45%, rgb(15 17 32 / 0.45) 78%, rgb(15 17 32 / 0.6) 100% ), url('/themes/cyberpunk-sidebar.svg'); --color-canvas: #07080f; --color-surface: #0f1120; --color-brand-50: #1a0e2e; --color-brand-100: #24123f; --color-brand-200: #4a1f7a; --color-brand-300: #5f27a0; --color-brand-400: #7a30cc; --color-brand-500: #8b31f0; --color-brand-600: #a24cff; --color-brand-700: #c79bff; --color-brand-800: #ddc4ff; --color-brand-900: #efe3ff; --color-brand-accent: #00e5ff; /* Blaustahl statt Neutralgrau - Grau waere hier zu brav. */ --color-ink-50: #141728; --color-ink-100: #1c2135; --color-ink-200: #2b4560; --color-ink-300: #6b7899; --color-ink-400: #8e9bbd; --color-ink-500: #a9b5d4; --color-ink-600: #c1cbe4; --color-ink-700: #d6ddf0; --color-ink-800: #e9eefa; --color-ink-900: #f7f9ff; --color-ink-950: #000000; --radius-sm: 0.125rem; --radius-md: 0.125rem; --radius-lg: 0.1875rem; --radius-xl: 0.25rem; --radius-2xl: 0.375rem; --shadow-card: 0 0 0 1px rgb(139 49 240 / 0.16), 0 0 22px rgb(139 49 240 / 0.2); --shadow-lift: 0 0 0 1px rgb(139 49 240 / 0.3), 0 0 40px rgb(139 49 240 / 0.35); --font-sans: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; --font-display: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; /* Die getoenten Pillen aus .dark ins Neon gezogen. */ --color-sky-50: #04202e; --color-sky-200: #0b4a63; --color-sky-700: #22d3ee; --color-amber-50: #2b1f04; --color-amber-200: #57400a; --color-amber-700: #ffcc4d; --color-violet-50: #1d1140; --color-violet-200: #382068; --color-violet-700: #d0a2ff; --color-red-50: #2c0e1c; --color-red-200: #5c1c34; --color-red-700: #ff6b8a; } /* * Cyberpunk, hell: Konzern bei Tageslicht. Kuehles Blaugrau statt warmem * Weiss, dasselbe Violett - nur dunkler, damit weisse Schrift darauf haelt. * Die scharfen Kanten bleiben, das Leuchten wird zu einem knappen kalten * Schatten: ein Halo auf hellem Grund sieht nach Fehler aus, nicht nach Neon. */ html.theme-cyberpunk:not(.dark) { /* Am Tag bleibt vom Neon nur ein Nachklang: heller Schleier, dicht genug, dass aus den Roehren blasse Linien werden statt dunkler Flecken. */ --theme-header-image: linear-gradient( 90deg, rgb(255 255 255 / 0.96) 0%, rgb(255 255 255 / 0.62) 30%, rgb(255 255 255 / 0.55) 55%, rgb(255 255 255 / 0.96) 100% ), url('/themes/cyberpunk-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(255 255 255 / 0.98) 0%, rgb(255 255 255 / 0.9) 45%, rgb(255 255 255 / 0.68) 78%, rgb(255 255 255 / 0.78) 100% ), url('/themes/cyberpunk-sidebar.svg'); --color-canvas: #eceff7; --color-surface: #ffffff; --color-brand-50: #f3ecff; --color-brand-100: #e6d9ff; --color-brand-200: #c9adff; --color-brand-300: #a97bff; --color-brand-400: #8d4df0; --color-brand-500: #7326d6; --color-brand-600: #5d1cb0; --color-brand-700: #4a1590; --color-brand-800: #380f6e; --color-brand-900: #290a52; --color-brand-accent: #7326d6; --color-ink-50: #f2f4fb; --color-ink-100: #e4e8f4; --color-ink-200: #c3cbe0; --color-ink-300: #98a2bd; --color-ink-400: #5d6786; --color-ink-500: #4a5473; --color-ink-600: #3a4260; --color-ink-700: #2c334d; --color-ink-800: #1e2438; --color-ink-900: #121728; --color-ink-950: #0a0d18; --radius-sm: 0.125rem; --radius-md: 0.125rem; --radius-lg: 0.1875rem; --radius-xl: 0.25rem; --radius-2xl: 0.375rem; --shadow-card: 0 1px 2px rgb(18 23 40 / 0.06), 0 6px 18px rgb(115 38 214 / 0.1); --shadow-lift: 0 12px 30px rgb(115 38 214 / 0.18); --font-sans: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; --font-display: 'Chakra Petch', 'Avenir Next', 'Nunito Sans', ui-sans-serif, system-ui, sans-serif; } /* =========================================================================== * Rainy - Regenwetter. * * Das einzige Theme mit Bewegung: Nieselregen faellt ueber den Bildschirm und * die Wolken in der oberen Leiste leuchten ab und zu auf. Das macht nicht CSS, * sondern components/RainyWeather.tsx; hier stehen nur die Farben, darunter * --rain-color fuer die Tropfen. * * Die Farbwelt ist Schieferblau auf kuehlem Grau - nass, aber nicht trist. * Schrift bleibt Nunito Sans: das Wetter ist Auffaelligkeit genug. * =========================================================================== */ html.theme-rainy:not(.dark) { /* Tagsueber bedeckt: der Schleier ist in der Mitte duenn genug, dass die grauen Wolken tragen und ein Blitz darauf sichtbar wird. */ --theme-header-image: linear-gradient( 90deg, rgb(248 250 252 / 0.96) 0%, rgb(248 250 252 / 0.35) 30%, rgb(248 250 252 / 0.3) 60%, rgb(248 250 252 / 0.96) 100% ), url('/themes/rainy-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(248 250 252 / 0.97) 0%, rgb(248 250 252 / 0.88) 45%, rgb(248 250 252 / 0.4) 78%, rgb(248 250 252 / 0.55) 100% ), url('/themes/rainy-sidebar.svg'); --rain-color: rgb(62 90 128 / 0.5); /* Auf hellem Grund macht "screen" aus Grau nur etwas helleres Grau - der Blitz waere kaum zu sehen. Tagsueber deckt er deshalb normal und bekommt einen warmen Stich, damit die Wolke sichtbar aufgluehen kann. */ --flash-color: rgb(255 244 196); --flash-blend: normal; --color-canvas: #e8ecf1; --color-surface: #f8fafc; --color-brand-50: #eaf1f7; --color-brand-100: #d3e2ee; --color-brand-200: #a9c5dc; --color-brand-300: #7ba5c6; --color-brand-400: #4f84ad; --color-brand-500: #2f6690; --color-brand-600: #255477; --color-brand-700: #1e4461; --color-brand-800: #17354c; --color-brand-900: #112839; --color-brand-accent: #2f6690; --color-ink-50: #f1f4f7; --color-ink-100: #e3e8ee; --color-ink-200: #cdd5de; --color-ink-300: #9aa6b4; --color-ink-400: #5f6b7a; --color-ink-500: #4d5866; --color-ink-600: #3d4753; --color-ink-700: #2f3741; --color-ink-800: #222931; --color-ink-900: #161b21; --color-ink-950: #0c0f13; --radius-sm: 0.25rem; --radius-md: 0.4rem; --radius-lg: 0.55rem; --radius-xl: 0.75rem; --radius-2xl: 1rem; --shadow-card: 0 1px 2px rgb(22 27 33 / 0.05), 0 6px 20px rgb(47 102 144 / 0.08); --shadow-lift: 0 12px 30px rgb(47 102 144 / 0.16); } /* * Rainy, dunkel: Gewitternacht. Tintenblaue Flaechen, die Wolken treten erst * beim Blitz richtig hervor. */ html.theme-rainy.dark { --theme-header-image: linear-gradient( 90deg, rgb(21 27 36 / 0.95) 0%, rgb(21 27 36 / 0.45) 30%, rgb(21 27 36 / 0.4) 60%, rgb(21 27 36 / 0.95) 100% ), url('/themes/rainy-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(21 27 36 / 0.97) 0%, rgb(21 27 36 / 0.88) 45%, rgb(21 27 36 / 0.5) 78%, rgb(21 27 36 / 0.62) 100% ), url('/themes/rainy-sidebar.svg'); --rain-color: rgb(170 195 225 / 0.45); --flash-color: rgb(225 236 255); --flash-blend: screen; --color-canvas: #0d1117; --color-surface: #151b24; --color-brand-50: #10202e; --color-brand-100: #15293b; --color-brand-200: #1f3d57; --color-brand-300: #2b5577; --color-brand-400: #3b6f99; --color-brand-500: #3a72a0; --color-brand-600: #447fae; --color-brand-700: #8fb8da; --color-brand-800: #bcd5ea; --color-brand-900: #e1edf7; --color-brand-accent: #7fb2dc; --color-ink-50: #1a212b; --color-ink-100: #222b37; --color-ink-200: #313c4a; --color-ink-300: #5d6a7b; --color-ink-400: #8593a4; --color-ink-500: #a0adbc; --color-ink-600: #b9c4d0; --color-ink-700: #d0d8e1; --color-ink-800: #e4e9ef; --color-ink-900: #f4f7fa; --color-ink-950: #000000; --radius-sm: 0.25rem; --radius-md: 0.4rem; --radius-lg: 0.55rem; --radius-xl: 0.75rem; --radius-2xl: 1rem; --shadow-card: 0 1px 2px rgb(0 0 0 / 0.3), 0 6px 20px rgb(0 0 0 / 0.25); --shadow-lift: 0 12px 32px rgb(0 0 0 / 0.4); } /* =========================================================================== * Snow - Winter. * * Zweites Theme mit Bewegung: Schnee faellt und sammelt sich am unteren * Fensterrand (components/SnowWeather.tsx). Hier stehen die Farben, dazu die * --snow-* Variablen fuer Flocken und Schneedecke. * * Eisblau auf fast weissem Grund. Weisse Flocken waeren darauf unsichtbar, * deshalb bekommen sie im hellen Modus eine zarte blaugraue Kontur, und die * Schneedecke eine Schattierung nach unten. * =========================================================================== */ html.theme-snow:not(.dark) { --theme-header-image: linear-gradient( 90deg, rgb(250 252 255 / 0.96) 0%, rgb(250 252 255 / 0.35) 30%, rgb(250 252 255 / 0.3) 60%, rgb(250 252 255 / 0.96) 100% ), url('/themes/snow-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(250 252 255 / 0.97) 0%, rgb(250 252 255 / 0.88) 45%, rgb(250 252 255 / 0.4) 78%, rgb(250 252 255 / 0.55) 100% ), url('/themes/snow-sidebar.svg'); --snow-flake: #f4f9ff; --snow-flake-edge: rgb(90 125 160 / 0.7); --snow-pile: #ffffff; --snow-pile-shade: #c2d3e4; --snow-pile-edge: rgb(80 115 150 / 0.75); --color-canvas: #edf2f7; --color-surface: #fafcff; --color-brand-50: #e9f3fa; --color-brand-100: #d0e5f3; --color-brand-200: #a3cbe6; --color-brand-300: #6eaad4; --color-brand-400: #3f89bd; --color-brand-500: #1f6a9e; --color-brand-600: #185681; --color-brand-700: #144566; --color-brand-800: #10354f; --color-brand-900: #0b273b; --color-brand-accent: #1f6a9e; --color-ink-50: #f1f5f9; --color-ink-100: #e2e9f0; --color-ink-200: #cbd6e1; --color-ink-300: #97a6b6; --color-ink-400: #5c6b7c; --color-ink-500: #4a5868; --color-ink-600: #3a4655; --color-ink-700: #2c3643; --color-ink-800: #1f2731; --color-ink-900: #141a21; --color-ink-950: #0a0e13; --radius-sm: 0.3rem; --radius-md: 0.45rem; --radius-lg: 0.65rem; --radius-xl: 0.85rem; --radius-2xl: 1.15rem; --shadow-card: 0 1px 2px rgb(20 26 33 / 0.05), 0 6px 20px rgb(31 106 158 / 0.08); --shadow-lift: 0 12px 30px rgb(31 106 158 / 0.16); } /* * Snow, dunkel: Winternacht. Tiefes Nachtblau, der Schnee leuchtet darauf von * selbst und braucht keine Kontur. */ html.theme-snow.dark { --theme-header-image: linear-gradient( 90deg, rgb(18 26 38 / 0.95) 0%, rgb(18 26 38 / 0.55) 30%, rgb(18 26 38 / 0.5) 60%, rgb(18 26 38 / 0.95) 100% ), url('/themes/snow-header.svg'); --theme-sidebar-image: linear-gradient( 180deg, rgb(18 26 38 / 0.97) 0%, rgb(18 26 38 / 0.88) 45%, rgb(18 26 38 / 0.55) 78%, rgb(18 26 38 / 0.65) 100% ), url('/themes/snow-sidebar.svg'); --snow-flake: rgb(240 246 252 / 0.9); --snow-flake-edge: transparent; --snow-pile: #e9f1f9; --snow-pile-shade: #a9bdd1; --snow-pile-edge: rgb(255 255 255 / 0.6); --color-canvas: #0b1119; --color-surface: #121a26; --color-brand-50: #0e1f2e; --color-brand-100: #13283c; --color-brand-200: #1c3d59; --color-brand-300: #27567c; --color-brand-400: #3571a0; --color-brand-500: #2f73a8; --color-brand-600: #3b80b5; --color-brand-700: #92c2e6; --color-brand-800: #c0dcf1; --color-brand-900: #e3f0fa; --color-brand-accent: #8cc6ef; --color-ink-50: #172131; --color-ink-100: #1f2a3b; --color-ink-200: #2e3b4e; --color-ink-300: #5b6a7e; --color-ink-400: #8493a7; --color-ink-500: #9fadbf; --color-ink-600: #b9c5d3; --color-ink-700: #d0d9e4; --color-ink-800: #e4eaf1; --color-ink-900: #f5f8fb; --color-ink-950: #000000; --radius-sm: 0.3rem; --radius-md: 0.45rem; --radius-lg: 0.65rem; --radius-xl: 0.85rem; --radius-2xl: 1.15rem; --shadow-card: 0 1px 2px rgb(0 0 0 / 0.3), 0 6px 20px rgb(0 0 0 / 0.25); --shadow-lift: 0 12px 32px rgb(0 0 0 / 0.4); } /* =========================================================================== * Aura - hell "Sunshine", dunkel "Mond". * * Ein Theme mit zwei Gesichtern: bei Tag Sonne in der Ecke oben rechts, * Himmelblau in der Leiste und warmes Bernstein als Akzent; bei Nacht der * Mond an derselben Stelle, Sterne und Lavendel. In beiden ziehen Wolken * langsam von links nach rechts durch die Leiste (reines CSS, siehe * .app-header::before weiter unten), bei Tag fallen dazu ab und zu * Sonnenstrahlen quer ueber den Bildschirm (components/AuraWeather.tsx). * * Die Leiste hat hier vier Ebenen statt zwei, und jede braucht eigene * Position und Groesse - deshalb stehen die in --aura-header-position und * --aura-header-size neben dem Bild. * =========================================================================== */ html.theme-aura:not(.dark) { /* Sonne ueber dem Schleier: sie soll leuchten, nicht verblassen. */ --theme-header-image: url('/themes/aura-sun.svg'), linear-gradient( 90deg, rgb(255 253 247 / 0.95) 0%, rgb(255 253 247 / 0.25) 30%, rgb(255 253 247 / 0.2) 70%, rgb(255 253 247 / 0.7) 100% ), linear-gradient(transparent, transparent), linear-gradient(180deg, #a9d6ff 0%, #d6ecff 70%, #fff3d6 100%); --aura-header-position: right -45px top -55px, center, center, center; --aura-header-size: 130px 130px, cover, cover, cover; --aura-cloud-opacity: 1; --theme-sidebar-image: linear-gradient( 180deg, rgb(255 253 247 / 0.97) 0%, rgb(255 253 247 / 0.88) 45%, rgb(255 253 247 / 0.35) 78%, rgb(255 253 247 / 0.5) 100% ), url('/themes/aura-wave-front.svg'), url('/themes/aura-wave-mid.svg'), url('/themes/aura-wave-back.svg'), url('/themes/aura-sidebar.svg'); --color-canvas: #fbf6ea; --color-surface: #fffdf7; /* Bernstein. 500/600 dunkel genug fuer weisse Schrift. */ --color-brand-50: #fff7e6; --color-brand-100: #ffeac2; --color-brand-200: #fdd28a; --color-brand-300: #f9b54f; --color-brand-400: #e48a17; --color-brand-500: #b85f08; --color-brand-600: #9a4d07; --color-brand-700: #7d3f0a; --color-brand-800: #66340c; --color-brand-900: #522b0d; --color-brand-accent: #e8930c; --color-ink-50: #f8f4ec; --color-ink-100: #efe8dc; --color-ink-200: #e0d6c5; --color-ink-300: #aca08e; --color-ink-400: #6f6454; --color-ink-500: #5b5145; --color-ink-600: #4a4137; --color-ink-700: #3a332b; --color-ink-800: #2a251f; --color-ink-900: #1c1814; --color-ink-950: #0f0d0a; --radius-sm: 0.35rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-2xl: 1.35rem; --shadow-card: 0 1px 2px rgb(28 24 20 / 0.04), 0 8px 24px rgb(232 147 12 / 0.1); --shadow-lift: 0 14px 34px rgb(232 147 12 / 0.18); } html.theme-aura.dark { /* Der Mond liegt wie die Sonne ueber dem Schleier - darunter verschwand er fast. Er sitzt so weit in der Ecke, dass er den Umschalter kaum streift. */ --theme-header-image: url('/themes/aura-moon.svg'), linear-gradient( 90deg, rgb(19 22 42 / 0.95) 0%, rgb(19 22 42 / 0.3) 30%, rgb(19 22 42 / 0.25) 70%, rgb(19 22 42 / 0.45) 100% ), url('/themes/aura-stars.svg'), linear-gradient(180deg, #0e1230 0%, #1b1f4a 100%); --aura-header-position: right -45px top -55px, center, center, center; --aura-header-size: 130px 130px, cover, cover, cover; /* Wolken bei Nacht: blass und vom Mond angeleuchtet statt weiss. */ --aura-cloud-opacity: 0.28; --theme-sidebar-image: linear-gradient( 180deg, rgb(19 22 42 / 0.97) 0%, rgb(19 22 42 / 0.9) 45%, rgb(19 22 42 / 0.6) 78%, rgb(19 22 42 / 0.7) 100% ), url('/themes/aura-wave-front.svg'), url('/themes/aura-wave-mid.svg'), url('/themes/aura-wave-back.svg'), url('/themes/aura-sidebar.svg'); --color-canvas: #0c0e1c; --color-surface: #13162a; /* Mondsilber und Lavendel */ --color-brand-50: #16183a; --color-brand-100: #1d2150; --color-brand-200: #2b3170; --color-brand-300: #3a4290; --color-brand-400: #4b55ad; --color-brand-500: #4d57b8; --color-brand-600: #5a64c6; --color-brand-700: #aab2f0; --color-brand-800: #cfd4fa; --color-brand-900: #eceefe; --color-brand-accent: #c9cff7; --color-ink-50: #171a30; --color-ink-100: #1f233c; --color-ink-200: #2d3250; --color-ink-300: #5b6182; --color-ink-400: #868cab; --color-ink-500: #a1a7c3; --color-ink-600: #babfd6; --color-ink-700: #d1d5e6; --color-ink-800: #e5e8f2; --color-ink-900: #f6f7fb; --color-ink-950: #000000; --radius-sm: 0.35rem; --radius-md: 0.5rem; --radius-lg: 0.75rem; --radius-xl: 1rem; --radius-2xl: 1.35rem; --shadow-card: 0 0 0 1px rgb(170 178 240 / 0.06), 0 8px 26px rgb(0 0 0 / 0.35); --shadow-lift: 0 0 0 1px rgb(170 178 240 / 0.12), 0 14px 36px rgb(0 0 0 / 0.45); } @layer base { html, body { height: 100%; } body { background-color: var(--color-canvas); color: var(--color-ink-900); font-family: var(--font-sans); font-weight: 400; -webkit-font-smoothing: antialiased; /* Farbwechsel beim Umschalten laeuft weich, ohne Layout zu bewegen */ transition: background-color 0.2s ease, color 0.2s ease; } h1, h2, h3, h4, h5, h6 { font-family: var(--font-display); font-weight: 800; letter-spacing: -0.015em; } /* * Die Feldbeschriftungen (labelClass in components/leads/leadUi.tsx) tragen * dieselbe Klasse, damit ein Theme sie mit einer Regel erreicht. Bei perinco * und Outdoor ist --font-display die Grundschrift, hier aendert sich also * nichts. */ .field-label { font-family: var(--font-display); } /* * Hintergrundbilder der oberen Leiste und der Seitenleiste. * * Ohne Theme steht in den Variablen nichts, dann greift der Rueckfall `none` * und beide Flaechen bleiben einfarbig - perinco sieht also aus wie immer. * * Die Variable enthaelt **zwei** Ebenen: einen Schleier aus der Flaechenfarbe * des Themes und darunter die Grafik. Das ist kein Umweg, sondern der Grund, * dass eine Grafik in beiden Modi funktioniert - der Schleier des * Dunkelmodus toent den blauen Himmel zur Nacht ab. Er ist ausserdem die * Bedingung dafuer, dass die Schrift lesbar bleibt: in der oberen Leiste * steht er an den Raendern dicht (Titel links, Knoepfe rechts) und laesst die * Grafik nur in der leeren Mitte durch, in der Seitenleiste liegt er oben * ueber den Menuepunkten und gibt nach unten frei. * * `bg-surface` an den Elementen bleibt: die Farbe liegt unter dem Bild und * traegt, solange die Grafik laedt oder fehlt. */ .app-header { background-image: var(--theme-header-image, none); background-repeat: no-repeat; background-position: center; background-size: cover; } .app-sidebar { background-image: var(--theme-sidebar-image, none); background-repeat: no-repeat; /* Unten verankert: dort sitzt in allen drei Grafiken das Motiv. */ background-position: bottom center; background-size: cover; } /* * Aura: vier Hintergrundebenen in der Leiste, jede mit eigener Lage. * * Dazu ziehende Wolken als ::before. Zwei nahtlose 800-px-Kacheln, die per * background-position genau um ihre Breite (bzw. das Doppelte) wandern - * beim Neustart der Animation steht dasselbe Bild, deshalb kein Ruck. Die * vordere Lage ist doppelt so schnell, das gibt Tiefe. * * isolation: isolate macht die Leiste zu einem eigenen Stapelkontext. Nur * so kann z-index: -1 die Wolken hinter Titel und Knoepfe legen, ohne dass * sie hinter dem Hintergrund der Leiste verschwinden. overflow: hidden * kommt bewusst nicht an die Leiste - sie enthaelt Aufklappmenues. */ html.theme-aura .app-header { isolation: isolate; background-position: var(--aura-header-position); background-size: var(--aura-header-size); } html.theme-aura .app-header::before { content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none; background-image: url('/themes/aura-clouds-near.svg'), url('/themes/aura-clouds-far.svg'); background-repeat: repeat-x; background-size: 800px 100%; opacity: var(--aura-cloud-opacity, 1); /* Links unter dem Titel ausblenden, rechts vor den Knoepfen duenner. */ mask-image: linear-gradient(90deg, transparent 0, #000 22%, #000 75%, rgb(0 0 0 / 0.4) 100%); animation: aura-clouds 150s linear infinite; } /* * Rollende Wellen in der Seitenleiste (Theme Aura, beide Modi). * * Drei Wellenkacheln (aura-wave-*.svg) liegen unter dem Schleier und ueber * dem Dunst. Jede ist genau eine Sinusperiode breit und wiederholt sich * waagerecht; @keyframes aura-waves schiebt jede um genau ihre eigene * Kachelbreite weiter, deshalb sieht das Bild am Ende der Runde aus wie am * Anfang und es ruckt nicht. Weil die Kacheln unterschiedlich breit sind, * laufen sie bei gleicher Dauer unterschiedlich schnell - vorne schneller * als hinten, das gibt Tiefe. * * Es bewegen sich nur die Wellen: der Schleier (erste Ebene) und der Dunst * (letzte) stehen still, sonst wanderte die Helligkeit unter den * Menuepunkten. */ html.theme-aura .app-sidebar { background-repeat: no-repeat, repeat-x, repeat-x, repeat-x, no-repeat; background-size: cover, 260px 165px, 320px 200px, 400px 235px, cover; animation: aura-waves 40s linear infinite; } /* Sonnenstrahl (components/AuraWeather.tsx): verjuengt zur Sonne hin. */ .aura-ray { position: absolute; transform-origin: 0 50%; opacity: 0; background: linear-gradient( 90deg, rgb(255 214 110 / 0.9) 0%, rgb(255 224 150 / 0.45) 35%, rgb(255 236 190 / 0.18) 75%, rgb(255 236 190 / 0) 100% ); clip-path: polygon(0 46%, 100% 0, 100% 100%, 0 54%); filter: blur(10px); } @media (prefers-reduced-motion: reduce) { html.theme-aura .app-header::before, html.theme-aura .app-sidebar { animation: none; } } /* * Wetterleuchten im Theme Rainy (components/RainyWeather.tsx legt das * Element exakt ueber die obere Leiste und blendet es kurz ein). * * Der Trick ist die Maske: sie ist dieselbe Wolken-SVG wie das * Hintergrundbild, gleich skaliert. Das Licht - ein Schein um eine zufaellige * Stelle (--flash-x) - erscheint deshalb nur auf den Wolken selbst, nicht auf * der ganzen Leiste. So leuchten die Wolken von innen auf. */ .rainy-flash { opacity: 0; background: radial-gradient( ellipse 30% 200% at var(--flash-x, 50%) 40%, var(--flash-color, #fff) 0%, transparent 70% ); mix-blend-mode: var(--flash-blend, screen); mask-image: url('/themes/rainy-header.svg'); mask-size: cover; mask-position: center; mask-repeat: no-repeat; } /* * Pixelschrift hat eine feste Zeichenbreite und traegt ihre Sperrung selbst; * die negative Laufweite der Ueberschriften wuerde die Buchstaben ineinander * schieben. Ausserdem kein Kantenglaetten - Pixel sollen Pixel bleiben. */ html.theme-pixel h1, html.theme-pixel h2, html.theme-pixel h3, html.theme-pixel h4, html.theme-pixel h5, html.theme-pixel h6, html.theme-pixel .field-label { letter-spacing: 0; -webkit-font-smoothing: none; font-smooth: never; } /* * Cyberpunk laesst die Ueberschriften atmen: die negative Laufweite zieht * Chakra Petch unangenehm zusammen, gesperrt liest es sich nach Anzeigetafel. */ html.theme-cyberpunk h1, html.theme-cyberpunk h2, html.theme-cyberpunk h3, html.theme-cyberpunk h4, html.theme-cyberpunk h5, html.theme-cyberpunk h6 { letter-spacing: 0.015em; } html.theme-cyberpunk .field-label { letter-spacing: 0.12em; } /* Fokus-Ring durchgaengig in CI-Gruen */ :focus-visible { outline: 2px solid var(--color-brand-500); outline-offset: 2px; } } @layer utilities { /* * Das perinco Logo ist fuer helle Hintergruende gemacht - der Zusatz unter * dem Wortzeichen ist dunkelgrau und waere im Dark Mode unsichtbar. invert * dreht die Helligkeit, hue-rotate holt das CI-Gruen zurueck. */ .dark .logo-adaptive { filter: invert(1) hue-rotate(180deg); } /* * "rounded-full" laesst sich nicht ueber eine Variable abschalten - Tailwind * schreibt dort einen festen Wert. Fuer Pixel Art muss es aber weg: runde * Punkte, Pillen und Bilder neben eckigen Karten waeren ein Bruch. Der * Attributselektor greift genau das eine Klassenwort und ist mit dem * vorangestellten html spezifischer als die Utility-Klasse, deshalb ohne * !important. */ html.theme-pixel [class~='rounded-full'] { border-radius: 0; } .scrollbar-slim::-webkit-scrollbar { width: 8px; height: 8px; } .scrollbar-slim::-webkit-scrollbar-track { background: transparent; } .scrollbar-slim::-webkit-scrollbar-thumb { background: var(--color-ink-200); border-radius: 999px; } .scrollbar-slim::-webkit-scrollbar-thumb:hover { background: var(--color-ink-300); } .line-clamp-3 { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; } } /* Ziehende Wolken im Theme Aura: vordere Kachel 2 x 800 px, hintere 1 x 800 px. */ @keyframes aura-clouds { from { background-position: 0 0, 0 0; } to { background-position: 1600px 0, 800px 0; } } /* * Rollende Wellen in der Seitenleiste (Theme Aura). Reihenfolge der Werte wie * im Bild: Schleier, Welle vorn, Welle Mitte, Welle hinten, Dunst. Jede Welle * wandert um genau ihre Kachelbreite - 260, 320 und 400 px - und ist damit * nach einer Runde wieder deckungsgleich. */ @keyframes aura-waves { from { background-position: center top, 0 bottom, 0 bottom, 0 bottom, center bottom; } to { background-position: center top, 260px bottom, 320px bottom, 400px bottom, center bottom; } }