const { useEffect, useRef, useState } = React;

const LANGUAGE_COOKIE_NAME = 'captainsean.lang';
const LANGUAGE_STORAGE_KEY = 'captainsean.lang';

const FALLBACK_IMAGES = [
  '/img/Carousel/CaptainSean.webp',
  '/img/Carousel/CaptainSean2.webp',
  '/img/Carousel/dolphin2.webp',
  '/img/Carousel/dolphin3.webp',
  '/img/Carousel/dolphin4.webp',
  '/img/Carousel/dolphin5.webp',
  '/img/Carousel/IMG_3379.webp',
  '/img/Carousel/manatee6.webp',
  '/img/Carousel/manatee7.webp',
  '/img/Carousel/Shells.webp'
];

const FALLBACK_GALLERY_IMAGES = [
  '/img/Gallery/dolphin2.webp',
  '/img/Gallery/dolphin3.webp',
  '/img/Gallery/dolphin4.webp',
  '/img/Gallery/dolphin5.webp',
  '/img/Gallery/IMG_3379.webp',
  '/img/Gallery/IMG_3384.webp',
  '/img/Gallery/IMG_3420.webp',
  '/img/Gallery/IMG_3421.webp',
  '/img/Gallery/IMG_3431.webp',
  '/img/Gallery/manatee6.webp',
  '/img/Gallery/manatee7.webp'
];

const HERO_INTERVAL_MS = 8000;

const LANGUAGE_OPTIONS = [
  { code: 'en', flagSrc: '/img/flags/flag-us.svg' },
  { code: 'es', flagSrc: '/img/flags/flag-es.svg' }
];

const TOUR_TRANSLATIONS = {
  'Sunset Dolphin Tour': {
    es: {
      title: 'Tour de delfines al atardecer',
      description: 'Vive los delfines de Cape Coral al atardecer.',
      duration: '2 horas'
    }
  },
  'Private Dolphin Adventure': {
    es: {
      title: 'Aventura privada con delfines',
      description: 'Tour privado para familias y grupos pequeños.',
      duration: '3 horas'
    }
  },
  'Eco and Wildlife Tour': {
    es: {
      title: 'Tour ecológico y de vida silvestre',
      description: 'Aprende sobre la fauna local y los ecosistemas.',
      duration: '2.5 horas'
    }
  }
};

