Lofty Morning Handoff ✦

Built at GlobeHack S1 · 36 hours · Solo


## 💡 Inspiration

Real estate is a game of timing — and most agents are losing it every night.

Leads don't browse listings at 9 AM. They browse at 2 AM. They submit pre-approval letters before dawn. They compare agents by the time the sun rises. And by the time a real estate agent opens their laptop
with their morning coffee, the window has already closed.

I kept thinking: what if the agent's AI never slept?

Lofty's AOS (Agent Operating System) already handles overnight automation — follow-ups, showing requests, buyer matching. But there was a missing layer: the intelligent morning handoff. A system that takes
everything AOS did overnight, synthesizes it, and hands it to the agent in a way they can act on in seconds — not minutes.

That became the obsession. Not just a dashboard. A morning briefing system — narrated, ranked, and approval-ready before the agent finishes their first cup of coffee.


## 🏗️ What We Built

### The Flow

Agent wakes up ↓ Opens Lofty Morning Handoff ↓
🎬 Narrated video briefing plays automatically ↓
📋 Transcript reveals all overnight action items

⚡ Priority queue — ranked by AI confidence score ↓
One-tap: Approve · Delegate · Edit · Snooze

Ask Lofty anything in plain English (⌘K)

### Key Features

🎬 Morning Briefing Video

  • Animated particle canvas with nebula glow backgrounds
  • Web Speech API narration — each slide advances only when speech completes
  • Per-lead slides with buyer/seller indicators, character avatars, score rings, and signal timelines
  • Cinematic reveal animations: clip-path scans, expanding rings, staggered signal entries, glowing confidence bars
  • Keyboard controls: Space pause · ←→ navigate · M mute · Esc skip

⚡ Priority Queue

  • AI-ranked leads with confidence scores and signal-driven explanations
  • Drag-to-reorder with custom priority saved to sessionStorage
  • Action modes per card: Approve & Execute · Edit recommendation · Delegate to team · Snooze
  • All actions logged to InsForge audit trail in real time

🧠 Confidence Score Engine

  • 5-factor weighted model:

$$\text{Confidence} = \frac{\sum_{i} w_i \cdot f_i}{\sum_{i} w_i}$$

| Factor | What it measures |
|---|---|
| 📊 Signal Volume | How many overnight signals the lead generated | | ⏱ Recency | How fresh the most recent signal is |
| 🎯 Lead Score | The CRM base score (0–100) |
| 🔀 Signal Diversity | Variety of signal types (visit, email, showing…) |
| ⚡ Urgency | Deadline pressure or missed response time |

  • All weights are live-adjustable via the ⚙ Tune Model drawer — changes apply instantly across every card
  • Weights persist to localStorage across sessions
  • Per-factor breakdown visible in the Why This Matters drawer

🤖 Ask Lofty (⌘K)

  • Natural language AI assistant powered by Llama 3.3-70B via HuggingFace
  • Passes live queue context into the system prompt — answers are lead-specific
  • Detects email drafts and renders them in monospace with a Copy button
  • Suggestion chips on focus · instant fallback if LLM is unavailable

## 🔧 How We Built It

### Architecture

┌─────────────────────────────────────────────────┐
│ Frontend │
│ Next.js 16 App Router · React 19 · TypeScript │ │ Tailwind v4 · Framer Motion · shadcn/ui │
└─────────────────┬───────────────────────────────┘

┌─────────┴─────────┐
▼ ▼
┌──────────────┐ ┌─────────────────┐ │ InsForge │ │ HuggingFace │
│ (Postgres + │ │ Llama 3.3-70B │
│ Auth + Edge │ │ Together Router│
│ Functions) │ └─────────────────┘
└──────────────┘

priority_queue · leads · lead_signals
approvals · audit_log

### Build Order

  1. → Scaffolded Next.js with Tailwind and shadcn/ui
  2. → Seeded lib/mock-data.ts with 5 realistic leads + overnight signals
  3. → Built the video player with particle canvas and Web Speech API narration
  4. → Built action cards with all 4 action modes and drag-to-reorder
  5. → Wired InsForge backend (priority_queue, leads, lead_signals tables)
  6. → Added HuggingFace Llama 3.3-70B for Ask Lofty and Why This Matters
  7. → Built the 5-factor confidence engine with adjustable weights
  8. → Animated all video slides into cinematic reveals
  9. → Dockerized and deployed

## 🚧 Challenges

→ Speech-driven slide timing
Browser speechSynthesis fires onend unreliably — sometimes never. The naive approach (fixed 6s timer) cut audio off mid-sentence. The fix: decouple visual progress (fills to 95% via interval) from advancement (triggered only by onend), with a 20s safety timeout as fallback and a advancedRef mutex to prevent double-advance.

→ Canvas sizing at mount
canvas.offsetWidth returns 0 before layout completes. Every particle animation started at 0×0. Fix: defer initialization one frame with requestAnimationFrame, use parentElement.clientWidth as the size source, and apply HiDPI scaling via ctx.setTransform(dpr, 0, 0, dpr, 0, 0).

→ Pause re-triggering speech
Adding paused to the speech useEffect dependency array caused the utterance to restart on every pause/resume — the opposite of pausing. Fix: remove paused from deps, use speechSynthesis.pause() / speechSynthesis.resume() in a separate effect, and read pause state through a pausedRef in callbacks.

→ Framer Motion + HTML drag events
motion.div's onDragStart has a different type signature than the HTML5 drag API. Wrapping motion.div in a plain <div> for drag events and keeping motion.div for animation only resolved the TypeScript conflict cleanly.

→ Confidence model UX
The model needs to feel controllable, not like a black box. The solution: a live-preview tuner where every slider change immediately re-renders all queue cards with the new weights, showing the before/after delta inline. Judges and agents can see exactly what's driving each score.


## 📚 What We Learned

  • Speech APIs are deceptively fragile — browser TTS has significant cross-platform inconsistencies that require layered fallbacks
  • Canvas timing requires patience — React's render cycle and browser layout don't align; requestAnimationFrame is the bridge
  • InsForge is genuinely fast to wire — Postgres + SDK took under 2 hours end-to-end including schema design and seeding
  • Explainability matters in AI UX — showing why a score is what it is (and letting users change it) builds far more trust than just showing a number
  • Hackathon shipping is about the demo moment — every technical decision was filtered through "will this look right at 2 AM with 5 minutes left?"

## 🛠️ Tech Stack

| Layer | Technology | |---|---| | Frontend | Next.js 16 (App Router), React 19, TypeScript | | Styling | Tailwind CSS v4, Framer Motion, shadcn/ui |
| AI / LLM | Llama 3.3-70B via HuggingFace Together Router | | Backend | InsForge (Postgres + Auth + Edge Functions) |
| Audio | Web Speech API (speechSynthesis) |
| Deploy | Docker + docker-compose |


Built With

  • audit-log)-apis-&-browser-apis-web-speech-api-(speechsynthesis)
  • auth
  • css-frameworks-&-libraries-next.js-16-(app-router)
  • docker-compose-dev-tools-claude-code-(ai-pair-programming)
  • edge-functions
  • framer-motion
  • git
  • github
  • html5-canvas-api
  • html5-drag-and-drop-api-infrastructure-&-devops-docker
  • huggingface-inference-api
  • javascript
  • languages-typescript
  • node.js
  • react-19
  • shadcn/ui-ai-/-llm-llama-3.3-70b-instruct-turbo-(via-huggingface-together-router)-backend-&-database-insforge-(postgres
  • tailwind-css-v4
Share this project:

Updates