diff --git a/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte b/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte
index db36577c1..838098a71 100644
--- a/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte
+++ b/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte
@@ -6,6 +6,7 @@
import ProfileStep from './steps/ProfileStep.svelte';
import AppsStep from './steps/AppsStep.svelte';
import AiTierStep from './steps/AiTierStep.svelte';
+ import SyncStep from './steps/SyncStep.svelte';
import CreditsStep from './steps/CreditsStep.svelte';
import CompleteStep from './steps/CompleteStep.svelte';
import { Check } from '@mana/shared-icons';
@@ -31,6 +32,7 @@
{ id: 'profile', label: 'Profil', component: ProfileStep },
{ id: 'apps', label: 'Apps', component: AppsStep },
{ id: 'ai-tier', label: 'KI', component: AiTierStep },
+ { id: 'sync', label: 'Sync', component: SyncStep },
{ id: 'credits', label: 'Credits', component: CreditsStep },
{ id: 'complete', label: 'Fertig', component: CompleteStep },
];
@@ -159,6 +161,10 @@
{:else if currentStepData.id === 'apps'}
+ {:else if currentStepData.id === 'ai-tier'}
+
+ {:else if currentStepData.id === 'sync'}
+
{:else if currentStepData.id === 'credits'}
{:else if currentStepData.id === 'complete'}
diff --git a/apps/mana/apps/web/src/lib/components/onboarding/steps/SyncStep.svelte b/apps/mana/apps/web/src/lib/components/onboarding/steps/SyncStep.svelte
new file mode 100644
index 000000000..54f69ec26
--- /dev/null
+++ b/apps/mana/apps/web/src/lib/components/onboarding/steps/SyncStep.svelte
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
Cloud Sync
+
+ Synchronisiere deine Daten verschlüsselt über alle Geräte — oder nutze Mana nur lokal.
+
+
+
+
+
+
+
+
+
Lokal — immer verfügbar
+
+ Alle deine Daten sind lokal auf deinem Gerät gespeichert. Mana funktioniert vollständig
+ offline — auch ohne Cloud Sync.
+
+
+
+
+
+
+ {#if syncBilling.active}
+
+
+
+
+
Cloud Sync ist aktiv
+
+ Deine Daten werden über alle Geräte synchronisiert.
+
+
+
+
+ {:else}
+
+
+
+
+
+
+
Cloud Sync aktivieren
+
+ Multi-Device-Sync, automatische Backups, Ende-zu-Ende-Verschlüsselung.
+
+
+
+
+
+
+ {#each ['monthly', 'quarterly', 'yearly'] as const as iv}
+
+
+ (selectedInterval = iv)}
+ class="h-3.5 w-3.5 text-primary"
+ />
+ {SYNC_PRICES[iv].label}
+
+ {SYNC_PRICES[iv].credits} Credits
+
+ {/each}
+
+
+ {#if error}
+
+ {/if}
+
+
+ {activating
+ ? 'Wird aktiviert...'
+ : `Sync aktivieren (${SYNC_PRICES[selectedInterval].credits} Credits)`}
+
+
+ {#if balance !== null && balance.balance < SYNC_PRICES[selectedInterval].credits}
+
+ Nicht genügend Credits ({balance.balance} verfügbar). Du kannst Sync jederzeit später in den
+ Einstellungen aktivieren.
+
+ {/if}
+
+ {/if}
+
+
+
+
+ Sync ist optional — du kannst diesen Schritt überspringen und Mana nur lokal nutzen. Alle
+ Features funktionieren auch ohne Sync. Du kannst jederzeit in den Einstellungen aktivieren.
+
+
+
diff --git a/apps/mana/apps/web/src/routes/(app)/+layout.svelte b/apps/mana/apps/web/src/routes/(app)/+layout.svelte
index 454e05ac8..5d4bf43f3 100644
--- a/apps/mana/apps/web/src/routes/(app)/+layout.svelte
+++ b/apps/mana/apps/web/src/routes/(app)/+layout.svelte
@@ -238,6 +238,72 @@
return first ? first.shortLabel.split(' (')[0] : 'KI';
});
+ // ── Sync status dropdown ────────────────────────────────
+ let syncStatusItems = $derived.by(() => {
+ const items: import('@mana/shared-ui').PillDropdownItem[] = [];
+
+ if (syncBilling.active) {
+ items.push({
+ id: 'sync-active',
+ label: 'Cloud Sync aktiv',
+ icon: 'cloudCheck',
+ active: true,
+ disabled: true,
+ });
+ if (syncBilling.nextChargeAt) {
+ const date = new Date(syncBilling.nextChargeAt).toLocaleDateString('de-DE', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ });
+ items.push({
+ id: 'sync-next',
+ label: `Nächste Abbuchung: ${date}`,
+ disabled: true,
+ });
+ }
+ } else if (syncBilling.paused) {
+ items.push({
+ id: 'sync-paused',
+ label: 'Sync pausiert — Credits aufladen',
+ icon: 'warning',
+ onClick: () => goto('/credits?tab=packages'),
+ });
+ } else {
+ items.push({
+ id: 'sync-inactive',
+ label: 'Sync aktivieren',
+ icon: 'cloudArrowUp',
+ onClick: () => goto('/settings/sync'),
+ });
+ items.push({
+ id: 'sync-info',
+ label: 'Nur lokal — ab 30 Credits/Monat',
+ disabled: true,
+ });
+ }
+
+ items.push({ id: 'sync-divider', label: '', divider: true });
+ items.push({
+ id: 'sync-settings',
+ label: 'Sync-Einstellungen',
+ icon: 'gear',
+ onClick: () => goto('/settings/sync'),
+ });
+
+ return items;
+ });
+
+ let currentSyncLabel = $derived(
+ syncBilling.loading
+ ? '...'
+ : syncBilling.active
+ ? 'Sync'
+ : syncBilling.paused
+ ? 'Pausiert'
+ : 'Lokal'
+ );
+
// ── User / Guest awareness ──────────────────────────────
let userEmail = $derived(
authStore.isAuthenticated ? authStore.user?.email || $_('nav.menu') : ''
@@ -750,6 +816,9 @@
showAiTierSelector={true}
{aiTierItems}
{currentAiTierLabel}
+ showSyncStatus={authStore.isAuthenticated}
+ {syncStatusItems}
+ {currentSyncLabel}
{appItems}
{userEmail}
settingsHref="/settings"
diff --git a/packages/shared-ui/src/navigation/PillNavigation.svelte b/packages/shared-ui/src/navigation/PillNavigation.svelte
index 05faac2c6..f7eb2e037 100644
--- a/packages/shared-ui/src/navigation/PillNavigation.svelte
+++ b/packages/shared-ui/src/navigation/PillNavigation.svelte
@@ -247,6 +247,12 @@
aiTierItems?: PillDropdownItem[];
/** Current AI tier label, e.g. "Browser" or "Server" */
currentAiTierLabel?: string;
+ /** Show sync status dropdown */
+ showSyncStatus?: boolean;
+ /** Sync status dropdown items */
+ syncStatusItems?: PillDropdownItem[];
+ /** Current sync status label */
+ currentSyncLabel?: string;
/** Primary color for active state (CSS custom property or hex) */
primaryColor?: string;
/** Elements to prepend before nav items (tab groups, dividers, nav items) */
@@ -342,6 +348,9 @@
showAiTierSelector = false,
aiTierItems = [],
currentAiTierLabel = 'KI',
+ showSyncStatus = false,
+ syncStatusItems = [],
+ currentSyncLabel = 'Sync',
themeMode = 'system',
onThemeModeChange,
appItems = [],
@@ -670,6 +679,16 @@
/>
{/if}
+
+ {#if showSyncStatus && syncStatusItems.length > 0}
+
+ {/if}
+
{#if showThemeToggle && onToggleTheme && !showThemeVariants}