// screens-desktop.jsx — adapter and missing desktop-only screens for the new desktop bundle

function desktopListings(ctx = {}) {
  return Array.isArray(ctx.listings) ? ctx.listings : (window.LISTINGS || []);
}

function desktopSearches(ctx = {}) {
  return Array.isArray(ctx.searches) ? ctx.searches : (window.SEARCHES || []);
}

function desktopNotifications(ctx = {}) {
  return Array.isArray(ctx.notifications) ? ctx.notifications : (window.NOTIFICATIONS || []);
}

function desktopJobs(ctx = {}) {
  return ctx.admin?.jobs || window.JOBS || [];
}

function desktopEvaluationMap(ctx = {}) {
  return ctx.evaluations || window.AI_EVALS || {};
}

function desktopEvaluationFor(ctx = {}, id) {
  const map = desktopEvaluationMap(ctx);
  return map[String(id)] || map[id] || null;
}

function desktopInvestmentItems(ctx = {}) {
  return ctx.investor?.evaluations || window.INVESTOR?.evaluations || [];
}

function desktopListingId(listing = {}) {
  return listing.id ?? listing.adId ?? listing.ad_id ?? null;
}

function desktopFindListing(ctx = {}, id = null) {
  if (id == null) return null;
  const key = String(id);
  const direct = desktopListings(ctx).find(item => (
    String(desktopListingId(item)) === key ||
    String(item.adId || '') === key ||
    String(item.evaluationId || '') === key
  ));
  if (direct) return direct;

  const investment = desktopInvestmentItems(ctx).find(item => (
    String(item.adId || '') === key ||
    String(item.id || '') === key ||
    String(desktopListingId(item.listing || {})) === key
  ));
  return investment?.listing || null;
}

function desktopEvaluationForListing(ctx = {}, listing = {}) {
  const id = desktopListingId(listing);
  const direct = desktopEvaluationFor(ctx, id);
  if (direct) return direct;
  const investment = desktopInvestmentItems(ctx).find(item => (
    String(item.adId || '') === String(id) ||
    String(desktopListingId(item.listing || {})) === String(id)
  ));
  return investment?.evaluation || null;
}

function desktopPhotos(listing = {}) {
  const fallback = [
    'data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%201200%20800%22%3E%3Crect%20width%3D%221200%22%20height%3D%22800%22%20fill%3D%22%23E8E0D2%22%2F%3E%3Cpath%20d%3D%22M447%20497l89-112%2069%2082%2050-61%2098%20121H447z%22%20fill%3D%22%23B9A98F%22%2F%3E%3Ccircle%20cx%3D%22729%22%20cy%3D%22318%22%20r%3D%2242%22%20fill%3D%22%23CDBFAD%22%2F%3E%3Ctext%20x%3D%22600%22%20y%3D%22612%22%20text-anchor%3D%22middle%22%20font-family%3D%22Arial%2C%20sans-serif%22%20font-size%3D%2238%22%20fill%3D%22%237A6A56%22%3EBild%20nicht%20verfügbar%3C%2Ftext%3E%3C%2Fsvg%3E',
  ];
  const photos = Array.isArray(listing.photos) && listing.photos.length ? listing.photos : fallback;
  return [0, 1, 2, 3].map(i => photos[i] || photos[0] || fallback[i]);
}

function desktopSearchPlatforms(search = {}) {
  return search.platforms || search.providers || ['kleinanzeigen'];
}

function desktopSearchKeywords(search = {}) {
  return search.keywords || search.requireKeywords || [];
}

function desktopSearchExcludeKeywords(search = {}) {
  return search.excludeKeywords || [];
}

function desktopMatchesQuery(item = {}, query = '') {
  const q = String(query || '').trim().toLowerCase();
  if (!q) return true;
  const text = [
    item.id,
    item.adId,
    item.taskId,
    item.searchId,
    item.name,
    item.title,
    item.city,
    item.district,
    item.address,
    item.plz,
    item.platform,
    item.sourceUrl,
    item.description,
    item.body,
    item.file,
    ...(Array.isArray(item.features) ? item.features : []),
    ...(Array.isArray(item.keywords) ? item.keywords : []),
    ...(Array.isArray(item.platforms) ? item.platforms : []),
  ].filter(Boolean).join(' ').toLowerCase();
  return text.includes(q);
}

function desktopFilterByQuery(items = [], query = '') {
  return String(query || '').trim() ? items.filter(item => desktopMatchesQuery(item, query)) : items;
}

function desktopFeatureText(listing = {}) {
  return [
    listing.title,
    listing.description,
    ...(Array.isArray(listing.features) ? listing.features : []),
  ].filter(Boolean).join(' ').toLowerCase();
}

function desktopFirstName(user = {}) {
  return (user.name || 'ImmoBot').split(/\s+/)[0] || 'ImmoBot';
}

function desktopUsedQuota(taskLimit = {}) {
  const max = taskLimit?.max_evaluations_per_day || 100;
  const remaining = taskLimit?.remaining_evaluations == null ? max : taskLimit.remaining_evaluations;
  return Math.max(0, max - remaining);
}

function fmtMetricPrice(value) {
  const n = Number(value || 0);
  if (!n) return '—';
  if (Math.abs(n) >= 1000000) return `${(n / 1000000).toLocaleString('de-DE', { maximumFractionDigits: 1 })} Mio. €`;
  return fmtPriceShort(n);
}

function fmtPct(value) {
  if (value == null || value === '') return '—';
  return `${Number(value).toLocaleString('de-DE', { maximumFractionDigits: 2 })}%`;
}

function desktopSyncGlobals(ctx = {}) {
  window.LISTINGS = desktopListings(ctx);
  window.SEARCHES = desktopSearches(ctx);
  window.NOTIFICATIONS = desktopNotifications(ctx);
  window.JOBS = desktopJobs(ctx);
  window.AI_EVALS = desktopEvaluationMap(ctx);
}

function desktopActiveFor(route) {
  if (['mietenDashboard', 'mietenResults', 'searchBuilderMiete'].includes(route)) return 'miete';
  if (['investor', 'compare'].includes(route)) return 'investor';
  if (route === 'portfolio') return 'merk';
  if (route === 'notifications') return 'mitt';
  if (['swipe', 'discover'].includes(route)) return 'entd';
  if (route === 'admin') return 'jobs';
  if (route === 'exports') return 'export';
  if (['profile', 'security'].includes(route)) return 'profile';
  return 'kauf';
}

