/* Lodestay — one guest, one city, one set of records.
   Every screen reads this file. Nothing is written twice: a place a trip names
   is the same object the search screen renders, and a figure that claims to
   come from a booking is computed from these numbers by metrics.js — never
   typed to look plausible.

   The clock is fixed so the derived flags are reproducible: Sunday 23 August
   2026, 18:40, the guest's own time. Everything dated relative to it below
   says why it is where it is. */

const LODESTAY_NOW = new Date(2026, 7, 23, 18, 40).getTime();
const at = (y, m, d, h, min) => new Date(y, m - 1, d, h || 0, min || 0).getTime();

const LODESTAY_PRODUCT = { name: "Lodestay", city: "Lisbon", country: "Portugal", center: [38.7185, -9.1520], zoom: 13 };

/* The guest. One account, and the only person this product speaks to. */
const LODESTAY_USER = {
  id: "g_iris",
  name: "Iris Vance",
  email: "iris@vance.studio",
  phone: "+44 7700 900 118",
  avatar: "../../assets/img/avatar-80-03.jpg",
  joinedAt: at(2023, 4, 18),
  homeCity: "Bristol",
  /* Nothing counts trips here — the trips screen counts the bookings array. */
};

const PAYMENT_METHODS = [
  { id: "pm_visa", brand: "Visa", last4: "4417", expiry: "04/28", isDefault: true },
  { id: "pm_mc", brand: "Mastercard", last4: "9080", expiry: "07/26", isDefault: false },
];

const HOSTS = [
  { id: "h_teresa", name: "Teresa Bandeira", avatar: "../../assets/img/avatar-80-01.jpg", joinedAt: at(2019, 6, 1), replyMinutes: 40, languages: ["Portuguese", "English", "French"], lives: "Alfama" },
  { id: "h_mateus", name: "Mateus Rocha", avatar: "../../assets/img/avatar-80-02.jpg", joinedAt: at(2021, 3, 12), replyMinutes: 180, languages: ["Portuguese", "English"], lives: "Alcântara" },
  { id: "h_lena", name: "Lena Aalto", avatar: "../../assets/img/avatar-80-04.jpg", joinedAt: at(2017, 9, 30), replyMinutes: 20, languages: ["English", "Finnish", "Portuguese"], lives: "Graça" },
  { id: "h_duarte", name: "Duarte Salgado", avatar: "../../assets/img/avatar-80-05.jpg", joinedAt: at(2022, 5, 4), replyMinutes: 360, languages: ["Portuguese", "Spanish"], lives: "Bairro Alto" },
  { id: "h_ines", name: "Inês Prado", avatar: "../../assets/img/avatar-80-06.jpg", joinedAt: at(2015, 2, 20), replyMinutes: 90, languages: ["Portuguese", "English", "German"], lives: "Belém" },
];

/* A review is one guest's rating and one sentence. The star figure on every
   screen is the mean of the array a place carries — a place with fewer than
   STAY_POLICY.ratingMinReviews of them prints no average at all. */
const rv = (id, name, daysAgo, rating, body) => ({ id: id, name: name, at: LODESTAY_NOW - daysAgo * 864e5, rating: rating, body: body });

