AI Runway — Product Documentation
This document is a complete reference for everything in the system — architecture, data flow, engine logic, API, security, and how to tune the report for your market.
Overview
AI Runway is a single-purpose lead-generation and consulting-qualification engine. It does three things:
- Collect structured business data through a Tapform-style conversational form (single question per slide, keyboard-driven, mobile-first).
- Score and diagnose that business across multiple dimensions — risk, mortality timeline, founder dependency, AI lag, distribution fragility, percentile rankings — using a tunable fear-calibrated engine.
- Deliver a personalised report that names problems vividly, visualises decline trajectories, and positions custom consulting as the recommended next step.
The system is intentionally small: one Express server, flat-file JSON storage, and vanilla HTML/CSS/JS on the client. No build step, no framework, no database server. It's designed to be read in an afternoon and deployed in a minute.
Architecture
The system has four browser-facing surfaces and one backend process:
/. Purely a hook into the form./form. Submits JSON to /api/submit./report?id=…. Renders agent-loading animation then a 20+ section diagnosis./admin (login-gated)./docs. Visibility toggled via config.server.js (Express). Handles routes, auth, JSON storage, config.Request flow
Landing (/) ──→ Form (/form)
│
│ POST /api/submit
▼
leads.json (flat file)
│
│ redirect
▼
Report (/report?id=…)
│
├─ GET /api/leads/:id (answers)
└─ GET /api/public-config (fear dials)
Admin (/admin)
├─ POST /api/login → token (in-memory Set)
├─ GET /api/analytics [Bearer]
├─ GET /api/leads [Bearer]
├─ GET /api/config [Bearer]
├─ POST /api/config [Bearer]
└─ GET /api/export?token=… (CSV download)
Tech Stack
| Layer | Choice | Reasoning |
|---|---|---|
| Server | Node.js + Express 4 | Minimal, battle-tested, zero config. |
| Storage | Flat JSON files (data/leads.json, data/config.json) | No external DB. Easy to back up, diff, and migrate later. |
| Frontend | Vanilla HTML / CSS / JS | No build step. Every page is independently editable. |
| Charts | Chart.js 4.4.0 (CDN) | Bundled via CDN on report and admin pages only. |
| Auth | In-memory token Set | Token returned on login, sent as Bearer. Cleared on restart. |
| Fonts | System stack (Inter fallback) | No font fetch overhead. |
Landing Page /
A deliberately minimal, single-viewport landing page. It never scrolls — on laptop or mobile. The job is to create urgency and push one click to /form.
Key ingredients
- Red urgency kicker: "73% of Indian SMBs plateau before ₹5Cr. Will yours?"
- Headline: Mixed red (problem) + purple gradient (promise) — "Your business has problems. More than you think."
- Subtitle: Names the mechanism — 8 AI agents, 32 critical points, silent leaks.
- CTA: "Run My Business Diagnosis" with an arrow that translates on hover.
- Trust row: Four one-line proofs (32 data points, 8 agents, 1,000+ benchmarks, instant).
- Status bar: A fake-but-plausible "247 diagnoses today" live ticker.
All sizing uses clamp() and multiple height-based @media queries so the page compresses cleanly down to 520px tall.
Diagnostic Form /form
A Tapform-style conversational UI — one question per slide, full keyboard control, mobile-optimised. Answers are kept in a single in-memory object, then POSTed to /api/submit on the final screen.
Question Categories
The form asks ~30+ questions across seven categories. Order is deliberate: identity first (to build trust), sensitive financials mid-form (after the user is committed), commitment signals last.
| Category | Examples |
|---|---|
| Identity | Name, WhatsApp, Email, Designation |
| Business | Industry, age, customer type, operating region, core product |
| Team & Ops | Team size, most demanding function, operational setup |
| Financials | Annual revenue, expenses (% of rev), personal withdrawal (% of rev), ad spend (% of rev), AI spend (% of rev) |
| Marketing | Customer acquisition channels, social media, CPL, revenue-per-customer |
| AI | Tools used, AI spend, tried AI before, outcome, top AI priority (multi-select max 3) |
| Goals & Commitment | 12-month goal, biggest challenge, what solving it means, urgency, decision maker, blockers |
Dynamic percentage options
Financial questions (expenses, personal withdrawal, ad spend, AI spend) generate their options dynamically from the user's selected revenue band. For example, if the user picks "₹1Cr – ₹5Cr" revenue, the "Monthly AI Spend" question generates ranges in absolute rupees mapped to that band's midpoint, rather than showing arbitrary bands.
Multi-select behaviour
- Select All and Clear buttons appear on multi-select questions.
- Questions with
maxSelectlimits (e.g., AI priority caps at 3) enforce it silently. - Exclusive options ("None of these", "Not sure yet", "We don't actively acquire…", "Other") clear all other selections when clicked, and vice-versa.
Conditional visibility
Q22 (AI outcome) is skipped if the user said they haven't tried AI. Q29 (who else is involved) is skipped if they said they're the sole decision-maker. Logic lives in the isVisible function at the top of form/index.html.
Keyboard controls
- Enter or →: next question
- Backspace in an empty input, or ←: previous question
- 1–9: select option by index on choice questions
Report Generation /report?id=…
Stage 1 — Loading Animation
On load, the page shows 8 AI agents "scanning" the responses. Each agent has a name, role, and 3–4 log lines that stream in sequence. Agents start staggered (900ms between each) and take ~3.6s each. The overall progress bar tracks completion.
The 8 Agents
- Revenue Diagnostics — Finding profit leaks
- Distribution Audit — Channel dependency
- AI Lag Detection — Competitive blindspot
- Constraint Mapping — Growth bottlenecks
- Fragility Scanner — Operational risk
- Risk Projection — Trajectory modelling
- Competitive Analysis — Industry benchmarks
- Diagnosis Assembly — Report synthesis
Stage 2 — Report Sections
The rendered report contains 13 core sections plus up to 10 optional sections (toggled from the admin settings panel). Core sections are always rendered; optional sections are controlled via config.sections.
Core sections (always on)
Optional sections (toggleable)
Scoring & Forging
The report computes six primary scores from the user's answers:
| Score | Range | Inputs |
|---|---|---|
| Business Risk Score | 30–98 | AI lag, ops maturity, founder dep, CPL blindness, channel concentration, ad spend, expense ratio |
| Mortality Timeline | 3–36 months | Derived from risk score; higher risk = fewer months |
| Founder Dependency | 25–97% | Team size, operational setup, number of demanding functions |
| AI Lag Index | 10–95 | Whether AI has been tried, AI spend tier |
| Distribution Fragility | 30–85 | Number of distinct customer-acquisition channels |
| Percentile Rankings | 5–95 per axis | Six axes: revenue, AI maturity, automation, marketing, team leverage, margin |
Score Forging
When fear.scoreForge is enabled, positive scores get compressed downward to create urgency. The forge function applies the following rule before rendering:
ratio = score / maxScore
if ratio ≥ tier1Cutoff (default 0.8): // high scores
output = maxScore × tier1Ratio // default 0.60 — so 5★ → 3★
else if ratio ≥ tier2Cutoff (default 0.4): // mid scores
output = maxScore × tier2Ratio // default 0.30 — so 4★ → 1.5★
else: // low scores
output = maxScore × tier3Ratio // default 0.15 — so 2★ → 0.75★
The same rule is used for percentiles: a user sitting at the 80th percentile on a positive axis would see themselves shown at ~48th. Risk scores (which are already "bad" metrics) are not forged — they're only boosted by fear.riskBoost.
Charts & Visuals
| Section | Chart | What it shows |
|---|---|---|
| 02 | SVG risk ring | Animated 0–100 score circle |
| 03 | Percentile bars | Six horizontal bars with 50th-percentile marker |
| 04 | Line chart (3 series) | Current path, compounded decline, with-intervention |
| 05 | Radar | You vs industry average vs top 10% |
| 08 | Doughnut | Channel concentration |
| 09 | Grouped bar | Sector leaders vs you, per function |
| 11 | Compounding bars | Cost-of-inaction by quarter |
| Death Timeline | Area line | Year-by-year revenue decline projection |
Admin Dashboard /admin
The admin dashboard is gated by a login overlay. After sign-in, a token is stored in localStorage and sent on every request. Four pages:
- Overview — Six stat cards (total leads, top industry, revenue band, AI adoption, ready-to-move, top AI priority) plus recent submissions table.
- Charts — Nine breakdown visualisations of all lead data.
- All Leads — Searchable table; click a row to open a full modal with every field and contact actions.
- Settings — Fear dials, forge ratios, timeline controls, section toggles, feature flags. See Config & Fear Dials.
API Endpoints
Public
POST /api/submit
Submits a lead. Body is a JSON object of the form answers. Returns { success: true, id }. Used by the form on final step and also by the report's callback form.
GET /api/leads/:id
Fetches a single lead by ID. Public so the report page can hydrate itself without auth.
GET /api/public-config
Returns the subset of config the form and report need: features, fear dials, forge ratios, timeline, and section toggles. Never returns meta or internal admin fields.
Admin (require Authorization: Bearer <token>)
POST /api/login
Body: { email, password }. Returns { success, token }. Credentials are hardcoded to one admin in server.js.
POST /api/logout
Invalidates the session token server-side.
GET /api/me
Returns { authenticated: boolean } — used by the admin page to decide whether to show the login overlay.
GET /api/leads
Returns all leads, newest first.
DELETE /api/leads/:id
Deletes a lead by ID.
GET /api/analytics
Returns aggregated counts: by industry, revenue, expenses, team size, AI spend, urgency, etc. Also submissionsByDay and recentLeads.
GET /api/export
CSV download of all leads. Accepts token via ?token=… query so direct links work.
GET /api/config POST /api/config
Read or merge-update the full config JSON. POST accepts a partial object and merges shallowly at the top level, deeply at one level below.
POST /api/config/reset
Resets config to hardcoded defaults.
Config & Fear Dials
All report behaviour is tunable from /admin → Settings without touching code. The file on disk is data/config.json. The API endpoints GET/POST /api/config read and write it.
Fear dials
| Dial | Default | Effect |
|---|---|---|
fear.level | 75 | Master dial (0–100). Affects copy intensity and emphasis on problems. |
fear.riskBoost | +15 | Points added to the raw business-risk score. |
fear.mortalityMultiplier | 0.55 | Multiplier on "months until stall". Lower = scarier timeline. |
fear.percentileDrop | −18 | Points subtracted from the user's percentile rankings. |
fear.compoundingRate | ×1.35 | Quarterly multiplier on cost-of-inaction projections. |
fear.problemRatio | 0.85 | % of report content that is problem-focused. |
fear.scoreForge | true | Master on/off for score forging logic. |
Forge ratios
| Tier | Cutoff | Output ratio | Example |
|---|---|---|---|
| Tier 1 (high) | ≥ 0.8 of max | 0.60 | 5★ → 3★ |
| Tier 2 (mid) | ≥ 0.4 of max | 0.30 | 4★ → 1.5★ |
| Tier 3 (low) | < 0.4 | 0.15 | 2★ → 0.75★ |
Timeline controls
| Setting | Default | Purpose |
|---|---|---|
timeline.baseMonths | 36 | Baseline months before risk adjustment. |
timeline.deathHorizonYears | 5 | Years of projection shown on the Death Timeline chart. |
timeline.projectionSeverity | 1.20 | Steepness multiplier on decline curve. |
Data Model
Lead (data/leads.json)
An array of objects. Each lead has:
{
"id": "uuid-v4",
"submittedAt": "2026-04-25T12:34:56.789Z",
"name": "Amritanshu",
"whatsapp": "+91…",
"email": "…",
"designation": "Founder / CEO",
"industry": "SaaS / Tech",
"business_age": "3–7 years",
"customer_type": "B2B",
"where_operates": "India",
"core_product": "…",
"team_size": "6 – 15",
"demanding_function": ["Sales", "Operations"],
"operational_setup": "Mix of manual + some tools",
"annual_revenue": "₹1Cr – ₹5Cr",
"annual_expenses": "40–60% of revenue",
"personal_withdrawal": "…",
"ad_spend": "…",
"customer_acquisition": ["Organic", "Referrals"],
"social_media": "…",
"cpl": "₹500 – ₹1,500",
"revenue_per_customer": "₹50K – ₹2L",
"tools_used": ["Notion", "Google Workspace"],
"ai_spend": "1–3% of revenue",
"tried_ai": "Yes, but results were underwhelming",
"ai_outcome": "…",
"ai_priority": ["Marketing content", "Lead qualification"],
"goal_12m": "…",
"biggest_challenge": "…",
"solving_means": "…",
"urgency": "Within 30 days",
"decision_maker": "Yes, solo",
"who_else": null,
"stop_reason": "Nothing — I'm ready to move",
"callback_requested": true,
"callback_name": "…",
"callback_phone": "…",
"callback_time": "…"
}
Config (data/config.json)
See Config & Fear Dials above for the full shape.
Auth & Security
Auth is single-admin, token-based, in-memory. The credentials are hardcoded in server.js:
ADMIN_EMAIL = 'amritanshu@scalex.club'
ADMIN_PASSWORD = '0000'
SESSIONS Set with Redis or JWT. The current implementation loses all tokens on server restart, and only allows one hardcoded admin. Also rotate the password — it's deliberately weak for local use.
Protected vs public routes
| Route | Auth? |
|---|---|
POST /api/submit | Public (form submission) |
GET /api/leads/:id | Public (report hydration) |
GET /api/public-config | Public (form + report needs) |
GET /api/leads | Admin |
DELETE /api/leads/:id | Admin |
GET /api/analytics | Admin |
GET /api/export | Admin (Bearer or ?token=…) |
GET/POST /api/config | Admin |
Dummy Data Mode
When features.dummyDataEnabled is true in config, a hidden "Fill with sample data" button appears on the form. It auto-populates every question with realistic sample values, letting you walk the form and report end-to-end for QA in one click.
Toggling this is the fastest way to:
- See how the report renders for different risk profiles
- Visually verify all new sections show correctly
- Test the agent-loading animation without typing
- Confirm that callback submissions write through to
/admin
/admin → Settings → Features, then open /form — the button appears at the bottom of the intro slide.
Running Locally
cd "AI Runway"
npm install
node server.js
# → http://localhost:3000
The server auto-creates data/, data/leads.json, and data/config.json on first boot if they don't exist.
There's no build step. Edit any HTML/CSS/JS file and refresh the browser — changes are live immediately. Config changes from /admin → Settings take effect on the next report render.
File Structure
AI Runway/
├── server.js # Express server, routes, auth, config
├── package.json
├── data/
│ ├── leads.json # All form submissions (auto-created)
│ └── config.json # Fear dials & feature flags (auto-created)
└── public/
├── landing/ # / — marketing page
│ └── index.html
├── form/ # /form — conversational diagnostic
│ └── index.html
├── report/ # /report — generated diagnosis
│ └── index.html
├── admin/ # /admin — dashboard + settings
│ └── index.html
└── docs/ # /docs — this page
└── index.html