function DesktopRouteScreen({ ctx, current }) {
  desktopSyncGlobals(ctx);
  const [searchQuery, setSearchQuery] = React.useState('');
  const active = desktopActiveFor(current.name);
  const setRoute = (route, tab, params = null) => {
    if (tab) ctx.setTab?.(tab);
    ctx.reset(route, params);
  };
  const onNav = (id) => {
    const map = {
      kauf: ['dashboard', 'immobilien'],
      miete: ['mietenDashboard', 'mieten'],
      investor: ['investor', 'investor'],
      merk: ['portfolio', 'portfolio'],
      mitt: ['notifications', 'notifications'],
      entd: ['discover', 'immobilien'],
      jobs: ['admin', 'profile'],
      export: ['exports', 'profile'],
      profile: ['profile', 'profile'],
      newSearch: ['searchBuilder', 'immobilien'],
    };
    const next = map[id] || map.kauf;
    setRoute(next[0], next[1]);
  };
  const handleSearchSubmit = (value) => {
    const q = String(value || searchQuery || '').trim();
    if (!q) return;
    const listing = desktopListings(ctx).find(item => desktopMatchesQuery(item, q));
    if (listing) {
      ctx.navigate('listing', { id: listing.id });
      return;
    }
    const search = desktopSearches(ctx).find(item => desktopMatchesQuery(item, q));
    if (search) {
      const route = search.category === 'miete' ? 'mietenResults' : 'results';
      ctx.navigate(route, { searchId: search.id });
    }
  };
  const topBarProps = {
    notifBadge: ctx.notifBadge || 0,
    searchValue: searchQuery,
    onSearchChange: setSearchQuery,
    onSearchSubmit: handleSearchSubmit,
    onNotifications: () => onNav('mitt'),
  };
  const common = {
    active,
    onNav,
    ctx,
    searchQuery,
    setSearchQuery,
    topBarProps,
    onNewSearch: () => setRoute('searchBuilder', 'immobilien'),
    onOpenAdmin: () => setRoute('admin', 'profile'),
    onOpenResults: (searchId) => {
      const searches = desktopSearches(ctx);
      const search = searches.find(s => String(s.id) === String(searchId)) || searches.find(s => s.category === 'kauf') || searches[0];
      const route = search?.category === 'miete' ? 'mietenResults' : 'results';
      ctx.navigate(route, { searchId: search?.id || searchId });
    },
    onOpenDetail: (id) => ctx.navigate('listing', { id }),
    onOpenAI: (id) => ctx.navigate('aiEval', { id: id || current.params?.id }),
    onSetResultsView: (view, search) => {
      const route = search?.category === 'miete' ? 'mietenResults' : 'results';
      ctx.navigate(route, { searchId: search?.id || current.params?.searchId, view }, true);
    },
    onRefresh: (search) => ctx.refreshSearch?.(search.taskId || search.id),
    onEvaluate: (search) => ctx.evaluateSearch?.(search.taskId || search.id),
    onExport: (search) => ctx.exportSearch?.(search.taskId || search.id),
    onReload: () => ctx.reloadAdmin?.(),
  };

  switch (current.name) {
    case 'searchBuilder':
      return <DesktopSearchBuilderScreen {...common} type="kauf" />;
    case 'searchBuilderMiete':
      return <DesktopSearchBuilderScreen {...common} type="miete" />;
    case 'results':
    case 'mietenResults':
      return <ResultsDesktop {...common} searchId={current.params?.searchId} routeView={current.params?.view} />;
    case 'listing':
      return <DetailDesktop key={current.params?.id || 'detail'} {...common} id={current.params?.id} />;
    case 'aiEval':
      return <AIEvalDesktop key={current.params?.id || 'aiEval'} {...common} id={current.params?.id} />;
    case 'mietenDashboard':
      return <DesktopCollectionScreen {...common} kind="rentals" />;
    case 'swipe':
    case 'discover':
      return <DesktopCollectionScreen {...common} kind="discover" />;
    case 'investor':
      return <InvestorScreen {...common} />;
    case 'compare':
      return <InvestorCompareScreen {...common} ids={current.params?.ids || []} />;
    case 'portfolio':
      return <DesktopCollectionScreen {...common} kind="portfolio" />;
    case 'notifications':
      return <DesktopNotificationsScreen {...common} />;
    case 'profile':
      return <DesktopProfileScreen {...common} />;
    case 'security':
      return <AccountSecurityScreen {...common} />;
    case 'admin':
      return <AdminDesktop {...common} />;
    case 'exports':
      return <DesktopExportsScreen {...common} />;
    default:
      return <DashboardDesktop {...common} />;
  }
}

function DesktopEmpty({ active = 'kauf', onNav = () => {}, ctx = {}, title, subtitle, icon }) {
  return (
    <DesktopFrame url="app.immobot.de">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'grid', placeItems: 'center', padding: 32, background: 'var(--bg)' }}>
        <DCard padding={30} style={{ maxWidth: 460, textAlign: 'center' }}>
          <div style={{ color: 'var(--primary)', display: 'inline-flex' }}>{icon}</div>
          <h2 style={{ margin: '12px 0 6px', fontFamily: 'var(--font-display)', fontSize: 28, fontWeight: 500 }}>{title}</h2>
          {subtitle && <p style={{ margin: 0, color: 'var(--ink-muted)', lineHeight: 1.55 }}>{subtitle}</p>}
        </DCard>
      </main>
    </DesktopFrame>
  );
}

function DesktopSearchBuilderScreen({ ctx, type = 'kauf', active = 'kauf', onNav = () => {}, topBarProps = {} }) {
  const [form, setForm] = React.useState({
    name: '',
    city: 'Berlin',
    radius: 10,
    priceMin: type === 'kauf' ? 250000 : 800,
    priceMax: type === 'kauf' ? 650000 : 1800,
    roomsMin: 2,
    roomsMax: 4,
    areaMin: 50,
    areaMax: 140,
    keywords: '',
    excludeKeywords: '',
  });
  const [message, setMessage] = React.useState(null);
  const update = (key, value) => setForm(v => ({ ...v, [key]: value }));
  const submit = async () => {
    setMessage(null);
    const created = await ctx.addSearch({
      type,
      property_type: 'wohnung',
      city: form.city,
      radius: Number(form.radius),
      price_min: Number(form.priceMin),
      price_max: Number(form.priceMax),
      rooms_min: Number(form.roomsMin),
      rooms_max: Number(form.roomsMax),
      area_min: Number(form.areaMin),
      area_max: Number(form.areaMax),
      require_keywords: form.keywords.split(',').map(s => s.trim()).filter(Boolean),
      exclude_keywords: form.excludeKeywords.split(',').map(s => s.trim()).filter(Boolean),
      active: true,
      run_now: true,
    });
    const route = type === 'miete' ? 'mietenResults' : 'results';
    ctx.navigate(route, { searchId: created?.id }, true);
  };
  return (
    <DesktopFrame url={`app.immobot.de/${type === 'miete' ? 'mieten' : 'kaufen'}/neu`}>
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar
          crumb={[type === 'miete' ? 'Mieten' : 'Kaufen', 'Neue Suche']}
          title={type === 'miete' ? 'Mietsuche erstellen' : 'Kaufsuche erstellen'}
          subtitle="Suchprofil, Budget und Schlüsselwörter in einer kompakten Desktop-Ansicht"
          actions={<DButton kind="primary" icon={<I.check />} onClick={submit}>Suche starten</DButton>}
          {...topBarProps}
        />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '26px 32px 42px' }}>
          <DCard padding={24} style={{ maxWidth: 920 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18 }}>
              <DField label="Suchname"><DInput value={form.name || `${type === 'miete' ? 'Mietwohnung' : 'Kaufobjekt'} ${form.city}`} onChange={v => update('name', v)} /></DField>
              <DField label="Stadt"><DInput value={form.city} onChange={v => update('city', v)} /></DField>
              <DField label="Radius km"><DInput type="number" value={form.radius} onChange={v => update('radius', v)} /></DField>
              <DField label="Zimmer"><DRange left={form.roomsMin} right={form.roomsMax} onLeft={v => update('roomsMin', v)} onRight={v => update('roomsMax', v)} /></DField>
              <DField label={type === 'miete' ? 'Warmmiete von/bis' : 'Kaufpreis von/bis'}><DRange left={form.priceMin} right={form.priceMax} onLeft={v => update('priceMin', v)} onRight={v => update('priceMax', v)} /></DField>
              <DField label="Wohnfläche von/bis"><DRange left={form.areaMin} right={form.areaMax} onLeft={v => update('areaMin', v)} onRight={v => update('areaMax', v)} /></DField>
              <DField label="Muss-Wörter"><DInput value={form.keywords} onChange={v => update('keywords', v)} placeholder="balkon, altbau" /></DField>
              <DField label="Ausschlüsse"><DInput value={form.excludeKeywords} onChange={v => update('excludeKeywords', v)} placeholder="wbs, tausch" /></DField>
            </div>
            {message && <div style={{ marginTop: 14, color: 'var(--danger)', fontSize: 13, fontWeight: 700 }}>{message}</div>}
            <div style={{ marginTop: 22, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
              <DButton kind="ghost" onClick={ctx.back}>Abbrechen</DButton>
              <DButton kind="primary" icon={<I.plus />} onClick={submit}>Speichern und suchen</DButton>
            </div>
          </DCard>
        </section>
      </main>
    </DesktopFrame>
  );
}

