diff --git a/docs/05-frontend.md b/docs/05-frontend.md
index 6312d03..dcb959d 100644
--- a/docs/05-frontend.md
+++ b/docs/05-frontend.md
@@ -138,7 +138,7 @@ Die Darstellung hat **zwei unabhängige Achsen**, beide als Klasse am ``:
| Achse | Klasse | Werte |
| --- | --- | --- |
-| Farbwelt | `theme-perinco`, `theme-pixel`, `theme-outdoor`, `theme-cyberpunk`, `theme-rainy`, `theme-snow` | perinco ist der Standard |
+| Farbwelt | `theme-perinco`, `theme-pixel`, `theme-outdoor`, `theme-cyberpunk`, `theme-rainy`, `theme-snow`, `theme-aura` | perinco ist der Standard |
| Modus | `dark` bzw. keine | hell, dunkel, oder der Systemeinstellung folgen |
`src/index.css` tauscht darin nur Variablenwerte: `canvas`, `surface`, `ink-*`,
@@ -212,9 +212,9 @@ Das ist kein Umweg, sondern erledigt zwei Dinge zugleich:
> nur Licht - über Dunkel leuchtet das Neon, über Weiss bleiben blasse farbige
> Linien. Wer eine Grafik ergänzt: **keine deckenden Flächen**, nur Motiv.
-### Rainy und Snow: Themes mit Wetter
+### Rainy, Snow und Aura: Themes mit Wetter
-Rainy und Snow sind die Themes mit Bewegung. Hier zuerst Rainy, Snow folgt unten. Farben und Grafiken funktionieren wie
+Rainy, Snow und Aura sind die Themes mit Bewegung. Hier zuerst Rainy, Snow und Aura folgen unten. Farben und Grafiken funktionieren wie
bei den anderen; dazu kommt `src/components/RainyWeather.tsx`, das der
`ThemeProvider` nur bei aktivem Rainy einhängt - auch auf der Anmeldeseite, und
beim Wechsel auf ein anderes Theme verschwindet es samt Animationsschleife.
@@ -262,6 +262,29 @@ neue Spaltenzahl umgerechnet, nach dem Neuladen ist der Boden frei.
> gleichzeitig landen also nur etwa 5 je Sekunde. Wer die Wachstumszeit
> ändert, rechnet mit dieser Zahl.
+#### Aura
+
+Ein Theme mit zwei Namen: hell heißt es **Sunshine**, dunkel **Mond**. Beides
+ist `theme-aura`, der Unterschied kommt wie immer aus `.dark`.
+
+| Teil | Umsetzung |
+| --- | --- |
+| **Leiste** | Vier Hintergrundebenen statt zwei: Sonne bzw. Mond (`aura-sun.svg`, `aura-moon.svg`, 130 px in der Ecke oben rechts, halb angeschnitten), Schleier, Sterne (`aura-stars.svg`, nur nachts) und ein Himmelsverlauf. Weil jede Ebene eigene Lage und Größe braucht, stehen die in `--aura-header-position` und `--aura-header-size` |
+| **Ziehende Wolken** | Reines CSS: `.app-header::before` mit zwei nahtlosen 800-px-Kacheln (`aura-clouds-near.svg`, `aura-clouds-far.svg`). `@keyframes aura-clouds` schiebt sie in 150 s um genau 1600 bzw. 800 px - Neustart ohne Ruck, vordere Lage doppelt so schnell. Nachts auf `--aura-cloud-opacity` 0,28 abgedunkelt |
+| **Sonnenstrahlen** | `src/components/AuraWeather.tsx`, nur hell. Alle 16–24 s zwei oder drei `.aura-ray`-Elemente: vom Sonnenmittelpunkt in Richtung Ecke unten links, leicht aufgefächert, per `clip-path` zur Sonne hin verjüngt und weichgezeichnet. Web Animations API: 4 s anschwellen, leicht schwenken, verblassen, danach entfernt |
+
+Zwei Stolperstellen:
+
+1. **Wolken hinter die Schrift.** `::before` mit `z-index: -1` verschwände
+ hinter dem Hintergrund der Leiste. `isolation: isolate` an `.app-header`
+ macht sie zum eigenen Stapelkontext - dann liegen die Wolken über dem
+ Himmel, aber unter Titel und Knöpfen. `overflow: hidden` an der Leiste wäre
+ der naheliegende Weg und ist falsch: sie enthält Aufklappmenüs.
+2. **Sonne und Strahlen gehören zusammen.** `SUN_FROM_RIGHT`/`SUN_FROM_TOP` in
+ `AuraWeather.tsx` sind aus Position und Größe der Sonne in `index.css`
+ gerechnet. Wer die Sonne verschiebt, muss beide mitziehen, sonst kommen die
+ Strahlen aus dem Nichts.
+
### Was ein neues Theme können muss
Die Rollen der Skalen sind über die ganze App gleich; ein Theme, das sie
diff --git a/docs/07-anleitung-benutzer.md b/docs/07-anleitung-benutzer.md
index 3e666fa..4b07877 100644
--- a/docs/07-anleitung-benutzer.md
+++ b/docs/07-anleitung-benutzer.md
@@ -66,7 +66,7 @@ die Scanner-Einstellungen. Alles andere ist für beide Rollen gleich.
zusammen - hilfreich auf kleinen Bildschirmen.
* Rechts oben stehen zwei Schalter. Der rechte wechselt zwischen **hell und
dunkel**; ein **Doppelklick** darauf folgt wieder der Systemeinstellung.
-* Links daneben steht das **Design**. Es gibt sechs zur Wahl, jedes in hell und
+* Links daneben steht das **Design**. Es gibt sieben zur Wahl, jedes in hell und
dunkel:
| Design | Aussehen |
@@ -77,6 +77,7 @@ die Scanner-Einstellungen. Alles andere ist für beide Rollen gleich.
| **Cyberpunk** | Neon in der Nacht: scharfe Kanten, violettes Leuchten, Blaustahl |
| **Rainy** | Regenwetter: Schieferblau, leichter Nieselregen, Wolken mit Wetterleuchten |
| **Snow** | Winter: Eisblau, Schneefall, der sich am unteren Rand zu einer Schneedecke sammelt |
+ | **Aura** | Hell „Sunshine“: Sonne, Himmelblau, warmes Bernstein; dunkel „Mond“: Mond, Sterne, Lavendel |
> **Die Wahl gehört dir, nicht dem Rechner.** Sie wird an deinem Benutzerkonto
> gespeichert. Meldest du dich an einem anderen Rechner an, ist dein Design
@@ -90,11 +91,11 @@ die Scanner-Einstellungen. Alles andere ist für beide Rollen gleich.
Die Designs neben perinco haben zusätzlich eine **Grafik** hinter der
oberen Leiste und der Seitenleiste: 8-bit-Himmel und Steinmauer, blauer Himmel
und Wiese, Neonröhren und Regen, Regenwolken und nasse Hügel, verschneite
- Berge und Tannen. Sie liegt hinter einem Schleier, damit die Schrift darauf
+ Berge und Tannen, Sonne bzw. Mond mit Himmel. Sie liegt hinter einem Schleier, damit die Schrift darauf
lesbar bleibt, und ist dort am deutlichsten, wo die Leisten frei sind - in der
Mitte oben und unten links. perinco bleibt einfarbig.
- **Rainy** und **Snow** sind die Designs, in denen sich etwas bewegt. Das
+ **Rainy**, **Snow** und **Aura** sind die Designs, in denen sich etwas bewegt. Das
Wetter liegt nur optisch über der Oberfläche: es fängt keine Klicks ab, und
geöffnete Dialoge liegen darüber.
@@ -107,9 +108,13 @@ die Scanner-Einstellungen. Alles andere ist für beide Rollen gleich.
kleines **Räumfahrzeug** in perinco-Grün mit Rundumleuchte vorbei und schiebt den Schnee
vom Bildschirm - abwechselnd von links nach rechts und zurück. Die erste
Fahrt startet gleich nach dem Laden der Seite.
+ * Bei **Aura** ziehen in der oberen Leiste langsam Wolken von links nach
+ rechts. Im hellen Modus („Sunshine“) fallen etwa alle 20 Sekunden zwei bis
+ drei Sonnenstrahlen von der Sonne oben rechts quer über den Bildschirm nach
+ unten links; im dunklen Modus („Mond“) steht dort der Mond, ohne Strahlen.
Wer im Betriebssystem **„Bewegung reduzieren"** eingeschaltet hat, sieht bei
- beiden nur Farben und Grafiken - ohne Regen, Blitz und Schnee.
+ allen nur Farben und Grafiken - ohne Regen, Blitz, Schnee, Wolkenzug und Sonnenstrahlen.

