/* global React, ReactDOM */
const { useState, useEffect, useRef, useCallback } = React;

/* ----------------------------------------------------------
   Router
   ---------------------------------------------------------- */

function useRouter() {
  const [path, setPath] = useState(window.location.pathname);
  useEffect(() => {
    const handler = () => setPath(window.location.pathname);
    window.addEventListener("popstate", handler);
    return () => window.removeEventListener("popstate", handler);
  }, []);
  const navigate = (to) => {
    window.history.pushState(null, "", to);
    setPath(to);
  };
  return { path, navigate };
}

/* ----------------------------------------------------------
   Shared bits
   ---------------------------------------------------------- */

const Wordmark = () => <div className="wordmark" role="img" aria-label="Xera AI" />;

const XMark = ({ size = 32, radius }) => (
  <span
    className="x-mark"
    role="img"
    aria-label="Xera AI"
    style={{
      width: size,
      height: size,
      borderRadius: radius ?? Math.round(size * 0.25),
    }}
  />
);

const Icon = {
  Sun: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="12" r="4"/>
      <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/>
    </svg>
  ),
  Moon: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
    </svg>
  ),
  Discord: () => (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
      <path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106 13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.892.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03z"/>
    </svg>
  ),
  Plus: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
      <path d="M12 5v14M5 12h14"/>
    </svg>
  ),
  Send: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>
    </svg>
  ),
  Check: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
      <path d="M20 6L9 17l-5-5"/>
    </svg>
  ),
  Lock: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
    </svg>
  ),
  Zap: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
      <path d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/>
    </svg>
  ),
  Cpu: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/>
      <path d="M9 2v2M15 2v2M9 20v2M15 20v2M2 9h2M2 15h2M20 9h2M20 15h2"/>
    </svg>
  ),
  History: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l3 2"/>
    </svg>
  ),
  Close: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
      <path d="M18 6L6 18M6 6l12 12"/>
    </svg>
  ),
  Sparkle: () => (
    <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 3l1.9 4.6L18.5 9.5l-4.6 1.9L12 16l-1.9-4.6L5.5 9.5l4.6-1.9L12 3z"/>
      <path d="M19 14l.6 1.4L21 16l-1.4.6L19 18l-.6-1.4L17 16l1.4-.6L19 14z"/>
    </svg>
  ),
  Gear: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="12" r="3"/>
      <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h.01a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
    </svg>
  ),
  Palette: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="13.5" cy="6.5" r="0.5" fill="currentColor"/>
      <circle cx="17.5" cy="10.5" r="0.5" fill="currentColor"/>
      <circle cx="8.5" cy="7.5" r="0.5" fill="currentColor"/>
      <circle cx="6.5" cy="12.5" r="0.5" fill="currentColor"/>
      <path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.83 0 1.5-.67 1.5-1.5 0-.39-.15-.74-.39-1.01-.23-.26-.38-.61-.38-.99 0-.83.67-1.5 1.5-1.5H16c3.31 0 6-2.69 6-6 0-5.52-4.5-10-10-10z"/>
    </svg>
  ),
  Globe: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="12" r="10"/>
      <path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
    </svg>
  ),
  Brain: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 4.44-1.04z"/>
      <path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-4.44-1.04z"/>
    </svg>
  ),
  Robot: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="4" y="8" width="16" height="12" rx="2"/>
      <path d="M12 2v4M9 14h.01M15 14h.01M9 18h6"/>
      <path d="M2 14v2M22 14v2"/>
    </svg>
  ),
  Crown: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
      <path d="M2 18h20l-2-9-5 4-3-7-3 7-5-4-2 9zm0 2h20v2H2v-2z"/>
    </svg>
  ),
  Code: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>
    </svg>
  ),
  Search: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="11" cy="11" r="7"/><path d="M21 21l-4.35-4.35"/>
    </svg>
  ),
  Web: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/>
      <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>
    </svg>
  ),
  Doc: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
      <polyline points="14 2 14 8 20 8"/>
    </svg>
  ),
  Menu: () => (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
      <path d="M3 6h18M3 12h18M3 18h18"/>
    </svg>
  ),
  ArrowRight: () => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M5 12h14M12 5l7 7-7 7"/>
    </svg>
  ),
  Shield: () => (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
    </svg>
  ),
  Server: () => (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/>
      <circle cx="6" cy="6" r="1" fill="currentColor"/><circle cx="6" cy="18" r="1" fill="currentColor"/>
    </svg>
  ),
  Speed: () => (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M13 2L3 14h7l-1 8 10-12h-7l1-8z"/>
    </svg>
  ),
  Trash: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2"/>
    </svg>
  ),
  Pencil: () => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/>
    </svg>
  ),
};

/* ----------------------------------------------------------
   i18n + config
   ---------------------------------------------------------- */
