# Seat Map Builder

Venue seat maps are reusable templates. `seats` belong to a `venue_seat_map`; `event_seats` represent per-event availability.

Supported layout concepts:

- Sections and stands
- Rows and columns
- Irregular positioned seats with x/y coordinates
- Tables, VIP boxes, standing zones, accessible seats
- Active/inactive/blocked/held/sold states

Checkout must create temporary holds in a transaction. Public reserved-seat checkout holds are 15 minutes from selection, and the customer UI must show this deadline clearly before checkout. Box Office Stripe payment links may use the organiser-configured payment-link expiry window, and those holds must be released if the link expires unpaid.

Operational rules:

- Expired holds are released whenever event detail, checkout, or seat-hold endpoints are touched.
- The scheduler-safe command `php bin/expire-seat-holds` should run every minute in production or staging so abandoned baskets and unpaid Box Office payment links return seats to public sale without waiting for another customer request. It emits JSON and exits non-zero if the database connection or release task fails, which makes it suitable for cron, Windows Task Scheduler, or external process monitoring. The command is extensionless by design; run it through PHP explicitly.
- Ticket issuing locks the event seat, verifies the active hold token for online checkout, and updates the seat to `sold` only if it is still in the expected state.
- Box-office sales may sell only seats that are still `available`.
- Checkout success must reject expired or released holds instead of issuing tickets.