const I18N = {
  en: {
    pageTitle: 'Home Page - CaptainSean',
    brand: {
      logoSrc: '/img/captain-sean-logo.svg',
      name: 'Captain Sean',
      subtitle: 'Cape Coral ECO Tours',
      homeAria: 'Captain Sean home'
    },
    language: {
      switcherLabel: 'Select language',
      english: 'English',
      spanish: 'Español'
    },
    nav: {
      primaryLabel: 'Primary',
      tours: 'Tours',
      about: 'About',
      gallery: 'Gallery',
      contact: 'Contact'
    },
    buttons: {
      reserveCruise: 'Reserve cruise',
      exploreTours: 'Explore tours',
      viewGallery: 'View gallery',
      reserve: 'Reserve',
      backToTop: 'Back to top'
    },
    hero: {
      kicker: 'Cape Coral dolphin tours | private coastal cruises',
      title: 'Relaxed wildlife cruises shaped by tide, light, and the best water of the day.',
      summary:
        'Captain Sean guides small-group tours through Cape Coral waterways with a quieter, more editorial feel: less rush, more shoreline, more room to spot dolphins and manatees.',
      panelKicker: 'On the water',
      highlights: [
        { label: 'Private cruises', value: 'Small groups, calm pace, local knowledge.' },
        { label: 'Wildlife focus', value: 'Dolphins, manatees, estuary birds, and quiet coves.' },
        { label: 'Cape Coral based', value: 'Designed around Southwest Florida light and water.' }
      ],
      previousSlide: 'Previous slide',
      nextSlide: 'Next slide',
      goToSlide: (index) => `Go to slide ${index}`
    },
    intro: {
      kicker: 'What we do',
      title: 'Private dolphin tours, sunset runs, and low-key wildlife watching.',
      points: [
        'Sunset cruises with room to slow down and watch the shoreline.',
        'Family-friendly rides shaped around comfort, safety, and wildlife timing.',
        'Local route knowledge for protected water, photo stops, and quieter inlets.'
      ]
    },
    tours: {
      kicker: 'Tours',
      title: 'Built for calm water, clean sight lines, and memorable wildlife passes.',
      customTiming: 'Custom timing available',
      customDescription: 'A custom route built around your group and the conditions that day.',
      callForDetails: 'Call for details'
    },
    story: {
      kicker: 'About Captain Sean',
      title: 'Local routes, steady pacing, and a tour style that leaves room to notice the water.',
      body:
        'Captain Sean has spent years running guests through Cape Coral channels and coastal edges. The goal is not to rush through a checklist. It is to shape a better day on the water: safe navigation, better timing, and more space for real wildlife encounters.',
      imageAlt: 'Captain Sean'
    },
    gallery: {
      kicker: 'Gallery',
      title: 'Captain Sean wildlife moments on the water.',
      countLabel: (count) => `${count} photos`,
      openImageLabel: (index) => `Open gallery image ${index}`,
      imageAlt: (index) => `Captain Sean dolphin and wildlife tour gallery photo ${index}`
    },
    lightbox: {
      close: 'Close gallery view',
      previous: 'Previous image',
      next: 'Next image'
    },
    contact: {
      kicker: 'Contact',
      title: 'Tell Captain Sean what kind of day on the water you want.',
      body:
        'Use the contact details below to ask about timing, group size, wildlife conditions, or private charter options. The visual tone stays refined and coastal, but the content remains centered on your tour business.',
      details: [
        { title: 'Call', value: '(555) 555-5555', href: 'tel:5555555555' },
        { title: 'Email', value: 'beingseanjones@gmail.com', href: 'mailto:beingseanjones@gmail.com' },
        { title: 'Launch area', value: 'Cape Coral, Florida', href: '#story' }
      ],
      form: {
        title: 'Send a quick message',
        nameLabel: 'Name',
        namePlaceholder: 'Your name',
        emailLabel: 'Email',
        emailPlaceholder: 'you@example.com',
        messageLabel: 'Trip details',
        messagePlaceholder: 'Preferred date, group size, dolphins, sunset, or special requests',
        submit: 'Send message',
        sending: 'Sending...',
        success: "Thanks! We'll reply shortly.",
        error: 'Something went wrong. Try again?'
      }
    },
    footer: {
      tagline: 'Captain Sean coastal tours for dolphin, manatee, and sunset cruises.',
      backToTop: 'Back to top'
    }
  },
  es: {
    pageTitle: 'Inicio - CaptainSean',
    brand: {
      logoSrc: '/img/captain-sean-logo-es.svg',
      name: 'Captain Sean',
      subtitle: 'Tours ecológicos en Cape Coral',
      homeAria: 'Inicio de Captain Sean'
    },
    language: {
      switcherLabel: 'Seleccionar idioma',
      english: 'English',
      spanish: 'Español'
    },
    nav: {
      primaryLabel: 'Principal',
      tours: 'Tours',
      about: 'Nosotros',
      gallery: 'Galería',
      contact: 'Contacto'
    },
    buttons: {
      reserveCruise: 'Reserva tu paseo',
      exploreTours: 'Explorar tours',
      viewGallery: 'Ver galería',
      reserve: 'Reservar',
      backToTop: 'Volver arriba'
    },
    hero: {
      kicker: 'Tours de delfines en Cape Coral | paseos costeros privados',
      title: 'Paseos tranquilos de vida silvestre guiados por la marea, la luz y el mejor momento del día en el agua.',
      summary:
        'Captain Sean guía tours en grupos pequeños por las vías navegables de Cape Coral con un ritmo más tranquilo: menos prisa, más costa y más oportunidades de ver delfines y manatíes.',
      panelKicker: 'En el agua',
      highlights: [
        { label: 'Cruceros privados', value: 'Grupos pequeños, ritmo tranquilo y conocimiento local.' },
        { label: 'Enfoque en la fauna', value: 'Delfines, manatíes, aves del estuario y calas tranquilas.' },
        { label: 'Con base en Cape Coral', value: 'Pensado alrededor de la luz y el agua del suroeste de Florida.' }
      ],
      previousSlide: 'Diapositiva anterior',
      nextSlide: 'Siguiente diapositiva',
      goToSlide: (index) => `Ir a la diapositiva ${index}`
    },
    intro: {
      kicker: 'Qué hacemos',
      title: 'Tours privados de delfines, paseos al atardecer y observación tranquila de vida silvestre.',
      points: [
        'Paseos al atardecer con tiempo para bajar el ritmo y mirar la costa.',
        'Recorridos para familias pensados para comodidad, seguridad y mejores horarios para ver fauna.',
        'Rutas locales por aguas protegidas, paradas para fotos y entradas más tranquilas.'
      ]
    },
    tours: {
      kicker: 'Tours',
      title: 'Diseñados para aguas tranquilas, buena visibilidad y encuentros memorables con la fauna.',
      customTiming: 'Horario personalizado disponible',
      customDescription: 'Una ruta personalizada pensada para tu grupo y las condiciones de ese día.',
      callForDetails: 'Llama para más detalles'
    },
    story: {
      kicker: 'Sobre Captain Sean',
      title: 'Rutas locales, ritmo sereno y un estilo de tour que deja espacio para apreciar el agua.',
      body:
        'Captain Sean lleva años guiando a sus huéspedes por los canales y costas de Cape Coral. El objetivo no es correr por una lista. Es crear un mejor día en el agua: navegación segura, mejor momento para salir y más espacio para encuentros reales con la vida silvestre.',
      imageAlt: 'Captain Sean'
    },
    gallery: {
      kicker: 'Galería',
      title: 'Momentos de vida silvestre de Captain Sean en el agua.',
      countLabel: (count) => `${count} fotos`,
      openImageLabel: (index) => `Abrir imagen de la galería ${index}`,
      imageAlt: (index) => `Foto ${index} de la galería de delfines y vida silvestre de Captain Sean`
    },
    lightbox: {
      close: 'Cerrar vista de galería',
      previous: 'Imagen anterior',
      next: 'Imagen siguiente'
    },
    contact: {
      kicker: 'Contacto',
      title: 'Cuéntale a Captain Sean qué tipo de día en el agua quieres.',
      body:
        'Usa los datos de contacto a continuación para preguntar por horarios, tamaño del grupo, condiciones para ver fauna u opciones de charter privado. El tono visual sigue siendo refinado y costero, pero el contenido está totalmente centrado en tu negocio de tours.',
      details: [
        { title: 'Llamar', value: '(555) 555-5555', href: 'tel:5555555555' },
        { title: 'Correo', value: 'beingseanjones@gmail.com', href: 'mailto:beingseanjones@gmail.com' },
        { title: 'Zona de salida', value: 'Cape Coral, Florida', href: '#story' }
      ],
      form: {
        title: 'Envia un mensaje rapido',
        nameLabel: 'Nombre',
        namePlaceholder: 'Tu nombre',
        emailLabel: 'Correo',
        emailPlaceholder: 'tu@correo.com',
        messageLabel: 'Detalles del paseo',
        messagePlaceholder: 'Fecha preferida, tamano del grupo, delfines, atardecer o peticiones especiales',
        submit: 'Enviar mensaje',
        sending: 'Enviando...',
        success: 'Gracias. Te responderemos pronto.',
        error: 'Algo salio mal. Intentalo de nuevo.'
      }
    },
    footer: {
      tagline: 'Tours costeros de Captain Sean para ver delfines, manatíes y atardeceres.',
      backToTop: 'Volver arriba'
    }
  }
};

