## Summary
This PR brings the Education investor memo — both the generated .docx export and the in-app EducationMemoView — into line with the manually-authored reference memo. It introduces period-aware table structures (so historical exports keep matching their original manuals), a new Crush AP vertical and Alpha Camps summary line, a curated Physical Schools roster regrouping, a favorable-variance convention for expense rows, and a number of structural/styling corrections to the per-vertical P&L, Income Statement, EBITDA, and Cash Flow tables. The backend now emits a single physical_schools_layout source of truth consumed by both the export and the UI, keeping the two surfaces from drifting.
## Business Value
The Education investor memo goes to investors and leadership; until now the auto-generated version diverged from the hand-built reference, forcing manual reconciliation each cycle. These changes make the generated memo match the reference structure, numbers, and presentation, reducing manual cleanup, eliminating reconciliation errors (e.g. Gauntlet and non-operating FX/rounding inflating operating expenses), and letting Finance trust and audit the export directly.
## Changes
- Period-aware structure (education_verticals.py, educationMemoTables.ts): added CRUSH_AP_START (Apr 2026) and PHYSICAL_SCHOOLS_REGROUP_START (May 2026) thresholds with is_post_crush_ap / is_post_physical_schools_regroup helpers, mirrored backend↔frontend, so older periods keep their original layout.
- Crush AP & Alpha Camps: new investment-crush-ap vertical (BU CrushAP) with its own P&L table, gated to Apr 2026+; Alpha Camps added as a summary-only line; Current Performance summary renumbered (14. Crush AP, 17. Alpha Camps, 18. Other) for that period onward.
- Physical Schools regrouping (May 2026+): curated _PHYSICAL_SCHOOL_ROSTER grouped Alpha Schools / Other Schools / Core Education; per-school actuals now sum each class_name across all QB companies (_fetch_qb_class_net_margins, build_roster_classes), capturing the dedicated per-school LLCs the old (company, class) mapping missed. Drill-downs resolve the same classes (_fetch_qb_school_accounts_by_classes, roster_class_names_for) so they tie out.
- Expense favorable-variance: expense rows (and their sub-items, plus Total Expenses) now present Delta as Budget − Actual so under-spend reads positive; Revenue/COGS/Gross Profit/Net Profit stay Actual − Budget. Applied in the export (_vals_to_row(invert=…)), the UI transform (fillRowValues), and the drill-down panel (invertDelta).
- Gauntlet & non-operating NHC excluded: Gauntlet removed from Tech Super Builders / Legends across all surfaces (per Finance); new EXCLUDE_NON_OPERATING_NHC_WHERE filters Realised FX and Rounding Gain/Loss out of operating NHC Expenses.
- Gross Margin delta is now the marginal margin on the variance (GP delta ÷ Revenue delta), rounded to whole percent.
- Combined revenue line for Virtual Charter and GT (Recurring + Non Recurring collapsed to one line that ties to Total Revenue) via COMBINED_REVENUE_KEY / combine_revenue.
- Table-structure fixes: Tech Super Builders gains a Legal Expenses NHC line; Schools Marketing rendered as a cost center (no revenue/COGS detail); Alpha AI gains a COGS section; Prequel/Private School/Virtual Charter line corrections; Crush AP table.
- Statement corrections: Education YTD Income Statement simplified (month-anchored YTD column headers, single Other income line, dropped D&A/acquisition lines); Education EBITDA YTD drops the interest add-back; YTD Cash Flows drops Lease obligations + Non-software investments for Education and adds a page break; "Education" wordmark on the Education cash-flow statement.
- Styling: heading colors and table header/subtotal shading updated to match the reference (#073763, #0090ca, #666666, subtotal EFEFEF); summary table corner header renamed to "Vertical"; District Sales restructured with a [TBD] intro paragraph + muted sub-heading.
- Audit completeness: drill-down detail query no longer filters out zero-value accounts (HAVING removed); UI surfaces template NHC sub-accounts the ledger omitted as $0 leaves so the audit CSV lists every line.
- Tests: new/updated unit tests across test_education_vertical_data.py, test_ytd_cf_excluded_line_items.py, and the frontend transform/detail-panel specs.
## Testing
1. Backend: cd klair-api && pytest tests/docx_reports/ tests/mfr/memos/ tests/test_education_vertical_data.py
2. Frontend: cd klair-client && pnpm test src/features/monthly-financial-reporting
3. Open the Education memo, select May 2026: confirm Crush AP (14) and Alpha Camps (17) lines, wind-down renumbered to 18, and the Crush AP subsection/table render.
4. Confirm Physical Schools is grouped Alpha Schools / Other Schools / Core Education and its Total matches summary line 1; click a school row, a section subtotal, and the grand Total — each drill-down ties out.
5. On a per-vertical P&L, click an expense row (e.g. NHC Expenses): Delta reads as a favorable variance and template sub-accounts with no ledger activity appear as $0.
6. Select March 2026 and confirm Crush AP / Alpha Camps are absent and the old Physical Schools grouping is used.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
http://localhost:3001/monthly-financial-reporting