/* ============================================================
 * Hostelova — Booking web app (book.html)
 * Real data, hostel detail, and Razorpay pay-to-confirm — the
 * exact same flow as the Android app:
 *   create-razorpay-order → Checkout → verify-razorpay-payment
 * Login is required only at the moment of payment.
 * ========================================================== */
const { useState, useEffect, useRef, useCallback, createContext, useContext } = React;
const API = window.HostelovaAPI;

// Module-level cache so navigating detail ⇄ booking doesn't refetch.
// `lastBooking` holds the booking the verify call just returned, so My
// Bookings can show it instantly even before the list query catches up.
const cache = { hostels: null, detail: {}, rooms: {}, lastBooking: null };
const { VerifiedListingBadge, isVerifiedListing } = window.HostelovaBadges;

const SHARING_OPTIONS = ['1 Sharing', '2 Sharing', '3 Sharing', '4+ Sharing'];
const DURATIONS = [
  { months: 1, label: '1 Month' },
  { months: 3, label: '3 Months' },
  { months: 6, label: '6 Months (1 Semester)' },
  { months: 12, label: '12 Months' },
];

const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);

// ── helpers ──────────────────────────────────────────────────
const truthy = (v) =>
  v === true || v === 1 || String(v).toLowerCase() === 'true' || String(v).toLowerCase() === 'yes';

const num = (v) => {
  const n = Number(v);
  return isNaN(n) ? 0 : n;
};

const compactAddress = (v) =>
  !v ? 'Lucknow' : String(v).split(',').slice(0, 2).join(',').trim() || v;

const docLabel = (key) =>
  String(key || '')
    .replace(/[_-]+/g, ' ')
    .replace(/\b\w/g, (c) => c.toUpperCase());

const monthlyRent = (hostel, room) =>
  (room && (room.final_price || room.price)) ||
  (hostel && (hostel.final_price || hostel.price)) ||
  0;

// A booking pre-pays the chosen stay duration:
//   base = months × (monthly + maintenance) + deposit   (fallback: one month)
// Mirrors the Flutter BookingPricing helper + the create-razorpay-order edge
// function so the displayed total always equals what Razorpay charges.
const feeBreakdown = (hostel, room, months) => {
  const m = Math.max(1, num(months) || 1);
  const monthly = monthlyRent(hostel, room);
  const deposit = num(hostel && hostel.security_deposit);
  const maintenance = num(hostel && hostel.maintenance_charge);
  const rentForStay = monthly * m;
  const maintenanceForStay = maintenance * m;
  let base = rentForStay + maintenanceForStay + deposit;
  if (base <= 0) base = monthly;
  const gateway = Math.round(base * 0.02);
  const platform = Math.round(base * 0.05);
  return {
    monthly, months: m, deposit, maintenance,
    rentForStay, maintenanceForStay,
    base, gateway, platform, total: base + gateway + platform,
  };
};

const fmtDate = (d) => {
  const m = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  return m[d.getMonth()] + ' ' + d.getDate() + ', ' + d.getFullYear();
};

const toInputDate = (d) => {
  const p = (n) => String(n).padStart(2, '0');
  return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate());
};

// Format an ISO/date string for display; returns '' for empty/invalid input.
const fmtISO = (s) => {
  if (!s) return '';
  const d = new Date(s);
  return isNaN(d.getTime()) ? '' : fmtDate(d);
};

const isPaid = (b) => String(b.payment_status || '').toLowerCase() === 'paid';