function DField({ label, children }) {
  return (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 7 }}>
      <span style={{ fontSize: 11, fontWeight: 800, color: 'var(--ink-muted)', textTransform: 'uppercase', letterSpacing: 0.06 }}>{label}</span>
      {children}
    </label>
  );
}

function DInput({ value, onChange, type = 'text', placeholder }) {
  return (
    <input
      type={type}
      value={value}
      placeholder={placeholder}
      onChange={e => onChange(e.target.value)}
      style={{ width: '100%', border: '1px solid var(--border)', background: 'var(--surface)', borderRadius: 10, padding: '11px 12px', fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--ink)' }}
    />
  );
}

function DRange({ left, right, onLeft, onRight }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
      <DInput type="number" value={left} onChange={onLeft} />
      <DInput type="number" value={right} onChange={onRight} />
    </div>
  );
}

function DesktopCollectionScreen({ active, onNav, ctx, kind, onOpenDetail, onNewSearch, searchQuery = '', topBarProps = {} }) {
  const listings = desktopListings(ctx);
  const favList = listings.filter(l => ctx.favs?.has(String(l.id)) || ctx.favs?.has(l.id));
  const configs = {
    rentals: {
      crumb: ['Arbeitsbereich', 'Mieten'],
      title: 'Miet-Suchen',
      subtitle: 'Aktive Mietprofile und neue Treffer',
      listings: listings.filter(l => l.type === 'miete'),
      action: <DButton kind="primary" icon={<I.plus />} onClick={() => ctx.reset('searchBuilderMiete')}>Neue Mietsuche</DButton>,
    },
    portfolio: {
      crumb: ['Arbeitsbereich', 'Merkliste'],
      title: 'Merkliste',
      subtitle: 'Gespeicherte Objekte mit aktualisierten Bewertungen',
      listings: favList,
      action: <DButton kind="primary" icon={<I.download />} onClick={ctx.exportPortfolio}>Portfolio CSV</DButton>,
    },
    discover: {
      crumb: ['Arbeitsbereich', 'Entdecken'],
      title: 'Entdecken',
      subtitle: 'Neue Vorschläge aus Ihren Suchmustern',
      listings: listings.filter(l => l.aiScore).sort((a, b) => (b.aiScore || 0) - (a.aiScore || 0)).slice(0, 12),
      action: <DButton kind="primary" icon={<I.plus />} onClick={onNewSearch}>Neue Suche</DButton>,
    },
  };
  const cfg = configs[kind];
  const [showTopOnly, setShowTopOnly] = React.useState(false);
  const shownListings = desktopFilterByQuery(cfg.listings, searchQuery)
    .filter(l => !showTopOnly || Number(l.aiScore || 0) >= 80);
  return (
    <DesktopFrame url={`app.immobot.de/${kind}`}>
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar
          crumb={cfg.crumb}
          title={cfg.title}
          subtitle={cfg.subtitle}
          actions={<>{kind !== 'portfolio' && <DButton kind={showTopOnly ? 'primary' : 'outline'} icon={<I.filter />} onClick={() => setShowTopOnly(v => !v)}>Top-Score</DButton>}{cfg.action}</>}
          {...topBarProps}
        />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px' }}>
          {shownListings.length ? (
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(240px, 1fr))', gap: 16 }}>
              {shownListings.map(l => <DesktopListingCard key={l.id} listing={l} onTap={onOpenDetail} isFav={ctx.favs?.has(String(l.id)) || ctx.favs?.has(l.id)} onFav={ctx.toggleFav} />)}
            </div>
          ) : (
            <DCard padding={28} style={{ maxWidth: 460 }}>
              <h2 style={{ margin: 0, fontFamily: 'var(--font-display)', fontSize: 26, fontWeight: 500 }}>Noch keine Einträge</h2>
              <p style={{ margin: '8px 0 0', color: 'var(--ink-muted)', lineHeight: 1.55 }}>Sobald passende Inserate vorhanden sind, erscheinen sie hier.</p>
            </DCard>
          )}
        </section>
      </main>
    </DesktopFrame>
  );
}

function DesktopNotificationsScreen({ active, onNav, ctx, searchQuery = '', topBarProps = {} }) {
  React.useEffect(() => { ctx.loadNotificationSettings?.().catch(() => {}); }, []);
  const notifications = desktopFilterByQuery(desktopNotifications(ctx), searchQuery);
  const allNotifications = desktopNotifications(ctx);
  const markAll = () => ctx.setNotifications?.(allNotifications.map(n => ({ ...n, read: true })));
  const click = (n) => {
    if (n.listingId) ctx.navigate('listing', { id: n.listingId });
    else if (n.searchId) ctx.navigate('results', { searchId: n.searchId });
  };
  return (
    <DesktopFrame url="app.immobot.de/mitteilungen">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar crumb={['Arbeitsbereich', 'Mitteilungen']} title="Mitteilungen" subtitle="Preisänderungen, neue Treffer und Systemmeldungen" actions={<DButton kind="outline" icon={<I.check />} onClick={markAll}>Alle gelesen</DButton>} {...topBarProps} />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px', display: 'grid', gridTemplateColumns: '1.2fr .8fr', gap: 18, alignItems: 'start' }}>
          <DCard padding={0}>
            {notifications.map((n, i) => (
              <button type="button" key={n.id} onClick={() => click(n)} style={{
                width: '100%', display: 'flex', gap: 14, padding: '16px 20px', textAlign: 'left', border: 0,
                borderBottom: i < notifications.length - 1 ? '1px solid var(--border)' : 'none',
                background: n.read ? 'var(--surface)' : 'linear-gradient(90deg, var(--primary-soft), var(--surface))',
                cursor: 'pointer', fontFamily: 'var(--font-sans)',
              }}>
                <span style={{ width: 36, height: 36, borderRadius: 10, background: n.read ? 'var(--surface-2)' : 'var(--primary)', color: n.read ? 'var(--ink-muted)' : '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  {n.kind === 'new-match' ? <I.search /> : n.kind === 'system' ? <I.shield /> : <I.spark />}
                </span>
                <span style={{ flex: 1 }}>
                  <span style={{ display: 'block', fontSize: 13.5, fontWeight: 700, color: 'var(--ink)' }}>{n.title}</span>
                  <span style={{ display: 'block', fontSize: 12.5, color: 'var(--ink-muted)', marginTop: 3, lineHeight: 1.5 }}>{n.body}</span>
                </span>
                <span style={{ fontSize: 11.5, color: 'var(--ink-muted)' }}>{fmtRelative(n.at)}</span>
              </button>
            ))}
          </DCard>
          <DCard padding={20}>
            <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 20 }}>Browser-Push</h3>
            <p style={{ margin: '6px 0 16px', color: 'var(--ink-muted)', fontSize: 12.5, lineHeight: 1.55 }}>
              {ctx.notificationSettings?.hasSubscriptions ? `${ctx.notificationSettings.subscriptions.length} Gerät(e) verbunden.` : 'Noch kein Gerät verbunden.'}
            </p>
            {(ctx.notificationSettings?.subscriptions || []).map(device => (
              <div key={device.id} style={{ padding: '10px 0', borderTop: '1px solid var(--border)', display: 'flex', gap: 10, alignItems: 'center' }}>
                <I.shield style={{ color: 'var(--primary)' }} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 800 }}>{device.label}</div>
                  <div style={{ fontSize: 11, color: 'var(--ink-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{device.endpointPreview}</div>
                </div>
                <DButton kind="ghost" size="sm" onClick={() => ctx.revokePushSubscription?.(device.endpoint)}>Entfernen</DButton>
              </div>
            ))}
            <div style={{ display: 'flex', gap: 8, marginTop: 16 }}>
              <DButton kind="outline" onClick={ctx.sendTestNotification}>Test senden</DButton>
              <DButton kind="ghost" onClick={ctx.revokeAllPushSubscriptions}>Alle entfernen</DButton>
            </div>
          </DCard>
        </section>
      </main>
    </DesktopFrame>
  );
}

