/* Main app — sidebar nav, view router, tweaks panel wiring */

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "clay",
  "density": "cozy",
  "sidebar": "expanded",
  "kpiLayout": "hero",
  "displayFont": "Geist"
}/*EDITMODE-END*/;

const THEME_PALETTES = {
  clay:     { name: "Warm Clay",  hue: "oklch(0.64 0.16 35)",   sub: "Terracotta · default" },
  forest:   { name: "Forest",     hue: "oklch(0.55 0.13 155)",  sub: "Sage green" },
  indigo:   { name: "Indigo",     hue: "oklch(0.55 0.16 270)",  sub: "Cool blue" },
  plum:     { name: "Plum",       hue: "oklch(0.55 0.16 340)",  sub: "Soft magenta" },
  sunshine: { name: "Sunshine",   hue: "oklch(0.7 0.16 70)",    sub: "Warm amber" },
};

const LoginScreen = ({ onLogin, onSignup, error }) => {
  const [mode, setMode] = React.useState("login"); // "login" | "signup"
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const [fullName, setFullName] = React.useState("");
  const [businessName, setBusinessName] = React.useState("");
  const [loading, setLoading] = React.useState(false);

  const switchMode = (m) => { setMode(m); setEmail(""); setPassword(""); setFullName(""); setBusinessName(""); };

  const handleSubmit = async (e) => {
    e.preventDefault();
    setLoading(true);
    if (mode === "login") await onLogin(email, password);
    else await onSignup(email, password, fullName, businessName);
    setLoading(false);
  };

  const inputStyle = { padding:"10px 12px", borderRadius:8, border:"1px solid var(--border,#e0e0e0)", fontSize:14, outline:"none", background:"var(--bg-base,#f5f0eb)", width:"100%", boxSizing:"border-box" };
  const labelStyle = { fontSize:13, fontWeight:500, color:"var(--text-1,#1a1a1a)" };
  const fieldStyle = { display:"flex", flexDirection:"column", gap:6 };

  return (
    <div style={{ display:"flex", alignItems:"center", justifyContent:"center", minHeight:"100vh", background:"var(--bg-base, #f5f0eb)", fontFamily:"Geist, sans-serif" }}>
      <div style={{ background:"var(--card, #fff)", borderRadius:16, padding:"40px 36px", boxShadow:"0 4px 32px rgba(0,0,0,0.10)", width:"100%", maxWidth:420 }}>

        <div style={{ textAlign:"center", marginBottom:28 }}>
          <img src="assets/creatorboard-icon.png" alt="" style={{ width:48, marginBottom:12 }} />
          <div style={{ fontSize:22, fontWeight:700, color:"var(--text-1,#1a1a1a)" }}>CreatorBoard</div>
          <div style={{ fontSize:14, color:"var(--text-2,#888)", marginTop:4 }}>
            {mode === "login" ? "Sign in to your account" : "Create your account"}
          </div>
        </div>

        {/* Toggle tabs */}
        <div style={{ display:"flex", background:"var(--bg-base,#f5f0eb)", borderRadius:10, padding:4, marginBottom:24, gap:4 }}>
          {["login","signup"].map(m => (
            <button key={m} onClick={() => switchMode(m)} style={{
              flex:1, padding:"8px 0", borderRadius:7, border:"none", fontSize:13.5, fontWeight:500, cursor:"pointer",
              background: mode === m ? "var(--card,#fff)" : "transparent",
              color: mode === m ? "var(--text-1,#1a1a1a)" : "var(--text-2,#888)",
              boxShadow: mode === m ? "0 1px 4px rgba(0,0,0,0.08)" : "none",
            }}>{m === "login" ? "Sign in" : "Create account"}</button>
          ))}
        </div>

        <form onSubmit={handleSubmit} style={{ display:"flex", flexDirection:"column", gap:14 }}>
          {mode === "signup" && (
            <>
              <div style={fieldStyle}>
                <label style={labelStyle}>Your name</label>
                <input type="text" value={fullName} onChange={e => setFullName(e.target.value)} required autoFocus placeholder="Jordan Lane" style={inputStyle} />
              </div>
              <div style={fieldStyle}>
                <label style={labelStyle}>Business name <span style={{ fontWeight:400, color:"var(--text-2,#888)" }}>(optional)</span></label>
                <input type="text" value={businessName} onChange={e => setBusinessName(e.target.value)} placeholder="Lumen Lab LLC" style={inputStyle} />
              </div>
            </>
          )}
          <div style={fieldStyle}>
            <label style={labelStyle}>Email</label>
            <input type="email" value={email} onChange={e => setEmail(e.target.value)} required autoFocus={mode === "login"} placeholder="you@example.com" style={inputStyle} />
          </div>
          <div style={fieldStyle}>
            <label style={labelStyle}>Password</label>
            <input type="password" value={password} onChange={e => setPassword(e.target.value)} required placeholder="••••••••" minLength={8} style={inputStyle} />
            {mode === "signup" && <div style={{ fontSize:12, color:"var(--text-2,#888)" }}>Minimum 8 characters</div>}
          </div>

          {error && <div style={{ fontSize:13, color:"#e04040", background:"#fff0f0", borderRadius:8, padding:"8px 12px" }}>{error}</div>}

          <button type="submit" disabled={loading} style={{
            marginTop:4, padding:"11px 0", borderRadius:8, border:"none",
            background:"oklch(0.64 0.16 35)", color:"#fff", fontSize:14, fontWeight:600,
            cursor: loading ? "default" : "pointer", opacity: loading ? 0.7 : 1,
          }}>
            {loading ? (mode === "login" ? "Signing in…" : "Creating account…") : (mode === "login" ? "Sign in" : "Create account")}
          </button>
        </form>

        {mode === "login" && (
          <div style={{ marginTop:20, textAlign:"center", fontSize:12, color:"var(--text-2,#888)" }}>
            Demo: jordan@lumenlab.tv / password123
          </div>
        )}
      </div>
    </div>
  );
};

