trips/mock/data.js
Greg Pomerantz 33ed5d9fe6 multi-trip mock + Colombia trip with a real walking router
The mock can now work several trips at once: a trip picker in the
topbar, per-trip state in localStorage (edits + version history survive
switching), and a per-trip router key so each trip routes on its own
OSRM extract.

Colombia (Cartagena -> Santa Marta, 8 days) becomes the default working
trip, inside a new walking-profile OSRM instance:
- scripts/crop_pbf.py: streaming 3-pass PBF cropper (no PBF library;
  the country file is too big for osmium extract -s complete_ways on
  this box). Framing validated against osmium test fixtures and
  osrm-extract. Drops relations (foot profile ignores restrictions).
- scripts/setup-osrm-colombia.sh: download -> crop (~4.6 MB) ->
  extract/partition/customize with the official foot.lua -> serve
  :5003. Verified: Getsemaní -> Castillo San Felipe 7.98 km / 96 min.
- server.js: ROUTERS map, ?router=colombia|northeast on /route,
  /table, /router-status (per-router probe points).
- app.js: legs touching a transit stop are fixed (flight/taxi/bus via
  M.multimodal), never re-routed; distance-0 router answers (both
  points on one node) read as adjacent stops, not out-of-coverage.
- multi-hotel fix: day-scope map fit uses the stays covering THAT day,
  so a two-city trip no longer fits the whole country per day.

Also: pre-cache-tiles.sh (Cartagena+Santa Marta z14-16, 2560 tiles),
tile cache write fix (Node rejects flags:'x'; buffer-then-write),
favicon, renderHotelMks load-order guard, tests updated for the new
default trip (all four puppeteer suites green, zero console errors).
2026-09-09 09:44:29 -04:00