function DesktopExportsScreen({ active, onNav, ctx, searchQuery = '', topBarProps = {} }) {
  const rows = desktopFilterByQuery(desktopSearches(ctx).map(s => ({
    id: s.taskId || s.id,
    file: `${s.name || 'suche'}-${s.id}.csv`,
    type: 'CSV',
    created: s.lastRun ? fmtDateTime(s.lastRun) : 'Noch nicht gelaufen',
    size: `${s.matches || 0} Zeilen`,
    name: s.name,
    city: s.city,
  })), searchQuery).slice(0, 6);
  return (
    <DesktopFrame url="app.immobot.de/exporte">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar crumb={['Verwaltung', 'Exporte']} title="Exporte" subtitle="CSV-Downloads für Suchergebnisse und Portfolio" actions={<DButton kind="primary" icon={<I.download />} onClick={ctx.exportPortfolio}>Portfolio exportieren</DButton>} {...topBarProps} />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px' }}>
          <DCard padding={0} style={{ maxWidth: 920 }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1.8fr 100px 180px 120px 40px', padding: '10px 20px', background: 'var(--surface-2)', borderBottom: '1px solid var(--border)', fontSize: 10.5, fontWeight: 700, color: 'var(--ink-muted)', letterSpacing: 0.06, textTransform: 'uppercase' }}>
              <span>Datei</span><span>Typ</span><span>Erstellt</span><span>Umfang</span><span />
            </div>
            {rows.map((row, i) => (
              <div key={row.file} style={{ display: 'grid', gridTemplateColumns: '1.8fr 100px 180px 120px 40px', padding: '14px 20px', alignItems: 'center', borderBottom: i < rows.length - 1 ? '1px solid var(--border)' : 'none', fontSize: 13 }}>
                <span style={{ fontWeight: 700, fontFamily: 'var(--font-mono)', fontSize: 12 }}>{row.file}</span>
                <DChip tone="accent">{row.type}</DChip>
                <span style={{ color: 'var(--ink-muted)' }}>{row.created}</span>
                <span>{row.size}</span>
                <DButton kind="icon" icon={<I.download />} onClick={() => ctx.exportSearch?.(row.id)} />
              </div>
            ))}
          </DCard>
        </section>
      </main>
    </DesktopFrame>
  );
}

function investorListingKey(item = {}) {
  return String(item.adId || desktopListingId(item.listing) || item.id || '');
}

function investorTopMetric(metrics = {}) {
  const entries = [
    ['gross_yield', metrics?.gross_yield],
    ['net_yield', metrics?.net_yield],
    ['cap_rate', metrics?.cap_rate],
    ['cash_on_cash', metrics?.cash_on_cash],
  ].filter(([, value]) => value != null && !Number.isNaN(Number(value)));
  if (!entries.length) return null;
  entries.sort((a, b) => Number(b[1]) - Number(a[1]));
  return entries[0];
}

function InvestorScreen({ ctx, active = 'investor', onNav = () => {}, searchQuery = '', topBarProps = {} }) {
  const [loaded, setLoaded] = React.useState(false);
  React.useEffect(() => {
    if (!loaded) {
      setLoaded(true);
      ctx.loadInvestor?.().catch(() => {});
    }
  }, [loaded]);

  const data = ctx.investor || {};
  const items = (data.evaluations || []).filter(item => (
    desktopMatchesQuery(item, searchQuery) || desktopMatchesQuery(item.listing || {}, searchQuery)
  ));
  const summary = data.summary || {};
  const locations = data.locations || [];
  const distribution = data.distribution || {};
  const topDeals = items.slice(0, 12);
  const heroDeal = topDeals[0] || null;
  const goListing = (item) => ctx.navigate('listing', { id: investorListingKey(item) });
  const avgNet = summary.averageNetYield ?? null;
  const avgGross = summary.averageGrossYield ?? null;
  const annualIncome = summary.estimatedAnnualIncome ?? data.portfolio?.summary?.estimatedAnnualIncome ?? null;
  const bestCashflow = items.reduce((best, item) => {
    const value = Number(item.metrics?.monthly_cash_flow?.net_cash_flow ?? -Infinity);
    return value > (best.value ?? -Infinity) ? { item, value } : best;
  }, {});
  const bestYield = items.reduce((best, item) => {
    const metric = investorTopMetric(item.metrics || {});
    const value = metric ? Number(metric[1]) : -Infinity;
    return value > (best.value ?? -Infinity) ? { item, value, key: metric?.[0] } : best;
  }, {});

  if (!ctx.isDesktop) {
    return (
      <div className="phone-scroll fade-in" style={{ padding: '56px 16px 106px', background: 'var(--bg)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12 }}>
          <div>
            <div style={{ fontSize: 12, color: 'var(--ink-muted)', fontWeight: 800, textTransform: 'uppercase', letterSpacing: .06 }}>Investment Cockpit</div>
            <h1 style={{ margin: '5px 0 0', fontFamily: 'var(--font-display)', fontSize: 38, lineHeight: 1, fontWeight: 400 }}>Investor</h1>
          </div>
          <button className="btn btn-soft" onClick={() => ctx.loadInvestor?.()} style={{ padding: '10px 12px', fontSize: 12 }}><I.spark style={{ width: 15, height: 15 }} /> Aktualisieren</button>
        </div>

        <div className="card" style={{ marginTop: 18, padding: 18, overflow: 'hidden', position: 'relative', background: 'linear-gradient(135deg, var(--ink) 0%, var(--ink-2) 58%, var(--primary-ink) 100%)', color: '#fff' }}>
          <div style={{ position: 'absolute', right: -34, top: -38, width: 150, height: 150, borderRadius: 999, background: 'rgba(211,151,54,.20)' }} />
          <div style={{ position: 'relative', display: 'flex', justifyContent: 'space-between', gap: 14 }}>
            <div>
              <div style={{ fontSize: 11, fontWeight: 900, color: 'var(--primary)', textTransform: 'uppercase', letterSpacing: .08 }}>Deal-Pipeline</div>
              <div style={{ marginTop: 6, fontFamily: 'var(--font-display)', fontSize: 30, lineHeight: 1 }}>{summary.count || items.length || 0} Objekte</div>
              <div style={{ marginTop: 8, color: 'rgba(255,255,255,.70)', fontSize: 12, lineHeight: 1.45 }}>Top-Score {summary.topScore || '—'} · Ø Score {summary.averageScore || '—'} · Volumen {fmtMetricPrice(summary.totalValue)}</div>
            </div>
            <div style={{ width: 72, height: 72, borderRadius: 22, background: 'rgba(255,255,255,.10)', border: '1px solid rgba(255,255,255,.18)', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
              <ScoreRing score={summary.topScore || 0} size={58} stroke={5} showGrade={false} />
            </div>
          </div>
          <div style={{ position: 'relative', marginTop: 16, display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
            <InvestorMini label="Brutto Ø" value={fmtPct(avgGross)} dark />
            <InvestorMini label="Netto Ø" value={fmtPct(avgNet)} dark />
            <InvestorMini label="Miete p.a." value={fmtMetricPrice(annualIncome)} dark />
          </div>
        </div>

        <div style={{ marginTop: 14, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
          <StatTile value={fmtPct(bestYield.value)} label="Bester Yield" delta={bestYield.item?.listing?.city || '—'} accent />
          <StatTile value={bestCashflow.value > -Infinity ? fmtMetricPrice(bestCashflow.value) : '—'} label="Top Cashflow/Mon." delta={bestCashflow.item?.listing?.city || '—'} />
        </div>

        <div style={{ marginTop: 22, display: 'flex', justifyContent: 'space-between', alignItems: 'end' }}>
          <div>
            <h3 className="section-title" style={{ margin: 0 }}>Top-Deals</h3>
            <div style={{ fontSize: 12, color: 'var(--ink-muted)', marginTop: 3 }}>Nach Score, Rendite und Risiko priorisiert</div>
          </div>
          <button onClick={() => ctx.navigate('compare', { ids: topDeals.slice(0, 4).map(x => x.id).filter(Boolean) })} className="chip" style={{ fontSize: 12 }}>Vergleich</button>
        </div>
        <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>
          {topDeals.map((item, i) => <InvestorMobileCard key={`${item.adId || item.id}-${i}`} item={item} rank={i + 1} onOpen={() => goListing(item)} />)}
          {!topDeals.length && <EmptyState icon={<I.trend />} title="Noch keine Investmentdaten" subtitle="Sobald Kaufobjekte bewertet sind, erscheinen hier Rendite, Risiko und Cashflow." />}
        </div>
      </div>
    );
  }

  return (
    <DesktopFrame url="app.immobot.de/investor">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar crumb={['Analyse', 'Investor']} title="Investor Cockpit" subtitle="Priorisierte Deals, Rendite, Cashflow, Risiko und Due-Diligence-Hinweise" actions={<><DButton kind="outline" icon={<I.download />} onClick={ctx.exportPortfolio}>CSV</DButton><DButton kind="primary" icon={<I.trend />} onClick={() => ctx.navigate('compare', { ids: (data.compare?.ids || topDeals.slice(0, 4).map(x => x.id)).slice(0, 4) })}>Top 4 vergleichen</DButton></>} {...topBarProps} />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px', display: 'flex', flexDirection: 'column', gap: 20 }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1.15fr .85fr', gap: 18 }}>
            <DCard padding={0} style={{ overflow: 'hidden', background: 'linear-gradient(135deg, var(--ink) 0%, var(--ink-2) 55%, var(--primary-ink) 100%)', color: '#fff' }}>
              <div style={{ padding: 24, display: 'grid', gridTemplateColumns: '1fr 190px', gap: 20, alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 11, fontWeight: 900, color: 'var(--primary)', textTransform: 'uppercase', letterSpacing: .08 }}>Investment Radar</div>
                  <h2 style={{ margin: '8px 0 0', fontFamily: 'var(--font-display)', fontSize: 38, fontWeight: 400, lineHeight: 1 }}>Die besten Objekte zuerst.</h2>
                  <p style={{ margin: '10px 0 0', maxWidth: 640, color: 'rgba(255,255,255,.72)', fontSize: 13.5, lineHeight: 1.6 }}>Kombiniert KI-Score, Renditeannahmen, Cashflow, CapEx, Leerstandsrisiko und Standortsignale zu einer schnellen Investment-Priorisierung.</p>
                  {heroDeal && <button onClick={() => goListing(heroDeal)} style={{ marginTop: 18, border: '1px solid rgba(255,255,255,.18)', borderRadius: 14, padding: 0, overflow: 'hidden', background: 'rgba(255,255,255,.08)', color: '#fff', display: 'grid', gridTemplateColumns: '86px 1fr auto', alignItems: 'center', minWidth: 520, textAlign: 'left', cursor: 'pointer' }}>
                    <div className="photo" style={{ height: 76, backgroundImage: `url(${desktopPhotos(heroDeal.listing)[0]})` }} />
                    <div style={{ padding: '0 14px', minWidth: 0 }}><div style={{ fontSize: 13, fontWeight: 900, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>#{1} {heroDeal.listing?.title || 'Top-Objekt'}</div><div style={{ marginTop: 4, fontSize: 12, color: 'rgba(255,255,255,.66)' }}>{heroDeal.listing?.district}, {heroDeal.listing?.city} · {fmtMetricPrice(heroDeal.listing?.price)}</div></div>
                    <div style={{ paddingRight: 14 }}><DScorePill score={heroDeal.evaluation?.score || 0} /></div>
                  </button>}
                </div>
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                  <InvestorHeroMetric label="Objekte" value={summary.count || items.length || 0} />
                  <InvestorHeroMetric label="Top Score" value={summary.topScore || '—'} accent />
                  <InvestorHeroMetric label="Ø Netto" value={fmtPct(avgNet)} />
                  <InvestorHeroMetric label="Volumen" value={fmtMetricPrice(summary.totalValue)} />
                </div>
              </div>
            </DCard>
            <DCard padding={20}>
              <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 22 }}>Portfolio-Qualität</h3>
              <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <InvestorMini label="Ø Brutto" value={fmtPct(avgGross)} />
                <InvestorMini label="Ø Netto" value={fmtPct(avgNet)} />
                <InvestorMini label="Jahresmiete" value={fmtMetricPrice(annualIncome)} />
                <InvestorMini label="Ø Preis" value={fmtMetricPrice(summary.averagePrice)} />
              </div>
              <div style={{ marginTop: 18 }}>
                <TinyBars title="Score-Verteilung" data={distribution} />
              </div>
            </DCard>
          </div>

          <div style={{ display: 'grid', gridTemplateColumns: '1.55fr .45fr', gap: 18, alignItems: 'start' }}>
            <DCard padding={0}>
              <div style={{ padding: '18px 20px 12px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div><h2 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 24 }}>Priorisierte Deal-Liste</h2><div style={{ marginTop: 3, fontSize: 12, color: 'var(--ink-muted)' }}>Score · Yield · Cashflow · Risiko · CapEx</div></div>
                <DChip>{items.length} Bewertungen</DChip>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '58px 1.45fr 94px 92px 92px 100px 92px 34px', padding: '10px 20px', background: 'var(--surface-2)', borderTop: '1px solid var(--border)', borderBottom: '1px solid var(--border)', fontSize: 10.5, fontWeight: 900, color: 'var(--ink-muted)', letterSpacing: .05, textTransform: 'uppercase', gap: 12 }}>
                <span>Rang</span><span>Objekt</span><span>Preis</span><span>Netto</span><span>Cashflow</span><span>Risiko</span><span>Score</span><span />
              </div>
              {topDeals.map((item, i) => <InvestorRow key={item.id || item.adId || i} item={item} rank={i + 1} onOpen={() => goListing(item)} last={i === topDeals.length - 1} />)}
              {!topDeals.length && <div style={{ padding: 28, color: 'var(--ink-muted)' }}>Noch keine Investment-Bewertungen vorhanden.</div>}
            </DCard>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 18 }}>
              <DCard padding={20}>
                <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 20 }}>Investment-These</h3>
                <div style={{ marginTop: 12, fontSize: 13.5, lineHeight: 1.6, color: 'var(--ink-2)' }}>{heroDeal?.evaluation?.investment_thesis || heroDeal?.evaluation?.summary || 'Noch keine Investment-These vorhanden.'}</div>
                {heroDeal?.evaluation?.next_steps?.length > 0 && <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>{heroDeal.evaluation.next_steps.slice(0, 4).map((step, idx) => <div key={idx} style={{ display: 'flex', gap: 8, fontSize: 12.5, lineHeight: 1.45, color: 'var(--ink-2)' }}><span style={{ width: 20, height: 20, borderRadius: 999, background: 'var(--primary-soft)', color: 'var(--primary-ink)', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 900, flexShrink: 0 }}>{idx + 1}</span><span>{step}</span></div>)}</div>}
              </DCard>
              <DCard padding={20}>
                <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 20 }}>Top-Lagen</h3>
                <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>
                  {locations.slice(0, 6).map(loc => <LocationBar key={loc.city} loc={loc} />)}
                  {!locations.length && <div style={{ fontSize: 13, color: 'var(--ink-muted)' }}>Noch keine Standortdaten.</div>}
                </div>
              </DCard>
              <DCard padding={20}>
                <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 20 }}>Due-Diligence Fokus</h3>
                <InvestorChecklist items={topDeals.slice(0, 5)} />
              </DCard>
            </div>
          </div>
        </section>
      </main>
    </DesktopFrame>
  );
}

