# Budget Mode

Build decks within a price target using per-card cost filtering and a soft-review summary.

---

## Overview

Budget Mode filters the card selection pool to cards at or near a per-card price ceiling you set. Cards that exceed the ceiling are excluded from the randomized pool, but the final deck is never hard-rejected — over-budget cards that slip through are flagged for your review in the build summary.

Enable with `ENABLE_BUDGET_MODE=1` (default: on).

---

## Setting a Budget

In the **New Deck modal**, enter a per-card price ceiling in the Budget field. This ceiling is a UI input — it is not set via an environment variable.

- Leave the field blank or set it to `0` to disable per-card filtering for that build.
- The ceiling applies to all non-land cards drawn from the selection pool.

---

## How Filtering Works

1. Price data is loaded from a local cache sourced from Scryfall bulk data.
2. Cards whose price exceeds `ceiling * (1 + BUDGET_POOL_TOLERANCE)` are excluded from the pool before selection begins.
3. `BUDGET_POOL_TOLERANCE` (default `0.15`) adds a 15% headroom above the ceiling. This smooths results for borderline-priced cards and avoids over-aggressive filtering.
4. Selection then runs normally on the filtered pool — locked cards and Must Include cards are inserted before filtering.

**Example:** Ceiling = $2.00, tolerance = 0.15 → cards priced above $2.30 are excluded from the pool.

---

## Build Summary

After a build completes, the budget summary panel shows:

- Total estimated deck cost
- Per-category cost breakdown (creatures, spells, lands, etc.)
- Cards flagged as over-budget (above the ceiling, not the tolerance threshold)
- Price badges on individual card rows: green (under ceiling), yellow (at ceiling), red (over ceiling)
- A stale price indicator (clock icon) when a cached price is older than `PRICE_STALE_WARNING_HOURS`

The summary JSON export (`deck_files/*.summary.json`) includes the price breakdown per category.

---

## Price Data & Caching

Prices are sourced from the Scryfall bulk data API and cached locally.

| Behavior | Setting |
|----------|---------|
| Background per-card refresh (7-day TTL) | `PRICE_LAZY_REFRESH=1` (default) |
| Full cache rebuild daily at 01:00 UTC | `PRICE_AUTO_REFRESH=1` |
| Stale indicator threshold | `PRICE_STALE_WARNING_HOURS=24` (set to `0` to disable) |

If a card has no price data, it is treated as free (fail-open) and selected normally. No card is hard-rejected due to a missing price.

To manually check cache status: `/api/price/stats` (when `SHOW_DIAGNOSTICS=1`).

---

## Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `ENABLE_BUDGET_MODE` | `1` | Enable budget controls and price display. Set to `0` to hide all budget UI. |
| `BUDGET_POOL_TOLERANCE` | `0.15` | Fractional overhead above the per-card ceiling before exclusion. |
| `PRICE_AUTO_REFRESH` | `0` | Rebuild the full price cache daily at 01:00 UTC. |
| `PRICE_LAZY_REFRESH` | `1` | Refresh stale per-card prices in the background. |
| `PRICE_STALE_WARNING_HOURS` | `24` | Hours before a cached price shows a stale indicator. `0` = disable. |

---

## FAQ

**Why is a card over my ceiling still in the deck?**
The pool tolerance allows cards slightly above the ceiling into the selection pool to avoid overly thin pools. Cards that land in your deck despite being over ceiling are flagged in red in the budget summary. You can swap them using the Replace feature in Step 5.

**Prices look stale — how do I refresh?**
Set `PRICE_LAZY_REFRESH=1` (default) to refresh automatically in the background. For an immediate full refresh, set `PRICE_AUTO_REFRESH=1` and restart the container, or trigger a manual refresh via the price stats API.

**Does budget mode affect the commander?**
No. The commander is never filtered by price — only cards drawn from the selection pool are subject to the ceiling.

**Can I use budget mode with Must Include cards?**
Yes. Must Include cards bypass the pool filter and are always added. They may appear as over-budget in the summary if they exceed the ceiling.
