I wrote `ttm.py`, ran it with `--period 2025-06`, and saved the output to `output/ttm.json`. It exited 0 and all the identity checks hold:

> TTM Jul 2024 to Jun 2025 revenue $11.3M vs budget $11.5M, variance ($157K); operating income $485K vs $598K, variance ($113K); reconciles.

**Usage:** `python3 ttm.py --input data/pnl_fy2024.csv data/pnl_fy2025.csv --output output/ttm.json [--period 2025-06]`

**How it's built:** `ttm.py` imports `bva.py`'s file reading, variance logic, rounding and display formats rather than copying them, so the two scripts can't drift apart. One consequence: `ttm.py` needs `bva.py` in the same folder. `bva.py` is unchanged.

**Checks I ran:**
- **Totals:** for each line, the twelve-month actual, budget and variance equal `bva.py`'s full-year 2024 total, minus its Jan–Jun 2024 total, plus its Jan–Jun 2025 total. All ten lines match exactly.
- **Month section:** the June 2025 section is identical to what `bva.py` gives for June 2025.
- **Window:** the monthly revenue list runs 2024-07 to 2025-06.
- **Bad input:** these all exit 2 with a clear message: a window with months missing (2024-06, or running on only the 2025 file), the same file passed twice, a period not in the files, and a missing file.

**What changed from `bva.py`'s output, and why:**
- **Key names:** `ytd` is now `ttm`, `months_ytd` is now `months_ttm`, and `ytd_revenue_variance` is now `ttm_revenue_variance`. The layout and the field names inside these blocks are the same. I renamed them so twelve-month figures aren't labelled year to date. If something downstream reads the old `ytd` keys, tell me and I'll switch them back.
- **Schema name:** it is now `budget-vs-actual-ttm/1`.
- **Labels:** `labels.ttm` reads "TTM Jul 2024 to Jun 2025".
- **Input block:** it now lists each file with its name, row count and hash (`sha12` in the display block), plus a total row count. `input` also gains `window: {from, to}`.
- **Stricter input checks**, needed because the window joins two files:
  - All twelve months must be present, with a row for every line in every month. Otherwise a missing half-year would silently count as zero.
  - A period and line can't appear in both files.
  - A line can't change category between files.
- **Peaks:** the running variance used to find each line's peak starts at the first month of the window, July 2024, so every peak falls within the window.