function InvestorHeroMetric({ label, value, accent = false }) {
  return <div style={{ padding: 13, borderRadius: 15, background: accent ? 'rgba(211,151,54,.22)' : 'rgba(255,255,255,.09)', border: '1px solid rgba(255,255,255,.14)' }}><div style={{ fontSize: 10, fontWeight: 900, color: accent ? 'var(--primary)' : 'rgba(255,255,255,.56)', textTransform: 'uppercase', letterSpacing: .06 }}>{label}</div><div style={{ marginTop: 5, fontFamily: 'var(--font-display)', fontSize: 24, lineHeight: 1 }}>{value || '—'}</div></div>;
}

function InvestorMini({ label, value, dark = false }) {
  return <div style={{ padding: '11px 10px', borderRadius: 14, background: dark ? 'rgba(255,255,255,.10)' : 'var(--surface-2)', border: dark ? '1px solid rgba(255,255,255,.14)' : '1px solid var(--border)' }}><div style={{ fontSize: 10, fontWeight: 900, color: dark ? 'rgba(255,255,255,.62)' : 'var(--ink-muted)', textTransform: 'uppercase', letterSpacing: .05 }}>{label}</div><div style={{ marginTop: 5, fontSize: 17, fontWeight: 900, color: dark ? '#fff' : 'var(--ink)', fontVariantNumeric: 'tabular-nums' }}>{value || '—'}</div></div>;
}