992 lines
67 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Fake data layer — same shapes the real API will return.
// Each stop: day, slot (mutual-exclusion group), state: planned | maybe | alt.
// Only one `planned` stop per slot per day; `alt`/`maybe` don't count against
// the day's time budget.
//
// Two demo cities: Florence (rich itinerary demo) and Boston (inside the
// local OSRM extract, so legs are routed on the real road network — see
// server.js /route proxy and app.js routeClient). ?city=florence|boston
const FLORENCE = {
trip: {
id: 'trip-2026-09',
version: 1,
title: 'Florence 1215 Sep',
// stays, not a single hotel: {hotel, place, checkIn, checkOut, state}.
// Days resolve their anchor per date; >1 candidate stay covering the
// same nights → worst-case anchoring for those nights.
// Multi-city trips = more stays, each anchoring its own dates.
stays: [
{ id: 'ST1', name: 'Hotel Palagio', at: [43.7684, 11.2566], place: 'Florence',
checkIn: '2026-09-12', checkOut: '2026-09-15', state: 'booked', price: 180 },
],
places: [
{ id: 'PL1', name: 'Florence', bbox: [43.73, 11.21, 43.80, 11.29],
center: [43.769, 11.255], vibe: 'art + food, relaxed pace',
dates: ['2026-09-12', '2026-09-15'] }
],
bookings: [
{ type: 'hotel', name: 'Hotel Palagio', ref: 'H-77821',
dates: '1215 Sep', where: [43.7684, 11.2566],
status: 'booked', source: 'user_booking' },
{ type: 'train', name: 'Frecciarossa 9512', ref: 'F-55812',
from: 'Firenze SMN', to: 'Pisa Centrale', date: '2026-09-15',
depart: '09:12', arrive: '09:58', status: 'booked', source: 'user_booking',
stations: { from: [43.7726, 11.2597], to: [43.7157, 10.3922] } }
]
},
days: [
{ id: 'D1', date: '2026-09-12', label: 'Day 1 · Fri 12',
startMin: 8 * 60, wakingHours: 11,
base: { name: 'Hotel Palagio', at: [43.7684, 11.2566] },
legs: [],
stops: [
{ id: 'S1', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [43.7684, 11.2566], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'triptadvisor.com' } },
{ id: 'S2', name: 'Palazzo Pitti', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.palazzopittiturin.it',
at: [43.7639, 11.2518], dur: 60, durConf: 5, // user override (sticky)
suggested: { value: 150, conf: 2, src: 'wikivoyage.org' },
price: { amount: 14, cur: '€', conf: 2, src: 'uffizi.it' },
note: 'guides suggest 23 h', img: ['🏛️', 'Palazzo Pitti', 'linear-gradient(135deg,#b5533c,#7a3a2c)',
'https://thumb.wikimedia.org/wikipedia/commons/thumb/7/72/Palacio_Pitti%2C_Florencia%2C_Italia%2C_2022-09-18%2C_DD_197.jpg/1280px-Palacio_Pitti%2C_Florencia%2C_Italia%2C_2022-09-18%2C_DD_197.jpg'],
detail: {
summary: 'The Medici familys riverside palazzo: the Galleria Palatina (Portraiture, the 500 Room) and the modern-art Galleria dArte Moderna. Rooms are small and crowded in peak season — the apartments alone run 1.52 h if you linger, ~1 h for the highlights. September afternoons are the sweet spot: light is good for photos and lines thin after 16:00.',
links: [
{ label: 'Official site', href: 'https://www.palazzopittiturin.it' },
{ label: 'Tickets', href: 'https://www.uffizi.it/en/visit/tickets' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Attraction_Review-g187791-d183726-Reviews-Palazzo_Pitti-Florence_Tuscany.html' },
{ label: 'Reddit r/Florence', href: 'https://www.reddit.com/r/Florence/search/?q=palazzo+pitti&restrict_sr=1' }
] } },
{ id: 'S4', name: 'Lunch — Trattoria Mario', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.trattoriamario.it',
at: [43.7667, 11.2544], dur: 45, durConf: 3,
suggested: { value: 90, conf: 2, src: 'trattoriamario.it' },
price: { amount: 25, cur: '€', conf: 2, src: 'trattoriamario.it' },
img: ['🍝', 'Trattoria Mario', 'linear-gradient(135deg,#d08b3c,#9c5f1e)',
'https://upload.wikimedia.org/wikipedia/commons/1/12/Bistecca_alla_fiorentina-01.jpg'],
detail: {
summary: 'A no-frills Florentine kitchen on Via dei Neri, open since 1954. Counter seating, short menu (bistecca, lampredotto, tripe soup), and the queue is real — arriving as lunch opens (12:00) is the difference between 10 minutes and 40. Budget ~1 h including eating.',
links: [
{ label: 'Official site', href: 'https://www.trattoriamario.it' },
{ label: 'Menu', href: 'https://www.trattoriamario.it/menu' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Restaurant_Review-g187791-d310951-Reviews-Trattoria_Mario-Florence_Tuscany.html' },
{ label: 'Reddit r/food', href: 'https://www.reddit.com/r/food/search/?q=trattoria+mario+florence&restrict_sr=1' }
] } },
{ id: 'S5', name: 'Boboli Gardens', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.boboli.it',
at: [43.7579, 11.2463], dur: 120, durConf: 3,
suggested: { value: 120, conf: 2, src: 'boboli.it' },
price: { amount: 10, cur: '€', conf: 2, src: 'uffizi.it' },
img: ['🌿', 'Boboli Gardens', 'linear-gradient(135deg,#4f9e63,#2c6b40)',
'https://thumb.wikimedia.org/wikipedia/commons/thumb/d/d2/Obelisco%2C_Boboli_Gardens%2C_Florence_%2826408174180%29.jpg/1280px-Obelisco%2C_Boboli_Gardens%2C_Florence_%2826408174180%29.jpg'],
detail: {
summary: 'The Medicis formal garden behind Pitti — statues, the Apollo fountain, a hilltop view over the city. The full loop with the Accademia annex runs 2 h; the upper terraces alone are a fine 45-minute pace-break. Pairs naturally with Pitti: the entrance is the courtyard behind it.',
links: [
{ label: 'Official site', href: 'https://www.boboli.it' },
{ label: 'Tickets', href: 'https://www.uffizi.it/en/visit/tickets' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Attraction_Review-g187791-d183872-Reviews-Giardini_Di_Boboli-Florence_Tuscany.html' },
{ label: 'Reddit r/Florence', href: 'https://www.reddit.com/r/Florence/search/?q=boboli&restrict_sr=1' }
] } },
{ id: 'S6', name: 'Gelato — Vivoli', kind: 'meal', mealKind: 'snack',
slot: 'snack', state: 'planned',
at: [43.7680, 11.2537], dur: 20, durConf: 3,
suggested: { value: 20, conf: 1, src: null },
price: null,
img: ['🍦', 'Vivoli gelato', 'linear-gradient(135deg,#e0a5c0,#a56a92)'] },
{ id: 'S7', name: 'Lunch — Ristorante Santini', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'alt', url: 'https://www.santini.it',
at: [43.7646, 11.2528], dur: 75, durConf: 3,
suggested: { value: 90, conf: 2, src: 'santini.it' },
price: { amount: 45, cur: '€', conf: 2, src: 'santini.it' },
img: ['🍷', 'Ristorante Santini', 'linear-gradient(135deg,#7a4a5c,#4c2b3a)',
'https://thumb.wikimedia.org/wikipedia/commons/thumb/c/c0/View_from_Giotto%27s_Bell_Tower_-_Florence.jpg/1280px-View_from_Giotto%27s_Bell_Tower_-_Florence.jpg'],
detail: {
summary: 'A classic Florentine restaurant in a 16th-century building on the Arno — the terrace looks across to Pitti. Set tables, longer menus, lunch from 12:30. The step-up option: worth holding if Marios queue looks long, or if the day goes well and you want to mark it.',
links: [
{ label: 'Official site', href: 'https://www.santini.it' },
{ label: 'Menu', href: 'https://www.santini.it/en/menu' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Restaurant_Review-g187791-d1442007-Reviews-Ristorante_Santini-Florence_Tuscany.html' },
{ label: 'Reddit r/food', href: 'https://www.reddit.com/r/food/search/?q=santini+florence&restrict_sr=1' }
] } }
]
},
{ id: 'D2', date: '2026-09-13', label: 'Day 2 · Sat 13',
startMin: 8 * 60 + 30, wakingHours: 11,
base: { name: 'Hotel Palagio', at: [43.7684, 11.2566] },
legs: [],
stops: [
{ id: 'S8', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [43.7684, 11.2566], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S9', name: 'Galleria dellAccademia (David)', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.galleriaaccademia.firenze.it',
at: [43.7734, 11.2628], dur: 90, durConf: 3,
suggested: { value: 120, conf: 2, src: 'wikivoyage.org' },
price: { amount: 16, cur: '€', conf: 2, src: 'galleriaaccademia.firenze.it' },
img: ['🗿', 'Accademia', 'linear-gradient(135deg,#6b7280,#3f4650)'],
detail: {
summary: 'Home of the original David plus Donatellos Prigione. Timed-entry tickets, and the David room moves in batches — a 09:30 slot is noticeably calmer than midday. Allow ~90 min; 2 h if you do the whole collection.',
links: [
{ label: 'Official site', href: 'https://www.galleriaaccademia.firenze.it' },
{ label: 'Tickets', href: 'https://www.galleriaaccademia.firenze.it/en/tickets' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Attraction_Review-g187791-d183692-Reviews-Galleria_dell_Accademia-Florence_Tuscany.html' },
{ label: 'Reddit r/Florence', href: 'https://www.reddit.com/r/Florence/search/?q=accademia+david&restrict_sr=1' }
] } },
{ id: 'S10', name: 'Lunch — Trattoria Sostanza', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.sostanza.it',
at: [43.7609, 11.2469], dur: 75, durConf: 3,
suggested: { value: 90, conf: 2, src: 'tripadvisor.com' },
price: { amount: 35, cur: '€', conf: 2, src: 'tripadvisor.com' },
img: ['🍕', 'Trattoria Sostanza', 'linear-gradient(135deg,#b0563c,#7a3a2c)'],
detail: {
summary: 'Oltrarno kitchen a short walk across the Arno from the Accademia: Florentine classics, a proper wine list, and a roomy back — the kind of lunch that runs long. Reservations recommended on Saturdays.',
links: [
{ label: 'Official site', href: 'https://www.sostanza.it' },
{ label: 'Menu', href: 'https://www.sostanza.it/menu' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Restaurant_Review-g187791-d559983-Reviews-Trattoria_Sostanza-Florence_Tuscany.html' },
{ label: 'Reddit r/food', href: 'https://www.reddit.com/r/food/search/?q=sostanza+florence&restrict_sr=1' }
] } },
{ id: 'S11', name: 'Oltrarno stroll · San Niccolò', kind: 'visit', slot: 'visit', state: 'planned',
at: [43.7613, 11.2497], dur: 60, durConf: 3,
suggested: { value: 60, conf: 1, src: null },
price: null,
img: ['⛪', 'Oltrarno', 'linear-gradient(135deg,#8f7a4f,#5c4d2e)'] }
]
},
{ id: 'D3', date: '2026-09-14', label: 'Day 3 · Sun 14',
startMin: 9 * 60, wakingHours: 11,
base: { name: 'Hotel Palagio', at: [43.7684, 11.2566] },
legs: [],
stops: [
{ id: 'S12', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [43.7684, 11.2566], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S13', name: 'San Marco & cloister', kind: 'visit', slot: 'visit', state: 'planned',
at: [43.7723, 11.2597], dur: 45, durConf: 3,
suggested: { value: 45, conf: 1, src: null },
price: { amount: 8, cur: '€', conf: 1, src: null },
img: ['🖼️', 'San Marco', 'linear-gradient(135deg,#7a6a4f,#4d422e)'] },
{ id: 'S14', name: 'Aperitivo — Il Latte', kind: 'meal', mealKind: 'dinner',
slot: 'dinner', state: 'maybe',
url: 'https://www.illatte.it',
at: [43.7700, 11.2560], dur: 60, durConf: 3,
suggested: { value: 60, conf: 1, src: null },
price: { amount: 12, cur: '€', conf: 2, src: 'tripadvisor.com' },
img: ['🥂', 'Il Latte', 'linear-gradient(135deg,#c9a13c,#8f6f1e)'] }
]
},
{ id: 'D4', date: '2026-09-15', label: 'Day 4 · Mon 15', isDeparture: true,
startMin: 8 * 60, wakingHours: 11,
base: { name: 'Hotel Palagio', at: [43.7684, 11.2566] },
legs: [],
stops: [
{ id: 'S15', name: 'Breakfast & check-out', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [43.7684, 11.2566], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S16', name: 'Train → Pisa Centrale', kind: 'transit', slot: 'transit', state: 'planned',
at: [43.7726, 11.2597], dur: 30, durConf: 4,
transit: { name: 'Frecciarossa 9512', ref: 'F-55812',
from: 'Firenze SMN', to: 'Pisa Centrale',
depart: '09:12', arrive: '09:58', arriveBy: '08:42',
buffer: '30 min before departure' },
suggested: null,
price: { amount: 45, cur: '€', conf: 4, src: 'booking' },
img: ['🚄', 'Frecciarossa 9512', 'linear-gradient(135deg,#274b8f,#16294d)'] }
]
}
],
// focus-mode candidate pools (what a slot search returns)
candidates: {
hotel: [
{ id: 'H1', name: 'Hotel Palagio', at: [43.7684, 11.2566], price: 180, dur: null,
tags: ['central', 'quiet', 'refundable'], pitch: 'Where you are now — refundable until the 8th.', url: null },
{ id: 'H2', name: 'Hotel Savoy', at: [43.7726, 11.2565], price: 210, dur: null,
tags: ['near SMN', 'grand', 'rooftop'], pitch: 'Grand hotel by the station — handy for the 15th, 10 min from the centre.', url: null },
{ id: 'H3', name: 'Residenza Oltrarno', at: [43.7604, 11.2510], price: 140, dur: null,
tags: ['oltrarno', 'small', 'value'], pitch: 'Six rooms above a bookshop — cheaper, and puts you on the south side.', url: null },
],
lunch: [
{ id: 'C1', name: 'Trattoria Mario', at: [43.7667, 11.2544], price: 25, dur: 90,
tags: ['counter', 'local', 'queue'], pitch: 'No-frills counter since 1954 — the queue is the point.',
url: 'https://www.trattoriamario.it', img: ['🍝', 'Trattoria Mario', 'linear-gradient(135deg,#d08b3c,#9c5f1e)', 'https://upload.wikimedia.org/wikipedia/commons/1/12/Bistecca_alla_fiorentina-01.jpg'] },
{ id: 'C2', name: 'Ristorante Santini', at: [43.7646, 11.2528], price: 45, dur: 90,
tags: ['views', 'terrace', 'splurge'], pitch: '16th-century set tables on the Arno, terrace across from Pitti.',
url: 'https://www.santini.it', img: ['🍷', 'Santini', 'linear-gradient(135deg,#7a4a5c,#4c2b3a)'] },
{ id: 'C3', name: 'Trattoria Sostanza', at: [43.7609, 11.2469], price: 35, dur: 90,
tags: ['oltrarno', 'roomy', 'wine'], pitch: 'Oltrarno kitchen, proper wine list, a roomy back — runs long.',
url: 'https://www.sostanza.it', img: ['🍕', 'Sostanza', 'linear-gradient(135deg,#b0563c,#7a3a2c)'] },
{ id: 'C4', name: 'Vendemmia', at: [43.7735, 11.2585], price: 30, dur: 75,
tags: ['vegetarian', 'garden', 'near SMN'], pitch: 'Garden terrace by SMN, strong vegetarian menu, calmer pace.',
url: 'https://www.vendemmia.it', img: ['🥗', 'Vendemmia', 'linear-gradient(135deg,#5f9e6b,#33613c)'] },
{ id: 'C5', name: 'La Cantina di San Niccolò', at: [43.7615, 11.2490], price: 22, dur: 75,
tags: ['cellar', 'cozy', 'oltrarno'], pitch: 'Stone cellar, candlelight, short honest menu — book ahead.',
url: 'https://www.lacantina.it', img: ['🕯️', 'La Cantina', 'linear-gradient(135deg,#8f7a4f,#5c4d2e)'] },
{ id: 'C6', name: 'Buonami Pizza', at: [43.7680, 11.2550], price: 15, dur: 45,
tags: ['fast', 'casual', 'near hotel'], pitch: 'Two-stop, fast, good — the reset-button lunch.',
url: 'https://www.buonami.it', img: ['🍕', 'Buonami', 'linear-gradient(135deg,#c98a2c,#8a5f1a)'] }
],
visit: [
{ id: 'V1', name: 'Piazzale Michelangelo', at: [43.7669, 11.2455], price: 0, dur: 60,
tags: ['views', 'sunset', 'free'], pitch: 'The postcard view — best from 17:00 as the light warms.',
url: 'https://en.wikipedia.org/wiki/Piazzale_Michelangelo', img: ['🌇', 'Piazzale Michelangelo', 'linear-gradient(135deg,#c97a3c,#8a4f1a)'] },
{ id: 'V2', name: 'San Miniato al Monte', at: [43.7620, 11.2521], price: 4, dur: 60,
tags: ['views', 'romanesque', 'calm'], pitch: 'Romanesque basilica on the hill — quiet, cool in the afternoon.',
url: 'https://en.wikipedia.org/wiki/San_Miniato_al_Monte', img: ['⛪', 'San Miniato', 'linear-gradient(135deg,#8f9a7a,#5c644d)'] },
{ id: 'V3', name: 'Santo Spirito square', at: [43.7603, 11.2496], price: 0, dur: 45,
tags: ['square', 'lazier', 'market'], pitch: 'Oltrarnos lazy square; Saturdays market is the event.',
url: 'https://en.wikipedia.org/wiki/Santo_Spirito,_Florence', img: ['☕', 'Santo Spirito', 'linear-gradient(135deg,#a58a6a,#6d5840)'] },
{ id: 'V4', name: 'Terme di Caracalla (ruins)', at: [43.7589, 11.2707], price: 10, dur: 75,
tags: ['ruins', 'open', 'afternoon'], pitch: 'Third-century baths in open fields — cool, empty, odd.',
url: 'https://en.wikipedia.org/wiki/Baths_of_Caracalla', img: ['🏛️', 'Terme di Caracalla', 'linear-gradient(135deg,#b0a58a,#756a4f)'] }
]
},
nudges: {
D1: [
{ id: 'N1', title: 'Lunch may be a little tight',
body: '45 min at Trattoria Mario — a meal there usually runs <b>~90 min</b> <i>(web)</i>, and the local queue adds to that.',
fix: { label: 'Extend to ~90 min', stop: 'S4', dur: 90,
msg: 'Lunch extended to 90 min — everything after it slides later; the day still fits.' } },
{ id: 'N2', title: 'Pitti: 1 h (you) vs ~2.5 h usually',
body: 'Guides suggest 23 h for the full complex. We kept your 1 h — no need to change anything.',
fix: { label: 'Keep as is' } }
],
D2: [],
D3: [
{ id: 'N3', title: 'Dinner is still an idea',
body: 'Il Latte is an option, not a plan — worth deciding by this evening so theres a fallback if its full.',
fix: { label: 'Keep as is' } }
]
},
multimodal: [
{ into: 'S5', dur: 18, sub: [{ mode: 'foot', dur: 6 }, { mode: 'tram', dur: 12 }] }
],
vibes: [
{ id: 'v1', label: 'art & food · relaxed pace' },
{ id: 'v2', label: 'gardens & walking' },
{ id: 'v3', label: 'day trip · Fiesole' },
{ id: 'v4', label: 'views & aperitivo' }
],
suggestions: [
{ stop: 'S2', pitch: 'The Palazzo Medicis private apartments — less crowded than the Uffizi, opens right at 9:30.',
enrich: 'open 09:3019:30 · skip-the-line available' },
{ stop: 'S4', pitch: 'No-frills Florentine kitchen the locals queue for. Expect a wait without a reservation.',
enrich: 'lunch 12:0014:30 · dinner from 19:30 · ~1 h typically' },
{ stop: 'S5', pitch: 'Renaissance gardens behind Pitti — a good pace-break in the heat; ~2 h is the sweet spot.',
enrich: 'open 08:1519:30 · last entry 18:15' },
{ stop: 'S7', pitch: 'The step-up lunch: set tables on the Arno, terrace across from Pitti. Worth holding if Marios queue looks long.',
enrich: 'lunch from 12:30 · book ahead in season' }
]
};
// ---------------------------------------------------------------------------
// Boston — same shapes, but INSIDE the local OSRM extract (NE-US), so the
// routeClient upgrades legs to real road-network times + geometry.
// ---------------------------------------------------------------------------
const BOSTON = {
trip: {
id: 'trip-2026-09-b',
version: 1,
title: 'Boston 1416 Sep',
stays: [
{ id: 'ST1', name: 'Hotel Commonwealth', at: [42.3495, -71.0960], place: 'Boston',
checkIn: '2026-09-14', checkOut: '2026-09-16', state: 'booked', price: 165 },
],
places: [
{ id: 'PL1', name: 'Boston', bbox: [42.33, -71.12, 42.375, -71.04],
center: [42.357, -71.063], vibe: 'history + harbor, food-first',
dates: ['2026-09-14', '2026-09-16'] }
],
bookings: [
{ type: 'hotel', name: 'Hotel Commonwealth', ref: 'H-55210',
dates: '1416 Sep', where: [42.3495, -71.0960], status: 'booked', source: 'user_booking' },
{ type: 'train', name: 'Amtrak 700', ref: 'A-33421',
from: 'Back Bay', to: 'Penn Station, NYC', date: '2026-09-16',
depart: '17:00', arrive: '19:40', status: 'booked', source: 'user_booking',
stations: { from: [42.3519, -71.0654], to: [40.7506, -73.9935] } }
]
},
days: [
{ id: 'D1', date: '2026-09-14', label: 'Day 1 · Mon 14',
startMin: 8 * 60, wakingHours: 11,
base: { name: 'Hotel Commonwealth', at: [42.3495, -71.0960] },
legs: [],
stops: [
{ id: 'S1', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [42.3495, -71.0960], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S2', name: 'Museum of Fine Arts', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.mfa.org',
at: [42.3394, -71.0949], dur: 120, durConf: 3,
suggested: { value: 150, conf: 2, src: 'mfa.org' },
price: { amount: 25, cur: '$', conf: 2, src: 'mfa.org' },
img: ['🖼️', 'MFA Boston', 'linear-gradient(135deg,#7a4a5c,#4c2b3a)',
'https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/MFA%2C_Boston%2C_MA.jpg/1280px-MFA%2C_Boston%2C_MA.jpg'],
detail: {
summary: 'One of the largest art museums in the US, next to the hotel. The Impressionist wing and the Asian galleries are the anchors — allow 2 h for highlights, 3 h if you linger in the Japanese scroll wing. Thursday/Friday the light in the skylit atrium is the best.',
links: [
{ label: 'Official site', href: 'https://www.mfa.org' },
{ label: 'Tickets', href: 'https://www.mfa.org/visit/tickets' },
{ label: 'TripAdvisor', href: 'https://www.tripadvisor.com/Attraction_Review-g1146335-d183805-Reviews-Museum_of_Fine_Arts-Boston_MA.html' }
] } },
{ id: 'S4', name: 'Lunch — State Street Fare', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.statestreetfare.com',
at: [42.3410, -71.0935], dur: 60, durConf: 3,
suggested: { value: 75, conf: 2, src: 'statestreetfare.com' },
price: { amount: 30, cur: '$', conf: 2, src: 'statestreetfare.com' },
img: ['🍽️', 'State Street Fare', 'linear-gradient(135deg,#d08b3c,#9c5f1e)'],
detail: {
summary: 'The MFAs own café with a garden terrace — New England seasonal plates. Its the sensible choice after a museum morning; the terrace gets crowded by 13:00 on weekends.',
links: [
{ label: 'Official site', href: 'https://www.statestreetfare.com' },
{ label: 'Menu', href: 'https://www.statestreetfare.com/menu' }
] } },
{ id: 'S5', name: 'Boston Public Library (Coppa Wing)', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.bpl.org',
at: [42.3499, -71.0769], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: 'bpl.org' },
price: { amount: 0, cur: '$', conf: 2, src: 'bpl.org' },
img: ['📚', 'BPL', 'linear-gradient(135deg,#5f7a4f,#3a4f2c)'],
detail: {
summary: 'The Beaux-Arts free reading room and the Domitian & Apollo statues in the courtyard. A good 4560 min breather on the walk toward the North End — the architecture alone is worth it.',
links: [
{ label: 'Official site', href: 'https://www.bpl.org' }
] } },
{ id: 'S6', name: 'Faneuil Hall & Quincy Market', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.boston.gov/departments/parks/faneuil-hall',
at: [42.3601, -71.0555], dur: 90, durConf: 3,
suggested: { value: 90, conf: 2, src: 'boston.gov' },
price: null,
img: ['🏛️', 'Faneuil Hall', 'linear-gradient(135deg,#b5533c,#7a3a2c)'],
detail: {
summary: 'The “Cradle of Liberty” and the covered food market. Best worked top-down: the market stalls first (clam chowder, lemonade), then the halls free exhibits. Weekends are busy — September afternoons are kinder.',
links: [
{ label: 'Official site', href: 'https://www.boston.gov/departments/parks/faneuil-hall' },
{ label: 'Quincy Market', href: 'https://www.quincymarket.com' }
] } }
]
},
{ id: 'D2', date: '2026-09-15', label: 'Day 2 · Tue 15',
startMin: 8 * 60 + 30, wakingHours: 11,
base: { name: 'Hotel Commonwealth', at: [42.3495, -71.0960] },
legs: [],
stops: [
{ id: 'S8', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [42.3495, -71.0960], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S9', name: 'Paul Revere House', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.boston.gov/departments/parks/paul-revere-house',
at: [42.3616, -71.0631], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: 'boston.gov' },
price: { amount: 10, cur: '$', conf: 2, src: 'boston.gov' },
img: ['🏠', 'Paul Revere House', 'linear-gradient(135deg,#8f7a4f,#5c4d2e)'],
detail: {
summary: 'The oldest house in Boston, a tight 3045 min with the resident docent. Timed entry; the North End around it is the walk.',
links: [
{ label: 'Official site', href: 'https://www.boston.gov/departments/parks/paul-revere-house' }
] } },
{ id: 'S10', name: 'Lunch — Union Oyster House', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.unionoysterhouse.com',
at: [42.3621, -71.0604], dur: 60, durConf: 3,
suggested: { value: 75, conf: 2, src: 'unionoysterhouse.com' },
price: { amount: 40, cur: '$', conf: 2, src: 'unionoysterhouse.com' },
img: ['🦪', 'Union Oyster House', 'linear-gradient(135deg,#4f7a9e,#2c4f6b)'],
detail: {
summary: 'Claimed oldest restaurant in the US (1826). Clam chowder and a short, serious menu. Book ahead for the lunch hour — the room is small.',
links: [
{ label: 'Official site', href: 'https://www.unionoysterhouse.com' }
] } },
{ id: 'S11', name: 'New England Aquarium', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://neaq.org',
at: [42.3598, -71.0483], dur: 120, durConf: 3,
suggested: { value: 120, conf: 2, src: 'neaq.org' },
price: { amount: 35, cur: '$', conf: 2, src: 'neaq.org' },
img: ['🐋', 'NE Aquarium', 'linear-gradient(135deg,#2f6fd6,#1d4f9c)'],
detail: {
summary: 'The whale tank and the Harborwalk beyond it. September is a good time — the new whale migration is winding down but the penguin and jelly exhibits are year-round. Allow 2 h.',
links: [
{ label: 'Official site', href: 'https://neaq.org' }
] } }
]
},
{ id: 'D3', date: '2026-09-16', label: 'Day 3 · Wed 16', isDeparture: true,
startMin: 8 * 60, wakingHours: 11,
base: { name: 'Hotel Commonwealth', at: [42.3495, -71.0960] },
legs: [],
stops: [
{ id: 'S15', name: 'Breakfast & check-out', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [42.3495, -71.0960], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S12', name: 'Boston Common & Public Garden', kind: 'visit', slot: 'visit', state: 'planned',
at: [42.3547, -71.0717], dur: 45, durConf: 3,
suggested: { value: 45, conf: 1, src: null },
price: null,
img: ['🌳', 'Public Garden', 'linear-gradient(135deg,#4f9e63,#2c6b40)'] },
{ id: 'S16', name: 'Train → Penn Station NYC', kind: 'transit', slot: 'transit', state: 'planned',
at: [42.3519, -71.0654], dur: 30, durConf: 4,
transit: { name: 'Amtrak 700', ref: 'A-33421',
from: 'Back Bay', to: 'Penn Station, NYC',
depart: '17:00', arrive: '19:40', arriveBy: '16:30',
buffer: '30 min before departure' },
suggested: null,
price: { amount: 65, cur: '$', conf: 4, src: 'booking' },
img: ['🚄', 'Amtrak 700', 'linear-gradient(135deg,#274b8f,#16294d)'] }
]
}
],
candidates: {
hotel: [
{ id: 'H1', name: 'Hotel Commonwealth', at: [42.3495, -71.0960], price: 165, dur: null,
tags: ['by the mfa', 'quiet', 'refundable'], pitch: 'Where you are now — one block from the MFA, refundable until the 12th.', url: null },
{ id: 'H2', name: 'The Liberty Hotel', at: [42.3580, -71.0530], price: 240, dur: null,
tags: ['north end', 'rooftop', 'grand'], pitch: 'Rooftop bar over the North End — closer to the food, pricier.', url: null },
{ id: 'H3', name: 'Haven Guest House', at: [42.3470, -71.0900], price: 120, dur: null,
tags: ['value', 'small', 'near mfa'], pitch: 'Six rooms off the MFAs garden — cheaper, a short walk to everything.', url: null }
],
lunch: [
{ id: 'C1', name: 'State Street Fare', at: [42.3410, -71.0935], price: 30, dur: 75,
tags: ['by the mfa', 'garden', 'seasonal'], pitch: 'The MFAs café — garden terrace, New England seasonal plates.',
url: 'https://www.statestreetfare.com', img: ['🍽️', 'State Street Fare', 'linear-gradient(135deg,#d08b3c,#9c5f1e)'] },
{ id: 'C2', name: 'Union Oyster House', at: [42.3621, -71.0604], price: 40, dur: 75,
tags: ['historic', 'oysters', 'book ahead'], pitch: 'Claimed oldest restaurant in the US — clam chowder, a short serious menu.',
url: 'https://www.unionoysterhouse.com', img: ['🦪', 'Union Oyster', 'linear-gradient(135deg,#4f7a9e,#2c4f6b)'] },
{ id: 'C3', name: 'Eataly Boston', at: [42.3595, -71.0540], price: 35, dur: 60,
tags: ['market', 'fast', 'central'], pitch: 'The food-hall option — a dozen kitchens, grab and go, by Quincy Market.',
url: 'https://boston.eataly.net', img: ['🍕', 'Eataly', 'linear-gradient(135deg,#b0563c,#7a3a2c)'] },
{ id: 'C4', name: 'Kazuo', at: [42.3540, -71.0620], price: 32, dur: 60,
tags: ['japanese', 'counter', 'near the common'], pitch: 'Japanese counter near the Common — quiet, good, a little pricier.',
url: 'https://kazooboston.com', img: ['🍣', 'Kazuo', 'linear-gradient(135deg,#5f6b7a,#3a434f)'] }
],
visit: [
{ id: 'V1', name: 'John F. Kennedy Presidential Library', at: [42.3599, -71.0590], price: 15, dur: 90,
tags: ['history', 'harbor view', 'indoor'], pitch: 'The JFK archives with a harbor view — a good 90 min, indoor if it turns.',
url: 'https://www.jfklibrary.org', img: ['🎙️', 'JFK Library', 'linear-gradient(135deg,#4a5a78,#2f3a50)'] },
{ id: 'V2', name: 'Harborwalk', at: [42.3590, -71.0420], price: 0, dur: 60,
tags: ['outdoor', 'harbor', 'walk'], pitch: 'The esplanade along the harbor — sunset is the point in September.',
url: 'https://www.boston.gov/departments/parks/harborwalk', img: ['🌅', 'Harborwalk', 'linear-gradient(135deg,#c97a3c,#8a4f1a)'] },
{ id: 'V3', name: 'T. F. Green Memorial', at: [42.3530, -71.0470], price: 0, dur: 45,
tags: ['views', 'free', 'calm'], pitch: 'The memorial at the harbors edge — a quiet, photo-friendly 3045 min.',
url: 'https://www.boston.gov', img: ['⛵', 'T. F. Green', 'linear-gradient(135deg,#7a8a9e,#4f5a6b)'] },
{ id: 'V4', name: 'Boston Tea Party Ships & Museum', at: [42.3555, -71.0535], price: 30, dur: 75,
tags: ['history', 'interactive', 'harbor'], pitch: 'A 75-min interactive re-enactment on the harbor — fun, a little kitsch.',
url: 'https://www.bostonteachallenge.org', img: ['🫖', 'Tea Party', 'linear-gradient(135deg,#8f4f4f,#5c2f2f)'] }
]
},
nudges: {
D1: [
{ id: 'N1', title: 'Lunch may be a little tight',
body: '60 min at State Street Fare — the terrace lunch usually runs <b>~75 min</b> <i>(web)</i>, and it crowds by 13:00.',
fix: { label: 'Extend to ~75 min', stop: 'S4', dur: 75,
msg: 'Lunch extended to 75 min — the afternoon slides a little later; the day still fits.' } },
{ id: 'N2', title: 'MFA: 2 h (you) vs ~2.5 h usually',
body: 'Guides suggest 2.53 h for the highlights. We kept your 2 h — the Impressionist wing alone runs that long.',
fix: { label: 'Keep as is' } }
],
D2: [
{ id: 'N3', title: 'Union Oyster House at the lunch hour',
body: 'The room is small and booked solid 12:0013:30 — a 12:00 seat beats a 12:30 walk-in.',
fix: { label: 'Keep as is' } }
],
D3: []
},
multimodal: [],
vibes: [
{ id: 'v1', label: 'history & harbor · relaxed pace' },
{ id: 'v2', label: 'museums & art' },
{ id: 'v3', label: 'food-first, North End' },
{ id: 'v4', label: 'views & sunset walk' }
],
suggestions: [
{ stop: 'S2', pitch: 'The MFAs Impressionist wing and the Japanese scroll room — 2 h for highlights, 3 h if you linger.',
enrich: 'open 10:0017:00 · free first Thu 49 · one block from the hotel' },
{ stop: 'S4', pitch: 'The MFAs own café — the garden terrace gets crowded by 13:00 on weekends.',
enrich: 'lunch 12:0015:00 · ~1 h · terrace seats' },
{ stop: 'S5', pitch: 'The Beaux-Arts free reading room and the courtyards Domitian & Apollo — a good breather toward the North End.',
enrich: 'open 10:0018:00 · free · ~45 min' },
{ stop: 'S6', pitch: 'Work the market stalls first (chowder, lemonade), then the free hall exhibits — weekends are busy.',
enrich: 'stalls 10:0017:00 · hall free · ~90 min' }
]
};
// ---------------------------------------------------------------------------
// COLOMBIA · Cartagena → Santa Marta — the working trip. Inside the local
// walking-profile OSRM extract (:5003, `colombia`), so every on-foot leg
// upgrades to real road-network times + geometry; the flights are fixed.
const COLOMBIA = {
id: 'colombia',
router: 'colombia',
trip: {
id: 'trip-2026-09-col',
version: 1,
title: 'Colombia · Cartagena → Santa Marta 2128 Sep',
stays: [
{ id: 'ST1', name: 'Hotel Casa San Antonio', at: [10.3952, -75.5522], place: 'Getsemaní, Cartagena',
checkIn: '2026-09-21', checkOut: '2026-09-24', state: 'booked', price: 120 },
{ id: 'ST2', name: 'Hotel Muelle de Bolívar', at: [11.2390, -74.2130], place: 'Centro, Santa Marta',
checkIn: '2026-09-25', checkOut: '2026-09-28', state: 'booked', price: 150 },
],
places: [
{ id: 'PL1', name: 'Cartagena', bbox: [10.36, -75.62, 10.48, -75.42],
center: [10.42, -75.52], vibe: 'forts, old town & Caribbean sea',
dates: ['2026-09-21', '2026-09-25'] },
{ id: 'PL2', name: 'Santa Marta', bbox: [11.08, -74.27, 11.28, -74.13],
center: [11.18, -74.20], vibe: 'malecón, muelle & Tayrona',
dates: ['2026-09-25', '2026-09-28'] },
],
bookings: [
{ type: 'hotel', name: 'Hotel Casa San Antonio', ref: 'C-20117',
dates: '2125 Sep', where: [10.3952, -75.5522], status: 'booked', source: 'user_booking' },
{ type: 'hotel', name: 'Hotel Muelle de Bolívar', ref: 'C-20118',
dates: '2528 Sep', where: [11.2390, -74.2130], status: 'booked', source: 'user_booking' },
{ type: 'flight', name: 'AV 1351', ref: 'AV-88213',
from: 'Rafael Núñez (CTG)', to: 'Simón Bolívar (SMR)', date: '2026-09-25',
depart: '08:40', arrive: '09:25', status: 'booked', source: 'user_booking',
stations: { from: [10.4622, -75.4385], to: [11.1165, -74.2330] } },
{ type: 'flight', name: 'AV 1352', ref: 'AV-88214',
from: 'Simón Bolívar (SMR)', to: 'Bogotá (BOG)', date: '2026-09-28',
depart: '15:10', arrive: '15:55', status: 'booked', source: 'user_booking',
stations: { from: [11.1165, -74.2330], to: [4.7016, -74.1469] } },
]
},
days: [
{ id: 'D1', date: '2026-09-21', label: 'Day 1 · Mon 21',
startMin: 9 * 60, wakingHours: 12,
base: { name: 'Hotel Casa San Antonio', at: [10.3952, -75.5522] },
legs: [],
stops: [
{ id: 'S1', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [10.3952, -75.5522], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S2', name: 'Getsemaní street-art walk', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Getseman%C3%AD,_Cartagena',
at: [10.3932, -75.5520], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: 'cartagenatourism.com' },
price: null,
img: ['🎨', 'Getsemaní street art', 'linear-gradient(135deg,#c98a3c,#8a5c24)'],
detail: {
summary: 'The former port neighbourhood — a hundred walls of murals between narrow lanes. Start at Plaza de los Coches and wander south to the market; mornings are cool and the lanes are empty before 10:00.',
links: [ { label: 'Getsemaní', href: 'https://en.wikipedia.org/wiki/Getseman%C3%AD,_Cartagena' } ] } },
{ id: 'S3', name: 'Lunch — La Casona de San Diego', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.tripadvisor.com',
at: [10.3940, -75.5545], dur: 75, durConf: 3,
suggested: { value: 75, conf: 2, src: 'tripadvisor.com' },
price: { amount: 18, cur: '$', conf: 2, src: 'tripadvisor.com' },
img: ['🍲', 'La Casona de San Diego', 'linear-gradient(135deg,#3c8f6b,#1f5c44)'],
detail: {
summary: 'Caribbean home cooking in a whitewashed casona — sancocho de pescado, mango con arepa. Cash only; the line moves fast at 12:30.',
links: [ { label: 'TripAdvisor', href: 'https://www.tripadvisor.com' } ] } },
{ id: 'S4', name: 'Mercado de Getsemaní', kind: 'visit', slot: 'visit', state: 'planned',
at: [10.3945, -75.5530], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: null },
price: null,
img: ['🥭', 'Mercado de Getsemaní', 'linear-gradient(135deg,#8f6b3c,#5c441f)'],
detail: {
summary: 'Fish, fruit and dried herbs stacked to the ceiling — the market the neighbourhood actually uses. A coconut and a fresh juice while you look at the catch is the correct way to spend 45 minutes here.',
links: [ { label: 'Cartagena tourism', href: 'https://www.cartagenatourism.com' } ] } },
{ id: 'S5', name: 'Plaza de la Aduana & Cathedral', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Cartagena_Cathedral',
at: [10.4615, -75.5550], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: 'cartagenatourism.com' },
price: null,
img: ['⛪', 'Catedral de Cartagena', 'linear-gradient(135deg,#8f3c5c,#5c1f3a)'],
detail: {
summary: 'The 1594 cathedral is the oldest in Colombia and sits on the square where the old citys life happens. Golden hour: the facade turns pink and the plaza fills with musicians. Entry is cheap; the belfry stairs are the view.',
links: [ { label: 'Cartagena Cathedral', href: 'https://en.wikipedia.org/wiki/Cartagena_Cathedral' } ] } }
]
},
{ id: 'D2', date: '2026-09-22', label: 'Day 2 · Tue 22',
startMin: 8 * 60 + 30, wakingHours: 12,
base: { name: 'Hotel Casa San Antonio', at: [10.3952, -75.5522] },
legs: [],
stops: [
{ id: 'S6', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [10.3952, -75.5522], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S7', name: 'Casa de la Moneda', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Casa_de_la_Moneda_(Cartagena)',
at: [10.4619, -75.5544], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: 'cartagenatourism.com' },
price: { amount: 5, cur: '$', conf: 2, src: 'cartagenatourism.com' },
img: ['🪙', 'Casa de la Moneda', 'linear-gradient(135deg,#8f8a3c,#5c571f)'],
detail: {
summary: 'The colonial mint — the coin-moulding room and the courtyard where the galleons silver was counted. Small, cool and fast: 45 minutes is exactly right.',
links: [ { label: 'Casa de la Moneda', href: 'https://en.wikipedia.org/wiki/Casa_de_la_Moneda_(Cartagena)' } ] } },
{ id: 'S8', name: 'Palacio de la Inquisición', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Palacio_de_la_Inquisici%C3%B3n_(Cartagena)',
at: [10.4604, -75.5561], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: 'cartagenatourism.com' },
price: { amount: 7, cur: '$', conf: 2, src: 'cartagenatourism.com' },
img: ['🏛️', 'Palacio de la Inquisición', 'linear-gradient(135deg,#3c5c8f,#1f3a5c)'],
detail: {
summary: 'The Inquisitions 16th-century house — the courtyard fountain, the old interrogation rooms and the museums quiet gold room. The 23:00 evening slot with the plaza lit by candles is the famous one; daytime is calm.',
links: [ { label: 'Palacio de la Inquisición', href: 'https://en.wikipedia.org/wiki/Palacio_de_la_Inquisici%C3%B3n_(Cartagena)' } ] } },
{ id: 'S9', name: 'San Pedro Claver', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/San_Pedro_Claver_Church',
at: [10.4645, -75.5557], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: null },
price: null,
img: ['🕊️', 'San Pedro Claver', 'linear-gradient(135deg,#7a7a8f,#4f4f61)'],
detail: {
summary: 'The whitest church in the old city — and the resting place of Pedro Claver, “apostle of the negroes”. Free, quiet, and the Plaza del Chopo behind it is where the old towns afternoon slows down.',
links: [ { label: 'San Pedro Claver', href: 'https://en.wikipedia.org/wiki/San_Pedro_Claver_Church' } ] } },
{ id: 'S10', name: 'Lunch — La Loma de San Diego', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.tripadvisor.com',
at: [10.4590, -75.5530], dur: 75, durConf: 3,
suggested: { value: 75, conf: 2, src: 'tripadvisor.com' },
price: { amount: 25, cur: '$', conf: 2, src: 'tripadvisor.com' },
img: ['🥘', 'La Loma de San Diego', 'linear-gradient(135deg,#c93c3c,#8a1f1f)'],
detail: {
summary: 'The old-town institution for arroz con coco and grilled fish — a big table under the patio, and the kitchen is part of the show. Go at 12:15 to skip the tour-group wave.',
links: [ { label: 'TripAdvisor', href: 'https://www.tripadvisor.com' } ] } },
{ id: 'S11', name: 'Cerro La Popa — sunset', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Cerro_de_La_Popa',
at: [10.4257, -75.5523], dur: 75, durConf: 3,
suggested: { value: 90, conf: 2, src: 'cartagenatourism.com' },
price: { amount: 10, cur: '$', conf: 2, src: 'cartagenatourism.com' },
img: ['🌅', 'Cerro La Popa', 'linear-gradient(135deg,#c95c3c,#8a3a1f)'],
detail: {
summary: 'The hilltop hermitage over the city — the panoramic terrace is the best single view in Cartagena: the fort, the old towns pink walls, the Gulf and Bocagrande in one frame. Be up 45 min before sunset or the terrace is elbow to elbow.',
links: [ { label: 'Cerro La Popa', href: 'https://en.wikipedia.org/wiki/Cerro_de_La_Popa' } ] } }
]
},
{ id: 'D3', date: '2026-09-23', label: 'Day 3 · Wed 23',
startMin: 8 * 60, wakingHours: 12,
base: { name: 'Hotel Casa San Antonio', at: [10.3952, -75.5522] },
legs: [],
stops: [
{ id: 'S12', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [10.3952, -75.5522], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S13', name: 'Castillo San Felipe', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://www.castillosanfelipe.gov.co',
at: [10.3930, -75.5565], dur: 90, durConf: 3,
suggested: { value: 120, conf: 2, src: 'castillosanfelipe.gov.co' },
price: { amount: 15, cur: '$', conf: 2, src: 'castillosanfelipe.gov.co' },
img: ['🏰', 'Castillo San Felipe', 'linear-gradient(135deg,#8a8f3c,#565b24)'],
detail: {
summary: 'The largest Spanish fort in the Americas — five levels of ramparts, tunnels and cannon lines around the whole peninsula. A full loop takes 2 h; 90 min gets the tunnels and the sea terrace. Go at opening (08:30) for the cold tunnels and no tour buses.',
links: [
{ label: 'Castillo San Felipe', href: 'https://www.castillosanfelipe.gov.co' },
{ label: 'Tickets', href: 'https://www.castillosanfelipe.gov.co/visita' }
] } },
{ id: 'S14', name: 'Castillo San Diego', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Castillo_San_Diego',
at: [10.4247, -75.5512], dur: 75, durConf: 3,
suggested: { value: 75, conf: 2, src: 'cartagenatourism.com' },
price: { amount: 8, cur: '$', conf: 2, src: 'cartagenatourism.com' },
img: ['⚓', 'Castillo San Diego', 'linear-gradient(135deg,#3c8f8a,#1f5c59)'],
detail: {
summary: 'The smaller fort guarding the harbour mouth from the other side — the 4th-floor terrace looks straight across to San Felipe. Pairs well as the afternoon half of a fort day.',
links: [ { label: 'Castillo San Diego', href: 'https://en.wikipedia.org/wiki/Castillo_San_Diego' } ] } },
{ id: 'S15', name: 'Lunch — Club El Boat', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.tripadvisor.com',
at: [10.4355, -75.5375], dur: 75, durConf: 3,
suggested: { value: 75, conf: 2, src: 'tripadvisor.com' },
price: { amount: 20, cur: '$', conf: 2, src: 'tripadvisor.com' },
img: ['🦐', 'Club El Boat', 'linear-gradient(135deg,#3c6b8f,#1f445c)'],
detail: {
summary: 'Seafood on the Bocagrande malecón — the langosta is the plate, the sunset the side order. Arrive 12:45 for the table with the water view before the 14:00 heat makes the terrace empty.',
links: [ { label: 'TripAdvisor', href: 'https://www.tripadvisor.com' } ] } },
{ id: 'S16', name: 'Malecón de Bocagrande', kind: 'visit', slot: 'visit', state: 'planned',
at: [10.4365, -75.5390], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: null },
price: null,
img: ['🌊', 'Malecón de Bocagrande', 'linear-gradient(135deg,#4f9ea0,#2c6b6d)'],
detail: {
summary: 'The 5 km seawall walk around the Caribbean — the long straight boardwalk is the citys living room. An hour south of the malecón covers the best stretch without getting sweaty.',
links: [ { label: 'Bocagrande', href: 'https://en.wikipedia.org/wiki/Bocagrande' } ] } }
]
},
{ id: 'D4', date: '2026-09-24', label: 'Day 4 · Thu 24',
startMin: 9 * 60, wakingHours: 12,
base: { name: 'Hotel Casa San Antonio', at: [10.3952, -75.5522] },
legs: [],
stops: [
{ id: 'S17', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [10.3952, -75.5522], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S18', name: 'Punta Canoa', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Punta_Canoa',
at: [10.4185, -75.5255], dur: 90, durConf: 3,
suggested: { value: 90, conf: 2, src: 'cartagenatourism.com' },
price: null,
img: ['🏖️', 'Punta Canoa', 'linear-gradient(135deg,#3cb5c9,#1f7a8a)'],
detail: {
summary: 'The old fishermens point — beach, hammocks and the best cheap seafood on a bench in the city. September mornings are calm; the water is flat glass before the wind comes off the gulf.',
links: [ { label: 'Punta Canoa', href: 'https://en.wikipedia.org/wiki/Punta_Canoa' } ] } },
{ id: 'S19', name: 'Lunch — beachside at Punta Canoa', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.tripadvisor.com',
at: [10.4190, -75.5245], dur: 75, durConf: 3,
suggested: { value: 75, conf: 2, src: 'tripadvisor.com' },
price: { amount: 15, cur: '$', conf: 2, src: 'tripadvisor.com' },
img: ['🍤', 'Punta Canoa beach', 'linear-gradient(135deg,#c9803c,#8a541f)'],
detail: {
summary: 'Whatever the boat landed this morning — fried whole, with coconut rice and yuca. Point to a table, the price is on the chalkboard, and the sea is the view.',
links: [ { label: 'TripAdvisor', href: 'https://www.tripadvisor.com' } ] } },
{ id: 'S20', name: 'Plaza del Chopo — farewell stroll', kind: 'visit', slot: 'visit', state: 'planned',
at: [10.4638, -75.5560], dur: 60, durConf: 3,
suggested: { value: 45, conf: 2, src: null },
price: null,
img: ['🌇', 'Plaza del Chopo', 'linear-gradient(135deg,#8f3c5c,#5c241f)'],
detail: {
summary: 'The old citys quietest square — a last loop past San Pedro Claver with the light going gold, and one more arepa con queso from the corner stall.',
links: [ { label: 'Cartagena old town', href: 'https://en.wikipedia.org/wiki/Cartagena' } ] } }
]
},
{ id: 'D5', date: '2026-09-25', label: 'Day 5 · Fri 25',
startMin: 7 * 60, wakingHours: 11,
base: { name: 'Hotel Muelle de Bolívar', at: [11.2390, -74.2130] },
legs: [],
stops: [
{ id: 'S21', name: 'Breakfast & check-out', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [10.3952, -75.5522], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S22', name: 'Vuelo CTG → SMR', kind: 'transit', slot: 'transit', state: 'planned',
at: [10.4622, -75.4385], dur: 45, durConf: 4,
transit: { name: 'AV 1351', ref: 'AV-88213',
from: 'Rafael Núñez (CTG)', to: 'Simón Bolívar (SMR)',
depart: '08:40', arrive: '09:25', arriveBy: '07:40',
buffer: '1 h before departure' },
suggested: null,
price: { amount: 89, cur: '$', conf: 4, src: 'booking' },
img: ['✈️', 'AV 1351', 'linear-gradient(135deg,#274b8f,#16294d)'] },
{ id: 'S23', name: 'Malecón & Plaza Colesio, Santa Marta', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Santa_Marta',
at: [11.2425, -74.2140], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: 'santamarta.gov.co' },
price: null,
img: ['🏙️', 'Malecón de Santa Marta', 'linear-gradient(135deg,#3c8f6b,#1f5c44)'],
detail: {
summary: 'The city Bolívar died in — the malecón runs from the muelle past Plaza Colesio under the palms. Check in, drop the bags, and walk the sea wall to shake the flight: the Caribbean here is warmer and emptier than Cartagenas.',
links: [ { label: 'Santa Marta', href: 'https://en.wikipedia.org/wiki/Santa_Marta' } ] } },
{ id: 'S24', name: 'Basílica de la Virgen del Perpetuo Socorro', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Bas%C3%ADlica_de_la_Virgen_del_Perpetuo_Socorro',
at: [11.2420, -74.2152], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: null },
price: null,
img: ['⛪', 'Basílica del Perpetuo Socorro', 'linear-gradient(135deg,#8f8a3c,#5c441f)'],
detail: {
summary: 'The citys most venerated image — the small baroque church on Plaza de la Aduana where the candles never stop. Ten minutes of quiet is enough; the plaza around it is the evening spot.',
links: [ { label: 'Basílica', href: 'https://en.wikipedia.org/wiki/Bas%C3%ADlica_de_la_Virgen_del_Perpetuo_Socorro' } ] } }
]
},
{ id: 'D6', date: '2026-09-26', label: 'Day 6 · Sat 26',
startMin: 8 * 60 + 30, wakingHours: 11,
base: { name: 'Hotel Muelle de Bolívar', at: [11.2390, -74.2130] },
legs: [],
stops: [
{ id: 'S25', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [11.2390, -74.2130], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S26', name: 'Casa de Raquel', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://en.wikipedia.org/wiki/Casa_de_Raquel',
at: [11.2418, -74.2118], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: 'santamarta.gov.co' },
price: { amount: 4, cur: '$', conf: 2, src: 'santamarta.gov.co' },
img: ['📜', 'Casa de Raquel', 'linear-gradient(135deg,#7a5c8f,#4f3a5c)'],
detail: {
summary: 'The house of Raquel Méndez — the girl whose love letters made Bolívars last years. The small museum upstairs has the original letters; the courtyard is the photo.',
links: [ { label: 'Casa de Raquel', href: 'https://en.wikipedia.org/wiki/Casa_de_Raquel' } ] } },
{ id: 'S27', name: 'Lunch — La Terraza del Muelle', kind: 'meal', mealKind: 'lunch',
slot: 'lunch', state: 'planned', url: 'https://www.tripadvisor.com',
at: [11.2375, -74.2140], dur: 75, durConf: 3,
suggested: { value: 75, conf: 2, src: 'tripadvisor.com' },
price: { amount: 20, cur: '$', conf: 2, src: 'tripadvisor.com' },
img: ['🐟', 'La Terraza del Muelle', 'linear-gradient(135deg,#3c6b8f,#1f445c)'],
detail: {
summary: 'Grilled fish on the terrace over the working harbour — the boats come in around noon and the menu follows the catch. The shrimp ceviche is the safe first order.',
links: [ { label: 'TripAdvisor', href: 'https://www.tripadvisor.com' } ] } },
{ id: 'S28', name: 'El Muelle & fish market', kind: 'visit', slot: 'visit', state: 'planned',
at: [11.2360, -74.2150], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: null },
price: null,
img: ['⚓', 'El Muelle de Santa Marta', 'linear-gradient(135deg,#5c6b8f,#3a445c)'],
detail: {
summary: 'The 19th-century stone pier and the market behind it — ice, line-caught fish, and the same crews who have run these boats for a century. The best 45 minutes in the city if you like to watch work happen.',
links: [ { label: 'El Muelle', href: 'https://en.wikipedia.org/wiki/El_Muelle_(Santa_Marta)' } ] } },
{ id: 'S29', name: 'Parque de los Novios', kind: 'visit', slot: 'visit', state: 'planned',
at: [11.2395, -74.2085], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: 'santamarta.gov.co' },
price: null,
img: ['💑', 'Parque de los Novios', 'linear-gradient(135deg,#6b8f4f,#445c2c)'],
detail: {
summary: 'The lakeside park where the city goes at dusk — the lagoon, the couples (it is in the name), and the walk back along the water to the hotel.',
links: [ { label: 'Santa Marta parks', href: 'https://santamarta.gov.co' } ] } }
]
},
{ id: 'D7', date: '2026-09-27', label: 'Day 7 · Sun 27',
startMin: 7 * 60 + 30, wakingHours: 12,
base: { name: 'Hotel Muelle de Bolívar', at: [11.2390, -74.2130] },
legs: [],
stops: [
{ id: 'S30', name: 'Breakfast at the hotel', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [11.2390, -74.2130], dur: 30, durConf: 3,
suggested: { value: 30, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S31', name: 'Bus → Tayrona (El Croado)', kind: 'transit', slot: 'transit', state: 'planned',
at: [11.1975, -74.1760], dur: 40, durConf: 3,
transit: { name: 'Bus Centro → El Croado', ref: 'TAY-01',
from: 'Santa Marta Centro', to: 'Parque Tayrona, El Croado',
depart: '08:30', arrive: '09:10', arriveBy: '08:30',
buffer: 'park gate opens 08:00' },
suggested: null,
price: { amount: 4, cur: '$', conf: 3, src: 'parquetayrona.gov.co' },
img: ['🚌', 'Bus to Tayrona', 'linear-gradient(135deg,#8f6b3c,#5c441f)'] },
{ id: 'S32', name: 'Playa Cangrejos', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://parquetayrona.gov.co',
at: [11.1765, -74.1690], dur: 60, durConf: 3,
suggested: { value: 60, conf: 2, src: 'parquetayrona.gov.co' },
price: { amount: 15, cur: '$', conf: 3, src: 'parquetayrona.gov.co' },
img: ['🌴', 'Playa Cangrejos', 'linear-gradient(135deg,#3cb5c9,#1f5c44)'],
detail: {
summary: 'The first beach on the park walk — jungle right up to the sand line, and the water so clear it looks edited. Two hours here is a long, correct rest after the walk in.',
links: [ { label: 'Parque Tayrona', href: 'https://parquetayrona.gov.co' } ] } },
{ id: 'S33', name: 'Cabo San Juan', kind: 'visit', slot: 'visit', state: 'planned',
url: 'https://parquetayrona.gov.co',
at: [11.1390, -74.1455], dur: 120, durConf: 3,
suggested: { value: 120, conf: 2, src: 'parquetayrona.gov.co' },
price: null,
img: ['🏝️', 'Cabo San Juan', 'linear-gradient(135deg,#c95c8a,#8a1f5c)'],
detail: {
summary: 'The parks far tip — the 2.5 km beach walk from Cangrejos is the trail: sand underfoot, jungle walls, the headland where the Caribbean hits the Sierra Nevada. September crowd levels are thin; the last boats out run 15:30, so turn around by 14:00.',
links: [ { label: 'Parque Tayrona', href: 'https://parquetayrona.gov.co' } ] } }
]
},
{ id: 'D8', date: '2026-09-28', label: 'Day 8 · Mon 28', isDeparture: true,
startMin: 9 * 60, wakingHours: 6,
base: { name: 'Hotel Muelle de Bolívar', at: [11.2390, -74.2130] },
legs: [],
stops: [
{ id: 'S34', name: 'Breakfast & check-out', kind: 'meal', mealKind: 'breakfast',
slot: 'breakfast', state: 'planned',
at: [11.2390, -74.2130], dur: 45, durConf: 3,
suggested: { value: 45, conf: 2, src: 'tripadvisor.com' } },
{ id: 'S35', name: 'Vuelo SMR → BOG', kind: 'transit', slot: 'transit', state: 'planned',
at: [11.1165, -74.2330], dur: 45, durConf: 4,
transit: { name: 'AV 1352', ref: 'AV-88214',
from: 'Simón Bolívar (SMR)', to: 'Bogotá (BOG)',
depart: '15:10', arrive: '15:55', arriveBy: '14:10',
buffer: '1 h before departure' },
suggested: null,
price: { amount: 95, cur: '$', conf: 4, src: 'booking' },
img: ['✈️', 'AV 1352', 'linear-gradient(135deg,#274b8f,#16294d)'] }
]
}
],
candidates: {
hotel: [
{ id: 'H1', name: 'Hotel Casa San Antonio', at: [10.3952, -75.5522], price: 120, dur: null,
tags: ['Getsemaní', 'colonial casona', 'roof terrace'], pitch: 'Where you are now — a whitewashed casona in Getsemaní with a terrace over the murals.', url: 'https://www.tripadvisor.com' },
{ id: 'H2', name: 'Hotel Casa 1800', at: [10.4606, -75.5547], price: 260, dur: null,
tags: ['old town', 'design', 'pool'], pitch: 'In the old town instead — the 19th-century palace turned design hotel; pricier, but you skip the walk entirely.', url: 'https://www.casa1800.com' },
{ id: 'H3', name: 'Villa El Banderito', at: [10.4340, -75.5365], price: 140, dur: null,
tags: ['Bocagrande', 'beachfront', 'quiet'], pitch: 'On the Bocagrande sand — wake up at the water; a 15-min tuktuk from Getsemaní.', url: 'https://www.tripadvisor.com' }
],
lunch: [
{ id: 'C1', name: 'La Casona de San Diego', at: [10.3940, -75.5545], price: 18, dur: 75,
tags: ['caribbean', 'getsemaní', 'cash only'], pitch: 'Sancocho and mango con arepa in a whitewashed casona — the neighbourhoods own lunch.',
url: 'https://www.tripadvisor.com', img: ['🍲', 'La Casona', 'linear-gradient(135deg,#3c8f6b,#1f5c44)'] },
{ id: 'C2', name: 'La Loma de San Diego', at: [10.4590, -75.5530], price: 25, dur: 75,
tags: ['old town', 'arroz con coco', 'institution'], pitch: 'The old-town classic — big patio table, kitchen as theatre, go at 12:15.',
url: 'https://www.tripadvisor.com', img: ['🥘', 'La Loma', 'linear-gradient(135deg,#c93c3c,#8a1f1f)'] },
{ id: 'C3', name: 'Club El Boat', at: [10.4355, -75.5375], price: 20, dur: 90,
tags: ['bocagrande', 'seafood', 'sunset'], pitch: 'Langosta on the malecón — the sunset is the side order.',
url: 'https://www.tripadvisor.com', img: ['🦐', 'El Boat', 'linear-gradient(135deg,#3c6b8f,#1f445c)'] },
{ id: 'C4', name: 'La Terraza del Muelle', at: [11.2375, -74.2140], price: 20, dur: 75,
tags: ['santa marta', 'harbour', 'catch of the day'], pitch: 'Grilled fish over the working pier — the boats set the menu.',
url: 'https://www.tripadvisor.com', img: ['🐟', 'Terraza del Muelle', 'linear-gradient(135deg,#3c6b8f,#1f445c)'] }
],
visit: [
{ id: 'V1', name: 'Playa Bocagrande', at: [10.4330, -75.5340], price: 0, dur: 90,
tags: ['beach', 'free', 'swim'], pitch: 'The city beach — a proper swim and a hammock, ten minutes from the old town by tuktuk.',
url: 'https://en.wikipedia.org/wiki/Bocagrande', img: ['🏖️', 'Bocagrande', 'linear-gradient(135deg,#3cb5c9,#1f7a8a)'] },
{ id: 'V2', name: 'Plaza Peñol', at: [10.4632, -75.5570], price: 0, dur: 30,
tags: ['free', 'old town', 'shades'], pitch: 'The old towns leafy pause — the black umbrellas and the shade are the whole point.',
url: 'https://en.wikipedia.org/wiki/Cartagena', img: ['☂️', 'Plaza Peñol', 'linear-gradient(135deg,#4f7a4f,#2c4f2c)'] },
{ id: 'V3', name: 'Iglesia de la Popa', at: [10.4253, -75.5530], price: 10, dur: 30,
tags: ['view', 'hilltop', 'sunset'], pitch: 'The church itself in 15 min — the terrace does the rest.',
url: 'https://en.wikipedia.org/wiki/Cerro_de_La_Popa', img: ['⛪', 'Iglesia de la Popa', 'linear-gradient(135deg,#7a7a8f,#4f4f61)'] },
{ id: 'V4', name: 'Playa Cocal, Tayrona', at: [11.1650, -74.1620], price: 0, dur: 60,
tags: ['tayrona', 'beach', 'jungle'], pitch: 'The quieter of the two main Tayrona beaches — the jungle wall is closer to the sand here.',
url: 'https://parquetayrona.gov.co', img: ['🌴', 'Playa Cocal', 'linear-gradient(135deg,#4f9e63,#2c6b40)'] },
{ id: 'V5', name: 'Cerro de La Popa (Santa Marta)', at: [11.2460, -74.2020], price: 2, dur: 60,
tags: ['view', 'santa marta', 'sunset'], pitch: 'The hilltop over Santa Marta — city, gulf and the Sierra Nevada in one frame.',
url: 'https://en.wikipedia.org/wiki/Santa_Marta', img: ['🌄', 'Cerro La Popa', 'linear-gradient(135deg,#c95c3c,#8a3a1f)'] }
]
},
nudges: {
D1: [
{ id: 'N1', title: 'Getsemaní is best before 10:00',
body: 'The street-art lanes fill with tour groups by late morning. Your 09:30 start is the right call — keep it.',
fix: { label: 'Keep as is' } }
],
D3: [
{ id: 'N2', title: 'Two forts in a day is a lot of stairs',
body: 'Castillo San Diego runs <b>~75 min</b> for the terrace-and-bastion loop <i>(web)</b>; 90 min if you take the rampart walk. As is, youll be tight but fine.',
fix: { label: 'Extend to ~90 min', stop: 'S14', dur: 90,
msg: 'San Diego extended to 90 min — the malecón stroll slides a little later; the day still fits.' } }
],
D7: [
{ id: 'N3', title: 'Tayrona: water, sunscreen, and the 15:30 boats',
body: 'The Cangrejos → Cabo San Juan stretch is <b>2.5 km of beach, no shade and no vendors</b>. The last boats out run 15:30 — your 14:00 turnaround is correct.',
fix: { label: 'Keep as is' } }
],
D4: [], D5: [], D6: [], D8: []
},
multimodal: [
{ into: 'S22', dur: 30, sub: [ { mode: 'car', dur: 25 } ] }, // taxi Getsemaní → CTG airport
{ into: 'S23', dur: 25, sub: [ { mode: 'car', dur: 20 } ] }, // taxi SMR → hotel
{ into: 'S31', dur: 40, sub: [ { mode: 'bus', dur: 35 } ] }, // bus Centro → Tayrona
{ into: 'S35', dur: 25, sub: [ { mode: 'car', dur: 20 } ] } // taxi hotel → SMR
],
vibes: [
{ id: 'v1', label: 'forts & old town' },
{ id: 'v2', label: 'Caribbean beaches & malecón' },
{ id: 'v3', label: 'street art (Getsemaní)' },
{ id: 'v4', label: 'Tayrona day trip' },
{ id: 'v5', label: 'Caribbean food' }
],
suggestions: [
{ stop: 'S2', pitch: 'A hundred walls of murals between narrow lanes — start at Plaza de los Coches and wander to the market.',
enrich: 'free · best before 10:00 · ~60 min' },
{ stop: 'S13', pitch: 'Five levels of ramparts, tunnels and cannon lines — go at opening (08:30) for the cold tunnels and no tour buses.',
enrich: 'open 08:30 · ~$15 · 90120 min' },
{ stop: 'S11', pitch: 'The best single view in Cartagena — be on the terrace 45 min before sunset.',
enrich: 'open 09:0018:00 · ~$10' },
{ stop: 'S33', pitch: 'Sand underfoot, jungle walls, the headland where the Caribbean hits the Sierra Nevada. Last boats out 15:30.',
enrich: 'park 08:0016:00 · entry ~$15 · 2.5 km beach walk' }
]
};
// ---------------- trip registry (multi-trip) ----------------
// Each trip is a full dataset. The app keeps per-trip state in
// localStorage (see app.js), so several trips can be worked on in parallel.
window.TRIPS = {
colombia: Object.assign({}, COLOMBIA),
boston: Object.assign({ id: 'boston' }, BOSTON),
florence: Object.assign({ id: 'florence' }, FLORENCE),
};
// ?trip= (legacy: ?city=) picks the starting trip; default: the one you're working on
const _city = ((new URLSearchParams(location.search).get('trip'))
|| (new URLSearchParams(location.search).get('city'))
|| 'colombia').toLowerCase();
window.MOCK = window.TRIPS[_city] || window.TRIPS.colombia;
window.MOCK_CITY = window.MOCK.id;