// ═══════════════════════════════════════════════════════════════════════
// Dyra Mobile — My Stays, Wishlist, List Your Home, About, Help
// ═══════════════════════════════════════════════════════════════════════

const PM3 = DYRA_MOBILE_PALETTE;
const TM3 = DYRA_MOBILE_TYPE;

// ═══════════════════════════════════════════════════════════════════════
// MY STAYS — guest dashboard
// ═══════════════════════════════════════════════════════════════════════
//
// Reads the magic-link token from window.location.hash (".../#stays?token=…")
// and fetches the booking from /api/guest/booking. The token is issued at
// payment confirmation time by the Stripe webhook and emailed to the guest.
//
// First-found tokens are persisted to sessionStorage (per-tab, cleared on tab
// close) so the token survives in-app navigation that rewrites the hash —
// e.g. tapping the menu's "Browse" then coming back to "My stays" via the
// drawer would otherwise drop ?token= from the URL.
//
// No token → empty state + an email-lookup form. The form posts to
// /api/guest/recover-link, which always returns ok:true (privacy-safe — never
// reveals whether the email is a guest) and emails a fresh magic link if there
// is a matching booking.
const M_GUEST_TOKEN_KEY = 'dyra_guest_token';

function readGuestToken() {
  try {
    const h = window.location.hash || '';
    const qi = h.indexOf('?');
    if (qi >= 0) {
      const sp = new URLSearchParams(h.slice(qi+1));
      const fromHash = sp.get('token');
      if (fromHash) {
        try { sessionStorage.setItem(M_GUEST_TOKEN_KEY, fromHash); } catch (_) {}
        return fromHash;
      }
    }
  } catch (e) {}
  try {
    return sessionStorage.getItem(M_GUEST_TOKEN_KEY) || '';
  } catch (_) {
    return '';
  }
}

function clearGuestToken() {
  try { sessionStorage.removeItem(M_GUEST_TOKEN_KEY); } catch (_) {}
}

function fmtMyStaysDate(iso) {
  if (!iso) return '';
  try {
    return new Date(iso + 'T00:00:00').toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
  } catch (e) { return iso; }
}

function fmtMoneyCentsClient(cents) {
  const dollars = Math.round((cents || 0) / 100);
  return `$${dollars.toLocaleString('en-US')}`;
}

// Compute the UTC ms timestamp of a local check-in time in a given IANA tz.
// Used by the countdown so the days/hours display tracks the property's
// actual check-in window, not the demo placeholder.
function checkInUtcMs(checkInISO, checkInTime, tz) {
  if (!checkInISO) return NaN;
  const ymd = String(checkInISO).slice(0, 10);
  const y = Number(ymd.slice(0, 4));
  const mo = Number(ymd.slice(5, 7));
  const d = Number(ymd.slice(8, 10));
  if (!y || !mo || !d) return NaN;
  let hh = 15, mm = 0;
  const t = String(checkInTime || '15:00').trim();
  const am = t.match(/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)?$/i);
  if (am) {
    hh = parseInt(am[1], 10) || 0;
    mm = parseInt(am[2] || '0', 10);
    if (/pm/i.test(am[3] || '') && hh < 12) hh += 12;
    if (/am/i.test(am[3] || '') && hh === 12) hh = 0;
  }
  // Compute the offset for that date in the given tz so the same wall-clock
  // time in winter (EST -5) and summer (EDT -4) both resolve correctly.
  const utcGuess = Date.UTC(y, mo - 1, d, hh, mm);
  const tzName = tz || 'America/New_York';
  let offset = 0;
  try {
    const dtf = new Intl.DateTimeFormat('en-US', {
      timeZone: tzName, hour12: false,
      year: 'numeric', month: '2-digit', day: '2-digit',
      hour: '2-digit', minute: '2-digit', second: '2-digit',
    });
    const parts = dtf.formatToParts(new Date(utcGuess));
    const get = (type) => parseInt((parts.find(p => p.type === type) || {}).value || '0', 10);
    const wallMs = Date.UTC(get('year'), get('month') - 1, get('day'), get('hour'), get('minute'), get('second'));
    offset = wallMs - utcGuess;
  } catch (_) {
    // Fallback: assume EDT.
    offset = -4 * 60 * 60 * 1000;
  }
  return utcGuess - offset;
}

// Generate a v4-ish UUID without depending on crypto.randomUUID (older
// mobile Safari). Used as request_id for cart commits — server enforces
// idempotency by request_id.
function genRequestId() {
  if (typeof crypto !== 'undefined' && crypto.randomUUID) {
    try { return crypto.randomUUID(); } catch (_) {}
  }
  return 'r-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 10);
}

