I've built the FY2025 revenue bridge and it ties. Revenue was essentially flat: **$11.24M in FY24 and $11.24M in FY25, down $4.9K (−0.04%)**. But the flat total hides a lot of movement underneath.

| | Enterprise | Mid-Market | SMB | **Total** |
|---|---:|---:|---:|---:|
| **FY2024 revenue** | 6,258,216 | 4,036,244 | 948,840 | **11,243,300** |
| + New | – | – | 462,893 | **462,893** |
| + Expansion | 607,747 | 500,692 | 102,212 | **1,210,651** |
| − Contraction | (220,558) | (165,706) | (57,058) | **(443,322)** |
| − Churn | (1,188,984) | – | (99,240) | **(1,288,224)** |
| ± Timing | 53,231 | (151) | – | **53,080** |
| **FY2025 revenue** | 5,509,652 | 4,371,079 | 1,357,647 | **11,238,378** |

**Main points**
- **Lost customers offset all of the growth.** Churn of $1.29M is larger than all expansion. Two Enterprise customers, Birchway Foods ($503K) and Fenwick Legal ($686K), make up 92% of it. Excluding the timing adjustments, revenue kept from existing customers was 95.4% including their growth (net retention) and 84.6% before it (gross retention).
- **Enterprise shrank by 12%, from $6.26M to $5.51M.** Mid-Market grew 8% and lost no customers. SMB grew 43%, almost all of it from 6 new customers worth $463K.
- **All new revenue came from SMB.** Enterprise and Mid-Market won no new customers.

**How I categorised each customer**
- The `timing_amount` column is its own bridge line. I treated it as revenue shifted between years, not a real change in what the customer buys.
- Each customer is classed as new, expansion, contraction, churn or flat based on its change after removing timing. This affects 3 customers:
  - Cobalt Dental: +$98.5K reported, of which $45.3K is real expansion.
  - Lumen Retail: −$13.8K reported, all timing, so it counts as flat.
  - Saltmarsh Energy: −$38.6K reported, which is −$52.3K contraction plus $13.7K timing.
- If `timing_amount` means something else in your data, tell me and I'll change the treatment.

**Files are in `output/`:**
- `revenue_bridge_total.csv`
- `revenue_bridge_by_segment.csv`
- `customer_classification.csv` (category for each customer, for audit)

To regenerate them, run `python3 revenue_bridge.py`. The script stops with an error if the bridge doesn't tie to FY2025 revenue.