// Keynara Hero — bold typographic hero + working inline chat agent + property card stream
const { useState: useS, useEffect: useE, useRef: useR, useMemo: useM } = React;
// Mock city dataset
const MOCK_LISTINGS = [
{ id: 1, city: 'Wien', zip: '1120', type: 'Kauf', rooms: 3, area: 110, price: 498000, rate: null, match: 96, tag: 'Top Match', note: 'Ruhig, Balkon West' },
{ id: 2, city: 'Wien', zip: '1090', type: 'Kauf', rooms: 3, area: 95, price: 475000, rate: null, match: 91, tag: 'Gute Lage', note: 'U4/U6, renoviert' },
{ id: 3, city: 'Wien', zip: '1030', type: 'Kauf', rooms: 3, area: 102, price: 489000, rate: null, match: 88, tag: 'Wertstabil', note: 'Altbau, hohe Decken' },
{ id: 4, city: 'Berlin', zip: '10117', type: 'Miete', rooms: 2, area: 62, price: 1490, rate: 'mtl.', match: 94, tag: 'Neu', note: 'Mitte, ab Mai' },
{ id: 5, city: 'Berlin', zip: '10115', type: 'Miete', rooms: 2, area: 58, price: 1390, rate: 'mtl.', match: 90, tag: 'Beliebt', note: 'Rosenthaler Platz' },
{ id: 6, city: 'Berlin', zip: '10178', type: 'Miete', rooms: 2, area: 65, price: 1550, rate: 'mtl.', match: 87, tag: 'Direktlage', note: 'Hackescher Markt' },
{ id: 7, city: 'Milano', zip: '20121', type: 'Investment', rooms: 3, area: 85, price: 620000, rate: null, match: 93, tag: '4.3% Rendite', note: 'Brera, vermietet' },
{ id: 8, city: 'Milano', zip: '20154', type: 'Investment', rooms: 2, area: 62, price: 420000, rate: null, match: 89, tag: '4.1% Rendite', note: 'Isola, Nähe Tram' },
{ id: 9, city: 'Milano', zip: '20145', type: 'Investment', rooms: 3, area: 90, price: 580000, rate: null, match: 85, tag: '3.9% Rendite', note: 'Sempione, Park' },
];
function detectScenario(text) {
const s = text.toLowerCase();
if (/milano|mail|invest|rendite|yield|milan/.test(s)) return 'milan';
if (/berlin|miete|rent/.test(s)) return 'berlin';
return 'vienna';
}
function scenarioListings(key) {
if (key === 'berlin') return MOCK_LISTINGS.slice(3, 6);
if (key === 'milan') return MOCK_LISTINGS.slice(6, 9);
return MOCK_LISTINGS.slice(0, 3);
}
function Hero({ t, lang, onFocus }) {
// Chat state
const [stage, setStage] = useS('idle'); // idle | typing-user | agent-asking | typing-user-2 | agent-thinking | results
const [value, setValue] = useS('');
const [messages, setMessages] = useS([]);
const [listings, setListings] = useS([]);
const [activeSuggest, setActiveSuggest] = useS(0);
const bodyRef = useR(null);
useE(() => {
if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
}, [messages, stage]);
// Idle demo: rotate suggestions
useE(() => {
if (stage !== 'idle') return;
const id = setInterval(() => setActiveSuggest(i => (i + 1) % t.hero.suggestions.length), 2800);
return () => clearInterval(id);
}, [stage, t.hero.suggestions.length]);
function submit(customText) {
const text = (customText ?? value).trim();
if (!text) return;
setValue('');
const scenario = detectScenario(text);
setMessages([{ role: 'user', text }]);
setStage('agent-asking');
setTimeout(() => {
const ask = lang === 'de'
? 'Verstanden. Kauf oder Miete? In welcher Stadt und mit welchem Budget?'
: 'Got it. Buy or rent? Which city and what budget?';
setMessages(m => [...m, { role: 'agent', text: ask }]);
}, 900);
setTimeout(() => {
const reply = lang === 'de' ? 'Budget steht, Lage passt — zeig mir die Treffer.' : 'Budget set, location fits — show me the matches.';
setMessages(m => [...m, { role: 'user', text: reply }]);
}, 2400);
setTimeout(() => {
setStage('agent-thinking');
}, 3000);
setTimeout(() => {
const intro = lang === 'de'
? 'Hier sind drei Treffer, nach Relevanz gewichtet:'
: 'Here are three matches, weighted by relevance:';
setMessages(m => [...m, { role: 'agent', text: intro, withResults: true }]);
setListings(scenarioListings(scenario));
setStage('results');
}, 4200);
}
function reset() {
setMessages([]);
setListings([]);
setValue('');
setStage('idle');
}
return (
{t.hero.sub}
{t.hero.title_1}
{t.hero.title_2}
{t.hero.title_3}