function readCookie(name) {
  if (typeof document === 'undefined') {
    return '';
  }

  const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  const match = document.cookie.match(new RegExp(`(?:^|; )${escapedName}=([^;]*)`));
  return match ? decodeURIComponent(match[1]) : '';
}

function normalizeLanguage(value) {
  return value === 'es' ? 'es' : 'en';
}

function getInitialLanguage() {
  if (typeof window === 'undefined') {
    return 'en';
  }

  const seededLanguage = normalizeLanguage(window.__CAPTAIN_SEAN_LANG__);
  const cookieLanguage = normalizeLanguage(readCookie(LANGUAGE_COOKIE_NAME));

  if (cookieLanguage === 'es') {
    return 'es';
  }

  if (seededLanguage === 'es') {
    return 'es';
  }

  try {
    return normalizeLanguage(window.localStorage.getItem(LANGUAGE_STORAGE_KEY));
  } catch {
    return 'en';
  }
}

function persistLanguage(language) {
  const normalized = normalizeLanguage(language);

  if (typeof document !== 'undefined') {
    document.cookie = `${LANGUAGE_COOKIE_NAME}=${encodeURIComponent(normalized)}; path=/; max-age=31536000; SameSite=Lax`;
  }

  if (typeof window !== 'undefined') {
    try {
      window.localStorage.setItem(LANGUAGE_STORAGE_KEY, normalized);
    } catch {
      // Ignore storage failures and keep the cookie as the source of truth.
    }
  }
}

