I've built the December 2025 scorecard: **4 of 8 KPIs met target.** It's saved at `reports/kpi_scorecard_2025-12.md`, and `scorecard.py` regenerates it (you can pass any month, e.g. `python3 scorecard.py 2025-11`).

| KPI | Actual | Target | Variance | Status | vs Nov |
|---|---:|---:|---:|---|---|
| Revenue | $826,751 | $823,600 | +$3,151 (+0.4%) | Met | down $71.9k |
| Gross margin | 26.1% | 26.0% | +6 bp | Met | improved |
| Days sales outstanding (DSO) | 51.2 | 45.0 | +6.2 days | **Missed** (3rd month in a row) | worse |
| On-time delivery | 93.3% | 95.0% | −170 bp | **Missed** (3rd month in a row) | worse |
| Order fill rate | 97.6% | 97.0% | +60 bp | Met | improved |
| Active customers | 40 | 42 | −2 | **Missed** (2nd month in a row) | worse |
| Inventory turns | 5.33 | 6.50 | −1.17 (−18%) | **Missed** | worse (6.67 in Nov) |
| Opex % of revenue | 25.1% | 25.2% | −14 bp | Met | worse vs Nov's 22.6% but still under budget |

**What to watch:**
- **DSO is getting steadily worse:** 47.3 → 48.6 → 51.2 days from October to December. This is the biggest miss and the clearest trend.
- **On-time delivery has slipped for three months:** 94.2% → 93.7% → 93.3%. July and August were also below target.
- **Inventory turns fell sharply** from 6.67 to 5.33, the lowest of the year. Turns are calculated from the last three months of cost of goods sold and month-end inventory. So either inventory went up or Q4 cost of goods sold went down; this data doesn't show which.
- **Active customers** has been at or below target since October.
- **Revenue** beat December's budget, but full-year revenue is **$11.24M against a $11.60M target (−$362k, −3.1%)**. Most of the shortfall came from March, July and August.

**How status is decided:** Met means the actual is at or better than target, in whichever direction counts as better for that KPI. The data doesn't define a tolerance band, so near-misses aren't marked separately. If you have red/amber/green thresholds, send them and I'll add them.