AI Runway — Product Documentation

A conversational business-diagnosis funnel that collects 30+ strategic data points across financials, marketing, team, and AI maturity, then generates a personalised 20+ section report designed to surface risks and position bespoke consulting as the natural next step.

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:

  1. Collect structured business data through a Tapform-style conversational form (single question per slide, keyboard-driven, mobile-first).
  2. 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.
  3. 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.

Design philosophy: The report is intentionally problem-focused (~80% risks, ~15–20% positives) because the product's job is to make the founder feel seen in their anxiety, then offer a concrete path out. Every fear dial in the config exists to control that ratio precisely.

Architecture

The system has four browser-facing surfaces and one backend process:

Landing
Single-viewport CRO page at /. Purely a hook into the form.
Form
Conversational 30+ question flow at /form. Submits JSON to /api/submit.
Report
At /report?id=…. Renders agent-loading animation then a 20+ section diagnosis.
Admin
Analytics, leads, charts, and fear-dial settings at /admin (login-gated).
Docs
This page, at /docs. Visibility toggled via config.
Server
Single 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

LayerChoiceReasoning
ServerNode.js + Express 4Minimal, battle-tested, zero config.
StorageFlat JSON files (data/leads.json, data/config.json)No external DB. Easy to back up, diff, and migrate later.
FrontendVanilla HTML / CSS / JSNo build step. Every page is independently editable.
ChartsChart.js 4.4.0 (CDN)Bundled via CDN on report and admin pages only.
AuthIn-memory token SetToken returned on login, sent as Bearer. Cleared on restart.
FontsSystem 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

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.

CategoryExamples
IdentityName, WhatsApp, Email, Designation
BusinessIndustry, age, customer type, operating region, core product
Team & OpsTeam size, most demanding function, operational setup
FinancialsAnnual revenue, expenses (% of rev), personal withdrawal (% of rev), ad spend (% of rev), AI spend (% of rev)
MarketingCustomer acquisition channels, social media, CPL, revenue-per-customer
AITools used, AI spend, tried AI before, outcome, top AI priority (multi-select max 3)
Goals & Commitment12-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

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

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

  1. Revenue Diagnostics — Finding profit leaks
  2. Distribution Audit — Channel dependency
  3. AI Lag Detection — Competitive blindspot
  4. Constraint Mapping — Growth bottlenecks
  5. Fragility Scanner — Operational risk
  6. Risk Projection — Trajectory modelling
  7. Competitive Analysis — Industry benchmarks
  8. Diagnosis Assembly — Report synthesis
The animation is cosmetic — it lasts a fixed ~10-11 seconds regardless of data. Its job is to make the output feel considered.

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)

01 Executive Diagnosis
02 Business Risk Score
03 Where You Actually Stand
04 12-Month Trajectory Projection
05 Competitive Gap Analysis
06 What's Actively Going Wrong
07 The Stall Clock
08 Distribution Crisis
09 AI Lag Report
10 Founder Dependency Risk
11 Cost of Inaction
12 Strategic Recommendations
13 90-Day Strategic Roadmap

Optional sections (toggleable)

Business Death Timeline (year-by-year)
Cash Runway Calculator
Market Erosion Curve
Talent & Team Risk
Revenue Concentration
Technology Decay Index
Competitor Acceleration Gap
Silent Revenue Leaks
Founder Burnout Risk
Industry Disruption Watch

Scoring & Forging

The report computes six primary scores from the user's answers:

ScoreRangeInputs
Business Risk Score30–98AI lag, ops maturity, founder dep, CPL blindness, channel concentration, ad spend, expense ratio
Mortality Timeline3–36 monthsDerived from risk score; higher risk = fewer months
Founder Dependency25–97%Team size, operational setup, number of demanding functions
AI Lag Index10–95Whether AI has been tried, AI spend tier
Distribution Fragility30–85Number of distinct customer-acquisition channels
Percentile Rankings5–95 per axisSix 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.

Ethics note: Forging is calibrated to match the report's psychological purpose — not to fabricate data. The underlying raw scores are stored on the lead object and visible in admin. The forge applies only to the rendered report.

Charts & Visuals

SectionChartWhat it shows
02SVG risk ringAnimated 0–100 score circle
03Percentile barsSix horizontal bars with 50th-percentile marker
04Line chart (3 series)Current path, compounded decline, with-intervention
05RadarYou vs industry average vs top 10%
08DoughnutChannel concentration
09Grouped barSector leaders vs you, per function
11Compounding barsCost-of-inaction by quarter
Death TimelineArea lineYear-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:

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

DialDefaultEffect
fear.level75Master dial (0–100). Affects copy intensity and emphasis on problems.
fear.riskBoost+15Points added to the raw business-risk score.
fear.mortalityMultiplier0.55Multiplier on "months until stall". Lower = scarier timeline.
fear.percentileDrop−18Points subtracted from the user's percentile rankings.
fear.compoundingRate×1.35Quarterly multiplier on cost-of-inaction projections.
fear.problemRatio0.85% of report content that is problem-focused.
fear.scoreForgetrueMaster on/off for score forging logic.

Forge ratios

TierCutoffOutput ratioExample
Tier 1 (high)≥ 0.8 of max0.605★ → 3★
Tier 2 (mid)≥ 0.4 of max0.304★ → 1.5★
Tier 3 (low)< 0.40.152★ → 0.75★

Timeline controls

SettingDefaultPurpose
timeline.baseMonths36Baseline months before risk adjustment.
timeline.deathHorizonYears5Years of projection shown on the Death Timeline chart.
timeline.projectionSeverity1.20Steepness 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'
Production note: For production deployment, replace the in-memory 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

RouteAuth?
POST /api/submitPublic (form submission)
GET /api/leads/:idPublic (report hydration)
GET /api/public-configPublic (form + report needs)
GET /api/leadsAdmin
DELETE /api/leads/:idAdmin
GET /api/analyticsAdmin
GET /api/exportAdmin (Bearer or ?token=…)
GET/POST /api/configAdmin

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:

The dummy button is hidden from users by default. Toggle it on at /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