function loadJson(url, fallbackValue) {
  return fetch(url)
    .then((response) => (response.ok ? response.json() : Promise.reject(new Error(response.statusText))))
    .catch(() => fallbackValue);
}

function classifyImageVariant(width, height) {
  if (!width || !height) {
    return 'landscape';
  }

  const ratio = width / height;

  if (ratio >= 1.65) {
    return 'panorama';
  }

  if (ratio >= 1.08) {
    return 'landscape';
  }

  if (ratio <= 0.82) {
    return 'portrait';
  }

  return 'square';
}

function resolveTourImage(tour, images, index) {
  if (tour.imageUrl && tour.imageUrl.startsWith('/img/Carousel/')) {
    return tour.imageUrl;
  }

  return images[index % images.length] || FALLBACK_IMAGES[index % FALLBACK_IMAGES.length];
}

function formatPrice(value, language) {
  if (typeof value !== 'number') {
    return I18N[language].tours.callForDetails;
  }

  return new Intl.NumberFormat(language === 'es' ? 'es-US' : 'en-US', {
    style: 'currency',
    currency: 'USD'
  }).format(value);
}

function translateDuration(duration, language) {
  if (!duration || language !== 'es') {
    return duration;
  }

  return duration
    .replace(/\bhours\b/gi, 'horas')
    .replace(/\bhour\b/gi, 'hora');
}

function translateTour(tour, language) {
  if (language !== 'es') {
    return tour;
  }

  const translation = TOUR_TRANSLATIONS[tour.title]?.es;

  return {
    ...tour,
    title: translation?.title ?? tour.title,
    description: translation?.description ?? tour.description,
    duration: translation?.duration ?? translateDuration(tour.duration, language)
  };
}

function galleryAltText(index, copy) {
  return copy.gallery.imageAlt(index + 1);
}