const LISTINGS = [
  {
    id: "l_alfama", name: "Tiled terrace above Alfama", kind: "apartment", neighbourhood: "alfama",
    lat: 38.7122, lng: -9.1295, sleeps: 4, bedrooms: 1, beds: 2, baths: 1, minNights: 2,
    nightlyCents: 16500, cleaningCents: 4500, bookingMode: "instant", cancelPolicy: "moderate",
    availability: "open", hostId: "h_teresa", listedAt: at(2021, 5, 4), stairs: 62,
    amenities: ["wifi", "kitchen", "terrace", "view", "aircon", "washer"],
    blurb: "One floor of a 19th-century building, with the terrace that gives it its name — azulejo tiles, two chairs, and the container ships going past below.",
    photos: ["Terrace, morning", "Living room", "Kitchen and hall"],
    reviews: [
      rv("r_a1", "Salomé", 19, 5, "The terrace is the flat. We ate every meal out there."),
      rv("r_a2", "Peter", 47, 5, "Teresa left a map with the tram stops marked. Small thing, used it daily."),
      rv("r_a3", "Yuna", 76, 4, "Steep walk up from Baixa, worth it. Church bells at eight if you sleep light."),
      rv("r_a4", "Marc", 138, 5, "Cool in August, which in Lisbon is not nothing."),
    ],
  },
  {
    id: "l_graca", name: "Attic with the whole city in the window", kind: "studio", neighbourhood: "graca",
    lat: 38.7181, lng: -9.1287, sleeps: 2, bedrooms: 1, beds: 1, baths: 1, minNights: 3,
    nightlyCents: 12000, cleaningCents: 3000, bookingMode: "request", cancelPolicy: "flexible",
    availability: "open", hostId: "h_lena", listedAt: at(2019, 11, 2), stairs: 84,
    amenities: ["wifi", "desk", "kitchen", "view"],
    blurb: "Top of the house, one room, one very large window. Lena works from the floor below and answers in minutes.",
    photos: ["The window", "Bed and desk", "Stairwell"],
    reviews: [
      rv("r_g1", "Hedda", 12, 5, "Woke up to the river twice and thought about moving here."),
      rv("r_g2", "Tomás", 33, 4, "Eighty-four stairs is the honest number. The view pays for them."),
      rv("r_g3", "Ali", 65, 5, "Best desk I have worked from in a year."),
    ],
  },
  {
    id: "l_principe", name: "Green door on Príncipe Real", kind: "entire_home", neighbourhood: "principe_real",
    lat: 38.7169, lng: -9.1481, sleeps: 6, bedrooms: 3, beds: 4, baths: 2, minNights: 4,
    nightlyCents: 24500, cleaningCents: 7000, bookingMode: "instant", cancelPolicy: "firm",
    availability: "open", hostId: "h_ines", listedAt: at(2017, 7, 19), stairs: 18,
    amenities: ["wifi", "kitchen", "washer", "aircon", "lift", "parking", "desk"],
    blurb: "A whole house behind a green door, two minutes from the garden and the Sunday market. Room for six without anybody sleeping in a corridor.",
    photos: ["Front door", "Long table", "First-floor bedroom"],
    reviews: [
      rv("r_p1", "The Ferrans", 109, 5, "Six of us, nobody argued about rooms. That is the review."),
      rv("r_p2", "Jonna", 152, 5, "Inês met us at the door with the keys and a bottle of something local."),
      rv("r_p3", "Rob", 201, 4, "Firm cancellation policy, so book it when you are sure. Otherwise faultless."),
    ],
  },
  {
    id: "l_baixa", name: "Quiet floor over Rua dos Correeiros", kind: "apartment", neighbourhood: "baixa",
    lat: 38.7101, lng: -9.1385, sleeps: 3, bedrooms: 1, beds: 2, baths: 1, minNights: 2,
    nightlyCents: 13800, cleaningCents: 3500, bookingMode: "instant", cancelPolicy: "moderate",
    /* Somebody else is at the checkout screen with these dates. The hold runs
       out eleven minutes from the fixed clock, which is what makes the
       disabled Reserve button on this place worth looking at. */
    availability: "held", holdUntil: LODESTAY_NOW + 11 * 60e3,
    hostId: "h_mateus", listedAt: at(2022, 2, 8), stairs: 24,
    amenities: ["wifi", "kitchen", "aircon", "lift", "desk"],
    blurb: "Double glazing on a pedestrian street: the Baixa outside, and none of it audible after ten.",
    photos: ["Street below", "Bedroom", "Bathroom"],
    reviews: [
      rv("r_b1", "Cleo", 8, 5, "Genuinely quiet in the middle of everything."),
      rv("r_b2", "Han", 28, 4, "Lift works, which matters here."),
      rv("r_b3", "Ferran", 54, 5, "Ten minutes from the ferry, twelve from the castle."),
    ],
  },
  {
    id: "l_bairro", name: "Two windows onto Bairro Alto", kind: "private_room", neighbourhood: "bairro_alto",
    lat: 38.7128, lng: -9.1442, sleeps: 2, bedrooms: 1, beds: 1, baths: 1, minNights: 1,
    nightlyCents: 7200, cleaningCents: 2000, bookingMode: "request", cancelPolicy: "flexible",
    availability: "open", hostId: "h_duarte", listedAt: at(2023, 1, 15), stairs: 40,
    amenities: ["wifi", "desk", "kitchen", "pets"],
    blurb: "A room in Duarte's flat, with the kitchen shared and the front door on the loudest street in the city. Bring the earplugs he leaves on the shelf.",
    photos: ["The room", "Shared kitchen", "The street at night"],
    reviews: [
      rv("r_ba1", "Ivo", 5, 4, "Loud until three at weekends, exactly as the listing says."),
      rv("r_ba2", "Sasha", 41, 5, "Duarte cooked for us on the second night."),
      rv("r_ba3", "Nell", 88, 4, "Cheapest thing I found this central."),
    ],
  },
  {
    id: "l_estrela", name: "Garden flat by the basilica", kind: "apartment", neighbourhood: "estrela",
    lat: 38.7139, lng: -9.1601, sleeps: 4, bedrooms: 2, beds: 2, baths: 1, minNights: 2,
    nightlyCents: 15200, cleaningCents: 4000, bookingMode: "instant", cancelPolicy: "moderate",
    availability: "open", hostId: "h_teresa", listedAt: at(2020, 8, 26), stairs: 0,
    amenities: ["wifi", "kitchen", "washer", "terrace", "pets", "desk"],
    blurb: "Ground floor with its own patch of garden, opposite the basilica and the park with the ducks.",
    photos: ["Garden", "Kitchen table", "Bedroom"],
    reviews: [
      rv("r_e1", "Ana", 15, 5, "The garden in the morning with coffee. We booked again for spring."),
      rv("r_e2", "Milo", 58, 5, "No stairs, which we needed with the pram."),
      rv("r_e3", "Greta", 94, 4, "Tram 28 rattles past. Charming, then less charming."),
    ],
  },
  {
    id: "l_alcantara", name: "Loft under the bridge", kind: "entire_home", neighbourhood: "alcantara",
    lat: 38.7048, lng: -9.1762, sleeps: 5, bedrooms: 2, beds: 3, baths: 2, minNights: 3,
    nightlyCents: 19800, cleaningCents: 5500, bookingMode: "request", cancelPolicy: "moderate",
    availability: "open", hostId: "h_mateus", listedAt: at(2021, 10, 6), stairs: 12,
    amenities: ["wifi", "desk", "kitchen", "washer", "aircon", "parking", "view"],
    blurb: "A converted print works with the 25 de Abril overhead. You hear the traffic as a hum, and the containers being stacked at six.",
    photos: ["Under the bridge", "Print-works ceiling", "Mezzanine"],
    reviews: [
      rv("r_al1", "Bea", 22, 5, "Enormous. We were five and it felt generous."),
      rv("r_al2", "Kostas", 61, 4, "The bridge is a presence. I liked it; my brother did not."),
      rv("r_al3", "Fen", 120, 5, "Mateus arranged the parking, which saved the trip."),
    ],
  },
  {
    id: "l_belem", name: "Ground floor with a lemon tree", kind: "apartment", neighbourhood: "belem",
    lat: 38.6981, lng: -9.2027, sleeps: 3, bedrooms: 1, beds: 2, baths: 1, minNights: 2,
    nightlyCents: 11800, cleaningCents: 3000, bookingMode: "instant", cancelPolicy: "flexible",
    availability: "open", hostId: "h_ines", listedAt: at(2018, 4, 11), stairs: 0,
    amenities: ["wifi", "kitchen", "terrace", "washer", "parking"],
    blurb: "Out west, past the monastery, where the city stops shouting. The tree in the yard is older than the flat.",
    photos: ["The lemon tree", "Living room", "Kitchen"],
    reviews: [
      rv("r_be1", "Iris", 7, 5, "Slept better here than anywhere in the middle of town."),
      rv("r_be2", "Pol", 36, 4, "Far from the nightlife, near the good pastries. We chose right."),
      rv("r_be3", "Tine", 70, 5, "Inês is a very calm host."),
    ],
  },
  {
    id: "l_boat", name: "Sleeping aboard in Doca de Alcântara", kind: "boat", neighbourhood: "alcantara",
    lat: 38.7015, lng: -9.1745, sleeps: 2, bedrooms: 1, beds: 1, baths: 1, minNights: 2,
    nightlyCents: 9500, cleaningCents: 2500, bookingMode: "request", cancelPolicy: "firm",
    availability: "open", hostId: "h_duarte", listedAt: at(2026, 5, 2), stairs: 0,
    amenities: ["wifi", "kitchen", "view"],
    blurb: "A 34-foot sailing boat that does not leave the dock. Water, power and a shower block forty metres up the pontoon.",
    photos: ["From the pontoon", "Cabin", "Galley"],
    /* Two reviews: below the line where a rating means anything, so the row
       prints "New place" instead of an average. */
    reviews: [
      rv("r_bo1", "Ludo", 44, 5, "Woke up rocking. Would do it again."),
      rv("r_bo2", "Mira", 72, 4, "Small, but it is a boat. The shower block is clean."),
    ],
  },
  {
    id: "l_mouraria", name: "Blue kitchen in Mouraria", kind: "studio", neighbourhood: "mouraria",
    lat: 38.7148, lng: -9.1348, sleeps: 2, bedrooms: 1, beds: 1, baths: 1, minNights: 2,
    nightlyCents: 8800, cleaningCents: 2500, bookingMode: "instant", cancelPolicy: "flexible",
    availability: "open", hostId: "h_lena", listedAt: LODESTAY_NOW - 18 * 864e5, stairs: 30,
    amenities: ["wifi", "kitchen", "washer", "desk"],
    blurb: "Lena's second place, opened last month: one room, a blue-tiled kitchen, and the best cheap lunch in the city three doors down.",
    photos: ["Blue kitchen", "Bed", "The street"],
    /* Listed eighteen days ago and nobody has stayed yet. */
    reviews: [],
  },
  {
    id: "l_alfama2", name: "Fado-quiet room off Largo das Portas do Sol", kind: "private_room", neighbourhood: "alfama",
    lat: 38.7127, lng: -9.1312, sleeps: 2, bedrooms: 1, beds: 1, baths: 1, minNights: 2,
    nightlyCents: 6800, cleaningCents: 1800, bookingMode: "request", cancelPolicy: "flexible",
    /* Saved by the guest in June, taken by somebody else for these dates. */
    availability: "booked", hostId: "h_teresa", listedAt: at(2022, 9, 3), stairs: 55,
    amenities: ["wifi", "kitchen", "view"],
    blurb: "Teresa's spare room, two doors from the viewpoint. Fado from the bar below on Fridays, finished by midnight.",
    photos: ["Viewpoint", "The room", "Shared terrace"],
    reviews: [
      rv("r_a21", "Otto", 26, 5, "Cheap, central, and Teresa is the best host in Lisbon."),
      rv("r_a22", "Lise", 63, 4, "Shared bathroom, spotless."),
      rv("r_a23", "Dan", 130, 5, "The viewpoint at seven in the morning, alone. Go."),
    ],
  },
  {
    id: "l_principe2", name: "Painter's studio, Príncipe Real", kind: "studio", neighbourhood: "principe_real",
    lat: 38.7159, lng: -9.1493, sleeps: 2, bedrooms: 1, beds: 1, baths: 1, minNights: 3,
    nightlyCents: 13500, cleaningCents: 3200, bookingMode: "instant", cancelPolicy: "moderate",
    /* The host has stopped taking bookings while the roof is done. */
    availability: "paused", hostId: "h_ines", listedAt: at(2020, 1, 22), stairs: 22,
    amenities: ["wifi", "desk", "kitchen", "aircon"],
    blurb: "North light, a sink big enough to wash brushes in, and a bed behind a curtain.",
    photos: ["North window", "Work table", "The curtain"],
    reviews: [
      rv("r_p21", "Nadia", 51, 5, "Painted for four days and left happy."),
      rv("r_p22", "Guy", 98, 5, "The light is the point."),
      rv("r_p23", "Ute", 143, 4, "Bed is behind a curtain, not a wall. Fine for one."),
    ],
  },
];