function MStays({ go, openMenu }) {
  const [tab, setTab] = React.useState('upcoming'); // upcoming | past
  const props = window.DyraStore?.state?.properties || window.DYRA?.PROPERTIES || [];
  const [token, setToken] = React.useState(readGuestToken());
  const [booking, setBooking] = React.useState(null);
  const [loading, setLoading] = React.useState(!!token);
  const [loadErr, setLoadErr] = React.useState('');

  // Email-recovery form (shown when there's no token in the URL).
  const [recoverEmail, setRecoverEmail] = React.useState('');
  const [recoverState, setRecoverState] = React.useState('idle'); // 'idle' | 'sending' | 'sent'
  const [recoverErr, setRecoverErr] = React.useState('');
  const submitRecover = async (e) => {
    e?.preventDefault();
    const trimmed = recoverEmail.trim();
    if (!trimmed || !/.+@.+\..+/.test(trimmed)) {
      setRecoverErr('Please enter a valid email.');
      return;
    }
    setRecoverErr('');
    setRecoverState('sending');
    try {
      const r = await fetch('/api/guest/recover-link', {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ email: trimmed }),
      });
      // Endpoint always returns ok:true to avoid leaking which emails are
      // guests, so we show the same success state regardless.
      if (!r.ok) throw new Error('Network error');
      setRecoverState('sent');
    } catch (err) {
      setRecoverErr('Could not reach the server. Please try again or WhatsApp 862-520-7797.');
      setRecoverState('idle');
    }
  };

  React.useEffect(() => {
    if (!token) return;
    let cancelled = false;
    (async () => {
      try {
        const r = await fetch('/api/guest/booking?token=' + encodeURIComponent(token));
        const j = await r.json().catch(() => ({}));
        if (cancelled) return;
        if (!r.ok) {
          // Bad / expired token — drop it so the email-recovery form shows.
          clearGuestToken();
          setToken('');
          setBooking(null);
          setLoadErr(j.error || 'This link has expired. Enter your email below for a fresh one.');
          setLoading(false);
          return;
        }
        setBooking(j.booking);
        setLoading(false);
      } catch (e) {
        if (cancelled) return;
        setLoadErr((e && e.message) || 'Network error.');
        setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [token]);

  // Build upcoming/past lists. With a token we have exactly one booking;
  // bucket it by check-out vs today.
  const today = new Date().toISOString().slice(0, 10);
  const upcoming = [];
  const past = [];
  if (booking) {
    const item = {
      id: booking.id,
      propertyId: booking.properties?.slug || null,
      propertyName: booking.properties?.name || 'Your stay',
      checkIn: fmtMyStaysDate(booking.check_in),
      checkOut: fmtMyStaysDate(booking.check_out),
      nights: booking.nights,
      guests: booking.guests_count,
      total: Math.round((booking.total_cents || 0) / 100),
      status: booking.status,
      daysOut: Math.max(0, Math.round((new Date(booking.check_in) - new Date()) / 86400000)),
    };
    if (booking.check_out >= today && booking.status !== 'cancelled') upcoming.push(item);
    else past.push(item);
  }
  const list = tab === 'upcoming' ? upcoming : past;

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
      <MAppBar
        scrolled
        title="My stays"
        leading={<MIconBtn label="Menu" onClick={openMenu}><MIco name="menu" size={22}/></MIconBtn>}
      />

      {/* segmented */}
      <div style={{ padding: '12px 16px 0', display: 'flex', gap: 4 }}>
        {[
          { id: 'upcoming', label: `Upcoming (${upcoming.length})` },
          { id: 'past',     label: `Past (${past.length})` },
        ].map(t => (
          <button key={t.id} onClick={() => setTab(t.id)} style={{
            flex: 1, padding: '10px 0', borderRadius: 10,
            background: tab === t.id ? PM3.ink : 'transparent',
            color: tab === t.id ? PM3.bg : PM3.muted,
            border: tab === t.id ? 'none' : `0.5px solid ${PM3.line}`,
            fontFamily: TM3.sans, fontSize: 13, fontWeight: 500,
            cursor: 'pointer',
          }}>{t.label}</button>
        ))}
      </div>

      <div style={{ flex: 1, overflow: 'auto', padding: '16px' }}>
        {loading ? (
          <div style={{ padding: '60px 24px', textAlign: 'center', color: PM3.muted, fontFamily: TM3.sans }}>
            Loading your stay…
          </div>
        ) : list.length === 0 ? (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {loadErr && (
              <div style={{
                padding: '14px 16px',
                background: '#fbeae5', border: '0.5px solid #e9c4bd', borderRadius: 12,
                fontFamily: TM3.sans, fontSize: 13, color: '#8c2c1f', lineHeight: 1.5,
              }}>{loadErr}</div>
            )}
            {/* Email-recovery form — only when there's no token (guest landed
                here without a magic link, or had a stale one we just cleared). */}
            {!token && (
              recoverState === 'sent' ? (
                <div style={{
                  padding: '40px 24px', textAlign: 'center',
                  background: PM3.surface, borderRadius: 16,
                  border: `0.5px solid ${PM3.lineSoft}`,
                }}>
                  <div style={{ fontFamily: TM3.serif, fontSize: 20, color: PM3.ink, fontWeight: 500 }}>
                    Check your inbox
                  </div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 10, lineHeight: 1.55 }}>
                    If we have a reservation under <strong style={{ color: PM3.ink }}>{recoverEmail.trim()}</strong>, we just sent you a link to your stay.
                  </div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.ink, marginTop: 14, lineHeight: 1.55 }}>
                    Don't see it? <strong>Check your spam folder.</strong>
                  </div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, marginTop: 8, lineHeight: 1.55 }}>
                    Still nothing after 5 minutes? WhatsApp{' '}
                    <a href="https://wa.me/18625207797" style={{ color: PM3.accent }}>862-520-7797</a>.
                  </div>
                </div>
              ) : (
                <form onSubmit={submitRecover} style={{
                  padding: '24px', background: PM3.surface, borderRadius: 16,
                  border: `0.5px solid ${PM3.lineSoft}`,
                }}>
                  <div style={{ fontFamily: TM3.serif, fontSize: 20, color: PM3.ink, fontWeight: 500 }}>
                    Find your reservation
                  </div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 8, lineHeight: 1.55 }}>
                    Enter the email you used at booking. We'll send a link to your stay.
                  </div>
                  <input
                    type="email"
                    inputMode="email"
                    autoComplete="email"
                    required
                    value={recoverEmail}
                    onChange={e => { setRecoverEmail(e.target.value); if (recoverErr) setRecoverErr(''); }}
                    placeholder="you@example.com"
                    disabled={recoverState === 'sending'}
                    style={{
                      width: '100%', boxSizing: 'border-box', marginTop: 16,
                      padding: '12px 14px', fontSize: 15, fontFamily: 'inherit',
                      background: PM3.bg, color: PM3.ink,
                      border: `0.5px solid ${recoverErr ? '#c0392b' : PM3.line}`,
                      borderRadius: 10, outline: 'none',
                    }}
                  />
                  {recoverErr && (
                    <div style={{ fontFamily: TM3.sans, fontSize: 12, color: '#c0392b', marginTop: 8, lineHeight: 1.5 }}>
                      {recoverErr}
                    </div>
                  )}
                  <div style={{ marginTop: 14 }}>
                    {/* MButton's default <button> type is "submit", so the form
                        onSubmit handler fires on click + Enter alike. */}
                    <MButton disabled={recoverState === 'sending'}>
                      {recoverState === 'sending' ? 'Sending…' : 'Email me my access link'}
                    </MButton>
                  </div>
                </form>
              )
            )}

            <div style={{
              padding: '60px 24px', textAlign: 'center',
              background: PM3.surface, borderRadius: 16,
              border: `0.5px solid ${PM3.lineSoft}`,
            }}>
              <div style={{ color: PM3.muted, marginBottom: 12 }}>
                <MIco name="calendar" size={36} color={PM3.muted}/>
              </div>
              <div style={{ fontFamily: TM3.serif, fontSize: 20, color: PM3.ink, fontWeight: 500 }}>{token ? 'No matching stays' : 'No upcoming stays'}</div>
              <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 8 }}>
                {token ? 'Switch tabs above to see past stays.' : 'Browse new ones, or use the form above to recover your access link.'}
              </div>
              <div style={{ marginTop: 20 }}>
                <MButton onClick={() => go('browse')}>Browse stays</MButton>
              </div>
            </div>
          </div>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            {list.map(b => {
              const p = props.find(x => x.id === b.propertyId) || { id: b.propertyId, name: b.propertyName, photos: [] };
              // Preserve the token in the URL so the detail screen can fetch
              // the same booking server-side. go() rewrites the hash entirely
              // and would drop ?token=, so we set the hash directly.
              const navigateToDetail = () => {
                if (token) {
                  window.location.hash = `stay:${b.id}?token=${encodeURIComponent(token)}`;
                } else {
                  go('stay:' + b.id);
                }
              };
              return (
                <button key={b.id} onClick={navigateToDetail} style={{
                  background: PM3.surface, border: `0.5px solid ${PM3.lineSoft}`,
                  borderRadius: 16, overflow: 'hidden', padding: 0,
                  textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
                  WebkitTapHighlightColor: 'transparent',
                }}>
                  <div style={{ position: 'relative' }}>
                    <MPhoto property={p} idx={0} style={{ width: '100%', height: 160 }}/>
                    {b.status === 'confirmed' && (
                      <div style={{
                        position: 'absolute', top: 12, left: 12,
                        background: PM3.success, color: '#fff',
                        fontFamily: TM3.sans, fontSize: 10, fontWeight: 600,
                        letterSpacing: '0.12em', textTransform: 'uppercase',
                        padding: '5px 10px', borderRadius: 100,
                      }}>Confirmed</div>
                    )}
                    {b.daysOut !== undefined && b.daysOut <= 14 && (
                      <div style={{
                        position: 'absolute', bottom: 12, right: 12,
                        background: 'rgba(20,15,10,0.7)', backdropFilter: 'blur(8px)',
                        color: '#fff', fontFamily: TM3.sans, fontSize: 11, fontWeight: 500,
                        padding: '5px 10px', borderRadius: 100,
                      }}>In {b.daysOut} days</div>
                    )}
                  </div>
                  <div style={{ padding: 16 }}>
                    <div style={{ fontSize: 9, letterSpacing: '0.18em', textTransform: 'uppercase', color: PM3.muted }}>{b.id}</div>
                    <div style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, marginTop: 4, lineHeight: 1.2 }}>{p.name}</div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 6 }}>
                      {b.checkIn} – {b.checkOut} · {b.guests} guests
                    </div>
                    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginTop: 12 }}>
                      <span style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.ink, fontWeight: 500 }}>${b.total} total</span>
                      <span style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.accent, fontWeight: 500 }}>View details →</span>
                    </div>
                  </div>
                </button>
              );
            })}
          </div>
        )}
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════
// MY STAY DETAIL — itinerary
// ═══════════════════════════════════════════════════════════════════════
// ─── Cancellation policy — My Stay surface ───────────────────────────
// "As of today" preview. Renders a tiered policy in Manage trip above
// the cancel row; the active tier highlights so the guest sees what
// they get back if they cancel right now. Numbers come from
// window.DyraCancellation (mirror of src/lib/cancellation.ts).
function MStayCancellationPolicy({ booking, palette, type }) {
  const PM = palette, TM = type;
  const [open, setOpen] = React.useState(false);
  if (!booking || !window.DyraCancellation) return null;
  if (String(booking.status || '').toLowerCase() === 'cancelled') {
    return null; // already cancelled — D1CancelBookingCard / cancelMsg covers state
  }
  const policy = window.DyraCancellation.getCancellationPolicy(booking, new Date());
  const fmt = window.DyraCancellation.formatRefundDollars;
  const refundLine = policy.withinGrace
    ? `Cancelling now refunds ${fmt(policy.eligibleRefundCents)} to your card.`
    : 'Cancelling now will not auto-refund any amount. Message the host to discuss exceptions.';
  return (
    <div style={{
      marginTop: 14, padding: 14, background: PM.surface, borderRadius: 12,
      border: `0.5px solid ${PM.lineSoft}`,
    }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
        <span style={{ color: policy.withinGrace ? PM.success : PM.muted, marginTop: 2 }}>
          <MIco name="shield" size={16}/>
        </span>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: TM.sans, fontSize: 11, letterSpacing: '0.14em', textTransform: 'uppercase', color: PM.muted, marginBottom: 6 }}>
            Cancellation policy
          </div>
          <div style={{ fontFamily: TM.sans, fontSize: 13, color: PM.ink, lineHeight: 1.5 }}>
            {policy.summary}
          </div>
          <div style={{
            fontFamily: TM.sans, fontSize: 12.5, marginTop: 8, lineHeight: 1.5,
            color: policy.withinGrace ? PM.success : PM.muted, fontWeight: 500,
          }}>
            {refundLine}
          </div>
          <div style={{ fontFamily: TM.sans, fontSize: 11, color: PM.muted, marginTop: 4 }}>
            {policy.processingNote}
          </div>
          <button
            type="button"
            onClick={() => setOpen(v => !v)}
            style={{
              marginTop: 10, background: 'transparent', border: 'none', padding: 0,
              color: PM.accent, fontFamily: TM.sans, fontSize: 12, fontWeight: 500,
              cursor: 'pointer', textDecoration: 'underline',
            }}
          >{open ? 'Hide full policy' : 'Show full policy'}</button>
          {open && (
            <div style={{ marginTop: 10, paddingTop: 10, borderTop: `0.5px solid ${PM.lineSoft}` }}>
              {policy.tiers.map(tier => (
                <div key={tier.id} style={{
                  display: 'flex', gap: 10, padding: '8px 0',
                  borderBottom: `0.5px solid ${PM.lineSoft}`,
                  opacity: tier.activeNow ? 1 : 0.7,
                }}>
                  <div style={{
                    width: 6, marginTop: 6, height: 6, borderRadius: 3,
                    background: tier.activeNow ? PM.success : PM.line,
                  }}/>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontFamily: TM.sans, fontSize: 12.5, color: PM.ink, fontWeight: tier.activeNow ? 600 : 500 }}>
                      {tier.label}{tier.activeNow ? ' · Current' : ''}
                    </div>
                    <div style={{ fontFamily: TM.sans, fontSize: 12, color: PM.muted, marginTop: 2, lineHeight: 1.45 }}>
                      {tier.refundDescription}
                    </div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Cancel confirmation modal — replaces window.confirm ─────────────
// Shows the exact refund amount the guest is about to receive, sourced
// from the same window.DyraCancellation policy helper. "Yes, cancel" vs
// "Keep my booking".
function MCancelConfirmModal({ open, booking, busy, onClose, onConfirm, palette, type }) {
  const PM = palette, TM = type;
  if (!open || !booking || !window.DyraCancellation) return null;
  const policy = window.DyraCancellation.getCancellationPolicy(booking, new Date());
  const fmt = window.DyraCancellation.formatRefundDollars;
  const refundCopy = policy.withinGrace
    ? `You'll get ${fmt(policy.eligibleRefundCents)} refunded back to your card.`
    : `This booking is past the ${window.DyraCancellation.GRACE_PERIOD_HOURS}-hour free-cancellation window. No automatic refund will be issued.`;
  return (
    <div onClick={busy ? undefined : onClose} style={{
      position: 'absolute', inset: 0, zIndex: 220,
      background: 'rgba(20,15,10,0.55)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      padding: 20,
    }}>
      <div onClick={e => e.stopPropagation()} style={{
        width: '100%', maxWidth: 360, background: PM.bg,
        borderRadius: 16, padding: 20,
        boxShadow: '0 12px 32px rgba(0,0,0,0.22)',
      }}>
        <div style={{ fontFamily: TM.serif, fontSize: 20, color: PM.ink, fontWeight: 500, lineHeight: 1.2 }}>
          Cancel this booking?
        </div>
        <div style={{
          marginTop: 14, padding: 12, borderRadius: 10,
          background: policy.withinGrace ? PM.surface : PM.card,
          border: `0.5px solid ${PM.lineSoft}`,
        }}>
          <div style={{ fontFamily: TM.sans, fontSize: 10, color: PM.muted, letterSpacing: '0.14em', textTransform: 'uppercase' }}>
            {policy.withinGrace ? 'Refund' : 'No automatic refund'}
          </div>
          {policy.withinGrace && (
            <div style={{ fontFamily: TM.serif, fontSize: 28, color: PM.ink, fontWeight: 500, marginTop: 4 }}>
              {fmt(policy.eligibleRefundCents)}
            </div>
          )}
          <div style={{ fontFamily: TM.sans, fontSize: 12.5, color: PM.muted, marginTop: 6, lineHeight: 1.5 }}>
            {refundCopy}
          </div>
          {policy.withinGrace && (
            <div style={{ fontFamily: TM.sans, fontSize: 11, color: PM.muted, marginTop: 6 }}>
              {policy.processingNote}
            </div>
          )}
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 16 }}>
          <button onClick={onConfirm} disabled={busy} style={{
            background: PM.danger || '#a8412e', color: '#fff', border: 'none',
            padding: '12px 16px', borderRadius: 10, fontFamily: TM.sans,
            fontSize: 14, fontWeight: 500, cursor: busy ? 'wait' : 'pointer',
            opacity: busy ? 0.65 : 1,
          }}>
            {busy ? 'Cancelling…' : 'Yes, cancel'}
          </button>
          <button onClick={onClose} disabled={busy} style={{
            background: 'transparent', color: PM.ink,
            border: `0.5px solid ${PM.line}`,
            padding: '12px 16px', borderRadius: 10, fontFamily: TM.sans,
            fontSize: 14, fontWeight: 500, cursor: busy ? 'not-allowed' : 'pointer',
          }}>
            Keep my booking
          </button>
        </div>
      </div>
    </div>
  );
}

function MStayDetail({ bookingId, go }) {
  const props = window.DyraStore?.state?.properties || window.DYRA?.PROPERTIES || [];
  const token = readGuestToken();
  const [booking, setBooking] = React.useState(null);
  const [loading, setLoading] = React.useState(!!token);
  const [loadErr, setLoadErr] = React.useState('');
  const [cancelBusy, setCancelBusy] = React.useState(false);
  const [cancelMsg, setCancelMsg] = React.useState('');
  const [showCancelConfirm, setShowCancelConfirm] = React.useState(false);
  const [catalog, setCatalog] = React.useState([]);
  const [addMsg, setAddMsg] = React.useState('');

  // Add-on cart. ADD only adds to cart — it does NOT charge. The two
  // explicit buttons in the cart bottom sheet ("Add to balance" vs
  // "Pay now") are the only paths that mutate the booking. This was
  // explicitly the bug the host reported: "when I clicked ADD it
  // instantly charged my card".
  // Each cart item: { upsell_id, variant_id?, qty, name, line_cents, per_night }.
  const [cart, setCart] = React.useState([]);
  const [committing, setCommitting] = React.useState(false);
  const [showCart, setShowCart] = React.useState(false);
  const [showShabbosSheet, setShowShabbosSheet] = React.useState(false);

  // Manage-trip sheets.
  const [showDates, setShowDates] = React.useState(false);
  const [showGuests, setShowGuests] = React.useState(false);
  const [showReceipt, setShowReceipt] = React.useState(false);

  // Countdown ticker — re-renders every minute so the displayed
  // days/hours track real time.
  const [nowTick, setNowTick] = React.useState(Date.now());
  React.useEffect(() => {
    const t = setInterval(() => setNowTick(Date.now()), 60 * 1000);
    return () => clearInterval(t);
  }, []);

  React.useEffect(() => {
    if (!token) return;
    let cancelled = false;
    (async () => {
      try {
        const r = await fetch('/api/guest/booking?token=' + encodeURIComponent(token));
        const j = await r.json().catch(() => ({}));
        if (cancelled) return;
        if (!r.ok) {
          // Bad / expired token — drop it so a refresh of /m lands on the
          // email-recovery form instead of looping back into this error.
          clearGuestToken();
          setLoadErr(j.error || 'Could not load your stay.');
          setLoading(false);
          return;
        }
        setBooking(j.booking);
        setLoading(false);
      } catch (e) {
        if (cancelled) return;
        setLoadErr((e && e.message) || 'Network error.');
        setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [token]);

  // Fetch the live upsells catalog for the "Add to your stay" section.
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const r = await fetch('/api/m/upsells');
        const j = await r.json().catch(() => ({}));
        if (cancelled) return;
        if (Array.isArray(j.upsells) && j.upsells.length) setCatalog(j.upsells);
        else setCatalog(window.DYRA?.UPSELLS || []);
      } catch (_) {
        if (!cancelled) setCatalog(window.DYRA?.UPSELLS || []);
      }
    })();
    return () => { cancelled = true; };
  }, []);

  // Server returns the upsell catalog with `price_cents`; demo seed uses
  // `priceFrom` (dollars). Normalize to cents.
  function unitPriceCents(u) {
    if (typeof u?.price_cents === 'number') return u.price_cents;
    if (typeof u?.priceFrom === 'number') return u.priceFrom * 100;
    return 0;
  }
  function previewLineCents(u, qty) {
    const unit = unitPriceCents(u);
    const perNight = !!(u.per_night || u.perNight);
    const n = perNight ? Math.max(1, Number(booking?.nights || 1)) : Math.max(1, qty || 1);
    return unit * n;
  }

  function addToCart(u) {
    setAddMsg('');
    setCart(prev => {
      // If already in the cart, no-op (cart is server-validated; multi-qty
      // for the same line is unusual for these add-on packages and
      // simpler to handle as "added").
      if (prev.some(p => p.upsell_id === u.id)) return prev;
      const linePerNight = !!(u.per_night || u.perNight);
      return [
        ...prev,
        {
          upsell_id: u.id,
          variant_id: null,
          qty: 1,
          name: u.name || u.n || 'Add-on',
          unit_cents: unitPriceCents(u),
          per_night: linePerNight,
          line_cents: previewLineCents(u, 1),
        },
      ];
    });
    setShowCart(true);
  }
  function removeFromCart(upsellId) {
    setCart(prev => prev.filter(p => p.upsell_id !== upsellId));
  }
  const cartCents = cart.reduce((s, p) => s + (p.line_cents || 0), 0);

  // Commit the cart. mode is 'pay_now' or 'add_to_balance'. Generates a
  // request_id once per call; the server dedupes against this so a
  // double-tap doesn't double-charge.
  async function commitCart(mode) {
    if (!token) {
      setAddMsg('Open this page from the link in your booking email to commit add-ons.');
      return;
    }
    if (cart.length === 0 || committing) return;
    setCommitting(true);
    setAddMsg('');
    const requestId = genRequestId();
    try {
      const r = await fetch('/api/guest/upsells', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          token,
          mode,
          request_id: requestId,
          cart: cart.map(p => ({
            upsell_id: p.upsell_id,
            variant_id: p.variant_id || undefined,
            qty: p.qty || 1,
          })),
        }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) {
        setAddMsg(j.error || 'Could not add — try again.');
        setCommitting(false);
        return;
      }
      // Success: clear cart, refresh booking so totals + the upsells list
      // pick up the new lines server-rendered.
      setCart([]);
      setShowCart(false);
      try {
        const r2 = await fetch('/api/guest/booking?token=' + encodeURIComponent(token));
        const j2 = await r2.json().catch(() => ({}));
        if (r2.ok && j2.booking) setBooking(j2.booking);
      } catch (_) {}
      setAddMsg(mode === 'pay_now'
        ? 'Charged to card on file · receipt sent.'
        : 'Added to your balance · the host has been notified.');
    } catch (e) {
      setAddMsg((e && e.message) || 'Network error.');
    } finally {
      setCommitting(false);
    }
  }

  function openCancelConfirm() {
    if (!token) {
      setCancelMsg('Open this page from the link in your booking email to cancel.');
      return;
    }
    setShowCancelConfirm(true);
  }
  async function handleCancel() {
    if (!token) {
      setCancelMsg('Open this page from the link in your booking email to cancel.');
      return;
    }
    setShowCancelConfirm(false);
    setCancelBusy(true);
    setCancelMsg('');
    try {
      const r = await fetch('/api/guest/cancel', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) {
        setCancelMsg(j.error || 'Cancel failed. Please WhatsApp the host.');
        setCancelBusy(false);
        return;
      }
      setBooking(prev => prev ? { ...prev, status: 'cancelled', refunded_cents: (prev.refunded_cents || 0) + (j.refunded_cents || 0) } : prev);
      setCancelMsg(j.refunded_cents > 0
        ? `Cancelled · refunding $${Math.round(j.refunded_cents / 100)} to your card.`
        : 'Cancelled. Your deposit isn’t auto-refundable past 48h, but the host will be in touch.');
      setCancelBusy(false);
    } catch (e) {
      setCancelMsg((e && e.message) || 'Cancel failed.');
      setCancelBusy(false);
    }
  }

  // Build a display-friendly object that the rest of the JSX can read.
  // Fall back to demo values if no token / no booking, so the prototype
  // still renders something for non-authenticated viewers.
  const b = booking ? {
    id: 'DYR-' + String(booking.id).slice(0, 6).toUpperCase(),
    propertyId: booking.properties?.slug || null,
    propertyName: booking.properties?.name || 'Your stay',
    checkIn: fmtMyStaysDate(booking.check_in),
    checkOut: fmtMyStaysDate(booking.check_out),
    nights: booking.nights || Math.max(1, Math.round((new Date(booking.check_out) - new Date(booking.check_in)) / 86400000)),
    guests: booking.guests_count,
    total: Math.round((booking.total_cents || 0) / 100),
    status: booking.status,
    isCancelled: booking.status === 'cancelled',
  } : {
    id: bookingId || 'DYR-PREVIEW',
    propertyId: 'ep-4br-2ba',
    propertyName: 'Your stay',
    checkIn: 'May 8, 2026',
    checkOut: 'May 11, 2026',
    nights: 3,
    guests: 6,
    total: 1925,
    status: 'preview',
    isCancelled: false,
  };
  const p = props.find(x => x.id === b.propertyId) || { id: b.propertyId, name: b.propertyName, photos: [] };

  if (loading) {
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
        <MAppBar scrolled title="Trip details" leading={<MIconBtn onClick={() => go('stays')} label="Back"><MIco name="back" size={22}/></MIconBtn>}/>
        <div style={{ padding: 60, textAlign: 'center', color: PM3.muted, fontFamily: TM3.sans }}>Loading…</div>
      </div>
    );
  }
  if (loadErr) {
    return (
      <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
        <MAppBar scrolled title="Trip details" leading={<MIconBtn onClick={() => go('stays')} label="Back"><MIco name="back" size={22}/></MIconBtn>}/>
        <div style={{ padding: 32, textAlign: 'center' }}>
          <div style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink }}>This link doesn't work</div>
          <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 10 }}>{loadErr}</div>
        </div>
      </div>
    );
  }

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
      <MAppBar
        scrolled title="Trip details"
        leading={<MIconBtn onClick={() => go('stays')} label="Back"><MIco name="back" size={22}/></MIconBtn>}
      />
      <div style={{ flex: 1, overflow: 'auto', padding: '8px 16px 32px' }}>
        {/* hero */}
        <MPhoto property={p} idx={0} style={{ width: '100%', height: 200, borderRadius: 16 }}/>
        <div style={{ marginTop: 16 }}>
          <div style={{ fontSize: 9, letterSpacing: '0.18em', textTransform: 'uppercase', color: b.isCancelled ? PM3.danger : PM3.success, fontWeight: 500 }}>{b.isCancelled ? 'Cancelled' : (b.status === 'preview' ? 'Preview' : 'Confirmed')} · {b.id}</div>
          <div style={{ fontFamily: TM3.serif, fontSize: 24, color: PM3.ink, fontWeight: 500, marginTop: 4, lineHeight: 1.15 }}>{p.name}</div>
          <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 6 }}>
            {b.checkIn} – {b.checkOut} · {b.nights} nights · {b.guests} guests
          </div>
        </div>

        {/* countdown card */}
        {(() => {
          const propsRow = booking?.properties || {};
          const checkInTime = propsRow.check_in_time || '15:00';
          const tz = propsRow.time_zone || 'America/New_York';
          const target = booking?.check_in ? checkInUtcMs(booking.check_in, checkInTime, tz) : NaN;
          const diffMs = isNaN(target) ? NaN : (target - nowTick);
          let display;
          if (b.isCancelled) {
            display = { headline: 'Cancelled', sub: 'This booking has been cancelled.' };
          } else if (!isFinite(diffMs)) {
            display = { headline: '—', sub: 'Add a check-in date to see the countdown.' };
          } else if (diffMs <= 0) {
            display = { headline: 'Welcome', sub: "It's check-in day — see Access info below." };
          } else {
            const totalHours = Math.floor(diffMs / (1000 * 60 * 60));
            const days = Math.floor(totalHours / 24);
            const hours = totalHours % 24;
            display = {
              days, hours,
              sub: `Check-in opens at ${checkInTime} · Address & lockbox code released 24h before arrival.`,
            };
          }
          return (
            <div style={{
              marginTop: 18, padding: 16, borderRadius: 14,
              background: PM3.ink, color: PM3.bg,
            }}>
              <div style={{ fontSize: 9, letterSpacing: '0.22em', textTransform: 'uppercase', opacity: 0.7 }}>Time to check-in</div>
              {display.headline ? (
                <div style={{ fontFamily: TM3.serif, fontSize: 30, fontWeight: 500, marginTop: 10 }}>
                  {display.headline}
                </div>
              ) : (
                <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 8 }}>
                  <span style={{ fontFamily: TM3.serif, fontSize: 36, fontWeight: 500 }}>{display.days}</span>
                  <span style={{ fontFamily: TM3.sans, fontSize: 14, opacity: 0.8 }}>{display.days === 1 ? 'day' : 'days'}</span>
                  <span style={{ fontFamily: TM3.serif, fontSize: 36, fontWeight: 500, marginLeft: 12 }}>{display.hours}</span>
                  <span style={{ fontFamily: TM3.sans, fontSize: 14, opacity: 0.8 }}>{display.hours === 1 ? 'hour' : 'hours'}</span>
                </div>
              )}
              <div style={{ marginTop: 12, fontFamily: TM3.sans, fontSize: 12, opacity: 0.85 }}>
                {display.sub}
              </div>
            </div>
          );
        })()}

        {/* check-in / out */}
        <div style={{ marginTop: 24, padding: 0 }}>
          <h3 style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, margin: 0 }}>Your itinerary</h3>
          <div style={{ marginTop: 14 }}>
            {(() => {
              // Hide the Shabbos row when the stay doesn't cover a Saturday.
              const start = booking?.check_in ? new Date(booking.check_in) : new Date(b.checkIn);
              const end   = booking?.check_out ? new Date(booking.check_out) : new Date(b.checkOut);
              let hasShabbos = true;
              if (!isNaN(start.getTime()) && !isNaN(end.getTime())) {
                hasShabbos = false;
                for (let t = start.getTime(); t <= end.getTime(); t += 86400000) {
                  if (new Date(t).getDay() === 6) { hasShabbos = true; break; }
                }
              }
              const propsRow = booking?.properties || {};
              const ciTime = propsRow.check_in_time || '3:00 PM';
              const coTime = propsRow.check_out_time || '11:00 AM';
              const rows = [
                { ico: 'key',      k: 'Check-in',  v: b.checkIn + ' · ' + ciTime, sub: 'Self check-in via lockbox' },
                hasShabbos ? { ico: 'calendar', k: 'Shabbos',   v: 'Fri May 8 · 7:38pm',     sub: 'Candle-lighting · Parshas Emor' } : null,
                { ico: 'home',     k: 'Check-out', v: b.checkOut + ' · ' + coTime, sub: 'Drop keys in lockbox' },
              ].filter(Boolean);
              return rows.map((row, i) => (
                <div key={i} style={{
                  display: 'flex', alignItems: 'flex-start', gap: 12,
                  padding: '14px 0', borderBottom: i < rows.length - 1 ? `0.5px solid ${PM3.lineSoft}` : 'none',
                }}>
                  <div style={{
                    width: 36, height: 36, borderRadius: 18, background: PM3.accentSoft,
                    color: PM3.accentDark, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
                  }}><MIco name={row.ico} size={18}/></div>
                  <div style={{ flex: 1 }}>
                    <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, letterSpacing: '0.06em', textTransform: 'uppercase' }}>{row.k}</div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 15, color: PM3.ink, fontWeight: 500, marginTop: 2 }}>{row.v}</div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, marginTop: 2 }}>{row.sub}</div>
                  </div>
                </div>
              ));
            })()}
          </div>
        </div>

        {/* access info — auto-shown on/after arrival */}
        {(() => {
          if (!booking?.check_in) return null;
          const todayISO = new Date().toISOString().slice(0, 10);
          const onOrAfterArrival = todayISO >= booking.check_in;
          if (!onOrAfterArrival) return null;
          const hasAny = booking.lock_code || booking.wifi_name || booking.arrival_instructions;
          if (!hasAny) return null;
          return (
            <div style={{ marginTop: 24 }}>
              <h3 style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, margin: 0 }}>Access info</h3>
              <div style={{ marginTop: 12, background: PM3.surface, border: `0.5px solid ${PM3.line}`, borderRadius: 12, padding: 14 }}>
                {booking.lock_code && (
                  <div style={{ paddingBottom: 12, borderBottom: `0.5px solid ${PM3.lineSoft}` }}>
                    <div style={{ fontFamily: TM3.sans, fontSize: 10, color: PM3.muted, letterSpacing: '0.12em', textTransform: 'uppercase' }}>Door code</div>
                    <div style={{ fontFamily: 'monospace', fontSize: 18, color: PM3.ink, marginTop: 4 }}>{booking.lock_code}</div>
                  </div>
                )}
                {booking.wifi_name && (
                  <div style={{ paddingTop: booking.lock_code ? 12 : 0, paddingBottom: 12, borderBottom: booking.arrival_instructions ? `0.5px solid ${PM3.lineSoft}` : 'none' }}>
                    <div style={{ fontFamily: TM3.sans, fontSize: 10, color: PM3.muted, letterSpacing: '0.12em', textTransform: 'uppercase' }}>Wifi</div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, marginTop: 4 }}>{booking.wifi_name}</div>
                    {booking.wifi_password && (
                      <div style={{ fontFamily: 'monospace', fontSize: 12, color: PM3.muted, marginTop: 2 }}>pw: {booking.wifi_password}</div>
                    )}
                  </div>
                )}
                {booking.arrival_instructions && (
                  <div style={{ paddingTop: (booking.lock_code || booking.wifi_name) ? 12 : 0 }}>
                    <div style={{ fontFamily: TM3.sans, fontSize: 10, color: PM3.muted, letterSpacing: '0.12em', textTransform: 'uppercase' }}>Arrival instructions</div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.ink, marginTop: 4, lineHeight: 1.5, whiteSpace: 'pre-wrap' }}>{booking.arrival_instructions}</div>
                  </div>
                )}
              </div>
            </div>
          );
        })()}

        {/* host contact */}
        <div style={{ marginTop: 24 }}>
          <h3 style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, margin: 0 }}>Need anything?</h3>
          <div style={{ marginTop: 12, display: 'flex', flexDirection: 'column', gap: 8 }}>
            {(() => {
              const ctxName = (p && p.name) || b.propertyName || b.id || 'your reservation';
              const waText = `Hi, I'm messaging about my reservation at ${ctxName}...`;
              const waHref = `https://wa.me/18625207797?text=${encodeURIComponent(waText)}`;
              return (
                <a href={waHref} style={{
                  display: 'flex', alignItems: 'center', gap: 12, padding: '14px 16px',
                  background: PM3.surface, border: `0.5px solid ${PM3.line}`, borderRadius: 12,
                  color: PM3.ink, textDecoration: 'none',
                }}>
                  <span style={{ width: 32, height: 32, borderRadius: 16, background: '#25D366', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
                    <MIco name="whatsapp" size={18} color="#fff"/>
                  </span>
                  <span style={{ flex: 1, fontFamily: TM3.sans, fontSize: 14 }}>WhatsApp host</span>
                  <MIco name="forward" size={16} color={PM3.muted}/>
                </a>
              );
            })()}
          </div>
        </div>

        {/* purchased add-ons */}
        {Array.isArray(booking?.upsells) && booking.upsells.length > 0 && (
          <div style={{ marginTop: 24 }}>
            <h3 style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, margin: 0 }}>Your add-ons</h3>
            <div style={{ marginTop: 12, background: PM3.surface, border: `0.5px solid ${PM3.line}`, borderRadius: 12, padding: '4px 14px' }}>
              {booking.upsells.map((u, i) => (
                <div key={i} style={{
                  display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
                  padding: '12px 0', borderTop: i === 0 ? 'none' : `0.5px solid ${PM3.lineSoft}`,
                }}>
                  <div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500 }}>{u.name}{u.variant_label ? ` · ${u.variant_label}` : ''}</div>
                    <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 2 }}>{u.added_after_booking ? 'Added after booking' : 'Selected at booking'}{u.qty > 1 ? ` · ×${u.qty}` : ''}</div>
                  </div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink }}>${Math.round((u.line_cents || 0) / 100)}</div>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* add to your stay — ADD only puts items in the cart. Pay/Add-to-balance happens from the cart sheet. */}
        {(() => {
          const purchasedIds = new Set((booking?.upsells || []).map(u => String(u.id)));
          const inCartIds = new Set(cart.map(c => String(c.upsell_id)));
          const available = (catalog || []).filter(u => u && u.active !== false && !purchasedIds.has(String(u.id)));
          if (available.length === 0 || b.isCancelled) return null;
          return (
            <div style={{ marginTop: 24 }}>
              <h3 style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, margin: 0 }}>Add to your stay</h3>
              <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, marginTop: 4 }}>Tap Add to put items in your cart — you choose Pay now or add to balance.</div>
              <div style={{ marginTop: 12, background: PM3.surface, border: `0.5px solid ${PM3.line}`, borderRadius: 12, padding: '4px 14px' }}>
                {available.map((u, i) => {
                  const inCart = inCartIds.has(String(u.id));
                  const dollars = u.priceFrom != null ? u.priceFrom : Math.round((u.price_cents || 0) / 100);
                  return (
                    <div key={u.id} style={{
                      display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                      padding: '12px 0', borderTop: i === 0 ? 'none' : `0.5px solid ${PM3.lineSoft}`, gap: 12,
                    }}>
                      <div style={{ flex: 1 }}>
                        <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500 }}>{u.name || u.n}</div>
                        {(u.description || u.desc) && (
                          <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 2, lineHeight: 1.4 }}>{u.description || u.desc}</div>
                        )}
                        <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 4 }}>+${dollars}{u.perNight || u.per_night ? ' / night' : ''}</div>
                      </div>
                      {inCart ? (
                        <button onClick={() => removeFromCart(u.id)} style={{
                          background: 'transparent', color: PM3.muted, border: `0.5px solid ${PM3.line}`,
                          padding: '8px 14px', borderRadius: 999, fontSize: 12, cursor: 'pointer', fontFamily: TM3.sans,
                        }}>Remove</button>
                      ) : (
                        <button onClick={() => addToCart(u)} style={{
                          background: PM3.ink, color: PM3.bg, border: 'none', padding: '8px 14px', borderRadius: 999,
                          fontSize: 12, cursor: 'pointer', fontFamily: TM3.sans,
                        }}>Add</button>
                      )}
                    </div>
                  );
                })}
              </div>
              {addMsg && (
                <div style={{ marginTop: 10, padding: '10px 14px', background: PM3.surface, borderRadius: 10, fontSize: 12, color: PM3.muted, fontFamily: TM3.sans }}>{addMsg}</div>
              )}
            </div>
          );
        })()}

        {/* manage trip — every row is wired to a sheet now */}
        <div style={{ marginTop: 24 }}>
          <h3 style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500, margin: 0 }}>Manage trip</h3>

          {/* Cancellation policy — "as of today" preview, mirrors what the
              cancel endpoint would actually refund right now. Sits in
              Manage trip directly above the cancel row. */}
          <MStayCancellationPolicy booking={booking} palette={PM3} type={TM3}/>

          <div style={{ marginTop: 12 }}>
            {[
              { l: 'Modify dates',       onClick: () => setShowDates(true) },
              { l: 'Update guest count', onClick: () => setShowGuests(true) },
              { l: 'Add Shabbos meal',   onClick: () => setShowShabbosSheet(true) },
              { l: 'Receipt & charges',  onClick: () => setShowReceipt(true) },
            ].map((row, i) => (
              <button key={i} onClick={row.onClick} style={{
                width: '100%', padding: '16px 0', textAlign: 'left',
                background: 'transparent', border: 'none',
                borderBottom: `0.5px solid ${PM3.lineSoft}`,
                fontFamily: TM3.sans, fontSize: 14, color: PM3.ink,
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                cursor: 'pointer',
              }}>
                <span>{row.l}</span>
                <MIco name="forward" size={16} color={PM3.muted}/>
              </button>
            ))}
            <button onClick={openCancelConfirm} disabled={cancelBusy || b.isCancelled} style={{
              width: '100%', padding: '16px 0', textAlign: 'left',
              background: 'transparent', border: 'none',
              fontFamily: TM3.sans, fontSize: 14,
              color: b.isCancelled ? PM3.muted : PM3.danger,
              display: 'flex', alignItems: 'center', justifyContent: 'space-between',
              cursor: b.isCancelled ? 'not-allowed' : 'pointer',
              opacity: cancelBusy ? 0.6 : 1,
            }}>
              <span>{b.isCancelled ? 'Already cancelled' : (cancelBusy ? 'Cancelling…' : 'Cancel reservation')}</span>
              {!b.isCancelled && <MIco name="forward" size={16} color={PM3.muted}/>}
            </button>
            {cancelMsg && (
              <div style={{ marginTop: 10, padding: '10px 14px', background: PM3.surface, borderRadius: 10, fontSize: 12, color: PM3.muted, fontFamily: TM3.sans }}>
                {cancelMsg}
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Cancel confirmation modal — replaces window.confirm so the guest
          sees the exact refund amount before tapping Yes. */}
      <MCancelConfirmModal
        open={showCancelConfirm}
        booking={booking}
        busy={cancelBusy}
        onClose={() => setShowCancelConfirm(false)}
        onConfirm={handleCancel}
        palette={PM3}
        type={TM3}
      />

      {/* Cart pill — only shown when there's something in the cart. */}
      {cart.length > 0 && !showCart && (
        <button onClick={() => setShowCart(true)} style={{
          position: 'absolute', left: 16, right: 16, bottom: 16, zIndex: 60,
          background: PM3.ink, color: PM3.bg, border: 'none', borderRadius: 999,
          padding: '14px 18px', fontFamily: TM3.sans, fontSize: 14, fontWeight: 500,
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          cursor: 'pointer', boxShadow: '0 8px 24px rgba(0,0,0,0.18)',
        }}>
          <span>{cart.length} in cart · {fmtMoneyCentsClient(cartCents)}</span>
          <span style={{ opacity: 0.85 }}>Review →</span>
        </button>
      )}

      <MCartSheet
        open={showCart}
        cart={cart}
        cartCents={cartCents}
        committing={committing}
        onClose={() => setShowCart(false)}
        onRemove={removeFromCart}
        onCommit={commitCart}
        booking={booking}
      />
      <MModifyDatesSheet
        open={showDates}
        onClose={() => setShowDates(false)}
        token={token}
        booking={booking}
      />
      <MGuestCountSheet
        open={showGuests}
        onClose={() => setShowGuests(false)}
        token={token}
        booking={booking}
        onUpdated={(newCount) => setBooking(prev => prev ? { ...prev, guests_count: newCount } : prev)}
      />
      <MShabbosMealSheet
        open={showShabbosSheet}
        onClose={() => setShowShabbosSheet(false)}
        catalog={catalog}
        purchasedIds={new Set((booking?.upsells || []).map(u => String(u.id)))}
        cartIds={new Set(cart.map(c => String(c.upsell_id)))}
        addToCart={(u) => { addToCart(u); setShowShabbosSheet(false); }}
      />
      <MReceiptSheet
        open={showReceipt}
        onClose={() => setShowReceipt(false)}
        token={token}
      />
    </div>
  );
}

