<script lang="ts">
  import { onMount, onDestroy } from 'svelte';
  import { readable, type Readable, type Unsubscriber } from 'svelte/store';
  import { Local } from '../utils/storage';
  import { keyboardShortcuts } from '../utils/keyboard-shortcuts';
  import { getDatabaseInfo, CURRENT_SCHEMA_VERSION, db } from '../utils/db';
  import { cacheManager } from '../utils/cache-manager';
  import { unregisterServiceWorker } from '../utils/sw-cache.js';
  import AppLockSettings from './AppLockSettings.svelte';
  import MailtoSettings from './components/MailtoSettings.svelte';
  import { forceDeleteAllDatabases } from '../utils/db-recovery.js';
  import { closeDatabase, terminateDbWorker } from '../utils/db-worker-client.js';
  import { deactivateDemoMode, isDemoBlockedError } from '../utils/demo-mode.js';
  import { refreshSyncWorkerPgpKeys, unlockPgpKey } from '../utils/sync-worker-client.js';
  import { initPerfObservers } from '../utils/perf-logger.ts';
  import { mailService, clearPgpKeyCache, invalidatePgpCachedBodies } from '../stores/mailService';
  import { searchStore } from '../stores/searchStore';
  const { health: healthStore, stats: searchStatsStore } = searchStore.state;
  const { checkHealth, rebuildFromCache } = searchStore.actions;
  import { Button } from '$lib/components/ui/button';
  import { Input } from '$lib/components/ui/input';
  import { Textarea } from '$lib/components/ui/textarea';
  import { Label } from '$lib/components/ui/label';
  import { Checkbox } from '$lib/components/ui/checkbox';
  import * as Card from '$lib/components/ui/card';
  import * as Alert from '$lib/components/ui/alert';
  import * as Select from '$lib/components/ui/select';
  import * as Dialog from '$lib/components/ui/dialog';
  import * as Tabs from '$lib/components/ui/tabs';
  import * as Tooltip from '$lib/components/ui/tooltip';
  import { Separator } from '$lib/components/ui/separator';
  import ChevronLeft from '@lucide/svelte/icons/chevron-left';
  import Plus from '@lucide/svelte/icons/plus';
  import Info from '@lucide/svelte/icons/info';
  import User from '@lucide/svelte/icons/user';
  import Pencil from '@lucide/svelte/icons/pencil';
  import X from '@lucide/svelte/icons/x';
  import MessageSquare from '@lucide/svelte/icons/message-square';
  import AlertCircle from '@lucide/svelte/icons/alert-circle';
  import CheckCircle from '@lucide/svelte/icons/check-circle';
  import AlertTriangle from '@lucide/svelte/icons/alert-triangle';
  import Download from '@lucide/svelte/icons/download';
  import ChevronDown from '@lucide/svelte/icons/chevron-down';
  import ExternalLink from '@lucide/svelte/icons/external-link';
  import { isTauri, isTauriDesktop, isTauriMobile } from '../utils/platform.js';
  import { openExternalUrl, supportsExternalBrowserOverride } from '../utils/external-links.js';

  const openExternal = async (url: string) => {
    try {
      await openExternalUrl(url, {
        log: (...args: unknown[]) => console.warn('[Settings]', ...args),
      });
    } catch (err) {
      console.warn('[Settings] opener failed:', err);
    }
  };

  // In-app account-deletion entry point. Apple Guideline 5.1.1(v) and Google
  // Play's data-deletion policy both require a discoverable in-app path to
  // delete your account; a hand-off to the web flow is permitted as long as
  // the in-app entry exists and links as directly as possible to deletion.
  // /my-account/security is where account deletion lives on forwardemail.net.
  const ACCOUNT_DELETE_URL = 'https://forwardemail.net/my-account/security';
  import {
    getLatestDesktopRelease,
    detectPlatform as detectDesktopPlatform,
    resolveAssetForPlatform,
    groupAssetsByPlatform,
    formatAssetSize,
  } from '../utils/desktop-download.js';
  import {
    accounts,
    currentAccount,
    loadAccounts,
    createLabel as createMailboxLabel,
    updateLabel as updateMailboxLabel,
    deleteLabel as deleteMailboxLabel,
    switchAccount,
    signOut as mailboxSignOut,
    syncProgress,
    layoutMode,
    setLayoutMode,
  } from '../stores/mailboxActions';
  import {
    settingsLabels,
    attachmentReminder,
    profileName,
    loadProfileName,
    setProfileName,
    fetchSettings,
    effectiveTheme,
    getEffectiveSettingValue,
    setSettingValue,
    LocalSettings,
    isSettingOverrideEnabled,
    setSettingOverrideEnabled,
    localSettingsVersion,
    getSettingDefinition,
    SETTING_SCOPES,
    fetchLabels as fetchSettingsLabels,
    settingsActions,
  } from '../stores/settingsStore';
  import { mailboxStore } from '../stores/mailboxStore';
  const { folders: foldersStore } = mailboxStore.state;
  const { loadFolders } = mailboxStore.actions;
  import { validateLabelName } from '../utils/label-validation.ts';
  import { DEFAULT_SPAM_REPORT_ADDRESS, isValidReportAddress } from '../utils/spam-report.js';
  import { config } from '../config.js';
  import { getFonts, loadFont, getFontFamily } from '../utils/font-loader.js';
  import { LABEL_PALETTE, pickLabelColor as pickLabelColorFromPalette } from '../utils/labels.js';
  import FeedbackModal from './FeedbackModal.svelte';
  import LabelModal from './components/LabelModal.svelte';
  import PushNotificationSettings from './components/PushNotificationSettings.svelte';

  interface ToastApi {
    show?: (message: string, type?: string) => void;
  }

  interface Props {
    navigate?: (path: string) => void;
    storageUsed?: Readable<number> | number;
    storageTotal?: Readable<number> | number;
    localUsage?: Readable<number> | number;
    localQuota?: Readable<number> | number;
    syncPending?: Readable<number> | number;
    indexCount?: Readable<number> | number;
    indexSize?: Readable<number> | number;
    bodyIndexingEnabled?: Readable<boolean> | boolean;
    rebuildIndex?: () => Promise<void>;
    toggleBodyIndexing?: (enabled: boolean) => void;
    toasts?: ToastApi | null;
    applyTheme?: (theme: string) => void;
    applyFont?: (family: string) => void;
  }

  let {
    navigate = () => {},
    storageUsed,
    storageTotal,
    localUsage,
    localQuota,
    syncPending,
    indexCount,
    indexSize,
    bodyIndexingEnabled,
    rebuildIndex = async () => {},
    toggleBodyIndexing = () => {},
    toasts = null,
    applyTheme = () => {},
    applyFont = () => {},
  }: Props = $props();

  const asStore = <T,>(value: Readable<T> | T | undefined): Readable<T> =>
    value && typeof (value as Readable<T>).subscribe === 'function'
      ? (value as Readable<T>)
      : readable((value ?? 0) as T);

  const storageUsedStore = asStore(storageUsed);
  const storageTotalStore = asStore(storageTotal);
  const localUsageStore = asStore(localUsage);
  const localQuotaStore = asStore(localQuota);
  const syncPendingStore = asStore(syncPending);
  const syncProgressStore = asStore(syncProgress);
  const indexCountStore = asStore(indexCount);
  const indexSizeStore = asStore(indexSize);
  const bodyIndexingStore = asStore(bodyIndexingEnabled);
  const layoutModeStore = asStore(layoutMode);

  let apiKey = $state('');
  let theme = $state('system');
  let layoutModeChoice = $state('full');
  let section = $state('general');
  let composePlainDefault = $state(false);
  let attachmentReminderEnabled = $state(false);
  let defaultReplyAll = $state(false);
  let sendAndArchiveDefault = $state(false);
  let signatureEnabled = $state(false);
  let signatureText = $state('');
  let undoSendDelay = $state('0');
  let archiveFolder = $state('');
  let sentFolder = $state('');
  let draftsFolder = $state('');
  let trashFolder = $state('');
  let junkFolder = $state('');
  let availableFolders = $state<string[]>([]);
  let deletableFolders = $state<Array<{ path: string; label: string; count: number | null }>>([]);
  let folderDeleting = $state('');
  let aliasEmail = $state('');
  let activeEmail = $state('');
  let bodyIndexingLocal = $state(true);
  let rebuildingIndex = $state(false);
  let rebuildConfirmVisible = $state(false);
  let pgpKeys = $state<{ name: string; value: string }[]>([]);
  let keyFormVisible = $state(false);
  let editingKeyName = $state('');
  let editingKeyValue = $state('');
  let editingKeyPassphrase = $state('');
  let blockRemoteImages = $state(false);
  let spamReportAddress = $state('');
  let notifyAppUpdates = $state(true);
  let blockTrackingPixels = $state(true);
  let viewPlainText = $state(false);
  let hideCompletedTodosValue = $state(false);
  let startWeekOnSundayValue = $state(true);
  let fontChoice = $state('system');
  let fontLoading = $state(false);
  let availableFonts = $state<{ key: string; name: string; family: string }[]>([]);
  let currentFontFamily = $state('system-ui, -apple-system, sans-serif');
  let externalBrowserOverride = $state('');
  const supportsBrowserOverrideSetting = supportsExternalBrowserOverride();
  let editingIndex = $state(-1);
  let error = $state('');
  let success = $state('');
  let alertClearTimer: ReturnType<typeof setTimeout> | undefined;
  let databaseVersion = $state(CURRENT_SCHEMA_VERSION);
  let databaseRecordCount = $state(0);
  let shortcutsList = $state<
    { label: string; key?: string; keys?: string[]; originalKey?: string }[]
  >([]);
  let capturingKey = $state<string | null>(null);
  const isMac =
    typeof navigator !== 'undefined' && navigator.platform.toUpperCase().includes('MAC');
  const buildAvailableFolders = (
    list: { path?: string; name?: string; fullName?: string; fullname?: string }[] = [],
  ) => {
    const seen = new Set<string>();
    const mapped: string[] = [];
    (list || []).forEach((folder) => {
      const path = folder?.path || folder?.name || folder?.fullName || folder?.fullname;
      if (!path || seen.has(path)) return;
      seen.add(path);
      mapped.push(path);
    });
    return mapped;
  };

  let cacheStats = $state<Record<string, unknown> | null>(null);
  let storageInfo = $state<Record<string, unknown> | null>(null);
  let loadingCacheStats = $state(false);
  let resettingStorage = $state(false);
  let debugPerfEnabled = $state(false);
  let profileNameValue = $state('');
  let lastProfileAccount = '';
  let editingProfileName = $state(false);
  let hybridOverrides = $state<Record<string, boolean>>({});
  const hybridSettingIds = ['theme', 'layout_mode', 'compose_plain_default'];

  const commitProfileName = () => {
    editingProfileName = false;
    const trimmed = (profileNameValue || '').trim();
    if (trimmed !== profileNameValue) {
      profileNameValue = trimmed;
    }
    setProfileName(trimmed, $currentAccount);
  };

  const handleProfileNameFocus = () => {
    editingProfileName = true;
  };

  const handleProfileNameKeydown = (event: KeyboardEvent) => {
    if (event.key === 'Enter') {
      event.preventDefault();
      (event.currentTarget as HTMLElement).blur();
    }
  };

  const getAccountId = () => activeEmail || getAliasEmail() || Local.get('email') || 'default';

  const isHybridSetting = (id: string) => getSettingDefinition(id)?.scope === SETTING_SCOPES.HYBRID;

  const getScopeMeta = (id: string) => {
    const def = getSettingDefinition(id);
    if (!def) return { label: 'Unclassified', variant: 'device' };
    if (def.scope === SETTING_SCOPES.ACCOUNT) {
      return { label: 'Synced', variant: 'account' };
    }
    if (def.scope === SETTING_SCOPES.DEVICE) {
      return { label: 'Device only', variant: 'device' };
    }
    const isOverridden = Boolean(hybridOverrides?.[id]);
    return isOverridden
      ? { label: 'Device override', variant: 'device' }
      : { label: 'Synced default', variant: 'account' };
  };

  const getScopeTooltip = (id: string) => {
    const def = getSettingDefinition(id);
    if (!def) return 'Scope not classified yet.';
    if (def.scope === SETTING_SCOPES.ACCOUNT) {
      return 'Synced: stored with your account and available on your other devices.';
    }
    if (def.scope === SETTING_SCOPES.DEVICE) {
      return 'Device only: stored on this device and not synced.';
    }
    if (def.scope === SETTING_SCOPES.HYBRID) {
      return hybridOverrides?.[id]
        ? 'Device override: this device uses a local value instead of the synced default.'
        : 'Synced default: saved with your account unless you override it on this device.';
    }
    return 'Scope not classified yet.';
  };

  const refreshHybridOverrides = () => {
    const acct = getAccountId();
    hybridOverrides = hybridSettingIds.reduce(
      (acc, id) => {
        acc[id] = isSettingOverrideEnabled(id, acct);
        return acc;
      },
      {} as Record<string, boolean>,
    );
  };

  const getSettingStateValue = (id: string) => {
    switch (id) {
      case 'theme':
        return theme;
      case 'layout_mode':
        return layoutModeChoice;
      case 'compose_plain_default':
        return composePlainDefault;
      default:
        return undefined;
    }
  };

  const applySettingStateValue = (id: string, value: unknown) => {
    switch (id) {
      case 'theme':
        theme = (value as string) || 'system';
        applyTheme?.(theme);
        break;
      case 'layout_mode':
        layoutModeChoice = (value as string) || 'full';
        break;
      case 'compose_plain_default':
        composePlainDefault = Boolean(value);
        break;
      default:
        break;
    }
  };

  const toggleSettingOverride = (id: string) => {
    const acct = getAccountId();
    const next = !hybridOverrides?.[id];
    const currentValue = getSettingStateValue(id);
    setSettingOverrideEnabled(id, next, { account: acct, value: currentValue });
    hybridOverrides = { ...hybridOverrides, [id]: next };
    if (!next) {
      const effective = getEffectiveSettingValue(id, { account: acct });
      applySettingStateValue(id, effective);
    }
  };
  const settingsLabelsStore = asStore(settingsLabels);
  let labelsLoading = $state(false);
  let labelsDeleting = $state('');
  const labelPalette = LABEL_PALETTE;
  let labelPaletteIndex = 0;
  let labelModalVisible = $state(false);
  let labelModalSaving = $state(false);
  let labelModalError = $state('');
  let labelModalMode = $state<'create' | 'edit'>('create');
  let labelModalKeyword = $state('');
  let labelModalName = $state('');
  let labelModalColor = $state('');

  let storageUsedValue = $state(0);
  let storageTotalValue = $state(0);
  let localUsageValue = $state(0);
  let localQuotaValue = $state(0);
  let syncPendingValue = $state(0);
  let syncProgressValue = $state<Record<string, unknown>>({});
  let indexCountValue = $state(0);
  let indexSizeValue = $state(0);

  interface SearchHealth {
    healthy: boolean;
    messagesCount: number;
    indexCount: number;
    divergence: number;
    needsRebuild: boolean;
  }

  let searchHealth = $state<SearchHealth>({
    healthy: true,
    messagesCount: 0,
    indexCount: 0,
    divergence: 0,
    needsRebuild: false,
  });
  let checkingHealth = $state(false);
  let rebuildingFromCache = $state(false);
  let lastHealthCheck = $state<Date | null>(null);

  // Update check state
  let checkingForUpdates = $state(false);
  let updateCheckResult = $state('');

  // Desktop download state (browser-only, resolved lazily on first
  // view of the "About & Help" section).
  const DESKTOP_RELEASES_URL =
    'https://github.com/forwardemail/mail.forwardemail.net/releases/latest';
  let desktopRelease = $state<any>(null);
  let desktopPlatform = $state<{
    os: 'mac' | 'windows' | 'linux' | 'unknown';
    arch: 'x64' | 'arm64' | 'unknown';
    label: string;
  } | null>(null);
  let desktopPrimaryAsset = $state<any>(null);
  let desktopAssetGroups = $state<{ mac: any[]; windows: any[]; linux: any[] }>({
    mac: [],
    windows: [],
    linux: [],
  });
  let desktopLoading = $state(false);
  let desktopError = $state('');
  let otherPlatformsOpen = $state(false);

  $effect(() => {
    if (section !== 'help') return;
    if (isTauriDesktop) return;
    if (desktopRelease || desktopLoading) return;

    desktopLoading = true;
    desktopError = '';
    (async () => {
      try {
        const [rel, plat] = await Promise.all([getLatestDesktopRelease(), detectDesktopPlatform()]);
        desktopRelease = rel;
        desktopPlatform = plat;
        desktopAssetGroups = groupAssetsByPlatform(rel.assets);
        desktopPrimaryAsset = resolveAssetForPlatform(rel.assets, plat);
      } catch (err) {
        desktopError = err instanceof Error ? err.message : 'Failed to fetch the latest release';
        console.warn('[Settings] Desktop release fetch failed:', err);
      } finally {
        desktopLoading = false;
      }
    })();
  });

  async function handleCheckForUpdates() {
    checkingForUpdates = true;
    updateCheckResult = '';
    // Prefer the NATIVE app version on Tauri: it's read from the built binary
    // (Cargo.toml), so it always matches what the store shows. The build-time
    // JS constant (VITE_PKG_VERSION) can lag the native version if the frontend
    // bundle and the native shell were built out of sync, which showed a stale
    // version in the help page on mobile. Fall back to the JS constant on web.
    let installedVersion =
      import.meta.env.VITE_PKG_VERSION || import.meta.env.VITE_APP_VERSION || '';
    if (isTauri) {
      try {
        const { getAppVersion } = await import('../utils/tauri-bridge');
        const native = await getAppVersion();
        if (native) installedVersion = native;
      } catch {
        // keep the build-time fallback
      }
    }

    try {
      // Use the globally exposed checkNow from web-updater (web)
      if (typeof window.__checkForWebUpdates === 'function') {
        const result = await window.__checkForWebUpdates();
        if (result.upToDate) {
          updateCheckResult = `You're on the latest version (v${result.currentVersion || result.latestVersion})`;
        }
        // If not up to date, web-updater's onUpdateAvailable will auto-reload
      } else {
        // Tauri: use the updater-bridge
        try {
          const { checkForUpdates, downloadAndInstall } =
            await import('../utils/updater-bridge.js');
          const result = await checkForUpdates({ force: true });
          if (result?.available) {
            // checkForUpdates() ONLY checks — the download + install + relaunch
            // has to be triggered explicitly here, otherwise the flow dead-ends
            // at "Update available" and nothing installs. On success the app
            // relaunches, so the lines after the await never run.
            updateCheckResult = `Update available: v${result.version} — downloading…`;
            try {
              await downloadAndInstall(result, ({ downloaded, contentLength }) => {
                if (contentLength) {
                  const pct = Math.floor((downloaded / contentLength) * 100);
                  updateCheckResult = `Downloading v${result.version}… ${pct}%`;
                }
              });
              updateCheckResult = `Update v${result.version} installed — restarting…`;
            } catch (err) {
              // updater-bridge throws friendly messages (e.g. the "move to
              // Applications" guidance for a bad install location).
              updateCheckResult = err instanceof Error ? err.message : 'Update failed to install';
            }
          } else if (installedVersion) {
            updateCheckResult = `You're on the latest version (v${installedVersion})`;
          } else {
            updateCheckResult = "You're on the latest version";
          }
        } catch {
          updateCheckResult = 'Could not check for updates';
        }
      }
    } catch {
      updateCheckResult = 'Could not check for updates';
    } finally {
      checkingForUpdates = false;
    }
  }

  interface SavedSearch {
    name: string;
    query: string;
  }

  let savedSearches = $state<SavedSearch[]>([]);
  let newSavedSearchName = $state('');
  let newSavedSearchQuery = $state('');
  let savingSearch = $state(false);
  let deletingSearch = $state('');
  let showOperators = $state(false);
  let showAdvancedCache = $state(false);
  const operatorHelp = [
    { label: 'from:alice', note: 'Filter sender' },
    { label: 'to:bob', note: 'Filter recipient (to/cc/bcc)' },
    { label: 'subject:invoice', note: 'Subject contains' },
    { label: 'has:attachment', note: 'Only messages with attachments' },
    { label: 'is:unread', note: 'Unread only (is:read also works)' },
    { label: 'label:work', note: 'Filter by label' },
    { label: 'in:all', note: 'Search across folders' },
    { label: 'before:2024-01-01', note: 'Date filters (after/on supported)' },
    { label: 'size:>5MB', note: 'Size filters (>, <, >=, <=)' },
    { label: '"exact phrase"', note: 'Quotes for phrase search' },
    { label: '(from:alice AND has:attachment)', note: 'Use AND/OR/NOT and parentheses' },
  ];

  let feedbackModalOpen = $state(false);

  const sectionIds = new Set([
    'general',
    'appearance',
    'privacy',
    'folders',
    'search',
    'advanced',
    'shortcuts',
    'help',
  ]);

  const formatStorageValue = (bytes = 0) => {
    if (!bytes) return '0 GB';
    const gb = bytes / 1024 / 1024 / 1024;
    if (gb >= 1) {
      const rounded = gb >= 10 ? Math.round(gb) : Math.round(gb * 10) / 10;
      return `${rounded} GB`;
    }
    const mb = bytes / 1024 / 1024;
    return `${Math.round(mb)} MB`;
  };

  const storagePercentValue = () => {
    if (!storageTotalValue) return 0;
    return Math.min(100, Math.round((storageUsedValue / storageTotalValue) * 100));
  };

  let subscriptions: Unsubscriber[] = [];

  onMount(() => {
    loadFromStorage();
    loadShortcuts();
    loadDatabaseInfo();

    const hash = window.location.hash?.slice(1) || '';
    if (hash === 'security' || hash === 'accounts') {
      section = 'privacy';
    } else if (sectionIds.has(hash)) {
      section = hash;
    }

    subscriptions = [
      currentAccount.subscribe((v: string) => {
        activeEmail = v || '';
        aliasEmail = activeEmail || getAliasEmail();
        loadLabelsList();
        loadFromStorage();
        loadFolders().catch(() => {});
        if (v && v !== lastProfileAccount) {
          lastProfileAccount = v;
          loadProfileName(v);
        }
      }),
      profileName.subscribe((name: string) => {
        if (!editingProfileName) {
          profileNameValue = name || '';
        }
      }),
      foldersStore.subscribe((list: unknown[]) => {
        const folderList = (list || []) as Array<{
          path?: string;
          name?: string;
          fullName?: string;
          fullname?: string;
          count?: number;
          totalCount?: number;
        }>;
        availableFolders = buildAvailableFolders(folderList);
        // Custom (deletable) folders: everything the store knows about that
        // isn't a protected system/special folder. Gives mobile a way to
        // delete folders, which is otherwise right-click-only (desktop).
        deletableFolders = folderList
          .filter((f) => f?.path && !mailboxStore.actions.isSystemFolder?.(f.path))
          .map((f) => ({
            path: f.path as string,
            label: f.name || f.fullName || f.fullname || f.path || '',
            count: typeof f.totalCount === 'number' ? f.totalCount : (f.count ?? null),
          }));
      }),
      effectiveTheme.subscribe((v: string) => {
        theme = v || 'system';
        applyTheme?.(theme);
      }),
      localSettingsVersion.subscribe(() => {
        refreshHybridOverrides();
      }),
      searchStore.state.savedSearches.subscribe((list: unknown[]) => {
        savedSearches = (list || []) as SavedSearch[];
      }),
      storageUsedStore.subscribe((v: number) => (storageUsedValue = v || 0)),
      storageTotalStore.subscribe((v: number) => (storageTotalValue = v || 0)),
      localUsageStore.subscribe((v: number) => (localUsageValue = v || 0)),
      localQuotaStore.subscribe((v: number) => (localQuotaValue = v || 0)),
      syncPendingStore.subscribe((v: number) => (syncPendingValue = v || 0)),
      syncProgressStore.subscribe((v: Record<string, unknown>) => (syncProgressValue = v || {})),
      indexCountStore.subscribe((v: number) => (indexCountValue = v || 0)),
      indexSizeStore.subscribe((v: number) => (indexSizeValue = v || 0)),
      bodyIndexingStore.subscribe((v: boolean) => (bodyIndexingLocal = !!v)),
      layoutModeStore.subscribe((v: string) => (layoutModeChoice = v || 'full')),
      healthStore.subscribe((v: SearchHealth) => (searchHealth = v || searchHealth)),
      attachmentReminder.subscribe((v: boolean) => (attachmentReminderEnabled = !!v)),
    ];
  });

  onDestroy(() => {
    subscriptions.forEach((fn) => fn?.());
  });

  const loadFromStorage = async () => {
    error = '';
    success = '';
    await fetchSettings();
    const currentAcct = getAccountId();
    apiKey = Local.get('api_key') || '';
    theme = getEffectiveSettingValue('theme', { account: currentAcct }) || 'system';
    layoutModeChoice = getEffectiveSettingValue('layout_mode', { account: currentAcct }) || 'full';
    composePlainDefault = Boolean(
      getEffectiveSettingValue('compose_plain_default', { account: currentAcct }),
    );
    defaultReplyAll = Boolean(
      getEffectiveSettingValue('default_reply_all', { account: currentAcct }),
    );
    sendAndArchiveDefault = Boolean(
      getEffectiveSettingValue('send_and_archive_default', { account: currentAcct }),
    );
    const sig = LocalSettings.getSignature();
    signatureEnabled = sig.enabled;
    signatureText = sig.text;
    undoSendDelay = String(
      getEffectiveSettingValue('undo_send_delay', { account: currentAcct }) || 0,
    );
    archiveFolder = getEffectiveSettingValue('archive_folder', { account: currentAcct }) || '';
    sentFolder = getEffectiveSettingValue('sent_folder', { account: currentAcct }) || '';
    draftsFolder = getEffectiveSettingValue('drafts_folder', { account: currentAcct }) || '';
    trashFolder = getEffectiveSettingValue('trash_folder', { account: currentAcct }) || '';
    junkFolder = getEffectiveSettingValue('junk_folder', { account: currentAcct }) || '';
    bodyIndexingLocal = Boolean(
      getEffectiveSettingValue('search_body_indexing', { account: currentAcct }),
    );
    aliasEmail = getAliasEmail();
    debugPerfEnabled = Local.get('debug_perf') === '1';

    try {
      const pgpKey = `pgp_keys_${currentAcct}`;
      const storedKeys = Local.get(pgpKey);
      pgpKeys = storedKeys ? JSON.parse(storedKeys) : [];
    } catch {
      pgpKeys = [];
    }
    blockRemoteImages = Boolean(
      getEffectiveSettingValue('block_remote_images', { account: currentAcct }),
    );
    spamReportAddress =
      String(
        getEffectiveSettingValue('spam_report_address', { account: currentAcct }) || '',
      ).trim() || DEFAULT_SPAM_REPORT_ADDRESS;
    notifyAppUpdates = getEffectiveSettingValue('notify_app_updates') !== false;
    blockTrackingPixels = Boolean(
      getEffectiveSettingValue('block_tracking_pixels', { account: currentAcct }),
    );
    viewPlainText = Boolean(getEffectiveSettingValue('view_plain_text', { account: currentAcct }));
    hideCompletedTodosValue = Boolean(
      getEffectiveSettingValue('hide_completed_todos', { account: currentAcct }),
    );
    startWeekOnSundayValue = Boolean(
      getEffectiveSettingValue('start_week_on_sunday', { account: currentAcct }),
    );

    fontChoice = getEffectiveSettingValue('font', { account: currentAcct }) || 'system';
    currentFontFamily = getFontFamily(fontChoice);
    availableFonts = getFonts();
    externalBrowserOverride = (
      getEffectiveSettingValue('external_browser_override', { account: currentAcct }) || ''
    ).trim();

    if (fontChoice !== 'system') {
      fontLoading = true;
      loadFont(fontChoice)
        .then((family: string) => {
          currentFontFamily = family;
          applyFont(family);
        })
        .catch((err: Error) => {
          console.error('Failed to load saved font:', err);
          fontChoice = 'system';
        })
        .finally(() => {
          fontLoading = false;
        });
    }

    keyFormVisible = false;
    editingKeyName = '';
    editingKeyValue = '';
    editingIndex = -1;
    section = section || 'general';
    refreshHybridOverrides();

    await loadAccounts();
    // Only initialize search store when credentials are available.
    // Settings.svelte is always mounted; on the login page this would
    // trigger sync-worker connection errors ("Missing auth header").
    if (Local.get('alias_auth') || Local.get('api_key') || Local.get('authToken')) {
      try {
        await searchStore.actions.ensureInitialized();
        await searchStore.actions.refreshSavedSearches();
      } catch (err) {
        console.warn('Failed to init search store', err);
      }
    }
    await loadLabelsList();
  };

  const getAliasEmail = () => {
    const aliasAuth = Local.get('alias_auth') || '';
    if (aliasAuth.includes(':')) return aliasAuth.split(':')[0];
    return Local.get('email') || aliasAuth || '';
  };

  const clearAlerts = () => {
    error = '';
    success = '';
    if (alertClearTimer) {
      clearTimeout(alertClearTimer);
      alertClearTimer = undefined;
    }
  };

  const setSuccess = (message: string) => {
    clearAlerts();
    success = message;
    alertClearTimer = setTimeout(() => {
      success = '';
    }, 4000);
  };

  const saveSavedSearch = async () => {
    clearAlerts();
    if (!newSavedSearchName.trim() || !newSavedSearchQuery.trim()) {
      setError('Please enter both a name and query.');
      return;
    }
    savingSearch = true;
    try {
      await searchStore.actions.saveSearch(newSavedSearchName.trim(), newSavedSearchQuery.trim());
      await searchStore.actions.refreshSavedSearches();
      newSavedSearchName = '';
      newSavedSearchQuery = '';
      setSuccess('Saved search added.');
    } catch (err) {
      setError((err as Error)?.message || 'Failed to save search.');
    } finally {
      savingSearch = false;
    }
  };

  const deleteSavedSearch = async (name: string) => {
    if (!name) return;
    deletingSearch = name;
    try {
      await searchStore.actions.deleteSavedSearch(name);
      await searchStore.actions.refreshSavedSearches();
      setSuccess('Saved search deleted.');
    } catch (err) {
      setError((err as Error)?.message || 'Failed to delete saved search.');
    } finally {
      deletingSearch = '';
    }
  };

  const setError = (message: string) => {
    clearAlerts();
    error = message;
    alertClearTimer = setTimeout(() => {
      error = '';
    }, 5000);
  };

  const setMutationError = (err: unknown, fallback: string) => {
    if (isDemoBlockedError(err)) return;
    setError((err as Error)?.message || fallback);
  };

  const showMutationError = (err: unknown, fallback: string) => {
    if (isDemoBlockedError(err)) return;
    toasts?.show?.((err as Error)?.message || fallback, 'error');
  };

  interface LabelItem {
    keyword?: string;
    id?: string;
    name?: string;
    color?: string;
  }

  const getLabelKey = (label: LabelItem | null) => label?.keyword || label?.id || label?.name;
  const pickLabelColor = () => {
    const color = pickLabelColorFromPalette(labelPaletteIndex, labelPalette);
    labelPaletteIndex += 1;
    return color;
  };

  const labelsList = $derived(
    ($settingsLabelsStore || []).filter((label: unknown) => getLabelKey(label as LabelItem)),
  );

  const loadLabelsList = async () => {
    labelsLoading = true;
    try {
      await fetchSettingsLabels(true);
    } catch (err) {
      setError((err as Error)?.message || 'Failed to load labels.');
    } finally {
      labelsLoading = false;
    }
  };

  const openCreateLabelModal = () => {
    labelModalMode = 'create';
    labelModalKeyword = '';
    labelModalName = '';
    labelModalColor = pickLabelColor();
    labelModalError = '';
    labelModalVisible = true;
  };

  const openEditLabelModal = (label: LabelItem) => {
    const key = getLabelKey(label);
    if (!key) return;
    labelModalMode = 'edit';
    labelModalKeyword = key;
    labelModalName = label?.name || key;
    labelModalColor = label?.color || '';
    labelModalError = '';
    labelModalVisible = true;
  };

  const closeLabelModal = () => {
    labelModalVisible = false;
    labelModalError = '';
  };

  const clearLabelModalError = () => {
    if (labelModalError) labelModalError = '';
  };

  const saveLabelModal = async () => {
    const name = (labelModalName || '').trim();
    const validation = validateLabelName(name);
    if (!validation.ok) {
      labelModalError = validation.error;
      return;
    }
    labelModalSaving = true;
    labelModalError = '';
    try {
      if (labelModalMode === 'edit') {
        const res = await updateMailboxLabel(labelModalKeyword, {
          name: validation.value,
          color: (labelModalColor || '').trim() || undefined,
        });
        if (!res?.success) {
          if (res?.blocked) return;
          labelModalError = res?.error || 'Failed to update label.';
          return;
        }
      } else {
        const res = await createMailboxLabel(
          validation.value,
          (labelModalColor || '').trim() || undefined,
        );
        if (!res?.success) {
          if (res?.blocked) return;
          labelModalError = res?.error || 'Failed to create label.';
          return;
        }
      }
      closeLabelModal();
    } catch (err) {
      if (!isDemoBlockedError(err)) {
        labelModalError = (err as Error)?.message || 'Unable to save label.';
      }
    } finally {
      labelModalSaving = false;
    }
  };

  const deleteLabel = async (label: LabelItem) => {
    const key = getLabelKey(label);
    if (!key) return;
    const labelName = label?.name || key;
    if (!confirm(`Delete label "${labelName}"?`)) return;
    labelsDeleting = key;
    try {
      const res = await deleteMailboxLabel(key);
      if (!res?.success) {
        if (res?.blocked) return;
        setError(res?.error || 'Failed to delete label.');
        return;
      }
    } catch (err) {
      setMutationError(err, 'Failed to delete label.');
    } finally {
      labelsDeleting = '';
    }
  };

  const deleteFolderFromSettings = async (folder: {
    path: string;
    label: string;
    count: number | null;
  }) => {
    if (!folder?.path) return;
    const n = folder.count ?? 0;
    const warning =
      n > 0
        ? `Delete "${folder.label}" and its ${n} message${n === 1 ? '' : 's'}? This cannot be undone.`
        : `Delete "${folder.label}"? This cannot be undone.`;
    if (!confirm(warning)) return;
    folderDeleting = folder.path;
    try {
      await mailboxStore.actions.deleteFolder?.(folder.path);
      setSuccess('Folder deleted.');
    } catch (err) {
      setMutationError(err, 'Failed to delete folder.');
    } finally {
      folderDeleting = '';
    }
  };

  const loadDatabaseInfo = async () => {
    try {
      const info = await getDatabaseInfo();
      if (info) {
        databaseVersion = info.version ?? CURRENT_SCHEMA_VERSION;
        const totalRecords = Object.values(info.counts || {}).reduce(
          (sum: number, count) => sum + (count as number),
          0,
        );
        databaseRecordCount = totalRecords;
      }
    } catch (err) {
      console.warn('Failed to load database info:', err);
    }
  };

  const togglePerfLogging = () => {
    debugPerfEnabled = !debugPerfEnabled;
    Local.set('debug_perf', debugPerfEnabled ? '1' : '0');
    if (debugPerfEnabled) {
      initPerfObservers();
    }
    toasts?.show?.(
      debugPerfEnabled ? 'Performance tracing enabled' : 'Performance tracing disabled',
      'info',
    );
  };

  const saveTheme = async () => {
    try {
      await setSettingValue('theme', theme, { account: getAccountId() });
      applyTheme?.(theme);
      toasts?.show?.('Theme updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save theme');
    }
  };

  const saveLayoutMode = async () => {
    try {
      await setLayoutMode?.(layoutModeChoice);
      toasts?.show?.('Layout updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save layout');
    }
  };

  const saveFont = async () => {
    fontLoading = true;
    try {
      const currentAcct = activeEmail || getAliasEmail() || Local.get('email') || '';
      const family = await loadFont(fontChoice);
      currentFontFamily = family;
      await setSettingValue('font', fontChoice, { account: currentAcct });
      applyFont(family);
      toasts?.show?.('Font updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to load font');
      fontChoice = 'system';
      currentFontFamily = getFontFamily('system');
      applyFont(currentFontFamily);
    } finally {
      fontLoading = false;
    }
  };

  const saveExternalBrowserOverride = async () => {
    const trimmed = externalBrowserOverride.trim();
    try {
      await setSettingValue('external_browser_override', trimmed, {
        account: getAccountId(),
      });
      externalBrowserOverride = trimmed;
      toasts?.show?.(
        trimmed
          ? 'External browser override saved'
          : 'External links will use the system default browser',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to save browser override');
    }
  };

  const clearExternalBrowserOverride = async () => {
    externalBrowserOverride = '';
    await saveExternalBrowserOverride();
  };

  const saveSpamReportAddress = async () => {
    const trimmed = spamReportAddress.trim();
    if (!isValidReportAddress(trimmed)) {
      toasts?.show?.('Enter a valid email address for spam reports', 'error');
      return;
    }
    try {
      // Store the default as empty so future default changes apply automatically.
      const stored = trimmed === DEFAULT_SPAM_REPORT_ADDRESS ? '' : trimmed;
      await setSettingValue('spam_report_address', stored, { account: getAccountId() });
      spamReportAddress = trimmed;
      toasts?.show?.(`Spam reports will be sent to ${trimmed}`, 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save spam report address');
    }
  };

  const resetSpamReportAddress = async () => {
    spamReportAddress = DEFAULT_SPAM_REPORT_ADDRESS;
    await saveSpamReportAddress();
  };

  const saveSignatureEnabled = () => {
    LocalSettings.setSignature({ enabled: signatureEnabled });
    toasts?.show?.(`Signature ${signatureEnabled ? 'enabled' : 'disabled'}`, 'success');
  };

  const saveSignatureText = () => {
    LocalSettings.setSignature({ text: signatureText });
    toasts?.show?.('Signature saved', 'success');
  };

  const saveUndoSendDelay = async () => {
    try {
      await setSettingValue('undo_send_delay', Number(undoSendDelay) || 0, {
        account: getAccountId(),
      });
      toasts?.show?.('Undo send updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save undo send');
    }
  };

  const saveComposePlainDefault = async () => {
    try {
      await setSettingValue('compose_plain_default', composePlainDefault, {
        account: getAccountId(),
      });
      toasts?.show?.('Composer default updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save composer default');
    }
  };

  const saveAttachmentReminder = async () => {
    try {
      await settingsActions.setAttachmentReminder(attachmentReminderEnabled);
      toasts?.show?.(
        attachmentReminderEnabled ? 'Attachment reminder enabled' : 'Attachment reminder disabled',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to save attachment reminder setting');
    }
  };

  const saveDefaultReplyAll = async () => {
    try {
      await setSettingValue('default_reply_all', defaultReplyAll, {
        account: getAccountId(),
      });
      toasts?.show?.(
        defaultReplyAll ? 'Reply All set as default' : 'Reply set as default',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to save reply default');
    }
  };

  const saveSendAndArchiveDefault = async () => {
    try {
      await setSettingValue('send_and_archive_default', sendAndArchiveDefault, {
        account: getAccountId(),
      });
      toasts?.show?.(
        sendAndArchiveDefault ? 'Send & Archive set as default' : 'Send set as default',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to save send default');
    }
  };

  const toggleNotifyAppUpdates = () => {
    try {
      setSettingValue('notify_app_updates', notifyAppUpdates, { account: getAccountId() });
      toasts?.show?.(
        notifyAppUpdates
          ? 'You will be notified when the app updates'
          : 'App update notifications turned off',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to update notification setting');
    }
  };

  const toggleBlockRemoteImages = () => {
    try {
      setSettingValue('block_remote_images', blockRemoteImages, { account: getAccountId() });
      toasts?.show?.(
        blockRemoteImages
          ? 'Remote images will be blocked for privacy'
          : 'Remote images will load automatically',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to update image blocking setting');
    }
  };

  const toggleBlockTrackingPixels = () => {
    try {
      setSettingValue('block_tracking_pixels', blockTrackingPixels, { account: getAccountId() });
      toasts?.show?.(
        blockTrackingPixels
          ? 'Tracking pixels will be blocked'
          : 'Tracking pixels will load (not recommended)',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to update tracking pixel setting');
    }
  };

  const toggleViewPlainText = () => {
    try {
      setSettingValue('view_plain_text', viewPlainText, { account: getAccountId() });
      toasts?.show?.(
        viewPlainText ? 'Emails will render as plain text' : 'Emails will render with formatting',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to update plain text setting');
    }
  };

  const toggleHideCompletedTodos = () => {
    try {
      setSettingValue('hide_completed_todos', hideCompletedTodosValue, {
        account: getAccountId(),
      });
      toasts?.show?.(
        hideCompletedTodosValue
          ? 'Completed todos will be hidden in the calendar'
          : 'Completed todos will be shown in the calendar',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to update completed todos setting');
    }
  };

  const toggleStartWeekOnSunday = () => {
    try {
      setSettingValue('start_week_on_sunday', startWeekOnSundayValue, {
        account: getAccountId(),
      });
      toasts?.show?.(
        startWeekOnSundayValue ? 'Week now starts on Sunday' : 'Week now starts on Monday',
        'success',
      );
    } catch (err) {
      showMutationError(err, 'Failed to update week start setting');
    }
  };

  const saveArchiveFolder = async () => {
    try {
      await setSettingValue('archive_folder', archiveFolder || null, { account: getAccountId() });
      toasts?.show?.('Archive folder updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save archive folder');
    }
  };

  const saveSentFolder = async () => {
    try {
      await setSettingValue('sent_folder', sentFolder || null, { account: getAccountId() });
      toasts?.show?.('Sent folder updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save sent folder');
    }
  };

  const saveDraftsFolder = async () => {
    try {
      await setSettingValue('drafts_folder', draftsFolder || null, { account: getAccountId() });
      toasts?.show?.('Drafts folder updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save drafts folder');
    }
  };

  const saveTrashFolder = async () => {
    try {
      await setSettingValue('trash_folder', trashFolder || null, { account: getAccountId() });
      toasts?.show?.('Trash folder updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save trash folder');
    }
  };

  const saveJunkFolder = async () => {
    try {
      await setSettingValue('junk_folder', junkFolder || null, { account: getAccountId() });
      toasts?.show?.('Junk folder updated', 'success');
    } catch (err) {
      showMutationError(err, 'Failed to save junk folder');
    }
  };

  const resetSectionChanges = () => {
    const currentSection = section;
    loadFromStorage();
    section = currentSection || 'general';
  };

  const openNewKey = () => {
    keyFormVisible = true;
    editingIndex = -1;
    editingKeyName = '';
    editingKeyValue = '';
    editingKeyPassphrase = '';
  };

  const editKey = (key: { name: string; value: string }) => {
    const idx = pgpKeys.indexOf(key);
    if (idx === -1) return;
    keyFormVisible = true;
    editingIndex = idx;
    editingKeyName = key.name || '';
    editingKeyValue = key.value || '';
    editingKeyPassphrase = '';
  };

  const loadStoredPgpPassphrases = (): Record<string, string> => {
    try {
      const currentAcct = activeEmail || getAliasEmail() || Local.get('email') || '';
      const raw = Local.get(`pgp_passphrases_${currentAcct}`);
      if (!raw) return {};
      const parsed = JSON.parse(raw);
      return parsed && typeof parsed === 'object' ? parsed : {};
    } catch {
      return {};
    }
  };

  const persistStoredPgpPassphrases = (passphrases: Record<string, string>) => {
    const currentAcct = activeEmail || getAliasEmail() || Local.get('email') || '';
    const storageKey = `pgp_passphrases_${currentAcct}`;
    const filtered = Object.fromEntries(
      Object.entries(passphrases || {}).filter(
        ([name, value]) => Boolean(name) && typeof value === 'string' && value.trim().length > 0,
      ),
    );
    if (Object.keys(filtered).length > 0) {
      Local.set(storageKey, JSON.stringify(filtered));
    } else {
      Local.remove(storageKey);
    }
  };

  const removeKey = async (key: { name: string; value: string }) => {
    const currentAcct = activeEmail || getAliasEmail() || Local.get('email') || '';
    pgpKeys = pgpKeys.filter((k) => k !== key);
    Local.set(`pgp_keys_${currentAcct}`, JSON.stringify(pgpKeys));

    const storedPassphrases = loadStoredPgpPassphrases();
    if (key?.name) {
      delete storedPassphrases[key.name];
      persistStoredPgpPassphrases(storedPassphrases);
    }

    refreshSyncWorkerPgpKeys();
    clearPgpKeyCache();
    try {
      await invalidatePgpCachedBodies(currentAcct);
    } catch (err) {
      // Same worker-timeout hazard as saveKey. The key is already removed;
      // stale cached bodies just linger until their next open.
      console.warn('[Settings] invalidatePgpCachedBodies failed:', err);
    }
    setSuccess('Encryption key removed.');
  };

  const cancelKeyForm = () => {
    keyFormVisible = false;
    editingIndex = -1;
    editingKeyName = '';
    editingKeyValue = '';
    editingKeyPassphrase = '';
  };

  let savingKey = $state(false);

  // Every await in here can reject, not just return an error result: sync
  // worker calls time out after 30s, and iOS WKWebView is known to hang
  // workers. An unhandled rejection here used to leave the form open with no
  // feedback and wedge the page's dropdowns until a refresh, so the whole
  // flow is wrapped and always surfaces an error message instead.
  const saveKey = async () => {
    if (savingKey) return;
    savingKey = true;
    try {
      const name = (editingKeyName || '').trim();
      const value = (editingKeyValue || '').trim();
      const passphrase = (editingKeyPassphrase || '').trim();
      if (!name || !value) {
        setError('Please provide a name and key.');
        return;
      }

      const checkResult = await unlockPgpKey({
        keyName: name,
        keyValue: value,
        checkOnly: true,
      });
      if (!checkResult?.success && checkResult?.needsPassphrase === false) {
        setError(
          /no secret key packet found/i.test(checkResult?.error || '')
            ? 'Please provide a PGP private key, not a public key.'
            : checkResult?.error || 'Please provide a valid PGP private key.',
        );
        return;
      }

      if (passphrase) {
        const unlockResult = await unlockPgpKey({
          keyName: name,
          passphrase,
          keyValue: value,
          remember: false,
        });
        if (!unlockResult?.success) {
          setError(unlockResult?.error || 'That passphrase could not unlock this private key.');
          return;
        }
      }

      const previousKey = editingIndex >= 0 ? pgpKeys[editingIndex] : null;
      const keys = [...pgpKeys];
      if (editingIndex >= 0) {
        keys[editingIndex] = { name, value };
      } else {
        keys.push({ name, value });
      }
      pgpKeys = keys;
      const currentAcct = activeEmail || getAliasEmail() || Local.get('email') || '';
      Local.set(`pgp_keys_${currentAcct}`, JSON.stringify(pgpKeys));

      const storedPassphrases = loadStoredPgpPassphrases();
      if (previousKey?.name && previousKey.name !== name) {
        delete storedPassphrases[previousKey.name];
      }
      if (passphrase) {
        storedPassphrases[name] = passphrase;
      } else {
        delete storedPassphrases[name];
      }
      persistStoredPgpPassphrases(storedPassphrases);

      refreshSyncWorkerPgpKeys();
      clearPgpKeyCache();
      try {
        await invalidatePgpCachedBodies(currentAcct);
      } catch (err) {
        // The key is saved at this point. A failed cache invalidation only
        // means previously cached bodies get re-decrypted on next open.
        console.warn('[Settings] invalidatePgpCachedBodies failed:', err);
      }
      cancelKeyForm();
      setSuccess(
        passphrase
          ? 'Encryption key saved and unlocked locally.'
          : 'Encryption key saved locally. If it is passphrase-protected, you will be prompted when opening an encrypted message.',
      );
    } catch (err) {
      setError(
        (err as Error)?.message ||
          'Could not validate the key right now. Please try again in a moment.',
      );
    } finally {
      savingKey = false;
    }
  };

  const clearData = () => {
    Local.clear();
    window.location.replace('/');
  };

  const signOut = async () => {
    await mailboxSignOut();
  };

  const loadCacheStatistics = async () => {
    loadingCacheStats = true;
    try {
      const [stats, storage] = await Promise.all([
        cacheManager.getCacheStatistics(),
        cacheManager.getStorageInfo(),
      ]);
      cacheStats = stats;
      storageInfo = storage;
    } catch (err) {
      console.error('Failed to load cache statistics:', err);
      setError('Failed to load cache statistics');
    } finally {
      loadingCacheStats = false;
    }
  };

  const openRebuildConfirm = () => {
    clearAlerts();
    rebuildConfirmVisible = true;
  };

  const closeRebuildConfirm = () => {
    rebuildConfirmVisible = false;
  };

  const confirmRebuildIndex = async () => {
    if (rebuildingIndex) return;
    rebuildingIndex = true;
    try {
      await rebuildIndex();
      setSuccess('Search index rebuilt.');
      toasts?.show?.('Search index rebuilt', 'success');
    } catch (err) {
      setError((err as Error)?.message || 'Failed to rebuild index.');
      toasts?.show?.((err as Error)?.message || 'Failed to rebuild index.', 'error');
    } finally {
      rebuildingIndex = false;
      closeRebuildConfirm();
    }
  };

  const runHealthCheck = async () => {
    if (checkingHealth) return;
    checkingHealth = true;
    clearAlerts();
    try {
      await checkHealth();
      lastHealthCheck = new Date();
      toasts?.show?.('Health check complete', 'success');
    } catch (err) {
      setError((err as Error)?.message || 'Failed to check index health');
    } finally {
      checkingHealth = false;
    }
  };

  const runRebuildFromCache = async () => {
    if (rebuildingFromCache) return;
    rebuildingFromCache = true;
    clearAlerts();
    try {
      await rebuildFromCache();
      lastHealthCheck = new Date();
      setSuccess('Index rebuilt from cache');
      toasts?.show?.('Index rebuilt from cache', 'success');
    } catch (err) {
      setError((err as Error)?.message || 'Failed to rebuild from cache');
      toasts?.show?.((err as Error)?.message || 'Failed to rebuild from cache', 'error');
    } finally {
      rebuildingFromCache = false;
    }
  };

  const toggleBodyIndexingOption = async () => {
    const newValue = !bodyIndexingLocal;
    bodyIndexingLocal = newValue;
    toggleBodyIndexing?.(newValue);
    setSuccess(
      newValue
        ? 'Body indexing enabled. Rebuild your search index to apply this change.'
        : 'Body indexing disabled.',
    );
  };

  const formatKey = (key: string | string[] | undefined) => {
    if (!key) return '';
    try {
      return keyboardShortcuts.formatKey(key);
    } catch {
      if (Array.isArray(key)) return key.join(' + ');
      return String(key || '');
    }
  };

  const loadShortcuts = () => {
    shortcutsList = keyboardShortcuts.getShortcutsList();
  };

  const resetShortcuts = () => {
    clearAlerts();
    if (capturingKey) cancelRebind();
    try {
      keyboardShortcuts.resetToDefaults();
      loadShortcuts();
      setSuccess('Keyboard shortcuts reset to defaults.');
      toasts?.show?.('Keyboard shortcuts reset to defaults', 'success');
    } catch (err) {
      setError((err as Error)?.message || 'Failed to reset shortcuts.');
      toasts?.show?.((err as Error)?.message || 'Failed to reset shortcuts.', 'error');
    }
  };

  const cancelRebind = () => {
    keyboardShortcuts.stopCapture();
    capturingKey = null;
  };

  const startRebind = (shortcut: { originalKey?: string }) => {
    if (!shortcut.originalKey) return;
    clearAlerts();
    capturingKey = shortcut.originalKey;
    keyboardShortcuts.startCapture((captured: string) => {
      keyboardShortcuts.stopCapture();

      if (!capturingKey) return; // rebind was cancelled before the key landed
      const originalKey = capturingKey;

      if (captured === 'esc') {
        capturingKey = null;
        return;
      }

      try {
        keyboardShortcuts.updateShortcut(originalKey, captured);
        loadShortcuts();
        capturingKey = null;
        setSuccess('Shortcut updated.');
        toasts?.show?.('Shortcut updated', 'success');
      } catch (err) {
        capturingKey = null;
        setError((err as Error)?.message || 'Failed to update shortcut.');
        toasts?.show?.((err as Error)?.message || 'Failed to update shortcut.', 'error');
      }
    });
  };

  let clearingCache = $state(false);
  const clearCacheAndReload = async () => {
    clearAlerts();
    if (clearingCache) return;

    clearingCache = true;
    try {
      if ('caches' in window) {
        const names = await caches.keys();
        await Promise.all(names.map((name) => caches.delete(name)));
      }

      if ('serviceWorker' in navigator) {
        const registration = await navigator.serviceWorker.getRegistration();
        if (registration) {
          await registration.update();
        }
      }

      // The HTTP cache + service worker above don't touch IndexedDB, where the
      // mailbox actually lives — so a folder emptied on the server still showed
      // stale rows after "Clear Cache" (only "Complete Reset" fixed it). Drop the
      // mail-view caches so the next load reconciles from the server. Drafts,
      // outbox, settings and the signed-in account are intentionally kept — that
      // is what separates this from a full reset.
      try {
        await Promise.all([
          db.messages.clear(),
          db.messageBodies.clear(),
          db.folders.clear(),
          db.syncManifests?.clear?.(),
          db.searchIndex.clear(),
          db.indexMeta.clear(),
        ]);
      } catch (dbErr) {
        console.warn('[Settings] Failed to clear mail cache tables:', dbErr);
      }

      setSuccess('Cache cleared. Reloading...');
      toasts?.show?.('Cache cleared. Reloading...', 'success');
      setTimeout(() => {
        window.location.reload();
      }, 500);
    } catch (err) {
      setError((err as Error)?.message || 'Failed to clear cache.');
      toasts?.show?.((err as Error)?.message || 'Failed to clear cache.', 'error');
      clearingCache = false;
    }
  };

  const forceResetStorage = async () => {
    clearAlerts();
    const confirmed = window.confirm(
      'Reset the service worker and clear ALL local data (local storage + IndexedDB)? This will log you out and cannot be undone.',
    );
    if (!confirmed || resettingStorage) return;

    resettingStorage = true;
    try {
      try {
        await closeDatabase();
      } catch {
        /* ignore */
      }
      try {
        terminateDbWorker();
      } catch {
        /* ignore */
      }
      await unregisterServiceWorker();
      await forceDeleteAllDatabases();
      // Set a flag for the fallback-recovery.js to delete IndexedDB on next page load
      // (the database may still be blocked by open connections on this page)
      localStorage.setItem('webmail_pending_idb_cleanup', '1');
      // Clear ALL localStorage (not just webmail_ prefixed keys)
      localStorage.clear();
      localStorage.setItem('webmail_pending_idb_cleanup', '1');
      sessionStorage.clear();
      // Also deactivate demo mode (clears fe_demo_mode key)
      deactivateDemoMode();
      setSuccess('Service worker reset and local data cleared. Redirecting to login...');
      toasts?.show?.(
        'Service worker reset and local data cleared. Redirecting to login...',
        'success',
      );
      setTimeout(() => {
        window.location.replace('/');
      }, 1000);
    } catch (err) {
      setError((err as Error)?.message || 'Failed to force reset storage.');
      toasts?.show?.((err as Error)?.message || 'Failed to force reset storage.', 'error');
    } finally {
      resettingStorage = false;
    }
  };

  const sections = [
    { id: 'general', label: 'General' },
    { id: 'appearance', label: 'Appearance' },
    { id: 'privacy', label: 'Privacy & Security' },
    { id: 'folders', label: 'Folders & Labels' },
    { id: 'calendar', label: 'Calendar' },
    { id: 'search', label: 'Search' },
    { id: 'advanced', label: 'Advanced' },
    { id: 'shortcuts', label: 'Keyboard Shortcuts' },
    { id: 'help', label: 'About & Help' },
  ];
</script>

<div
  class="flex h-14 items-center gap-3 px-4"
  style="padding-top: env(safe-area-inset-top, 0px); box-sizing: content-box;"
  data-testid="settings-header"
>
  <Button variant="ghost" size="icon" onclick={() => navigate('/mailbox')} aria-label="Back">
    <ChevronLeft class="h-5 w-5" />
  </Button>
  <div class="flex flex-col">
    <h1 class="text-lg font-semibold">Settings</h1>
    <span class="text-xs text-muted-foreground">{aliasEmail}</span>
  </div>
</div>

{#if error}
  <Alert.Root variant="destructive" class="mx-4 mt-4">
    <AlertCircle class="h-4 w-4" />
    <Alert.Description>{error}</Alert.Description>
  </Alert.Root>
{/if}
{#if success}
  <Alert.Root
    class="mx-4 mt-4 border-green-500 bg-green-50 text-green-900 dark:bg-green-950 dark:text-green-100"
  >
    <CheckCircle class="h-4 w-4" />
    <Alert.Description>{success}</Alert.Description>
  </Alert.Root>
{/if}

<div
  class="flex min-h-0 flex-col md:flex-row"
  style="height: calc(100dvh - 3.5rem - var(--sai-top));"
>
  <!-- Sidebar -->
  <aside class="hidden w-56 shrink-0 border-r border-border p-4 md:block">
    <nav class="flex flex-col gap-1">
      {#each sections as sec}
        <button
          type="button"
          class="px-3 py-2 text-left text-sm font-medium transition-colors {section === sec.id
            ? 'bg-primary/10 text-primary'
            : 'text-muted-foreground hover:bg-accent hover:text-foreground'}"
          onclick={() => {
            section = sec.id;
            history.replaceState(null, '', `#${sec.id}`);
            if (sec.id === 'folders') loadLabelsList();
            if (sec.id === 'advanced') loadCacheStatistics();
          }}
        >
          {sec.label}
        </button>
      {/each}
    </nav>
  </aside>

  <!-- Mobile Section Selector -->
  <div class="w-full md:hidden">
    <div class="border-b border-border p-4">
      <Select.Root
        type="single"
        value={section}
        onValueChange={(v) => {
          section = v;
          history.replaceState(null, '', `#${v}`);
          if (v === 'folders') loadLabelsList();
          if (v === 'advanced') loadCacheStatistics();
        }}
      >
        <Select.Trigger class="w-full">
          {sections.find((s) => s.id === section)?.label || 'Select section'}
        </Select.Trigger>
        <Select.Content>
          {#each sections.filter((s) => s.id !== 'shortcuts') as sec}
            <Select.Item value={sec.id}>{sec.label}</Select.Item>
          {/each}
        </Select.Content>
      </Select.Root>
    </div>
  </div>

  <!-- Content -->
  <div class="fe-mobile-page-scroll flex-1 overflow-y-auto p-4 md:p-6">
    <div class="mx-auto max-w-4xl space-y-6">
      {#if section === 'general'}
        <Card.Root>
          <Card.Header>
            <Card.Title class="flex items-center gap-2">
              Your accounts
              <Tooltip.Provider>
                <Tooltip.Root>
                  <Tooltip.Trigger>
                    <Info class="h-4 w-4 text-muted-foreground" />
                  </Tooltip.Trigger>
                  <Tooltip.Content>
                    Accounts are stored on this device only and are not synced.
                  </Tooltip.Content>
                </Tooltip.Root>
              </Tooltip.Provider>
            </Card.Title>
            <Card.Description>Manage linked mailboxes and identities.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-3">
            {#each $accounts as account}
              <div
                class="flex items-center justify-between border border-border p-3 {account.email ===
                $currentAccount
                  ? 'bg-primary/5'
                  : ''}"
              >
                <div class="flex items-center gap-3">
                  <User class="h-5 w-5 text-muted-foreground" />
                  <div>
                    <div class="font-medium">{account.email}</div>
                    <div class="text-xs text-muted-foreground">
                      {account.email === $currentAccount
                        ? 'Active account'
                        : `Added ${new Date(account.addedAt).toLocaleDateString()}`}
                    </div>
                  </div>
                </div>
                {#if account.email === $currentAccount}
                  <Button variant="destructive" size="sm" onclick={signOut}>Sign out</Button>
                {:else}
                  <Button variant="ghost" size="sm" onclick={() => switchAccount(account)}
                    >Switch to</Button
                  >
                {/if}
              </div>
            {/each}
            <Button variant="outline" class="mt-4" onclick={() => navigate?.('/?add_account=true')}>
              <Plus class="mr-2 h-4 w-4" />
              Add account
            </Button>
          </Card.Content>
        </Card.Root>

        {#if isTauriMobile}
          <PushNotificationSettings {toasts} {openExternal} />
        {/if}

        <Card.Root>
          <Card.Header>
            <Card.Title>Notifications</Card.Title>
            <Card.Description>Choose which system notifications this app shows.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox bind:checked={notifyAppUpdates} onCheckedChange={toggleNotifyAppUpdates} />
              <span>Notify when the app updates</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Shows a system notification when a new version of the app is available or installed.
              Turning this off does not affect new mail notifications.
            </p>
          </Card.Content>
        </Card.Root>

        <!-- In-app account deletion (App Store 5.1.1(v) / Google Play). Distinct
             from "Sign out", which only removes the account from this device. -->
        <Card.Root>
          <Card.Header>
            <Card.Title class="flex items-center gap-2">
              <AlertTriangle class="h-5 w-5 text-destructive" />
              Delete account
            </Card.Title>
          </Card.Header>
          <Card.Content>
            <p class="mb-4 text-sm text-muted-foreground">
              Permanently delete your Forward Email account and all of its data. This opens your
              account settings on forwardemail.net to confirm and complete deletion. This is
              different from signing out and cannot be undone.
            </p>
            <Button variant="destructive" onclick={() => openExternal(ACCOUNT_DELETE_URL)}>
              <ExternalLink class="mr-2 h-4 w-4" />
              Delete account
            </Button>
          </Card.Content>
        </Card.Root>

        {#if storageTotalValue > 0}
          <Card.Root>
            <Card.Header>
              <Card.Title>Storage</Card.Title>
            </Card.Header>
            <Card.Content>
              <div class="space-y-2">
                <div class="flex justify-between text-sm">
                  <span>Storage</span>
                  <span>{storagePercentValue()}%</span>
                </div>
                <div class="h-2 w-full overflow-hidden bg-secondary">
                  <div
                    class="h-full bg-primary transition-all"
                    style="width: {storagePercentValue()}%"
                  ></div>
                </div>
                <div class="text-xs text-muted-foreground">
                  {formatStorageValue(storageUsedValue)} of {formatStorageValue(storageTotalValue)}
                </div>
              </div>
              <Button
                variant="outline"
                class="mt-4"
                onclick={() => openExternal('https://forwardemail.net/my-account/billing')}
              >
                Increase storage
              </Button>
            </Card.Content>
          </Card.Root>
        {/if}
      {/if}

      {#if section === 'appearance'}
        <Card.Root>
          <Card.Header>
            <Card.Title>Theme</Card.Title>
          </Card.Header>
          <Card.Content class="space-y-3">
            <div class="flex flex-wrap gap-4">
              <label class="flex items-center gap-2">
                <input
                  type="radio"
                  name="theme"
                  value="system"
                  bind:group={theme}
                  onchange={saveTheme}
                  class="accent-primary"
                />
                <span>Auto (follow system)</span>
              </label>
              <label class="flex items-center gap-2">
                <input
                  type="radio"
                  name="theme"
                  value="light"
                  bind:group={theme}
                  onchange={saveTheme}
                  class="accent-primary"
                />
                <span>Light</span>
              </label>
              <label class="flex items-center gap-2">
                <input
                  type="radio"
                  name="theme"
                  value="dark"
                  bind:group={theme}
                  onchange={saveTheme}
                  class="accent-primary"
                />
                <span>Dark</span>
              </label>
            </div>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Layout</Card.Title>
          </Card.Header>
          <Card.Content class="space-y-3">
            <div class="flex flex-col gap-2">
              <label class="flex items-center gap-2">
                <input
                  type="radio"
                  name="layout"
                  value="full"
                  bind:group={layoutModeChoice}
                  onchange={saveLayoutMode}
                  class="accent-primary"
                />
                <span>Full Screen (Recommended)</span>
              </label>
              <label class="flex items-center gap-2">
                <input
                  type="radio"
                  name="layout"
                  value="classic"
                  bind:group={layoutModeChoice}
                  onchange={saveLayoutMode}
                  class="accent-primary"
                />
                <span>Classic View (vertical split)</span>
              </label>
            </div>
            <p class="text-sm text-muted-foreground">
              Full Screen: compact layout with full-screen reader; Classic: side-by-side message
              list and reader.
            </p>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Composer defaults</Card.Title>
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={composePlainDefault}
                onCheckedChange={saveComposePlainDefault}
              />
              <span>Use plain text by default</span>
            </label>
            <p class="text-sm text-muted-foreground">
              We'll remember this for new messages. You can still switch formats while composing.
            </p>
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={attachmentReminderEnabled}
                onCheckedChange={saveAttachmentReminder}
              />
              <span>Remind me about attachments</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Get a reminder if you mention attachments but forget to add them.
            </p>
            <label class="flex items-center gap-3">
              <Checkbox bind:checked={defaultReplyAll} onCheckedChange={saveDefaultReplyAll} />
              <span>Reply All by default</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Use Reply All as the default reply action instead of Reply.
            </p>
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={sendAndArchiveDefault}
                onCheckedChange={saveSendAndArchiveDefault}
              />
              <span>Send & Archive by default</span>
            </label>
            <p class="text-sm text-muted-foreground">
              When replying, the primary send button will archive the conversation after sending.
            </p>
            <div class="space-y-2">
              <Label for="undo-send-select">Undo send</Label>
              <Select.Root
                type="single"
                bind:value={undoSendDelay}
                onValueChange={saveUndoSendDelay}
              >
                <Select.Trigger id="undo-send-select" class="w-full">
                  {undoSendDelay === '0' ? 'Off' : `${Number(undoSendDelay) / 1000} seconds`}
                </Select.Trigger>
                <Select.Content>
                  <Select.Item value="0">Off</Select.Item>
                  <Select.Item value="5000">5 seconds</Select.Item>
                  <Select.Item value="10000">10 seconds</Select.Item>
                  <Select.Item value="30000">30 seconds</Select.Item>
                </Select.Content>
              </Select.Root>
              <p class="text-sm text-muted-foreground">
                Hold sent messages briefly so you can undo. Longer delays give more time to cancel.
              </p>
            </div>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Signature</Card.Title>
            <Card.Description>
              Added to new messages, replies, and forwards. Plain text; stored on this device.
            </Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox bind:checked={signatureEnabled} onCheckedChange={saveSignatureEnabled} />
              <span>Include my signature</span>
            </label>
            <div class="space-y-2">
              <Label for="signature-textarea">Signature</Label>
              <Textarea
                id="signature-textarea"
                bind:value={signatureText}
                onchange={saveSignatureText}
                placeholder={'Jane Doe\nForward Email'}
                class="min-h-[120px] font-mono text-sm"
              />
            </div>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Font</Card.Title>
            <Card.Description
              >Choose a custom font for better readability. System default is fastest.</Card.Description
            >
          </Card.Header>
          <Card.Content class="space-y-4">
            <div class="space-y-2">
              <Label for="font-select">Typeface</Label>
              <Select.Root type="single" bind:value={fontChoice} onValueChange={() => saveFont()}>
                <Select.Trigger class="w-full" style="font-family: {currentFontFamily}">
                  {availableFonts.find((f) => f.key === fontChoice)?.name || 'System Default'}
                </Select.Trigger>
                <Select.Content>
                  {#each availableFonts as font}
                    <Select.Item value={font.key}>{font.name}</Select.Item>
                  {/each}
                </Select.Content>
              </Select.Root>
            </div>
            {#if fontLoading}
              <p class="text-sm text-primary">Loading font...</p>
            {/if}
            {#if fontChoice !== 'system'}
              <div class="border border-border p-3" style="font-family: {currentFontFamily}">
                The quick brown fox jumps over the lazy dog.
                <strong>Bold text</strong> and regular text for email reading.
                <br />
                <span class="text-sm text-muted-foreground"
                  >Numbers: 0123456789 | Symbols: @#$%&*</span
                >
              </div>
            {/if}
          </Card.Content>
        </Card.Root>

        {#if supportsBrowserOverrideSetting}
          <Card.Root>
            <Card.Header>
              <Card.Title>External Links</Card.Title>
              <Card.Description>
                On Windows, you can choose a browser command for web links opened from the desktop
                app. Leave this blank to use the Windows default browser.
              </Card.Description>
            </Card.Header>
            <Card.Content class="space-y-4">
              <div class="space-y-2">
                <Label for="external-browser-override">Preferred browser command</Label>
                <Input
                  id="external-browser-override"
                  bind:value={externalBrowserOverride}
                  placeholder="Examples: firefox, chrome, msedge, or C:\\Program Files\\Mozilla Firefox\\firefox.exe"
                  autocomplete="off"
                  autocapitalize="off"
                  spellcheck={false}
                  onblur={() => saveExternalBrowserOverride()}
                />
              </div>
              <p class="text-sm text-muted-foreground">
                If the chosen browser cannot be launched, Forward Email falls back to the system
                default browser so links still open.
              </p>
              <div class="flex flex-wrap gap-2">
                <Button type="button" onclick={() => saveExternalBrowserOverride()}>Save</Button>
                <Button
                  type="button"
                  variant="outline"
                  onclick={() => clearExternalBrowserOverride()}
                  disabled={!externalBrowserOverride.trim()}
                >
                  Clear override
                </Button>
              </div>
            </Card.Content>
          </Card.Root>
        {/if}
      {/if}

      {#if section === 'privacy'}
        <!-- Default Email App (mailto handler) -->
        <MailtoSettings />

        <!-- App Lock section - above PGP encryption -->
        <AppLockSettings />

        <Card.Root>
          <Card.Header>
            <Card.Title>PGP encryption</Card.Title>
            {#if aliasEmail}
              <Card.Description>Signed in as <strong>{aliasEmail}</strong></Card.Description>
            {/if}
          </Card.Header>
          <Card.Content class="space-y-4">
            <div class="flex items-center justify-between">
              <span class="text-sm text-muted-foreground">PGP encryption keys</span>
              <Button variant="ghost" size="sm" onclick={openNewKey}>Add key</Button>
            </div>
            <div class="space-y-2">
              {#if pgpKeys.length === 0}
                <p class="text-sm text-muted-foreground">No keys added yet.</p>
              {/if}
              {#each pgpKeys as key, index (index)}
                <div class="flex items-center justify-between border border-border p-2">
                  <span class="font-medium">{key.name || 'Key'}</span>
                  <div class="flex gap-1">
                    <Button
                      variant="ghost"
                      size="icon"
                      onclick={() => editKey(key)}
                      aria-label="Edit"
                    >
                      <Pencil class="h-4 w-4" />
                    </Button>
                    <Button
                      variant="ghost"
                      size="icon"
                      onclick={() => removeKey(key)}
                      aria-label="Remove"
                    >
                      <X class="h-4 w-4" />
                    </Button>
                  </div>
                </div>
              {/each}
            </div>
            {#if keyFormVisible}
              <div class="space-y-3 border border-border p-4">
                <Input
                  type="text"
                  placeholder="Key name (e.g., Personal)"
                  bind:value={editingKeyName}
                />
                <Textarea
                  rows={6}
                  placeholder="PGP private key (ASCII armor)"
                  bind:value={editingKeyValue}
                />
                <Input
                  type="password"
                  placeholder="Private key passphrase (optional)"
                  bind:value={editingKeyPassphrase}
                />
                <div class="flex gap-2">
                  <Button variant="ghost" onclick={cancelKeyForm}>Cancel</Button>
                  <Button onclick={saveKey} disabled={savingKey}>
                    {savingKey ? 'Saving…' : 'Save key'}
                  </Button>
                </div>
                <p class="text-xs text-muted-foreground">
                  Stored locally only. If your private key is passphrase-protected, enter that
                  passphrase here so encrypted messages can be unlocked immediately after you save
                  the key.
                </p>
              </div>
            {/if}
            <Button variant="ghost" onclick={clearData}>Clear saved data</Button>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Privacy</Card.Title>
            <Card.Description>Control how external content is handled in emails.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={blockRemoteImages}
                onCheckedChange={toggleBlockRemoteImages}
              />
              <span>Block all external images by default</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Blocks all external images from loading. You can still load images for individual
              emails.
            </p>
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={blockTrackingPixels}
                onCheckedChange={toggleBlockTrackingPixels}
              />
              <span>Block tracking pixels</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Blocks tiny invisible images used to track email opens.
            </p>
            <label class="flex items-center gap-3">
              <Checkbox bind:checked={viewPlainText} onCheckedChange={toggleViewPlainText} />
              <span>View emails as plain text</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Strip HTML, CSS, and remote content from incoming emails and render them as plain
              text. Links and image alt text are preserved.
            </p>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Spam reporting</Card.Title>
            <Card.Description>Where the Report spam action sends reported emails.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <div class="flex gap-2">
              <Input
                type="email"
                placeholder={DEFAULT_SPAM_REPORT_ADDRESS}
                bind:value={spamReportAddress}
              />
              <Button variant="ghost" onclick={saveSpamReportAddress}>Save</Button>
              {#if spamReportAddress.trim() !== DEFAULT_SPAM_REPORT_ADDRESS}
                <Button variant="ghost" onclick={resetSpamReportAddress}>Reset</Button>
              {/if}
            </div>
            <p class="text-sm text-muted-foreground">
              Choosing <strong>Report spam</strong> on a message forwards the original email
              (including full headers) as an attachment to this address, then deletes the message
              from your mailbox. Reports go to {DEFAULT_SPAM_REPORT_ADDRESS} by default, which helps Forward
              Email improve spam detection for everyone. No copy is kept in your Sent folder.
            </p>
          </Card.Content>
        </Card.Root>
      {/if}

      {#if section === 'folders'}
        <Card.Root>
          <Card.Header>
            <Card.Title>Special Folders</Card.Title>
            <Card.Description
              >Configure which folders to use for archiving, sending, drafting, trashing, and junk
              messages.</Card.Description
            >
          </Card.Header>
          <Card.Content class="space-y-4">
            <div class="space-y-2">
              <Label for="archive-folder">Archive folder</Label>
              <Select.Root
                type="single"
                bind:value={archiveFolder}
                onValueChange={() => saveArchiveFolder()}
              >
                <Select.Trigger class="w-full">
                  {archiveFolder || 'Auto-detect (Archive)'}
                </Select.Trigger>
                <Select.Content>
                  <Select.Item value="">Auto-detect (Archive)</Select.Item>
                  {#each availableFolders as folder}
                    <Select.Item value={folder}>{folder}</Select.Item>
                  {/each}
                </Select.Content>
              </Select.Root>
            </div>
            <div class="space-y-2">
              <Label for="sent-folder">Sent folder</Label>
              <Select.Root
                type="single"
                bind:value={sentFolder}
                onValueChange={() => saveSentFolder()}
              >
                <Select.Trigger class="w-full">
                  {sentFolder || 'Auto-detect (Sent)'}
                </Select.Trigger>
                <Select.Content>
                  <Select.Item value="">Auto-detect (Sent)</Select.Item>
                  {#each availableFolders as folder}
                    <Select.Item value={folder}>{folder}</Select.Item>
                  {/each}
                </Select.Content>
              </Select.Root>
            </div>
            <div class="space-y-2">
              <Label for="drafts-folder">Drafts folder</Label>
              <Select.Root
                type="single"
                bind:value={draftsFolder}
                onValueChange={() => saveDraftsFolder()}
              >
                <Select.Trigger class="w-full">
                  {draftsFolder || 'Auto-detect (Drafts)'}
                </Select.Trigger>
                <Select.Content>
                  <Select.Item value="">Auto-detect (Drafts)</Select.Item>
                  {#each availableFolders as folder}
                    <Select.Item value={folder}>{folder}</Select.Item>
                  {/each}
                </Select.Content>
              </Select.Root>
            </div>
            <div class="space-y-2">
              <Label for="trash-folder">Trash folder</Label>
              <Select.Root
                type="single"
                bind:value={trashFolder}
                onValueChange={() => saveTrashFolder()}
              >
                <Select.Trigger class="w-full">
                  {trashFolder || 'Auto-detect (Trash)'}
                </Select.Trigger>
                <Select.Content>
                  <Select.Item value="">Auto-detect (Trash)</Select.Item>
                  {#each availableFolders as folder}
                    <Select.Item value={folder}>{folder}</Select.Item>
                  {/each}
                </Select.Content>
              </Select.Root>
            </div>
            <div class="space-y-2">
              <Label for="junk-folder">Junk folder</Label>
              <Select.Root
                type="single"
                bind:value={junkFolder}
                onValueChange={() => saveJunkFolder()}
              >
                <Select.Trigger class="w-full">
                  {junkFolder || 'Auto-detect (Junk)'}
                </Select.Trigger>
                <Select.Content>
                  <Select.Item value="">Auto-detect (Junk)</Select.Item>
                  {#each availableFolders as folder}
                    <Select.Item value={folder}>{folder}</Select.Item>
                  {/each}
                </Select.Content>
              </Select.Root>
            </div>
            <p class="text-sm text-muted-foreground">
              Leave as "Auto-detect" to automatically find folders by standard names.
            </p>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Your folders</Card.Title>
            <Card.Description>
              Delete custom folders. System folders (Inbox, Sent, Drafts, Trash, and the like) can't
              be removed.
            </Card.Description>
          </Card.Header>
          <Card.Content class="space-y-2">
            {#if deletableFolders.length}
              {#each deletableFolders as folder (folder.path)}
                <div class="flex items-center justify-between border border-border p-2">
                  <div class="min-w-0">
                    <div class="truncate font-medium">{folder.label}</div>
                    {#if folder.count != null}
                      <div class="text-xs text-muted-foreground">
                        {folder.count}
                        {folder.count === 1 ? 'message' : 'messages'}
                      </div>
                    {/if}
                  </div>
                  <Button
                    variant="ghost"
                    size="icon"
                    onclick={() => deleteFolderFromSettings(folder)}
                    disabled={folderDeleting === folder.path}
                    aria-label={`Delete folder ${folder.label}`}
                  >
                    <X class="h-4 w-4" />
                  </Button>
                </div>
              {/each}
            {:else}
              <p class="text-sm text-muted-foreground">No custom folders.</p>
            {/if}
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Labels</Card.Title>
            <Card.Description>Manage labels for this account.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <div class="flex items-center justify-between">
              <span class="text-sm text-muted-foreground">Your labels</span>
              <Button variant="ghost" size="sm" onclick={openCreateLabelModal}>Add label</Button>
            </div>
            <div class="space-y-2">
              {#if labelsLoading}
                <p class="text-sm text-muted-foreground">Loading labels...</p>
              {:else if labelsList && labelsList.length}
                {#each labelsList as label (getLabelKey(label))}
                  <div class="flex items-center justify-between border border-border p-2">
                    <div class="flex items-center gap-3">
                      <div class="h-4 w-4" style="background: {label.color || '#9ca3af'}"></div>
                      <div>
                        <div class="font-medium">{label.name || getLabelKey(label)}</div>
                        <div class="text-xs text-muted-foreground">
                          Keyword: <code class="bg-muted px-1">{getLabelKey(label)}</code>
                        </div>
                      </div>
                    </div>
                    <div class="flex gap-1">
                      <Button
                        variant="ghost"
                        size="icon"
                        onclick={() => openEditLabelModal(label)}
                        aria-label="Edit"
                      >
                        <Pencil class="h-4 w-4" />
                      </Button>
                      <Button
                        variant="ghost"
                        size="icon"
                        onclick={() => deleteLabel(label)}
                        disabled={labelsDeleting === getLabelKey(label)}
                        aria-label="Remove"
                      >
                        <X class="h-4 w-4" />
                      </Button>
                    </div>
                  </div>
                {/each}
              {:else}
                <p class="text-sm text-muted-foreground">No labels yet.</p>
              {/if}
            </div>
          </Card.Content>
        </Card.Root>
      {/if}

      {#if section === 'calendar'}
        <Card.Root>
          <Card.Header>
            <Card.Title>Week</Card.Title>
            <Card.Description>Choose which day the calendar week begins on.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={startWeekOnSundayValue}
                onCheckedChange={toggleStartWeekOnSunday}
              />
              <span>Start week on Sunday</span>
            </label>
            <p class="text-sm text-muted-foreground">
              When enabled, week and month views begin on Sunday. Otherwise they begin on Monday.
            </p>
          </Card.Content>
        </Card.Root>
        <Card.Root>
          <Card.Header>
            <Card.Title>Tasks</Card.Title>
            <Card.Description>Control how tasks appear in your calendar.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox
                bind:checked={hideCompletedTodosValue}
                onCheckedChange={toggleHideCompletedTodos}
              />
              <span>Hide completed todos</span>
            </label>
            <p class="text-sm text-muted-foreground">
              Completed tasks will be hidden from the calendar view. You can still find them via the
              calendar event details.
            </p>
          </Card.Content>
        </Card.Root>
      {/if}

      {#if section === 'search'}
        <Card.Root>
          <Card.Header>
            <Card.Title>Search Indexing</Card.Title>
            <Card.Description
              >Control how your mailbox content is indexed for search.</Card.Description
            >
          </Card.Header>
          <Card.Content class="space-y-4">
            <label class="flex items-center gap-3">
              <Checkbox checked={bodyIndexingLocal} onCheckedChange={toggleBodyIndexingOption} />
              <span>Index message bodies</span>
            </label>
            <p class="text-sm text-muted-foreground">
              When enabled, message content is indexed for full-text search.
            </p>
            <div class="bg-muted p-3 text-sm">
              <div>Indexed messages: {indexCountValue || 0}</div>
              <div>Index size: {Math.round(indexSizeValue / 1024 / 1024) || 0} MB</div>
            </div>
            <div class="flex gap-2">
              <Button variant="outline" onclick={openRebuildConfirm} disabled={rebuildingIndex}>
                Rebuild search index
              </Button>
              <Button variant="ghost" onclick={resetSectionChanges}>Reset</Button>
            </div>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Index health</Card.Title>
            <Card.Description>Monitor the health of your search index.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <div
              class="p-3 {searchHealth.healthy
                ? 'bg-green-50 dark:bg-green-950'
                : 'bg-yellow-50 dark:bg-yellow-950'}"
            >
              <div
                class="flex items-center gap-2 font-semibold {searchHealth.healthy
                  ? 'text-green-600'
                  : 'text-yellow-600'}"
              >
                {#if searchHealth.healthy}
                  <CheckCircle class="h-4 w-4" />
                  Healthy
                {:else}
                  <AlertTriangle class="h-4 w-4" />
                  {searchHealth.needsRebuild ? 'Needs Rebuild' : 'Partial'}
                {/if}
              </div>
              <div class="mt-2 text-sm text-muted-foreground">
                <div>Messages in cache: {searchHealth.messagesCount || 0}</div>
                <div>Messages indexed: {searchHealth.indexCount || 0}</div>
                {#if searchHealth.divergence > 0}
                  <div>Missing from index: {searchHealth.divergence}</div>
                {/if}
                {#if lastHealthCheck}
                  <div>Last checked: {lastHealthCheck.toLocaleTimeString()}</div>
                {/if}
              </div>
            </div>
            <div class="flex gap-2">
              <Button variant="outline" onclick={runHealthCheck} disabled={checkingHealth}>
                {checkingHealth ? 'Checking...' : 'Check health'}
              </Button>
              {#if searchHealth.needsRebuild || searchHealth.divergence > 0}
                <Button
                  variant="outline"
                  onclick={runRebuildFromCache}
                  disabled={rebuildingFromCache}
                >
                  {rebuildingFromCache ? 'Rebuilding...' : 'Rebuild from cache'}
                </Button>
              {/if}
            </div>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Saved searches</Card.Title>
            <Card.Description>Quickly re-run your frequent queries.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            <div class="space-y-3">
              <div class="space-y-2">
                <Label for="search-name">Name</Label>
                <Input
                  id="search-name"
                  placeholder="e.g. Invoices with attachments"
                  bind:value={newSavedSearchName}
                />
              </div>
              <div class="space-y-2">
                <Label for="search-query">Query</Label>
                <Input
                  id="search-query"
                  placeholder="e.g. subject:invoice has:attachment"
                  bind:value={newSavedSearchQuery}
                />
              </div>
              <div class="flex gap-2">
                <Button variant="outline" onclick={saveSavedSearch} disabled={savingSearch}>
                  {savingSearch ? 'Saving...' : 'Save search'}
                </Button>
                <Button
                  variant="ghost"
                  onclick={() => {
                    newSavedSearchName = '';
                    newSavedSearchQuery = '';
                  }}>Clear</Button
                >
              </div>
            </div>
            {#if savedSearches && savedSearches.length}
              <div class="space-y-2">
                {#each savedSearches as saved}
                  <div class="flex items-center justify-between border border-border p-2">
                    <div>
                      <div class="font-medium">{saved.name}</div>
                      <div class="text-xs text-muted-foreground">{saved.query}</div>
                    </div>
                    <Button
                      variant="ghost"
                      size="sm"
                      onclick={() => deleteSavedSearch(saved.name)}
                      disabled={deletingSearch === saved.name}
                    >
                      {deletingSearch === saved.name ? '...' : 'Delete'}
                    </Button>
                  </div>
                {/each}
              </div>
            {:else}
              <p class="text-sm text-muted-foreground">No saved searches yet.</p>
            {/if}
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <div class="flex items-center justify-between">
              <Card.Title>Search operators</Card.Title>
              <Button variant="ghost" size="sm" onclick={() => (showOperators = !showOperators)}>
                {showOperators ? 'Hide' : 'Show'}
              </Button>
            </div>
            <Card.Description>Quick reference for advanced search syntax.</Card.Description>
          </Card.Header>
          {#if showOperators}
            <Card.Content>
              <div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
                {#each operatorHelp as op}
                  <div class="border border-border bg-muted/50 p-2">
                    <code class="rounded bg-muted px-1 text-sm">{op.label}</code>
                    <div class="mt-1 text-xs text-muted-foreground">{op.note}</div>
                  </div>
                {/each}
              </div>
            </Card.Content>
          {/if}
        </Card.Root>
      {/if}

      {#if section === 'advanced'}
        <Card.Root>
          <Card.Header>
            <Card.Title>Database Information</Card.Title>
            <Card.Description>View technical details about your local database.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-2 text-sm">
            <div><strong>Database Version:</strong> {databaseVersion || '—'}</div>
            <div><strong>Total Records:</strong> {databaseRecordCount || 0}</div>
            <div><strong>Queued Actions:</strong> {syncPendingValue || 0}</div>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Mailbox Storage</Card.Title>
            <Card.Description>Server-side storage used by this alias.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            {#if storageTotalValue > 0}
              <div class="space-y-2 text-sm">
                <div>
                  <strong>Used:</strong>
                  {formatStorageValue(storageUsedValue)} / {formatStorageValue(storageTotalValue)} ({storagePercentValue()}%)
                </div>
                <div>
                  <strong>Available:</strong>
                  {formatStorageValue(storageTotalValue - storageUsedValue)}
                </div>
              </div>
              <div class="h-2 w-full overflow-hidden bg-secondary">
                <div
                  class="h-full bg-primary transition-all"
                  style="width: {storagePercentValue()}%"
                ></div>
              </div>
              {#if storagePercentValue() > 90}
                <Alert.Root variant="destructive">
                  <AlertTriangle class="h-4 w-4" />
                  <Alert.Description
                    >Mailbox storage almost full! Consider upgrading your plan.</Alert.Description
                  >
                </Alert.Root>
              {/if}
            {:else}
              <p class="text-sm text-muted-foreground">Storage information unavailable</p>
            {/if}
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Local Cache</Card.Title>
            <Card.Description>Data cached on this device for offline access.</Card.Description>
          </Card.Header>
          <Card.Content class="space-y-4">
            {#if loadingCacheStats}
              <p class="text-sm text-muted-foreground">Loading statistics...</p>
            {:else if storageInfo}
              <div class="space-y-2 text-sm">
                <div>
                  <strong>Cache Usage:</strong>
                  {storageInfo.usageFormatted || '—'} / {storageInfo.quotaFormatted || '—'} ({(
                    storageInfo.percentage as number
                  )?.toFixed(1) || 0}%)
                </div>
              </div>
              <div class="h-2 w-full overflow-hidden bg-secondary">
                <div
                  class="h-full bg-primary transition-all"
                  style="width: {(storageInfo.percentage as number) || 0}%"
                ></div>
              </div>
            {:else}
              <p class="text-sm text-muted-foreground">Unable to load cache information</p>
            {/if}
            <Button variant="outline" onclick={loadCacheStatistics} disabled={loadingCacheStats}>
              {loadingCacheStats ? 'Loading...' : 'Refresh Statistics'}
            </Button>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Performance Tracing</Card.Title>
            <Card.Description>Enable detailed performance logging for debugging.</Card.Description>
          </Card.Header>
          <Card.Content>
            <label class="flex items-center gap-3">
              <Checkbox checked={debugPerfEnabled} onCheckedChange={togglePerfLogging} />
              <span>Enable performance logs</span>
            </label>
            <p class="mt-2 text-sm text-muted-foreground">
              When enabled, console shows [perf] entries for request and message load stages.
            </p>
          </Card.Content>
        </Card.Root>

        <Card.Root class="border-destructive">
          <Card.Header>
            <Card.Title class="text-destructive">Danger Zone</Card.Title>
            <Card.Description
              >Use these options if you're experiencing issues with the app.</Card.Description
            >
          </Card.Header>
          <Card.Content class="space-y-4">
            <div>
              <Button variant="outline" onclick={clearCacheAndReload} disabled={clearingCache}>
                {clearingCache ? 'Clearing...' : 'Clear Cache & Reload'}
              </Button>
              <p class="mt-1 text-xs text-muted-foreground">
                Clears cached app files and reloads. You'll stay logged in.
              </p>
            </div>
            <div>
              <Button variant="destructive" onclick={forceResetStorage} disabled={resettingStorage}>
                {resettingStorage ? 'Resetting...' : 'Complete Reset'}
              </Button>
              <p class="mt-1 text-xs text-muted-foreground">
                <strong>WARNING:</strong> Clears all local data and logs you out.
              </p>
            </div>
          </Card.Content>
        </Card.Root>
      {/if}

      {#if section === 'shortcuts'}
        <Card.Root class="hidden md:block">
          <Card.Header class="flex flex-row items-start justify-between gap-4">
            <div>
              <Card.Title>Keyboard shortcuts</Card.Title>
              <Card.Description
                >Customize or review the shortcuts available throughout the app.</Card.Description
              >
            </div>
            <Button variant="outline" size="sm" onclick={resetShortcuts}>Reset to defaults</Button>
          </Card.Header>
          <Card.Content>
            <div class="max-h-[600px] overflow-y-auto border border-border">
              <div
                class="sticky top-0 grid grid-cols-[1fr_auto] gap-4 border-b border-border bg-muted p-3 font-semibold"
              >
                <span>Action</span>
                <span>Shortcut</span>
              </div>
              {#each shortcutsList as shortcut}
                <div
                  class="grid grid-cols-[1fr_auto] items-center gap-4 border-b border-border p-3 last:border-b-0"
                >
                  <span>{shortcut.label}</span>
                  {#if capturingKey === shortcut.originalKey}
                    <div class="flex items-center gap-2">
                      <code class="animate-pulse bg-muted px-2 py-1 text-sm"
                        >Press a key… (Esc to cancel)</code
                      >
                      <Button variant="ghost" size="sm" onclick={cancelRebind}>Cancel</Button>
                    </div>
                  {:else}
                    <div class="flex items-center gap-2">
                      <code class="bg-muted px-2 py-1 text-sm">
                        {formatKey(shortcut.key || shortcut.keys || shortcut.originalKey) || '—'}
                      </code>
                      <Button
                        variant="ghost"
                        size="sm"
                        disabled={capturingKey !== null}
                        onclick={() => startRebind(shortcut)}>Edit</Button
                      >
                    </div>
                  {/if}
                </div>
              {/each}
            </div>
          </Card.Content>
        </Card.Root>
      {/if}

      {#if section === 'help'}
        <Card.Root>
          <Card.Header>
            <Card.Title>{isTauriDesktop ? 'Desktop App' : 'Download Desktop App'}</Card.Title>
            <Card.Description>
              {#if isTauriDesktop}
                You're running the native desktop app. Other platforms and the latest installers are
                available below.
              {:else}
                Install the native desktop app for faster startup, system notifications, and offline
                support. After installing, the app updates itself automatically.
              {/if}
            </Card.Description>
          </Card.Header>
          <Card.Content class="space-y-3">
            {#if desktopLoading}
              <div class="text-sm text-muted-foreground">Loading latest release…</div>
            {:else if desktopError || !desktopRelease}
              <div class="space-y-2 text-sm">
                <div class="text-muted-foreground">Couldn't auto-detect the latest release.</div>
                <Button
                  variant="outline"
                  href={DESKTOP_RELEASES_URL}
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  <ExternalLink class="mr-2 h-4 w-4" />
                  View all downloads on GitHub
                </Button>
              </div>
            {:else}
              {#if desktopPrimaryAsset}
                <Button
                  href={desktopPrimaryAsset.url}
                  download={desktopPrimaryAsset.name}
                  target="_blank"
                  rel="noopener noreferrer"
                >
                  <Download class="mr-2 h-4 w-4" />
                  Download for {desktopPlatform?.label ?? 'your platform'}
                </Button>
                <div class="text-xs text-muted-foreground">
                  Version {desktopRelease.version}
                  {#if desktopPrimaryAsset.size}
                    · {formatAssetSize(desktopPrimaryAsset.size)}
                  {/if}
                  · {desktopPrimaryAsset.format.toUpperCase()}
                </div>
              {:else}
                <div class="text-sm text-muted-foreground">
                  We couldn't pick an installer for your platform automatically. Use the options
                  below.
                </div>
              {/if}

              {#if desktopPlatform?.os === 'mac'}
                <p class="text-xs text-muted-foreground">
                  Open the DMG and drag Forward Email to your Applications folder.
                </p>
              {:else if desktopPlatform?.os === 'windows'}
                <p class="text-xs text-muted-foreground">
                  Run the installer and follow the prompts. Windows may ask you to confirm.
                </p>
              {:else if desktopPlatform?.os === 'linux'}
                <p class="text-xs text-muted-foreground">
                  Make the AppImage executable (<code>chmod +x</code>) and run it, or install the
                  <code>.deb</code>
                  / <code>.rpm</code> with your package manager.
                </p>
              {/if}

              <button
                type="button"
                class="flex items-center gap-1 text-xs text-sky-500 hover:text-sky-400 hover:underline"
                onclick={() => (otherPlatformsOpen = !otherPlatformsOpen)}
                aria-expanded={otherPlatformsOpen}
              >
                <ChevronDown
                  class="h-3 w-3 transition-transform {otherPlatformsOpen ? 'rotate-180' : ''}"
                />
                {otherPlatformsOpen ? 'Hide other platforms' : 'Other platforms'}
              </button>

              {#if otherPlatformsOpen}
                <div class="space-y-3 border-t border-border pt-3">
                  {#each [{ key: 'mac', label: 'macOS' }, { key: 'windows', label: 'Windows' }, { key: 'linux', label: 'Linux' }] as group (group.key)}
                    {@const entries = desktopAssetGroups[group.key as 'mac' | 'windows' | 'linux']}
                    {#if entries.length > 0}
                      <div>
                        <div class="mb-1 text-xs font-semibold uppercase text-muted-foreground">
                          {group.label}
                        </div>
                        <ul class="space-y-1 text-sm">
                          {#each entries as asset (asset.name)}
                            <li>
                              <a
                                href={asset.url}
                                download={asset.name}
                                target="_blank"
                                rel="noopener noreferrer"
                                class="text-sky-500 hover:text-sky-400 hover:underline"
                              >
                                {asset.name}
                              </a>
                              {#if asset.size}
                                <span class="text-xs text-muted-foreground">
                                  ({formatAssetSize(asset.size)})
                                </span>
                              {/if}
                            </li>
                          {/each}
                        </ul>
                      </div>
                    {/if}
                  {/each}
                  <div class="pt-1 text-xs">
                    <a
                      href={desktopRelease.htmlUrl || DESKTOP_RELEASES_URL}
                      target="_blank"
                      rel="noopener noreferrer"
                      class="text-sky-500 hover:text-sky-400 hover:underline"
                    >
                      View full release notes on GitHub →
                    </a>
                  </div>
                </div>
              {/if}
            {/if}
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Send Feedback</Card.Title>
            <Card.Description>Report bugs, request features, or ask questions.</Card.Description>
          </Card.Header>
          <Card.Content>
            <Button onclick={() => (feedbackModalOpen = true)}>
              <MessageSquare class="mr-2 h-4 w-4" />
              Send Feedback
            </Button>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Documentation & Resources</Card.Title>
            <Card.Description
              >Learn more about features and how to use the webmail client.</Card.Description
            >
          </Card.Header>
          <Card.Content>
            <ul class="list-inside list-disc space-y-1 text-sm">
              <li>
                <a
                  href="https://forwardemail.net/faq"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">FAQ</a
                >
              </li>
              <li>
                <a
                  href="https://forwardemail.net/guides"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">Guides</a
                >
              </li>
              <li>
                <a
                  href="https://forwardemail.net/help"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">Help & Support</a
                >
              </li>
              <li>
                <a
                  href="https://forwardemail.net/email-api"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">Email API Documentation</a
                >
              </li>
              <li>
                <a
                  href="https://forwardemail.net/privacy"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">Privacy Policy</a
                >
              </li>
            </ul>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Community & Contact</Card.Title>
            <Card.Description>Connect with us and the community.</Card.Description>
          </Card.Header>
          <Card.Content>
            <ul class="list-inside list-disc space-y-1 text-sm">
              <li>
                <a
                  href="https://github.com/forwardemail"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">GitHub</a
                >
              </li>
              <li>
                <a
                  href="https://matrix.to/#/#forward-email:matrix.org"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">Matrix Chat</a
                >
              </li>
              <li>
                <a
                  href="https://x.com/fwdemail"
                  target="_blank"
                  rel="noopener noreferrer"
                  class="text-sky-500 hover:text-sky-400 hover:underline">X (Twitter)</a
                >
              </li>
            </ul>
          </Card.Content>
        </Card.Root>

        <Card.Root>
          <Card.Header>
            <Card.Title>Version Information</Card.Title>
          </Card.Header>
          <Card.Content class="text-sm space-y-2">
            <div><strong>App Version:</strong> {import.meta.env.VITE_PKG_VERSION || '0.0.0'}</div>
            <div><strong>Database Schema:</strong> v{databaseVersion}</div>
            <div class="pt-2">
              <Button
                variant="outline"
                size="sm"
                disabled={checkingForUpdates}
                onclick={handleCheckForUpdates}
              >
                {checkingForUpdates ? 'Checking…' : 'Check for Updates'}
              </Button>
              {#if updateCheckResult}
                <span class="ml-2 text-xs text-muted-foreground">{updateCheckResult}</span>
              {/if}
            </div>
          </Card.Content>
        </Card.Root>
      {/if}
    </div>
  </div>
</div>

<LabelModal
  visible={labelModalVisible}
  mode={labelModalMode}
  keyword={labelModalKeyword}
  bind:name={labelModalName}
  bind:color={labelModalColor}
  palette={labelPalette}
  error={labelModalError}
  saving={labelModalSaving}
  onClose={closeLabelModal}
  onSave={saveLabelModal}
  onClearError={clearLabelModalError}
/>

<Dialog.Root bind:open={rebuildConfirmVisible}>
  <Dialog.Content class="sm:max-w-md">
    <Dialog.Header>
      <Dialog.Title>Rebuild index?</Dialog.Title>
      <Dialog.Description>
        This can take several minutes depending on mailbox size.
      </Dialog.Description>
    </Dialog.Header>
    <Dialog.Footer>
      <Button variant="ghost" onclick={closeRebuildConfirm}>Cancel</Button>
      <Button onclick={confirmRebuildIndex} disabled={rebuildingIndex}>Rebuild</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog.Root>

{#if feedbackModalOpen}
  <FeedbackModal onClose={() => (feedbackModalOpen = false)} />
{/if}