function Header({ language, copy, onLanguageChange }) {
  const headerRef = useRef(null);

  useEffect(() => {
    const header = headerRef.current;

    if (!header) {
      return undefined;
    }

    const root = document.documentElement;
    let frameId = 0;

    const updateHeaderOffset = () => {
      const height = Math.ceil(header.getBoundingClientRect().height);
      root.style.setProperty('--header-offset', `${height + 20}px`);
    };

    const scheduleUpdate = () => {
      window.cancelAnimationFrame(frameId);
      frameId = window.requestAnimationFrame(updateHeaderOffset);
    };

    updateHeaderOffset();

    const resizeObserver = typeof ResizeObserver === 'function'
      ? new ResizeObserver(scheduleUpdate)
      : null;

    resizeObserver?.observe(header);
    window.addEventListener('resize', scheduleUpdate);
    window.addEventListener('orientationchange', scheduleUpdate);

    return () => {
      window.cancelAnimationFrame(frameId);
      resizeObserver?.disconnect();
      window.removeEventListener('resize', scheduleUpdate);
      window.removeEventListener('orientationchange', scheduleUpdate);
    };
  }, []);

  return (
    <header className="site-header" ref={headerRef}>
      <div className="site-shell header-inner">
        <a className="brand-mark" href="#top" aria-label={copy.brand.homeAria}>
          <img className="brand-logo" src={copy.brand.logoSrc} alt="Captain Sean logo" />
          <span className="brand-copy">
            <strong>{copy.brand.name}</strong>
            <small>{copy.brand.subtitle}</small>
          </span>
        </a>
        <nav className="site-nav" aria-label={copy.nav.primaryLabel}>
          <a href="#tours">{copy.nav.tours}</a>
          <a href="#story">{copy.nav.about}</a>
          <a href="#gallery">{copy.nav.gallery}</a>
          <a href="#contact">{copy.nav.contact}</a>
        </nav>
        <div className="header-tools">
          <div className="lang-toggle" role="group" aria-label={copy.language.switcherLabel}>
            {LANGUAGE_OPTIONS.map((option) => {
              const isActive = option.code === language;
              const label = option.code === 'es' ? copy.language.spanish : copy.language.english;

              return (
                <button
                  key={option.code}
                  type="button"
                  className={`lang-btn${isActive ? ' active' : ''}`}
                  aria-label={label}
                  aria-pressed={isActive}
                  title={label}
                  onClick={() => onLanguageChange(option.code)}
                >
                  <img src={option.flagSrc} alt="" aria-hidden="true" />
                </button>
              );
            })}
          </div>
          <a className="pill-button pill-button-light header-cta" href="#contact">{copy.buttons.reserveCruise}</a>
        </div>
      </div>
    </header>
  );
}

function Hero({ images, copy }) {
  const [active, setActive] = useState(0);
  const [variants, setVariants] = useState({});

  const handleImageLoad = (src) => (event) => {
    const nextVariant = classifyImageVariant(
      event.currentTarget.naturalWidth,
      event.currentTarget.naturalHeight
    );

    setVariants((current) => (
      current[src] === nextVariant ? current : { ...current, [src]: nextVariant }
    ));
  };

  useEffect(() => {
    if (images.length < 2) {
      return undefined;
    }

    const id = window.setInterval(() => {
      setActive((previous) => (previous + 1) % images.length);
    }, HERO_INTERVAL_MS);

    return () => {
      window.clearInterval(id);
    };
  }, [images]);

  useEffect(() => {
    setActive(0);
  }, [images]);

  const goPrev = () => setActive((previous) => (previous <= 0 ? images.length - 1 : previous - 1));
  const goNext = () => setActive((previous) => (previous + 1) % images.length);

  return (
    <section className="hero-full" id="top">
      <div className="hero-carousel">
        <div className="hero-slides" aria-live="polite">
          {images.map((src, index) => {
            const isActive = index === active;
            const variant = variants[src] || 'landscape';
            return (
              <div
                key={src}
                className={`hero-slide hero-slide--${variant}${isActive ? ' is-active' : ''}`}
                aria-hidden={!isActive}
              >
                <img
                  src={src}
                  alt=""
                  aria-hidden="true"
                  className={`hero-slide-media hero-slide-media--${variant}`}
                  decoding="async"
                  fetchPriority={index === 0 ? 'high' : 'auto'}
                  loading={index === 0 ? 'eager' : 'lazy'}
                  onLoad={handleImageLoad(src)}
                />
              </div>
            );
          })}
        </div>
      </div>

      <div className="hero-scrim" />

      <div className="site-shell hero-shell">
        <div className="hero-copy">
          <p className="section-kicker">{copy.hero.kicker}</p>
          <h1>{copy.hero.title}</h1>
          <p className="hero-summary">{copy.hero.summary}</p>
          <div className="hero-actions">
            <a className="pill-button pill-button-light" href="#tours">{copy.buttons.exploreTours}</a>
            <a className="pill-button pill-button-ghost" href="#gallery">{copy.buttons.viewGallery}</a>
          </div>
        </div>

        <aside className="hero-panel">
          <p className="panel-kicker">{copy.hero.panelKicker}</p>
          <div className="hero-stat-list">
            {copy.hero.highlights.map((item) => (
              <article className="hero-stat" key={item.label}>
                <h2>{item.label}</h2>
                <p>{item.value}</p>
              </article>
            ))}
          </div>
        </aside>
      </div>

      {images.length > 1 && (
        <>
          <div className="hero-nav">
            <button type="button" className="hero-arrow" onClick={goPrev} aria-label={copy.hero.previousSlide}>&lt;</button>
            <button type="button" className="hero-arrow" onClick={goNext} aria-label={copy.hero.nextSlide}>&gt;</button>
          </div>
          <div className="hero-dots">
            {images.map((src, index) => (
              <button
                key={src}
                type="button"
                className={index === active ? 'is-active' : ''}
                onClick={() => setActive(index)}
                aria-label={copy.hero.goToSlide(index + 1)}
              />
            ))}
          </div>
        </>
      )}
    </section>
  );
}

