// Keynara subpages
const { useState: usS, useEffect: usE } = React;
// ============ HOW IT WORKS PAGE ============
function HowPage({ t, lang, onStart }) {
return (
{t.how_page.eyebrow}
{t.how_page.title}
{t.how_page.lead}
{[
{ n: '01', t: lang === 'de' ? 'LLM-Dialogsystem' : 'LLM dialog system', d: lang === 'de' ? 'Kontextverständnis, adaptive Rückfragen, Multi-Turn-Logik.' : 'Context understanding, adaptive follow-ups, multi-turn logic.' },
{ n: '02', t: lang === 'de' ? 'Matching-Algorithmen' : 'Matching algorithms', d: lang === 'de' ? 'Gewichtete Relevanz-Scores basierend auf Nutzerpräferenzen und Marktdaten.' : 'Weighted relevance scores based on preferences and market data.' },
{ n: '03', t: lang === 'de' ? 'Datenaggregation' : 'Data aggregation', d: lang === 'de' ? 'Echtzeit-Aggregation aus Portalen, Makler-APIs und Marktdaten.' : 'Real-time aggregation from portals, broker APIs and market data.' },
].map((x, i) => (
))}
);
}
// ============ PRICING ============
function PricingPage({ t, onStart }) {
return (
{t.pricing.eyebrow}
{t.pricing.title}
{t.pricing.lead}
{t.pricing.plans.map((p, i) => (
))}
);
}
function Plan({ p, onStart }) {
return (
{p.highlight && (
Most popular
)}
{p.name}
{p.price}
{p.per}
{p.d}
{p.cta}
{p.features.map((f, j) => (
{f}
))}
);
}
// ============ INVESTORS PAGE ============
function InvestorsPage({ t, lang, onStart }) {
return (
{t.investors.eyebrow}
{t.investors.title}
{t.investors.lead}
{[
{ tag: 'B2B', n: '01', t: lang === 'de' ? 'Lead-Generierung' : 'Lead generation', d: lang === 'de' ? 'Qualifizierte Käufer- und Mieter-Leads für Makler und Plattformen. CPC/CPL-Basis.' : 'Qualified buyer and renter leads for brokers and platforms. CPC/CPL basis.' },
{ tag: 'B2B', n: '02', t: lang === 'de' ? 'Premium-Platzierungen' : 'Premium placements', d: lang === 'de' ? 'Hervorgehobene Listings für Makler mit Premium-Accounts. Monatsmodell.' : 'Highlighted listings for brokers with premium accounts. Monthly subscription.' },
{ tag: 'B2C', n: '03', t: lang === 'de' ? 'Power-User Subscription' : 'Power-User subscription', d: lang === 'de' ? 'Erweiterte Analyse- und Suchfunktionen für Investoren und Vielsucher.' : 'Advanced analytics and search features for investors and heavy searchers.' },
].map((x, i) => (
{x.tag}
{x.n}
{x.t}
{x.d}
))}
{lang === 'de' ? 'Investor Relations' : 'Investor Relations'}
{lang === 'de' ? 'Pitch-Deck und Finanzmodell auf Anfrage.' : 'Pitch deck and financial model on request.'}
{lang === 'de' ? 'Wir teilen Details unter NDA. Schreib uns kurz, wer du bist — wir antworten innerhalb von 48 Stunden.' : 'We share details under NDA. Send a short intro — we reply within 48 hours.'}
investors@keynara.com
);
}
function CompetitiveMatrix({ t, lang }) {
return (
{/* Axes */}
{lang === 'de' ? 'RELEVANZ' : 'RELEVANCE'} ↑
→ {lang === 'de' ? 'DATENMENGE' : 'DATA VOLUME'}
{/* Quadrant grid */}
{/* Competitors */}
{[
{ x: 500, y: 300, n: 'willhaben', type: 'comp' },
{ x: 540, y: 260, n: 'ImmoScout24', type: 'comp' },
{ x: 480, y: 320, n: 'Idealista', type: 'comp' },
{ x: 460, y: 340, n: 'Immowelt', type: 'comp' },
{ x: 140, y: 150, n: lang === 'de' ? 'Lokale Makler' : 'Local brokers', type: 'comp' },
{ x: 180, y: 240, n: lang === 'de' ? 'Kleine Portale' : 'Small portals', type: 'comp' },
{ x: 420, y: 80, n: 'keynara', type: 'key' },
].map((p, i) => (
{p.type === 'key' && (
)}
{p.n}
))}
{/* Quadrant labels */}
{lang === 'de' ? 'WENIG DATEN · HOHE RELEVANZ' : 'LOW DATA · HIGH RELEVANCE'}
{lang === 'de' ? 'VIEL DATEN · HOHE RELEVANZ' : 'HIGH DATA · HIGH RELEVANCE'}
{lang === 'de' ? 'WENIG DATEN · GERINGE RELEVANZ' : 'LOW DATA · LOW RELEVANCE'}
{lang === 'de' ? 'VIEL DATEN · GERINGE RELEVANZ' : 'HIGH DATA · LOW RELEVANCE'}
);
}
function VisionRoadmap({ lang }) {
const phases = [
{ n: '01', t: lang === 'de' ? 'Phase 1 · 2026' : 'Phase 1 · 2026', title: lang === 'de' ? 'Immobilien DACH & IT' : 'Real estate DACH & IT', d: lang === 'de' ? 'Marktführer in KI-gestützter Immobiliensuche im deutschsprachigen Raum und Italien.' : 'Leader in AI-powered real-estate search across DACH and Italy.' },
{ n: '02', t: lang === 'de' ? 'Phase 2 · 2027' : 'Phase 2 · 2027', title: lang === 'de' ? 'Finanzierung & Versicherung' : 'Financing & insurance', d: lang === 'de' ? 'Erweiterung auf angrenzende Bereiche: Baufinanzierung und Versicherungsvergleich.' : 'Expansion into adjacent domains: mortgage and insurance comparison.' },
{ n: '03', t: lang === 'de' ? 'Phase 3 · 2028+' : 'Phase 3 · 2028+', title: lang === 'de' ? 'Universeller Agent' : 'Universal agent', d: lang === 'de' ? 'Langfristig: Keynara als Entscheidungs-Agent für große Lebensplanung.' : 'Long-term: Keynara as a decision agent for major life planning.' },
];
return (
{phases.map((p, i) => (
{p.n}
{p.t}
{p.title}
{p.d}
))}
);
}
// ============ ABOUT ============
function AboutPage({ t, lang }) {
const values = [
{ t: lang === 'de' ? 'Präzision' : 'Precision', d: lang === 'de' ? 'Wir liefern gezielte, relevante Ergebnisse. Keine Masse, nur Qualität.' : 'We deliver targeted, relevant results. Not volume — quality.' },
{ t: lang === 'de' ? 'Klarheit' : 'Clarity', d: lang === 'de' ? 'Komplexes wird verständlich. Kein Fachjargon, keine Floskeln.' : 'Complexity made understandable. No jargon, no fluff.' },
{ t: lang === 'de' ? 'Effizienz' : 'Efficiency', d: lang === 'de' ? 'Weniger Aufwand, bessere Entscheidungen. Zeit ist das wertvollste Gut.' : 'Less effort, better decisions. Time is the most valuable asset.' },
{ t: lang === 'de' ? 'Vertrauen' : 'Trust', d: lang === 'de' ? 'Kompetente Begleitung auf dem wichtigsten Weg im Leben.' : 'Competent guidance on the most important journey in life.' },
];
const team = [
{ n: 'Léa Marchetti', r: lang === 'de' ? 'Mitgründerin, CEO' : 'Co-founder, CEO', init: 'LM' },
{ n: 'Jonas Wieland', r: lang === 'de' ? 'Mitgründer, CTO' : 'Co-founder, CTO', init: 'JW' },
{ n: 'Sofia Romano', r: lang === 'de' ? 'Head of Product' : 'Head of Product', init: 'SR' },
{ n: 'Daniel Kofler', r: lang === 'de' ? 'Head of Data' : 'Head of Data', init: 'DK' },
];
return (
{t.about.eyebrow}
{t.about.title}
{t.about.lead}
{values.map((v, i) => (
))}
{team.map((p, i) => (
))}
);
}
// ============ BLOG ============
function BlogPage({ t, lang }) {
return (
{t.blog.eyebrow}
{t.blog.title}
{t.blog.lead}
{/* Featured post */}
{t.blog.posts[0].tag}
{t.blog.posts[0].t}
{t.blog.posts[0].d}
{t.blog.posts[0].date}
·
{t.blog.posts[0].read}
{lang === 'de' ? 'Featured' : 'Featured'}
{/* Grid of posts */}
{t.blog.posts.slice(1).map((p, i) => (
{p.tag}
{p.date}
{p.t}
{p.d}
{p.read}
))}
);
}
function PostCover({ idx }) {
const palettes = [
['#EEF1F4', '#1A2B3C', '#00C9A7'],
['#1A2B3C', '#FFFFFF', '#00C9A7'],
['#00C9A7', '#1A2B3C', '#FFFFFF'],
['#EEF1F4', '#4A6278', '#1A2B3C'],
['#FFFFFF', '#00C9A7', '#1A2B3C'],
];
const p = palettes[idx % palettes.length];
const patterns = [
,
,
,
,
,
];
return (
{patterns[idx % patterns.length]}
);
}
// ============ CONTACT ============
function ContactPage({ t, lang }) {
const [sent, setSent] = usS(false);
const [form, setForm] = usS({ name: '', email: '', org: '', topic: t.contact.topics[0], msg: '' });
function update(k, v) { setForm(f => ({ ...f, [k]: v })); }
return (
{t.contact.eyebrow}
{t.contact.title}
{t.contact.lead}
{lang === 'de' ? 'Büros' : 'Offices'}
{t.contact.offices.map((o, i) => (
))}
);
}
const inputStyle = {
width: '100%', padding: '12px 14px', fontFamily: 'inherit', fontSize: 15,
border: '1px solid var(--line)', borderRadius: 10, background: 'var(--fog-2)',
color: 'var(--navy)', outline: 'none', transition: 'border-color 160ms',
};
function Field({ label, children }) {
return (
{label}
{children}
);
}
// ============ AGENT PAGE ============
function AgentPage({ t, lang }) {
const ta = t.agent;
// !! WICHTIG: Nach Railway-Deployment diese URL anpassen !!
const API_URL = "https://web-production-c80b9.up.railway.app";
const [email, setEmail] = React.useState('');
const [status, setStatus] = React.useState('idle'); // idle | loading | analysiert | fertig | fehler
const [statusText, setStatusText] = React.useState('');
const [jobId, setJobId] = React.useState(null);
const [kundenName, setKundenName] = React.useState('');
const [fehler, setFehler] = React.useState('');
const pollingRef = React.useRef(null);
async function verarbeiten() {
if (!email.trim()) { setFehler(lang === 'de' ? 'Bitte E-Mail-Text einfügen.' : 'Please paste an e-mail text.'); return; }
setFehler(''); setStatus('loading'); setStatusText(ta.status_1);
try {
const r = await fetch(API_URL + '/verarbeiten', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
});
const d = await r.json();
if (d.fehler) { setFehler(d.fehler); setStatus('idle'); return; }
setJobId(d.job_id);
startePolling(d.job_id);
} catch(e) {
setFehler('Verbindungsfehler: ' + e.message); setStatus('idle');
}
}
function startePolling(id) {
pollingRef.current = setInterval(async () => {
try {
const r = await fetch(API_URL + '/status/' + id);
const d = await r.json();
if (d.status === 'analysiert') { setStatus('analysiert'); setStatusText(ta.status_2); }
else if (d.status === 'fertig') { clearInterval(pollingRef.current); setKundenName(d.name || ''); setStatus('fertig'); }
else if (d.status === 'fehler') { clearInterval(pollingRef.current); setFehler(d.meldung || 'Fehler'); setStatus('idle'); }
} catch { clearInterval(pollingRef.current); setFehler('Verbindungsfehler'); setStatus('idle'); }
}, 2500);
}
const safeName = (kundenName || 'angebot').toLowerCase().replace(/\s+/g, '_');
return (
{/* Header */}
{ta.eyebrow}
{ta.title}
{ta.lead}
{/* Generator Card */}
{/* Label */}
{ta.label}
{/* Textarea */}
setEmail(e.target.value)}
placeholder={ta.placeholder}
rows={9}
style={{
width: '100%',
border: '1px solid var(--line)',
borderRadius: 12,
padding: '14px 16px',
fontFamily: 'inherit',
fontSize: 14,
lineHeight: 1.65,
color: 'var(--navy)',
background: 'var(--fog-2)',
resize: 'vertical',
outline: 'none',
transition: 'border-color 160ms',
}}
onFocus={e => e.target.style.borderColor = 'var(--mint-dark)'}
onBlur={e => e.target.style.borderColor = 'var(--line)'}
/>
{/* Button */}
{ta.btn}
{/* Fehler */}
{fehler && (
{fehler}
)}
{/* Ladestatus */}
{(status === 'loading' || status === 'analysiert') && (
)}
{/* Ergebnis */}
{status === 'fertig' && (
{[['DE','🇩🇪','Deutsch'],['IT','🇮🇹','Italiano'],['EN','🇬🇧','English']].map(([code, flag, label]) => (
{ e.currentTarget.style.borderColor = 'var(--mint-dark)'; e.currentTarget.style.background = 'var(--fog-2)'; }}
onMouseLeave={e => { e.currentTarget.style.borderColor = 'var(--line)'; e.currentTarget.style.background = 'white'; }}
>
{flag}
{label}
))}
{ta.note}
)}
);
}
Object.assign(window, { HowPage, PricingPage, InvestorsPage, AboutPage, BlogPage, ContactPage, AgentPage });