// ── routing ──────────────────────────────────────────────────
function parseRoute() {
  const h = (location.hash || '#/').replace(/^#/, '');
  const parts = h.split('/').filter(Boolean);
  if (!parts.length) return { name: 'browse' };
  if (parts[0] === 'hostel') return { name: 'detail', id: decodeURIComponent(parts[1] || '') };
  if (parts[0] === 'book') return { name: 'book', id: decodeURIComponent(parts[1] || '') };
  if (parts[0] === 'bookings') return { name: 'bookings' };
  if (parts[0] === 'profile') return { name: 'profile' };
  if (parts[0] === 'success') return { name: 'success' };
  return { name: 'browse' };
}
const navigate = (to) => { location.hash = to; };

// useTheme comes from the shared site-chrome.jsx (loaded before this file).

// ── Toasts ───────────────────────────────────────────────────
const Toasts = ({ items }) => (
  <div className="bk-toasts">
    {items.map((t) => (
      <div key={t.id} className={'bk-toast' + (t.error ? ' error' : '')}>
        <span className="bk-toast-ico">
          {t.error ? <Icon.Flag width="15" height="15" /> : <Icon.Check width="15" height="15" />}
        </span>
        <div>
          <div className="bk-toast-title">{t.title}</div>
          {t.body && <div className="bk-toast-body">{t.body}</div>}
        </div>
      </div>
    ))}
  </div>
);

// ── Booking-app actions injected into the SHARED Nav (site-chrome.jsx) ──
const loginNext = () => 'login.html?next=' + encodeURIComponent('book.html' + (location.hash || '#/'));

const BookNavActions = ({ user, activeProfile }) => (
  <>
    <a className="nav-link nav-link-desktop" href="#/bookings">My bookings</a>
    {user ? (
      <NavAvatar user={user} active={activeProfile} />
    ) : (
      <a className="nav-cta nav-cta-desktop" href={loginNext()}>Sign in</a>
    )}
  </>
);

// Booking links for the mobile drawer (so My bookings / profile are reachable).
const BookDrawerActions = ({ user }) => (
  <>
    <div className="sd-section-title"><span className="sd-sec-ico"><Icon.Bookmark width="16" height="16" /></span><span>Your account</span></div>
    <div className="sd-list">
      <a className="sd-link" href="book.html#/">
        <span className="sd-link-ico"><Icon.Home width="16" height="16" /></span>
        <span className="sd-link-text">Browse hostels</span>
        <span className="sd-link-arrow"><Icon.ArrowRight width="14" height="14" /></span>
      </a>
      <a className="sd-link" href="book.html#/bookings">
        <span className="sd-link-ico"><Icon.Bookmark width="16" height="16" /></span>
        <span className="sd-link-text">My bookings</span>
        <span className="sd-link-arrow"><Icon.ArrowRight width="14" height="14" /></span>
      </a>
      {user ? (
        <a className="sd-link" href="book.html#/profile">
          <span className="sd-link-ico"><Icon.UserSolid width="16" height="16" /></span>
          <span className="sd-link-text">My profile</span>
          <span className="sd-link-arrow"><Icon.ArrowRight width="14" height="14" /></span>
        </a>
      ) : (
        <a className="sd-link" href={loginNext()}>
          <span className="sd-link-ico"><Icon.UserSolid width="16" height="16" /></span>
          <span className="sd-link-text">Sign in</span>
          <span className="sd-link-arrow"><Icon.ArrowRight width="14" height="14" /></span>
        </a>
      )}
    </div>
  </>
);

// ── Claim/verified badge ─────────────────────────────────────
const VerifyBadge = ({ hostel }) => (
  <VerifiedListingBadge hostel={hostel} className="bk-badge" />
);

// ── Hostel card ──────────────────────────────────────────────
const HostelCard = ({ h }) => {
  const img = h.cover_image_url || '';
  const price = num(h.price);
  const left = h.available_rooms;
  return (
    <a className="bk-card" href={'#/hostel/' + encodeURIComponent(h.id)}>
      <div
        className={'bk-card-media' + (img ? '' : ' noimg')}
        style={img ? { backgroundImage: `linear-gradient(180deg, rgba(0,0,0,.05), rgba(0,0,0,.5)), url("${img}")` } : undefined}
      >
        <div className="bk-card-badges">
          <VerifyBadge hostel={h} />
          {truthy(h.ac) && <span className="bk-badge dark">AC</span>}
        </div>
        {Number.isFinite(left) && left > 0 && left <= 3 && (
          <span className="bk-card-left">{left} left</span>
        )}
      </div>
      <div className="bk-card-body">
        <div className="bk-card-name">{h.name || 'Hostel'}</div>
        <div className="bk-card-meta">
          <Icon.Pin width="12" height="12" /> {compactAddress(h.area || h.location)}
        </div>
        <div className="bk-card-row">
          <div className="bk-card-price">
            {price ? API.money(price) : 'Price on request'}
            {price ? <small>/mo</small> : null}
          </div>
          <div className="bk-card-rating">
            <Icon.Star width="13" height="13" /> {num(h.rating) > 0 ? num(h.rating).toFixed(1) : 'New'}
          </div>
        </div>
      </div>
    </a>
  );
};

// ── Browse ───────────────────────────────────────────────────
const BrowseView = () => {
  const { notify } = useApp();
  const [hostels, setHostels] = useState(cache.hostels || []);
  const [loading, setLoading] = useState(!cache.hostels);
  const [q, setQ] = useState('');
  const [filters, setFilters] = useState({ verified: false, ac: false, under1500: false });

  useEffect(() => {
    if (cache.hostels) return;
    let alive = true;
    API.fetchHostels()
      .then((rows) => { if (!alive) return; cache.hostels = rows; setHostels(rows); })
      .catch(() => notify('Could not load hostels', 'Please check your connection and retry.', true))
      .finally(() => alive && setLoading(false));
    return () => { alive = false; };
  }, []);

  const toggle = (k) => setFilters((f) => ({ ...f, [k]: !f[k] }));

  const filtered = hostels.filter((h) => {
    if (q) {
      const blob = [h.name, h.area, h.location, h.college, h.room_type, h.display_room_type]
        .filter(Boolean).join(' ').toLowerCase();
      if (blob.indexOf(q.toLowerCase()) === -1) return false;
    }
    if (filters.verified && !isVerifiedListing(h)) return false;
    if (filters.ac && !truthy(h.ac)) return false;
    if (filters.under1500 && num(h.price) >= 1500) return false;
    return true;
  });

  const chips = [
    ['verified', 'Verified only'],
    ['ac', 'AC rooms'],
    ['under1500', 'Under ₹1,500'],
  ];

  return (
    <main className="bk-page">
      <div className="bk-hero">
        <div className="bk-hero-glow" />
        <span className="bk-eyebrow"><Icon.Sparkle width="12" height="12" /> Book online · pay securely</span>
        <h1 className="bk-hero-title">Find your stay, <span className="accent">book in minutes.</span></h1>
        <p className="bk-hero-sub">Browse verified hostels, compare rooms, and confirm instantly with Razorpay.</p>
        <div className="bk-searchbar">
          <Icon.Search width="18" height="18" />
          <input
            type="text"
            placeholder="Search by hostel, area, or college…"
            value={q}
            onChange={(e) => setQ(e.target.value)}
          />
          <span className="bk-searchbar-loc"><Icon.Pin width="14" height="14" /> Lucknow, UP</span>
        </div>
        <div className="bk-chips">
          {chips.map(([k, label]) => (
            <button key={k} className={'bk-chip' + (filters[k] ? ' on' : '')} onClick={() => toggle(k)}>
              {label}
              {filters[k] && <Icon.X width="11" height="11" />}
            </button>
          ))}
        </div>
      </div>

      <div className="bk-results-head">
        <span>{loading ? 'Loading hostels…' : `${filtered.length} hostel${filtered.length === 1 ? '' : 's'} available`}</span>
      </div>

      {loading ? (
        <div className="bk-grid">
          {[0, 1, 2, 3, 4, 5].map((i) => <div key={i} className="bk-card skeleton" />)}
        </div>
      ) : filtered.length === 0 ? (
        <div className="bk-empty">
          <Icon.Search width="28" height="28" />
          <p>No hostels match your search. Try clearing a filter.</p>
        </div>
      ) : (
        <div className="bk-grid">
          {filtered.map((h) => <HostelCard key={h.id} h={h} />)}
        </div>
      )}
    </main>
  );
};

// ── Image gallery ────────────────────────────────────────────
const Gallery = ({ images }) => {
  const [active, setActive] = useState(0);
  if (!images.length) {
    return <div className="bk-gallery-main noimg"><Icon.Photo width="34" height="34" /></div>;
  }
  return (
    <div className="bk-gallery">
      <div className="bk-gallery-main" style={{ backgroundImage: `url("${images[active]}")` }} />
      {images.length > 1 && (
        <div className="bk-gallery-thumbs">
          {images.slice(0, 8).map((u, i) => (
            <button
              key={i}
              className={'bk-gallery-thumb' + (i === active ? ' on' : '')}
              style={{ backgroundImage: `url("${u}")` }}
              onClick={() => setActive(i)}
              aria-label={'Photo ' + (i + 1)}
            />
          ))}
        </div>
      )}
    </div>
  );
};

// ── Room type deck ───────────────────────────────────────────
const RoomDeck = ({ rooms, selectedId, onSelect }) => (
  <div className="bk-roomdeck">
    {rooms.map((r) => {
      const id = r.id || r.name;
      const price = r.final_price || r.price || 0;
      const left = r.available_rooms;
      const soldOut = left != null && left <= 0;
      const on = selectedId === id;
      return (
        <button
          key={id}
          className={'bk-roomcard' + (on ? ' on' : '') + (soldOut ? ' sold' : '')}
          onClick={() => !soldOut && onSelect(r)}
          disabled={soldOut}
        >
          <div className="bk-roomcard-top">
            <Icon.Bed width="18" height="18" />
            {on && <Icon.Check width="16" height="16" />}
          </div>
          <div className="bk-roomcard-name">{r.name || 'Room'}</div>
          <div className="bk-roomcard-price">{API.money(price)}<small>/mo</small></div>
          {left != null && (
            <div className={'bk-roomcard-left' + (soldOut ? ' full' : left <= 2 ? ' low' : '')}>
              {soldOut ? 'Full' : `${left} room${left === 1 ? '' : 's'} left`}
            </div>
          )}
        </button>
      );
    })}
  </div>
);

// ── Detail ───────────────────────────────────────────────────
const DetailView = ({ id }) => {
  const { notify, setSelection } = useApp();
  const [hostel, setHostel] = useState(cache.detail[id] || null);
  const [rooms, setRooms] = useState(cache.rooms[id] || null);
  const [loading, setLoading] = useState(!cache.detail[id]);
  const [picked, setPicked] = useState(null);

  useEffect(() => {
    let alive = true;
    if (!cache.detail[id]) setLoading(true);
    Promise.all([
      cache.detail[id] ? Promise.resolve(cache.detail[id]) : API.fetchHostelDetail(id),
      cache.rooms[id] ? Promise.resolve(cache.rooms[id]) : API.fetchRoomTypes(id),
    ])
      .then(([h, r]) => {
        if (!alive) return;
        cache.detail[id] = h; cache.rooms[id] = r || [];
        setHostel(h); setRooms(r || []);
        // Pre-select first available room type.
        const firstAvail = (r || []).find((x) => x.available_rooms == null || x.available_rooms > 0);
        setPicked(firstAvail || (r || [])[0] || null);
      })
      .catch(() => notify('Could not load hostel', 'Please try again.', true))
      .finally(() => alive && setLoading(false));
    return () => { alive = false; };
  }, [id]);

  if (loading) return <main className="bk-page"><div className="bk-detail-skel" /></main>;
  if (!hostel) return (
    <main className="bk-page">
      <div className="bk-empty"><Icon.Eye width="28" height="28" /><p>This hostel is no longer available.</p>
        <a className="bk-cta-btn" href="#/">Back to browse</a></div>
    </main>
  );

  const images = API.parseImages(hostel.images);
  if (hostel.cover_image_url && images.indexOf(hostel.cover_image_url) === -1) images.unshift(hostel.cover_image_url);
  const tags = Array.isArray(hostel.tags) ? hostel.tags : [];
  const amenities = Array.isArray(hostel.amenities) ? hostel.amenities : [];
  const reqDocs = Array.isArray(hostel.required_documents) ? hostel.required_documents : [];
  const roomList = rooms || [];
  // Detail page has no duration selector — show a per-month starting price.
  const fees = feeBreakdown(hostel, picked, 1);
  const anyAvailable = roomList.some((r) => r.available_rooms == null || r.available_rooms > 0) || roomList.length === 0;

  const book = () => {
    if (picked && picked.available_rooms != null && picked.available_rooms <= 0) {
      notify('Room full', 'Please pick another room type.', true);
      return;
    }
    setSelection({
      hostelId: id,
      roomTypeId: picked ? (picked.id || picked.name) : null,
      months: 6,
      moveIn: toInputDate(new Date()),
      sharing: SHARING_OPTIONS[1],
      roomChoice: null,
    });
    navigate('#/book/' + encodeURIComponent(id));
  };

  return (
    <main className="bk-page bk-detail">
      <a className="bk-back" href="#/"><Icon.ArrowLeft width="15" height="15" /> All hostels</a>
      <Gallery images={images} />

      <div className="bk-detail-grid">
        <div className="bk-detail-main">
          <div className="bk-detail-head">
            <div className="bk-detail-headmain">
              <div className="bk-detail-titlerow">
                <h1 className="bk-detail-title">{hostel.name}</h1>
                <VerifyBadge hostel={hostel} />
              </div>
              <div className="bk-detail-meta">
                <span><Icon.Pin width="13" height="13" /> {hostel.location || hostel.area || 'Lucknow'}</span>
                {hostel.nearby_college && <span><Icon.Building width="13" height="13" /> {hostel.nearby_college}</span>}
              </div>
            </div>
            <div className="bk-detail-rating">
              <Icon.Star width="15" height="15" />
              <b>{num(hostel.rating) > 0 ? num(hostel.rating).toFixed(1) : 'New'}</b>
              {num(hostel.reviews) > 0 && <small>· {num(hostel.reviews)} reviews</small>}
            </div>
          </div>

          {(hostel.hostel_type || hostel.property_type || tags.length > 0) && (
            <div className="bk-detail-tagrow">
              {hostel.hostel_type && <span className="bk-pill">{docLabel(hostel.hostel_type)}</span>}
              {hostel.property_type && <span className="bk-pill">{docLabel(hostel.property_type)}</span>}
              {tags.slice(0, 6).map((t, i) => <span key={i} className="bk-pill ghost">{t}</span>)}
            </div>
          )}

          {hostel.description && (
            <section className="bk-section">
              <h2 className="bk-section-title">About this hostel</h2>
              <p className="bk-detail-desc">{hostel.description}</p>
            </section>
          )}

          <section className="bk-section">
            <h2 className="bk-section-title"><Icon.Bed width="16" height="16" /> Choose a room</h2>
            {roomList.length === 0 ? (
              <div className="bk-soft">Room details will be confirmed by the owner during allotment.</div>
            ) : (
              <RoomDeck rooms={roomList} selectedId={picked ? (picked.id || picked.name) : null} onSelect={setPicked} />
            )}
          </section>

          {amenities.length > 0 && (
            <section className="bk-section">
              <h2 className="bk-section-title"><Icon.Sparkle width="16" height="16" /> Amenities</h2>
              <div className="bk-amenities">
                {amenities.slice(0, 18).map((a, i) => (
                  <span key={i} className="bk-amenity"><Icon.Check width="12" height="12" /> {docLabel(a)}</span>
                ))}
              </div>
            </section>
          )}

          {reqDocs.length > 0 && (
            <section className="bk-section">
              <h2 className="bk-section-title"><Icon.Shield width="16" height="16" /> Documents to carry</h2>
              <div className="bk-amenities">
                {reqDocs.map((d, i) => (
                  <span key={i} className="bk-amenity"><Icon.Flag width="12" height="12" /> {docLabel(d)}</span>
                ))}
              </div>
            </section>
          )}
        </div>

        {/* Sticky booking card */}
        <aside className="bk-detail-aside">
          <div className="bk-bookcard">
            <div className="bk-bookcard-price">
              {API.money(fees.monthly)}<small>/month</small>
            </div>
            {picked && <div className="bk-bookcard-room"><Icon.Bed width="13" height="13" /> {picked.name}</div>}
            <div className="bk-bookcard-lines">
              {fees.deposit > 0 && <div><span>Security deposit</span><b>{API.money(fees.deposit)}</b></div>}
              {fees.maintenance > 0 && <div><span>Maintenance /mo</span><b>{API.money(fees.maintenance)}</b></div>}
            </div>
            <div className="bk-bookcard-upfront">
              <span>From (1 month, before fees)</span>
              <b>{API.money(fees.base)}</b>
            </div>
            <button className="bk-pay-btn" onClick={book} disabled={!anyAvailable}>
              {anyAvailable ? <>Book now <Icon.ArrowRight width="15" height="15" /></> : 'Fully booked'}
            </button>
            <div className="bk-bookcard-note">
              <Icon.Lock width="12" height="12" /> Choose your duration next — total scales with months (incl. 2% gateway + 5% platform fee).
            </div>
          </div>
        </aside>
      </div>
    </main>
  );
};

// ── Picker row (modal-free inline select via <details>-like chips) ─
const ChoiceRow = ({ icon, label, value, children }) => (
  <div className="bk-choicerow">
    <div className="bk-choicerow-label">{icon} {label}</div>
    <div className="bk-choicerow-value">{children || value}</div>
  </div>
);

// ── Booking / payment summary ────────────────────────────────
const BookingView = ({ id }) => {
  const { notify, user, selection, setSelection } = useApp();
  const [hostel, setHostel] = useState(cache.detail[id] || null);
  const [rooms, setRooms] = useState(cache.rooms[id] || null);
  const [loading, setLoading] = useState(!cache.detail[id]);
  const [paying, setPaying] = useState(false);

  // local editable selection (falls back to sensible defaults)
  const sel = selection && selection.hostelId === id ? selection : null;
  const [roomId, setRoomId] = useState(sel ? sel.roomTypeId : null);
  const [months, setMonths] = useState(sel ? sel.months : 6);
  const [moveIn, setMoveIn] = useState(sel ? sel.moveIn : toInputDate(new Date()));
  const [sharing, setSharing] = useState(sel ? sel.sharing : SHARING_OPTIONS[1]);
  const [roomChoice, setRoomChoice] = useState(sel ? sel.roomChoice : null);

  // A resumed selection (after the login redirect) may arrive AFTER this view
  // mounts — sync the editable fields when it does.
  useEffect(() => {
    if (selection && selection.hostelId === id) {
      if (selection.roomTypeId) setRoomId(selection.roomTypeId);
      if (selection.months) setMonths(selection.months);
      if (selection.moveIn) setMoveIn(selection.moveIn);
      if (selection.sharing) setSharing(selection.sharing);
      if (selection.roomChoice !== undefined) setRoomChoice(selection.roomChoice);
    }
    // eslint-disable-next-line
  }, [selection, id]);

  useEffect(() => {
    let alive = true;
    if (!cache.detail[id]) setLoading(true);
    Promise.all([
      cache.detail[id] ? Promise.resolve(cache.detail[id]) : API.fetchHostelDetail(id),
      cache.rooms[id] ? Promise.resolve(cache.rooms[id]) : API.fetchRoomTypes(id),
    ])
      .then(([h, r]) => {
        if (!alive) return;
        cache.detail[id] = h; cache.rooms[id] = r || [];
        setHostel(h); setRooms(r || []);
        if (!roomId) {
          const first = (r || []).find((x) => x.available_rooms == null || x.available_rooms > 0) || (r || [])[0];
          if (first) setRoomId(first.id || first.name);
        }
      })
      .catch(() => notify('Could not load booking', 'Please try again.', true))
      .finally(() => alive && setLoading(false));
    return () => { alive = false; };
  }, [id]);

  if (loading) return <main className="bk-page"><div className="bk-detail-skel" /></main>;
  if (!hostel) return <main className="bk-page"><div className="bk-empty"><p>Hostel unavailable.</p><a className="bk-cta-btn" href="#/">Browse</a></div></main>;

  const roomList = rooms || [];
  const room = roomList.find((r) => (r.id || r.name) === roomId) || roomList[0] || null;
  const fees = feeBreakdown(hostel, room, months);
  const floorRooms = API.parseFloorRooms(hostel.floor_room_numbers);
  // show only rooms whose type matches the chosen room type (tolerant)
  const visibleChoices = (() => {
    const t = String((room && room.name) || '').trim().toLowerCase();
    const hasTyped = floorRooms.some((c) => c.roomType.trim());
    if (!hasTyped || !t) return floorRooms;
    return floorRooms.filter((c) => {
      const ct = c.roomType.trim().toLowerCase();
      return ct && (ct === t || ct.indexOf(t) !== -1 || t.indexOf(ct) !== -1);
    });
  })();

  const persist = (patch) => {
    const next = {
      hostelId: id,
      roomTypeId: roomId, months, moveIn, sharing, roomChoice,
      ...patch,
    };
    setSelection(next);
  };

  const pay = async () => {
    if (!room) { notify('Select a room', 'Please choose a room type.', true); return; }
    if (room.available_rooms != null && room.available_rooms <= 0) {
      notify('Room full', 'This room type is fully booked. Pick another.', true); return;
    }
    if (visibleChoices.length && !roomChoice) {
      notify('Select a room', 'Pick your preferred room from the floor map.', true); return;
    }

    // Login gate — only here. Persist selection and bounce to login.
    if (!API.isLoggedIn()) {
      API.savePending({ hostelId: id, roomTypeId: roomId, months, moveIn, sharing, roomChoice });
      const next = 'book.html#/book/' + encodeURIComponent(id);
      location.href = 'login.html?next=' + encodeURIComponent(next);
      return;
    }

    setPaying(true);
    try {
      const order = await API.createOrder({
        hostelId: id,
        hostelName: hostel.name || 'Hostel',
        roomType: (room && room.name) || 'Room',
        baseAmount: fees.base,
        durationMonths: months,
      });
      await API.loadRazorpay();

      const u = user || API.currentUser() || {};
      const meta = u.user_metadata || {};
      const ctx = {
        orderId: order.order_id,
        hostelLocation: hostel.location || hostel.area || '',
        hostelImage: (room && room.image_url) || hostel.cover_image_url || '',
        selectedFloor: roomChoice ? roomChoice.floor : null,
        selectedRoomNumber: roomChoice ? roomChoice.roomNumber : '',
        durationMonths: months,
        moveInDate: new Date(moveIn + 'T00:00:00').toISOString(),
        preferredSharing: sharing,
      };

      const rzp = new window.Razorpay({
        key: order.key_id,
        order_id: order.order_id,
        amount: order.amount,
        currency: order.currency || 'INR',
        name: hostel.name || 'Hostelova',
        description: ((room && room.name) || 'Room') + ' · ' + months + ' month(s)',
        image: 'assets/logo-128.png',
        prefill: { name: meta.name || '', email: u.email || '', contact: meta.phone || '' },
        notes: { hostel_id: id, room_type: (room && room.name) || '' },
        theme: { color: '#2F5FE3' },
        modal: { ondismiss: () => setPaying(false) },
        handler: async (resp) => {
          try {
            const result = await API.verifyPayment({
              orderId: resp.razorpay_order_id,
              paymentId: resp.razorpay_payment_id,
              signature: resp.razorpay_signature,
              hostelLocation: ctx.hostelLocation,
              hostelImage: ctx.hostelImage,
              selectedFloor: ctx.selectedFloor,
              selectedRoomNumber: ctx.selectedRoomNumber,
              durationMonths: ctx.durationMonths,
              moveInDate: ctx.moveInDate,
              preferredSharing: ctx.preferredSharing,
            });
            API.clearPending();
            // Keep the just-created booking so My Bookings shows it instantly,
            // even if the list query lags behind (replication / RLS).
            cache.lastBooking = (result && result.booking) ? result.booking : {
              id: 'tmp-' + resp.razorpay_payment_id,
              hostel_name: hostel.name, hostel_image: ctx.hostelImage,
              room_type: (room && room.name) || 'Room', duration_months: months,
              total_amount: fees.total, payment_status: 'paid', status: 'active',
              move_in_date: ctx.moveInDate, booked_at: new Date().toISOString(),
            };
            // Wipe cached availability everywhere so the decremented seat
            // count shows on the browse list AND the hostel detail next time.
            delete cache.rooms[id];
            delete cache.detail[id];
            cache.hostels = null;
            setSelection({ done: true, hostelName: hostel.name });
            navigate('#/success');
            notify('Booking confirmed 🎉', 'A receipt has been emailed to you.');
          } catch (e) {
            notify('Verification failed', e.message || 'Please contact support with your payment id.', true);
          } finally {
            setPaying(false);
          }
        },
      });
      rzp.on('payment.failed', (r) => {
        setPaying(false);
        notify('Payment failed', (r && r.error && r.error.description) || 'No money was deducted. Try again.', true);
      });
      rzp.open();
    } catch (e) {
      setPaying(false);
      notify('Could not start payment', e.message || 'Please try again.', true);
    }
  };

  return (
    <main className="bk-page bk-booking">
      <a className="bk-back" href={'#/hostel/' + encodeURIComponent(id)}><Icon.ArrowLeft width="15" height="15" /> Back to hostel</a>
      <div className="bk-booking-grid">
        <div className="bk-booking-main">
          <h1 className="bk-booking-title">Confirm your booking</h1>
          <div className="bk-booking-host"><Icon.Building width="14" height="14" /> {hostel.name}</div>

          {!API.isLoggedIn() && (
            <div className="bk-soft info">
              <Icon.Lock width="14" height="14" /> You'll sign in at the last step to pay securely — your selection is saved.
            </div>
          )}

          <section className="bk-section">
            <h2 className="bk-section-title"><Icon.Bed width="16" height="16" /> Room type</h2>
            {roomList.length === 0 ? (
              <div className="bk-soft">The owner will assign a room during allotment.</div>
            ) : (
              <RoomDeck
                rooms={roomList}
                selectedId={room ? (room.id || room.name) : null}
                onSelect={(r) => { setRoomId(r.id || r.name); setRoomChoice(null); persist({ roomTypeId: r.id || r.name, roomChoice: null }); }}
              />
            )}
          </section>

          {visibleChoices.length > 0 && (
            <section className="bk-section">
              <h2 className="bk-section-title"><Icon.Grid width="16" height="16" /> Pick your room</h2>
              <FloorMap
                choices={visibleChoices}
                selected={roomChoice}
                onSelect={(c) => { setRoomChoice(c); persist({ roomChoice: c }); }}
              />
            </section>
          )}

          <section className="bk-section">
            <h2 className="bk-section-title"><Icon.Calendar width="16" height="16" /> Stay preference</h2>
            <div className="bk-prefs">
              <label className="bk-field">
                <span>Move-in date</span>
                <input
                  type="date"
                  value={moveIn}
                  min={toInputDate(new Date())}
                  onChange={(e) => { setMoveIn(e.target.value); persist({ moveIn: e.target.value }); }}
                />
              </label>
              <label className="bk-field">
                <span>Duration</span>
                <select value={months} onChange={(e) => { const m = Number(e.target.value); setMonths(m); persist({ months: m }); }}>
                  {DURATIONS.map((d) => <option key={d.months} value={d.months}>{d.label}</option>)}
                </select>
              </label>
              <label className="bk-field">
                <span>Sharing</span>
                <select value={sharing} onChange={(e) => { setSharing(e.target.value); persist({ sharing: e.target.value }); }}>
                  {SHARING_OPTIONS.map((s) => <option key={s} value={s}>{s}</option>)}
                </select>
              </label>
            </div>
          </section>
        </div>

        {/* Payment summary */}
        <aside className="bk-booking-aside">
          <div className="bk-summary">
            <div className="bk-summary-title">Payment summary</div>
            <div className="bk-summary-room">
              <Icon.Bed width="14" height="14" /> {(room && room.name) || 'Room'} · {months} month(s)
            </div>
            <div className="bk-summary-lines">
              <div><span>Room rent ({months} × {API.money(fees.monthly)})</span><b>{API.money(fees.rentForStay)}</b></div>
              {fees.deposit > 0 && <div><span>Security deposit</span><b>{API.money(fees.deposit)}</b></div>}
              {fees.maintenanceForStay > 0 && <div><span>Maintenance ({months} mo)</span><b>{API.money(fees.maintenanceForStay)}</b></div>}
              <div className="muted"><span>Payment gateway (2%)</span><b>{API.money(fees.gateway)}</b></div>
              <div className="muted"><span>Platform fee (5%)</span><b>{API.money(fees.platform)}</b></div>
            </div>
            <div className="bk-summary-total">
              <span>Total payable</span>
              <b>{API.money(fees.total)}</b>
            </div>
            <button className="bk-pay-btn lg" onClick={pay} disabled={paying}>
              {paying ? <><span className="bk-spin" /> Processing…</> : <><Icon.Lock width="15" height="15" /> Pay {API.money(fees.total)}</>}
            </button>
            <div className="bk-summary-secure">
              <img src="https://razorpay.com/favicon.png" alt="" width="14" height="14" onError={(e) => { e.target.style.display = 'none'; }} />
              Secured by Razorpay · UPI · Cards · Netbanking
            </div>
          </div>
        </aside>
      </div>
    </main>
  );
};

// ── Floor map (seat selection) ───────────────────────────────
const FloorMap = ({ choices, selected, onSelect }) => {
  const byFloor = {};
  choices.forEach((c) => { (byFloor[c.floor] = byFloor[c.floor] || []).push(c); });
  const floors = Object.keys(byFloor).map(Number).sort((a, b) => a - b);
  return (
    <div className="bk-floormap">
      {floors.map((f) => (
        <div key={f} className="bk-floor">
          <div className="bk-floor-head">
            <span>Floor {f}</span>
            <span className="bk-floor-count">{byFloor[f].length} rooms</span>
          </div>
          <div className="bk-seats">
            {byFloor[f].sort((a, b) => a.roomNumber.localeCompare(b.roomNumber, undefined, { numeric: true })).map((c) => {
              const on = selected && selected.floor === c.floor && selected.roomNumber === c.roomNumber;
              return (
                <button key={c.floor + '::' + c.roomNumber} className={'bk-seat' + (on ? ' on' : '')} onClick={() => onSelect(c)}>
                  <Icon.Grid width="13" height="13" />
                  <span>{c.roomNumber}</span>
                </button>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
};

// ── Booking detail modal ─────────────────────────────────────
const Row = ({ icon, label, value }) =>
  value ? (
    <div className="bk-bdm-row">
      <span className="bk-bdm-label">{icon} {label}</span>
      <span className="bk-bdm-value">{value}</span>
    </div>
  ) : null;

const BookingDetailModal = ({ booking, onClose, onCancelled }) => {
  const { notify } = useApp();
  const [confirm, setConfirm] = useState(false);
  const [cancelling, setCancelling] = useState(false);
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, []);
  if (!booking) return null;
  const b = booking;
  const paid = isPaid(b);
  const isCancelled = String(b.status || '').toLowerCase() === 'cancelled';
  const cancellable = paid && !isCancelled;
  const room = [b.selected_floor != null ? 'Floor ' + b.selected_floor : '', b.selected_room_number ? 'Room ' + b.selected_room_number : '']
    .filter(Boolean).join(' · ');

  const doCancel = async () => {
    setCancelling(true);
    try {
      const res = await API.cancelBooking(b.id);
      notify('Booking cancelled', 'Refund of ' + API.money(res.refund_amount || 0) + ' initiated to your source.');
      if (onCancelled) onCancelled(b.id, res.refund_amount || 0);
      onClose();
    } catch (e) {
      setCancelling(false);
      notify('Could not cancel', e.message || 'Please try again or contact support.', true);
    }
  };
  return (
    <div className="bk-modal-backdrop" onMouseDown={onClose}>
      <div className="bk-modal" role="dialog" aria-modal="true" onMouseDown={(e) => e.stopPropagation()}>
        <button className="bk-modal-close" onClick={onClose} aria-label="Close"><Icon.X width="18" height="18" /></button>
        <div className="bk-bdm-hero" style={b.hostel_image ? { backgroundImage: `linear-gradient(180deg, rgba(0,0,0,.1), rgba(0,0,0,.6)), url("${b.hostel_image}")` } : undefined}>
          {!b.hostel_image && <Icon.Building width="30" height="30" />}
          <div className="bk-bdm-hero-info">
            <span className={'bk-status ' + (paid ? 'paid' : 'pending')}>{paid ? 'Paid' : (b.status || 'Pending')}</span>
            <h2>{b.hostel_name || 'Hostel'}</h2>
            {b.hostel_location && <p><Icon.Pin width="12" height="12" /> {b.hostel_location}</p>}
          </div>
        </div>

        <div className="bk-bdm-body">
          {b.booking_code && (
            <div className="bk-bdm-code">
              <span>Booking code</span>
              <b>{b.booking_code}</b>
            </div>
          )}
          <div className="bk-bdm-grid">
            <Row icon={<Icon.Bed width="13" height="13" />} label="Room type" value={b.room_type} />
            <Row icon={<Icon.Grid width="13" height="13" />} label="Room" value={room} />
            <Row icon={<Icon.Calendar width="13" height="13" />} label="Move-in" value={fmtISO(b.move_in_date)} />
            <Row icon={<Icon.Calendar width="13" height="13" />} label="Duration" value={b.duration_months ? b.duration_months + ' month(s)' : ''} />
            <Row icon={<Icon.Users width="13" height="13" />} label="Sharing" value={b.preferred_sharing} />
            <Row icon={<Icon.Check width="13" height="13" />} label="Booked on" value={fmtISO(b.booked_at || b.created_at)} />
          </div>

          <div className="bk-bdm-paid">
            <div>
              <span>Amount paid</span>
              <b>{b.total_amount ? API.money(b.total_amount) : '—'}</b>
            </div>
            <div className="bk-bdm-paid-meta">
              {b.payment_method && <span>via {b.payment_method}</span>}
              {paid && <span className="ok"><Icon.Check width="11" height="11" /> Receipt emailed</span>}
            </div>
          </div>

          {isCancelled && (
            <div className="bk-bdm-cancelled">
              <Icon.Flag width="14" height="14" /> Cancelled{b.refund_amount ? ` · ${API.money(b.refund_amount)} refunded` : ''}
            </div>
          )}

          <a className="bk-pay-btn" href={'#/hostel/' + encodeURIComponent(b.hostel_id || '')} onClick={onClose} style={{ textDecoration: 'none' }}>
            View hostel <Icon.ArrowRight width="15" height="15" />
          </a>

          {cancellable && (confirm ? (
            <div className="bk-bdm-confirm">
              <p>Cancel this booking? The booking amount is refunded to your original payment method — the 2% gateway + 5% platform service fees are non-refundable. Your room is freed for others.</p>
              <div className="bk-bdm-confirm-row">
                <button className="bk-ghost-btn" onClick={() => setConfirm(false)} disabled={cancelling}>Keep booking</button>
                <button className="bk-ghost-btn danger" onClick={doCancel} disabled={cancelling}>
                  {cancelling ? <><span className="bk-spin" /> Cancelling…</> : 'Yes, cancel'}
                </button>
              </div>
            </div>
          ) : (
            <button className="bk-cancel-btn" onClick={() => setConfirm(true)}>Cancel booking</button>
          ))}
        </div>
      </div>
    </div>
  );
};

// ── My bookings ──────────────────────────────────────────────
// Merge any list rows with the booking we just created (cache.lastBooking),
// de-duped by id, newest first — so a fresh booking always shows even if the
// list query lags or RLS is slow to propagate.
const mergeBookings = (rows) => {
  const list = Array.isArray(rows) ? rows.slice() : [];
  const lb = cache.lastBooking;
  if (lb && !list.some((b) => String(b.id) === String(lb.id))) list.unshift(lb);
  return list;
};

const BookingsView = () => {
  const { notify } = useApp();
  const [bookings, setBookings] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(false);
  const [active, setActive] = useState(null);

  const load = useCallback((silent) => {
    if (!silent) { setLoading(true); setError(false); }
    let cancelled = false;
    API.fetchMyBookings()
      .then(async (rows) => {
        let list = rows || [];
        // Replication can lag a beat right after paying — retry once.
        if (list.length === 0 && cache.lastBooking) {
          await new Promise((r) => setTimeout(r, 1500));
          try { list = (await API.fetchMyBookings()) || []; } catch (e) {}
        }
        if (!cancelled) { setBookings(mergeBookings(list)); setError(false); }
      })
      .catch(() => {
        if (cancelled) return;
        // Even on error, show the just-booked one rather than a blank screen.
        if (cache.lastBooking) setBookings(mergeBookings([]));
        else { setError(true); notify('Could not load bookings', 'Tap refresh to retry.', true); }
      })
      .finally(() => { if (!cancelled) setLoading(false); });
    return () => { cancelled = true; };
  }, [notify]);

  useEffect(() => {
    if (!API.isLoggedIn()) {
      location.href = 'login.html?next=' + encodeURIComponent('book.html#/bookings');
      return;
    }
    return load(false);
  }, [load]);

  if (loading) return <main className="bk-page"><div className="bk-detail-skel short" /></main>;

  return (
    <main className="bk-page">
      <div className="bk-page-head">
        <h1 className="bk-page-title">My bookings</h1>
        <button className="bk-ghost-btn" onClick={() => load(false)} aria-label="Refresh">
          <Icon.ArrowRight width="14" height="14" /> Refresh
        </button>
      </div>
      {!bookings || bookings.length === 0 ? (
        <div className="bk-empty">
          <Icon.Bookmark width="28" height="28" />
          <p>{error ? 'Could not load your bookings.' : 'No bookings yet. Find a hostel and confirm your stay.'}</p>
          {error
            ? <button className="bk-cta-btn" onClick={() => load(false)}>Try again</button>
            : <a className="bk-cta-btn" href="#/">Browse hostels</a>}
        </div>
      ) : (
        <div className="bk-booking-list">
          {bookings.map((b) => (
            <button key={b.id} type="button" className="bk-booking-item" onClick={() => setActive(b)}>
              <div className="bk-booking-item-media" style={b.hostel_image ? { backgroundImage: `url("${b.hostel_image}")` } : undefined}>
                {!b.hostel_image && <Icon.Building width="22" height="22" />}
              </div>
              <div className="bk-booking-item-body">
                <div className="bk-booking-item-name">{b.hostel_name || 'Hostel'}</div>
                <div className="bk-booking-item-meta">
                  {b.room_type && <span><Icon.Bed width="12" height="12" /> {b.room_type}</span>}
                  {b.duration_months ? <span><Icon.Calendar width="12" height="12" /> {b.duration_months} mo</span> : null}
                </div>
                <div className="bk-booking-item-row">
                  <span className={'bk-status ' + (isPaid(b) ? 'paid' : 'pending')}>
                    {isPaid(b) ? 'Paid' : (b.status || 'Pending')}
                  </span>
                  {b.total_amount ? <b>{API.money(b.total_amount)}</b> : null}
                </div>
              </div>
              <span className="bk-booking-item-chev"><Icon.ArrowRight width="16" height="16" /></span>
            </button>
          ))}
        </div>
      )}
      {active && (
        <BookingDetailModal
          booking={active}
          onClose={() => setActive(null)}
          onCancelled={(id, refund) => {
            setBookings((list) => (list || []).map((x) =>
              String(x.id) === String(id)
                ? { ...x, status: 'cancelled', payment_status: 'refunded', refund_amount: refund }
                : x));
            if (cache.lastBooking && String(cache.lastBooking.id) === String(id)) cache.lastBooking = null;
            // Availability changed (seat freed) — refresh caches.
            cache.hostels = null; cache.detail = {}; cache.rooms = {};
          }}
        />
      )}
    </main>
  );
};

// ── Profile ──────────────────────────────────────────────────
const ProfileView = () => {
  const { user } = useApp();
  useEffect(() => {
    if (!API.isLoggedIn()) {
      location.href = 'login.html?next=' + encodeURIComponent('book.html#/profile');
    }
  }, []);
  const u = user || API.currentUser() || {};
  const meta = u.user_metadata || {};
  const name = (meta.name || '').trim();
  const initial = (name || u.email || '?').trim().charAt(0).toUpperCase();
  const memberSince = u.created_at ? fmtISO(u.created_at) : '';

  return (
    <main className="bk-page bk-profile">
      <h1 className="bk-page-title">My profile</h1>
      <div className="bk-profile-card">
        <div className="bk-profile-head">
          <div className="bk-profile-avatar">{initial}</div>
          <div className="bk-profile-id">
            <div className="bk-profile-name">{name || 'Hostelova user'}</div>
            <div className="bk-profile-email">{u.email || ''}</div>
          </div>
        </div>

        <div className="bk-profile-rows">
          <div className="bk-bdm-row">
            <span className="bk-bdm-label"><Icon.UserSolid width="13" height="13" /> Full name</span>
            <span className="bk-bdm-value">{name || '—'}</span>
          </div>
          <div className="bk-bdm-row">
            <span className="bk-bdm-label"><Icon.Mail width="13" height="13" /> Email</span>
            <span className="bk-bdm-value">{u.email || '—'}</span>
          </div>
          <div className="bk-bdm-row">
            <span className="bk-bdm-label"><Icon.Phone width="13" height="13" /> Mobile</span>
            <span className="bk-bdm-value">{meta.phone || '—'}</span>
          </div>
          {memberSince && (
            <div className="bk-bdm-row">
              <span className="bk-bdm-label"><Icon.Calendar width="13" height="13" /> Member since</span>
              <span className="bk-bdm-value">{memberSince}</span>
            </div>
          )}
        </div>

        <div className="bk-profile-actions">
          <a className="bk-pay-btn" href="#/bookings" style={{ textDecoration: 'none' }}>
            <Icon.Bookmark width="15" height="15" /> View my bookings
          </a>
          <button className="bk-ghost-btn danger" onClick={() => { API.logout(); location.href = 'book.html#/'; }}>
            Sign out
          </button>
        </div>
      </div>
    </main>
  );
};

// ── Payment success ──────────────────────────────────────────
const SuccessView = () => {
  const b = cache.lastBooking;
  useEffect(() => {
    window.scrollTo(0, 0);
    if (!b) navigate('#/bookings');
  }, []);
  if (!b) return null;
  const u = API.currentUser() || {};
  return (
    <main className="bk-page bk-success">
      <div className="bk-success-card">
        <div className="bk-success-ico"><Icon.Check width="34" height="34" /></div>
        <h1 className="bk-success-title">Booking confirmed 🎉</h1>
        <p className="bk-success-sub">Your stay at <b>{b.hostel_name || 'the hostel'}</b> is booked.</p>

        {b.booking_code && (
          <div className="bk-success-code">
            <span>Booking code</span>
            <b>{b.booking_code}</b>
          </div>
        )}

        <div className="bk-success-lines">
          {b.room_type && <div><span>Room</span><b>{b.room_type}</b></div>}
          {b.duration_months ? <div><span>Duration</span><b>{b.duration_months} month(s)</b></div> : null}
          {b.move_in_date && <div><span>Move-in</span><b>{fmtISO(b.move_in_date)}</b></div>}
          {b.total_amount ? <div className="total"><span>Paid</span><b>{API.money(b.total_amount)}</b></div> : null}
        </div>

        <div className="bk-success-mail">
          <Icon.Mail width="14" height="14" /> Receipt emailed{u.email ? ' to ' + u.email : ''}
        </div>

        <div className="bk-success-actions">
          <a className="bk-pay-btn" href="#/bookings" style={{ textDecoration: 'none' }}>
            <Icon.Bookmark width="15" height="15" /> View my bookings
          </a>
          <a className="bk-ghost-btn" href="#/" style={{ justifyContent: 'center', height: '46px' }}>
            Browse more hostels
          </a>
        </div>
      </div>
    </main>
  );
};

// ── Root app ─────────────────────────────────────────────────
const BookApp = () => {
  const [theme, setTheme] = useTheme();
  const [route, setRoute] = useState(parseRoute());
  const [user, setUser] = useState(API.currentUser());
  const [selection, setSelection] = useState(null);
  const [toasts, setToasts] = useState([]);

  const notify = useCallback((title, body, error) => {
    const id = Date.now() + Math.random();
    setToasts((t) => [...t, { id, title, body, error }]);
    setTimeout(() => setToasts((t) => t.filter((x) => x.id !== id)), 4200);
  }, []);

  useEffect(() => {
    const onHash = () => { setRoute(parseRoute()); window.scrollTo(0, 0); };
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  // Resume a pending booking after returning from login.
  useEffect(() => {
    if (!API.isLoggedIn()) return;
    const pending = API.loadPending();
    if (pending && pending.hostelId) {
      setSelection(pending);
      // Consume it once — payment success clears it too, but this stops a
      // re-trigger if the user abandons checkout and revisits later.
      API.clearPending();
      if (route.name !== 'book') navigate('#/book/' + encodeURIComponent(pending.hostelId));
      notify('Welcome back', 'Continue your payment to confirm the booking.');
    }
    // eslint-disable-next-line
  }, []);

  let view;
  if (route.name === 'detail') view = <DetailView id={route.id} />;
  else if (route.name === 'book') view = <BookingView id={route.id} />;
  else if (route.name === 'bookings') view = <BookingsView />;
  else if (route.name === 'profile') view = <ProfileView />;
  else if (route.name === 'success') view = <SuccessView />;
  else view = <BrowseView />;

  return (
    <AppCtx.Provider value={{ notify, user, setUser, selection, setSelection }}>
      <div className="bk-shell">
        {/* Same Nav + Footer as the landing — reused from site-chrome.jsx */}
        <Nav
          theme={theme}
          setTheme={setTheme}
          rightActions={<BookNavActions user={user} activeProfile={route.name === 'profile'} />}
          drawerActions={<BookDrawerActions user={user} />}
        />
        {view}
        <Footer />
      </div>
      <Toasts items={toasts} />
    </AppCtx.Provider>
  );
};

ReactDOM.createRoot(document.getElementById('book-root')).render(<BookApp />);