// ─────────── My-Stay sheet primitives ───────────

function MSheet({ open, onClose, title, children, maxHeight = '85%' }) {
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position: 'absolute', inset: 0, zIndex: 200,
      background: 'rgba(20,15,10,0.45)',
      display: 'flex', alignItems: 'flex-end',
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxHeight, background: PM3.bg,
        borderTopLeftRadius: 20, borderTopRightRadius: 20,
        boxShadow: '0 -10px 30px rgba(0,0,0,0.18)',
        display: 'flex', flexDirection: 'column',
      }}>
        <div style={{
          display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          padding: '14px 16px', borderBottom: `0.5px solid ${PM3.lineSoft}`,
        }}>
          <div style={{ fontFamily: TM3.serif, fontSize: 18, color: PM3.ink, fontWeight: 500 }}>{title}</div>
          <button onClick={onClose} aria-label="Close" style={{
            background: 'transparent', border: 'none', cursor: 'pointer', padding: 6,
          }}><MIco name="close" size={20} color={PM3.muted}/></button>
        </div>
        <div style={{ flex: 1, overflow: 'auto', padding: '14px 16px 24px' }}>
          {children}
        </div>
      </div>
    </div>
  );
}

function MCartSheet({ open, cart, cartCents, committing, onClose, onRemove, onCommit, booking }) {
  const balanceCents = Number(booking?.balance_cents || 0);
  // If the balance is already fully paid we hide "Add to balance" and only
  // offer "Pay now" — matches the spec: "if balance was already fully paid
  // then it should say Pay".
  const balanceFullyPaid = balanceCents <= 0;
  return (
    <MSheet open={open} onClose={onClose} title="Your add-on cart">
      {cart.length === 0 ? (
        <div style={{ padding: '40px 0', textAlign: 'center', color: PM3.muted, fontFamily: TM3.sans, fontSize: 13 }}>
          Your cart is empty.
        </div>
      ) : (
        <>
          <div style={{ background: PM3.surface, border: `0.5px solid ${PM3.lineSoft}`, borderRadius: 12, padding: '4px 14px' }}>
            {cart.map((p, i) => (
              <div key={p.upsell_id} style={{
                display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                padding: '12px 0', borderTop: i === 0 ? 'none' : `0.5px solid ${PM3.lineSoft}`, gap: 12,
              }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500 }}>{p.name}</div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 2 }}>
                    {p.per_night ? `${fmtMoneyCentsClient(p.unit_cents)} / night` : fmtMoneyCentsClient(p.unit_cents)}
                  </div>
                </div>
                <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, marginRight: 8 }}>{fmtMoneyCentsClient(p.line_cents)}</div>
                <button onClick={() => onRemove(p.upsell_id)} aria-label="Remove" style={{
                  background: 'transparent', border: 'none', cursor: 'pointer', padding: 4,
                }}><MIco name="close" size={18} color={PM3.muted}/></button>
              </div>
            ))}
          </div>
          <div style={{
            marginTop: 14, padding: '10px 14px', background: PM3.surface, borderRadius: 12,
            display: 'flex', justifyContent: 'space-between', fontFamily: TM3.sans, fontSize: 14,
          }}>
            <span style={{ color: PM3.muted }}>Cart total</span>
            <strong>{fmtMoneyCentsClient(cartCents)}</strong>
          </div>
          <div style={{ marginTop: 16, display: 'flex', flexDirection: 'column', gap: 10 }}>
            {!balanceFullyPaid && (
              <button disabled={committing} onClick={() => onCommit('add_to_balance')} style={{
                background: 'transparent', color: PM3.ink, border: `1px solid ${PM3.line}`,
                padding: '14px', borderRadius: 12, fontFamily: TM3.sans, fontSize: 14, fontWeight: 500,
                cursor: committing ? 'wait' : 'pointer', opacity: committing ? 0.6 : 1,
              }}>{committing ? 'Adding…' : 'Add to balance'}</button>
            )}
            <button disabled={committing} onClick={() => onCommit('pay_now')} style={{
              background: PM3.ink, color: PM3.bg, border: 'none',
              padding: '14px', borderRadius: 12, fontFamily: TM3.sans, fontSize: 14, fontWeight: 500,
              cursor: committing ? 'wait' : 'pointer', opacity: committing ? 0.6 : 1,
            }}>{committing ? 'Charging…' : (balanceFullyPaid ? `Pay ${fmtMoneyCentsClient(cartCents)}` : `Pay now ${fmtMoneyCentsClient(cartCents)}`)}</button>
          </div>
          <div style={{ marginTop: 12, fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, lineHeight: 1.5 }}>
            Pay now charges your card on file immediately. Add to balance bumps the amount owed on your reservation — no charge until your normal balance-due date.
          </div>
        </>
      )}
    </MSheet>
  );
}

