any this field of vestibular rehabilitation any scope of developing web based app
vestibular rehabilitation web app digital health 2024 2025
vestibular rehabilitation digital technology app
vestibular rehabilitation telehealth gaps unmet needs patient outcomes 2025
PMID: 37695671
vestibular rehab app existing software BPPV dizziness home exercise program 2024
| Gap | What a Web App Can Do |
|---|---|
| Home exercise delivery | Guided video/animation-based exercise programs (Cawthorne-Cooksey, Epley, Brandt-Daroff, gaze stabilization) |
| Symptom journaling | Daily dizziness severity logs using validated scales (DHI, VSS-SF, ABC scale) |
| Outcome tracking | Visual dashboards showing symptom trends over time for both patient and clinician |
| Clinician-patient communication | Asynchronous messaging, exercise prescription remotely |
| Education | Condition-specific explainers (BPPV canal repositioning, compensation theory) |
| Fall risk screening | Simple questionnaires + self-test guidance (Romberg, tandem stance) |
| Adherence reminders | Push notifications and session logging |
| Triage support | Symptom checkers that flag red flags (central causes) and recommend urgency |
Create a business plan outline for a vestibular rehab web app
vestibular disorders dizziness prevalence India patients statistics 2024
India digital health app market size D2C consumer healthcare 2025 revenue model
ls -la /home/daytona/workspace/ 2>/dev/null || echo "workspace does not exist yet"
mkdir -p /home/daytona/workspace/vestibular-app-bizplan
~/vestibular-app-bizplan/generate_bizplan.py
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import cm, mm
from reportlab.platypus import (
SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
HRFlowable, PageBreak, KeepTogether
)
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_JUSTIFY
from reportlab.pdfgen import canvas
from reportlab.platypus.doctemplate import PageTemplate, BaseDocTemplate
from reportlab.platypus.frames import Frame
import os
OUTPUT_PATH = "/home/daytona/workspace/vestibular-app-bizplan/VestibularRehab_BusinessPlan.pdf"
# ─── Color palette ───────────────────────────────────────────────────
PRIMARY = colors.HexColor("#1A4B7A") # deep navy blue
ACCENT = colors.HexColor("#2E8BC0") # medium blue
LIGHT_ACCENT = colors.HexColor("#D6EAF8") # pale blue fill
HIGHLIGHT = colors.HexColor("#E67E22") # orange accent
DARK_TEXT = colors.HexColor("#1C2833")
MID_GRAY = colors.HexColor("#7F8C8D")
LIGHT_GRAY = colors.HexColor("#F2F3F4")
WHITE = colors.white
# ─── Page numbering ──────────────────────────────────────────────────
class NumberedCanvas(canvas.Canvas):
def __init__(self, *args, **kwargs):
canvas.Canvas.__init__(self, *args, **kwargs)
self._saved_page_states = []
def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
def save(self):
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.draw_page_number(num_pages)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_number(self, page_count):
page_num = self._saved_page_states.index(dict(self.__dict__)) + 1
if page_num == 1:
return # skip cover page
self.setFont("Helvetica", 8)
self.setFillColor(MID_GRAY)
self.drawRightString(A4[0] - 1.5*cm, 1.2*cm,
f"VestibBalance | Business Plan 2026 | Page {page_num} of {page_count}")
# header line
self.setStrokeColor(ACCENT)
self.setLineWidth(0.5)
self.line(1.5*cm, A4[1] - 1.3*cm, A4[0] - 1.5*cm, A4[1] - 1.3*cm)
self.setFillColor(ACCENT)
self.setFont("Helvetica-Bold", 8)
self.drawString(1.5*cm, A4[1] - 1.1*cm, "VestibBalance — Vestibular Rehabilitation Web Platform")
# ─── Cover page drawn directly on canvas ────────────────────────────
def draw_cover(c, doc):
W, H = A4
# Background gradient-like blocks
c.setFillColor(PRIMARY)
c.rect(0, H * 0.45, W, H * 0.55, fill=1, stroke=0)
c.setFillColor(ACCENT)
c.rect(0, H * 0.38, W, H * 0.08, fill=1, stroke=0)
c.setFillColor(LIGHT_GRAY)
c.rect(0, 0, W, H * 0.38, fill=1, stroke=0)
# Decorative circle top-right
c.setFillColor(colors.HexColor("#1E5799"))
c.circle(W - 2*cm, H - 2*cm, 3.5*cm, fill=1, stroke=0)
# Logo placeholder text
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 28)
c.drawString(1.8*cm, H - 3.5*cm, "VestibBalance")
c.setFont("Helvetica", 11)
c.setFillColor(LIGHT_ACCENT)
c.drawString(1.8*cm, H - 4.2*cm, "Smart Vestibular Rehabilitation — Anywhere, Anytime")
# Tagline bar
c.setFillColor(HIGHLIGHT)
c.rect(1.8*cm, H * 0.50, 8*cm, 0.08*cm, fill=1, stroke=0)
# Main title
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 20)
c.drawString(1.8*cm, H * 0.52, "BUSINESS PLAN")
c.setFont("Helvetica", 13)
c.setFillColor(LIGHT_ACCENT)
c.drawString(1.8*cm, H * 0.485, "Direct-to-Consumer | India Market | 2026–2029")
# Mid band text
c.setFillColor(WHITE)
c.setFont("Helvetica-Bold", 12)
c.drawCentredString(W/2, H * 0.41, "Transforming Dizziness Care Through Digital Rehabilitation")
# Bottom section details
details = [
("Prepared for:", "Internal Review / Investor Presentation"),
("Version:", "1.0 — June 2026"),
("Geography:", "India (Tier 1 & Tier 2 Cities)"),
("Business Model:", "Direct-to-Consumer (D2C) SaaS Subscription"),
]
c.setFont("Helvetica-Bold", 9)
c.setFillColor(PRIMARY)
y = H * 0.30
for label, value in details:
c.setFillColor(MID_GRAY)
c.setFont("Helvetica", 9)
c.drawString(2*cm, y, label)
c.setFillColor(DARK_TEXT)
c.setFont("Helvetica-Bold", 9)
c.drawString(6.5*cm, y, value)
y -= 0.7*cm
# Confidential footer
c.setFillColor(MID_GRAY)
c.setFont("Helvetica-Oblique", 8)
c.drawCentredString(W/2, 1.5*cm,
"CONFIDENTIAL — This document contains proprietary business information.")
# ─── Style definitions ───────────────────────────────────────────────
styles = getSampleStyleSheet()
def S(name, **kw):
return ParagraphStyle(name, **kw)
sty_h1 = S("H1",
fontName="Helvetica-Bold", fontSize=15, textColor=PRIMARY,
spaceBefore=18, spaceAfter=6, leading=20)
sty_h2 = S("H2",
fontName="Helvetica-Bold", fontSize=11, textColor=ACCENT,
spaceBefore=14, spaceAfter=4, leading=15)
sty_h3 = S("H3",
fontName="Helvetica-Bold", fontSize=10, textColor=DARK_TEXT,
spaceBefore=10, spaceAfter=3, leading=13)
sty_body = S("Body",
fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT,
spaceBefore=3, spaceAfter=3, leading=14, alignment=TA_JUSTIFY)
sty_bullet = S("Bullet",
fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT,
spaceBefore=2, spaceAfter=2, leading=13, leftIndent=14,
bulletIndent=4, alignment=TA_LEFT)
sty_caption = S("Caption",
fontName="Helvetica-Oblique", fontSize=8.5, textColor=MID_GRAY,
spaceBefore=2, spaceAfter=6, alignment=TA_CENTER)
sty_highlight = S("Highlight",
fontName="Helvetica-Bold", fontSize=9.5, textColor=PRIMARY,
spaceBefore=3, spaceAfter=3, leading=13)
sty_toc_title = S("TOCTitle",
fontName="Helvetica-Bold", fontSize=10, textColor=DARK_TEXT,
spaceBefore=4, spaceAfter=2, leading=14)
sty_toc_entry = S("TOCEntry",
fontName="Helvetica", fontSize=9.5, textColor=DARK_TEXT,
spaceBefore=2, spaceAfter=2, leading=13, leftIndent=8)
def hr():
return HRFlowable(width="100%", thickness=0.5, color=ACCENT,
spaceBefore=4, spaceAfter=8)
def section_header(title, number):
return [
Spacer(1, 0.3*cm),
Table([[Paragraph(f"<font color='#{ACCENT.hexval()[2:]}'>Section {number}</font> {title}",
sty_h1)]],
colWidths=[17*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_ACCENT),
("LEFTPADDING", (0,0), (-1,-1), 10),
("RIGHTPADDING", (0,0), (-1,-1), 10),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("ROUNDEDCORNERS", [4]),
])),
Spacer(1, 0.2*cm),
]
def bullet(text):
return Paragraph(f"<bullet>\u2022</bullet> {text}", sty_bullet)
def kv_table(rows, col_w=None):
"""Two-column key-value styled table."""
if not col_w:
col_w = [5.5*cm, 11.5*cm]
data = []
for k, v in rows:
data.append([
Paragraph(f"<b>{k}</b>", sty_body),
Paragraph(v, sty_body)
])
return Table(data, colWidths=col_w,
style=TableStyle([
("BACKGROUND", (0,0), (0,-1), LIGHT_GRAY),
("BACKGROUND", (1,0), (1,-1), WHITE),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 8),
("RIGHTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
def stat_box(items):
"""Horizontal stat cards: list of (value, label)."""
n = len(items)
cw = [17*cm / n] * n
row_vals = [Paragraph(f"<font color='#{HIGHLIGHT.hexval()[2:]}'><b>{v}</b></font>",
S("sv", fontName="Helvetica-Bold", fontSize=16,
textColor=HIGHLIGHT, alignment=TA_CENTER))
for v, _ in items]
row_labs = [Paragraph(l, S("sl", fontName="Helvetica", fontSize=8,
textColor=MID_GRAY, alignment=TA_CENTER))
for _, l in items]
t = Table([row_vals, row_labs], colWidths=cw,
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_ACCENT),
("BOX", (0,0), (-1,-1), 0.5, ACCENT),
("INNERGRID", (0,0), (-1,-1), 0.3, ACCENT),
("TOPPADDING", (0,0), (-1,-1), 10),
("BOTTOMPADDING", (0,0), (-1,-1), 8),
]))
return t
# ─── Build story ─────────────────────────────────────────────────────
story = []
# ── TABLE OF CONTENTS ─────────────────────────────────────────────
story.append(PageBreak())
story.append(Paragraph("Table of Contents", sty_h1))
story.append(hr())
toc_entries = [
("1", "Executive Summary"),
("2", "Problem Statement"),
("3", "Solution & Product Description"),
("4", "Market Analysis"),
("5", "Target User Personas"),
("6", "Product Features & Roadmap"),
("7", "Business Model & Revenue Strategy"),
("8", "Competitive Landscape"),
("9", "Marketing & Growth Strategy"),
("10", "Technology Architecture"),
("11", "Regulatory & Compliance Considerations"),
("12", "Team & Advisory Requirements"),
("13", "Financial Projections"),
("14", "Risk Analysis & Mitigation"),
("15", "Conclusion & Next Steps"),
]
for num, title in toc_entries:
story.append(Paragraph(f"<font color='#{ACCENT.hexval()[2:]}'><b>Section {num}</b></font> — {title}", sty_toc_entry))
story.append(PageBreak())
# ── SECTION 1: EXECUTIVE SUMMARY ──────────────────────────────────
story += section_header("Executive Summary", "1")
story.append(Paragraph(
"VestibBalance is a direct-to-consumer web-based platform that delivers clinically validated "
"vestibular rehabilitation therapy (VRT) to patients suffering from dizziness, vertigo, and "
"balance disorders — without requiring specialist visits. Built for the Indian market, the "
"platform combines guided exercise programs, symptom tracking, validated outcome measures, "
"and educational content, enabling patients to manage their condition from home with "
"clinical-grade support.", sty_body))
story.append(Spacer(1, 0.3*cm))
story.append(stat_box([
("1 in 3", "Adults over 40 experience vestibular symptoms"),
("~19B USD", "India Digital Health Market (2025)"),
("18%+", "Market CAGR through 2034"),
("<5", "Vestibular-specific digital platforms globally"),
]))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("<b>Mission:</b> Make evidence-based vestibular rehabilitation accessible, affordable, "
"and effective for every patient in India — regardless of geography.", sty_body))
story.append(Paragraph("<b>Vision:</b> To become India's leading digital platform for vestibular health by 2029, "
"serving 100,000+ active users across Tier 1 and Tier 2 cities.", sty_body))
story.append(Spacer(1, 0.2*cm))
story.append(kv_table([
("Product Type", "Web-based SaaS platform (mobile-responsive, no app install required)"),
("Revenue Model", "Freemium + monthly/annual subscription; clinic referral partnerships"),
("Target Users", "Adults 30–70 years with BPPV, vestibular neuritis, Meniere's disease, PPPD, post-concussion dizziness"),
("Launch Market", "India — Tier 1 cities (Mumbai, Delhi, Bengaluru, Hyderabad, Chennai) in Year 1"),
("Funding Ask", "INR 1.5 Crore seed funding for MVP development, clinical validation, and first 1,000 users"),
]))
# ── SECTION 2: PROBLEM STATEMENT ──────────────────────────────────
story.append(PageBreak())
story += section_header("Problem Statement", "2")
story.append(Paragraph(
"Vestibular disorders are among the most common yet underdiagnosed neurological conditions. "
"In India, access to specialist care is severely limited, leading to delayed diagnosis, "
"unnecessary investigations, and a heavy psychological burden on patients.", sty_body))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>The Core Problems:</b>", sty_h3))
probs = [
("Specialist scarcity", "Fewer than 200 trained vestibular physiotherapists serve India's 1.4 billion population. Most are concentrated in metros."),
("Misdiagnosis & delay", "Average time from symptom onset to correct diagnosis exceeds 18 months. Patients undergo unnecessary CT/MRI scans averaging INR 8,000–15,000 per scan."),
("Poor home adherence", "Paper exercise handouts yield <35% adherence at 6 weeks. Patients perform exercises incorrectly without visual guidance."),
("No outcome tracking", "Clinicians have no visibility into how patients progress between visits, leading to suboptimal treatment adjustment."),
("Psychological burden", "Up to 65% of vestibular patients develop secondary anxiety and depression (Frontiers in Neurology, 2024), yet this is rarely addressed in standard care."),
("Geographic inequity", "Patients in Tier 2/3 cities and rural India have virtually no access to vestibular rehabilitation services."),
]
story.append(kv_table(probs, col_w=[4.5*cm, 12.5*cm]))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph(
"A 2025 scoping review (Malaysian Journal of Medical Sciences) confirmed that home-based "
"vestibular rehabilitation significantly reduces dizziness, improves balance and gait, lowers "
"fall risk, and enhances quality of life — validating the clinical basis for a digital program.", sty_body))
# ── SECTION 3: SOLUTION & PRODUCT ─────────────────────────────────
story.append(PageBreak())
story += section_header("Solution & Product Description", "3")
story.append(Paragraph(
"VestibBalance delivers a structured, condition-specific rehabilitation program entirely through "
"the browser. No hardware, no app download, no specialist required for enrollment.", sty_body))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>Platform Modules:</b>", sty_h3))
modules = [
["Module", "Description", "Clinical Basis"],
["Intake & Triage", "Symptom questionnaire + red-flag screening. Routes to appropriate program.", "DHI, VVAS, red-flag checklists"],
["Exercise Library", "Video-guided programs for BPPV, unilateral hypofunction, bilateral hypofunction, PPPD, post-concussion dizziness.", "Cawthorne-Cooksey, Gaze Stabilization, Epley Maneuver protocols"],
["Symptom Diary", "Daily dizziness severity logs with provocation triggers. Graphs trends over time.", "VSS-SF, VAS dizziness scale"],
["Outcome Tracker", "Auto-scored DHI (Dizziness Handicap Inventory), charted and benchmarked against recovery curves.", "DHI normative data"],
["Education Hub", "Condition-specific explainers: VOR mechanism, habituation, compensation theory, fall prevention.", "Evidence-based patient education"],
["Progress Dashboard", "Visual weekly summary showing exercise completion, symptom trend, and milestone achievements.", "Behaviour change / gamification"],
["Clinician Sharing", "One-click PDF report of outcomes shareable with the patient's ENT/neurologist.", "Continuity of care"],
["Anxiety Screener", "GAD-7 / PHQ-2 embedded for secondary psychological symptoms with guided breathing exercises.", "Comorbidity management"],
]
t = Table(modules, colWidths=[3.5*cm, 7.5*cm, 5.5*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), PRIMARY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8.5),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t)
# ── SECTION 4: MARKET ANALYSIS ────────────────────────────────────
story.append(PageBreak())
story += section_header("Market Analysis", "4")
story.append(Paragraph("<b>Total Addressable Market (TAM)</b>", sty_h3))
story.append(Paragraph(
"India has ~1.4 billion people. Global studies report vestibular disorder prevalence of "
"approximately 35% in adults over 40. India's 40+ population exceeds 450 million people, "
"suggesting a TAM of 150+ million individuals with current or future vestibular symptoms.", sty_body))
story.append(Spacer(1, 0.1*cm))
story.append(stat_box([
("150M+", "TAM — Adults 40+ with vestibular symptoms"),
("15M", "SAM — Urban, smartphone-literate, 35–70 yrs"),
("150K", "SOM — Year 3 target (1% SAM capture)"),
]))
story.append(Spacer(1, 0.3*cm))
market_data = [
("India Digital Health Market (2025)", "USD 19.14 Billion"),
("Projected Market Size (2034)", "USD 90 Billion"),
("CAGR (2026–2034)", "18.06%"),
("Smartphone penetration in India (2025)", ">750 million users"),
("Urban internet users", ">500 million — primary platform target"),
("Vestibular disorder share of dizziness complaints", "~27% (BPPV alone: 14–20% of dizziness cases)"),
]
story.append(kv_table(market_data))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>Key Market Drivers:</b>", sty_h3))
for item in [
"Rising geriatric population — India's 60+ population will reach 340 million by 2050",
"Post-COVID-19 acceleration of telehealth adoption across urban and semi-urban India",
"Ayushman Bharat Digital Mission (ABDM) promoting digital health ID and connected care",
"Growing health insurance penetration driving demand for preventive and rehabilitative care",
"Increased awareness of dizziness management through ENT and neurology OPDs",
"Urban lifestyle factors (screen time, sedentary habits) increasing vestibular migraine prevalence",
]:
story.append(bullet(item))
# ── SECTION 5: USER PERSONAS ──────────────────────────────────────
story.append(PageBreak())
story += section_header("Target User Personas", "5")
personas = [
["Persona", "Profile", "Pain Point", "How VestibBalance Helps"],
["Rekha, 52\nHousewife, Chennai",
"Diagnosed BPPV, referred to VRT by ENT. Lives 40 km from nearest physiotherapist.",
"Cannot travel for frequent sessions; scared to perform Epley alone.",
"Guided Epley walkthrough at home; symptom diary reassures her; clinician-share PDF."],
["Arjun, 38\nIT professional, Bengaluru",
"Vestibular migraine with dizziness during screen use. High anxiety about symptoms.",
"Misses workdays; anxious that dizziness = serious disease.",
"Education module demystifies his condition; GAD-7 screening; gaze stabilization exercises."],
["Sunita, 67\nRetired teacher, Lucknow",
"Unilateral vestibular hypofunction post-neuritis. High fall risk.",
"No access to specialist; family worried about falls.",
"Gaze stabilization + balance retraining program; fall-risk education; progress tracking."],
["Dr. Meera, 44\nENT surgeon, Mumbai",
"Refers patients for VRT but has no tool to send structured home programs.",
"Patients forget instructions; no way to track compliance remotely.",
"Clinic referral code — assigns digital program; receives progress reports."],
]
t = Table(personas, colWidths=[3*cm, 4*cm, 5*cm, 5*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), ACCENT),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t)
# ── SECTION 6: PRODUCT ROADMAP ────────────────────────────────────
story.append(PageBreak())
story += section_header("Product Features & Roadmap", "6")
roadmap = [
["Phase", "Timeline", "Features", "Milestone"],
["MVP\nPhase 1", "Q3 2026\n(3 months)",
"User registration & onboarding\nDHI intake questionnaire\nBPPV exercise program (Epley, Semont, Brandt-Daroff)\nSymptom diary (daily log)\nBasic progress dashboard\nEmail reminders",
"100 beta users\nClinical advisor onboarded"],
["Growth\nPhase 2", "Q4 2026 –\nQ1 2027\n(6 months)",
"Full exercise library (5 condition programs)\nVOR gaze stabilization module\nGAD-7 anxiety screener\nClinician sharing (PDF report)\nRazorpay payment integration\nHindi language support",
"1,000 paying users\nINR 5L MRR target"],
["Scale\nPhase 3", "Q2 2027 –\nQ4 2027\n(9 months)",
"Clinic partner portal (B2B referrals)\nWearable accelerometer integration (exercise quality)\nLive group Q&A with vestibular physiotherapist (monthly)\nTamil, Telugu, Kannada localization\nAndroid PWA packaging",
"10,000 active users\nClinic partnerships (20+)"],
["Expansion\nPhase 4", "2028–2029",
"AI-driven personalized program adaptation\nTelehealth video consult booking\nInsurance tie-ups (ICICI Lombard, Star Health)\nContent partnerships with ENT/neurology societies",
"50,000+ users\nBreakeven"],
]
t = Table(roadmap, colWidths=[2.5*cm, 2.8*cm, 7.5*cm, 4.2*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), PRIMARY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t)
# ── SECTION 7: BUSINESS MODEL ─────────────────────────────────────
story.append(PageBreak())
story += section_header("Business Model & Revenue Strategy", "7")
story.append(Paragraph("<b>Primary Revenue Streams:</b>", sty_h3))
rev_data = [
["Stream", "Model", "Price Point", "Notes"],
["Free Tier", "Freemium", "INR 0", "BPPV self-check, 3 exercises, 7-day symptom log — drives acquisition"],
["Basic Plan", "Monthly subscription", "INR 299/month", "Full exercise library, symptom diary, dashboard"],
["Premium Plan", "Monthly subscription", "INR 499/month", "Basic + clinician sharing, outcome reports, anxiety screener"],
["Annual Plan", "Annual subscription", "INR 3,599/year", "Premium features, 40% discount vs monthly — improves LTV"],
["Clinic Referral Pack", "B2B SaaS per seat", "INR 1,500/patient/year", "ENT/neurology/physiotherapy clinics assign programs to patients"],
["Sponsored Content", "Pharma partnerships", "Revenue share", "Vertigo medication brands (betahistine, cinnarizine) — Phase 3+"],
]
t = Table(rev_data, colWidths=[3.5*cm, 3.5*cm, 3.5*cm, 6.5*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), ACCENT),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t)
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("<b>Unit Economics (Year 2 Target):</b>", sty_h3))
story.append(kv_table([
("ARPU (Average Revenue Per User)", "INR 3,800/year (blended Premium + Annual mix)"),
("CAC (Customer Acquisition Cost)", "INR 800–1,200 (SEO + ENT referral + Meta ads)"),
("LTV (Lifetime Value)", "INR 7,600 (avg. 2-year engagement for chronic conditions)"),
("LTV : CAC Ratio", "~6:1 — healthy for a D2C health SaaS"),
("Gross Margin Target", "72–78% (low COGS: hosting, payment gateway, content updates)"),
("Monthly Burn Rate (Year 1)", "INR 4.5–6 Lakhs (team of 4 + infra + marketing)"),
]))
# ── SECTION 8: COMPETITIVE LANDSCAPE ──────────────────────────────
story.append(PageBreak())
story += section_header("Competitive Landscape", "8")
story.append(Paragraph(
"There is no dominant, vestibular-specific digital platform in India or globally. "
"The closest competitors are generic physiotherapy apps and hardware-based VR systems.", sty_body))
story.append(Spacer(1, 0.2*cm))
comp_data = [
["Competitor", "Type", "Vestibular Specific?", "India Presence", "Gap We Fill"],
["Physitrack", "Generic physio HEP", "No", "Limited", "No vestibular intelligence, no D2C model"],
["Rehab My Patient", "Generic physio HEP", "No", "No", "Clinician-only tool, no patient self-service"],
["PhysioSensing / VR platforms", "Hardware VR system", "Yes", "No", "Requires hardware; INR 5–20L cost; not accessible"],
["YouTube / generic content", "Free videos", "Partial", "Yes", "No personalization, no outcome tracking, unsafe"],
["Hospital OPD programs", "In-person service", "Yes", "Metro only", "Geographic barrier; high cost; limited availability"],
["VestibBalance (us)", "Web SaaS, D2C", "Yes — fully", "India-first", "Evidence-based, affordable, accessible, tracked"],
]
t = Table(comp_data, colWidths=[3.5*cm, 3*cm, 2.5*cm, 2.5*cm, 5.5*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), PRIMARY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("BACKGROUND", (0,-1), (-1,-1), colors.HexColor("#D5F5E3")),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTNAME", (0,-1), (-1,-1), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 6),
("RIGHTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t)
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>Our Moats:</b>", sty_h3))
for m in [
"Condition-specific clinical logic — not a generic exercise app",
"Validated outcome tools (DHI, VSS-SF) built in — rare in consumer apps",
"Clinician trust through referral program and outcome sharing",
"India-first content (Hindi/regional languages, culturally relevant examples)",
"First-mover advantage in a specific, underserved niche",
]:
story.append(bullet(m))
# ── SECTION 9: MARKETING & GROWTH ────────────────────────────────
story.append(PageBreak())
story += section_header("Marketing & Growth Strategy", "9")
story.append(Paragraph("<b>Acquisition Channels:</b>", sty_h3))
mktg = [
("ENT & Neurology Referrals", "Partner with 50–100 ENT surgeons and neurologists in metros. Provide prescription pads, QR code referral slips, and co-branded patient leaflets. Highest conversion; lowest CAC."),
("SEO / Content Marketing", "Build authority on terms: 'BPPV treatment at home India', 'dizziness exercises', 'vertigo cure'. Blog articles, YouTube educational videos, patient testimonials. Long-term organic traffic."),
("Social Media (Meta/Instagram)", "Targeted ads to adults 35–65 in metro areas. Focus on symptom identification ('Is your dizziness BPPV? Take the 30-second test'). Lead magnet: free BPPV self-assessment."),
("Google Search Ads", "Target high-intent queries: 'vestibular physiotherapy near me', 'BPPV exercises India'. Moderate CPC; strong intent signal."),
("Healthcare Professional (HCP) Events", "Exhibit at ENT/neurology CME events, AIIMS OPDs, Apollo/Fortis clinic educational programs."),
("PR & Media", "Pitch to health journalists on India's dizziness epidemic. Feature in The Hindu Health, Times of India Wellness, Femina, Lybrate."),
("WhatsApp Community", "Free BPPV support group; educational content; converts to paid subscribers through consistent value delivery."),
]
story.append(kv_table(mktg, col_w=[4*cm, 13*cm]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>Retention Strategy:</b>", sty_h3))
for r in [
"Weekly progress emails with symptom trend graphs",
"Milestone badges (7 days consistent, DHI improved, exercise streak)",
"Monthly live Q&A with empanelled vestibular physiotherapist (Phase 3)",
"Personalized program adaptation based on symptom log patterns",
"Reduced-price annual renewal with loyalty discount",
]:
story.append(bullet(r))
# ── SECTION 10: TECHNOLOGY ────────────────────────────────────────
story.append(PageBreak())
story += section_header("Technology Architecture", "10")
story.append(kv_table([
("Frontend", "React.js (TypeScript) — component-based UI; responsive for mobile and desktop; PWA-ready for Phase 3"),
("Backend", "Node.js + Express (REST API) or Django (Python) — depends on team expertise"),
("Database", "PostgreSQL — structured symptom/user/outcome data; JSONB for flexible exercise data"),
("Authentication", "Auth0 / Firebase Auth — OAuth2, JWT, role-based access (patient / clinician)"),
("Video Delivery", "Cloudflare Stream or Vimeo Pro (private) — adaptive bitrate for low-bandwidth users in Tier 2"),
("Hosting", "AWS (Mumbai region) / Google Cloud India — for DPDP Act data residency compliance"),
("Payments", "Razorpay — UPI, cards, net banking; supports subscription billing"),
("Email/Notifications", "SendGrid + Firebase Cloud Messaging (browser push notifications)"),
("Analytics", "Mixpanel for product analytics; Google Analytics 4 for marketing attribution"),
("CI/CD", "GitHub Actions + Docker — automated testing and deployment pipeline"),
]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(
"The web-first approach ensures zero installation barrier, works on any smartphone browser, "
"and allows rapid iteration without app store review delays. A Progressive Web App (PWA) "
"wrapper in Phase 3 enables offline exercise playback and home screen installation.", sty_body))
# ── SECTION 11: REGULATORY & COMPLIANCE ──────────────────────────
story.append(PageBreak())
story += section_header("Regulatory & Compliance Considerations", "11")
story.append(Paragraph(
"VestibBalance operates as a wellness and rehabilitation support platform, not a diagnostic "
"medical device. This places it in a lighter regulatory category, but compliance with the "
"following frameworks is essential:", sty_body))
story.append(Spacer(1, 0.1*cm))
story.append(kv_table([
("Digital Personal Data Protection Act 2023 (DPDP)", "India's primary data protection law. User health data must be stored on Indian servers, collected with explicit consent, and deletable on request. Privacy policy + consent flows are mandatory at launch."),
("IT Act 2000 / IT Rules 2021", "Classifies health data as 'sensitive personal data'. Requires documented security practices, data breach notification, and a Data Protection Officer (DPO) role."),
("Medical Devices Rules 2017 (CDSCO)", "As a wellness platform (not making diagnostic claims), VestibBalance does not require CDSCO registration. Must avoid language suggesting it 'diagnoses' or 'treats' conditions medically."),
("Telemedicine Practice Guidelines 2020", "If live clinician consultations are added (Phase 4), the platform must follow MCI/NMC telemedicine guidelines for registered practitioners."),
("Content Disclaimer", "All exercise content must carry disclaimer: 'This program is for rehabilitation support only. Consult your doctor before beginning. Stop if symptoms worsen.'"),
("Clinical Validation", "Partner with a medical college (e.g., AIIMS, CMC Vellore) for a pilot study to generate Indian-population efficacy data — strengthens credibility and regulatory positioning."),
]))
# ── SECTION 12: TEAM ──────────────────────────────────────────────
story.append(PageBreak())
story += section_header("Team & Advisory Requirements", "12")
story.append(Paragraph("<b>Founding Team (Required):</b>", sty_h3))
story.append(kv_table([
("Founder/CEO", "Domain expertise in audiology, ENT, or physiotherapy; clinical credibility to drive HCP partnerships"),
("Co-founder/CTO", "Full-stack developer (React + Node/Django); experience in health tech or SaaS products"),
("Clinical Lead", "Trained vestibular physiotherapist or audiologist — content accuracy, exercise protocol design"),
("Growth Lead (Part-time)", "Digital marketing: SEO, Meta ads, healthcare content strategy"),
]))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>Advisory Board (Target):</b>", sty_h3))
for a in [
"Senior ENT surgeon / neuro-otologist (clinical advisory + referral network)",
"Vestibular physiotherapist with published research (content credibility)",
"Digital health entrepreneur with India SaaS experience (go-to-market)",
"Healthcare regulatory consultant (DPDP / CDSCO guidance)",
]:
story.append(bullet(a))
# ── SECTION 13: FINANCIAL PROJECTIONS ────────────────────────────
story.append(PageBreak())
story += section_header("Financial Projections", "13")
story.append(Paragraph(
"Conservative projections assuming organic + referral-led growth, no paid advertising "
"in Year 1, and a 5% free-to-paid conversion rate.", sty_body))
story.append(Spacer(1, 0.2*cm))
fin_data = [
["Metric", "Year 1 (2026–27)", "Year 2 (2027–28)", "Year 3 (2028–29)"],
["Registered Users", "2,000", "15,000", "60,000"],
["Paying Users", "500", "3,500", "18,000"],
["Free-to-Paid Conversion", "25%*", "23%", "30%"],
["ARPU (INR/year)", "2,400", "3,200", "3,800"],
["Gross Revenue (INR Lakhs)", "12 L", "112 L", "684 L"],
["Operating Expenses (INR Lakhs)", "54 L", "110 L", "280 L"],
["EBITDA (INR Lakhs)", "(42 L)", "2 L", "404 L"],
["Monthly Burn Rate", "INR 4.5 L", "INR 9 L", "INR 23 L"],
]
t = Table(fin_data, colWidths=[5*cm, 4*cm, 4*cm, 4*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), PRIMARY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 6),
("BOTTOMPADDING", (0,0), (-1,-1), 6),
("ALIGN", (1,0), (-1,-1), "CENTER"),
]))
story.append(t)
story.append(Paragraph(
"* Year 1 conversion higher due to targeted ENT referral traffic (pre-qualified users).",
sty_caption))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("<b>Use of Seed Funds (INR 1.5 Crore):</b>", sty_h3))
seed_use = [
["Category", "Amount (INR)", "% of Total"],
["Product Development (MVP)", "45 Lakhs", "30%"],
["Clinical Content Creation (videos, protocols)", "22 Lakhs", "15%"],
["Team (salaries x 12 months)", "45 Lakhs", "30%"],
["Marketing & User Acquisition (Year 1)", "22 Lakhs", "15%"],
["Legal, Compliance & Regulatory", "8 Lakhs", "5%"],
["Operations & Infrastructure (cloud, tools)", "8 Lakhs", "5%"],
["Total", "1,50,00,000", "100%"],
]
t2 = Table(seed_use, colWidths=[8*cm, 5*cm, 4*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), ACCENT),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("BACKGROUND", (0,-1), (-1,-1), LIGHT_ACCENT),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTNAME", (0,-1), (-1,-1), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 9),
("ROWBACKGROUNDS", (0,1), (-1,-2), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 8),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
]))
story.append(t2)
# ── SECTION 14: RISK ANALYSIS ─────────────────────────────────────
story.append(PageBreak())
story += section_header("Risk Analysis & Mitigation", "14")
risks = [
["Risk", "Likelihood", "Impact", "Mitigation"],
["Low awareness of vestibular rehab among Indian patients", "High", "High",
"Content marketing + ENT doctor education; make symptom identification (not rehab) the entry point"],
["Users abandon program before clinical benefit (4–6 week window)", "High", "Medium",
"Gamification, progress notifications, 2-week quick-win milestone; benchmark expectations at onboarding"],
["Misuse by patients with serious central pathology (stroke, tumour)", "Low", "Very High",
"Mandatory red-flag screening at intake; strong safety disclaimers; recommend urgent medical review"],
["Data breach or privacy concern", "Low", "High",
"DPDP-compliant infrastructure; encryption at rest and transit; annual security audit"],
["Regulatory reclassification as medical device", "Low", "High",
"Legal counsel; avoid diagnostic language; position as wellness support tool"],
["Clinician reluctance to refer to a digital platform", "Medium", "High",
"Evidence sharing; pilot study partnership; outcome data transparency"],
["Competition from well-funded generic health app expanding to vestibular", "Medium", "Medium",
"Build clinical moat early; publish outcome data; vestibular-specific intelligence hard to replicate quickly"],
]
t = Table(risks, colWidths=[4*cm, 2*cm, 2*cm, 9*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,0), PRIMARY),
("TEXTCOLOR", (0,0), (-1,0), WHITE),
("FONTNAME", (0,0), (-1,0), "Helvetica-Bold"),
("FONTSIZE", (0,0), (-1,-1), 8),
("ROWBACKGROUNDS", (0,1), (-1,-1), [WHITE, LIGHT_GRAY]),
("GRID", (0,0), (-1,-1), 0.3, colors.HexColor("#BDC3C7")),
("LEFTPADDING", (0,0), (-1,-1), 6),
("TOPPADDING", (0,0), (-1,-1), 5),
("BOTTOMPADDING", (0,0), (-1,-1), 5),
("VALIGN", (0,0), (-1,-1), "TOP"),
]))
story.append(t)
# ── SECTION 15: CONCLUSION ────────────────────────────────────────
story.append(PageBreak())
story += section_header("Conclusion & Next Steps", "15")
story.append(Paragraph(
"VestibBalance addresses a real, large, and clinically validated unmet need. Vestibular "
"disorders affect millions of Indians, existing solutions are inaccessible or ineffective, "
"and the digital health market tailwind is strong. The platform's clinical specificity, "
"validated outcome tools, and affordable D2C model create a durable competitive position "
"in a niche with no dominant incumbent.", sty_body))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("<b>Immediate Next Steps (Next 90 Days):</b>", sty_h3))
for i, step in enumerate([
"Recruit a vestibular physiotherapist as clinical co-founder or lead advisor",
"Conduct 20-patient discovery interviews at ENT OPDs (Apollo/Manipal/AIIMS)",
"Build clickable prototype of onboarding + exercise flow (Figma)",
"Identify 3 ENT clinics willing to pilot the referral model",
"Register company (private limited) and apply for DIPP Startup India recognition",
"Engage a healthcare legal consultant for DPDP compliance framework",
"Apply to healthcare-focused accelerators: Axilor, Villgro, CIIE.CO",
], 1):
story.append(bullet(f"<b>Step {i}:</b> {step}"))
story.append(Spacer(1, 0.4*cm))
# Final box
story.append(Table([[
Paragraph(
"VestibBalance is not just an app — it is a clinical access point for the millions of "
"Indians who live with daily dizziness, falls, and anxiety, waiting years for care that "
"should reach them in days.",
S("quote", fontName="Helvetica-Oblique", fontSize=10, textColor=PRIMARY,
alignment=TA_CENTER, leading=16))
]], colWidths=[17*cm],
style=TableStyle([
("BACKGROUND", (0,0), (-1,-1), LIGHT_ACCENT),
("BOX", (0,0), (-1,-1), 1.5, ACCENT),
("LEFTPADDING", (0,0), (-1,-1), 20),
("RIGHTPADDING", (0,0), (-1,-1), 20),
("TOPPADDING", (0,0), (-1,-1), 16),
("BOTTOMPADDING", (0,0), (-1,-1), 16),
])))
# ─── Build PDF ───────────────────────────────────────────────────────
doc = SimpleDocTemplate(
OUTPUT_PATH,
pagesize=A4,
leftMargin=1.5*cm, rightMargin=1.5*cm,
topMargin=1.8*cm, bottomMargin=1.8*cm,
title="VestibBalance Business Plan 2026",
author="VestibBalance Team",
subject="Business Plan — Vestibular Rehabilitation Web Platform",
)
class CoverDocTemplate(SimpleDocTemplate):
def handle_pageBegin(self):
super().handle_pageBegin()
# Build with custom canvas
doc.build(story,
onFirstPage=draw_cover,
onLaterPages=lambda c, d: None,
canvasmaker=NumberedCanvas)
print(f"PDF generated: {OUTPUT_PATH}")
cd /home/daytona/workspace/vestibular-app-bizplan && python generate_bizplan.py 2>&1