I built the FY2025 revenue bridge and saved it to `out/revenue_bridge_fy2025.csv`. It has a total row and one row per segment, and every row reconciles to zero.

**FY2024 to FY2025 bridge (total)**

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

Total revenue is down about 4.9k, roughly flat.

**By segment**
- **Enterprise:** 6.26M to 5.51M. Churn of 1.19M (Birchway Foods and Fenwick Legal) outweighs 608k of expansion.
- **Mid-Market:** 4.04M to 4.37M. It has no new customers and no churn, and the growth is expansion.
- **SMB:** 0.95M to 1.36M. All the new customers are here, and so is the remaining churn (Eastfield Library, Quill Publishing).

**How I classified customers**
- **New:** zero FY2024 revenue and positive FY2025 revenue.
- **Churn:** positive FY2024 revenue and zero FY2025 revenue.
- **Expansion or contraction:** everything else, using the change in revenue excluding timing.

**Assumption to confirm:** the file doesn't define `timing_amount`. I treated it as the part of a customer's year-over-year change that comes from timing rather than a real change in the account. I pulled it into its own "Timing" step and took it out of expansion and contraction. It touches three customers:
- Cobalt Dental: 53,231
- Lumen Retail: −13,813
- Saltmarsh Energy: 13,662

If it should be treated differently, for example left inside expansion and contraction or netted against something else, the buckets will change. The total won't. Tell me which and I'll rerun it.