const STRINGS = {
  de: {
    pitch: "Dein privater KI-Assistent — 100 % lokal, keine Cloud, keine Daten verlassen dein Netzwerk.",
    f_free: "5 freie Prompts",
    f_pro: "Unlimited mit Pro",
    f_stream: "Streaming-Antworten",
    f_history: "Chat-Verlauf",
    discord_login: "Mit Discord anmelden",
    or_terminal: "oder via Terminal (Xera Pro)",
    copy: "Kopieren",
    copied: "Kopiert",
    footer: "END-TO-END LOKAL",
    no_telemetry: "NULL TELEMETRIE",
    new_chat: "Neuer Chat",
    history: "Verlauf",
    search_history: "Chats durchsuchen...",
    today: "Heute",
    yesterday: "Gestern",
    last_7_days: "Letzte 7 Tage",
    older: "Aelter",
    delete_chat: "Chat loeschen",
    rename_chat: "Umbenennen",
    connected: "lokal verbunden",
    sign_out: "Abmelden",
    welcome_title: "Willkommen bei Xera AI",
    welcome_sub: "Stell mir eine Frage oder gib mir eine Aufgabe. Alles laeuft lokal — niemand schaut zu.",
    placeholder: "Nachricht an Xera AI...",
    disclaimer_local: "LOKAL",
    disclaimer_text: "KEINE DATEN VERLASSEN DEIN NETZWERK",
    you: "Du",
    prompts: "Prompts",
    unlimited: "unlimited",
    limit_title: "Prompt-Limit erreicht",
    limit_body_1: "Du hast deine 5 freien Prompts aufgebraucht.",
    limit_body_2: "Tritt unserem Discord bei und erhalte die Xera Pro Rolle fuer unlimitierten Zugang.",
    limit_body_guest: "Melde dich mit Discord an, um weiterzuchatten.",
    join_discord: "Discord beitreten",
    settings: "Einstellungen",
    sec_appearance: "Erscheinungsbild",
    sec_language: "Sprache",
    sec_model: "Modell",
    sec_agents: "Agents",
    sec_subscription: "Abonnement",
    sec_terminal: "Terminal",
    terminal_hint: "CLI-Zugang zu Xera AI (nur Xera Pro).",
    theme: "Theme",
    theme_dark: "Dunkel",
    theme_light: "Hell",
    accent: "Akzentfarbe",
    language_label: "Sprache der Oberflaeche",
    model_active: "Aktives Modell",
    model_hint: "Lokal geladenes Sprachmodell. Groesser = bessere Antworten, mehr RAM.",
    agents_hint: "Aktiviere Agents, um Xera AI mit speziellen Faehigkeiten zu erweitern.",
    plan_free: "Free",
    plan_pro: "Pro",
    free_p1: "5 Prompts insgesamt",
    free_p2: "Basis-Modell",
    free_p3: "Chat-Verlauf",
    pro_p1: "Unlimitierte Prompts",
    pro_p2: "Alle Modelle",
    pro_p3: "Alle Agents",
    pro_p4: "Prioritaet bei Updates",
    current: "AKTIV",
    upgrade: "UPGRADE",
    err_server: "Fehler: Server nicht erreichbar.",
    err_timeout: "Zeitueberschreitung — bitte erneut versuchen.",
    err_connection: "Verbindung fehlgeschlagen — bitte pruefen ob der Server erreichbar ist.",
    guest_welcome: "Teste Xera AI — 5 Prompts gratis, ohne Anmeldung.",
    try_free: "Kostenlos testen",
    login: "Anmelden",
    landing_hero: "Dein privater KI-Assistent",
    landing_sub: "100 % lokal. Keine Cloud. Keine Daten verlassen dein Netzwerk. Powered by deinem eigenen GPU-Server.",
    landing_feat1_title: "Komplett lokal",
    landing_feat1_desc: "Laeuft auf deiner eigenen Hardware — keine externen APIs, keine Abhaengigkeiten.",
    landing_feat2_title: "Privat & sicher",
    landing_feat2_desc: "Deine Daten bleiben in deinem Netzwerk. Kein Tracking, keine Telemetrie.",
    landing_feat3_title: "Blitzschnell",
    landing_feat3_desc: "36.4 Tokens/s Streaming auf deinem GPU-Cluster. Antworten in Echtzeit.",
    landing_stat1: "Tokens/s",
    landing_stat2: "Lokal",
    landing_stat3: "Daten gesendet",
    landing_about_title: "Built by",
    landing_about_text: "Lernender Informatiker EFZ Plattformentwicklung.",
    landing_demo_title: "Erlebe es selbst",
    landing_demo_user: "Erklaere mir Docker in 2 Saetzen.",
    landing_demo_ai: "Docker packt Anwendungen in isolierte Container — sie laufen ueberall gleich, egal ob auf deinem Laptop oder Server. Statt einer ganzen VM teilen sich Container den Host-Kernel und starten in Sekunden.",
    creator: "Built by",
  },
  en: {
    pitch: "Your private AI assistant — 100 % local, no cloud, no data leaves your network.",
    f_free: "5 free prompts",
    f_pro: "Unlimited with Pro",
    f_stream: "Streaming responses",
    f_history: "Chat history",
    discord_login: "Sign in with Discord",
    or_terminal: "or via terminal (Xera Pro)",
    copy: "Copy",
    copied: "Copied",
    footer: "END-TO-END LOCAL",
    no_telemetry: "ZERO TELEMETRY",
    new_chat: "New chat",
    history: "History",
    search_history: "Search chats...",
    today: "Today",
    yesterday: "Yesterday",
    last_7_days: "Last 7 days",
    older: "Older",
    delete_chat: "Delete chat",
    rename_chat: "Rename",
    connected: "local connected",
    sign_out: "Sign out",
    welcome_title: "Welcome to Xera AI",
    welcome_sub: "Ask me a question or give me a task. Everything runs locally — no one's watching.",
    placeholder: "Message Xera AI...",
    disclaimer_local: "LOCAL",
    disclaimer_text: "NO DATA LEAVES YOUR NETWORK",
    you: "You",
    prompts: "prompts",
    unlimited: "unlimited",
    limit_title: "Prompt limit reached",
    limit_body_1: "You've used your 5 free prompts.",
    limit_body_2: "Join our Discord and grab the Xera Pro role for unlimited access.",
    limit_body_guest: "Sign in with Discord to continue chatting.",
    join_discord: "Join Discord",
    settings: "Settings",
    sec_appearance: "Appearance",
    sec_language: "Language",
    sec_model: "Model",
    sec_agents: "Agents",
    sec_subscription: "Subscription",
    sec_terminal: "Terminal",
    terminal_hint: "CLI access to Xera AI (Xera Pro only).",
    theme: "Theme",
    theme_dark: "Dark",
    theme_light: "Light",
    accent: "Accent color",
    language_label: "Interface language",
    model_active: "Active model",
    model_hint: "Locally loaded language model. Bigger = better answers, more RAM.",
    agents_hint: "Enable agents to extend Xera AI with specialised capabilities.",
    plan_free: "Free",
    plan_pro: "Pro",
    free_p1: "5 prompts total",
    free_p2: "Base model",
    free_p3: "Chat history",
    pro_p1: "Unlimited prompts",
    pro_p2: "All models",
    pro_p3: "All agents",
    pro_p4: "Priority updates",
    current: "ACTIVE",
    upgrade: "UPGRADE",
    err_server: "Error: Server not reachable.",
    err_timeout: "Timeout — please try again.",
    err_connection: "Connection failed — please check if the server is reachable.",
    guest_welcome: "Try Xera AI — 5 free prompts, no sign-up required.",
    try_free: "Try for free",
    login: "Sign in",
    landing_hero: "Your private AI assistant",
    landing_sub: "100 % local. No cloud. No data leaves your network. Powered by your own GPU server.",
    landing_feat1_title: "Fully local",
    landing_feat1_desc: "Runs on your own hardware — no external APIs, no dependencies.",
    landing_feat2_title: "Private & secure",
    landing_feat2_desc: "Your data stays in your network. No tracking, no telemetry.",
    landing_feat3_title: "Lightning fast",
    landing_feat3_desc: "36.4 tokens/s streaming on your GPU cluster. Real-time responses.",
    landing_stat1: "Tokens/s",
    landing_stat2: "Local",
    landing_stat3: "Data sent",
    landing_about_title: "Built by",
    landing_about_text: "Apprentice IT specialist EFZ platform development.",
    landing_demo_title: "See it in action",
    landing_demo_user: "Explain Docker in 2 sentences.",
    landing_demo_ai: "Docker packages applications into isolated containers — they run the same everywhere, whether on your laptop or server. Instead of a full VM, containers share the host kernel and start in seconds.",
    creator: "Built by",
  },
};

