I built the FY2025 revenue bridge, and it reconciles to FY2025 revenue exactly (check = 0). I saved it as `revenue_bridge_fy25.csv` and the script as `bridge.py`. I didn't make a chart.

| Step | Amount |
|---|---|
| FY2024 revenue | 11,243,300 |
| New customers (6) | +462,893 |
| Expansion, excluding timing (23 customers) | +1,210,651 |
| Contraction, excluding timing (11 customers) | −443,322 |
| Churned customers (4) | −1,288,224 |
| Timing items | +53,080 |
| **FY2025 revenue** | **11,238,378** |

Revenue is roughly flat, down about 5k. Churn of 1.29M offsets most of the 1.21M expansion.

The file doesn't say what `timing_amount` means, so I assumed it is the part of a customer's year-over-year change that comes from timing rather than underlying performance. I took it out of expansion and contraction and showed it as its own step. If it should stay inside expansion and contraction instead, tell me and I'll fold it back in.

Customers with revenue in only one year are classed as new or churned:
- **New:** the six with FY2024 revenue of 0 (Riverbend Water, Summit Orthopedics, Thistle Gardens, Umber Coffee, Verdant Landscaping and Wren Architecture).
- **Churned:** the four with FY2025 revenue of 0 (Birchway Foods, Fenwick Legal, Eastfield Library and Quill Publishing).

Four retained customers are unchanged and appear in neither expansion nor contraction.