Three ways to be booked.
A table at 19:30, a cottage for four nights, or forty minutes with a named person. Three modes, each with its own pricing and its own idea of what availability means.

They are not the same problem.
A restaurant allocates tables from a party size. A rental counts nights between two dates. An appointment needs a specific person to be free. Each gets its own pricing strategy rather than a shared one with branches in it.
Time-slot seating with a party size and a duration, allocated across your tables. Two parties do not get the same table.
Date ranges with nightly pricing, minimum and maximum stays, and check-in and check-out times.
A duration against a named provider, with buffers between slots so the day is not booked back to back.
Every mode needs a resource of the right kind behind it — a table, a unit, a person. Without one the calendar draws and offers nothing, which is the single most common setup mistake.
A booking is a sequence, not a status field.
It starts as a hold while the guest is still typing, and moves only along transitions the engine allows.
Availability comes from weekly rules, narrowed by the exception periods you add for a closure or a private hire, and pricing rules can move the price by day, season or party size.

Nights, tiers, and a door list that is the same record as the sale.
An event has occurrences — generate a whole run at once rather than making twelve events. Each occurrence sells ticket types with their own prices and allocations, into the same cart your products use.
Issued tickets carry a code that renders as a QR, and check-in is its own service. Availability is fetched at runtime and never written into the cached page — so nothing on a published page ever claims a seat is free when it is not.
Set up one service and take a test booking.
Remember the resource — a service without a table, a unit or a person behind it will show a calendar with nothing in it.