* Die Menüpunkte **Akquise**, **Opportunities**, **Kontakte**, **Mails** und
diff --git a/docs/09-quellcode.md b/docs/09-quellcode.md
index 3df1680..a8aced6 100644
--- a/docs/09-quellcode.md
+++ b/docs/09-quellcode.md
@@ -295,7 +295,7 @@ Uhrzeit, dieses Jahr Tag und Monat, sonst mit Jahr.
| Datei | Hält | Eigenheit |
| --- | --- | --- |
| `auth.tsx` | Benutzer, `isAdmin`, `login`, `logout`, `updateUser` | prüft beim Start das gespeicherte Token gegen `/auth/me` |
-| `theme.tsx` | Farbwelt (perinco/Pixel Art/Outdoor/Cyberpunk/Rainy/Snow, Hintergrundgrafiken in `public/themes/`; bei Rainy hängt er zusätzlich `components/RainyWeather.tsx` mit Regen und Wetterleuchten ein, bei Snow `components/SnowWeather.tsx` mit Schneefall und Schneedecke) und Modus (hell/dunkel/System) | Beides gehört dem Benutzer und steht in `crm_users`; `localStorage` ist nur der Zwischenspeicher fürs Inline-Skript in `index.html`. Doppelklick auf den Modus-Schalter folgt wieder dem System |
+| `theme.tsx` | Farbwelt (perinco/Pixel Art/Outdoor/Cyberpunk/Rainy/Snow, Hintergrundgrafiken in `public/themes/`; bei Rainy hängt er zusätzlich `components/RainyWeather.tsx` mit Regen und Wetterleuchten ein, bei Snow `components/SnowWeather.tsx` mit Schneefall, Schneedecke und Räumfahrzeug, bei Aura `components/AuraWeather.tsx` mit Sonnenstrahlen) und Modus (hell/dunkel/System) | Beides gehört dem Benutzer und steht in `crm_users`; `localStorage` ist nur der Zwischenspeicher fürs Inline-Skript in `index.html`. Doppelklick auf den Modus-Schalter folgt wieder dem System |
| `navigation.tsx` | `currentView`, `goTo(view, { tab, leadId })` | erspart es, Callbacks durch vier Ebenen zu reichen; `leadId` öffnet den Kontakt direkt |
| `whisper.tsx` | Modellstatus, Worker, Fortschritt, `transcribe` | ein Worker für die ganze App |
diff --git a/index.html b/index.html
index d0b0659..ccd696e 100644
--- a/index.html
+++ b/index.html
@@ -30,7 +30,8 @@
palette === 'outdoor' ||
palette === 'cyberpunk' ||
palette === 'rainy' ||
- palette === 'snow') {
+ palette === 'snow' ||
+ palette === 'aura') {
document.documentElement.classList.add('theme-' + palette);
}
} catch (e) {
diff --git a/public/themes/aura-clouds-far.svg b/public/themes/aura-clouds-far.svg
new file mode 100644
index 0000000..6cfd4fe
--- /dev/null
+++ b/public/themes/aura-clouds-far.svg
@@ -0,0 +1,19 @@
+
diff --git a/public/themes/aura-clouds-near.svg b/public/themes/aura-clouds-near.svg
new file mode 100644
index 0000000..8ca9cb3
--- /dev/null
+++ b/public/themes/aura-clouds-near.svg
@@ -0,0 +1,18 @@
+
diff --git a/public/themes/aura-moon.svg b/public/themes/aura-moon.svg
new file mode 100644
index 0000000..30c1c21
--- /dev/null
+++ b/public/themes/aura-moon.svg
@@ -0,0 +1,25 @@
+
diff --git a/public/themes/aura-sidebar.svg b/public/themes/aura-sidebar.svg
new file mode 100644
index 0000000..163766f
--- /dev/null
+++ b/public/themes/aura-sidebar.svg
@@ -0,0 +1,20 @@
+
diff --git a/public/themes/aura-stars.svg b/public/themes/aura-stars.svg
new file mode 100644
index 0000000..f7868e0
--- /dev/null
+++ b/public/themes/aura-stars.svg
@@ -0,0 +1,15 @@
+
diff --git a/public/themes/aura-sun.svg b/public/themes/aura-sun.svg
new file mode 100644
index 0000000..ad2b2d7
--- /dev/null
+++ b/public/themes/aura-sun.svg
@@ -0,0 +1,22 @@
+
diff --git a/server/src/userFields.js b/server/src/userFields.js
index d1f8546..c6701d0 100644
--- a/server/src/userFields.js
+++ b/server/src/userFields.js
@@ -48,7 +48,7 @@ export function normalizeAvatar(value) {
* Standard gesetzt: sonst faellt ein Tippfehler im Frontend nie auf, die
* Oberflaeche zeigt weiter das alte Design und niemand weiss, warum.
*/
-export const THEMES = ['perinco', 'pixel', 'outdoor', 'cyberpunk', 'rainy', 'snow'];
+export const THEMES = ['perinco', 'pixel', 'outdoor', 'cyberpunk', 'rainy', 'snow', 'aura'];
export const THEME_MODES = ['light', 'dark'];
/** Farbwelt; null bzw. '' bedeutet "perinco", der Standard. */
diff --git a/src/components/AuraWeather.tsx b/src/components/AuraWeather.tsx
new file mode 100644
index 0000000..185f908
--- /dev/null
+++ b/src/components/AuraWeather.tsx
@@ -0,0 +1,120 @@
+import React, { useEffect, useRef } from 'react';
+
+/*
+ * Sonnenstrahlen im Theme "Aura", hell ("Sunshine").
+ *
+ * Etwa alle 20 Sekunden fallen zwei bis drei Strahlen von der Sonne oben
+ * rechts quer ueber den Bildschirm in die Ecke unten links, schwellen an und
+ * verblassen wieder. Im dunklen Modus ("Mond") passiert nichts - dort steht
+ * der Mond, und Mondstrahlen waeren Kitsch.
+ *
+ * Die ziehenden Wolken macht nicht diese Komponente, sondern reines CSS in
+ * index.css (.app-header::before). Hier steht nur, was Zufall braucht.
+ *
+ * Wie beim Regen und Schnee: pointer-events: none, unter den Dialogen, und bei
+ * "Bewegung reduzieren" still.
+ */
+
+/** Pause zwischen zwei Strahlenbuendeln in ms - "etwa alle 20 Sekunden". */
+const RAYS_MIN_MS = 16000;
+const RAYS_MAX_MS = 24000;
+const RAYS_DURATION_MS = 4200;
+
+/*
+ * Mittelpunkt der Sonne relativ zur rechten oberen Ecke der Leiste. Muss zu
+ * background-position und -size der Sonne in index.css passen
+ * (right -45px top -55px, 130px): Mitte = 65 - 45 von rechts, 65 - 55 von oben.
+ */
+const SUN_FROM_RIGHT = 20;
+const SUN_FROM_TOP = 10;
+
+function random(min: number, max: number) {
+ return min + Math.random() * (max - min);
+}
+
+function prefersReducedMotion() {
+ return window.matchMedia?.('(prefers-reduced-motion: reduce)').matches ?? false;
+}
+
+export const AuraWeather: React.FC = () => {
+ const layerRef = useRef(null);
+
+ useEffect(() => {
+ const layer = layerRef.current;
+ if (!layer || prefersReducedMotion() || typeof layer.animate !== 'function') return;
+
+ let timer = 0;
+ let cancelled = false;
+ const schedule = (delay = random(RAYS_MIN_MS, RAYS_MAX_MS)) => {
+ if (cancelled) return;
+ timer = window.setTimeout(shine, delay);
+ };
+
+ const shine = () => {
+ const header = document.querySelector('.app-header');
+ // Nur bei Tag und nur, wenn jemand hinsieht.
+ if (!header || document.hidden || document.documentElement.classList.contains('dark')) {
+ schedule();
+ return;
+ }
+
+ const rect = header.getBoundingClientRect();
+ const sunX = rect.right - SUN_FROM_RIGHT;
+ const sunY = rect.top + SUN_FROM_TOP;
+
+ // Richtung: in die Ecke unten links. Die Laenge reicht etwas darueber
+ // hinaus, damit der Strahl nicht mitten im Bild abbricht.
+ const dx = 0 - sunX;
+ const dy = window.innerHeight - sunY;
+ const baseAngle = (Math.atan2(dy, dx) * 180) / Math.PI;
+ const length = Math.hypot(dx, dy) * 1.15;
+
+ const count = Math.random() < 0.5 ? 2 : 3;
+ for (let i = 0; i < count; i++) {
+ const beam = document.createElement('div');
+ const thickness = random(70, 150);
+ // Gefaechert um die Diagonale; jeder Strahl etwas versetzt im Takt.
+ const angle = baseAngle + (i - (count - 1) / 2) * random(5, 8);
+ beam.className = 'aura-ray';
+ beam.style.left = `${sunX}px`;
+ beam.style.top = `${sunY - thickness / 2}px`;
+ beam.style.width = `${length}px`;
+ beam.style.height = `${thickness}px`;
+ layer.appendChild(beam);
+
+ const animation = beam.animate(
+ [
+ { opacity: 0, transform: `rotate(${angle - 1.5}deg)` },
+ { opacity: random(0.55, 0.85), offset: 0.4 },
+ { opacity: 0, transform: `rotate(${angle + 1.5}deg)` },
+ ],
+ {
+ duration: RAYS_DURATION_MS + random(-600, 600),
+ delay: i * random(250, 500),
+ easing: 'ease-in-out',
+ fill: 'both',
+ }
+ );
+ animation.onfinish = () => beam.remove();
+ }
+
+ schedule();
+ };
+
+ // Der erste Strahl kommt frueher, damit man das Theme beim Umschalten erlebt.
+ schedule(random(2500, 4000));
+ return () => {
+ cancelled = true;
+ window.clearTimeout(timer);
+ layer.replaceChildren();
+ };
+ }, []);
+
+ return (
+
+ );
+};
diff --git a/src/index.css b/src/index.css
index d108b9a..1935872 100644
--- a/src/index.css
+++ b/src/index.css
@@ -10,8 +10,8 @@
*/
/*
- * Sechs Farbwelten, jede in hell und dunkel. Sie haengen an einer Klasse am
- * (theme-pixel, theme-outdoor, theme-cyberpunk, theme-rainy, theme-snow); ohne Klasse gilt
+ * 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
@@ -865,6 +865,148 @@ html.theme-snow.dark {
--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-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-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 {
@@ -937,6 +1079,62 @@ html.theme-snow.dark {
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;
+ }
+
+ /* 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 {
+ animation: none;
+ }
+ }
+
/*
* Wetterleuchten im Theme Rainy (components/RainyWeather.tsx legt das
* Element exakt ueber die obere Leiste und blendet es kurz ein).
@@ -1045,3 +1243,13 @@ html.theme-snow.dark {
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;
+ }
+}
diff --git a/src/lib/theme.tsx b/src/lib/theme.tsx
index ad4681a..ab8db2f 100644
--- a/src/lib/theme.tsx
+++ b/src/lib/theme.tsx
@@ -12,11 +12,12 @@ import { useAuth } from './auth';
import { THEME_PALETTES, type ThemePalette } from '../types';
import { RainyWeather } from '../components/RainyWeather';
import { SnowWeather } from '../components/SnowWeather';
+import { AuraWeather } from '../components/AuraWeather';
/*
* Darstellung der Oberflaeche. Sie hat zwei unabhaengige Achsen:
*
- * Farbwelt (palette) perinco | pixel | outdoor | cyberpunk | rainy | snow
+ * Farbwelt (palette) perinco | pixel | outdoor | cyberpunk | rainy | snow | aura
* -> Klasse theme-* am
* Modus (mode) hell | dunkel -> Klasse dark am
*
@@ -224,7 +225,7 @@ export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ childre
);
/*
- * Rainy und Snow sind die Themes mit Bewegung. Das Wetter haengt hier und nicht
+ * Rainy, Snow und Aura sind die Themes mit Bewegung. Das Wetter haengt hier und nicht
* im Layout, damit es auch auf der Anmeldeseite regnet - und damit es beim
* Wechsel auf ein anderes Theme sofort samt Animationsschleife verschwindet.
*/
@@ -233,6 +234,7 @@ export const ThemeProvider: React.FC<{ children: React.ReactNode }> = ({ childre
{children}
{palette === 'rainy' && }
{palette === 'snow' && }
+ {palette === 'aura' && }
);
};
diff --git a/src/types.ts b/src/types.ts
index 06fab62..604c3ce 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -88,7 +88,7 @@ export type Role = 'admin' | 'user';
* Tippfehler hier nicht still zum Standarddesign fuehrt.
* server/test/userFields.test.mjs prueft, dass beide Listen gleich bleiben.
*/
-export const THEME_PALETTES = ['perinco', 'pixel', 'outdoor', 'cyberpunk', 'rainy', 'snow'] as const;
+export const THEME_PALETTES = ['perinco', 'pixel', 'outdoor', 'cyberpunk', 'rainy', 'snow', 'aura'] as const;
export type ThemePalette = (typeof THEME_PALETTES)[number];
@@ -103,6 +103,10 @@ export const THEME_PALETTE_LABELS: Record