/* What the guest is searching for, and what every figure on the search screen
   is scoped to. The scope bar states it in words; the filters below it narrow
   the list without moving it.

   Three sets of dates, because the length of the stay is what makes a place
   answer differently: two nights is under the minimum at four of these places,
   and the disabled Reserve button says so. */
const SEARCH_DATE_OPTIONS = [
  { value: "oct", checkIn: at(2026, 10, 9), checkOut: at(2026, 10, 14) },
  { value: "nov", checkIn: at(2026, 11, 6), checkOut: at(2026, 11, 10) },
  { value: "sep", checkIn: at(2026, 9, 28), checkOut: at(2026, 9, 30) },
];
const SEARCH_SCOPE = { dates: "oct", checkIn: SEARCH_DATE_OPTIONS[0].checkIn, checkOut: SEARCH_DATE_OPTIONS[0].checkOut, guests: 2 };

/* Saved places, in the guest's own two lists. Two of them cannot be booked for
   the dates in the scope — which is the whole reason this screen exists. */
const COLLECTIONS = [
  { id: "sv_october", name: "Lisbon, October", savedAt: at(2026, 6, 14), listingIds: ["l_alfama", "l_principe", "l_alfama2", "l_principe2", "l_graca"] },
  { id: "sv_someday", name: "Someday", savedAt: at(2025, 11, 2), listingIds: ["l_boat", "l_alcantara"] },
];