function Intro({ copy }) {
  return (
    <section className="section-block">
      <div className="site-shell intro-grid">
        <div>
          <p className="section-kicker section-kicker-dark">{copy.intro.kicker}</p>
          <h2 className="section-title">{copy.intro.title}</h2>
        </div>
        <div className="section-copy">
          <ul className="detail-list">
            {copy.intro.points.map((point) => (
              <li key={point}>{point}</li>
            ))}
          </ul>
        </div>
      </div>
    </section>
  );
}

function TourCard({ tour, image, language, copy }) {
  return (
    <article className="tour-card">
      <div className="tour-image">
        <img src={image} alt={tour.title} />
      </div>
      <div className="tour-body">
        <p className="card-eyebrow">{tour.duration || copy.tours.customTiming}</p>
        <h3>{tour.title}</h3>
        <p>{tour.description || copy.tours.customDescription}</p>
        <div className="tour-meta">
          <span>{formatPrice(tour.price, language)}</span>
          <a href="#contact">{copy.buttons.reserve}</a>
        </div>
      </div>
    </article>
  );
}

function Tours({ tours, images, language, copy }) {
  const localizedTours = tours.map((tour) => translateTour(tour, language));

  return (
    <section className="section-soft" id="tours">
      <div className="site-shell">
        <div className="section-heading">
          <div>
            <p className="section-kicker section-kicker-dark">{copy.tours.kicker}</p>
            <h2 className="section-title">{copy.tours.title}</h2>
          </div>
        </div>
        <div className="tour-grid">
          {localizedTours.map((tour, index) => (
            <TourCard key={tour.id || tour.title} tour={tour} image={resolveTourImage(tour, images, index)} language={language} copy={copy} />
          ))}
        </div>
      </div>
    </section>
  );
}

function Story({ copy }) {
  return (
    <section className="section-block" id="story">
      <div className="site-shell story-grid">
        <div className="story-copy">
          <p className="section-kicker section-kicker-dark">{copy.story.kicker}</p>
          <h2 className="section-title">{copy.story.title}</h2>
          <p>{copy.story.body}</p>
        </div>
        <div className="portrait-frame">
          <img src="/img/Carousel/CaptainSean2.webp" alt={copy.story.imageAlt} loading="lazy" decoding="async" />
        </div>
      </div>
    </section>
  );
}