function MModifyDatesSheet({ open, onClose, token, booking }) {
  const [ci, setCi] = React.useState('');
  const [co, setCo] = React.useState('');
  const [notes, setNotes] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState('');
  React.useEffect(() => {
    if (!open) return;
    setCi(booking?.check_in || '');
    setCo(booking?.check_out || '');
    setNotes('');
    setMsg('');
  }, [open, booking]);
  async function submit() {
    if (!token) { setMsg('Open this page from your booking email to request changes.'); return; }
    if (!ci || !co || co <= ci) { setMsg('Pick a valid check-in / check-out.'); return; }
    setBusy(true);
    setMsg('');
    try {
      const r = await fetch('/api/guest/modify-dates', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, check_in: ci, check_out: co, notes }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) { setMsg(j.error || 'Could not file the request.'); setBusy(false); return; }
      setMsg('Request received — the host will reply within a few hours.');
    } catch (e) {
      setMsg((e && e.message) || 'Network error.');
    } finally {
      setBusy(false);
    }
  }
  const inputStyle = {
    width: '100%', boxSizing: 'border-box', padding: '12px 14px',
    border: `1px solid ${PM3.line}`, borderRadius: 10, background: PM3.surface,
    fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, marginTop: 6,
  };
  return (
    <MSheet open={open} onClose={onClose} title="Request a date change">
      <p style={{ margin: 0, fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, lineHeight: 1.55 }}>
        We'll check availability + pricing for the new dates and reply directly. Nothing changes on your booking until the host confirms.
      </p>
      <label style={{ display: 'block', marginTop: 14, fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>
        Check-in
        <input type="date" value={ci || ''} onChange={(e) => setCi(e.target.value)} style={inputStyle}/>
      </label>
      <label style={{ display: 'block', marginTop: 12, fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>
        Check-out
        <input type="date" value={co || ''} onChange={(e) => setCo(e.target.value)} style={inputStyle}/>
      </label>
      <label style={{ display: 'block', marginTop: 12, fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>
        Note for the host (optional)
        <textarea value={notes} onChange={(e) => setNotes(e.target.value)} rows={3}
          style={{ ...inputStyle, resize: 'vertical' }}
          placeholder="Anything we should know — flight changes, simcha dates, etc."/>
      </label>
      <button disabled={busy} onClick={submit} style={{
        width: '100%', marginTop: 16, padding: 14, borderRadius: 12,
        background: PM3.ink, color: PM3.bg, border: 'none',
        fontFamily: TM3.sans, fontSize: 14, fontWeight: 500,
        cursor: busy ? 'wait' : 'pointer', opacity: busy ? 0.6 : 1,
      }}>{busy ? 'Sending…' : 'Send request to host'}</button>
      {msg && <div style={{ marginTop: 12, padding: '10px 14px', background: PM3.surface, borderRadius: 10, fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>{msg}</div>}
    </MSheet>
  );
}

function MGuestCountSheet({ open, onClose, token, booking, onUpdated }) {
  const cap = (booking?.properties?.max_guests ?? booking?.properties?.sleeps) || null;
  const initial = Number(booking?.guests_count || 1);
  const [count, setCount] = React.useState(initial);
  const [busy, setBusy] = React.useState(false);
  const [msg, setMsg] = React.useState('');
  React.useEffect(() => {
    if (!open) return;
    setCount(Number(booking?.guests_count || 1));
    setMsg('');
  }, [open, booking]);
  async function submit() {
    if (!token) { setMsg('Open this page from your booking email to update.'); return; }
    if (cap && count > cap) { setMsg(`This home sleeps ${cap}. WhatsApp the host for more capacity.`); return; }
    setBusy(true); setMsg('');
    try {
      const r = await fetch('/api/guest/guest-count', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, guests_count: count }),
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok) { setMsg(j.error || 'Could not update.'); setBusy(false); return; }
      if (typeof onUpdated === 'function') onUpdated(j.guests_count || count);
      setMsg('Updated · the host has been notified.');
    } catch (e) {
      setMsg((e && e.message) || 'Network error.');
    } finally {
      setBusy(false);
    }
  }
  return (
    <MSheet open={open} onClose={onClose} title="Update guest count">
      <p style={{ margin: 0, fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, lineHeight: 1.55 }}>
        Lets the host plan towels, parking, and key handoff.{cap ? ` This home sleeps ${cap}.` : ''}
      </p>
      <div style={{
        marginTop: 18, display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        background: PM3.surface, borderRadius: 12, padding: 14,
      }}>
        <span style={{ fontFamily: TM3.sans, fontSize: 14 }}>Guests</span>
        <div style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
          <button onClick={() => setCount(c => Math.max(1, c - 1))} aria-label="Decrease" style={{
            width: 38, height: 38, borderRadius: 19, border: `1px solid ${PM3.line}`, background: PM3.bg,
            fontSize: 18, cursor: 'pointer',
          }}>−</button>
          <span style={{ fontFamily: TM3.serif, fontSize: 22, minWidth: 24, textAlign: 'center' }}>{count}</span>
          <button onClick={() => setCount(c => Math.min(cap || 30, c + 1))} aria-label="Increase" style={{
            width: 38, height: 38, borderRadius: 19, border: `1px solid ${PM3.line}`, background: PM3.bg,
            fontSize: 18, cursor: 'pointer',
          }}>+</button>
        </div>
      </div>
      <button disabled={busy} onClick={submit} style={{
        width: '100%', marginTop: 16, padding: 14, borderRadius: 12,
        background: PM3.ink, color: PM3.bg, border: 'none',
        fontFamily: TM3.sans, fontSize: 14, fontWeight: 500,
        cursor: busy ? 'wait' : 'pointer', opacity: busy ? 0.6 : 1,
      }}>{busy ? 'Saving…' : 'Update guest count'}</button>
      {msg && <div style={{ marginTop: 12, padding: '10px 14px', background: PM3.surface, borderRadius: 10, fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>{msg}</div>}
    </MSheet>
  );
}

function MShabbosMealSheet({ open, onClose, catalog, purchasedIds, cartIds, addToCart }) {
  // Filter to add-ons whose name or category mentions Shabbos / pantry —
  // matches the demo seed labels; harmless if catalog uses different
  // copy (then this sheet just shows nothing and the user can use the
  // main "Add to your stay" list instead).
  const meals = (catalog || []).filter(u => {
    const name = String(u.name || u.n || '').toLowerCase();
    const cat = String(u.category || u.tags || '').toLowerCase();
    return /shabbos|shabbat|pantry|meal/.test(name) || /shabbos|pantry|meal/.test(cat);
  });
  const available = meals.filter(u => !purchasedIds.has(String(u.id)) && !cartIds.has(String(u.id)));
  return (
    <MSheet open={open} onClose={onClose} title="Add a Shabbos meal">
      {available.length === 0 ? (
        <div style={{ padding: '20px 0', fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, lineHeight: 1.55 }}>
          No Shabbos packages available right now — WhatsApp the host (862-520-7797) to arrange directly.
        </div>
      ) : (
        <div style={{ background: PM3.surface, border: `0.5px solid ${PM3.lineSoft}`, borderRadius: 12, padding: '4px 14px' }}>
          {available.map((u, i) => {
            const dollars = u.priceFrom != null ? u.priceFrom : Math.round((u.price_cents || 0) / 100);
            return (
              <div key={u.id} style={{
                display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                padding: '12px 0', borderTop: i === 0 ? 'none' : `0.5px solid ${PM3.lineSoft}`, gap: 12,
              }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500 }}>{u.name || u.n}</div>
                  {(u.description || u.desc) && (
                    <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 2, lineHeight: 1.4 }}>{u.description || u.desc}</div>
                  )}
                  <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 4 }}>+${dollars}{u.perNight || u.per_night ? ' / night' : ''}</div>
                </div>
                <button onClick={() => addToCart(u)} style={{
                  background: PM3.ink, color: PM3.bg, border: 'none', padding: '8px 14px', borderRadius: 999,
                  fontSize: 12, cursor: 'pointer', fontFamily: TM3.sans,
                }}>Add to cart</button>
              </div>
            );
          })}
        </div>
      )}
    </MSheet>
  );
}

function MReceiptSheet({ open, onClose, token }) {
  const [data, setData] = React.useState(null);
  const [err, setErr] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  React.useEffect(() => {
    if (!open || !token) return;
    let cancelled = false;
    (async () => {
      setLoading(true); setErr('');
      try {
        const r = await fetch('/api/guest/receipt?token=' + encodeURIComponent(token));
        const j = await r.json().catch(() => ({}));
        if (cancelled) return;
        if (!r.ok) { setErr(j.error || 'Could not load receipt.'); setLoading(false); return; }
        setData(j); setLoading(false);
      } catch (e) {
        if (!cancelled) { setErr((e && e.message) || 'Network error.'); setLoading(false); }
      }
    })();
    return () => { cancelled = true; };
  }, [open, token]);
  return (
    <MSheet open={open} onClose={onClose} title="Receipt & charges">
      {loading ? (
        <div style={{ padding: 30, textAlign: 'center', color: PM3.muted, fontFamily: TM3.sans }}>Loading…</div>
      ) : err ? (
        <div style={{ padding: 20, fontFamily: TM3.sans, fontSize: 13, color: PM3.danger }}>{err}</div>
      ) : !data ? null : (
        <>
          <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, marginBottom: 14 }}>
            {data.booking.property} · {fmtMyStaysDate(data.booking.check_in)} → {fmtMyStaysDate(data.booking.check_out)}
          </div>

          <div style={{ background: PM3.surface, border: `0.5px solid ${PM3.lineSoft}`, borderRadius: 12, padding: '4px 14px' }}>
            {data.lineItems.map((row, i) => (
              <div key={i} style={{
                display: 'flex', justifyContent: 'space-between', padding: '12px 0',
                borderTop: i === 0 ? 'none' : `0.5px solid ${PM3.lineSoft}`, gap: 12,
              }}>
                <div style={{ flex: 1 }}>
                  <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink }}>{row.label}</div>
                  {row.sublabel && (
                    <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 2 }}>{row.sublabel}</div>
                  )}
                </div>
                <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink }}>{fmtMoneyCentsClient(row.cents)}</div>
              </div>
            ))}
            <div style={{
              display: 'flex', justifyContent: 'space-between', padding: '12px 0',
              borderTop: `0.5px solid ${PM3.lineSoft}`, fontFamily: TM3.sans, fontSize: 15, fontWeight: 600,
            }}>
              <span>Total</span>
              <span>{fmtMoneyCentsClient(data.totals.total_cents)}</span>
            </div>
          </div>

          <h4 style={{ fontFamily: TM3.serif, fontSize: 16, color: PM3.ink, fontWeight: 500, margin: '20px 0 8px' }}>Payments</h4>
          <div style={{ background: PM3.surface, border: `0.5px solid ${PM3.lineSoft}`, borderRadius: 12, padding: '4px 14px' }}>
            {data.payments.length === 0 ? (
              <div style={{ padding: '12px 0', fontFamily: TM3.sans, fontSize: 13, color: PM3.muted }}>
                No charges yet.
              </div>
            ) : data.payments.map((p, i) => (
              <div key={p.id || i} style={{
                display: 'flex', justifyContent: 'space-between', padding: '10px 0',
                borderTop: i === 0 ? 'none' : `0.5px solid ${PM3.lineSoft}`,
                fontFamily: TM3.sans, fontSize: 13,
              }}>
                <div>
                  <div style={{ color: PM3.ink }}>{p.label}</div>
                  <div style={{ color: PM3.muted, fontSize: 11, marginTop: 2 }}>{(p.date || '').slice(0, 10)}</div>
                </div>
                <div style={{ color: p.cents < 0 ? PM3.danger : PM3.ink }}>
                  {p.cents < 0 ? '−' : ''}{fmtMoneyCentsClient(Math.abs(p.cents))}
                </div>
              </div>
            ))}
            <div style={{
              display: 'flex', justifyContent: 'space-between', padding: '10px 0',
              borderTop: `0.5px solid ${PM3.lineSoft}`, fontFamily: TM3.sans, fontSize: 14, fontWeight: 600,
            }}>
              <span>Balance remaining</span>
              <span>{fmtMoneyCentsClient(data.balanceRemainingCents)}</span>
            </div>
          </div>

          {data.paymentMethod && (
            <div style={{ marginTop: 14, fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, textAlign: 'center' }}>
              Card on file: {(data.paymentMethod.brand || 'card')} ····{data.paymentMethod.last4 || '—'}
            </div>
          )}
        </>
      )}
    </MSheet>
  );
}