/* ── Bookings ──────────────────────────────────────────────────────────────
   nightlyCents and cleaningCents are stored on the booking, not read from the
   place: they are the price that was agreed, and the host may change the
   listing tomorrow. Everything else about the money — fees, tax, total,
   deposit, balance, refund — is computed by metrics.js from these two numbers
   and the dates, in one function, so the checkout preview, the trip screen and
   the payments table cannot disagree. */

const money = (listingId, checkIn, checkOut, guests) => {
  const listing = LISTINGS.find((l) => l.id === listingId);
  return quoteFor({ nightlyCents: listing.nightlyCents, cleaningCents: listing.cleaningCents, checkIn: checkIn, checkOut: checkOut, guests: guests });
};

/* The rows come from metrics.js paymentPlan — the same function the checkout
   screen prints before a booking exists — so a stay's payments and the
   schedule the guest was shown cannot disagree. This adds only what happened
   to each movement afterwards. */
function paymentsFor(spec) {
  const q = money(spec.listingId, spec.checkIn, spec.checkOut, spec.guests);
  return paymentPlan({ total: q.total, checkIn: spec.checkIn, bookedAt: spec.bookedAt }).map((row, i) => {
    const failed = row.kind === "deposit" && spec.depositStatus === "failed";
    /* A stay that has happened has been paid for: its balance came off the card
       on the day it fell due, which is what the payments table shows. */
    const settled = spec.status === "completed" || spec.status === "cancelled";
    const status = failed ? "failed" : row.kind === "balance" && !settled ? "scheduled" : "paid";
    return {
      id: spec.id + "_p" + (i + 1), kind: row.kind, amountCents: row.amountCents, status: status,
      method: spec.method, dueAt: row.dueAt, paidAt: status === "paid" ? row.dueAt : null,
      failedReason: failed ? "The card was declined by the bank." : undefined,
    };
  });
}

