**FY2025 revenue bridge** (48 customers)

| Step | Customers | Amount |
|---|---|---|
| **FY2024 revenue** | | **11,243,300** |
| New | 6 | +462,893 |
| Expansion | 23 | +1,210,651 |
| Contraction | 11 | −443,322 |
| Churn | 4 | −1,288,224 |
| Timing | 3 | +53,080 |
| **FY2025 revenue** | | **11,238,378** |

The bridge reconciles to actuals with a difference of 0.00. Four customers are flat (Redwood Pharmacy, Hawthorn Veterinary, Jetty Seafood, and one other), so they add nothing.

- **Net change:** −4,922 (−0.04%). Underlying revenue fell 58,002, and timing added 53,080.
- **Enterprise:** down 801,795 underlying. Churn of Birchway Foods and Fenwick Legal accounts for 1.19M of that, partly offset by 608K of expansion.
- **Mid-Market:** up 334,986, with no churn.
- **SMB:** up 408,807, driven by six new logos (462,893) against 99K of churn.

**Assumption to confirm:** the file doesn't define `timing_amount`. I treated it as the part of each customer's FY25 change that is timing (for example phasing or one-offs), already included in `fy2025_revenue`. I split it into its own bridge step. Expansion and contraction are measured after removing it, so for example Cobalt Dental's +98.5K counts as +45.3K expansion and +53.2K timing. If timing means something else, for example revenue that should be excluded, the Expansion, Contraction and Timing lines will change.

The script is `revenue_bridge.py` and the per-customer detail is `revenue_bridge_detail.csv`, both in the working directory. I haven't made a chart. I can add a waterfall if you want one.