function Gallery({ images, onOpen, copy }) {
  const [variants, setVariants] = useState({});

  const handleImageLoad = (src) => (event) => {
    const nextVariant = classifyImageVariant(
      event.currentTarget.naturalWidth,
      event.currentTarget.naturalHeight
    );

    setVariants((current) => (
      current[src] === nextVariant ? current : { ...current, [src]: nextVariant }
    ));
  };

  return (
    <section className="section-soft" id="gallery">
      <div className="site-shell">
        <div className="section-heading">
          <div>
            <p className="section-kicker section-kicker-dark">{copy.gallery.kicker}</p>
            <h2 className="section-title">{copy.gallery.title}</h2>
          </div>
          <span className="gallery-count">{copy.gallery.countLabel(images.length)}</span>
        </div>
        <div className="gallery-grid">
          {images.map((src, index) => (
            <button
              key={src}
              type="button"
              className={`gallery-tile gallery-tile--${variants[src] || 'landscape'}`}
              onClick={() => onOpen(index)}
              aria-label={copy.gallery.openImageLabel(index + 1)}
            >
              <img
                src={src}
                alt={galleryAltText(index, copy)}
                className={`gallery-tile-media gallery-tile-media--${variants[src] || 'landscape'}`}
                loading="lazy"
                decoding="async"
                onLoad={handleImageLoad(src)}
              />
            </button>
          ))}
        </div>
      </div>
    </section>
  );
}

function Lightbox({ images, index, onClose, onPrev, onNext, copy }) {
  useEffect(() => {
    const handler = (event) => {
      if (event.key === 'Escape') onClose();
      if (event.key === 'ArrowLeft') onPrev();
      if (event.key === 'ArrowRight') onNext();
    };

    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [onClose, onPrev, onNext]);

  if (index < 0 || images.length === 0) {
    return null;
  }

  const image = images[index];

  return (
    <div className="lightbox" role="dialog" aria-modal="true">
      <button type="button" className="lightbox__close" onClick={onClose} aria-label={copy.lightbox.close}>x</button>
      <button type="button" className="lightbox__arrow lightbox__arrow--left" onClick={onPrev} aria-label={copy.lightbox.previous}>&lt;</button>
      <figure className="lightbox__content">
        <img src={image} alt={galleryAltText(index, copy)} decoding="async" />
      </figure>
      <button type="button" className="lightbox__arrow lightbox__arrow--right" onClick={onNext} aria-label={copy.lightbox.next}>&gt;</button>
    </div>
  );
}

function Contact({ copy }) {
  const [form, setForm] = useState({ name: '', email: '', message: '' });
  const [status, setStatus] = useState('idle');

  const handleChange = (event) => {
    const { name, value } = event.target;
    setForm((current) => ({ ...current, [name]: value }));
  };

  const handleSubmit = async (event) => {
    event.preventDefault();

    if (!event.currentTarget.reportValidity()) {
      return;
    }

    setStatus('loading');

    try {
      const response = await fetch('/api/contact', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          name: form.name.trim(),
          email: form.email.trim(),
          message: form.message.trim()
        })
      });

      if (!response.ok) {
        throw new Error('Contact request failed.');
      }

      setForm({ name: '', email: '', message: '' });
      setStatus('success');
    } catch {
      setStatus('error');
    }
  };

  return (
    <section className="contact-section" id="contact">
      <div className="site-shell contact-grid">
        <div className="contact-copy">
          <p className="section-kicker section-kicker-light">{copy.contact.kicker}</p>
          <h2 className="section-title light">{copy.contact.title}</h2>
          <p>{copy.contact.body}</p>
        </div>
        <div className="contact-stack">
          <div className="contact-cards">
            {copy.contact.details.map((item) => (
              <a className="contact-card" href={item.href} key={item.title}>
                <span>{item.title}</span>
                <strong>{item.value}</strong>
              </a>
            ))}
          </div>
          <div className="contact-form-card">
            <h3 className="contact-form-title">{copy.contact.form.title}</h3>
            <form className="contact-form" onSubmit={handleSubmit}>
              <label>
                <span>{copy.contact.form.nameLabel}</span>
                <input
                  autoComplete="name"
                  name="name"
                  onChange={handleChange}
                  placeholder={copy.contact.form.namePlaceholder}
                  required
                  type="text"
                  value={form.name}
                />
              </label>
              <label>
                <span>{copy.contact.form.emailLabel}</span>
                <input
                  autoComplete="email"
                  name="email"
                  onChange={handleChange}
                  placeholder={copy.contact.form.emailPlaceholder}
                  required
                  type="email"
                  value={form.email}
                />
              </label>
              <label>
                <span>{copy.contact.form.messageLabel}</span>
                <textarea
                  name="message"
                  onChange={handleChange}
                  placeholder={copy.contact.form.messagePlaceholder}
                  required
                  rows="5"
                  value={form.message}
                />
              </label>
              <button className="pill-button pill-button-light contact-submit" disabled={status === 'loading'} type="submit">
                {status === 'loading' ? copy.contact.form.sending : copy.contact.form.submit}
              </button>
              {status === 'success' && (
                <p className="contact-status contact-status-success">{copy.contact.form.success}</p>
              )}
              {status === 'error' && (
                <p className="contact-status contact-status-error">{copy.contact.form.error}</p>
              )}
            </form>
          </div>
        </div>
      </div>
    </section>
  );
}

