diff --git a/.env.local.example b/.env.local.example
new file mode 100644
index 0000000..cd3b19e
--- /dev/null
+++ b/.env.local.example
@@ -0,0 +1,14 @@
+# Copy to .env.local and fill in your values.
+# Never commit .env.local to git.
+
+# Anthropic Claude API — required for document analysis and quote explainer
+ANTHROPIC_API_KEY=sk-ant-...
+
+# Supabase — required for document storage and user profiles (Phase 1 optional)
+NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
+NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ...
+SUPABASE_SERVICE_ROLE_KEY=eyJ...
+
+# Clerk — required for authentication (Phase 1 optional)
+NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
+CLERK_SECRET_KEY=sk_test_...
diff --git a/.gitignore b/.gitignore
index 01c8a5f..8d1ac13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,16 @@ node_modules/
dist/
.env
.env.*
+!.env.local.example
*.log
.DS_Store
Thumbs.db
+
+# Next.js
+.next/
+out/
+.vercel
+next-env.d.ts
+
+# Uploads (local dev only — use S3 in production)
+uploads/
diff --git a/ROADMAP.md b/ROADMAP.md
new file mode 100644
index 0000000..d5a7d63
--- /dev/null
+++ b/ROADMAP.md
@@ -0,0 +1,244 @@
+# FairDrive Auto — AI Roadmap
+
+> **Vision:** The first AI-native auto insurer that prices drivers on *who they are*, not just *where they've been insured*. FairDrive replaces the blunt U.S.-history penalty with a multi-dimensional FairScore — unlocking affordable coverage for 8M+ immigrants, international students, and new U.S. drivers who are systematically overcharged.
+
+---
+
+## Why This Market Is Broken
+
+Traditional auto insurance uses U.S. insurance history as a proxy for risk. A driver with 15 years of safe driving in Germany, India, or Brazil arrives in the U.S. and is quoted the same premium as a teenager with no history at all. This is not actuarially sound — it is a data gap that incumbents have never bothered to close.
+
+FairDrive closes that gap with AI.
+
+---
+
+## Core Differentiators
+
+| What incumbents do | What FairDrive does |
+|---|---|
+| Require 3+ years U.S. history for best rates | Credit verified international driving history (FairCredit) |
+| Black-box pricing with no explanation | Transparent FairScore with factor breakdown |
+| Static annual pricing | Continuous behavioral repricing (telematics) |
+| One-size pricing ignores mileage | Pay-per-mile option for low-usage drivers |
+| Require agents for every change | Self-serve AI coach with quantified savings actions |
+| English-only, complex forms | Multi-language AI onboarding (Claude-powered) |
+| No help understanding your quote | AI Quote Explainer: plain-language breakdown of every line item |
+
+---
+
+## Phased Roadmap
+
+### Phase 1 — AI Foundation (0–6 months)
+
+**Goal:** Replace the demo prototype with a production-grade AI pricing engine and real document processing.
+
+#### 1.1 Tech Stack Modernization
+- [ ] Migrate to Next.js 14+ (App Router) with TypeScript
+- [ ] Replace CDN Three.js with local `@react-three/fiber` + `@react-three/drei`
+- [ ] Set up PostgreSQL (Supabase) for user profiles and document metadata
+- [ ] Add Clerk or Auth0 for authentication + MFA
+- [ ] Deploy on Vercel with edge functions for sub-100ms API responses
+- [ ] Add Sentry for error tracking, PostHog for product analytics
+
+#### 1.2 FairScore Engine (implemented in v0.1)
+- [x] Multi-factor pricing model: vehicle risk, driving credential, geographic, mileage, coverage
+- [x] FairCredit: international driving years mapped to actuarial credit rate
+- [x] Traditional vs. FairDrive premium comparison
+- [x] AI coach: ranked savings recommendations with quantified monthly impact
+- [x] Score narrative: plain-language explanation of what's helping/hurting
+- [ ] Calibrate factors against ISO/NCCI rate filings for 10 target states
+- [ ] Bayesian confidence intervals on premium range (replace simple ±10%)
+- [ ] A/B test FairScore vs. traditional form for conversion rate
+
+#### 1.3 Document Intelligence (Claude API)
+- [ ] Integrate Claude claude-sonnet-4-6 for policy document OCR + extraction
+- [ ] Extract: carrier name, effective dates, premium breakdown, coverage limits, deductibles
+- [ ] Cross-reference extracted limits against state minimums — flag gaps automatically
+- [ ] International document support: parse IDP (International Driving Permit), foreign no-claims letters
+- [ ] Structured output schema (JSON) for downstream pricing and comparison
+- [ ] Confidence scoring on extracted fields — human review queue for low-confidence items
+
+#### 1.4 Insurance Passport MVP
+- [ ] Secure document upload (S3 + pre-signed URLs, client-side encryption)
+- [ ] Six document types: U.S. license, foreign license, no-claims proof, vehicle registration, current policy, telematics consent
+- [ ] Document verification status with estimated completion time
+- [ ] Passport share link: generate a read-only URL drivers share with agents
+- [ ] Privacy controls: per-document consent, granular partner sharing toggles
+- [ ] GDPR/CCPA-compliant deletion
+
+---
+
+### Phase 2 — Intelligence Layer (6–12 months)
+
+**Goal:** Build the AI systems that create a sustainable moat — behavioral pricing, document intelligence at scale, and multi-language onboarding.
+
+#### 2.1 Telematics & Behavioral Scoring
+- [ ] SDK integration: Arity (Allstate), Verisk DriveAbility, or LexisNexis Telematics One
+- [ ] Mobile SDK for iOS/Android: accelerometer + GPS trip scoring (no constant tracking)
+- [ ] Drive Score factors: hard braking, acceleration, cornering, night driving %, distracted driving proxy
+- [ ] Consent-first architecture: users see their own score before it's shared with any carrier
+- [ ] Drive Score improves FairScore continuously — monthly premium repricing for good drivers
+
+#### 2.2 Claude-Powered Quote Explainer
+- [ ] Streaming AI explanation of every quote line item (Claude claude-sonnet-4-6 via Anthropic API)
+- [ ] Source-backed answers: every AI claim links to the policy document page or carrier rate filing
+- [ ] Multi-turn conversation: "Why is my comprehensive so high?" → follow-up questions
+- [ ] Guardrails: no guaranteed savings claims, no specific carrier recommendations without licensed-agent handoff
+- [ ] Prompt caching for common query patterns — reduce latency and API cost
+- [ ] Multilingual: Spanish, Hindi, Mandarin, Korean, Portuguese, Tagalog (top immigrant languages)
+
+#### 2.3 Market Intelligence Layer
+- [ ] Carrier rate filing scraper: parse state DOI rate filings (public record) for 10 states
+- [ ] Market median premium by: vehicle + state + profile tier + coverage level
+- [ ] "You are paying X% above market median" benchmark shown to every user
+- [ ] Carrier appetite model: which carriers are currently hungry for new-to-US customers
+- [ ] Integration with Verisk LOCATION, ISO territories for geographic risk data
+
+#### 2.4 AI Onboarding
+- [ ] Replace long-form application with conversational AI intake (Claude-powered chat)
+- [ ] Dynamic question tree: ask only what's relevant based on prior answers
+- [ ] Reduce average onboarding time from 12 minutes → under 3 minutes
+- [ ] Sentiment analysis: detect frustrated users, route to human agent proactively
+- [ ] Voice onboarding: Web Speech API for accessibility
+
+---
+
+### Phase 3 — Market Entry (12–18 months)
+
+**Goal:** Go from intelligence platform to licensed insurance distribution. First revenue.
+
+#### 3.1 Licensed Distribution Infrastructure
+- [ ] Apply for MGA (Managing General Agent) license in top 5 states (TX, CA, FL, NY, IL)
+- [ ] Carrier appointments: Progressive, Mercury, Dairyland (appetite for new drivers)
+- [ ] E&O insurance for agent operations
+- [ ] Compliance engine: state-by-state rule matrix for what AI can/cannot recommend
+- [ ] Licensed agent network: 1099 agents on standby for regulated handoffs
+- [ ] ACORD XML integration for carrier submission
+
+#### 3.2 FairBind Product
+- [ ] Real-time bindable quotes from 3+ carriers for each FairScore profile
+- [ ] "Best carrier match" AI: routes each profile to the carrier currently pricing that risk favorably
+- [ ] Instant digital ID cards (PDF + Apple/Google Wallet)
+- [ ] Policy management: mid-term changes, payments, claims FNOL via chat
+- [ ] Carrier API integrations: Progressive API, Openly, Coterie (modern carriers with APIs)
+
+#### 3.3 FairCredit Verification Network
+- [ ] Partner with international insurers (UK, India, Germany, Canada) for direct record verification
+- [ ] CARFAX International (existing database of 30+ countries)
+- [ ] Certified translation service for foreign no-claims letters
+- [ ] Risk model recalibration: measure loss ratio of FairCredit customers vs. traditional new drivers
+- [ ] Actuarial sign-off: file rate adjustments for verified FairCredit cohort
+
+#### 3.4 Pay-Per-Mile Product
+- [ ] White-label Metromile/Mile Auto for FairDrive customers
+- [ ] Base rate (parked car coverage) + per-mile rate
+- [ ] Target: immigrant drivers who commute by transit and drive <6,000 mi/year
+- [ ] OBD-II plug-in device or mobile app odometer (no GPS required for basic tier)
+
+---
+
+### Phase 4 — Scale (18–24 months)
+
+**Goal:** National footprint, category leadership, and defensible AI data moat.
+
+#### 4.1 Continuous Learning Pricing
+- [ ] Cohort loss ratio analysis: FairCredit customers vs. traditional new-driver cohort
+- [ ] Monthly model refresh: retrain FairScore weights on actual loss data
+- [ ] Adverse selection detection: flag profiles scoring anomalously high against loss history
+- [ ] Federated learning: improve Drive Score model without centralizing raw GPS data
+- [ ] MLflow for experiment tracking, model versioning, and rollback
+
+#### 4.2 Community & Network Effects
+- [ ] Referral program: immigrant community networks (Desi diaspora, Indian Professionals, etc.)
+- [ ] Group policies: employers sponsoring FairDrive for H1B hires (B2B channel)
+- [ ] University partnerships: international student insurance packages
+- [ ] Affinity groups: alumni associations, cultural organizations
+
+#### 4.3 Claims Intelligence
+- [ ] AI-first FNOL (First Notice of Loss): guided photo documentation via mobile
+- [ ] Computer vision for damage severity estimation (reduce cycle time)
+- [ ] Fraud detection: cross-reference claim patterns against network data
+- [ ] Subrogation automation: identify recovery opportunities automatically
+
+#### 4.4 Platform Expansion
+- [ ] Renters insurance bundling (natural adjacent product for apartment-dwelling immigrants)
+- [ ] International health insurance bridge product (gap coverage during first 90 days)
+- [ ] Data licensing to carriers: anonymized FairScore insights (opt-in only)
+
+---
+
+## Technology Architecture
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ FairDrive Platform │
+├──────────────┬──────────────────────┬───────────────────┤
+│ Web/App │ AI Services │ Data & Carriers │
+├──────────────┼──────────────────────┼───────────────────┤
+│ Next.js 14 │ Claude claude-sonnet-4-6 │ PostgreSQL/Supabase│
+│ React Three │ - Quote Explainer │ S3 (documents) │
+│ Fiber (3D) │ - Doc Extraction │ Verisk APIs │
+│ Clerk Auth │ - Onboarding Chat │ LexisNexis │
+│ │ FairScore Engine │ ISO Territory │
+│ │ - Multi-factor model │ Carrier APIs: │
+│ │ - FairCredit calc │ - Progressive │
+│ │ - Coach recs │ - Mercury │
+│ │ Drive Score SDK │ - Openly │
+│ │ - Arity / Verisk │ ACORD XML │
+│ │ MLflow (versioning) │ Stripe (payments) │
+└──────────────┴──────────────────────┴───────────────────┘
+```
+
+---
+
+## Regulatory Strategy
+
+| Requirement | Approach |
+|---|---|
+| State insurance license | MGA structure — partner with licensed carrier fronts (Homepoint, Spinnaker) |
+| Rate filing | File FairScore methodology with state DOIs; actuarial justification for FairCredit |
+| AI fairness | Avoid protected classes; FairCredit uses years of history, not country of origin |
+| Data privacy | CCPA/GDPR compliant; per-document consent; right to deletion |
+| Document AI | Disclosures when AI is used for extraction; human review queue |
+| Savings claims | Never promise specific savings; always "estimate" with disclaimer |
+
+---
+
+## Revenue Model
+
+| Stream | Timing | Margin |
+|---|---|---|
+| Commission on bound policies (12–15% of premium) | Phase 3 | ~75% gross margin |
+| Pay-per-mile policy administration fee | Phase 3 | ~65% |
+| FairCredit verification premium ($15–25/verification) | Phase 2 | ~80% |
+| Group policy admin fee (employer channel) | Phase 4 | ~70% |
+| Carrier data licensing (opt-in anonymized FairScore data) | Phase 4 | ~90% |
+
+---
+
+## Key Risk Guardrails
+
+1. **Never promise guaranteed savings** — always use "estimate" and show disclaimer
+2. **International history = credit, not substitution** — FairCredit supplements US history, never replaces actuarial factors
+3. **Immigration documents are optional** — never required; ITIN ≠ immigration status for pricing
+4. **No protected class proxies** — country of birth is never an input; only driving years and verified records
+5. **Licensed agent handoff** — AI coaches and explains; a licensed human binds the policy
+6. **Source-backed AI answers** — every Claude response must cite the policy document or rate filing
+7. **Consent before sharing** — users approve each partner data share; no silent routing
+
+---
+
+## Success Metrics
+
+| Metric | Phase 1 | Phase 2 | Phase 3 | Phase 4 |
+|---|---|---|---|---|
+| Users with FairScore computed | 1,000 | 25,000 | 200,000 | 1M+ |
+| Avg FairScore delta vs. traditional | +8 pts | +12 pts | +14 pts | +16 pts |
+| Monthly savings surfaced (median) | $42 | $58 | $67 | $74 |
+| Policies bound | 0 | 0 | 5,000 | 50,000 |
+| Loss ratio (FairCredit cohort) | — | — | <72% | <68% |
+| NPS | — | 58 | 65 | 72 |
+
+---
+
+*FairDrive Auto — pricing drivers on who they are, not just where they've been insured.*
diff --git a/ai-engine.js b/ai-engine.js
new file mode 100644
index 0000000..b3c2279
--- /dev/null
+++ b/ai-engine.js
@@ -0,0 +1,266 @@
+// FairDrive AI Pricing Engine
+// Multi-factor FairScore model: replaces blunt "no US history" penalty
+// with a holistic assessment that credits international driving experience.
+
+const VEHICLE_DATA = {
+ accord: {
+ label: "2023 Honda Accord",
+ msrp: 28000,
+ repairIndex: 62,
+ theftIndex: 45,
+ safetyRating: 88,
+ isEV: false,
+ baseMonthly: 320,
+ },
+ camry: {
+ label: "2023 Toyota Camry",
+ msrp: 27000,
+ repairIndex: 58,
+ theftIndex: 42,
+ safetyRating: 90,
+ isEV: false,
+ baseMonthly: 300,
+ },
+ bmw: {
+ label: "2023 BMW 330i",
+ msrp: 44000,
+ repairIndex: 88,
+ theftIndex: 55,
+ safetyRating: 85,
+ isEV: false,
+ baseMonthly: 480,
+ },
+ tesla: {
+ label: "2023 Tesla Model 3",
+ msrp: 41000,
+ repairIndex: 82,
+ theftIndex: 38,
+ safetyRating: 92,
+ isEV: true,
+ baseMonthly: 440,
+ },
+ crv: {
+ label: "2023 Honda CR-V",
+ msrp: 31000,
+ repairIndex: 60,
+ theftIndex: 40,
+ safetyRating: 86,
+ isEV: false,
+ baseMonthly: 285,
+ },
+};
+
+// State rate multipliers relative to national average (1.0)
+const STATE_MULTIPLIERS = {
+ CA: 1.42, FL: 1.38, MI: 1.52, NY: 1.35, TX: 1.28,
+ IL: 1.18, NJ: 1.45, MA: 1.25, MD: 1.30, GA: 1.22,
+ WA: 1.12, CO: 1.15, AZ: 1.19, PA: 1.10, VA: 1.07,
+ NC: 1.08, OH: 1.05, MN: 1.06, WI: 1.01, TN: 1.03,
+};
+
+// Mapping of international driving years to credit rate (0.0 – 0.30)
+// FairCredit: the core differentiator — foreign history is not zero.
+const INTL_CREDIT_TIERS = [
+ { minYears: 10, rate: 0.30 },
+ { minYears: 5, rate: 0.22 },
+ { minYears: 3, rate: 0.15 },
+ { minYears: 1, rate: 0.08 },
+ { minYears: 0, rate: 0.00 },
+];
+
+function getIntlCreditRate(years) {
+ const tier = INTL_CREDIT_TIERS.find(t => years >= t.minYears);
+ return tier ? tier.rate : 0;
+}
+
+// FairScore: 0–100. Higher = lower risk = lower premium.
+// Market average is ~58. New-to-US drivers with no history score ~32–40.
+// With FairCredit applied, those same drivers can reach 50–65.
+function computeFairScore(inputs) {
+ const { vehicle, usHistoryYears, intlHistoryYears, annualMiles, state, cleanRecord, defensiveCourse } = inputs;
+ const vd = VEHICLE_DATA[vehicle];
+
+ // Factor 1: Vehicle Risk (max 30 pts)
+ // Lower repair cost + lower theft + higher safety = more points
+ const repairPenalty = (vd.repairIndex / 100) * 12;
+ const theftPenalty = (vd.theftIndex / 100) * 6;
+ const safetyBonus = (vd.safetyRating / 100) * 12;
+ const vehicleScore = Math.max(0, Math.min(30, 30 - repairPenalty - theftPenalty + safetyBonus - 12));
+
+ // Factor 2: Driving Credential (max 35 pts)
+ // FairCredit: international history gets partial credit instead of zero.
+ const usCredit = Math.min(usHistoryYears * 4, 20);
+ const intlCredit = getIntlCreditRate(intlHistoryYears) * 20;
+ const recordBonus = cleanRecord ? 8 : 0;
+ const courseBonus = defensiveCourse ? 5 : 0;
+ // Cold-start penalty only if truly zero history (no US, no intl)
+ const coldStart = (usHistoryYears === 0 && intlHistoryYears === 0) ? -15 : 0;
+ const credentialScore = Math.max(0, Math.min(35, usCredit + intlCredit + recordBonus + courseBonus + coldStart));
+
+ // Factor 3: Geographic Risk (max 20 pts)
+ const stateMult = STATE_MULTIPLIERS[state] || 1.10;
+ const geoScore = Math.max(0, Math.min(20, 20 - (stateMult - 1.0) * 50));
+
+ // Factor 4: Mileage Pattern (max 10 pts)
+ // Low mileage = lower exposure
+ const mileScore =
+ annualMiles <= 5000 ? 10 :
+ annualMiles <= 7500 ? 8 :
+ annualMiles <= 12000 ? 6 :
+ annualMiles <= 20000 ? 3 : 1;
+
+ // Factor 5: Coverage Behavior (max 5 pts — higher deductibles show responsibility)
+ const coverageScore = 5;
+
+ const raw = vehicleScore + credentialScore + geoScore + mileScore + coverageScore;
+ const total = Math.max(10, Math.min(95, Math.round(raw)));
+
+ return {
+ total,
+ factors: {
+ vehicle: { score: Math.round(Math.max(0, vehicleScore)), max: 30, label: "Vehicle Risk" },
+ credential: { score: Math.round(Math.max(0, credentialScore)), max: 35, label: "Driving Credential" },
+ geographic: { score: Math.round(Math.max(0, geoScore)), max: 20, label: "Geographic Risk" },
+ mileage: { score: mileScore, max: 10, label: "Mileage Pattern" },
+ coverage: { score: coverageScore, max: 5, label: "Coverage Behavior" },
+ },
+ };
+}
+
+// What a traditional insurer would score — ignoring international history.
+function computeTraditionalScore(inputs) {
+ return computeFairScore({ ...inputs, intlHistoryYears: 0 });
+}
+
+// Monthly premium from FairScore + state multiplier + coverage level.
+function computePremium(inputs, fairScore) {
+ const { vehicle, state, coverageLevel } = inputs;
+ const vd = VEHICLE_DATA[vehicle];
+ const stateMult = STATE_MULTIPLIERS[state] || 1.10;
+ const coverageMult =
+ coverageLevel === "basic" ? 0.72 :
+ coverageLevel === "premium" ? 1.28 : 1.0;
+
+ // Each point above 58 shaves ~1.2% off premium; below 58 adds 1.2%
+ const fairAdj = 1 - ((fairScore.total - 58) * 0.012);
+ const monthly = Math.max(80, Math.round(vd.baseMonthly * stateMult * coverageMult * fairAdj));
+
+ const tradScore = computeTraditionalScore(inputs);
+ const tradAdj = 1 - ((tradScore.total - 58) * 0.012);
+ const tradMonthly = Math.max(80, Math.round(vd.baseMonthly * stateMult * coverageMult * tradAdj));
+
+ const fairSavings = Math.max(0, tradMonthly - monthly);
+
+ return {
+ monthly,
+ tradMonthly,
+ fairSavings,
+ range: [Math.round(monthly * 0.90), Math.round(monthly * 1.12)],
+ annualEstimate: monthly * 12,
+ };
+}
+
+// AI coach: ranked, savings-quantified action items.
+function getCoachRecommendations(inputs, fairScore, premium) {
+ const recs = [];
+ const vd = VEHICLE_DATA[inputs.vehicle];
+
+ if (inputs.intlHistoryYears >= 2 && inputs.usHistoryYears < 2) {
+ recs.push({
+ priority: 1,
+ icon: "✦",
+ title: "Submit International Driving Record (FairCredit)",
+ detail: `${inputs.intlHistoryYears} years of verified foreign driving qualifies for FairCredit recognition. Most partner carriers accept a certified no-claims letter from your home country insurer.`,
+ action: "Add to Passport",
+ savings: Math.round(premium.fairSavings * 0.65),
+ });
+ }
+
+ if (!inputs.cleanRecord) {
+ recs.push({
+ priority: 2,
+ icon: "◎",
+ title: "Clear or Dispute DMV Record Items",
+ detail: "Even one at-fault incident can add 30–50% to your premium for 3 years. If the incident was minor or ≥3 years ago, some carriers will re-rate sooner.",
+ action: "Review record",
+ savings: Math.round(premium.monthly * 0.22),
+ });
+ }
+
+ if (!inputs.defensiveCourse) {
+ recs.push({
+ priority: 3,
+ icon: "◆",
+ title: "Complete a Defensive Driving Course",
+ detail: "Online courses take 4–6 hours and cost $20–$50. Most U.S. carriers offer 5–10% discount for verified completion and it signals commitment to safe driving.",
+ action: "Find a course",
+ savings: Math.round(premium.monthly * 0.07),
+ });
+ }
+
+ if (inputs.annualMiles > 8000) {
+ recs.push({
+ priority: 4,
+ icon: "◈",
+ title: "Enroll in Pay-Per-Mile Coverage",
+ detail: `At ${inputs.annualMiles.toLocaleString()} miles/year you're paying for more exposure than you use. Usage-based policies from carriers like Metromile or Mile Auto can save 20–40% for drivers under 10,000 mi/year.`,
+ action: "Check eligibility",
+ savings: Math.round(premium.monthly * 0.18),
+ });
+ }
+
+ if (vd.repairIndex > 75) {
+ recs.push({
+ priority: 5,
+ icon: "◇",
+ title: `Switch to a Lower Repair-Cost Vehicle`,
+ detail: `The ${vd.label} has a repair cost index of ${vd.repairIndex}/100 — well above average. A Honda CR-V (index 60) or Toyota Camry (index 58) could reduce your premium significantly without sacrificing quality.`,
+ action: "Compare vehicles",
+ savings: Math.round(premium.monthly * 0.17),
+ });
+ }
+
+ recs.push({
+ priority: 6,
+ icon: "◉",
+ title: "Raise Collision Deductible to $1,000",
+ detail: "Moving from a $500 to $1,000 collision deductible typically reduces monthly premium by 8–12%. Keep the difference in an emergency fund — statistically, most drivers go years without a claim.",
+ action: "Model the tradeoff",
+ savings: Math.round(premium.monthly * 0.10),
+ });
+
+ return recs.sort((a, b) => a.priority - b.priority);
+}
+
+// Describe what's hurting and helping the FairScore
+function getScoreNarrative(inputs, fairScore) {
+ const helps = [];
+ const hurts = [];
+ const vd = VEHICLE_DATA[inputs.vehicle];
+
+ if (vd.safetyRating >= 88) helps.push(`${vd.label} has a top safety rating`);
+ if (vd.repairIndex > 75) hurts.push(`${vd.label} has above-average repair costs`);
+ if (inputs.intlHistoryYears >= 3) helps.push(`${inputs.intlHistoryYears} years of international driving history (FairCredit)`);
+ if (inputs.usHistoryYears === 0 && inputs.intlHistoryYears === 0) hurts.push("no driving history on file anywhere");
+ if (inputs.cleanRecord) helps.push("clean driving record");
+ if (!inputs.cleanRecord) hurts.push("incidents on driving record");
+ if (inputs.defensiveCourse) helps.push("defensive driving certification");
+ if (inputs.annualMiles <= 7500) helps.push("low annual mileage");
+ if (inputs.annualMiles > 15000) hurts.push("high annual mileage increases exposure");
+
+ const stateMult = STATE_MULTIPLIERS[inputs.state] || 1.10;
+ if (stateMult >= 1.30) hurts.push(`${inputs.state} is a high-cost insurance state`);
+ if (stateMult <= 1.08) helps.push(`${inputs.state} has below-average insurance rates`);
+
+ return { helps, hurts };
+}
+
+window.FairDriveAI = {
+ computeFairScore,
+ computeTraditionalScore,
+ computePremium,
+ getCoachRecommendations,
+ getScoreNarrative,
+ VEHICLE_DATA,
+ STATE_MULTIPLIERS,
+};
diff --git a/app.js b/app.js
index 643f4d4..3f06291 100644
--- a/app.js
+++ b/app.js
@@ -1,14 +1,16 @@
-const viewButtons = document.querySelectorAll("[data-view]");
+// FairDrive App — view routing, vehicle selection, AI engine wiring
+
+const viewButtons = document.querySelectorAll("[data-view]");
const targetButtons = document.querySelectorAll("[data-view-target]");
-const panels = document.querySelectorAll("[data-view-panel]");
-const quoteUpload = document.querySelector("#quoteUpload");
+const panels = document.querySelectorAll("[data-view-panel]");
+const quoteUpload = document.querySelector("#quoteUpload");
const quoteExplanation = document.querySelector("#quoteExplanation");
const estimatorForm = document.querySelector("#estimatorForm");
const vehicleSelect = document.querySelector("#vehicleSelect");
const garageVehicleSelect = document.querySelector("#garageVehicleSelect");
const selectedVehicleName = document.querySelector("#selectedVehicleName");
-const vehicleDesignLabel = document.querySelector("#vehicleDesignLabel");
-const historySelect = document.querySelector("#historySelect");
+const vehicleDesignLabel = document.querySelector("#vehicleDesignLabel");
+const historySelect = document.querySelector("#historySelect");
const estimateResult = document.querySelector("#estimateResult");
const popover = document.querySelector("#coveragePopover");
const hotspots = document.querySelectorAll(".hotspot");
@@ -17,97 +19,261 @@ let currentVehicle = "accord";
const vehicleModels = {
accord: { label: "2023 Honda Accord", design: "Mid-size sedan" },
- camry: { label: "2023 Toyota Camry", design: "Angular sedan" },
- bmw: { label: "2023 BMW 330i", design: "Sport luxury sedan" },
- tesla: { label: "2023 Tesla Model 3", design: "Minimal EV fastback" },
- crv: { label: "2023 Honda CR-V", design: "Compact SUV" },
+ camry: { label: "2023 Toyota Camry", design: "Angular sedan" },
+ bmw: { label: "2023 BMW 330i", design: "Sport luxury sedan" },
+ tesla: { label: "2023 Tesla Model 3",design: "Minimal EV fastback" },
+ crv: { label: "2023 Honda CR-V", design: "Compact SUV" },
};
const estimates = {
accord: { none: [310, 430], short: [250, 350], long: [190, 280] },
- camry: { none: [290, 410], short: [235, 330], long: [180, 265] },
- bmw: { none: [430, 590], short: [360, 500], long: [275, 405] },
- tesla: { none: [390, 540], short: [320, 455], long: [245, 370] },
- crv: { none: [275, 390], short: [225, 315], long: [175, 255] },
+ camry: { none: [290, 410], short: [235, 330], long: [180, 265] },
+ bmw: { none: [430, 590], short: [360, 500], long: [275, 405] },
+ tesla: { none: [390, 540], short: [320, 455], long: [245, 370] },
+ crv: { none: [275, 390], short: [225, 315], long: [175, 255] },
};
const coverageCopy = {
- Collision: "Protects your car after an accident with another vehicle or object.",
- Comprehensive: "Protects against theft, hail, glass damage, flood, fire, and other non-collision events.",
- Medical: "Helps cover medical costs for you or passengers after a covered accident.",
+ Collision: "Protects your car after an accident with another vehicle or object.",
+ Comprehensive:"Protects against theft, hail, glass damage, flood, fire, and other non-collision events.",
+ Medical: "Helps cover medical costs for you or passengers after a covered accident.",
};
-function setView(view) {
- viewButtons.forEach((button) => {
- button.classList.toggle("active", button.dataset.view === view);
- });
+// ── View routing ──────────────────────────────────────────────────────────────
- panels.forEach((panel) => {
- panel.classList.toggle("active", panel.dataset.viewPanel === view);
- });
+function setView(view) {
+ viewButtons.forEach(b => b.classList.toggle("active", b.dataset.view === view));
+ panels.forEach(p => p.classList.toggle("active", p.dataset.viewPanel === view));
}
function setVehicle(vehicle) {
currentVehicle = vehicle;
const model = vehicleModels[vehicle];
selectedVehicleName.textContent = model.label;
- vehicleDesignLabel.textContent = model.design;
+ vehicleDesignLabel.textContent = model.design;
garageVehicleSelect.value = vehicle;
vehicleSelect.value = vehicle;
window.FairDriveCarViewer?.setVehicleModel(vehicle);
}
-viewButtons.forEach((button) => {
- button.addEventListener("click", () => setView(button.dataset.view));
-});
+viewButtons.forEach(b => b.addEventListener("click", () => setView(b.dataset.view)));
+targetButtons.forEach(b => b.addEventListener("click", () => setView(b.dataset.viewTarget)));
+garageVehicleSelect?.addEventListener("change", () => setVehicle(garageVehicleSelect.value));
+vehicleSelect?.addEventListener("change", () => setVehicle(vehicleSelect.value));
-targetButtons.forEach((button) => {
- button.addEventListener("click", () => setView(button.dataset.viewTarget));
-});
+// ── Coverage hotspots ─────────────────────────────────────────────────────────
-garageVehicleSelect?.addEventListener("change", () => {
- setVehicle(garageVehicleSelect.value);
+hotspots.forEach(hs => {
+ hs.addEventListener("mouseenter", () => {
+ const label = hs.dataset.coverage;
+ popover.innerHTML = `
+
Coverage
+ ${label}
+ ${coverageCopy[label]}
+ `;
+ });
});
-vehicleSelect?.addEventListener("change", () => {
- setVehicle(vehicleSelect.value);
-});
+// ── Quote upload (simulated OCR) ──────────────────────────────────────────────
quoteUpload?.addEventListener("change", () => {
const file = quoteUpload.files?.[0];
if (!file) return;
-
quoteExplanation.innerHTML = `
- Simulated extraction
+ Simulated extraction — ${file.name}
- ${file.name} is ready for analysis. In the MVP, this will extract premium,
- deductibles, liability limits, discounts, and likely price drivers.
+ In production, Claude AI will extract: carrier name, effective dates, premium
+ breakdown, coverage limits, deductibles, and flag gaps against state minimums.
+ International no-claims letters from 30+ countries are also parsed.
`;
});
-estimatorForm?.addEventListener("submit", (event) => {
- event.preventDefault();
- const vehicle = vehicleSelect.value;
- const history = historySelect.value;
- const [low, high] = estimates[vehicle][history];
+// ── Simple estimator ──────────────────────────────────────────────────────────
+estimatorForm?.addEventListener("submit", e => {
+ e.preventDefault();
+ const v = vehicleSelect.value;
+ const h = historySelect.value;
+ const [lo, hi] = estimates[v][h];
estimateResult.innerHTML = `
- ${vehicleModels[vehicle].label}
- $${low} - $${high}/mo
- This range is a planning estimate. Real quotes depend on garaging address, coverage, carrier appetite, driving record, and state rules.
+ ${vehicleModels[v].label}
+ $${lo} – $${hi}/mo
+ This range is a planning estimate. Use the FairScore tab to see the impact of
+ your international driving history, state, and mileage on the full model.
`;
});
-hotspots.forEach((hotspot) => {
- hotspot.addEventListener("mouseenter", () => {
- const label = hotspot.dataset.coverage;
- popover.innerHTML = `
- Coverage
- ${label}
- ${coverageCopy[label]}
+// ── FairScore engine ──────────────────────────────────────────────────────────
+
+const fairscoreForm = document.querySelector("#fairscoreForm");
+const fairscoreResult = document.querySelector("#fairscoreResult");
+const premiumCompPanel = document.querySelector("#premiumComparePanel");
+const scoreNumber = document.querySelector("#scoreNumber");
+const scoreTier = document.querySelector("#scoreTier");
+const scoreBench = document.querySelector("#scoreBench");
+const gaugeFill = document.querySelector("#gaugeFill");
+const factorBarsEl = document.querySelector("#factorBars");
+const scoreNarrativeEl = document.querySelector("#scoreNarrative");
+const fairMonthlyEl = document.querySelector("#fairMonthly");
+const tradMonthlyEl = document.querySelector("#tradMonthly");
+const savingsBannerEl = document.querySelector("#savingsBanner");
+const savingsAmountEl = document.querySelector("#savingsAmount");
+const annualEstEl = document.querySelector("#annualEstimate");
+const premiumRangeEl = document.querySelector("#premiumRange");
+const fairscorePill = document.querySelector("#fairscoreStatusPill");
+const coachList = document.querySelector("#coachList");
+const coachPill = document.querySelector("#coachPill");
+const coachHeading = document.querySelector("#coachHeading");
+const totalSavingsEl = document.querySelector("#totalSavingsDisplay");
+
+// Gauge arc: the SVG path spans 0°→180° (semicircle); map score 0–100 → dashoffset.
+// Arc length of "M10,65 A55,55,0,0,1,110,65" ≈ 172.8 px.
+const GAUGE_ARC = 172.8;
+
+function setGauge(score) {
+ const filled = (score / 100) * GAUGE_ARC;
+ gaugeFill.style.strokeDasharray = `${filled} ${GAUGE_ARC}`;
+ gaugeFill.style.strokeDashoffset = 0;
+ const hue = Math.round(score * 1.2); // 0 = red, 72 = green-ish at 60
+ gaugeFill.style.stroke = `hsl(${hue}, 72%, 52%)`;
+}
+
+function scoreTierLabel(score) {
+ if (score >= 80) return { label: "Excellent", cls: "tier-green" };
+ if (score >= 65) return { label: "Good", cls: "tier-good" };
+ if (score >= 50) return { label: "Fair", cls: "tier-fair" };
+ if (score >= 35) return { label: "Building", cls: "tier-warn" };
+ return { label: "High Risk", cls: "tier-danger" };
+}
+
+function renderFactorBars(factors) {
+ factorBarsEl.innerHTML = Object.values(factors).map(f => {
+ const pct = Math.round((f.score / f.max) * 100);
+ const color = pct >= 70 ? "var(--brand)" : pct >= 45 ? "var(--brand-2)" : "var(--warn)";
+ return `
+
`;
- });
+ }).join("");
+}
+
+function renderNarrative(helps, hurts) {
+ const helpHtml = helps.length ? `
+
+
Helping your score
+
${helps.map(h => `${h} `).join("")}
+
` : "";
+ const hurtHtml = hurts.length ? `
+
+
Hurting your score
+
${hurts.map(h => `${h} `).join("")}
+
` : "";
+ scoreNarrativeEl.innerHTML = helpHtml + hurtHtml;
+}
+
+function renderCoach(recs, totalSavings) {
+ coachList.innerHTML = recs.map(r => `
+
+
+ ${r.detail}
+ ${r.action} →
+
+ `).join("");
+ coachHeading.textContent = `${recs.length} savings actions found`;
+ coachPill.textContent = "AI-powered";
+ if (totalSavingsEl) totalSavingsEl.textContent = `$${totalSavings}/mo potential`;
+}
+
+// Stored last computed results so Coach view can render them
+let lastCoachRecs = null;
+let lastTotalSavings = 0;
+
+fairscoreForm?.addEventListener("submit", e => {
+ e.preventDefault();
+ const AI = window.FairDriveAI;
+ if (!AI) return;
+
+ const inputs = {
+ vehicle: document.querySelector("#fsVehicle").value,
+ state: document.querySelector("#fsState").value,
+ usHistoryYears: parseInt(document.querySelector("#fsUsHistory").value, 10),
+ intlHistoryYears: parseInt(document.querySelector("#fsIntlHistory").value, 10),
+ annualMiles: parseInt(document.querySelector("#fsMileage").value, 10),
+ coverageLevel: document.querySelector("#fsCoverage").value,
+ cleanRecord: document.querySelector("#fsCleanRecord").checked,
+ defensiveCourse: document.querySelector("#fsDefensive").checked,
+ };
+
+ const fairScore = AI.computeFairScore(inputs);
+ const premium = AI.computePremium(inputs, fairScore);
+ const narrative = AI.getScoreNarrative(inputs, fairScore);
+ const recs = AI.getCoachRecommendations(inputs, fairScore, premium);
+ const totalSav = recs.reduce((sum, r) => sum + (r.savings || 0), 0);
+
+ // Store for coach view
+ lastCoachRecs = recs;
+ lastTotalSavings = totalSav;
+
+ // Reveal results
+ fairscoreResult.hidden = false;
+ premiumCompPanel.hidden = false;
+
+ // Score gauge
+ scoreNumber.textContent = fairScore.total;
+ setGauge(fairScore.total);
+ const tier = scoreTierLabel(fairScore.total);
+ scoreTier.textContent = tier.label;
+ scoreTier.className = `score-tier ${tier.cls}`;
+ scoreBench.textContent = `Market average: 58 · Your score: ${fairScore.total}`;
+ fairscorePill.textContent = `Score: ${fairScore.total}`;
+
+ // Factor bars
+ renderFactorBars(fairScore.factors);
+
+ // Narrative
+ renderNarrative(narrative.helps, narrative.hurts);
+
+ // Premium comparison
+ fairMonthlyEl.textContent = `$${premium.monthly}`;
+ tradMonthlyEl.textContent = `$${premium.tradMonthly}`;
+ annualEstEl.textContent = `$${premium.annualEstimate.toLocaleString()}/yr`;
+ premiumRangeEl.textContent = `$${premium.range[0]} – $${premium.range[1]}/mo`;
+
+ if (premium.fairSavings > 0) {
+ savingsBannerEl.hidden = false;
+ savingsAmountEl.textContent = `$${premium.fairSavings}/mo`;
+ } else {
+ savingsBannerEl.hidden = true;
+ }
+
+ // Sync vehicle to garage
+ setVehicle(inputs.vehicle);
+
+ // Pre-populate coach
+ renderCoach(recs, totalSav);
+ if (totalSavingsEl) totalSavingsEl.textContent = `$${totalSav}/mo potential`;
});
+// Render coach when user navigates there directly (if FairScore already computed)
+viewButtons.forEach(b => {
+ if (b.dataset.view === "coach") {
+ b.addEventListener("click", () => {
+ if (lastCoachRecs) renderCoach(lastCoachRecs, lastTotalSavings);
+ });
+ }
+});
+
+// ── Init ──────────────────────────────────────────────────────────────────────
+
setVehicle(currentVehicle);
diff --git a/index.html b/index.html
index 3a240a2..7a65d85 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,9 @@
Garage
+
+ FairScore
+
Quote
@@ -40,8 +43,8 @@
@@ -71,11 +74,11 @@ $386/mo estimated premium
insurance history and the vehicle has above-average repair cost.
-
- Explain my quote
+
+ Compute FairScore
-
- Check another car
+
+ Explain my quote
@@ -111,6 +114,7 @@ $386/mo estimated premium
+