const MODELS = [
  { id: "xera-35b", name: "Xera Local 35B", size: "35B", hint_de: "Standard — beste Allround-Performance", hint_en: "Standard — best all-round performance" },
  { id: "xera-13b", name: "Xera Local 13B", size: "13B", hint_de: "Schneller, weniger RAM", hint_en: "Faster, less RAM" },
  { id: "xera-7b", name: "Xera Local 7B", size: "7B", hint_de: "Minimal — laeuft auf jedem Laptop", hint_en: "Minimal — runs on any laptop" },
  { id: "xera-code-22b", name: "Xera Code 22B", size: "22B", hint_de: "Spezialisiert auf Code & Tools", hint_en: "Specialised for code & tools" },
];

const AGENTS = [
  { id: "code", name_de: "Code Agent", name_en: "Code Agent", hint_de: "Liest, schreibt und debuggt Code lokal", hint_en: "Reads, writes and debugs code locally", IconKey: "Code" },
  { id: "search", name_de: "Search Agent", name_en: "Search Agent", hint_de: "Durchsucht deine lokalen Dateien & Notizen", hint_en: "Searches your local files & notes", IconKey: "Search" },
  { id: "web", name_de: "Web Agent", name_en: "Web Agent", hint_de: "Holt Infos aus dem Web (opt-in)", hint_en: "Pulls info from the web (opt-in)", IconKey: "Web" },
  { id: "docs", name_de: "Docs Agent", name_en: "Docs Agent", hint_de: "Liest PDFs, Markdown, Notion-Exports", hint_en: "Reads PDFs, Markdown, Notion exports", IconKey: "Doc" },
];

const SUGGESTIONS = [
  { label: "Code", text: "Schreib mir ein Python-Script, das alle .log Dateien aelter als 7 Tage loescht." },
  { label: "Erklaer", text: "Erklaer den Unterschied zwischen Promises und async/await." },
  { label: "Debug", text: "Warum bekomme ich 'CORS error: No Access-Control-Allow-Origin' beim fetch?" },
  { label: "Schreib", text: "Formuliere eine hoefliche E-Mail-Absage auf eine Bewerbung." },
];

/* ----------------------------------------------------------
   Theme + topbar
   ---------------------------------------------------------- */

function ThemeToggle({ theme, setTheme }) {
  return (
    <div className="theme-toggle-segment" role="group" aria-label="Theme">
      <button type="button" aria-pressed={theme === "dark"} onClick={() => setTheme("dark")} title="Dark mode">
        <Icon.Moon />
      </button>
      <button type="button" aria-pressed={theme === "light"} onClick={() => setTheme("light")} title="Light mode">
        <Icon.Sun />
      </button>
    </div>
  );
}

function SettingsButton({ onClick }) {
  return (
    <button className="icon-btn" onClick={onClick} type="button" aria-label="Settings" title="Einstellungen">
      <Icon.Gear />
    </button>
  );
}

/* ----------------------------------------------------------
   Settings Modal
   ---------------------------------------------------------- */