function Footer({ copy }) {
  return (
    <footer className="site-footer">
      <div className="site-shell footer-inner">
        <p>{copy.footer.tagline}</p>
        <a href="#top">{copy.footer.backToTop}</a>
      </div>
    </footer>
  );
}

function App() {
  const [language, setLanguage] = useState(getInitialLanguage);
  const [carouselImages, setCarouselImages] = useState(FALLBACK_IMAGES);
  const [galleryImages, setGalleryImages] = useState(FALLBACK_GALLERY_IMAGES);
  const [tours, setTours] = useState([]);
  const [lightboxIndex, setLightboxIndex] = useState(-1);

  const copy = I18N[language] || I18N.en;

  useEffect(() => {
    persistLanguage(language);
    document.documentElement.lang = language;
    document.title = copy.pageTitle;
  }, [language, copy.pageTitle]);

  useEffect(() => {
    loadJson('/api/carousel', FALLBACK_IMAGES).then((images) => {
      setCarouselImages(Array.isArray(images) && images.length > 0 ? images : FALLBACK_IMAGES);
    });

    loadJson('/api/tours', []).then((items) => {
      setTours(Array.isArray(items) ? items : []);
    });

    loadJson('/api/gallery', FALLBACK_GALLERY_IMAGES).then((images) => {
      setGalleryImages(Array.isArray(images) && images.length > 0 ? images : FALLBACK_GALLERY_IMAGES);
    });
  }, []);

  const openLightbox = (index) => setLightboxIndex(index);
  const closeLightbox = () => setLightboxIndex(-1);
  const prevLightbox = () =>
    setLightboxIndex((current) => (current <= 0 ? galleryImages.length - 1 : current - 1));
  const nextLightbox = () =>
    setLightboxIndex((current) => (current + 1) % galleryImages.length);

  return (
    <div className="site-page">
      <Header language={language} copy={copy} onLanguageChange={setLanguage} />
      <Hero images={carouselImages} copy={copy} />
      <Intro copy={copy} />
      <Tours tours={tours} images={carouselImages} language={language} copy={copy} />
      <Story copy={copy} />
      <Gallery images={galleryImages} onOpen={openLightbox} copy={copy} />
      <Contact copy={copy} />
      <Footer copy={copy} />
      {lightboxIndex >= 0 && galleryImages.length > 0 && (
        <Lightbox
          images={galleryImages}
          index={lightboxIndex}
          onClose={closeLightbox}
          onPrev={prevLightbox}
          onNext={nextLightbox}
          copy={copy}
        />
      )}
    </div>
  );
}

const rootElement = document.getElementById('root');

if (rootElement) {
  ReactDOM.createRoot(rootElement).render(<App />);
}