const BOOKING_SPECS = [
  /* Balance falls due on 29 August, six days from the clock — inside the
     "due soon" window, so this trip needs the guest. */
  { id: "b_estrela", listingId: "l_estrela", status: "confirmed", checkIn: at(2026, 9, 12, 15), checkOut: at(2026, 9, 17, 11), guests: 2, bookedAt: at(2026, 8, 2, 9, 20), purpose: "holiday", method: "pm_visa", arrivalPlan: null, hostNote: "Iris — the balance comes off the Visa on the 29th, nothing for you to do unless it fails. Teresa" },
  /* Check-in tomorrow and the host has asked twice what time they arrive. */
  { id: "b_graca", listingId: "l_graca", status: "confirmed", checkIn: at(2026, 8, 24, 15), checkOut: at(2026, 8, 27, 11), guests: 1, bookedAt: at(2026, 8, 10, 21, 5), purpose: "work", method: "pm_visa", arrivalPlan: null },
  /* Deposit declined three days ago; the stay is still confirmed and the host
     has not been told. Money first: this is the reason the row prints. */
  { id: "b_bairro", listingId: "l_bairro", status: "confirmed", checkIn: at(2026, 9, 28, 15), checkOut: at(2026, 10, 2, 11), guests: 1, bookedAt: at(2026, 8, 20, 11, 40), purpose: "work", method: "pm_mc", depositStatus: "failed", arrivalPlan: null },
  /* Nothing needed: deposit paid, balance not due until December. */
  { id: "b_mouraria", listingId: "l_mouraria", status: "confirmed", checkIn: at(2026, 12, 19, 16), checkOut: at(2026, 12, 26, 11), guests: 2, bookedAt: at(2026, 8, 5, 8, 15), purpose: "family", method: "pm_visa", arrivalPlan: "Landing at 14:10, at the door around 16:00." },
  /* With the host. Mateus has until 20:15 tonight — 24 hours from the ask. */
  { id: "b_alcantara", listingId: "l_alcantara", status: "requested", checkIn: at(2026, 11, 6, 15), checkOut: at(2026, 11, 10, 11), guests: 4, bookedAt: at(2026, 8, 22, 20, 15), purpose: "holiday", method: "pm_visa", noPayments: true, arrivalPlan: null },
  /* Stayed a week ago, review window closes on 30 August. */
  { id: "b_belem", listingId: "l_belem", status: "completed", checkIn: at(2026, 8, 12, 15), checkOut: at(2026, 8, 16, 11), guests: 2, bookedAt: at(2026, 7, 28, 19, 0), purpose: "holiday", method: "pm_visa", arrivalPlan: "Train from Santa Apolónia, at the door by 17:00." },
  /* Stayed in May and reviewed it. */
  { id: "b_principe", listingId: "l_principe", status: "completed", checkIn: at(2026, 5, 1, 15), checkOut: at(2026, 5, 6, 11), guests: 4, bookedAt: at(2026, 2, 9, 13, 30), purpose: "family", method: "pm_visa", arrivalPlan: "Six of us, two cars, arriving late afternoon.", review: { rating: 5, at: at(2026, 5, 8, 20, 12), body: "Three bedrooms and a table everybody could sit at. Inês thought of everything, including the coffee." } },
  /* The host said no, in July. Nothing was ever taken. */
  { id: "b_boat", listingId: "l_boat", status: "declined", checkIn: at(2026, 7, 20, 16), checkOut: at(2026, 7, 22, 10), guests: 2, bookedAt: at(2026, 7, 3, 10, 45), purpose: "holiday", method: "pm_visa", noPayments: true, closedAt: at(2026, 7, 4, 8, 30), closedNote: "The boat was out of the water that week for antifouling. Try me in September." },
  /* The guest called it off in May, nine days out and inside the flexible
     window, so all of it came back. */
  { id: "b_alfama2", listingId: "l_alfama2", status: "cancelled", checkIn: at(2026, 6, 6, 15), checkOut: at(2026, 6, 9, 11), guests: 2, bookedAt: at(2026, 5, 12, 17, 25), purpose: "work", method: "pm_visa", refunded: true, closedAt: at(2026, 5, 28, 9, 10), cancelReason: "dates_changed" },
];

