The mechanic
Every court has a unique constraint on (court_id, time_range). When the database sees two bookings whose ranges overlap on the same court, the second insert is rejected. The UI then translates that error into a friendly message — "That slot was taken about a second ago" — and refreshes the grid so the member sees the actual state of the world.
What the member sees
On their booking page, the slot greys out with a small "just booked" tag. They can pick another slot. If they were halfway through Stripe checkout when the conflict happened, the payment intent is voided automatically — no double charge, no manual refund.
What the audit log records
The successful booking lands in the audit log as booking.create. The failed attempt is logged at debug level (visible in the dashboard's diagnostics tab but not in the public audit log). If you suspect abuse — a member spamming bookings to deny others — both the success and failure are visible to staff.