// ==========================================================================
// LandingHome — Liquid Glass zero-state.
//
// Renders when a thread has 0 messages AND the canvas is hidden.
// Voice: a translucent studio panel floating over the iridescent stage.
// Greeting period word ("night", "morning") picks up the iris gradient as
// a wow-moment. Composer + starter tiles are glass surfaces with shimmer.
// ==========================================================================
function LandingHome() {
const { me, threads, sendMessage, uploadFiles, currentThread, route, navigate, streaming } = useStore();
const [value, setValue] = React.useState('');
const fileInputRef = React.useRef(null);
const disabled = !route.threadId || streaming.active;
const hour = new Date().getHours();
const period = hour < 5 ? 'night' : hour < 12 ? 'morning' : hour < 17 ? 'afternoon' : hour < 22 ? 'evening' : 'night';
const firstName = (me && me.name) ? me.name.split(' ')[0] : 'there';
const send = () => {
const v = value.trim();
if (!v) return;
setValue('');
sendMessage(v);
};
const onFiles = (files) => {
const arr = Array.from(files);
if (arr.length) uploadFiles(arr);
};
const starters = [
{ kicker: 'Brame', label: 'Dry January push', prompt: 'Launch a Dry January push for BRAME. 3 concepts, Meta + TikTok, approved claims only.' },
{ kicker: 'Craft Heritage', label: 'Post-launch readout', prompt: 'Readout of last month’s Craft Heritage launch. Which creatives won, which fatigued, what to double down on.' },
{ kicker: 'Q2 panel', label: 'Refresh', prompt: 'Refresh the consumer panel for Q2 — tilt toward occasion-led drinkers, keep the current skews.' },
{ kicker: 'LinkedIn', label: 'Creator brief', prompt: 'Write a 1-page creator brief for a LinkedIn-native founder story for BRAME.' },
{ kicker: 'Low-cal RTD', label: 'Naming sprint', prompt: 'Run a naming sprint for a low-calorie RTD. 15 candidates, 3 territories, availability checked.' },
];
const recentThreads = (threads || []).filter(t => t.id !== currentThread.id).slice(0, 6);
const brands = [
{ id: 'b_brame', name: 'BRAME', kicker: 'Botanical aperitif · 3 SKU' },
{ id: 'b_craft', name: 'Craft Heritage', kicker: 'Aged spirits · Q3 launch' },
{ id: 'b_loop', name: 'Loop Foods', kicker: 'Plant-forward · house brand' },
{ id: 'b_veldt', name: 'Veldt', kicker: 'Single-origin · on-trade' },
];
const subline =
period === 'morning' ? 'What are we launching today?'
: period === 'afternoon' ? 'Pick up where you left off — or start something new.'
: period === 'evening' ? 'A quiet hour for the big thinking.'
: 'Burning the late shift — what are we cracking?';
const today = new Date();
const folio = `Vol. ${String(today.getFullYear()).slice(-2)}.${String(today.getMonth() + 1).padStart(2, '0')} · No. ${String(today.getDate()).padStart(2, '0')}`;
const dateLine = today.toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' });
return (
{/* ── Folio bar ─────────────────────────────────────────────────── */}
{/* ── Hero: greeting + glass composer + starter tiles ──────────── */}
Good {period}, {firstName}.
{subline}
{/* Composer — glass desk, iridescent send pill */}
{ onFiles(e.target.files); e.target.value = ''; }} />
{ e.preventDefault(); }}
onDrop={(e) => { e.preventDefault(); onFiles(e.dataTransfer.files); }}
className="glass-strong"
style={{
width: '100%',
borderRadius: 'var(--radius-lg)',
padding: '20px 24px 18px',
display: 'flex', flexDirection: 'column', gap: 12,
}}>
Brief desk
Open
{/* Starting points — glass tiles with iridescent hover */}
Starting points
{starters.map((s, i) => (
sendMessage(s.prompt)} />
))}
{/* ── Lower zone: Lately + Brands as glass columns ────────────── */}
({
id: t.id,
title: t.title,
meta: relTimeLanding(t.updatedAt) + (t.messageCount ? ` · ${t.messageCount} msg` : ''),
onOpen: () => navigate(`/t/${t.id}`),
}))}
/>
({
id: b.id,
title: b.name,
meta: b.kicker,
onOpen: () => sendMessage(`What's going on with ${b.name} right now?`),
}))}
/>
);
}
// ──────────────────────────────────────────────────────────────────────
// Liquid-glass primitives
// ──────────────────────────────────────────────────────────────────────
function relTimeLanding(ts) {
if (!ts) return 'just now';
const diff = Date.now() - ts;
const h = Math.round(diff / 3600000);
if (h < 1) return 'just now';
if (h < 24) return `${h}h ago`;
return `${Math.round(h / 24)}d ago`;
}
function LandingStarter({ index, kicker, label, onClick }) {
const [hover, setHover] = React.useState(false);
return (
);
}
function CatalogueColumn({ kicker, count, items, empty }) {
return (
{kicker}
{typeof count === 'number' && (
{String(count).padStart(2, '0')}
)}
{items.length === 0 && (
{empty || 'Nothing yet.'}
)}
{items.map((it, i) => (
))}
);
}
function CatalogueRow({ index, title, meta, onOpen, isLast }) {
const [hover, setHover] = React.useState(false);
return (
);
}
Object.assign(window, { LandingHome });