// ═══════════════════════════════════════════════════════════════════════
// WISHLIST
// ═══════════════════════════════════════════════════════════════════════
function MWishlist({ go, openMenu }) {
  const props = window.DyraStore?.state?.properties || window.DYRA?.PROPERTIES || [];
  const saved = props.slice(0, 3);
  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
      <MAppBar scrolled title="Saved" leading={<MIconBtn label="Menu" onClick={openMenu}><MIco name="menu" size={22}/></MIconBtn>}/>
      <div style={{ flex: 1, overflow: 'auto', padding: '12px 16px 24px' }}>
        <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, padding: '0 4px 12px' }}>{saved.length} saved homes</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
          {saved.map(p => (
            <button key={p.id} onClick={() => go('listing:' + p.id)} style={{
              display: 'flex', gap: 12, padding: 12, alignItems: 'center',
              background: PM3.surface, border: `0.5px solid ${PM3.lineSoft}`,
              borderRadius: 14,
              cursor: 'pointer', textAlign: 'left', fontFamily: 'inherit',
              WebkitTapHighlightColor: 'transparent',
            }}>
              <MPhoto property={p} idx={0} style={{ width: 96, height: 96, borderRadius: 10, flexShrink: 0 }}/>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontFamily: TM3.serif, fontSize: 15, color: PM3.ink, fontWeight: 500, lineHeight: 1.2 }}>{p.name}</div>
                <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 4 }}>{p.bedrooms} bed · sleeps {p.sleeps}</div>
                <div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginTop: 8 }}>
                  <span style={{ fontFamily: TM3.serif, fontSize: 17, color: PM3.ink, fontWeight: 500 }}>${p.priceFrom}</span>
                  <span style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted }}>/ night</span>
                </div>
              </div>
              <MIconBtn label="Remove" onClick={(e) => { e.stopPropagation(); }}>
                <MIco name="close" size={20} color={PM3.muted}/>
              </MIconBtn>
            </button>
          ))}
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════
// LIST YOUR HOME
// ═══════════════════════════════════════════════════════════════════════
function MListYourHome({ go, openMenu }) {
  // Same fields as the desktop D1ListYourHome form, broken into 4 phone-friendly
  // steps so you only ever see ~4 inputs at a time. Submitted form posts the
  // same payload shape into DyraStore.inquiries (kind: 'listYourHome'), so the
  // admin Inquiries inbox sees mobile + desktop submissions in one stream.
  const [step, setStep] = React.useState(0); // 0 = intro, 1-4 = form steps, 5 = done
  const [scrolled, setScrolled] = React.useState(false);
  const [sending, setSending] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [f, setF] = React.useState({
    crossStreets: '', bedrooms: '', bathrooms: '', sleeps: '',
    basement: '', fullKitchen: '', separateMeatDairy: '',
    targetRate: '', arrangement: '',
    name: '', email: '', phone: '', notes: '',
  });
  const set = (k, v) => setF(s => ({ ...s, [k]: v }));

  // Inline pill chip — same shape as desktop LYHChip, sized for thumbs.
  const Chip = ({ active, children, onClick }) => (
    <button onClick={onClick} type="button" style={{
      flex: 1, minHeight: 44,
      padding: '10px 14px', borderRadius: 999,
      background: active ? PM3.accent : 'transparent',
      color: active ? '#fff' : PM3.ink,
      border: `1px solid ${active ? PM3.accent : PM3.line}`,
      fontFamily: TM3.sans, fontSize: 13, fontWeight: 500,
      cursor: 'pointer', WebkitTapHighlightColor: 'transparent',
      whiteSpace: 'nowrap',
    }}>{children}</button>
  );

  // INTRO — original pitch screen kept verbatim
  if (step === 0) {
    return (
      <div onScroll={(e) => setScrolled(e.target.scrollTop > 4)}
        style={{ height: '100%', overflow: 'auto', background: PM3.bg }}>
        <MAppBar transparent scrolled={scrolled} title="List your home"
          leading={<MIconBtn onClick={openMenu} label="Menu"><MIco name="menu" size={22} color="#fff"/></MIconBtn>}/>

        <div style={{ position: 'relative', marginTop: -56, height: 380, overflow: 'hidden' }}>
          <Placeholder src="assets/presidential/04-parlor-mantel.jpg" style={{ position: 'absolute', inset: 0 }} objectPosition="center 60%"/>
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(180deg, rgba(20,15,10,0.35) 0%, rgba(20,15,10,0.5) 100%)',
          }}/>
          <div style={{
            position: 'absolute', inset: 0, padding: '110px 24px 28px',
            display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', color: '#fff',
          }}>
            <MEyebrow color="rgba(255,255,255,0.85)">For Crown Heights owners</MEyebrow>
            <div style={{ fontFamily: TM3.serif, fontSize: 38, lineHeight: 1.04, fontWeight: 400, marginTop: 12, letterSpacing: '-0.01em' }}>
              We treat your home<br/>like our own.
            </div>
          </div>
        </div>

        <div style={{ padding: '24px 20px 0' }}>
          <p style={{ fontFamily: TM3.sans, fontSize: 15, lineHeight: 1.6, color: PM3.inkSoft, textWrap: 'pretty', margin: 0 }}>
            Tell us about your place. We'll review and follow up personally to discuss how we can work together — full management, rental arbitrage, or a simple listing.
          </p>

          <div style={{ marginTop: 28, display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 12 }}>
            {[
              { v: '92%',   l: 'Avg. occupancy' },
              { v: '4.9',   l: 'Avg. guest rating' },
              { v: '$4.2k', l: 'Avg. monthly net' },
              { v: '11',    l: 'Homes managed' },
            ].map((s, i) => (
              <div key={i} style={{
                padding: 16, background: PM3.surface, borderRadius: 12,
                border: `0.5px solid ${PM3.lineSoft}`,
              }}>
                <div style={{ fontFamily: TM3.serif, fontSize: 28, color: PM3.ink, fontWeight: 500 }}>{s.v}</div>
                <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 4, letterSpacing: '0.04em', textTransform: 'uppercase' }}>{s.l}</div>
              </div>
            ))}
          </div>

          <h3 style={{ fontFamily: TM3.serif, fontSize: 22, color: PM3.ink, fontWeight: 500, marginTop: 32, lineHeight: 1.2 }}>
            What we handle
          </h3>
          <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column' }}>
            {[
              ['Kashering',     'Kitchens kashered to standard before launch'],
              ['Pricing',       'Yom Tov + Shabbos rate management'],
              ['Bookings',      'Direct calls + WhatsApp + this site'],
              ['Cleaning',      'Frum cleaning crew between every stay'],
              ['Guest support', '24/7 phone, even Shabbos emergencies'],
              ['Compliance',    'NYC short-term rental rules + insurance'],
            ].map((row, i) => (
              <div key={i} style={{
                display: 'flex', gap: 14, padding: '14px 0',
                borderBottom: i < 5 ? `0.5px solid ${PM3.lineSoft}` : 'none',
              }}>
                <span style={{ color: PM3.accent, marginTop: 2 }}><MIco name="check" size={18}/></span>
                <div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500 }}>{row[0]}</div>
                  <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, marginTop: 2 }}>{row[1]}</div>
                </div>
              </div>
            ))}
          </div>

          <div style={{ marginTop: 32, marginBottom: 32 }}>
            <MButton onClick={() => setStep(1)}>Get a custom estimate</MButton>
            <div style={{ marginTop: 10, fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, textAlign: 'center' }}>
              Takes 2 minutes · No commitment
            </div>
          </div>
        </div>
      </div>
    );
  }

  // SUBMISSION HANDLER — same payload + DyraStore mirror as desktop
  async function submit() {
    const arrangementLabels = {
      manage: 'Full management',
      arbitrage: 'Rental arbitrage',
      listOnly: 'List only',
      unsure: 'Not sure yet',
    };
    const payload = {
      _subject: 'Dyra List My Home',
      name: f.name, email: f.email, phone: f.phone,
      cross_streets: f.crossStreets,
      bedrooms: f.bedrooms, bathrooms: f.bathrooms, sleeps: f.sleeps,
      basement: f.basement, full_kitchen: f.fullKitchen,
      separate_meat_dairy: f.separateMeatDairy,
      target_rate: f.targetRate,
      looking_for: arrangementLabels[f.arrangement] || '',
      notes: f.notes,
    };
    setSending(true); setError(null);
    if (window.DyraStore && window.DyraStore.update) {
      try {
        window.DyraStore.update(s => {
          const id = window.DyraStore.uid('inq');
          s.inquiries.unshift({
            id, kind: 'listYourHome', status: 'new',
            source: 'list-your-home-mobile',
            receivedAt: new Date().toISOString().slice(0, 10),
            name: f.name, email: f.email, phone: f.phone,
            bedrooms: Number(f.bedrooms) || null,
            address: f.crossStreets,
            kosher: f.separateMeatDairy === 'Yes',
            notes: [
              f.notes,
              f.targetRate && `Target rate: ${f.targetRate}`,
              f.bathrooms && `${f.bathrooms} bathrooms`,
              f.sleeps && `Sleeps ${f.sleeps}`,
              f.basement && `Basement: ${f.basement}`,
              f.fullKitchen && `Kitchen: ${f.fullKitchen}`,
              payload.looking_for && `Looking for: ${payload.looking_for}`,
            ].filter(Boolean).join(' · '),
          });
          s.notifications.unshift({
            id: window.DyraStore.uid('ntf'), at: new Date().toISOString(),
            kind: 'inquiry', read: false,
            title: `New inquiry · ${f.name || 'unnamed'}`,
            body: `${f.bedrooms || '?'}BR · ${f.crossStreets || 'address pending'}`,
            link: { screen: 'inquiries' },
          });
        });
      } catch (e) { /* fine */ }
    }
    try {
      const res = await fetch('https://formspree.io/f/xjgjlowo', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
        body: JSON.stringify(payload),
      });
      if (!res.ok) throw new Error('Network response was not ok');
      setStep(5);
    } catch (err) {
      setError('Something went wrong. Please try again, or WhatsApp directly.');
    } finally {
      setSending(false);
    }
  }

  // FORM STEPS
  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
      <MAppBar
        scrolled title={step === 5 ? 'Submitted' : 'List your home'}
        leading={step < 5 && (
          <MIconBtn onClick={() => step > 1 ? setStep(s => s - 1) : setStep(0)} label="Back">
            <MIco name="back" size={22}/>
          </MIconBtn>
        )}
      />
      {step >= 1 && step <= 4 && (
        <div style={{ padding: '0 16px 12px', display: 'flex', gap: 4 }}>
          {[1,2,3,4].map(s => (
            <div key={s} style={{
              flex: 1, height: 3, borderRadius: 2,
              background: s <= step ? PM3.accent : PM3.lineSoft,
            }}/>
          ))}
        </div>
      )}

      <div style={{ flex: 1, overflow: 'auto', padding: '8px 20px 24px' }}>
        {/* STEP 1 — The property: cross streets, bedrooms, bathrooms, sleeps */}
        {step === 1 && (
          <div>
            <MEyebrow>The property</MEyebrow>
            <h2 style={{ fontFamily: TM3.serif, fontSize: 26, color: PM3.ink, fontWeight: 500, margin: '8px 0 0', lineHeight: 1.2 }}>
              Tell us about your home
            </h2>
            <p style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 6 }}>
              Address stays private until we talk.
            </p>
            <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', gap: 14 }}>
              <MField label="Cross streets" value={f.crossStreets}
                onChange={v => set('crossStreets', v)}
                placeholder="e.g. President & Kingston" />
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 10 }}>
                <MField label="Bedrooms" type="number" value={f.bedrooms} onChange={v => set('bedrooms', v)} placeholder="3"/>
                <MField label="Baths"    type="number" value={f.bathrooms} onChange={v => set('bathrooms', v)} placeholder="2"/>
                <MField label="Sleeps"   type="number" value={f.sleeps} onChange={v => set('sleeps', v)} placeholder="6"/>
              </div>
            </div>
          </div>
        )}

        {/* STEP 2 — basement / full kitchen / meat & dairy chip-pick */}
        {step === 2 && (
          <div>
            <MEyebrow>The property</MEyebrow>
            <h2 style={{ fontFamily: TM3.serif, fontSize: 26, color: PM3.ink, fontWeight: 500, margin: '8px 0 0', lineHeight: 1.2 }}>
              Kitchen &amp; layout
            </h2>
            <p style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 6 }}>
              Don't worry if it isn't kosher yet — we kasher every kitchen ourselves.
            </p>

            <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', gap: 22 }}>
              <div>
                <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 10 }}>
                  Is this a basement apartment?
                </div>
                <div style={{ display: 'flex', gap: 8 }}>
                  {['Yes', 'No', 'Garden-level'].map(o => (
                    <Chip key={o} active={f.basement === o} onClick={() => set('basement', o)}>{o}</Chip>
                  ))}
                </div>
              </div>
              <div>
                <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 10 }}>
                  Full kitchen?
                </div>
                <div style={{ display: 'flex', gap: 8 }}>
                  {['Yes', 'Kitchenette', 'No'].map(o => (
                    <Chip key={o} active={f.fullKitchen === o} onClick={() => set('fullKitchen', o)}>{o}</Chip>
                  ))}
                </div>
              </div>
              <div>
                <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 10 }}>
                  Separate meat &amp; dairy setups?
                </div>
                <div style={{ display: 'flex', gap: 8 }}>
                  {['Yes', 'No', 'Can be set up'].map(o => (
                    <Chip key={o} active={f.separateMeatDairy === o} onClick={() => set('separateMeatDairy', o)}>{o}</Chip>
                  ))}
                </div>
              </div>
            </div>
          </div>
        )}

        {/* STEP 3 — target rate + arrangement preference */}
        {step === 3 && (
          <div>
            <MEyebrow>The arrangement</MEyebrow>
            <h2 style={{ fontFamily: TM3.serif, fontSize: 26, color: PM3.ink, fontWeight: 500, margin: '8px 0 0', lineHeight: 1.2 }}>
              How would you like to work together?
            </h2>

            <div style={{ marginTop: 22, display: 'flex', flexDirection: 'column', gap: 14 }}>
              <MField label="Target nightly rate"
                value={f.targetRate} onChange={v => set('targetRate', v)}
                placeholder="e.g. $350 / night"/>

              <div style={{ marginTop: 4 }}>
                <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted, letterSpacing: '0.06em', textTransform: 'uppercase', marginBottom: 10 }}>
                  What are you looking for?
                </div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                  {[
                    ['manage',    'Full management',  'We handle listing, bookings, guests, cleaning, everything.'],
                    ['arbitrage', 'Rental arbitrage', 'We lease from you at a fixed monthly rate and sublet.'],
                    ['listOnly',  'List only',        'You keep management; we just list and send bookings.'],
                    ['unsure',    'Not sure yet',     'Happy to walk through options on a call.'],
                  ].map(([id, title, sub]) => {
                    const active = f.arrangement === id;
                    return (
                      <button key={id} type="button" onClick={() => set('arrangement', id)} style={{
                        textAlign: 'left', padding: '14px 16px', borderRadius: 12, cursor: 'pointer',
                        fontFamily: TM3.sans,
                        background: active ? PM3.accentSoft : PM3.surface,
                        border: `1.5px solid ${active ? PM3.accent : PM3.line}`,
                        color: PM3.ink, WebkitTapHighlightColor: 'transparent',
                      }}>
                        <div style={{ fontSize: 14, fontWeight: 500 }}>{title}</div>
                        <div style={{ fontSize: 12, color: PM3.muted, marginTop: 3, lineHeight: 1.45 }}>{sub}</div>
                      </button>
                    );
                  })}
                </div>
              </div>
            </div>
          </div>
        )}

        {/* STEP 4 — contact + notes + estimate preview */}
        {step === 4 && (
          <div>
            <MEyebrow>Your contact</MEyebrow>
            <h2 style={{ fontFamily: TM3.serif, fontSize: 26, color: PM3.ink, fontWeight: 500, margin: '8px 0 0', lineHeight: 1.2 }}>
              How can we reach you?
            </h2>
            <p style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 6 }}>
              We'll WhatsApp within 24 hours.
            </p>

            <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 12 }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
                <MField label="Name"  value={f.name}  onChange={v => set('name', v)}/>
                <MField label="Phone" type="tel" value={f.phone} onChange={v => set('phone', v)}/>
              </div>
              <MField label="Email" type="email" value={f.email} onChange={v => set('email', v)}/>
              <MField label="Anything else?" multiline rows={4}
                value={f.notes} onChange={v => set('notes', v)}
                placeholder="Photos available, unique features, timing, questions…"/>
            </div>

            {/* Estimate preview — same vibe as the original screen */}
            <div style={{
              marginTop: 22, padding: 18, borderRadius: 14, background: PM3.ink, color: PM3.bg,
            }}>
              <MEyebrow color={PM3.accentSoft}>Estimated</MEyebrow>
              <div style={{ display: 'flex', alignItems: 'baseline', gap: 6, marginTop: 8 }}>
                <span style={{ fontFamily: TM3.serif, fontSize: 36, fontWeight: 500 }}>$3,800</span>
                <span style={{ fontFamily: TM3.sans, fontSize: 13, opacity: 0.7 }}>– $5,200 / month net</span>
              </div>
              <div style={{ marginTop: 10, fontFamily: TM3.sans, fontSize: 12, opacity: 0.85, lineHeight: 1.5 }}>
                Final estimate after we walk through the home together.
              </div>
            </div>

            {error && (
              <div style={{ marginTop: 14, padding: 12, borderRadius: 10, background: '#fbeaea', color: '#b3261e', fontSize: 12, fontFamily: TM3.sans, textAlign: 'center' }}>
                {error}
              </div>
            )}
            <div style={{ fontFamily: TM3.sans, fontSize: 11, color: PM3.muted, marginTop: 14, textAlign: 'center' }}>
              Or WhatsApp directly at <a href="https://wa.me/18625207797" style={{ color: PM3.accent, textDecoration: 'none' }}>862-520-7797</a>
            </div>
          </div>
        )}

        {/* STEP 5 — Submitted */}
        {step === 5 && (
          <div style={{ paddingTop: 40, textAlign: 'center' }}>
            <div style={{
              width: 84, height: 84, borderRadius: 42, background: PM3.success,
              margin: '0 auto', display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              animation: 'mPop 400ms cubic-bezier(0.34, 1.56, 0.64, 1)',
            }}><MIco name="check" size={48} color="#fff"/></div>
            <MEyebrow>Inquiry received</MEyebrow>
            <div style={{
              fontFamily: TM3.serif, fontSize: 30, color: PM3.ink, fontWeight: 500,
              marginTop: 16, lineHeight: 1.1, letterSpacing: '-0.01em',
            }}>Thank you.</div>
            <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.muted, marginTop: 10, lineHeight: 1.6, maxWidth: 320, marginInline: 'auto' }}>
              We'll review your details and reach out personally within 24 hours
              to walk through next steps.
            </div>
            <div style={{ marginTop: 28 }}>
              <MButton onClick={() => go('home')}>Back to home</MButton>
            </div>
          </div>
        )}
      </div>

      {step >= 1 && step <= 4 && (
        <div style={{ padding: '12px 16px 28px', borderTop: `0.5px solid ${PM3.line}`, background: PM3.bg }}>
          <MButton
            disabled={sending || (step === 4 && (!f.name || !f.email))}
            onClick={() => step === 4 ? submit() : setStep(s => s + 1)}
          >
            {step === 4 ? (sending ? 'Sending…' : 'Send inquiry') : 'Continue'}
          </MButton>
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════
// HELP / CONTACT screen
// ═══════════════════════════════════════════════════════════════════════
function MHelp({ go, openMenu }) {
  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: PM3.bg }}>
      <MAppBar scrolled title="Help & contact"
        leading={<MIconBtn onClick={openMenu} label="Menu"><MIco name="menu" size={22}/></MIconBtn>}/>
      <div style={{ flex: 1, overflow: 'auto', padding: '16px' }}>
        <h2 style={{ fontFamily: TM3.serif, fontSize: 26, color: PM3.ink, fontWeight: 500, margin: 0, lineHeight: 1.15 }}>
          We answer fast.
        </h2>
        <p style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 8 }}>
          WhatsApp gets through fastest — 11pm Friday, before Shabbos, even when you forgot the lockbox code.
        </p>

        <div style={{ marginTop: 20, display: 'flex', flexDirection: 'column', gap: 10 }}>
          <a href="https://wa.me/18625207797" style={{
            display: 'flex', alignItems: 'center', gap: 14, padding: '16px',
            background: PM3.surface, border: `0.5px solid ${PM3.line}`, borderRadius: 14,
            color: PM3.ink, textDecoration: 'none',
          }}>
            <span style={{ width: 44, height: 44, borderRadius: 22, background: '#25D366', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
              <MIco name="whatsapp" size={22} color="#fff"/>
            </span>
            <div style={{ flex: 1 }}>
              <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>WhatsApp · fastest</div>
              <div style={{ fontFamily: TM3.sans, fontSize: 15, color: PM3.ink, fontWeight: 500 }}>862-520-7797</div>
            </div>
            <MIco name="forward" size={18} color={PM3.muted}/>
          </a>
          <a href="mailto:dyrakosherrentals@gmail.com" style={{
            display: 'flex', alignItems: 'center', gap: 14, padding: '16px',
            background: PM3.surface, border: `0.5px solid ${PM3.line}`, borderRadius: 14,
            color: PM3.ink, textDecoration: 'none',
          }}>
            <span style={{ width: 44, height: 44, borderRadius: 22, background: '#EA4335', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
              <MIco name="mail" size={20} color="#fff"/>
            </span>
            <div style={{ flex: 1 }}>
              <div style={{ fontFamily: TM3.sans, fontSize: 12, color: PM3.muted }}>Email</div>
              <div style={{ fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500 }}>dyrakosherrentals@gmail.com</div>
            </div>
            <MIco name="forward" size={18} color={PM3.muted}/>
          </a>
        </div>

        {/* FAQ */}
        <h3 style={{ fontFamily: TM3.serif, fontSize: 20, color: PM3.ink, fontWeight: 500, marginTop: 32, marginBottom: 8 }}>Quick answers</h3>
        <div>
          {[
            ['Are all kitchens kosher?', 'Yes. Every Dyra apartment has a kosher kitchen with separate meat and dairy. We kasher them ourselves before listing.'],
            ['Can I check in on Shabbos?', 'Yes — self check-in via lockbox. We send the code 48 hours before arrival.'],
            ['Do you cater?', 'We don\'t — but we coordinate with kosher caterers and stock kitchens with Shabbos meals on request.'],
          ].map((row, i) => (
            <details key={i} style={{
              padding: '14px 0', borderBottom: i < 3 ? `0.5px solid ${PM3.lineSoft}` : 'none',
            }}>
              <summary style={{
                fontFamily: TM3.sans, fontSize: 14, color: PM3.ink, fontWeight: 500,
                cursor: 'pointer', listStyle: 'none', display: 'flex', justifyContent: 'space-between',
              }}>
                {row[0]}
                <span style={{ color: PM3.muted }}>+</span>
              </summary>
              <p style={{ fontFamily: TM3.sans, fontSize: 13, color: PM3.muted, marginTop: 8, lineHeight: 1.5 }}>{row[1]}</p>
            </details>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { MStays, MStayDetail, MWishlist, MListYourHome, MHelp });