function TinyBars({ title, data = {} }) {
  const max = Math.max(1, ...Object.values(data || { x: 1 }));
  return <div><div style={{ fontSize: 11, fontWeight: 900, color: 'var(--ink-muted)', textTransform: 'uppercase', letterSpacing: .05 }}>{title}</div><div style={{ marginTop: 9, display: 'flex', flexDirection: 'column', gap: 8 }}>{Object.entries(data).map(([label, count]) => <div key={label}><div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 4 }}><span>{label}</span><strong>{count}</strong></div><div style={{ height: 7, borderRadius: 99, background: 'var(--surface-2)', overflow: 'hidden' }}><div style={{ width: `${(count / max) * 100}%`, height: '100%', background: 'linear-gradient(90deg, var(--primary), var(--primary-deep))' }} /></div></div>)}</div></div>;
}

function LocationBar({ loc }) {
  return <div><div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, marginBottom: 5 }}><strong>{loc.city}</strong><span style={{ color: 'var(--ink-muted)' }}>{loc.count} · {loc.share}%</span></div><div style={{ height: 8, borderRadius: 99, background: 'var(--surface-2)', overflow: 'hidden' }}><div style={{ width: `${loc.share || 0}%`, height: '100%', background: 'var(--ink)' }} /></div></div>;
}

function InvestorChecklist({ items = [] }) {
  const risks = items.flatMap(item => (item.evaluation?.risks || item.evaluation?.key_risks || []).slice(0, 2)).slice(0, 5);
  const list = risks.length ? risks : ['Mietannahmen prüfen', 'Hausgeld/Nebenkosten verifizieren', 'Energieausweis und Sanierungsbedarf prüfen'];
  return <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 9 }}>{list.map((text, i) => <div key={i} style={{ display: 'flex', gap: 9, fontSize: 12.5, lineHeight: 1.45, color: 'var(--ink-2)' }}><span style={{ width: 20, height: 20, borderRadius: 999, background: 'var(--primary-soft)', color: 'var(--primary-ink)', display: 'grid', placeItems: 'center', fontSize: 11, fontWeight: 900, flexShrink: 0 }}>{i + 1}</span><span>{text}</span></div>)}</div>;
}