const BOOKINGS = BOOKING_SPECS.map((spec) => {
  const listing = LISTINGS.find((l) => l.id === spec.listingId);
  let payments = spec.noPayments ? [] : paymentsFor(spec);
  if (spec.refunded) {
    /* Cancelled inside the flexible window, so everything that had been taken
       came back in one movement. */
    payments = payments.concat([{ id: spec.id + "_refund", kind: "refund", amountCents: payments.reduce((sum, p) => sum + p.amountCents, 0), status: "refunded", method: spec.method, paidAt: spec.closedAt, dueAt: spec.closedAt }]);
  }
  return {
    id: spec.id,
    reference: "LS-" + spec.id.slice(2, 5).toUpperCase() + "-" + new Date(spec.checkIn).getFullYear(),
    listingId: spec.listingId,
    hostId: listing.hostId,
    status: spec.status,
    checkIn: spec.checkIn,
    checkOut: spec.checkOut,
    guests: spec.guests,
    purpose: spec.purpose,
    bookedAt: spec.bookedAt,
    /* The price that was agreed, not the price the place asks today. */
    nightlyCents: listing.nightlyCents,
    cleaningCents: listing.cleaningCents,
    cancelPolicy: listing.cancelPolicy,
    arrivalPlan: spec.arrivalPlan || null,
    hostNote: spec.hostNote || null,
    closedAt: spec.closedAt || null,
    closedNote: spec.closedNote || null,
    cancelReason: spec.cancelReason || null,
    review: spec.review || null,
    /* A request the host has not answered: 24 hours from the ask. */
    respondBy: spec.status === "requested" ? spec.bookedAt + 864e5 : null,
    payments: payments,
  };
});

