Replace two-step Eurostar fetch (HTML timetable + GraphQL prices) with a
single GraphQL call that returns timing, train numbers, prices, and seats.
Support indirect services (e.g. Amsterdam) by joining multi-leg train numbers
with ' + ' and keeping the earliest arrival per departure time.
Fix half-pound prices by casting displayPrice to float instead of int.
Wrap each train number segment in white-space:nowrap so 'ES 9132 + ER 9363'
never breaks mid-segment.
Format Eurostar prices with two decimal places.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fetch_prices now returns {'price': ..., 'seats': ...} per departure.
Seat count (labelled "N at this price") is shown below the fare — it
reflects price-band depth rather than total remaining seats. A yellow
notice is shown when the API returns journeys but all prices are null
(tickets not yet on sale).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the Eurostar timetable link with the search URL
(eurostar.com/search/uk-en?adult=1&origin=…&destination=…&outbound=…)
so the footer links directly to the page that shows prices for the
specific date and destination.
Add a Bristol Temple Meads → Paddington departures link on RTT alongside
the existing Paddington arrivals link.
Also update "morning service unavailable" badge and tests to reflect the
removal of the morning-only cutoff filter from find_unreachable_morning_eurostars.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fetches prices via the site-api.eurostar.com GraphQL gateway
(NewBookingSearch operation, discovered with Playwright). Adds
fetch_prices() to scraper/eurostar.py using requests, caches results,
annotates each trip with eurostar_price and total_price, and shows an
ES Std column plus total cost (duration + price) in the results table.
The Transfer column is hidden on small screens for mobile usability.
Closes#4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>