function InvestorMobileCard({ item, rank, onOpen }) {
  const l = item.listing || {};
  const e = item.evaluation || {};
  const m = item.metrics || {};
  const cash = m.monthly_cash_flow?.net_cash_flow;
  const risks = (e.risks || e.key_risks || []).slice(0, 2);
  return <button onClick={onOpen} type="button" className="card" style={{ padding: 0, overflow: 'hidden', textAlign: 'left', border: '1px solid var(--border)', background: 'var(--surface)', color: 'var(--ink)', fontFamily: 'var(--font-sans)' }}>
    <div style={{ display: 'grid', gridTemplateColumns: '112px 1fr', minHeight: 124 }}>
      <div className="photo" style={{ backgroundImage: `url(${desktopPhotos(l)[0]})`, minHeight: 124 }} />
      <div style={{ padding: 13, minWidth: 0 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8, alignItems: 'start' }}>
          <span className="chip chip-accent" style={{ fontSize: 10, padding: '4px 7px' }}>#{rank}</span>
          <DScorePill score={e.score || l.aiScore || 0} size={28} />
        </div>
        <div style={{ marginTop: 8, fontSize: 14, fontWeight: 900, lineHeight: 1.2, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{l.title || 'Objekt'}</div>
        <div style={{ marginTop: 4, fontSize: 11.5, color: 'var(--ink-muted)' }}>{l.district}, {l.city}</div>
        <div style={{ marginTop: 8, fontSize: 12, lineHeight: 1.45, color: 'var(--ink-muted)', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{e.investment_thesis || e.summary || l.description || 'Investment-Bewertung verfügbar.'}</div>
        <div style={{ marginTop: 8, display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          <span className="chip" style={{ fontSize: 11 }}>{fmtMetricPrice(l.price)}</span>
          <span className="chip" style={{ fontSize: 11 }}>Netto {fmtPct(m.net_yield)}</span>
          <span className="chip" style={{ fontSize: 11, color: cash >= 0 ? 'var(--success)' : 'var(--danger)' }}>{cash != null ? `${fmtMetricPrice(cash)}/Mon.` : 'Cashflow —'}</span>
        </div>
      </div>
    </div>
    <div style={{ padding: '10px 13px 12px', borderTop: '1px solid var(--border)', display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 8 }}>
      <InvestorMini label="Miete/m²" value={m.rent_per_sqm ? `${Number(m.rent_per_sqm).toLocaleString('de-DE', { maximumFractionDigits: 1 })} €` : '—'} />
      <InvestorMini label="Faktor" value={m.kaufpreisfaktor ? Number(m.kaufpreisfaktor).toLocaleString('de-DE', { maximumFractionDigits: 1 }) : '—'} />
      <InvestorMini label="CapEx" value={fmtMetricPrice(m.estimated_capex_reserve_annual)} />
    </div>
    {risks.length > 0 && <div style={{ padding: '0 13px 13px', display: 'flex', flexDirection: 'column', gap: 5 }}>{risks.map((r, i) => <div key={i} style={{ fontSize: 11.5, color: 'var(--ink-muted)', lineHeight: 1.35 }}>⚠️ {r}</div>)}</div>}
  </button>;
}

function InvestorRow({ item, rank, onOpen, last }) {
  const l = item.listing || {};
  const e = item.evaluation || {};
  const m = item.metrics || {};
  const cash = m.monthly_cash_flow?.net_cash_flow;
  const risk = (e.risks || e.key_risks || [])[0] || (m.vacancy_risk_pct ? `Leerstand ${fmtPct(m.vacancy_risk_pct)}` : 'Keine Hauptrisiken');
  return (
    <button type="button" onClick={onOpen} style={{ width: '100%', display: 'grid', gridTemplateColumns: '58px 1.45fr 94px 92px 92px 100px 92px 34px', gap: 12, alignItems: 'center', padding: '13px 20px', border: 0, borderBottom: last ? 'none' : '1px solid var(--border)', background: 'var(--surface)', cursor: 'pointer', textAlign: 'left', fontFamily: 'var(--font-sans)', color: 'var(--ink)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}><span style={{ fontSize: 12, fontWeight: 900, color: 'var(--ink-muted)' }}>#{rank}</span><div className="photo" style={{ width: 38, height: 38, borderRadius: 10, backgroundImage: `url(${desktopPhotos(l)[0]})` }} /></div>
      <div style={{ minWidth: 0 }}><div style={{ fontSize: 13.5, fontWeight: 900, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{l.title}</div><div style={{ fontSize: 11.5, color: 'var(--ink-muted)', marginTop: 2 }}>{l.district}, {l.city} · {l.area || '—'} m² · {l.rooms || '—'} Zi.</div></div>
      <strong style={{ fontFamily: 'var(--font-display)', fontSize: 18, fontWeight: 500 }}>{fmtMetricPrice(l.price || 0)}</strong>
      <span style={{ fontWeight: 800 }}>{fmtPct(m.net_yield ?? m.gross_yield)}</span>
      <span style={{ fontWeight: 800, color: cash >= 0 ? 'var(--success)' : 'var(--danger)' }}>{cash != null ? fmtMetricPrice(cash) : '—'}</span>
      <span style={{ fontSize: 11.5, color: 'var(--ink-muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{risk}</span>
      <DScorePill score={e.score || 0} />
      <I.chevR style={{ color: 'var(--ink-faint)' }} />
    </button>
  );
}

function InvestorCompareScreen({ ctx, active = 'investor', onNav = () => {}, ids = [], topBarProps = {} }) {
  const [payload, setPayload] = React.useState(null);
  React.useEffect(() => {
    ctx.loadInvestorCompare?.(ids).then(setPayload).catch(() => {});
  }, [ids.join(',')]);
  const items = payload?.items || ctx.investor?.compare?.items || [];
  const content = (
    <DCard padding={0}>
      <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 120px 120px 120px 120px', padding: '10px 20px', background: 'var(--surface-2)', borderBottom: '1px solid var(--border)', fontSize: 10.5, fontWeight: 800, color: 'var(--ink-muted)', letterSpacing: 0.06, textTransform: 'uppercase' }}>
        <span>Objekt</span><span>Preis</span><span>Score</span><span>Brutto</span><span>Cap Rate</span>
      </div>
      {items.map((item, i) => <button type="button" key={item.id || item.adId || i} onClick={() => ctx.navigate('listing', { id: item.adId || desktopListingId(item.listing) })} style={{ display: 'grid', gridTemplateColumns: '1.6fr 120px 120px 120px 120px', width: '100%', padding: '15px 20px', border: 0, borderBottom: i < items.length - 1 ? '1px solid var(--border)' : 'none', alignItems: 'center', fontSize: 13, textAlign: 'left', background: 'var(--surface)', color: 'var(--ink)', cursor: 'pointer', fontFamily: 'var(--font-sans)' }}>
        <span style={{ fontWeight: 800 }}>{item.listing?.title || 'Objekt'}</span>
        <span>{fmtPriceShort(item.listing?.price || 0)}</span>
        <span>{item.evaluation?.score || '—'}</span>
        <span>{fmtPct(item.metrics?.gross_yield)}</span>
        <span>{fmtPct(item.metrics?.cap_rate)}</span>
      </button>)}
    </DCard>
  );
  if (!ctx.isDesktop) return <div className="phone-scroll" style={{ padding: 20 }}>{content}</div>;
  return (
    <DesktopFrame url="app.immobot.de/investor/vergleich">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar crumb={['Analyse', 'Vergleich']} title="Objektvergleich" subtitle="Bis zu vier Investment-Kandidaten nebeneinander" {...topBarProps} />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px' }}>{content}</section>
      </main>
    </DesktopFrame>
  );
}

function DesktopProfileScreen({ ctx, active = 'profile', onNav = () => {}, topBarProps = {} }) {
  const user = ctx.user || {};
  const inquiryRef = React.useRef(null);
  const [form, setForm] = React.useState(() => ({
    name: user.name || '',
    email: user.email || '',
    telegram_chat_id: user.telegram_chat_id || '',
    ebay_userid: user.ebay_userid || '',
    ebay_contactname: user.ebay_contactname || '',
    ebay_email: user.ebay_email || '',
    ebay_password: '',
    default_reply_message: user.default_reply_message || '',
    rental_profile: { ...RENTAL_PROFILE_DEFAULTS, ...(user.rental_profile || {}) },
  }));
  const [message, setMessage] = React.useState(null);
  const update = (key, value) => setForm(v => ({ ...v, [key]: value }));
  const updateRentalProfile = (key, value) => setForm(v => ({
    ...v,
    rental_profile: { ...RENTAL_PROFILE_DEFAULTS, ...(v.rental_profile || {}), [key]: value },
  }));
  const save = async () => {
    setMessage(null);
    await ctx.updateProfile?.(form);
    setMessage('Gespeichert');
    update('ebay_password', '');
  };
  React.useEffect(() => {
    const shouldScroll = sessionStorage.getItem('immobotScrollInquiryProfile') === '1' || window.location.hash.includes('inquiry=1');
    if (!shouldScroll) return;
    sessionStorage.removeItem('immobotScrollInquiryProfile');
    setTimeout(() => inquiryRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 120);
  }, []);
  return (
    <DesktopFrame url="app.immobot.de/profil">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar crumb={['Konto', 'Profil']} title="Profil" subtitle={user.email} actions={<><DButton kind="outline" icon={<I.shield />} onClick={() => ctx.navigate('security')}>Sicherheit</DButton><DButton kind="primary" icon={<I.logout />} onClick={ctx.signOut}>Abmelden</DButton></>} {...topBarProps} />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px', display: 'grid', gridTemplateColumns: '.8fr 1.2fr', gap: 18, alignItems: 'start' }}>
          <DCard padding={22}>
            <div style={{ width: 64, height: 64, borderRadius: 999, background: 'linear-gradient(135deg, var(--primary), var(--primary-deep))', color: '#fff', display: 'grid', placeItems: 'center', fontFamily: 'var(--font-display)', fontSize: 28 }}>{user.initials || 'I'}</div>
            <h2 style={{ margin: '14px 0 4px', fontFamily: 'var(--font-display)', fontSize: 28, fontWeight: 500 }}>{user.name || 'ImmoBot Nutzer'}</h2>
            <div style={{ color: 'var(--ink-muted)', fontSize: 13 }}>{user.email}</div>
          </DCard>
          <DCard padding={22}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
              <h3 style={{ margin: 0, fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500 }}>Plan: {ctx.plan?.label || 'Free'}</h3>
              {ctx.plan?.key !== 'premium' && ctx.plan?.billing_configured && (
                <DButton kind="primary" onClick={ctx.startCheckout}>⚡ Premium holen</DButton>
              )}
              {ctx.plan?.key === 'premium' && ctx.plan?.billing_configured && !user.isAdmin && (
                <DButton kind="outline" onClick={ctx.openBillingPortal}>Abo verwalten</DButton>
              )}
            </div>
            <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
              <DStat label="Tokens übrig" value={ctx.plan?.tokens_unlimited ? '∞' : (ctx.plan?.tokens_balance ?? 0)} accent />
              <DStat label="Aktive Suchen" value={ctx.taskLimit?.current_tasks || 0} />
              <DStat label="Freie Slots" value={ctx.taskLimit?.remaining_slots ?? '∞'} />
              <DStat label="KI übrig" value={ctx.taskLimit?.remaining_evaluations ?? '∞'} />
            </div>
            {!ctx.plan?.tokens_unlimited && (ctx.plan?.tokens_balance ?? 0) <= 3 && (
              <div style={{ marginTop: 12, fontSize: 13, color: 'var(--warning, #b7791f)' }}>
                {ctx.plan?.tokens_balance <= 0
                  ? 'Deine Tokens sind aufgebraucht — neue Treffer werden nicht mehr gemeldet.'
                  : 'Deine Tokens sind fast aufgebraucht.'} Mit Premium erhältst du unbegrenzte Sofort-Benachrichtigungen.
              </div>
            )}
            {user.emailVerified === false && (
              <div style={{ marginTop: 12, fontSize: 13, color: 'var(--warning, #b7791f)' }}>
                Bitte bestätige deine E-Mail-Adresse, um Suchen anzulegen.{' '}
                <a href="#" onClick={(e) => { e.preventDefault(); ctx.resendVerification?.(); }} style={{ fontWeight: 700 }}>E-Mail erneut senden</a>
              </div>
            )}
          </DCard>
          <DCard padding={22} style={{ gridColumn: '1 / -1' }}>
            <h3 style={{ margin: '0 0 16px', fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500 }}>Basisdaten</h3>
            <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: 12 }}>
              <ProfileInput label="Name" value={form.name} onChange={v => update('name', v)} />
              <ProfileInput label="E-Mail" value={form.email} onChange={v => update('email', v)} type="email" />
              <div>
                <div style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-muted)', marginBottom: 6, textTransform: 'uppercase' }}>Telegram</div>
                {user.telegram_connected ? (
                  <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                    <span className="chip chip-accent" style={{ fontSize: 12 }}>✓ Verbunden</span>
                    <DButton kind="outline" onClick={ctx.disconnectTelegram}>Trennen</DButton>
                  </div>
                ) : (
                  <DButton kind="primary" onClick={ctx.connectTelegram}>Mit Telegram verbinden</DButton>
                )}
              </div>
              <ProfileInput label="Kontaktname" value={form.ebay_contactname} onChange={v => update('ebay_contactname', v)} />
              <ProfileInput label="Kleinanzeigen User-ID" value={form.ebay_userid} onChange={v => update('ebay_userid', v)} />
              <ProfileInput label="Kleinanzeigen E-Mail" value={form.ebay_email} onChange={v => update('ebay_email', v)} type="email" />
            </div>
            <div style={{ marginTop: 12 }}>
              <ProfileTextarea label="Standardnachricht" value={form.default_reply_message} onChange={v => update('default_reply_message', v)} />
            </div>
          </DCard>
          <div ref={inquiryRef} style={{ gridColumn: '1 / -1', scrollMarginTop: 12 }}>
            <DCard padding={22}>
              <h3 style={{ margin: '0 0 16px', fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500 }}>Anfrageprofil</h3>
              <InquiryProfileFields value={form.rental_profile} onChange={updateRentalProfile} columns={2} />
              {message && <div style={{ marginTop: 12, color: 'var(--success)', fontSize: 13, fontWeight: 800 }}>{message}</div>}
              <div style={{ marginTop: 14 }}>
                <DButton kind="primary" onClick={save}>Profil speichern</DButton>
              </div>
            </DCard>
          </div>
        </section>
      </main>
    </DesktopFrame>
  );
}

function AccountSecurityScreen({ ctx, active = 'profile', onNav = () => {}, topBarProps = {} }) {
  const [form, setForm] = React.useState({ current_password: '', password: '', password_confirmation: '' });
  const [deletePassword, setDeletePassword] = React.useState('');
  const [message, setMessage] = React.useState(null);
  const update = (key, value) => setForm(v => ({ ...v, [key]: value }));
  const save = async () => {
    setMessage(null);
    await ctx.updatePassword?.(form);
    setMessage('Passwort aktualisiert.');
    setForm({ current_password: '', password: '', password_confirmation: '' });
  };
  const remove = async () => {
    if (confirm('Konto wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.')) {
      await ctx.deleteProfile?.(deletePassword);
    }
  };
  if (!ctx.isDesktop) {
    return (
      <div style={{ height: '100%', background: 'var(--bg)' }}>
        <TopBar title="Sicherheit" onBack={ctx.back} />
        <div className="phone-scroll" style={{ padding: '0 16px 100px' }}>
          <div className="card" style={{ padding: 16 }}>
            <ProfileInput label="Aktuelles Passwort" value={form.current_password} onChange={v => update('current_password', v)} type="password" />
            <ProfileInput label="Neues Passwort" value={form.password} onChange={v => update('password', v)} type="password" />
            <ProfileInput label="Wiederholen" value={form.password_confirmation} onChange={v => update('password_confirmation', v)} type="password" />
            <button onClick={save} className="btn btn-primary" style={{ width: '100%', marginTop: 12 }}>Speichern</button>
          </div>
        </div>
      </div>
    );
  }
  return (
    <DesktopFrame url="app.immobot.de/profil/sicherheit">
      <Sidebar active={active} notifBadge={ctx.notifBadge || 0} onNav={onNav} user={ctx.user} taskLimit={ctx.taskLimit} />
      <main style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0, background: 'var(--bg)' }}>
        <DTopBar crumb={['Konto', 'Sicherheit']} title="Sicherheit & Konto" subtitle="Passwort ändern oder Konto löschen" {...topBarProps} />
        <section className="desktop-scroll" style={{ flex: 1, overflowY: 'auto', padding: '24px 32px 40px', display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 18, alignItems: 'start' }}>
          <DCard padding={22}>
            <h3 style={{ margin: '0 0 16px', fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500 }}>Passwort ändern</h3>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              <DField label="Aktuelles Passwort"><DInput type="password" value={form.current_password} onChange={v => update('current_password', v)} /></DField>
              <DField label="Neues Passwort"><DInput type="password" value={form.password} onChange={v => update('password', v)} /></DField>
              <DField label="Wiederholen"><DInput type="password" value={form.password_confirmation} onChange={v => update('password_confirmation', v)} /></DField>
              {message && <div style={{ color: 'var(--success)', fontSize: 13, fontWeight: 800 }}>{message}</div>}
              <DButton kind="primary" onClick={save}>Passwort speichern</DButton>
            </div>
          </DCard>
          <DCard padding={22} style={{ borderColor: 'var(--danger-soft)' }}>
            <h3 style={{ margin: '0 0 8px', fontFamily: 'var(--font-display)', fontSize: 22, fontWeight: 500 }}>Konto löschen</h3>
            <p style={{ margin: '0 0 16px', color: 'var(--ink-muted)', fontSize: 13, lineHeight: 1.55 }}>Löscht das Konto, Suchen und gespeicherte Einstellungen dauerhaft.</p>
            <DField label="Passwort bestätigen"><DInput type="password" value={deletePassword} onChange={setDeletePassword} /></DField>
            <div style={{ marginTop: 14 }}><DButton kind="outline" icon={<I.trash />} onClick={remove}>Konto löschen</DButton></div>
          </DCard>
        </section>
      </main>
    </DesktopFrame>
  );
}

Object.assign(window, {
  DesktopRouteScreen,
  InvestorScreen,
  InvestorCompareScreen,
  AccountSecurityScreen,
  fmtMetricPrice,
  fmtPct,
});