const App = () => {
  const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);
  const [view, setView] = React.useState("dashboard");
  const [viewState, setViewState] = React.useState({});
  const [exportOpen, setExportOpen] = React.useState(false);

  // Mobile sidebar drawer state — only used at <860px
  const [sidebarOpen, setSidebarOpen] = React.useState(false);

  // ── Appearance preference (light / dark / auto) ──
  // This is an end-user setting, so it lives in localStorage — not Tweaks.
  // "auto" resolves through prefers-color-scheme.
  const [appearance, setAppearance] = React.useState(
    () => localStorage.getItem("creatorboard_appearance") || "light"
  );
  const [systemDark, setSystemDark] = React.useState(
    () => window.matchMedia?.("(prefers-color-scheme: dark)").matches || false
  );
  React.useEffect(() => {
    const mq = window.matchMedia?.("(prefers-color-scheme: dark)");
    if (!mq) return;
    const handler = (e) => setSystemDark(e.matches);
    mq.addEventListener("change", handler);
    return () => mq.removeEventListener("change", handler);
  }, []);
  const resolvedDark =
    appearance === "dark" ? true :
    appearance === "light" ? false :
    systemDark;
  const updateAppearance = (v) => {
    setAppearance(v);
    localStorage.setItem("creatorboard_appearance", v);
  };
  const toggleDark = () => updateAppearance(resolvedDark ? "light" : "dark");

  // Auth gate — "checking" while we ping /me, "out" = show login, "in" = logged in
  const [authState, setAuthState] = React.useState("checking");
  const [authError, setAuthError] = React.useState("");
  const [userMenuOpen, setUserMenuOpen] = React.useState(false);

  const handleLogout = async () => {
    try { await window.CB_API.logout(); } catch(_) {}
    localStorage.removeItem("creatorboard_onboarded");
    setAuthState("out");
    setApiUser(null);
    setUserMenuOpen(false);
  };

  // Onboarding gate — driven by DB (user.onboarded), not localStorage.
  // Starts false; set to true only when DB confirms user is not yet onboarded.
  const [showOnboarding, setShowOnboarding] = React.useState(false);
  const finishOnboarding = async (data) => {
    // data = { countryCode, currency, taxEntity } from the final onboarding step
    // undefined when user clicks "Skip setup"
    if (data && window.CB_API) {
      try {
        const updates = {};
        if (data.countryCode) updates.countryCode = data.countryCode;
        if (data.currency)    updates.baseCurrency = data.currency;
        await window.CB_API.updateMe(updates);
        await window.CB_API.setOnboarded();
        setProfileState(prev => ({
          ...prev,
          country: data.countryCode || prev.country,
          baseCurrency: data.currency || prev.baseCurrency,
        }));
      } catch(e) { /* non-fatal — still proceed */ }
    } else if (window.CB_API) {
      try { await window.CB_API.setOnboarded(); } catch(e) {}
    }
    localStorage.setItem("creatorboard_onboarded", "1");
    setShowOnboarding(false);
  };
  const replayOnboarding = () => {
    localStorage.removeItem("creatorboard_onboarded");
    setShowOnboarding(true);
  };

  // Lifted state — starts empty when backend is online, mock data only as offline fallback
  const [txState, setTxState] = React.useState([]);
  const [accountState, setAccountState] = React.useState([]);
  const [channelState, setChannelState] = React.useState([]);
  const [payoutsState, setPayoutsState] = React.useState([]);
  const [rulesState, setRulesState] = React.useState([]);
  const [invoiceState, setInvoiceState] = React.useState([]);
  const [profileState, setProfileState] = React.useState(window.CB_DATA.profile);
  const [apiUser, setApiUser] = React.useState(null);
  const [backendOnline, setBackendOnline] = React.useState(false);

  // Merge a user record into profileState — only use DB values, never fall back to mock data.
  // Also keeps window.CB_DATA.profile in sync so export.js always reads real data.
  const applyUserProfile = (user) => {
    const updates = {
      ownerName: user.fullName || "",
      email: user.email || "",
      businessName: user.businessName || "",
      addressLine1: user.addressLine1 || "",
      addressLine2: user.addressLine2 || "",
      country: user.countryCode || "",
      countryCode: user.countryCode || "",
      region: user.region || "",
      filingStatus: user.filingStatus || "",
      businessEntityId: user.businessEntityId || "",
      taxIdLabel: user.taxIdLabel || "EIN",
      taxId: user.taxId || "",
      website: user.website || "",
      phone: user.phone || "",
      baseCurrency: user.baseCurrency || "USD",
      bankDetails: user.bankDetails || "",
    };
    window.CB_DATA.profile = { ...window.CB_DATA.profile, ...updates };
    setProfileState(prev => ({ ...prev, ...updates }));
  };

  // Load all user data from the backend — called after login, signup, and on mount
  const loadUserData = React.useCallback(async () => {
    try {
      const [txRes, invs, rls, accs] = await Promise.allSettled([
        window.CB_API.getTransactions({ limit: 500 }),
        window.CB_API.getInvoices(),
        window.CB_API.getRules(),
        window.CB_API.getAccounts(),
      ]);

      if (txRes.status === "fulfilled") {
        setTxState((txRes.value?.data || []).map(t => ({
          id: t.id, date: t.date, type: t.type, category: t.category,
          vendor: t.vendor, amount: t.amount, currency: t.currency || "USD",
          fxRateToUsd: t.fxRateToUsd ?? null,
          account: t.account, note: t.note, status: t.status, receipt: t.receipt,
          needsReview: t.needsReview, autoCategorized: t.autoCategorized,
        })));
      }

      if (invs.status === "fulfilled") {
        setInvoiceState((invs.value || []).map(i => ({
          id: i.id, number: i.number, client: i.client,
          issuedAt: i.issuedAt, dueAt: i.dueAt, paidAt: i.paidAt,
          status: i.status, notes: i.notes,
          items: (i.items || []).map(it => ({ desc: it.desc, qty: it.qty, rate: it.rate })),
        })));
      }

      if (rls.status === "fulfilled") {
        setRulesState((rls.value || []).map(r => ({
          id: r.id, match: r.match, category: r.category,
          description: r.description, priority: r.priority,
        })));
      }

      if (accs.status === "fulfilled") {
        setAccountState(accs.value || []);
      }

      // Load categories and merge into CB_DATA so all modals see custom ones
      try {
        const cats = await window.CB_API.getCategories();
        if (cats?.income && cats?.expense) {
          window.CB_DATA.categories.income = cats.income;
          window.CB_DATA.categories.expense = cats.expense;
        }
      } catch (_) {}

      // Load YouTube channels + pending payouts
      try {
        const [chs, payoutsRes] = await Promise.allSettled([
          window.CB_API.getChannels(),
          window.CB_API.getYouTubePayouts(),
        ]);
        if (chs.status === "fulfilled") {
          setChannelState(chs.value || []);
          // Keep CB_DATA.channel in sync so export.js reads real channel data
          const primary = (chs.value || []).find(c => c.primary) || (chs.value || [])[0];
          if (primary) window.CB_DATA.channel = { ...window.CB_DATA.channel, name: primary.name, handle: primary.handle };
        }
        if (payoutsRes.status === "fulfilled") setPayoutsState(payoutsRes.value?.payouts || []);
      } catch (_) {}

      // Auto-reconnect: if we just came back from OAuth and have a saved handle, look it up now
      const pendingHandle = localStorage.getItem("yt_pending_handle");
      if (pendingHandle) {
        localStorage.removeItem("yt_pending_handle");
        try {
          const res = await window.CB_API.lookupYouTubeChannel(pendingHandle);
          if (res?.ok) {
            const chs = await window.CB_API.getChannels();
            setChannelState(chs || []);
            setTimeout(() => window.toast?.({ message: `Channel reconnected!` }), 200);
          } else {
            setTimeout(() => window.toast?.({ type: "error", message: `Auto-reconnect failed — go to Settings → Channel and paste your URL` }), 200);
          }
        } catch (e) {
          console.error("[yt auto-reconnect]", e);
          setTimeout(() => window.toast?.({ type: "error", message: e?.data?.error || e?.message || "Auto-reconnect failed — go to Settings → Channel and paste your URL" }), 200);
        }
      }
    } catch (_) {}
  }, []);

  // On mount: check session, load data if logged in, otherwise show login
  React.useEffect(() => {
    if (!window.CB_API) { setAuthState("out"); return; }
    window.CB_API.getMe().then(user => {
      setApiUser(user);
      setBackendOnline(true);
      applyUserProfile(user);
      setShowOnboarding(!user.onboarded);
      if (user.onboarded) localStorage.setItem("creatorboard_onboarded", "1");
      setAuthState("in");
      loadUserData();
      // Silently backfill historical FX rates for any transactions missing one
      window.CB_API.backfillFxRates().catch(() => {});
    }).catch(() => { setAuthState("out"); });
  }, []);

  // Handle YouTube OAuth redirect-back (?yt_connected=N | ?yt_auth_done=1 | ?yt_error=...)
  React.useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const ytConnected = params.get("yt_connected");
    const ytAuthDone = params.get("yt_auth_done");
    const ytError = params.get("yt_error");
    if (!ytConnected && !ytAuthDone && !ytError) return;

    window.history.replaceState({}, "", window.location.pathname);

    if (ytConnected || ytAuthDone) {
      // Go to the Channel section — loadUserData (called after auth) will handle
      // loading channels and running any pending auto-reconnect
      setView("settings");
      localStorage.setItem("yt_open_channel_tab", "1");

      // If no channels were auto-detected but we have a saved handle, queue it
      if (ytAuthDone) {
        const savedHandle = localStorage.getItem("yt_last_handle");
        if (savedHandle) localStorage.setItem("yt_pending_handle", savedHandle);
      }
    } else if (ytError && ytError !== "cancelled") {
      window.toast?.({ type: "error", message: `YouTube connection failed: ${ytError}` });
    }
  }, []);

  // Live count of items needing review for the sidebar badge
  const reviewCount = React.useMemo(
    () => window.CB_INTEL.reviewQueue(txState).length,
    [txState]
  );

  const goTo = (v, state) => {
    if (v === "export") { setExportOpen(true); setSidebarOpen(false); return; }
    setView(v);
    setViewState(state || {});
    setSidebarOpen(false);
  };

  // Dismissed alert IDs — "Mark all read" wipes them.
  // Dismissed alert IDs — persisted to localStorage so they survive refresh/navigation.
  const [dismissedAlerts, setDismissedAlerts] = React.useState(() => {
    try {
      const saved = localStorage.getItem("cb_dismissed_alerts");
      return saved ? new Set(JSON.parse(saved)) : new Set();
    } catch { return new Set(); }
  });
  const persistDismissed = (set) => {
    try { localStorage.setItem("cb_dismissed_alerts", JSON.stringify([...set])); } catch {}
    setDismissedAlerts(set);
  };
  const dismissAlert = (id) => persistDismissed(new Set([...dismissedAlerts, id]));
  const markAllRead = (ids) => persistDismissed(new Set(ids));

  // Keep window.CB_DATA.profile in sync with React state so export.js always
  // reads the current country/currency even if changed mid-session in Settings.
  React.useEffect(() => {
    window.CB_DATA.profile = { ...window.CB_DATA.profile, ...profileState };
  }, [profileState]);

  // Re-render whenever FX rates update so converted amounts refresh
  const [, forceFx] = React.useReducer(x => x + 1, 0);
  React.useEffect(() => window.CB_FX.subscribe(forceFx), []);

  // Apply theme class
  React.useEffect(() => {
    document.body.className =
      "theme-" + tweaks.theme +
      " density-" + tweaks.density +
      " layout-" + tweaks.kpiLayout +
      (resolvedDark ? " dark" : "");
    document.documentElement.style.setProperty("--font-display",
      `"${tweaks.displayFont}", ui-serif, Georgia, serif`);
  }, [tweaks.theme, tweaks.density, tweaks.kpiLayout, tweaks.displayFont, resolvedDark]);

  const ch = CB_DATA.channel;

  // Auth gate
  if (authState === "checking") {
    return (
      <div style={{ display:"flex", alignItems:"center", justifyContent:"center", height:"100vh", background:"var(--bg-base, #f5f0eb)" }}>
        <div style={{ textAlign:"center", color:"var(--text-2, #888)" }}>
          <img src="assets/creatorboard-icon.png" alt="" style={{ width:48, marginBottom:16, opacity:0.7 }} />
          <div>Loading…</div>
        </div>
      </div>
    );
  }

  if (authState === "out") {
    return (
      <LoginScreen
        onLogin={async (email, password) => {
          setAuthError("");
          try {
            const res = await window.CB_API.login({ email, password });
            setApiUser(res);
            setBackendOnline(true);
            applyUserProfile(res);
            setShowOnboarding(!res.onboarded);
            if (res.onboarded) localStorage.setItem("creatorboard_onboarded", "1");
            await loadUserData();
            setAuthState("in");
          } catch(e) { setAuthError("Login failed. Check your email and password."); }
        }}
        onSignup={async (email, password, fullName, businessName) => {
          setAuthError("");
          try {
            const res = await window.CB_API.signup({ email, password, fullName, businessName });
            setApiUser(res);
            setBackendOnline(true);
            applyUserProfile(res);
            // New user — clear mock data and start fresh
            setTxState([]); setInvoiceState([]); setRulesState([]); setAccountState([]);
            localStorage.removeItem("creatorboard_onboarded");
            setShowOnboarding(true);
            setAuthState("in");
          } catch(e) {
            setAuthError(e?.data?.error || "Sign up failed. That email may already be registered.");
          }
        }}
        error={authError}
      />
    );
  }

  if (showOnboarding) {
    return <Onboarding onFinish={finishOnboarding} />;
  }

  return (
    <div className={"app " + (tweaks.sidebar === "icons" ? "sidebar-icons" : "")}>
      {/* Mobile header — only visible on small screens */}
      <header className="mobile-header">
        <button className="icon-btn" onClick={() => setSidebarOpen(true)} aria-label="Open menu">
          <Icon name="filter" size={18} />
        </button>
        <div className="brand-mini" style={{ flex: 1 }}>
          <img src="assets/creatorboard-icon.png" alt="" />
          <span>CreatorBoard</span>
        </div>
        <AlertsButton
          alerts={window.computeAlerts({ txState, invoiceState, accounts: accountState, profile: profileState })}
          onJump={(target) => target && goTo(target)}
          onMarkAllRead={markAllRead}
          onDismiss={dismissAlert}
          dismissed={dismissedAlerts}
        />
        <button className="icon-btn" onClick={toggleDark} aria-label={resolvedDark ? "Switch to light mode" : "Switch to dark mode"}>
          <Icon name={resolvedDark ? "sun" : "moon"} size={16} />
        </button>
      </header>

      {/* Sidebar overlay for mobile drawer */}
      {sidebarOpen && <div className="sidebar-veil" onClick={() => setSidebarOpen(false)} />}

      {/* Sidebar */}
      <aside className={"sidebar " + (sidebarOpen ? "open" : "")}>
        <div className="brand">
          <div className="mark"><img src="assets/creatorboard-icon.png" alt="CreatorBoard" /></div>
          <span className="label">
            CreatorBoard
            <span className="version-badge">v{window.CB_VERSION}</span>
          </span>
        </div>

        <div className="nav-section">
          <span className="label">Workspace</span>
        </div>
        {[
          { id: "dashboard",     icon: "dashboard", label: "Dashboard" },
          { id: "ledger",        icon: "ledger",    label: "Transactions" },
          { id: "review",        icon: "bell",      label: "Review", badge: reviewCount },
          { id: "invoices",      icon: "file",      label: "Invoices" },
          { id: "receipts",      icon: "receipt",   label: "Receipts" },
          { id: "subscriptions", icon: "refresh",   label: "Subscriptions" },
          { id: "forecast",      icon: "sparkles",  label: "Forecast" },
          { id: "reports",       icon: "reports",   label: "P&L Reports" },
          { id: "settings",      icon: "settings",  label: "Settings" },
        ].map(item => (
          <button key={item.id}
            className={"nav-item " + (view === item.id ? "active" : "")}
            onClick={() => goTo(item.id)}>
            <Icon name={item.icon} size={18} />
            <span className="label">{item.label}</span>
            {item.badge > 0 && (
              <span style={{
                marginLeft: "auto",
                background: "var(--primary)", color: "white",
                fontSize: 10.5, fontWeight: 600,
                padding: "1px 7px", borderRadius: 8,
                minWidth: 18, textAlign: "center",
              }}>{item.badge}</span>
            )}
          </button>
        ))}

        <div className="nav-section">
          <span className="label">Quick</span>
        </div>
        <button className="nav-item" onClick={() => goTo("ledger", { addTx: true })}>
          <Icon name="plus" size={18} />
          <span className="label">Add transaction</span>
        </button>
        <button className="nav-item" onClick={() => setExportOpen(true)}>
          <Icon name="download" size={18} />
          <span className="label">Export</span>
        </button>
        <button className="nav-item" onClick={toggleDark} title={resolvedDark ? "Switch to light mode" : "Switch to dark mode"}>
          <Icon name={resolvedDark ? "sun" : "moon"} size={18} />
          <span className="label">{resolvedDark ? "Light mode" : "Dark mode"}</span>
        </button>

        <div className="channel-card" style={{ position:"relative" }}>
          <button
            onClick={() => setUserMenuOpen(o => !o)}
            style={{ display:"flex", alignItems:"center", gap:10, background:"none", border:"none", cursor:"pointer", width:"100%", padding:0, textAlign:"left" }}
          >
            <div className="ch-thumb">
              {(() => {
                const name = profileState?.ownerName || apiUser?.fullName || ch.name || "";
                const parts = name.trim().split(/\s+/).filter(Boolean);
                if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
                if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
                return "??";
              })()}
            </div>
            <div style={{ overflow:"hidden", flex:1 }}>
              <div className="ch-name">{profileState?.ownerName || apiUser?.fullName || ch.name}</div>
              <div className="ch-sub">{apiUser?.email || ch.handle}</div>
            </div>
            <Icon name="chevronDown" size={14} style={{ opacity:0.4, flexShrink:0, transform: userMenuOpen ? "rotate(180deg)" : "rotate(0deg)", transition:"transform 0.15s" }} />
          </button>

          {userMenuOpen && (
            <div style={{
              position:"absolute", bottom:"calc(100% + 8px)", left:0, right:0,
              background:"var(--card,#fff)", border:"1px solid var(--border,#e8e0d8)",
              borderRadius:10, boxShadow:"0 4px 20px rgba(0,0,0,0.12)",
              overflow:"hidden", zIndex:200
            }}>
              <button
                onClick={() => { goTo("settings"); setUserMenuOpen(false); }}
                style={{ display:"flex", alignItems:"center", gap:10, width:"100%", padding:"10px 14px", background:"none", border:"none", cursor:"pointer", fontSize:13.5, color:"var(--text-1,#1a1a1a)", textAlign:"left" }}
                onMouseEnter={e => e.currentTarget.style.background="var(--bg-base,#f5f0eb)"}
                onMouseLeave={e => e.currentTarget.style.background="none"}
              >
                <Icon name="settings" size={15} />
                Profile &amp; Settings
              </button>
              <div style={{ height:1, background:"var(--border,#e8e0d8)", margin:"0 10px" }} />
              <button
                onClick={handleLogout}
                style={{ display:"flex", alignItems:"center", gap:10, width:"100%", padding:"10px 14px", background:"none", border:"none", cursor:"pointer", fontSize:13.5, color:"#e04040", textAlign:"left" }}
                onMouseEnter={e => e.currentTarget.style.background="var(--bg-base,#f5f0eb)"}
                onMouseLeave={e => e.currentTarget.style.background="none"}
              >
                <Icon name="logout" size={15} />
                Log out
              </button>
            </div>
          )}
        </div>
      </aside>

      <main className="main">
        {view === "dashboard" && <Dashboard goTo={goTo} profile={profileState} txState={txState} accountState={accountState} payoutsState={payoutsState} channelState={channelState} backendOnline={backendOnline} />}
        {view === "ledger" && <Ledger goTo={goTo} initialAddOpen={!!viewState.addTx} txState={txState} setTxState={setTxState} />}
        {view === "review" && <ReviewInbox goTo={goTo} txState={txState} setTxState={setTxState} rules={rulesState} setRules={setRulesState} />}
        {view === "subscriptions" && <Subscriptions goTo={goTo} txState={txState} profile={profileState} />}
        {view === "invoices" && <Invoices goTo={goTo} invoiceState={invoiceState} setInvoiceState={setInvoiceState} txState={txState} setTxState={setTxState} profile={profileState} createFrom={viewState.createFrom} />}
        {view === "receipts" && <Receipts goTo={goTo} txState={txState} setTxState={setTxState} />}
        {view === "forecast" && <Forecast goTo={goTo} txState={txState} profile={profileState} />}
        {view === "reports" && <Reports goTo={goTo} txState={txState} profile={profileState} />}
        {view === "settings" && <Settings
          goTo={goTo}
          accountState={accountState} setAccountState={setAccountState}
          channelState={channelState} setChannelState={setChannelState}
          rulesState={rulesState} setRulesState={setRulesState}
          txState={txState} setTxState={setTxState}
          appearance={appearance} setAppearance={updateAppearance}
          profileState={profileState} setProfileState={setProfileState}
          onReplayOnboarding={replayOnboarding}
          backendOnline={backendOnline}
          initialSection={(() => { const s = localStorage.getItem("yt_open_channel_tab"); if (s) localStorage.removeItem("yt_open_channel_tab"); return s ? "channel" : null; })()}
        />}
      </main>

      <ExportModal open={exportOpen} onClose={() => setExportOpen(false)} txs={txState} />
      <ToastContainer />

      {/* Top-right floating notifications bell, like an e-commerce cart icon.
          Hidden on mobile because the bell already lives in the mobile header. */}
      <div className="alerts-floating" style={{
        position: "fixed", top: 20, right: 24, zIndex: 80,
      }}>
        <AlertsButton
          alerts={window.computeAlerts({ txState, invoiceState, accounts: accountState, profile: profileState })}
          onJump={(target) => target && goTo(target)}
          onMarkAllRead={markAllRead}
          onDismiss={dismissAlert}
          dismissed={dismissedAlerts}
        />
      </div>

      {/* Tweaks panel */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Theme" />
        <div style={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", gap: 6, padding: "0 2px 14px" }}>
          {Object.entries(THEME_PALETTES).map(([id, p]) => (
            <button key={id}
              onClick={() => setTweak("theme", id)}
              style={{
                border: "1.5px solid " + (tweaks.theme === id ? p.hue : "var(--hairline)"),
                borderRadius: 10, padding: 8, cursor: "pointer",
                background: tweaks.theme === id ? "var(--surface-2)" : "var(--surface)",
                display: "flex", flexDirection: "column", alignItems: "center", gap: 6,
              }}>
              <div style={{ width: 22, height: 22, borderRadius: "50%", background: p.hue }} />
              <span style={{ fontSize: 10, fontWeight: 500 }}>{p.name.split(" ")[0]}</span>
            </button>
          ))}
        </div>

        <TweakSection label="Layout" />
        <TweakRadio
          label="Density"
          value={tweaks.density}
          onChange={(v) => setTweak("density", v)}
          options={[
            { value: "cozy", label: "Cozy" },
            { value: "compact", label: "Compact" },
          ]}
        />
        <TweakRadio
          label="Sidebar"
          value={tweaks.sidebar}
          onChange={(v) => setTweak("sidebar", v)}
          options={[
            { value: "expanded", label: "Expanded" },
            { value: "icons", label: "Icons" },
          ]}
        />
        <TweakRadio
          label="KPI grid"
          value={tweaks.kpiLayout}
          onChange={(v) => setTweak("kpiLayout", v)}
          options={[
            { value: "hero", label: "Hero net" },
            { value: "equal", label: "Equal grid" },
          ]}
        />

        <TweakSection label="Typography" />
        <TweakSelect
          label="Display font"
          value={tweaks.displayFont}
          onChange={(v) => setTweak("displayFont", v)}
          options={[
            { value: "Instrument Serif", label: "Instrument Serif (default)" },
            { value: "Geist", label: "Geist (sans, unified)" },
          ]}
        />
      </TweaksPanel>
    </div>
  );
};

// Mount
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