/* ── Conversations ─────────────────────────────────────────────────────────
   Authored against the bookings above: every message agrees with its stay's
   status and its timeline at the moment it was sent. Two threads carry an
   unread message from the host, which is the number in the nav badge. */
const THREADS = [
  {
    id: "t_graca", hostId: "h_lena", bookingId: "b_graca", listingId: "l_graca",
    messages: [
      { id: "m1", from: "guest", at: at(2026, 8, 10, 21, 8), body: "Booked for the 24th — coming for work, so I will be in and out early. Is the desk chair adjustable?", read: true },
      { id: "m2", from: "host", at: at(2026, 8, 10, 21, 26), body: "It is, and there is a second monitor in the cupboard if you want it. Just say.", read: true },
      { id: "m3", from: "guest", at: at(2026, 8, 22, 9, 2), body: "Monitor please, that would help a lot. See you Monday.", read: true },
      { id: "m4", from: "host", at: at(2026, 8, 23, 11, 15), body: "Monitor is out and set up. What time do you think you will get here? I am out between four and six, so either before or after — I will leave the key with the café downstairs if it lands in the middle.", read: false },
    ],
  },
  {
    id: "t_estrela", hostId: "h_teresa", bookingId: "b_estrela", listingId: "l_estrela",
    messages: [
      { id: "m1", from: "guest", at: at(2026, 8, 2, 9, 34), body: "Booked the garden flat for September — is the tram noise as loud as the reviews say?", read: true },
      { id: "m2", from: "host", at: at(2026, 8, 2, 10, 12), body: "Honest answer: you hear the 28 in the front room until about eleven. The bedrooms are at the back and you hear nothing.", read: true },
      { id: "m3", from: "host", at: at(2026, 8, 21, 8, 40), body: "Nothing needed from you, but a heads up — the balance comes off your card on the 29th. The garden gate is being repainted the week before you arrive, so it may smell of paint for a day.", read: false },
    ],
  },
  {
    id: "t_alcantara", hostId: "h_mateus", bookingId: "b_alcantara", listingId: "l_alcantara",
    messages: [
      { id: "m1", from: "guest", at: at(2026, 8, 22, 20, 17), body: "Just sent a request for 6–10 November, four of us. Two are light sleepers — how bad is the six a.m. container noise in the back bedroom?", read: true },
    ],
  },
  {
    id: "t_belem", hostId: "h_ines", bookingId: "b_belem", listingId: "l_belem",
    messages: [
      { id: "m1", from: "guest", at: at(2026, 8, 16, 10, 40), body: "Left the keys in the bowl. Thank you — the lemon tree was the best part of the week.", read: true },
      { id: "m2", from: "host", at: at(2026, 8, 16, 12, 4), body: "Anytime, Iris. I have put you on the list for the spring rate if you come back.", read: true },
    ],
  },
  {
    /* An enquiry with no booking behind it: the guest asked about the house in
       Príncipe Real and has not booked it. Threads are not routes; this one
       lives in the inbox and nowhere else. */
    id: "t_principe", hostId: "h_ines", bookingId: null, listingId: "l_principe",
    messages: [
      { id: "m1", from: "guest", at: at(2026, 8, 19, 18, 22), body: "Would the green door work for six adults in October, or is the third bedroom really a study?", read: true },
      { id: "m2", from: "host", at: at(2026, 8, 19, 19, 50), body: "Six adults is fine — the third room has a proper double, not a sofa. October is open apart from the last weekend.", read: true },
    ],
  },
];

