// Public pages — content driven by /api/content, narrative copy kept inline
const { useState: useStateP, useEffect: useEffectP, useRef: useRefP } = React;

// Hook to read site content from the App-level context
const useSite = () => (window.useContent ? window.useContent() : null) || { settings: {}, reviews: [], menu_cards: [], gallery: [], pages: [], widgets: [] };

// Resolve a photo reference. Accepts:
//   - a full URL → returned as-is
//   - an R2 key → prefixed with /media/
//   - an IMAGES.<key> id → returned from the Wix-hotlinked fallback map
const resolvePhoto = (v, fallback = '') => {
  if (!v) return fallback;
  if (typeof v !== 'string') return fallback;
  if (v.startsWith('http://') || v.startsWith('https://') || v.startsWith('/')) return v;
  if (IMAGES[v]) return IMAGES[v];
  return '/media/' + v;
};

const HomePage = ({ navigate }) => {
  const { settings, reviews } = useSite();
  const bookProps = bookCtaProps(settings);
  const featured = reviews.find(r => r.featured) || reviews[0];
  return (
  <>
    <section className="hero-cinematic">
      <div className="hero-bg">
        <video
          autoPlay muted loop playsInline
          preload="auto"
          poster={IMAGES.hero}
          aria-hidden="true"
          className="hero-video"
        >
          {/* Mobile gets 480p, tablet 720p, desktop 1080p. Browser picks the first it can use. */}
          <source src="/media/gallery/hero-480p.mp4" type="video/mp4" media="(max-width: 640px)"/>
          <source src="/media/gallery/hero-720p.mp4" type="video/mp4" media="(max-width: 1280px)"/>
          <source src="/media/gallery/hero.mp4"      type="video/mp4"/>
        </video>
      </div>
      {/* Soft dark scrim at the top so the white wordmark + transparent nav stay legible
          against bright daylight frames of the hero video. */}
      <div aria-hidden="true" style={{position: 'absolute', top: 0, left: 0, right: 0, height: 180, background: 'linear-gradient(to bottom, rgba(15,35,38,0.55), rgba(15,35,38,0))', pointerEvents: 'none', zIndex: 1}}/>
      <div className="hero-inner">
        <div className="hero-eyebrow">Puerto Vallarta · Since 1999</div>
        <h1 className="hero-title">Arrive and revive.</h1>
        <p className="hero-lead">A private beachfront villa on Playa Punta Negra. Four en-suite bedrooms, private infinity pool, full-time chef, daily housekeeping, and a villa manager on call. Family-owned since 1999.</p>
        <div className="hero-ctas">
          <a className="btn btn-primary" {...bookProps}>Book Your Stay</a>
          <button className="btn-ghost-light" onClick={() => navigate('rooms')}>Tour the villa</button>
        </div>
        <div className="hero-stats">
          <div><div className="stat-num">4.9</div><div className="stat-label">Google reviews</div></div>
          <div><div className="stat-num">4</div><div className="stat-label">Boutique suites</div></div>
          <div><div className="stat-num">1999</div><div className="stat-label">Original owners</div></div>
        </div>
      </div>
      <div className="hero-scroll-indicator">Scroll</div>
    </section>
    {/* "At a glance" strip — the things a prospect wants confirmed before they scroll. */}
    <section style={{padding: '40px 0', borderBottom: '1px solid var(--line-soft)', background: 'var(--bg-warm)'}}>
      <div className="container">
        <div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', gap: 24, alignItems: 'center'}}>
          {[
            ['Sleeps', 'Up to 12'],
            ['Bedrooms', '4 · all en-suite'],
            ['Pool', 'Private infinity'],
            ['Staff', 'Chef · Housekeeping'],
            ['Beach', 'Direct access'],
            ['Rates', 'Per-night on the calendar'],
          ].map(([k, v]) => (
            <div key={k} style={{flex: '1 1 160px', minWidth: 140}}>
              <div className="eyebrow" style={{marginBottom: 6, fontSize: 10}}>{k}</div>
              <div style={{fontFamily: 'var(--serif)', fontSize: 20, fontWeight: 300, letterSpacing: '-0.01em'}}>{v}</div>
            </div>
          ))}
        </div>
      </div>
    </section>
    <section className="section-pullquote">
      <div className="container">
        <div className="pullquote-grid">
          <div className="pullquote-img"><img src={IMAGES.elSol3} alt="Indoor pineapple fountain at the heart of Villa Karaway's central courtyard"/></div>
          <div className="pullquote-text">
            <div className="eyebrow" style={{marginBottom: 28, color: 'var(--terracotta)'}}>The Villa</div>
            <p className="display" style={{fontSize: 'clamp(28px, 3vw, 44px)', lineHeight: 1.25, fontWeight: 300, letterSpacing: '-0.015em'}}>The water comes all the way to the backyard.</p>
            <p className="body" style={{fontSize: 17, marginTop: 24, maxWidth: 520}}>Each boutique bedroom is en suite. An architectural wonder built around a central courtyard fountain, where the sound of the sea permeates every wall.</p>
          </div>
        </div>
      </div>
    </section>
    <section className="section section-warm">
      <div className="container">
        <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 60, flexWrap: 'wrap', gap: 24}}>
          <div>
            <div className="eyebrow" style={{marginBottom: 16}}>Accommodations</div>
            <h2 className="display" style={{fontSize: 'clamp(36px, 4.5vw, 56px)'}}>Four suites, four moods.</h2>
          </div>
          <button className="btn btn-ghost" onClick={() => navigate('rooms')}>View all rooms</button>
        </div>
        <div className="grid-4 suite-grid">
          {[
            { name: 'el Cielo',  sub: 'Master Suite',   img: IMAGES.elCielo1 },
            { name: 'el Sol',    sub: 'Courtyard · 2 Queens', img: IMAGES.elSol1 },
            { name: 'las Nubes', sub: 'King + Queen',   img: IMAGES.nubes1 },
            { name: 'el Mar',    sub: 'Garden · 2 Twin', img: IMAGES.mar1 },
          ].map(r => (
            <div key={r.name} className="suite-card" onClick={() => navigate('rooms')}>
              <div className="suite-photo"><img src={r.img} alt={r.name} loading="lazy"/></div>
              <div className="suite-meta">
                <div className="eyebrow">{r.sub}</div>
                <h3 className="serif">{r.name}</h3>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
    <section className="section">
      <div className="container">
        <div className="grid-2" style={{alignItems: 'center', gap: 80}}>
          <div className="photo" style={{aspectRatio: '4/5'}}><img src={IMAGES.porch1} alt="Covered oceanfront patio with palapa roof at Villa Karaway, Puerto Vallarta"/></div>
          <div>
            <div className="eyebrow" style={{marginBottom: 20}}>The Experience</div>
            <h2 className="display" style={{fontSize: 'clamp(36px, 4vw, 52px)', marginBottom: 28}}>Staff who make you feel like family.</h2>
            <p className="body" style={{marginBottom: 20, fontSize: 17}}>Enrique, Gabriel, and Ana have been the heart of Villa Karaway for years. Five-star cooking on request, fresh-pressed juice at sunrise, margaritas on the front porch at sunset.</p>
            <p className="body" style={{marginBottom: 32, fontSize: 16}}>You'll not feel like a guest, but an owner of the house.</p>
            <button className="btn btn-outline" onClick={() => navigate('staff')}>Meet the staff</button>
          </div>
        </div>
      </div>
    </section>
    <section className="section section-dark">
      <div className="container-narrow text-center">
        <div className="eyebrow" style={{marginBottom: 32, color: 'rgba(250,246,239,0.6)'}}>From our guests</div>
        <p className="display" style={{fontSize: 'clamp(26px, 3vw, 38px)', fontStyle: 'italic', fontWeight: 300, lineHeight: 1.4, color: 'var(--bg)', marginBottom: 40}}>"{featured?.text || 'A private beachfront villa, cared for by a full staff — the kind of place you arrive at and instantly slow down.'}"</p>
        <div className="eyebrow" style={{color: 'var(--sand)'}}>— {featured?.author || 'Guest review'}</div>
        <div style={{marginTop: 48}}><button className="btn btn-light" onClick={() => navigate('reviews')}>Read all reviews</button></div>
      </div>
    </section>
    <section className="section">
      <div className="container">
        <div className="grid-2" style={{alignItems: 'center', gap: 80}}>
          <div>
            <div className="eyebrow" style={{marginBottom: 20}}>Location</div>
            <h2 className="display" style={{fontSize: 'clamp(36px, 4vw, 52px)', marginBottom: 28}}>Playa Punta Negra, Puerto Vallarta.</h2>
            <p className="body" style={{marginBottom: 24, fontSize: 16}}>Six-and-a-half kilometers south of downtown on Highway 200 (the old road to Barra de Navidad) — about 25–35 minutes from PVR airport. Far enough to feel private, close enough to be in town in fifteen minutes.</p>
            <div className="stack" style={{marginTop: 32, paddingTop: 32, borderTop: '1px solid var(--line-soft)'}}>
              <div style={{display: 'flex', gap: 16}}><Icon name="mapPin" size={20}/><div><div style={{fontWeight: 500, marginBottom: 4}}>Address</div><div className="body" style={{fontSize: 14}}>Carretera a Barra de Navidad Km. 7.5, Playa Punta Negra, Puerto Vallarta</div></div></div>
              <div style={{display: 'flex', gap: 16}}><Icon name="phone" size={20}/><div><div style={{fontWeight: 500, marginBottom: 4}}>Book by phone</div><div className="body" style={{fontSize: 14}}>720-422-2216</div></div></div>
            </div>
          </div>
          <div className="photo" style={{aspectRatio: '1/1'}}><img src={IMAGES.rooms} alt="Aerial view of Villa Karaway and Playa Punta Negra beach in Puerto Vallarta"/></div>
        </div>
      </div>
    </section>
  </>
  );
};

// Each room/space pulls its photos from the D1 gallery via tag match. The hero image is
// the first match (or the explicit fallback); thumbnails below swap the hero on click.
// Split a tag cell ("pool" or "pool,beach") into an array — tags is stored as text, so a photo
// can belong to multiple categories. Both RoomCard and GalleryPage use this.
const splitTags = (raw) => (raw ? String(raw).split(',').map(t => t.trim()).filter(Boolean) : []);

const RoomCard = ({ r, index }) => {
  const { gallery } = useSite();
  const tagSet = new Set(r.tags || []);
  const matched = (gallery || []).filter(g => splitTags(g.tags).some(t => tagSet.has(t)));
  // Sort so an explicit hero key (r.heroKey, matched against r2_key) floats to the front.
  const photos = (matched.length ? matched : (r.fallback ? [{ r2_key: r.fallback.replace(/^\/media\//, ''), alt_text: r.name }] : []))
    .slice()
    .sort((a, b) => {
      if (!r.heroKey) return 0;
      const aHit = a.r2_key?.endsWith(r.heroKey) ? -1 : 0;
      const bHit = b.r2_key?.endsWith(r.heroKey) ? -1 : 0;
      return aHit - bHit;
    });
  const initial = photos[0] ? resolvePhoto(photos[0].r2_key) : r.fallback;
  const [active, setActive] = useStateP(initial);
  // If gallery loads after mount (or the curated hero changes), reset the displayed hero
  // to the newly computed default instead of staying frozen on the fallback.
  useEffectP(() => { setActive(initial); }, [initial]);

  return (
    <div className={`room-card ${index % 2 === 1 ? 'reverse' : ''}`}>
      <div>
        <div className="room-card-img"><img src={active} alt={r.name} loading="lazy" style={{width: '100%', height: '100%', objectFit: 'cover'}}/></div>
        {photos.length > 1 && (
          <div style={{display: 'flex', gap: 8, marginTop: 10, flexWrap: 'wrap'}}>
            {photos.slice(0, 6).map(p => {
              const src = resolvePhoto(p.r2_key);
              const isActive = src === active;
              return (
                <button
                  key={p.id || p.r2_key}
                  onClick={() => setActive(src)}
                  aria-label={p.alt_text || r.name}
                  style={{
                    flex: 1,
                    minWidth: 0,
                    aspectRatio: '4/3',
                    padding: 0,
                    border: isActive ? '2px solid var(--terracotta)' : '2px solid transparent',
                    background: 'transparent',
                    cursor: 'pointer',
                    overflow: 'hidden',
                    opacity: isActive ? 1 : 0.7,
                    transition: 'opacity 0.2s, border-color 0.2s',
                  }}
                  onMouseEnter={e => { if (!isActive) e.currentTarget.style.opacity = '1'; }}
                  onMouseLeave={e => { if (!isActive) e.currentTarget.style.opacity = '0.7'; }}
                >
                  <img src={src} alt="" loading="lazy" style={{width: '100%', height: '100%', objectFit: 'cover', display: 'block'}}/>
                </button>
              );
            })}
          </div>
        )}
      </div>
      <div className="room-card-content">
        <div className="eyebrow">{r.sub}</div>
        <h3>{r.name}</h3>
        <p className="body" style={{fontSize: 16}}>{r.desc}</p>
        {r.bed && <div className="room-card-meta">
          <span><Icon name="bed" size={16}/> {r.bed}</span>
          <span><Icon name="users" size={16}/> {r.guests}</span>
          <span><Icon name="bath" size={16}/> {r.bath}</span>
        </div>}
      </div>
    </div>
  );
};

const RoomsPage = ({ navigate }) => {
  const { settings } = useSite();
  const bookProps = bookCtaProps(settings);

  // The four boutique bedroom suites. Named "sea to heaven" (del mar, nubes, sol, cielo).
  // `tags` resolve against D1 gallery_images.tags (set in airbnb-uploaded.json).
  const suites = [
    { name: 'el Cielo',  sub: 'Heaven — Master Suite',          desc: "The main master bedroom: king bed + a futon/couch for an extra sleeper, and a large private porch to take in the ocean, wildlife, and some of the best sunsets in the bay. The suite was made to be lived in, not just slept in.", bed: 'King + Futon',     guests: '3', bath: 'En suite · Large shower', tags: ['elCielo', 'elCielo_bath', 'elCielo_deck'], heroKey: '5631c3fc5700.jpg', fallback: IMAGES.elCielo1 },
    { name: 'el Sol',    sub: 'The Sun — Courtyard Suite',      desc: "Two queen beds next door to the master. Overlooks the fountain courtyard, so the sound of water and the ocean carries straight through. Private en-suite bath.",                                                      bed: '2 Queen',          guests: '4', bath: 'En suite · Private',      tags: ['elSol'],                                    fallback: IMAGES.elSol1 },
    { name: 'las Nubes', sub: 'The Clouds — Courtyard Suite',   desc: "One of the largest bedrooms at the villa: a king and a queen bed together, and an oversized bathroom with a huge shower. Looks onto the courtyard fountain.",                                                 bed: 'King + Queen',     guests: '4', bath: 'En suite · Large shower', tags: ['elNubes'],                                  heroKey: 'b47dff2dee09.jpg', fallback: IMAGES.nubes1 },
    { name: 'el Mar',    sub: 'The Sea — Garden / Ground Floor', desc: "A downstairs bedroom — super quiet, and ideal for guests who'd rather avoid stairs. Two twin beds that can be combined into a king on request. Private en-suite bath (recently renovated).",                   bed: '2 Twin (or King)', guests: '2', bath: 'En suite · Ground floor', tags: ['elMar', 'elMar_bath'],                      heroKey: '6141941ed4aa.jpg', fallback: IMAGES.mar1 },
  ];

  const spaces = [
    { name: 'Private Infinity Pool', sub: 'Beachfront',                  desc: "Your own private soaking pool opens straight onto the sand, with uninterrupted bay views. Sunrise coffee, afternoon laps, sunset margaritas, late-night swims under the stars — most of your stay will happen here.",                                tags: ['pool'],               heroKey: 'a0d66d17aa35.jpg', fallback: '/media/gallery/airbnb/a0d66d17aa35.jpg' },
    { name: 'Upstairs Deck',         sub: 'Off el Cielo · Master Porch', desc: "A private porch connected to the master suite with uninterrupted views of the Pacific, the bay, and the sunset. Ideal spot for yoga at sunrise or a quiet drink after dinner.",                                                                  tags: ['elCielo_deck'],       fallback: '/media/gallery/airbnb/d061382afc05.jpg' },
    { name: 'Living Room',           sub: 'Indoor–Outdoor Flow',         desc: "Soaring ceilings, natural light, and a direct opening onto the patio. Sofas face the waves, the breeze moves through, and the sound of the surf is constant.",                                                                                   tags: ['living'],             fallback: '/media/gallery/airbnb/5a4745400ff0.jpg' },
    { name: 'Dining Room',           sub: 'Table for Twelve',            desc: "The large dining table seats up to 12 — sized for sunset dinners, shared breakfasts, and the kind of long evenings that pile up into memories. Indoor dining plus an outdoor table with the best view in the house.",                           tags: ['dining'],             heroKey: '5eb2dd7a9dfb.jpg', fallback: '/media/gallery/airbnb/5eb2dd7a9dfb.jpg' },
    { name: 'Kitchen',               sub: 'Staff-Run, Guest-Open',       desc: "Top-of-the-line appliances, kept immaculate by the staff. Your chef prepares meals customized for your group — pre-paid meal plan optional (ask for details), or pay separately for groceries and drinks.",                                     tags: ['kitchen'],            fallback: '/media/gallery/airbnb/4a79fa9827f6.jpg' },
    { name: 'Central Courtyard',     sub: "The Villa's Heart",           desc: "An architectural wonder built around a central fountain. Every bedroom opens onto the courtyard, so the sound of running water and the Pacific carries through every wall.",                                                                     tags: ['courtyard', 'foyer'], heroKey: 'e6d3dd86d560.jpg', fallback: '/media/gallery/airbnb/e6d3dd86d560.jpg' },
    { name: 'Beach & Water Toys',    sub: 'Playa Punta Negra',           desc: "Steps off the back patio onto Playa Punta Negra. Lifeguard-patrolled, quiet, uncrowded. Paddleboards, kayaks, boogie boards, and pool toys are all included with your stay.",                                                                    tags: ['beach', 'toys'],      heroKey: '0938dc3112aa.jpg', fallback: '/media/gallery/airbnb/0938dc3112aa.jpg' },
  ];

  return (
    <>
      <section style={{padding: '120px 0 40px'}}>
        <div className="container">
          <SectionHead
            as="h1"
            eyebrow="Tour the Villa"
            title="4 bedrooms, one private villa."
            intro="Villa Karaway is an open-air, 3-story, 4,000+ sq ft oceanfront villa in Puerto Vallarta’s South Zone. Four en-suite bedrooms — named sea-to-heaven — wrap around a central courtyard fountain, so the sound of water and the Pacific carries through every room. Private infinity soaking pool. Chef and maid included. Up to 12 guests."
          />
          <div className="villa-facts">
            {[
              ['Bedrooms',  '4 · all en-suite'],
              ['Bathrooms', '5'],
              ['Sleeps',    'Up to 12'],
              ['Size',      '4,000+ sq ft'],
              ['Stories',   '3'],
              ['Pool',      'Private infinity'],
              ['Staff',     'Chef · Maid · Manager'],
              ['Wi-Fi',     'Fiber optic, every room'],
            ].map(([k, v]) => (
              <div key={k} className="villa-fact">
                <div className="eyebrow">{k}</div>
                <div className="villa-fact-val">{v}</div>
              </div>
            ))}
          </div>
        </div>
      </section>

      <div className="container">{suites.map((r, i) => <RoomCard key={r.name} r={r} index={i}/>)}</div>

      <section style={{padding: '80px 0 32px'}}>
        <div className="container">
          <SectionHead eyebrow="Shared Spaces" title="The rest of the villa." intro="The common areas are where the villa opens up — the private pool, the upstairs deck, the dining room and kitchen, the courtyard, and the beach just past the back patio."/>
        </div>
      </section>

      <div className="container">{spaces.map((r, i) => <RoomCard key={r.name} r={r} index={i}/>)}</div>

      <section className="section text-center">
        <div className="container-narrow">
          <div className="eyebrow" style={{marginBottom: 20}}>Ready to book?</div>
          <h2 className="display" style={{fontSize: 'clamp(32px, 4vw, 48px)', marginBottom: 32}}>The villa is waiting.</h2>
          <div style={{display: 'flex', gap: 20, justifyContent: 'center', flexWrap: 'wrap'}}>
            <a className="btn btn-primary" {...bookProps}>Book Your Stay</a>
            <button className="btn btn-outline" onClick={() => navigate('availability')}>See availability</button>
          </div>
        </div>
      </section>
    </>
  );
};

// Groups map raw image tags (from airbnb-uploaded.json / gallery_images.tags) to user-facing categories.
// A photo can belong to multiple groups via its tag; the "All" filter shows every image.
const GALLERY_GROUPS = [
  { id: 'all',       label: 'All' },
  { id: 'pool',      label: 'Pool & Beach',  tags: ['pool', 'beach', 'common_pool', 'sunset', 'view'] },
  { id: 'bedrooms',  label: 'Bedrooms',      tags: ['elCielo', 'elCielo_bath', 'elCielo_deck', 'elSol', 'elNubes', 'elMar', 'elMar_bath', 'bath'] },
  { id: 'villa',     label: 'Villa Spaces',  tags: ['courtyard', 'foyer', 'living', 'dining', 'kitchen', 'stairs', 'misc'] },
  { id: 'chef',      label: 'Chef & Food',   tags: ['chef'] },
  { id: 'wellness',  label: 'Wellness',      tags: ['yoga', 'massage', 'toys'] },
];

const GalleryPage = ({ navigate }) => {
  const { gallery, settings } = useSite();
  const bookProps = bookCtaProps(settings);
  const [active, setActive] = useStateP('all');
  const [lightbox, setLightbox] = useStateP(null); // {src, alt}

  // Visual variety: cycle wide/tall modifiers across the grid so it doesn't feel uniform.
  const modifierAt = (i) => {
    if (i === 0) return 'wide';
    if (i % 9 === 4) return 'wide';
    if (i % 7 === 3) return 'tall';
    return '';
  };

  const group = GALLERY_GROUPS.find(g => g.id === active) || GALLERY_GROUPS[0];
  const items = (gallery || []).filter(g => !group.tags || splitTags(g.tags).some(t => group.tags.includes(t)));

  return (
    <>
      <section style={{padding: '120px 0 40px'}}>
        <div className="container">
          <SectionHead as="h1" eyebrow="The Villa in Pictures" title="Gallery." intro="Every corner of Villa Karaway — from the private infinity pool to the master suite — framed by ocean, courtyard, or garden."/>
          <div className="gallery-filter" style={{display: 'flex', flexWrap: 'wrap', gap: 8, paddingTop: 32, borderTop: '1px solid var(--line-soft)'}}>
            {GALLERY_GROUPS.map(g => (
              <button
                key={g.id}
                onClick={() => setActive(g.id)}
                className={active === g.id ? 'filter-btn filter-btn-active' : 'filter-btn'}
                style={{
                  padding: '10px 18px',
                  fontFamily: 'var(--sans)',
                  fontSize: 12,
                  letterSpacing: '0.14em',
                  textTransform: 'uppercase',
                  fontWeight: 500,
                  border: active === g.id ? '1px solid var(--terracotta)' : '1px solid var(--line)',
                  color: active === g.id ? 'var(--terracotta)' : 'var(--ink)',
                  background: 'transparent',
                  cursor: 'pointer',
                  transition: 'all 0.2s',
                }}
              >
                {g.label}
              </button>
            ))}
            <div style={{flex: 1}}></div>
            <div className="eyebrow" style={{alignSelf: 'center'}}>{items.length} photo{items.length !== 1 ? 's' : ''}</div>
          </div>
        </div>
      </section>
      <div className="container" style={{paddingBottom: 120, paddingTop: 24}}>
        {items.length === 0 ? (
          <p className="body" style={{color: 'var(--muted)', fontSize: 15, textAlign: 'center', padding: '80px 0'}}>No photos in this category yet.</p>
        ) : (
          <div className="gallery-grid">
            {items.map((it, i) => {
              const src = resolvePhoto(it.r2_key);
              const alt = it.alt_text || it.label || 'Villa Karaway';
              return (
                <div
                  key={it.id || it.r2_key}
                  className={`gallery-item ${modifierAt(i)}`}
                  onClick={() => setLightbox({ src, alt })}
                  role="button"
                  tabIndex={0}
                  onKeyDown={e => { if (e.key === 'Enter' || e.key === ' ') setLightbox({ src, alt }); }}
                >
                  <div className="photo" style={{height: '100%'}}><img src={src} alt={alt} loading="lazy"/></div>
                </div>
              );
            })}
          </div>
        )}
      </div>
      <section className="section section-warm">
        <div className="container-narrow text-center">
          <div className="eyebrow" style={{marginBottom: 20}}>Ready to see real dates?</div>
          <h2 className="display" style={{fontSize: 'clamp(28px, 3.5vw, 42px)', marginBottom: 28}}>Check the calendar.</h2>
          <p className="body" style={{fontSize: 16, marginBottom: 32}}>Every photo here is yours for the week you book. Rates and open nights are live on the HostMoat calendar.</p>
          <div style={{display: 'flex', gap: 16, justifyContent: 'center', flexWrap: 'wrap'}}>
            <a className="btn btn-primary" {...bookProps}>Book your stay</a>
            <button className="btn btn-outline" onClick={() => navigate('availability')}>See availability</button>
          </div>
        </div>
      </section>
      {lightbox && (
        <div
          onClick={() => setLightbox(null)}
          role="dialog"
          aria-modal="true"
          style={{position: 'fixed', inset: 0, background: 'rgba(15,35,38,0.92)', zIndex: 1000, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 40, cursor: 'zoom-out'}}
        >
          <button
            onClick={(e) => { e.stopPropagation(); setLightbox(null); }}
            aria-label="Close"
            style={{position: 'absolute', top: 20, right: 20, background: 'transparent', border: 'none', color: 'var(--bg)', cursor: 'pointer', padding: 8}}
          >
            <Icon name="close" size={28}/>
          </button>
          <img
            src={lightbox.src}
            alt={lightbox.alt}
            style={{maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', boxShadow: '0 20px 60px rgba(0,0,0,0.5)'}}
            onClick={(e) => e.stopPropagation()}
          />
          {lightbox.alt && (
            <div style={{position: 'absolute', bottom: 30, left: 0, right: 0, textAlign: 'center', color: 'rgba(250,246,239,0.85)', fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 16, padding: '0 40px'}}>{lightbox.alt}</div>
          )}
        </div>
      )}
    </>
  );
};

const MenuPage = () => {
  const { menu_cards, gallery } = useSite();
  // Pull chef-tagged photos from D1 gallery so the visitor sees actual plated food before the PDF download.
  const chefPhotos = (gallery || []).filter(g => splitTags(g.tags).includes('chef')).slice(0, 6);
  return (
  <>
    <section style={{padding: '120px 0 40px'}}>
      <div className="container">
        <SectionHead as="h1" eyebrow="Cooked by Chef Gabriel" title="The Villa Menu." intro="Your stay is made more memorable by the fresh cuisine Gabriel prepares on-site. Select from the menu in advance and he'll meet almost any dietary need — vegetarian, gluten-free, low-calorie. Off-menu requests welcome, and appetizers can become entrées (or vice versa)."/>
      </div>
    </section>

    {chefPhotos.length > 0 && (
      <div className="container" style={{paddingBottom: 40}}>
        <div className="eyebrow" style={{marginBottom: 24}}>A taste of the kitchen</div>
        <div className="grid-3">
          {chefPhotos.map(p => (
            <div key={p.id || p.r2_key} style={{display: 'flex', flexDirection: 'column'}}>
              <div className="photo" style={{aspectRatio: '4/3'}}><img src={resolvePhoto(p.r2_key)} alt={p.alt_text || 'Villa Karaway food'} loading="lazy"/></div>
              {p.alt_text && (
                <p className="body" style={{fontSize: 13, color: 'var(--muted)', fontStyle: 'italic', marginTop: 12, lineHeight: 1.5}}>{p.alt_text}</p>
              )}
            </div>
          ))}
        </div>
      </div>
    )}

    <section style={{padding: '40px 0 80px'}}>
      <div className="container">
        <div className="eyebrow" style={{marginBottom: 24}}>Full menus (PDF)</div>
        <div className="grid-3">
          {menu_cards.map(m => (
            <a key={m.id} href={m.url} target="_blank" rel="noreferrer" className="card" style={{padding: 32, textDecoration: 'none', display: 'flex', flexDirection: 'column', gap: 14}}>
              <Icon name="file" size={24}/>
              <h3 className="serif" style={{fontSize: 22, fontWeight: 400, letterSpacing: '-0.01em'}}>{m.label}</h3>
              <div style={{flex: 1}}></div>
              <div style={{fontFamily: 'var(--sans)', fontSize: 11, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--terracotta)', fontWeight: 500}}>View PDF →</div>
            </a>
          ))}
        </div>
      </div>
    </section>

    <section className="section section-warm">
      <div className="container">
        <div className="grid-2" style={{alignItems: 'center', gap: 80}}>
          <div className="photo" style={{aspectRatio: '4/5'}}><img src={IMAGES.living2} alt="Villa Karaway formal dining room set for twelve with ocean view"/></div>
          <div>
            <div className="eyebrow" style={{marginBottom: 20}}>Pre-paid Food & Drink (optional)</div>
            <h2 className="display" style={{fontSize: 'clamp(32px, 3.8vw, 46px)', marginBottom: 24}}>Take the guesswork out of the grocery tab.</h2>
            <p className="body" style={{fontSize: 17, marginBottom: 20}}>If your group would like the pre-paid food & drink option, mention it on the arrival form or send Hillary a note. Current pricing is shown in the arrival form (it does shift over time with ingredient costs).</p>
            <p className="body" style={{fontSize: 17, marginBottom: 32}}>The whole group must opt in for all dates you've booked. If you choose it, we'll send a separate invoice.</p>
            <button className="btn btn-primary" onClick={() => window.__karaway_navigate && window.__karaway_navigate('contact')}>Ask Hillary</button>
          </div>
        </div>
      </div>
    </section>

    <section className="section">
      <div className="container-narrow text-center">
        <div className="eyebrow" style={{marginBottom: 24}}>A taste of what we cook</div>
        <p className="display" style={{fontSize: 'clamp(24px, 2.6vw, 34px)', lineHeight: 1.4, marginBottom: 40}}>Fresh-pressed juice at sunrise. Whole grilled fish. Margaritas on the porch. Gabriel's Piña Fresh and Strawberry juices (mix with vodka or tequila, or don't). Whatever you're craving, he can make it.</p>
        <p className="body" style={{fontSize: 16}}>Tell Gabriel what you love — he'll cook it.</p>
      </div>
    </section>
  </>
  );
};

const ExperiencePage = ({ navigate }) => {
  const { settings } = useSite();
  // Staff roster matches the current guidebook: Hillary (US-side guest experience), Enrique (villa
  // manager & concierge), Gabriel (chef), Ana (housekeeping). Owners Wayne + Karen Harding
  // purchased the property new in late 1999.
  const staff = [
    { name: 'Enrique Perez Jr.', role: 'Villa Manager & Concierge', bio: "Enrique — Kike to the guests who keep coming back — runs the house. He lives nearby, speaks perfect English, and can arrange anything you need: airport pickup, a boat to Yelapa, fresh flowers in your room before you arrive.", img: IMAGES.staffEnrique },
    { name: 'Gabriel',           role: 'Chef',                      bio: "Gabriel brings a culinary degree and a passion for bringing families together through food. He has a huge repertoire of dishes and happily tailors meals to whatever you and your group love — gluten-free, vegetarian, low-calorie, off-menu.",       img: IMAGES.staffGabriel },
    { name: 'Ana',               role: 'Housekeeping',              bio: "Ana keeps the villa immaculate — every bedroom, fresh towels and clean linens, Monday through Saturday. She arrives in the morning and is quietly everywhere at once. Dependable, trustworthy, and extremely hard-working.",                                 img: IMAGES.staffAna },
    { name: 'Hillary Shepperd',  role: 'Manager of Guest Experience', bio: "Hillary grew up visiting Villa Karaway and now looks after every guest group from the US side. Pre-arrival logistics, dietary preferences, grocery lists, pre-paid food & drink, airport arrangements — she's your first point of contact before you arrive.", img: IMAGES.staffHillary },
  ];
  return (
    <>
      <section style={{padding: '120px 0 80px'}}>
        <div className="container"><SectionHead as="h1" eyebrow="Meet the Team" title="The staff who make Karaway home." intro="Enrique, Gabriel, and Ana are the reason guests return year after year — together with Hillary, who manages every group's arrival from the US side. The villa staff work Monday through Saturday; if you arrive on a Sunday, you'll still find them at the villa getting everything ready for you."/></div>
      </section>
      <div className="container" style={{paddingBottom: 40}}>
        <div className="grid-4">
          {staff.map(s => (
            <div key={s.name}>
              <div className="photo" style={{aspectRatio: '1/1'}}><img src={s.img} alt={`${s.name} — ${s.role} at Villa Karaway`}/></div>
              <div style={{paddingTop: 20}}>
                <div className="eyebrow" style={{marginBottom: 6}}>{s.role}</div>
                <h3 className="serif" style={{fontSize: 24, fontWeight: 400, marginBottom: 8}}>{s.name}</h3>
                <p className="body" style={{fontSize: 14}}>{s.bio}</p>
              </div>
            </div>
          ))}
        </div>
      </div>
      <div className="container" style={{paddingBottom: 80}}>
        <div className="card" style={{padding: 40, background: 'var(--bg-warm)', border: 'none', marginTop: 40}}>
          <div className="grid-2" style={{alignItems: 'center', gap: 40}}>
            <div className="photo" style={{aspectRatio: '4/3'}}><img src={IMAGES.staffTeam} alt="The Villa Karaway team — Enrique, Gabriel, Ana, and Hillary — on the villa patio"/></div>
            <div>
              <div className="eyebrow" style={{marginBottom: 12}}>Owners</div>
              <h3 className="serif" style={{fontSize: 28, fontWeight: 400, marginBottom: 16}}>Wayne & Karen Harding</h3>
              <p className="body" style={{fontSize: 15}}>Wayne and Karen purchased Villa Karaway new in late 1999 and have run it as a family villa ever since. They welcome every guest personally through the arrival letter and are reachable by phone or email throughout your stay.</p>
            </div>
          </div>
        </div>
      </div>
      <section className="section section-warm">
        <div className="container">
          <div className="grid-2" style={{alignItems: 'center', gap: 80}}>
            <div>
              <div className="eyebrow" style={{marginBottom: 20}}>Ready to meet them?</div>
              <h2 className="display" style={{fontSize: 'clamp(32px, 4vw, 48px)', marginBottom: 28}}>Book your stay.</h2>
              <p className="body" style={{fontSize: 16, marginBottom: 20}}>The team's been at the villa long enough that most guests come back year after year. Check dates, lock in a week, and we'll do the rest from the moment you land in Puerto Vallarta.</p>
              <p className="body" style={{fontSize: 16, marginBottom: 32}}>Got a question first? Hillary (our manager of guest experience) answers every email personally.</p>
              <div style={{display: 'flex', gap: 16, flexWrap: 'wrap'}}>
                <a className="btn btn-primary" {...bookCtaProps(settings)}>Book your stay</a>
                <button className="btn btn-outline" onClick={() => navigate('contact')}>Ask Hillary</button>
              </div>
            </div>
            <div className="photo" style={{aspectRatio: '4/3'}}><img src={IMAGES.living1} alt="Villa Karaway living room opening through to private infinity pool and Pacific ocean"/></div>
          </div>
        </div>
      </section>
    </>
  );
};

const ReviewsPage = () => {
  const { reviews } = useSite();
  const avg = reviews.length ? (reviews.reduce((a, r) => a + (r.rating || 0), 0) / reviews.length) : 0;
  return (
    <>
      <section style={{padding: '120px 0 60px'}}>
        <div className="container">
          <SectionHead as="h1" eyebrow="Guest Comments" title="From our guests." intro="A decade and a half of families, friends, honeymooners, and yogis."/>
          <div style={{display: 'flex', gap: 40, alignItems: 'center', paddingTop: 32, borderTop: '1px solid var(--line-soft)'}}>
            <div>
              <div style={{fontFamily: 'var(--serif)', fontSize: 56, fontWeight: 300, lineHeight: 1}}>{avg ? avg.toFixed(1) : '—'}</div>
              <div style={{display: 'flex', gap: 2, marginTop: 8, color: 'var(--terracotta)'}}>{[1,2,3,4,5].map(i => <Icon key={i} name="star" size={14} filled/>)}</div>
            </div>
            <div>
              <div className="eyebrow" style={{marginBottom: 6}}>Average rating</div>
              <div className="body" style={{fontSize: 14}}>A selection of comments from 25+ years of guests on Google, Airbnb, and in our villa guest book.</div>
            </div>
          </div>
        </div>
      </section>
      <div className="container" style={{paddingBottom: 120}}>
        <div style={{columns: '2 360px', columnGap: 32}}>
          {reviews.length === 0 ? (
            <p style={{color: 'var(--muted)', fontSize: 15}}>No reviews yet.</p>
          ) : reviews.map((r) => (
            <div key={r.id} className="card" style={{padding: 32, marginBottom: 32, breakInside: 'avoid'}}>
              <div style={{display: 'flex', gap: 2, color: 'var(--terracotta)', marginBottom: 16}}>{Array.from({length: r.rating}).map((_, j) => <Icon key={j} name="star" size={12} filled/>)}</div>
              <p style={{fontFamily: 'var(--serif)', fontSize: 19, fontWeight: 300, lineHeight: 1.5, fontStyle: 'italic', marginBottom: 24}}>"{r.text}"</p>
              <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingTop: 16, borderTop: '1px solid var(--line-soft)'}}>
                <div style={{fontWeight: 500, fontSize: 14}}>{r.author}</div>
                <div className="eyebrow">{r.source || '—'}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </>
  );
};

const ContactPage = () => {
  const { settings } = useSite();
  const emptyForm = { name: '', email: '', phone: '', subject: 'General question', checkin_date: '', checkout_date: '', message: '', website: '' };
  const [form, setForm] = useStateP(emptyForm);
  const [state, setState] = useStateP({ sending: false, sent: false, error: null, routed: null });

  // Disallow check-out before/equal-to check-in by setting min on the checkout input.
  const checkoutMin = form.checkin_date || undefined;
  const datesValid = !form.checkin_date || !form.checkout_date || form.checkout_date > form.checkin_date;

  const submit = async (e) => {
    e.preventDefault();
    if (!datesValid) { setState({ sending: false, sent: false, error: 'Check-out must be after check-in.', routed: null }); return; }
    setState({ sending: true, sent: false, error: null, routed: null });
    try {
      const r = await fetch('/api/contact', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(form),
      });
      if (!r.ok) throw new Error(await r.text());
      const data = await r.json().catch(() => ({}));
      setState({ sending: false, sent: true, error: null, routed: data.routed || null });
      setForm(emptyForm);
    } catch (e) {
      setState({ sending: false, sent: false, error: String(e), routed: null });
    }
  };

  return (
  <>
    <section style={{padding: '120px 0 40px'}}>
      <div className="container"><SectionHead as="h1" eyebrow="Contact" title="Get in touch." intro="Send a message using the form below — Hillary reads every note personally and typically replies within 24 hours. Already booked? Check the guidebook first; most answers are in there."/></div>
    </section>

    <div className="container" style={{paddingBottom: 40}}>
      <div className="card" style={{padding: 32, background: 'var(--bg-warm)', border: 'none', display: 'grid', gridTemplateColumns: 'auto 1fr', gap: 24, alignItems: 'center'}}>
        <div style={{width: 96, height: 96, borderRadius: '50%', overflow: 'hidden', flexShrink: 0}}>
          <img src={IMAGES.staffHillary} alt="Hillary Shepperd, Villa Karaway Manager of Guest Experience" style={{width: '100%', height: '100%', objectFit: 'cover'}}/>
        </div>
        <div>
          <div className="eyebrow" style={{marginBottom: 6}}>Manager of Guest Experience</div>
          <h3 className="serif" style={{fontSize: 24, fontWeight: 400, marginBottom: 8, letterSpacing: '-0.01em'}}>Hillary Shepperd</h3>
          <p className="body" style={{fontSize: 14}}>Hillary grew up visiting Villa Karaway and handles every guest group personally from the US side — pre-arrival logistics, dietary preferences, grocery requests, airport arrangements. Typical reply time: under 24 hours.</p>
        </div>
      </div>
    </div>

    <div className="container" style={{paddingBottom: 120}}>
      <div className="grid-2" style={{alignItems: 'flex-start', gap: 64}}>
        <form onSubmit={submit}>
          <h3 className="serif" style={{fontSize: 32, fontWeight: 400, marginBottom: 20}}>Send a message</h3>
          <div className="form-row"><label className="label">Your name</label><input className="input" placeholder="Full name" value={form.name} onChange={e => setForm({...form, name: e.target.value})} required/></div>
          <div className="form-row"><label className="label">Email</label><input className="input" type="email" placeholder="you@example.com" value={form.email} onChange={e => setForm({...form, email: e.target.value})} required/></div>
          <div className="form-row"><label className="label">Phone <span style={{color: 'var(--muted)', fontWeight: 400}}>(optional)</span></label><input className="input" type="tel" placeholder="+1 555 555 5555" value={form.phone} onChange={e => setForm({...form, phone: e.target.value})}/></div>
          <div className="form-row"><label className="label">Subject</label>
            <select className="select" value={form.subject} onChange={e => setForm({...form, subject: e.target.value})}>
              <option>General question</option><option>Custom stay</option><option>Press / media</option><option>Other</option>
            </select>
          </div>
          <div className="form-row" style={{display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12}}>
            <div><label className="label">Check-in <span style={{color: 'var(--muted)', fontWeight: 400}}>(optional)</span></label><input className="input" type="date" value={form.checkin_date} onChange={e => setForm({...form, checkin_date: e.target.value})}/></div>
            <div><label className="label">Check-out</label><input className="input" type="date" min={checkoutMin} value={form.checkout_date} onChange={e => setForm({...form, checkout_date: e.target.value})}/></div>
          </div>
          <p className="small" style={{color: 'var(--muted)', marginTop: -4, marginBottom: 16}}>Have dates? Add them and we'll route this straight to our reservations system.</p>
          <div className="form-row"><label className="label">Message</label><textarea className="textarea" rows={6} placeholder="How can we help?" value={form.message} onChange={e => setForm({...form, message: e.target.value})} required/></div>
          {/* honeypot, hidden */}
          <input type="text" name="website" value={form.website} onChange={e => setForm({...form, website: e.target.value})} tabIndex={-1} autoComplete="off" style={{position: 'absolute', left: '-9999px', opacity: 0}}/>
          <button type="submit" className="btn btn-primary" disabled={state.sending || !datesValid}>{state.sending ? 'Sending…' : (state.sent ? 'Sent ✓' : 'Send Message')}</button>
          {state.error && <p className="small" style={{color: '#a0403a', marginTop: 16}}>{state.error}</p>}
          {state.sent && <p className="small" style={{color: 'var(--sage)', marginTop: 16}}>Thanks — we'll get back to you soon{state.routed === 'hostmoat' ? '. Your dated inquiry was sent to our reservations system' : ''}.</p>}
        </form>
        <div className="card" style={{padding: 40, background: 'var(--bg-warm)', border: 'none'}}>
          <div className="stack-lg">
            {settings.hostmoat_booking_url && (
              <div>
                <div className="eyebrow" style={{marginBottom: 16}}>Book your stay</div>
                <a className="btn btn-primary" href={settings.hostmoat_booking_url} target="_blank" rel="noreferrer" style={{width: '100%', justifyContent: 'center'}}>Open Booking System</a>
              </div>
            )}
            {settings.phone && <div><div className="eyebrow" style={{marginBottom: 12}}>Call us</div><div className="serif" style={{fontSize: 28, fontWeight: 400}}>{settings.phone}</div></div>}
            {settings.email && <div><div className="eyebrow" style={{marginBottom: 12}}>Email</div><div style={{fontSize: 17}}>{settings.email}</div></div>}
            <div><div className="eyebrow" style={{marginBottom: 12}}>Address</div><div className="body" style={{fontSize: 15}}>{settings.address_line1 || 'Carretera a Barra de Navidad Km. 7.5'}<br/>{settings.address_line2 || 'Playa Punta Negra, Puerto Vallarta'}<br/>{settings.address_line3 || 'Jalisco, Mexico'}</div></div>
            <div><div className="eyebrow" style={{marginBottom: 12}}>Follow</div>
              <div style={{display: 'flex', gap: 14}}>
                {settings.instagram_url && <a href={settings.instagram_url} target="_blank" rel="noreferrer"><Icon name="instagram" size={22}/></a>}
                {settings.facebook_url && <a href={settings.facebook_url} target="_blank" rel="noreferrer"><Icon name="facebook" size={22}/></a>}
                {settings.airbnb_url && <a href={settings.airbnb_url} target="_blank" rel="noreferrer" style={{fontSize: 13, fontWeight: 500}}>Airbnb</a>}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </>
  );
};

// ============ NEW PAGES ============

// Real Villa Karaway YouTube IDs, ripped from karaway.com /video and /video-vk on 2026-04-19.
const VideoPage = () => {
  const [active, setActive] = useStateP('tg__9dnY6G0');
  const videos = [
    { id: 'tg__9dnY6G0', title: 'Welcome to Villa Karaway',        desc: 'See what Villa Karaway is all about.' },
    { id: 'jeoH1u0E-qA', title: 'Yoga Retreat 2014',                desc: 'Meditation, adventure, new friendships, and yoga.' },
    { id: 'AZHcWzDsChc', title: 'Villa Karaway by Air',             desc: 'The villa and the surrounding coast from above.' },
    { id: '1ln7Qhw7LdY', title: 'Kayak Time',                       desc: 'There’s a kayak at the villa — great way to explore the cove.' },
    { id: 'ngbpPZ2RX7I', title: 'How To Vacation',                  desc: 'A walkthrough of what the villa has to offer.' },
    { id: '1UXfwAMeL1I', title: 'Escape',                           desc: 'No worries here.' },
    { id: 'X6DJmWBei_s', title: 'Want a Better View?',              desc: 'A flight around the grounds and inside the villa.' },
  ];
  return (
  <>
    <section style={{padding: '120px 0 60px'}}>
      <div className="container"><SectionHead as="h1" eyebrow="Video Tour" title="See the villa in motion." intro="The main Villa Karaway tour, plus a handful of films guests and friends have made of the place over the years."/></div>
    </section>
    <div className="container" style={{paddingBottom: 120}}>
      <div style={{position: 'relative', aspectRatio: '16/9', background: 'var(--ocean)', marginBottom: 40, overflow: 'hidden'}}>
        <iframe
          key={active}
          src={`https://www.youtube.com/embed/${active}?autoplay=0&rel=0&modestbranding=1`}
          title={videos.find(v => v.id === active)?.title || 'Villa Karaway'}
          style={{width: '100%', height: '100%', border: 'none'}}
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
          allowFullScreen
        />
      </div>
      <div className="grid-3">
        {videos.map(v => (
          <button key={v.id} onClick={() => { setActive(v.id); window.scrollTo({top: 0, behavior: 'smooth'}); }} style={{cursor: 'pointer', textAlign: 'left', background: 'transparent', border: 'none', padding: 0, opacity: active === v.id ? 1 : 0.85}}>
            <div style={{position: 'relative', aspectRatio: '16/10', overflow: 'hidden', border: active === v.id ? '2px solid var(--terracotta)' : '2px solid transparent'}}>
              <img src={`https://i.ytimg.com/vi/${v.id}/hqdefault.jpg`} alt={v.title} style={{width: '100%', height: '100%', objectFit: 'cover'}} loading="lazy"/>
            </div>
            <div style={{padding: '16px 4px'}}>
              <h3 className="serif" style={{fontSize: 22, fontWeight: 400, marginBottom: 6}}>{v.title}</h3>
              <p className="body" style={{fontSize: 13, color: 'var(--muted)'}}>{v.desc}</p>
            </div>
          </button>
        ))}
      </div>
    </div>
  </>
  );
};

const WebcamPage = () => {
  const { settings } = useSite();
  const webcamUrl = settings.webcam_url || '';
  const webcamType = settings.webcam_type || 'iframe'; // iframe | youtube | mp4 | still
  const locationName = settings.webcam_location_name || 'Punta Negra · Jalisco';
  const weatherLoc = settings.weather_location || 'Puerto Vallarta';
  const [now, setNow] = useStateP(() => new Date());
  const [weather, setWeather] = useStateP(null);

  useEffectP(() => {
    const t = setInterval(() => setNow(new Date()), 60000);
    return () => clearInterval(t);
  }, []);

  // Pull current conditions from wttr.in (free, no key needed)
  useEffectP(() => {
    let cancel = false;
    fetch(`https://wttr.in/${encodeURIComponent(weatherLoc)}?format=j1`, { cache: 'no-store' })
      .then(r => r.ok ? r.json() : null)
      .then(data => {
        if (cancel || !data?.current_condition?.[0]) return;
        const c = data.current_condition[0];
        setWeather({
          tempF: c.temp_F,
          desc: c.weatherDesc?.[0]?.value || '',
          windMph: c.windspeedMiles,
          windDir: c.winddir16Point,
          humidity: c.humidity,
        });
      })
      .catch(() => {});
    return () => { cancel = true; };
  }, [weatherLoc]);

  const renderCam = () => {
    if (!webcamUrl) {
      return (
        <div style={{width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'rgba(250,246,239,0.7)', fontFamily: "'EB Garamond', Georgia, serif", fontSize: 24, fontWeight: 300, textAlign: 'center', padding: 40}}>
          Live feed temporarily offline.<br/>
          <span style={{fontSize: 13, fontFamily: 'var(--sans)', color: 'rgba(250,246,239,0.5)', letterSpacing: '0.08em', marginTop: 12, display: 'block'}}>Check back shortly — the camera should return soon.</span>
        </div>
      );
    }
    if (webcamType === 'youtube') {
      // Accept full URL, video ID, or livestream URL.
      // `id` is sanitized to the 11-char YouTube ID so a malicious webcam_url can't inject into the embed URL.
      const m = String(webcamUrl).match(/([A-Za-z0-9_-]{11})/);
      const id = m ? m[1] : '';
      if (!id) return null;
      return <iframe src={`https://www.youtube.com/embed/${id}?autoplay=1&mute=1&controls=0&rel=0`} style={{width: '100%', height: '100%', border: 'none'}} allow="autoplay; encrypted-media; picture-in-picture" allowFullScreen sandbox="allow-scripts allow-presentation"/>;
    }
    if (webcamType === 'mp4') {
      return <video src={webcamUrl} autoPlay muted loop playsInline style={{width: '100%', height: '100%', objectFit: 'cover'}}/>;
    }
    if (webcamType === 'still') {
      // Cache-bust every 30s
      return <img src={webcamUrl + (webcamUrl.includes('?') ? '&' : '?') + '_=' + Math.floor(now.getTime() / 30000)} style={{width: '100%', height: '100%', objectFit: 'cover'}} alt={locationName}/>;
    }
    // iframe fallback — scrolling disabled so the embedded Wix page doesn't show scrollbars.
    // Dropped `allow-same-origin` from the sandbox: combined with `allow-scripts` it was a no-op per spec,
    // and the embedded cam page doesn't need same-origin access to us. Still allow scripts for the cam widget.
    return <iframe src={webcamUrl} scrolling="no" style={{width: '100%', height: '100%', border: 'none', overflow: 'hidden'}} allow="fullscreen; autoplay" sandbox="allow-scripts"/>;
  };

  return (
  <>
    <section style={{padding: '120px 0 60px'}}>
      <div className="container"><SectionHead as="h1" eyebrow={`Live from ${locationName}`} title="The Karaway webcam." intro="A live view of Bahía de Banderas from Villa Karaway in Puerto Vallarta. The image refreshes approximately every 10 minutes."/></div>
    </section>
    <div className="container" style={{paddingBottom: 60}}>
      <div style={{position: 'relative', aspectRatio: '16/9', overflow: 'hidden', background: 'var(--ocean)'}}>
        {renderCam()}
      </div>
      <div className="grid-4" style={{marginTop: 48, gap: 24}}>
        {[
          { label: 'Temperature', value: weather ? `${weather.tempF}°F` : '—' },
          { label: 'Conditions',  value: weather ? weather.desc : '—' },
          { label: 'Wind',        value: weather ? `${weather.windMph} mph ${weather.windDir}` : '—' },
          { label: 'Humidity',    value: weather ? `${weather.humidity}%` : '—' },
        ].map(c => (
          <div key={c.label} style={{paddingTop: 24, borderTop: '1px solid var(--line)'}}>
            <div className="eyebrow" style={{marginBottom: 8}}>{c.label}</div>
            <div style={{fontFamily: 'var(--serif)', fontSize: 32, fontWeight: 300, letterSpacing: '-0.01em'}}>{c.value}</div>
          </div>
        ))}
      </div>
      <p className="small" style={{color: 'var(--muted)', marginTop: 24}}>Weather via wttr.in · {weatherLoc}. The camera feed is provided for entertainment only — experimental, may be delayed, intermittently unavailable, or inaccurate. Do not rely on it for weather, ocean conditions, or safety decisions.</p>
    </div>
    <section className="section section-warm">
      <div className="container-narrow text-center">
        <div className="eyebrow" style={{marginBottom: 20}}>Skip the window — come see it</div>
        <h2 className="display" style={{fontSize: 'clamp(28px, 3.5vw, 42px)', marginBottom: 28}}>Book the view.</h2>
        <p className="body" style={{fontSize: 16, marginBottom: 32}}>This is the view from the villa deck — yours for the week you book.</p>
        <a className="btn btn-primary" {...bookCtaProps(settings)}>Book your stay</a>
      </div>
    </section>
  </>
  );
};

const GuestInfoPage = ({ navigate }) => {
  const { settings } = useSite();
  const preArrival = settings.pre_arrival_url;
  const guidebook  = settings.hostmoat_guidebook_url || 'https://www.hostmoat.com/guide/80a7799e';
  const hillary    = settings.hillary_email || 'hillary@karaway.com';

  const rulesLeft = [
    'Lock doors and take a key when leaving',
    'No loud noise or partying after 11 PM',
    'No glass near the pool or beach',
    'Children under 10 supervised at the pool at all times',
    'No pool furniture on the beach — use the beach chairs',
  ];
  const rulesRight = [
    'Swimsuits always worn; swim diapers for young children',
    'Check with Ana before using the washer/dryer',
    'Help conserve water and electricity — Mexican power is expensive',
    'Villa tile floors are very slippery when wet — be careful',
    'Full rules are in the guidebook',
  ];

  const essentialsLeft = [
    ['Check-in',  '3:00 PM — check in first at the Los Palmares 5th-floor lobby, then head down to the villa. Kike will meet you at the door with cold towels and agua fresca.'],
    ['Check-out', '11:00 AM — late check-out available on request.'],
    ['Airport transfer', 'PVR to the villa runs about 25–35 minutes. Pre-arranged pickup via Enrique — email enriquepv@karaway.com or +52-322-135-0619 before you fly.'],
    ['Wi-Fi', 'Fiber throughout the villa and front porch. Password provided on arrival.'],
  ];
  const essentialsRight = [
    ['Beach access', 'Private steps from the back patio directly onto Playa Punta Negra. Lifeguard-patrolled, uncrowded.'],
    ['Groceries',    `Stock the villa before arrival — email Hillary at ${hillary}.`],
    ['Payment',      'All bookings and invoicing handled by HostMoat. Staff gratuities welcome at departure.'],
    ['Languages',    'All staff speak English and Spanish. Kike also speaks French.'],
  ];

  const packing = [
    'Passports',
    'Light sweater or windbreaker for winter evenings',
    'Sunscreen and bug repellent',
    'Sunhats and/or umbrellas',
    'Snorkel masks and fins (we no longer provide these)',
  ];
  const provided = [
    'Beach towels, shampoo, conditioner, and bath gel',
    'Life jackets for adults and children',
    'Paddle boards, kayaks, and a boogie board',
    'Beach chairs and umbrellas',
    'Pool toys for the kids',
  ];

  const card = ({ icon, eyebrow, title, body, cta, href, onClick }) => {
    const inner = (
      <>
        <Icon name={icon} size={28}/>
        <div className="eyebrow" style={{color: 'var(--terracotta)'}}>{eyebrow}</div>
        <h3 className="serif" style={{fontSize: 28, fontWeight: 400}}>{title}</h3>
        <p className="body" style={{fontSize: 15, flex: 1}}>{body}</p>
        <div style={{fontFamily: 'var(--sans)', fontSize: 12, letterSpacing: '0.16em', textTransform: 'uppercase', color: 'var(--terracotta)', fontWeight: 500}}>{cta}</div>
      </>
    );
    const styleProps = {padding: 40, display: 'flex', flexDirection: 'column', gap: 16, textAlign: 'left', textDecoration: 'none'};
    if (onClick) return <button key={title} onClick={onClick} className="card" style={{...styleProps, cursor: 'pointer', border: 'none', font: 'inherit', background: 'var(--white)'}}>{inner}</button>;
    return <a key={title} href={href} target="_blank" rel="noreferrer" className="card" style={styleProps}>{inner}</a>;
  };

  return (
  <>
    <section style={{padding: '120px 0 60px'}}>
      <div className="container">
        <SectionHead
          as="h1"
          eyebrow="Before You Arrive"
          title="Everything for your stay."
          intro="Two HostMoat-hosted resources cover the whole trip: the pre-arrival form (for logistics we need from you) and the guidebook (for everything you need from us). Most answers are in there. If yours isn't, send Hillary a note."
        />
      </div>
    </section>

    <div className="container" style={{paddingBottom: 80}}>
      <div className="grid-3">
        {card({
          icon: 'calendar',
          eyebrow: 'Step 1 · 5+ days ahead',
          title: 'Pre-Arrival Form',
          body: 'Flight details, dietary preferences, grocery requests, pre-paid food & drink, airport pickup. One person fills it out for the whole group; you can redo it anytime.',
          cta: preArrival ? 'Open Form →' : 'Ask Hillary →',
          ...(preArrival ? { href: preArrival } : { onClick: () => navigate('contact') }),
        })}
        {card({
          icon: 'file',
          eyebrow: 'Everything else',
          title: 'The Guidebook',
          body: 'Welcome letter from Wayne & Karen, full contact list, airport & transport, Wi-Fi, pool & beach, chef & menus, bedrooms, concierge, and house rules. The single source of truth for your stay.',
          cta: 'Open Guidebook →',
          href: guidebook,
        })}
        {card({
          icon: 'mapPin',
          eyebrow: "While you're here",
          title: 'Things to Do in PV',
          body: 'Whale watching, zip-lining, horseback trails, snorkeling at Los Arcos, sport fishing, and yoga at the villa. Kike will help you plan and book through reputable outfitters.',
          cta: 'Browse Activities →',
          onClick: () => navigate('activities'),
        })}
      </div>
    </div>

    <section className="section section-warm">
      <div className="container">
        <SectionHead eyebrow="Quick Reference" title="The essentials."/>
        <div className="grid-2" style={{gap: 60}}>
          <div className="stack-lg">
            {essentialsLeft.map(([k, v]) => (
              <div key={k}>
                <div className="eyebrow" style={{marginBottom: 8}}>{k}</div>
                <div className="body" style={{fontSize: 17}}>{v}</div>
              </div>
            ))}
          </div>
          <div className="stack-lg">
            {essentialsRight.map(([k, v]) => (
              <div key={k}>
                <div className="eyebrow" style={{marginBottom: 8}}>{k}</div>
                <div className="body" style={{fontSize: 17}}>{v}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>

    <section className="section">
      <div className="container">
        <div className="grid-2" style={{alignItems: 'flex-start', gap: 80}}>
          <div>
            <div className="eyebrow" style={{marginBottom: 20}}>What to Pack</div>
            <h2 className="display" style={{fontSize: 'clamp(28px, 3.5vw, 42px)', marginBottom: 28}}>Bring these.</h2>
            <ul style={{listStyle: 'none', padding: 0, fontSize: 16, lineHeight: 1.9, margin: 0}}>
              {packing.map(p => (<li key={p} style={{paddingLeft: 28, position: 'relative', marginBottom: 10}}><span style={{position: 'absolute', left: 0, color: 'var(--terracotta)'}}>✓</span>{p}</li>))}
            </ul>
          </div>
          <div>
            <div className="eyebrow" style={{marginBottom: 20}}>Provided at the Villa</div>
            <h2 className="display" style={{fontSize: 'clamp(28px, 3.5vw, 42px)', marginBottom: 28}}>We've got these.</h2>
            <ul style={{listStyle: 'none', padding: 0, fontSize: 16, lineHeight: 1.9, margin: 0}}>
              {provided.map(p => (<li key={p} style={{paddingLeft: 28, position: 'relative', marginBottom: 10}}><span style={{position: 'absolute', left: 0, color: 'var(--sage)'}}>✓</span>{p}</li>))}
            </ul>
          </div>
        </div>
      </div>
    </section>

    <section className="section section-dark">
      <div className="container">
        <div style={{maxWidth: 720, marginBottom: 40}}>
          <div className="eyebrow" style={{color: 'rgba(250,246,239,0.6)', marginBottom: 20}}>House Rules (summary)</div>
          <h2 className="display" style={{color: 'var(--bg)', fontSize: 'clamp(28px, 3.5vw, 42px)', marginBottom: 16}}>A few housekeeping notes.</h2>
          <p className="body" style={{fontSize: 16, color: 'rgba(250,246,239,0.75)'}}>These cover the basics — the full list, plus pool and Los Palmares rules, is in the guidebook.</p>
        </div>
        <div className="grid-2" style={{gap: 40, color: 'rgba(250,246,239,0.9)'}}>
          <ul style={{listStyle: 'none', padding: 0, fontSize: 15, lineHeight: 1.8, margin: 0}}>
            {rulesLeft.map(r => (<li key={r} style={{paddingLeft: 22, position: 'relative', marginBottom: 12}}><span style={{position: 'absolute', left: 0, color: 'var(--sand)'}}>·</span>{r}</li>))}
          </ul>
          <ul style={{listStyle: 'none', padding: 0, fontSize: 15, lineHeight: 1.8, margin: 0}}>
            {rulesRight.map(r => (<li key={r} style={{paddingLeft: 22, position: 'relative', marginBottom: 12}}><span style={{position: 'absolute', left: 0, color: 'var(--sand)'}}>·</span>{r}</li>))}
          </ul>
        </div>
        <div style={{marginTop: 40}}><a className="btn btn-light" href={guidebook} target="_blank" rel="noreferrer">Open full guidebook →</a></div>
      </div>
    </section>

    <section className="section">
      <div className="container-narrow text-center">
        <div className="eyebrow" style={{marginBottom: 20}}>Still have questions?</div>
        <h2 className="display" style={{fontSize: 'clamp(28px, 3vw, 38px)', marginBottom: 24}}>Just ask Hillary.</h2>
        <p className="body" style={{fontSize: 16, marginBottom: 32}}>Hillary handles every guest group personally, from pre-arrival planning through departure. If the guidebook doesn't answer it, she will.</p>
        <button className="btn btn-primary" onClick={() => navigate('contact')}>Ask Hillary</button>
      </div>
    </section>
  </>
  );
};

const ActivitiesPage = () => {
  const { settings } = useSite();
  const bookProps = bookCtaProps(settings);
  // Split activities by where they happen. On-villa items can use real villa photos and sit at the top.
  // Off-villa items use a cleaner text-forward list so visitors aren't looking at the master bedroom
  // next to "Zip-lining". Kike (Enrique) helps plan + book everything off-site.
  const onVilla = [
    { title: 'Kayak the Cove',     cat: 'Included',                  desc: 'A kayak lives at the villa — head out for a quiet morning paddle along the coast. Paddle boards and boogie boards included too.',            img: IMAGES.porch2 },
    { title: 'Yoga at the Villa',  cat: 'On Request',                desc: "Private instructor on the front porch at sunrise. Villa Karaway has hosted full yoga retreats. The upstairs deck is the spot.",            img: IMAGES.elCielo3 },
    { title: 'Whale Watching',     cat: 'Mid-Dec – Late March',      desc: 'Humpback whales migrate past Bahía de Banderas each winter to calve — sometimes visible right from the deck. Morning charters from Marina Vallarta.', img: IMAGES.rooms },
    { title: 'Snorkel Off the Beach', cat: 'Steps from the Patio',   desc: 'Clear water and small reefs just off Playa Punta Negra. Bring your own mask + fins (we no longer provide them).',                       img: IMAGES.porch1 },
  ];
  const offVilla = [
    { title: 'Sport Fishing',        cat: 'Half / Full Day',        desc: 'Marlin, dorado, sailfish. Private charters from Marina Vallarta or Boca de Tomatlán.' },
    { title: 'Zip-lining',           cat: 'Jungle Canopy',          desc: 'See the Sierra Madre from above. Several outfitters run half-day tours from PV.' },
    { title: 'Horseback Riding',     cat: 'Coast & Jungle',         desc: 'Trail rides along the beach and up into the jungle. Morning and sunset sessions.' },
    { title: 'Los Arcos & Marietas', cat: 'Snorkel / Dive',         desc: 'Los Arcos National Marine Park for easy snorkel; the Marietas Islands for a UNESCO day out.' },
    { title: 'Hiking',               cat: 'Sierra Madre',           desc: 'Trails in the Vallarta Botanical Gardens, Yelapa waterfalls, and El Cuale up the river.' },
    { title: 'Vallarta Zoo',         cat: 'Mismaloya — 20 min S',   desc: 'Small, hands-on zoo south of the villa. Good for families with kids.' },
    { title: 'Downtown Vallarta',    cat: '15 Min North',           desc: 'El Malecón, Los Muertos beach, the old town cathedral, galleries, taquerías, and nightlife.' },
    { title: 'Shopping & Nightlife', cat: 'Old Town',               desc: 'Artisan markets by day, rooftop bars and beach clubs by night. Most are 15 min away.' },
    { title: 'Massage at the Villa', cat: 'Two Tables On-Site',     desc: 'We have two massage tables. 60 or 90 minute sessions arranged by staff. Spa robes provided.' },
    { title: 'Private Chef Dinner',  cat: 'At the Villa',           desc: 'Ask Gabriel for a special-occasion menu — anniversary, birthday, just-because. Sunset on the patio.' },
  ];
  return (
    <>
      <section style={{padding: '120px 0 40px'}}>
        <div className="container"><SectionHead as="h1" eyebrow="Find Adventure" title="Puerto Vallarta awaits." intro="Some of the best of Puerto Vallarta happens right at the villa — kayaks, yoga, whales in the bay from mid-December through late March. For everything else, Kike helps you plan and books through reputable outfitters."/></div>
      </section>

      <section style={{padding: '0 0 40px'}}>
        <div className="container">
          <div className="eyebrow" style={{marginBottom: 24}}>At the Villa</div>
          <div className="grid-4">
            {onVilla.map(a => (
              <div key={a.title} className="card" style={{display: 'flex', flexDirection: 'column'}}>
                <div className="photo" style={{aspectRatio: '4/3'}}><img src={a.img} alt={a.title} loading="lazy"/></div>
                <div style={{padding: 24, flex: 1, display: 'flex', flexDirection: 'column'}}>
                  <div className="eyebrow" style={{marginBottom: 8, color: 'var(--terracotta)'}}>{a.cat}</div>
                  <h3 className="serif" style={{fontSize: 22, fontWeight: 400, marginBottom: 10, letterSpacing: '-0.01em'}}>{a.title}</h3>
                  <p className="body" style={{fontSize: 14}}>{a.desc}</p>
                </div>
              </div>
            ))}
          </div>
        </div>
      </section>

      <section style={{padding: '40px 0 80px'}}>
        <div className="container">
          <div className="eyebrow" style={{marginBottom: 24}}>Around Puerto Vallarta</div>
          <div className="grid-2" style={{gap: 40}}>
            {offVilla.map(a => (
              <div key={a.title} style={{paddingTop: 24, borderTop: '1px solid var(--line-soft)'}}>
                <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 16, marginBottom: 8}}>
                  <h3 className="serif" style={{fontSize: 22, fontWeight: 400, letterSpacing: '-0.01em'}}>{a.title}</h3>
                  <div className="eyebrow" style={{color: 'var(--terracotta)', whiteSpace: 'nowrap'}}>{a.cat}</div>
                </div>
                <p className="body" style={{fontSize: 14}}>{a.desc}</p>
              </div>
            ))}
          </div>
        </div>
      </section>

      <section className="section section-warm">
        <div className="container-narrow text-center">
          <div className="eyebrow" style={{marginBottom: 20}}>Let Kike handle it</div>
          <h2 className="display" style={{fontSize: 'clamp(28px, 3.5vw, 42px)', marginBottom: 24}}>Plan on arrival.</h2>
          <p className="body" style={{fontSize: 16, marginBottom: 32}}>Kike (our villa manager) books every activity for guests through vetted operators. Tell him what you're interested in when you arrive and he'll line it up.</p>
          <a className="btn btn-primary" {...bookProps}>Book your stay</a>
        </div>
      </section>
    </>
  );
};

// Generic page renderer for admin-created pages that don't have hand-coded components.
// Renders hero + sanitized body_html + optional widgets bound to this page.
const GenericPage = ({ pageId }) => {
  const { pages, widgets } = useSite();
  const page = pages.find(p => p.id === pageId || p.slug === '/' + pageId || p.slug === pageId);
  const pageWidgets = widgets.filter(w => w.placement === pageId + ' page' || w.placement === 'custom page');

  if (!page) {
    return (
      <section style={{padding: '160px 0', textAlign: 'center'}}>
        <div className="container">
          <SectionHead as="h1" eyebrow="Page not found" title={pageId}/>
          <p className="body">This page doesn't exist yet.</p>
        </div>
      </section>
    );
  }

  return (
    <>
      <section style={{padding: '120px 0 60px'}}>
        <div className="container">
          <SectionHead as="h1" eyebrow={page.title} title={page.hero_headline || page.title}/>
        </div>
      </section>
      {page.hero_image && (
        <div className="container" style={{marginBottom: 60}}>
          <div className="photo" style={{aspectRatio: '16/9'}}>
            <img src={resolvePhoto(page.hero_image)} alt={page.title}/>
          </div>
        </div>
      )}
      {page.body_html && (
        <div className="container-narrow" style={{paddingBottom: 80}}>
          <div className="body-serif prose" dangerouslySetInnerHTML={{__html: page.body_html}}/>
        </div>
      )}
      {pageWidgets.length > 0 && (
        <div className="container" style={{paddingBottom: 120}}>
          {pageWidgets.map(w => (
            <div key={w.id} style={{marginBottom: 32}}>
              <iframe
                src={w.kind === 'iframe' ? w.src : undefined}
                srcDoc={w.kind === 'html' ? w.src : undefined}
                style={{width: w.width || '100%', height: (w.height || 400) + 'px', border: 'none', maxWidth: '100%'}}
                sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
              />
            </div>
          ))}
        </div>
      )}
    </>
  );
};

// Availability page — embeds HostMoat's calendar-embed edge function.
// IMPORTANT: calendar-embed returns `Content-Type: text/plain` to browsers, so a direct
// <iframe src=...> would render the HTML as raw text. Workaround: fetch as text
// client-side, then set as `srcDoc`. This mirrors what HostMoat's own booking-site does.
const AvailabilityPage = () => {
  const { settings } = useSite();
  const propId = settings.hostmoat_property_id;
  const bookProps = bookCtaProps(settings);
  const [html, setHtml] = useStateP(null);
  const [err, setErr] = useStateP(null);
  const [frameHeight, setFrameHeight] = useStateP(720);
  const frameRef = useRefP(null);
  const observerRef = useRefP(null);
  // Track the last-applied height in a ref so async callbacks still compare against
  // the current value rather than the height captured at initial render.
  const lastHeightRef = useRefP(720);

  useEffectP(() => {
    if (!propId) return;
    let cancel = false;
    const url = `https://tofekzsdwdpyevrdluhb.supabase.co/functions/v1/calendar-embed?property_id=${encodeURIComponent(propId)}&months=6&show_prices=1`;
    fetch(url, { credentials: 'omit' })
      .then(r => r.ok ? r.text() : Promise.reject(new Error(`HTTP ${r.status}`)))
      .then(t => { if (!cancel) setHtml(t); })
      .catch(e => { if (!cancel) setErr(String(e)); });
    return () => { cancel = true; };
  }, [propId]);

  // Disconnect the inner-document observer when the page unmounts so the iframe's
  // contentDocument can be GC'd cleanly.
  useEffectP(() => () => {
    if (observerRef.current) { observerRef.current.disconnect(); observerRef.current = null; }
  }, []);

  // Auto-size: srcDoc iframes with allow-same-origin are accessible, so we can measure
  // the embedded document and grow the iframe to fit — no internal scrollbar needed.
  const handleFrameLoad = () => {
    const f = frameRef.current;
    if (!f) return;
    const doc = f.contentDocument;
    if (!doc) return;
    // Kill inner scrolling — we always resize the iframe to fit, so a scrollbar inside
    // the calendar means our resize hasn't caught up yet. Hiding overflow makes the
    // iframe simply grow when content gets taller, never scroll.
    try {
      if (doc.documentElement) doc.documentElement.style.overflow = 'hidden';
      if (doc.body) doc.body.style.overflow = 'hidden';
      // The inquiry-toggle inside the embed calls form.scrollIntoView() when the form
      // opens. With overflow hidden that walks up to the parent window and yanks the
      // page. Neutralize it inside the iframe so expanding the form is purely layout.
      const w = f.contentWindow;
      if (w?.Element) w.Element.prototype.scrollIntoView = function(){};
    } catch { /* cross-origin safety */ }
    const measure = () => {
      try {
        const h = Math.max(
          doc.documentElement.scrollHeight,
          doc.body?.scrollHeight || 0,
        );
        if (h && Math.abs(h - lastHeightRef.current) > 4) {
          lastHeightRef.current = h + 8;
          setFrameHeight(h + 8);
        }
      } catch { /* cross-origin safety */ }
    };
    measure();
    // Watch the inner document so font/image loads, month nav clicks, and any other
    // dynamic reflow inside the calendar grow the iframe instead of triggering its
    // own scrollbar.
    if (observerRef.current) observerRef.current.disconnect();
    const ro = new ResizeObserver(measure);
    ro.observe(doc.documentElement);
    if (doc.body) ro.observe(doc.body);
    observerRef.current = ro;
  };

  return (
    <>
      <section style={{padding: '120px 0 40px'}}>
        <div className="container">
          <SectionHead as="h1" eyebrow="Check the calendar" title="Availability." intro="Pick your dates. Rates and open nights come straight from our HostMoat booking calendar — you’ll see exactly what we see."/>
        </div>
      </section>
      <div className="container" style={{paddingBottom: 40}}>
        {!propId ? (
          <div style={{padding: '40px 32px', border: '1px solid var(--line)', background: 'var(--bg-warm)', textAlign: 'center'}}>
            <p className="body" style={{fontSize: 16, marginBottom: 20}}>Our live calendar is offline at the moment. For current availability and rates, send us a quick message and Hillary will get back to you within 24 hours.</p>
            <a className="btn btn-primary" {...bookProps}>Check availability</a>
          </div>
        ) : err ? (
          <p className="body" style={{color: '#a0403a'}}>Couldn’t load the calendar: {err}. <a {...bookProps} style={{color: 'var(--terracotta)'}}>Open the booking site instead →</a></p>
        ) : html ? (
          <iframe
            ref={frameRef}
            srcDoc={html}
            title="Villa Karaway Availability"
            scrolling="no"
            onLoad={handleFrameLoad}
            style={{width: '100%', height: frameHeight, border: '1px solid var(--line)', background: 'var(--white)', overflow: 'hidden', display: 'block'}}
            sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
          />
        ) : (
          <div style={{width: '100%', height: 720, border: '1px solid var(--line)', background: 'var(--bg-warm)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'var(--muted)', fontSize: 14, letterSpacing: '0.08em'}}>Loading calendar…</div>
        )}
      </div>
      <section className="section section-warm">
        <div className="container-narrow text-center">
          <div className="eyebrow" style={{marginBottom: 20}}>Ready to book?</div>
          <h2 className="display" style={{fontSize: 'clamp(32px, 4vw, 48px)', marginBottom: 28}}>Head to the direct booking site.</h2>
          <p className="body" style={{fontSize: 16, marginBottom: 32}}>Every confirmed booking is managed through HostMoat — you’ll get the reservation, payment, and pre-arrival form all in one place.</p>
          <a className="btn btn-primary" {...bookProps}>Open Booking Site</a>
        </div>
      </section>
    </>
  );
};

Object.assign(window, { HomePage, RoomsPage, GalleryPage, MenuPage, ExperiencePage, ReviewsPage, ContactPage, VideoPage, WebcamPage, GuestInfoPage, ActivitiesPage, AvailabilityPage, GenericPage });
