◆ Epps.ai · Narrative Intelligence
KPI Story
Engine
Enter KPIs and context. Olesya drafts the narrative — what changed, why, what it means, and what comes next.
All Formats
Executive Summary
Board
LP Report
Quarterly Report
◆ Portfolio Context
◆ KPI Inputs
Current Period
Prior Period
Budget / Target
Occupancy (%)
NOI ($000s)
Collections Rate (%)
Revenue ($000s)
OpEx ($000s)
Avg Rent ($/unit/mo)
◆ Lease-Up (if applicable)
◆ Drivers & Context
"
Enter KPIs.
Get the story.
Olesya reads your numbers and drafts the narrative — what changed, why it happened, what risks exist, and what comes next. Four audience-ready formats.
What Changed
Drivers
Risks
Forward Outlook
Executive Summary
Board Paragraph
LP Paragraph
Quarterly Report
Olesya is writing your story
Reading KPI data...
Unable to generate narrative.
Check your API connection and try again. If the issue persists, the narrative engine may be temporarily unavailable.
◆ KPI Narrative ·
AI-generated narrative — review and edit before distribution. Not a substitute for operator judgment or audited financial statements.
◆ KPI Snapshot
Full Story
Executive Summary
Board
LP Report
Quarterly Report
◆ What Changed
AI Narrative · Performance Summary
◆ Performance Drivers
AI Narrative · Key Drivers
◆ Risks & Watchlist
AI Narrative · Risk Factors
◆ Forward Outlook
AI Narrative · Forward Guidance
◆ Audience-Specific Paragraphs
Executive Summary Paragraph
Board Paragraph
LP Report Paragraph
Quarterly Report Paragraph
Ask Olesya
Workflow Copilot · EPPS.AI
Built by an operator. Underwriting · Reporting · Development · Advisory
Olesya Epps · Founder, Epps.ai
Institutional Real Estate · Operator-built AI workflows
Olesya Epps
Workflow Copilot · EPPS.AI Institutional Real Estate Operator-built AI Workflows
About Epps.ai →
Ask Olesya
Hi — I'm Olesya, your Workflow Copilot.

Trained on Epps.ai workflows and real-world real estate operating practices, I can help you navigate underwriting, reporting, development, and investment workflows.

What are you working on today?
"; var blob = new Blob([html],{type:"application/msword"}); var url = URL.createObjectURL(blob); var a = document.createElement("a"); a.href = url; a.download = (inp.assetName||"KPI_Narrative").replace(/[^a-z0-9]/gi,"_") + "_" + (inp.period||"").replace(/\s/g,"_") + ".doc"; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // ════════════════════════════════════════════════ // SAMPLE + CLEAR // ════════════════════════════════════════════════ function loadSample(){ var fields = { assetName:"Sunbelt Residential Portfolio",period:"Q1 2026",market:"Phoenix, AZ", "occ-cur":"93.5","occ-pri":"91.2","occ-bud":"92.0", "noi-cur":"842","noi-pri":"798","noi-bud":"820", "coll-cur":"98.2","coll-pri":"97.8","coll-bud":"97.5", "rev-cur":"1258","rev-pri":"1201","rev-bud":"1240", "opex-cur":"416","opex-pri":"403","opex-bud":"420", "rent-cur":"2147","rent-pri":"2098","rent-bud":"2130", "leased-cur":"194","leased-total":"216","new-leases":"22","renewal-rate":"68", drivers:"Strong Phoenix in-migration drove above-budget lease-up. Vendor renegotiations reduced maintenance costs $18K vs prior quarter. Concession environment normalizing.", risks:"18,000 units delivering Phoenix MSA through 2026 — monitor absorption. HVAC replacement capex ($95K) deferred to Q2. ROAD Act reconciliation pending.", outlook:"Q2 2026: occupancy target 94%+, rent growth projection 2.8%, HVAC capex execution, stabilization milestone June 30. Distribution $1.8M targeted Q3.", "tone-notes":"LP base is institutional pension — conservative, metrics-first. executive summary confident but not promotional." }; Object.keys(fields).forEach(function(k){ var el = document.getElementById(k); if(el) el.value = fields[k]; }); } function clearAll(){ document.querySelectorAll(".lp-body input,.lp-body textarea,.lp-body select").forEach(function(el){ if(el.tagName==="SELECT") el.selectedIndex=0; else el.value=""; }); setState("empty"); } // ════════════════════════════════════════════════ // INIT // ════════════════════════════════════════════════ window.addEventListener("DOMContentLoaded", function(){ setState("empty"); switchTab("all", null); });