/* What the bell opens. Every one points at a route that exists and a record
   that agrees with it. */
const NOTIFICATIONS = [
  { id: "n1", at: at(2026, 8, 23, 11, 15), title: "Lena asked what time you arrive", body: "Check-in is tomorrow at the attic in Graça.", route: "/inbox" },
  { id: "n2", at: at(2026, 8, 22, 20, 15), title: "Request sent to Mateus", body: "He has until 20:15 tonight to answer the loft under the bridge.", route: "/trips/b_alcantara" },
  { id: "n3", at: at(2026, 8, 21, 8, 40), title: "Teresa sent a note about the garden flat", body: "The balance comes off your card on 29 August.", route: "/trips/b_estrela" },
  { id: "n4", at: at(2026, 8, 20, 11, 42), title: "A payment did not go through", body: "The Mastercard was declined for the deposit on the room in Bairro Alto.", route: "/trips/b_bairro" },
];

/* Password reset links. The token is in the URL; whether the screen behind it
   is the form or the dead-link state is derived from this expiry against the
   clock, never from a stored flag. One live link asked for eighteen minutes
   ago, one from three days back that nobody used. */
const RESET_LINKS = [
  { token: "8f2c41", email: LODESTAY_USER.email, requestedAt: LODESTAY_NOW - 18 * 60000, expiresAt: LODESTAY_NOW - 18 * 60000 + AUTH_POLICY.resetLinkMinutes * 60000 },
  { token: "1a90ee", email: LODESTAY_USER.email, requestedAt: at(2026, 8, 20, 9, 5), expiresAt: at(2026, 8, 20, 9, 5) + AUTH_POLICY.resetLinkMinutes * 60000 },
];

Object.assign(window, {
  LODESTAY_NOW, LODESTAY_PRODUCT, LODESTAY_USER, PAYMENT_METHODS, RESET_LINKS,
  HOSTS, LISTINGS, SEARCH_DATE_OPTIONS, SEARCH_SCOPE, COLLECTIONS, BOOKINGS, THREADS, NOTIFICATIONS,
});