function SettingsModal({ open, onClose, config, setConfig, user, t }) {
  const [tab, setTab] = useState("appearance");
  const [copied, setCopied] = useState(false);
  if (!open) return null;

  const isPro = user && (user.is_pro || user.is_admin);
  const isGuest = !user || user.is_guest;
  const stop = (e) => e.stopPropagation();
  const toggleAgent = (id) => {
    setConfig(c => ({
      ...c,
      agents: c.agents.includes(id) ? c.agents.filter(a => a !== id) : [...c.agents, id],
    }));
  };

  const copyCmd = () => {
    navigator.clipboard?.writeText("ssh cli@xera-app.com").catch(() => {});
    setCopied(true);
    setTimeout(() => setCopied(false), 1600);
  };

  const tabs = [
    { id: "appearance", label: t.sec_appearance, Icon: Icon.Palette },
    { id: "language", label: t.sec_language, Icon: Icon.Globe },
    { id: "model", label: t.sec_model, Icon: Icon.Brain },
    { id: "agents", label: t.sec_agents, Icon: Icon.Robot },
    ...(!isGuest && isPro ? [{ id: "terminal", label: t.sec_terminal, Icon: Icon.Code }] : []),
    { id: "subscription", label: t.sec_subscription, Icon: Icon.Crown },
  ];

  return (
    <div className="settings-overlay" onClick={onClose}>
      <div className="settings-modal" onClick={stop} role="dialog" aria-modal="true">
        <aside className="settings-side">
          <div className="title">{t.settings}</div>
          {tabs.map(({ id, label, Icon: I }) => (
            <button key={id} className={tab === id ? "active" : ""} onClick={() => setTab(id)} type="button">
              <I /> {label}
            </button>
          ))}
          <div className="settings-side-bottom">
            <div className="creator">{t.creator} <span className="name">Miguel Rodriguez</span></div>
            <div className="ver">XERA AI v1.0</div>
          </div>
        </aside>

        <section className="settings-main">
          <div className="settings-header">
            <h2>{tabs.find(x => x.id === tab)?.label}</h2>
            <button className="close-x" onClick={onClose} type="button" aria-label="Close">
              <Icon.Close />
            </button>
          </div>

          {tab === "appearance" && (
            <>
              <div className="setting-row">
                <div className="label"><div className="name">{t.theme}</div></div>
                <div className="seg">
                  <button className={config.theme === "dark" ? "on" : ""} onClick={() => setConfig(c => ({...c, theme: "dark"}))} type="button"><Icon.Moon /> {t.theme_dark}</button>
                  <button className={config.theme === "light" ? "on" : ""} onClick={() => setConfig(c => ({...c, theme: "light"}))} type="button"><Icon.Sun /> {t.theme_light}</button>
                </div>
              </div>
              <div className="setting-row">
                <div className="label"><div className="name">{t.accent}</div></div>
                <div className="seg">
                  {[
                    { h: 282, n: "Violet" }, { h: 260, n: "Indigo" }, { h: 200, n: "Cyan" }, { h: 320, n: "Pink" },
                  ].map(opt => (
                    <button key={opt.h} className={config.accentHue === opt.h ? "on" : ""} onClick={() => setConfig(c => ({...c, accentHue: opt.h}))} type="button">
                      <span style={{ width: 10, height: 10, borderRadius: "50%", background: `oklch(0.72 0.18 ${opt.h})`, display: "inline-block", boxShadow: `0 0 6px oklch(0.72 0.18 ${opt.h} / 0.6)` }} />
                      {opt.n}
                    </button>
                  ))}
                </div>
              </div>
            </>
          )}

          {tab === "language" && (
            <div className="setting-row">
              <div className="label"><div className="name">{t.language_label}</div></div>
              <select className="sel" value={config.language} onChange={(e) => setConfig(c => ({...c, language: e.target.value}))}>
                <option value="de">Deutsch</option>
                <option value="en">English</option>
              </select>
            </div>
          )}

          {tab === "model" && (
            <>
              <div className="setting-row" style={{ borderBottom: 0 }}>
                <div className="label">
                  <div className="name">{config.language === "en" ? "Coming soon" : "Kommt bald"}</div>
                  <div className="hint">{config.language === "en" ? "Multi-model selection is planned for a future update." : "Multi-Modell-Auswahl ist fuer ein zukuenftiges Update geplant."}</div>
                </div>
              </div>
              <div className="agent-list" style={{ marginTop: 8, opacity: 0.4, pointerEvents: "none" }}>
                {MODELS.map(m => (
                  <div key={m.id} className="agent-row" style={{ cursor: "default" }}>
                    <div className="agent-icon"><Icon.Brain /></div>
                    <div className="agent-meta">
                      <div className="agent-name">{m.name} <span className="plan-pill free" style={{ marginLeft: 6 }}>{m.size}</span></div>
                      <div className="agent-hint">{config.language === "en" ? m.hint_en : m.hint_de}</div>
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}

          {tab === "agents" && (
            <>
              <div className="setting-row" style={{ paddingTop: 0, borderBottom: 0 }}>
                <div className="label"><div className="hint">{t.agents_hint}</div></div>
              </div>
              <div className="agent-list">
                {AGENTS.map(a => {
                  const on = config.agents.includes(a.id);
                  const I = Icon[a.IconKey];
                  return (
                    <div key={a.id} className={"agent-row" + (on ? " on" : "")} onClick={() => toggleAgent(a.id)}>
                      <div className="agent-icon"><I /></div>
                      <div className="agent-meta">
                        <div className="agent-name">{config.language === "en" ? a.name_en : a.name_de}</div>
                        <div className="agent-hint">{config.language === "en" ? a.hint_en : a.hint_de}</div>
                      </div>
                      <div className={"switch" + (on ? " on" : "")}><i /></div>
                    </div>
                  );
                })}
              </div>
            </>
          )}

          {tab === "terminal" && isPro && (
            <>
              <div className="setting-row" style={{ borderBottom: 0 }}>
                <div className="label"><div className="hint">{t.terminal_hint}</div></div>
              </div>
              <div className="terminal-block" style={{ margin: "8px 0" }}>
                <span className="prompt-glyph">$</span>
                <code>ssh cli@xera-app.com</code>
                <button type="button" className={"copy-btn" + (copied ? " copied" : "")} onClick={copyCmd}>
                  {copied ? <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}><Icon.Check /> {t.copied}</span> : t.copy}
                </button>
              </div>
            </>
          )}

          {tab === "subscription" && (
            <div className="plan-grid">
              {[
                { id: "free", name: t.plan_free, price: config.language === "en" ? "Free" : "Gratis", per: "", feats: [t.free_p1, t.free_p2, t.free_p3] },
                { id: "pro", name: t.plan_pro, price: "Discord", per: " role", feats: [t.pro_p1, t.pro_p2, t.pro_p3, t.pro_p4] },
              ].map(p => {
                const isCurrent = (p.id === "pro" && config.isPro) || (p.id === "free" && !config.isPro);
                return (
                  <button key={p.id} type="button" className={"plan-card" + (isCurrent ? " on" : "")}>
                    <div className="plan-head">
                      <span className="plan-name">{p.id === "pro" && <Icon.Crown />} {p.name}</span>
                      <span className="plan-badge">{isCurrent ? t.current : t.upgrade}</span>
                    </div>
                    <div className="plan-price">{p.price}<span className="per">{p.per}</span></div>
                    <ul className="plan-list">
                      {p.feats.map((f, i) => <li key={i}>{f}</li>)}
                    </ul>
                  </button>
                );
              })}
            </div>
          )}
        </section>
      </div>
    </div>
  );
}

/* ----------------------------------------------------------
   Demo chat preview (typewriter effect)
   ---------------------------------------------------------- */

function DemoPreview({ t }) {
  const [aiText, setAiText] = useState("");
  const [done, setDone] = useState(false);
  const fullText = t.landing_demo_ai;
  const intervalRef = useRef(null);

  useEffect(() => {
    let i = 0;
    setAiText("");
    setDone(false);
    const delay = setTimeout(() => {
      intervalRef.current = setInterval(() => {
        i++;
        setAiText(fullText.slice(0, i));
        if (i >= fullText.length) {
          clearInterval(intervalRef.current);
          setDone(true);
        }
      }, 18);
    }, 1200);
    return () => { clearTimeout(delay); clearInterval(intervalRef.current); };
  }, [fullText]);

  return (
    <section className="landing-demo">
      <h2>{t.landing_demo_title}</h2>
      <div className="demo-window">
        <div className="demo-titlebar">
          <div className="demo-dots">
            <span className="demo-dot demo-dot-r" />
            <span className="demo-dot demo-dot-y" />
            <span className="demo-dot demo-dot-g" />
          </div>
          <span className="demo-titlebar-text">xera-app.com</span>
        </div>
        <div className="demo-chat">
          <div className="demo-msg demo-msg-user">
            <div className="demo-msg-bubble demo-bubble-user">{t.landing_demo_user}</div>
          </div>
          <div className="demo-msg demo-msg-ai">
            <div className="demo-msg-avatar"><XMark size={20} radius={6} /></div>
            <div className="demo-msg-bubble demo-bubble-ai">
              {aiText}
              {!done && <span className="demo-cursor" />}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ----------------------------------------------------------
   Landing page — xera-app.com/
   ---------------------------------------------------------- */

function LandingPage({ navigate, theme, setTheme, openSettings, t }) {
  return (
    <div className="landing-page">
      <div className="landing-bg">
        <div className="landing-orb landing-orb-1" />
        <div className="landing-orb landing-orb-2" />
        <div className="landing-orb landing-orb-3" />
      </div>
      <div className="bg-grid" />

      <nav className="landing-nav">
        <div className="landing-nav-brand">
          <XMark size={28} />
          <span className="landing-nav-name">XERA<span className="accent"> AI</span></span>
        </div>
        <div className="landing-nav-right">
          <ThemeToggle theme={theme} setTheme={setTheme} />
          <button className="landing-nav-login" onClick={() => navigate("/login")} type="button">
            {t.login}
          </button>
        </div>
      </nav>

      <section className="landing-hero">
        <div className="landing-hero-badge">
          <Icon.Lock /> {t.footer}
        </div>
        <h1>{t.landing_hero}</h1>
        <p className="landing-hero-sub">{t.landing_sub}</p>
        <div className="landing-hero-btns">
          <button className="landing-btn-primary" onClick={() => navigate("/c")} type="button">
            {t.try_free} <Icon.ArrowRight />
          </button>
          <button className="landing-btn-secondary" onClick={() => navigate("/login")} type="button">
            <Icon.Discord /> {t.discord_login}
          </button>
        </div>
      </section>

      <section className="landing-stats">
        <div className="landing-stat">
          <span className="landing-stat-num">36.4</span>
          <span className="landing-stat-label">{t.landing_stat1}</span>
        </div>
        <div className="landing-stat-divider" />
        <div className="landing-stat">
          <span className="landing-stat-num">100%</span>
          <span className="landing-stat-label">{t.landing_stat2}</span>
        </div>
        <div className="landing-stat-divider" />
        <div className="landing-stat">
          <span className="landing-stat-num">0</span>
          <span className="landing-stat-label">{t.landing_stat3}</span>
        </div>
      </section>

      <section className="landing-features">
        <div className="landing-feature-card">
          <div className="landing-feature-icon"><Icon.Server /></div>
          <h3>{t.landing_feat1_title}</h3>
          <p>{t.landing_feat1_desc}</p>
        </div>
        <div className="landing-feature-card">
          <div className="landing-feature-icon"><Icon.Shield /></div>
          <h3>{t.landing_feat2_title}</h3>
          <p>{t.landing_feat2_desc}</p>
        </div>
        <div className="landing-feature-card">
          <div className="landing-feature-icon"><Icon.Speed /></div>
          <h3>{t.landing_feat3_title}</h3>
          <p>{t.landing_feat3_desc}</p>
        </div>
      </section>

      <DemoPreview t={t} />

      <section className="landing-about">
        <div className="landing-about-card">
          <div className="landing-about-avatar">MR</div>
          <div>
            <h3>{t.landing_about_title} <span className="accent">Miguel Rodriguez</span></h3>
            <p>{t.landing_about_text}</p>
          </div>
        </div>
      </section>

      <footer className="landing-footer">
        <span>XERA AI v1.0</span>
        <span className="dot">&bull;</span>
        <span>{t.no_telemetry}</span>
      </footer>
    </div>
  );
}

/* ----------------------------------------------------------
   Login page — xera-app.com/login
   ---------------------------------------------------------- */

function LoginPage({ navigate, theme, setTheme, openSettings, t }) {
  return (
    <div className="login-page">
      <div className="bg-grid" />
      <div className="topbar">
        <SettingsButton onClick={openSettings} />
        <ThemeToggle theme={theme} setTheme={setTheme} />
      </div>

      <div className="login-card">
        <div className="brand">
          <Wordmark />
        </div>

        <p className="login-pitch">{t.pitch}</p>

        <div className="feature-grid">
          <div className="feature"><Icon.Zap /><span>{t.f_free}</span></div>
          <div className="feature"><Icon.Sparkle /><span>{t.f_pro}</span></div>
          <div className="feature"><Icon.Cpu /><span>{t.f_stream}</span></div>
          <div className="feature"><Icon.History /><span>{t.f_history}</span></div>
        </div>

        <a href="/auth/login" className="discord-btn">
          <Icon.Discord />
          {t.discord_login}
        </a>

        <div className="divider">{t.or_terminal}</div>

        <button className="guest-btn" onClick={() => navigate("/c")} type="button">
          <Icon.Zap />
          {t.try_free} — 5 {t.prompts}
        </button>

        <p className="login-footer">
          <Icon.Lock /> &nbsp;{t.footer} <span className="dot">&bull;</span> {t.no_telemetry}
        </p>
      </div>
    </div>
  );
}

/* ----------------------------------------------------------
   Markdown renderer
   ---------------------------------------------------------- */

function renderMarkdown(text) {
  const blocks = [];
  const parts = text.split(/```/);
  parts.forEach((part, idx) => {
    if (idx % 2 === 1) {
      const firstLineEnd = part.indexOf("\n");
      const code = firstLineEnd >= 0 ? part.slice(firstLineEnd + 1) : part;
      blocks.push(<pre key={"c" + idx}><code>{code}</code></pre>);
    } else {
      const paragraphs = part.split(/\n\n+/);
      paragraphs.forEach((p, pi) => {
        if (!p.trim()) return;
        if (p.split("\n").every(l => l.startsWith(">"))) {
          const cleaned = p.split("\n").map(l => l.replace(/^>\s?/, "")).join("\n");
          blocks.push(
            <blockquote key={"q" + idx + "-" + pi} style={{
              borderLeft: "3px solid var(--accent)",
              padding: "4px 0 4px 12px",
              margin: "0 0 10px",
              color: "var(--text-2)",
              whiteSpace: "pre-wrap",
            }}>{cleaned}</blockquote>
          );
          return;
        }
        blocks.push(<p key={"p" + idx + "-" + pi}>{renderInline(p)}</p>);
      });
    }
  });
  return blocks;
}

function renderInline(text) {
  const out = [];
  const re = /(`[^`]+`|\*\*[^*]+\*\*)/g;
  let last = 0, m, k = 0;
  while ((m = re.exec(text)) !== null) {
    if (m.index > last) out.push(text.slice(last, m.index));
    const t = m[0];
    if (t.startsWith("`")) out.push(<code key={k++}>{t.slice(1, -1)}</code>);
    else out.push(<strong key={k++} style={{ fontWeight: 600 }}>{t.slice(2, -2)}</strong>);
    last = m.index + t.length;
  }
  if (last < text.length) out.push(text.slice(last));
  return out;
}

/* ----------------------------------------------------------
   Chat page — xera-app.com/c and xera-app.com/c/:id
   ---------------------------------------------------------- */

function ChatPage({ user, setUser, navigate, theme, setTheme, openSettings, config, setConfig, t }) {
  const [sessions, setSessions] = useState([]);
  const [activeId, setActiveId] = useState(null);
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const [streaming, setStreaming] = useState(false);
  const [streamBuf, setStreamBuf] = useState("");
  const [showLimit, setShowLimit] = useState(false);
  const [promptCount, setPromptCount] = useState(user?.prompt_count || 0);
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [searchQ, setSearchQ] = useState("");
  const [editingId, setEditingId] = useState(null);
  const [editTitle, setEditTitle] = useState("");

  const FREE_LIMIT = user?.limit || 5;
  const isPro = user?.is_pro || user?.is_admin;
  const isGuest = !user || user.is_guest;

  const messagesRef = useRef(null);
  const taRef = useRef(null);

  const activeModel = MODELS.find(m => m.id === config.model) || MODELS[0];
  const isEmpty = !messages.length && !streaming;
  const userInitials = (user?.username || "G").slice(0, 2).toUpperCase();

  // Parse session ID from URL
  useEffect(() => {
    const match = window.location.pathname.match(/^\/c\/(\d+)$/);
    if (match) setActiveId(parseInt(match[1]));
  }, []);

  useEffect(() => {
    if (isGuest) return;
    fetch("/api/sessions").then(r => r.json()).then(data => {
      if (Array.isArray(data)) setSessions(data);
    }).catch(() => {});
  }, [isGuest]);

  useEffect(() => {
    if (!activeId) { setMessages([]); return; }
    if (isGuest) return;
    fetch(`/api/sessions/${activeId}/messages`).then(r => r.json()).then(data => {
      if (Array.isArray(data)) {
        setMessages(data.map(m => ({ role: m.role, content: m.content })));
      }
    }).catch(() => {});
  }, [activeId, isGuest]);

  useEffect(() => {
    const ta = taRef.current;
    if (!ta) return;
    ta.style.height = "auto";
    ta.style.height = Math.min(ta.scrollHeight, 200) + "px";
  }, [input]);

  useEffect(() => {
    const el = messagesRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [messages.length, streamBuf, streaming]);

  const newChat = () => {
    setActiveId(null);
    setMessages([]);
    navigate("/c");
    setSidebarOpen(false);
  };

  const openSession = (id) => {
    setActiveId(id);
    navigate(`/c/${id}`);
    setSidebarOpen(false);
  };

  const deleteSession = (id, e) => {
    e.stopPropagation();
    fetch(`/api/sessions/${id}`, { method: "DELETE" }).then(() => {
      setSessions(prev => prev.filter(s => s.id !== id));
      if (activeId === id) newChat();
    });
  };

  const startRename = (s, e) => {
    e.stopPropagation();
    setEditingId(s.id);
    setEditTitle(s.title);
  };

  const commitRename = (id) => {
    const title = editTitle.trim();
    if (!title) { setEditingId(null); return; }
    fetch(`/api/sessions/${id}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ title }),
    }).then(() => {
      setSessions(prev => prev.map(s => s.id === id ? { ...s, title } : s));
      setEditingId(null);
    });
  };

  const groupSessions = (list) => {
    const now = new Date();
    const todayStr = now.toISOString().slice(0, 10);
    const yest = new Date(now); yest.setDate(yest.getDate() - 1);
    const yesterdayStr = yest.toISOString().slice(0, 10);
    const weekAgo = new Date(now); weekAgo.setDate(weekAgo.getDate() - 7);

    const groups = { today: [], yesterday: [], week: [], older: [] };
    list.forEach(s => {
      const d = (s.created_at || "").slice(0, 10);
      if (d === todayStr) groups.today.push(s);
      else if (d === yesterdayStr) groups.yesterday.push(s);
      else if (new Date(d) >= weekAgo) groups.week.push(s);
      else groups.older.push(s);
    });
    return groups;
  };

  const filteredSessions = searchQ
    ? sessions.filter(s => s.title.toLowerCase().includes(searchQ.toLowerCase()))
    : sessions;
  const grouped = groupSessions(filteredSessions);

  const send = useCallback((overrideText) => {
    const text = (overrideText ?? input).trim();
    if (!text || streaming) return;

    if (!isPro && promptCount >= FREE_LIMIT) {
      setShowLimit(true);
      return;
    }

    setInput("");
    const newMessages = [...messages, { role: "user", content: text }];
    setMessages(newMessages);
    setStreaming(true);
    setStreamBuf("");

    const body = { messages: newMessages, session_id: activeId || undefined };
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 90000);

    fetch("/api/chat", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
      signal: controller.signal,
    }).then(async (response) => {
      clearTimeout(timeout);
      if (response.status === 403) {
        setStreaming(false);
        setShowLimit(true);
        return;
      }
      if (!response.ok) {
        setStreaming(false);
        setMessages(prev => [...prev, { role: "assistant", content: t.err_server }]);
        return;
      }

      const reader = response.body.getReader();
      const decoder = new TextDecoder();
      let buf = "";
      let fullResponse = "";
      let sessionId = activeId;

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buf += decoder.decode(value, { stream: true });
        const lines = buf.split("\n");
        buf = lines.pop();

        for (const line of lines) {
          if (!line.startsWith("data: ")) continue;
          const data = line.slice(6);
          if (data === "[DONE]") continue;
          try {
            const parsed = JSON.parse(data);
            if (parsed.session_id && !sessionId) {
              sessionId = parsed.session_id;
              setActiveId(sessionId);
              window.history.replaceState(null, "", `/c/${sessionId}`);
            }
            if (parsed.choices) {
              const delta = parsed.choices[0]?.delta?.content || "";
              if (delta) { fullResponse += delta; setStreamBuf(fullResponse); }
            }
            if (parsed.content) { fullResponse += parsed.content; setStreamBuf(fullResponse); }
          } catch (e) {}
        }
      }

      setStreaming(false);
      setStreamBuf("");
      if (fullResponse) {
        setMessages(prev => [...prev, { role: "assistant", content: fullResponse }]);
      }
      setPromptCount(c => c + 1);

      if (!isGuest) {
        fetch("/api/sessions").then(r => r.json()).then(data => {
          if (Array.isArray(data)) setSessions(data);
        }).catch(() => {});
      }

    }).catch((err) => {
      clearTimeout(timeout);
      setStreaming(false);
      const msg = err.name === "AbortError" ? t.err_timeout : t.err_connection;
      setMessages(prev => [...prev, { role: "assistant", content: msg }]);
    });
  }, [input, streaming, messages, activeId, isPro, promptCount, FREE_LIMIT, isGuest, t, navigate]);

  const onKey = (e) => {
    if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); }
  };

  return (
    <div className="chat-page">
      <div className="bg-grid" />

      <div className={"sidebar-overlay" + (sidebarOpen ? " open" : "")} onClick={() => setSidebarOpen(false)} />
      <div className="app-shell">
        <aside className={"sidebar" + (sidebarOpen ? " open" : "")}>
          <div className="sidebar-header">
            <div className="sidebar-brand" onClick={() => navigate("/")} style={{ cursor: "pointer" }}>
              <XMark size={32} />
              <h2>XERA<span className="accent"> AI</span></h2>
            </div>
            <button className="new-chat-btn" onClick={newChat} type="button">
              <Icon.Plus /> {t.new_chat}
            </button>
          </div>

          <div className="sidebar-search">
            <Icon.Search />
            <input
              type="text"
              placeholder={t.search_history}
              value={searchQ}
              onChange={e => setSearchQ(e.target.value)}
            />
            {searchQ && (
              <button type="button" className="sidebar-search-clear" onClick={() => setSearchQ("")}>
                <Icon.Close />
              </button>
            )}
          </div>

          <div className="sessions-list">
            {[
              { key: "today", label: t.today, items: grouped.today },
              { key: "yesterday", label: t.yesterday, items: grouped.yesterday },
              { key: "week", label: t.last_7_days, items: grouped.week },
              { key: "older", label: t.older, items: grouped.older },
            ].filter(g => g.items.length > 0).map(g => (
              <div key={g.key} className="session-group">
                <div className="session-group-label">{g.label}</div>
                {g.items.map(s => (
                  <div
                    key={s.id}
                    className={"session-item" + (s.id === activeId ? " active" : "")}
                    onClick={() => editingId !== s.id && openSession(s.id)}
                  >
                    {editingId === s.id ? (
                      <input
                        className="session-rename-input"
                        value={editTitle}
                        onChange={e => setEditTitle(e.target.value)}
                        onBlur={() => commitRename(s.id)}
                        onKeyDown={e => { if (e.key === "Enter") commitRename(s.id); if (e.key === "Escape") setEditingId(null); }}
                        autoFocus
                        onClick={e => e.stopPropagation()}
                      />
                    ) : (
                      <>
                        <span className="session-title">{s.title}</span>
                        <div className="session-actions">
                          <button type="button" className="session-action-btn" onClick={e => startRename(s, e)} title={t.rename_chat}>
                            <Icon.Pencil />
                          </button>
                          <button type="button" className="session-action-btn session-action-delete" onClick={e => deleteSession(s.id, e)} title={t.delete_chat}>
                            <Icon.Trash />
                          </button>
                        </div>
                      </>
                    )}
                  </div>
                ))}
              </div>
            ))}
          </div>

          <div className="sidebar-footer">
            <div className="sidebar-controls">
              <button type="button" className={"ctrl-btn" + (config.theme === "dark" ? " on" : "")} onClick={() => setTheme("dark")} title={t.theme_dark}>
                <Icon.Moon />
              </button>
              <button type="button" className={"ctrl-btn" + (config.theme === "light" ? " on" : "")} onClick={() => setTheme("light")} title={t.theme_light}>
                <Icon.Sun />
              </button>
              <div className="ctrl-divider" />
              <button type="button" className="ctrl-btn" onClick={openSettings} title={t.settings}>
                <Icon.Gear />
              </button>
            </div>

            {isGuest ? (
              <a href="/auth/login" className="discord-btn" style={{ margin: "8px 0 0", fontSize: 13, padding: "8px 12px", textAlign: "center" }}>
                <Icon.Discord />
                {t.discord_login}
              </a>
            ) : (
              <>
                <div className="user-info">
                  <div className="avatar">{userInitials}</div>
                  <div className="user-meta">
                    <div className="user-name" style={{ display: "flex", alignItems: "center", gap: 6 }}>
                      {user.username}
                      {isPro && <span className="plan-pill pro"><Icon.Crown /> PRO</span>}
                    </div>
                    <div className="user-status">
                      <span className="dot"></span> {t.connected}
                    </div>
                  </div>
                </div>
                <a href="/auth/logout" className="logout-btn">{t.sign_out}</a>
              </>
            )}
          </div>
        </aside>

        <main className="chat-main">
          <div className="chat-header">
            <button className="mobile-hamburger" onClick={() => setSidebarOpen(true)} type="button" aria-label="Menu">
              <Icon.Menu />
            </button>
            <div className="chat-title">
              {sessions.find(s => s.id === activeId)?.title || t.new_chat}
              <span className="model-chip">{activeModel.size}</span>
            </div>
            <div className="prompt-counter">
              {isPro ? (
                <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
                  <span className="plan-pill pro"><Icon.Crown /> PRO</span>
                  <span>{t.unlimited}</span>
                </span>
              ) : (
                <>
                  <span><span className="num">{promptCount}</span> / {FREE_LIMIT} {t.prompts}</span>
                  <div className="bar"><div style={{ width: (promptCount / FREE_LIMIT * 100) + "%" }} /></div>
                </>
              )}
            </div>
          </div>

          <div className="messages" ref={messagesRef}>
            {isEmpty && (
              <div className="welcome-msg">
                <h2>{t.welcome_title}</h2>
                <p>{isGuest ? t.guest_welcome : t.welcome_sub}</p>
                <div className="suggestions">
                  {SUGGESTIONS.map((s, i) => (
                    <button key={i} className="suggestion" onClick={() => send(s.text)} type="button">
                      <span className="s-label">{s.label}</span>
                      {s.text}
                    </button>
                  ))}
                </div>
              </div>
            )}

            {messages.map((m, i) => (
              <div key={i} className={"msg-row " + m.role}>
                <div className="msg-avatar">
                  {m.role === "user" ? userInitials : <XMark size={30} />}
                </div>
                <div className="msg-body">
                  <div className="msg-role">{m.role === "user" ? t.you : "Xera AI"}</div>
                  <div className="msg-content">{renderMarkdown(m.content)}</div>
                </div>
              </div>
            ))}

            {streaming && (
              <div className="msg-row assistant">
                <div className="msg-avatar"><XMark size={30} /></div>
                <div className="msg-body">
                  <div className="msg-role">Xera AI</div>
                  <div className="msg-content">
                    {streamBuf ? renderMarkdown(streamBuf) : null}
                    <span className="cursor-blink" />
                  </div>
                </div>
              </div>
            )}
          </div>

          <div className="input-area">
            <div className="input-wrapper">
              <textarea
                ref={taRef}
                rows={1}
                value={input}
                onChange={(e) => setInput(e.target.value)}
                onKeyDown={onKey}
                placeholder={t.placeholder}
                disabled={streaming}
              />
              <button
                type="button"
                className={"send-btn" + (input.trim() && !streaming ? " active" : "")}
                onClick={() => send()}
                disabled={!input.trim() || streaming}
                aria-label="Senden"
              >
                <Icon.Send />
              </button>
            </div>
            <p className="disclaimer">
              <Icon.Lock /> &nbsp;{t.disclaimer_local} <span className="dot">&bull;</span> {t.disclaimer_text}
            </p>
          </div>

          {showLimit && (
            <div className="limit-overlay" onClick={(e) => { if (e.target === e.currentTarget) setShowLimit(false); }}>
              <div className="limit-card">
                <button className="close-x" onClick={() => setShowLimit(false)} type="button" aria-label="Close">
                  <Icon.Close />
                </button>
                <div className="limit-icon"><Icon.Sparkle /></div>
                <h3>{t.limit_title}</h3>
                <p>{t.limit_body_1}</p>
                {isGuest ? (
                  <>
                    <p>{t.limit_body_guest}</p>
                    <a href="/auth/login" className="discord-btn">
                      <Icon.Discord />
                      {t.discord_login}
                    </a>
                  </>
                ) : (
                  <>
                    <p>{t.limit_body_2}</p>
                    <a href="https://discord.gg/YOUR_INVITE_LINK" className="discord-btn" target="_blank" rel="noopener">
                      <Icon.Discord />
                      {t.join_discord}
                    </a>
                  </>
                )}
              </div>
            </div>
          )}
        </main>
      </div>
    </div>
  );
}

/* ----------------------------------------------------------
   App root — router
   ---------------------------------------------------------- */

function App() {
  const { path, navigate } = useRouter();
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [settingsOpen, setSettingsOpen] = useState(false);

  const saved = (() => {
    try { return JSON.parse(localStorage.getItem("xera-config") || "{}"); } catch { return {}; }
  })();
  const [config, setConfig] = useState({
    theme: saved.theme || "dark",
    accentHue: saved.accentHue || 282,
    language: saved.language || "de",
    model: saved.model || "xera-35b",
    agents: saved.agents || ["code", "search"],
    isPro: false,
  });

  useEffect(() => {
    fetch("/api/me").then(r => r.json()).then(data => {
      setUser(data);
      setConfig(c => ({ ...c, isPro: data.is_pro || data.is_admin }));
      setLoading(false);
    }).catch(() => {
      setUser(null);
      setLoading(false);
    });
  }, []);

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", config.theme);
    document.documentElement.style.setProperty("--accent-h", config.accentHue);
    const { isPro, ...toSave } = config;
    localStorage.setItem("xera-config", JSON.stringify(toSave));
  }, [config]);

  useEffect(() => {
    if (!settingsOpen) return;
    const onKey = (e) => { if (e.key === "Escape") setSettingsOpen(false); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [settingsOpen]);

  const setTheme = (mode) => setConfig(c => ({ ...c, theme: mode }));
  const openSettings = () => setSettingsOpen(true);
  const closeSettings = () => setSettingsOpen(false);
  const t = STRINGS[config.language] || STRINGS.de;

  if (loading) return null;

  // If logged in and on / or /login → redirect to /c
  if (user && !user.is_guest && (path === "/" || path === "/login")) {
    navigate("/c");
  }

  let page;
  if (path === "/") {
    page = <LandingPage navigate={navigate} theme={config.theme} setTheme={setTheme} openSettings={openSettings} t={t} />;
  } else if (path === "/login") {
    page = <LoginPage navigate={navigate} theme={config.theme} setTheme={setTheme} openSettings={openSettings} t={t} />;
  } else if (path === "/c" || path.startsWith("/c/")) {
    const guestUser = user || { id: "guest", username: "Guest", is_guest: true, is_pro: false, is_admin: false, prompt_count: 0, limit: 5 };
    page = <ChatPage user={guestUser} setUser={setUser} navigate={navigate} theme={config.theme} setTheme={setTheme} openSettings={openSettings} config={config} setConfig={setConfig} t={t} />;
  } else {
    page = <LandingPage navigate={navigate} theme={config.theme} setTheme={setTheme} openSettings={openSettings} t={t} />;
  }

  return (
    <>
      {page}
      <SettingsModal open={settingsOpen} onClose={closeSettings} config={config} setConfig={setConfig} user={user} t={t} />
    </>
  );
}

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