/* =============================================================================
   Curry & Kebab Restaurant — app.jsx
   JHOL format (restaurant-site skill) on the lite-site stack (no-build, Babel).
   One deliberate deviation from the starter contract (see PRD.md): the
   reservation form POSTs to the SaangCRM Supabase (anon INSERT-only). Every
   other part of the site stays network-silent.
   ============================================================================ */
const { useState, useEffect, useRef, useCallback } = React;

const REDUCED = !!(window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches);

/* ----- per-project config -------------------------------------------------- */
const BRAND = "Curry & Kebab";
const BRAND_FULL = "Curry & Kebab Restaurant";
const DEFAULT_LANG = "en";
const PHONE = "061 053 8058";
const PHONE_TEL = "tel:+66610538058";
const MAPS_URL = "https://www.google.com/maps/search/?api=1&query=" + encodeURIComponent("Curry & Kebab Restaurant, Charoen Krung 36 Alley, Bang Rak, Bangkok");
const IG_URL = "https://www.instagram.com/curry_kebab_restaurant/";
const FB_URL = "https://www.facebook.com/61578118597870";
const TIKTOK_URL = "https://www.tiktok.com/@currykebab2";

/* Reservations → SaangCRM (Supabase, publishable key: RLS = anon INSERT-only). */
const SUPA_URL = "https://zwyzqxdtmghfqunhdgil.supabase.co";
const SUPA_KEY = "sb_publishable_5-NTwOr5kySKQW6UcVXfaQ_mTdwspk-";
const RES_ENDPOINT = SUPA_URL + "/rest/v1/reservations";
const RES_SITE = "curry-kebab";
window.__RES_ENDPOINT = RES_ENDPOINT; // exposed for tests

const SITE_URL = "https://curry-kebab-restaurant.vercel.app"; // must match index.html
const GA_ID = null;
const OG_LOCALE = { en: "en_US", th: "th_TH" };

/* Locale-specific title/description — Googlebot renders JS, so the ?lang=th URL
   (own hreflang alternate) indexes with the Thai snippet. Keywords chosen for
   what Thai diners actually type: cuisine terms + neighbourhood landmarks. */
const SEO_META = {
  en: {
    title: "Curry & Kebab Restaurant — Halal Pakistani & Indian, Bang Rak Bangkok",
    desc: "Family-run halal Pakistani & Indian restaurant opposite Central Bangrak, steps from BTS Saphan Taksin. Karahi, biryani, tandoori naan. Open daily 11:00–23:00. Book a table online.",
  },
  th: {
    title: "Curry & Kebab — ร้านอาหารอินเดีย ปากีสถาน ฮาลาล บางรัก ใกล้ BTS สะพานตากสิน",
    desc: "ร้านอาหารอินเดียและปากีสถานฮาลาล ย่านบางรัก ถนนเจริญกรุง ใกล้ BTS สะพานตากสิน ตรงข้ามเซ็นทรัลบางรัก — ไก่การาฮี ข้าวหมกบิรยานี นานอบเตาทันดูร์ แกงกอร์มา เปิดทุกวัน 11:00–23:00 จองโต๊ะออนไลน์ได้",
  },
};

function initialLang() {
  try {
    const p = new URLSearchParams(window.location.search).get("lang");
    if (p === "en" || p === "th") return p;
    return localStorage.getItem("site-lang") || DEFAULT_LANG;
  } catch { return DEFAULT_LANG; }
}

function enhanceSeo(lang) {
  try {
    const base = SITE_URL.replace(/\/$/, "");
    const url = lang === DEFAULT_LANG ? base + "/" : base + "/?lang=" + lang;
    const set = (sel, attr, val) => { const el = document.querySelector(sel); if (el) el.setAttribute(attr, val); };
    document.documentElement.lang = lang;
    set('link[rel="canonical"]', "href", url);
    set('meta[property="og:url"]', "content", url);
    set('meta[property="og:locale"]', "content", OG_LOCALE[lang] || "en_US");
    const meta = SEO_META[lang] || SEO_META.en;
    document.title = meta.title;
    set('meta[name="description"]', "content", meta.desc);
    set('meta[property="og:title"]', "content", meta.title);
    set('meta[property="og:description"]', "content", meta.desc);
    set('meta[name="twitter:title"]', "content", meta.title);
    set('meta[name="twitter:description"]', "content", meta.desc);
  } catch {}
}

function loadAnalytics() {
  if (!GA_ID || document.getElementById("ga4-src")) return;
  const s = document.createElement("script");
  s.id = "ga4-src"; s.async = true;
  s.src = "https://www.googletagmanager.com/gtag/js?id=" + GA_ID;
  document.head.appendChild(s);
  window.dataLayer = window.dataLayer || [];
  window.gtag = function () { window.dataLayer.push(arguments); };
  window.gtag("js", new Date());
  window.gtag("config", GA_ID);
}

/* ----- menu data (prices EXACTLY as printed on the client's menu card) ------ */
const KARAHI = [
  { img: "assets/karahi-chicken.jpg", en: "Chicken Karahi",        th: "ไก่การาฮี",          half: "฿320", full: "฿600" },
  { img: "assets/karahi-desi.jpg",    en: "Desi Chicken Karahi",   th: "ไก่บ้านการาฮี",       half: "฿400", full: "฿800" },
  { img: "assets/karahi-white.jpg",   en: "White Chicken Karahi",  th: "การาฮีไก่ขาว",        half: "฿400", full: "฿800" },
  { img: "assets/karahi-achari.jpg",  en: "Chicken Achari Karahi", th: "ไก่อาจารีการาฮี",     half: "฿400", full: "฿800" },
];
const FAVES = [
  { en: "Lamb Biryani",  th: "ข้าวหมกบิรยานีแกะ" },
  { en: "Mutton Pulao",  th: "ข้าวปูเลาเนื้อแกะ" },
  { en: "Chicken Korma", th: "แกงกอร์มาไก่" },
  { en: "Rogni Naan",    th: "โรกนีนาน" },
];

/* ----- i18n ---------------------------------------------------------------- */
const DICT = {
  en: {
    nav: { story: "Our Story", menu: "Menu", gallery: "Gallery", visit: "Visit Us", resv: "Reservations", cta: "Book a table" },
    hero: { alt: "Curry & Kebab Restaurant storefront on Charoen Krung, Bang Rak" },
    resv: {
      eyebrow: "Reservations", title: "Book a table",
      body: "Reserve ahead and your table is waiting. We confirm every booking by phone.",
      name: "Your name", phone: "Phone number", party: "Guests", date: "Date", time: "Time", notes: "Anything else (optional)",
      submit: "Send reservation", sending: "Sending…",
      okT: "Reservation received!", okB: "Thank you — we will call you shortly to confirm your table.",
      errT: "Something went wrong", errB: "We could not send your reservation. Please call us instead:",
      phoneLine: "Prefer to talk? Call us at", errReq: "This field is required",
      errPhone: "Enter a valid phone number", errParty: "Must be 1–40", errDate: "Pick today or a future date",
      partyUnit: (n) => n + (n === 1 ? " guest" : " guests"),
    },
    story: {
      eyebrow: "Our Story", title: "Good food, great vibes",
      body1: "Curry & Kebab is a family-run halal kitchen in the heart of Bang Rak — a few steps from Saphan Taksin BTS, opposite Central Bangrak. We cook authentic Pakistani and Indian food: karahi seared over a hard flame, biryani layered the slow way, naan pulled from the tandoor to your table.",
      body2: "Flavorful dishes, warm hospitality and a place you'll love to return to. Dine in, take away, or have it delivered.",
    },
    menu: {
      eyebrow: "Our Menu", title: "This week's karahi picks",
      body: "Karahi is our signature — chicken seared in a blackened steel wok with tomato, ginger and green chilli. Half feeds two; full is for the table.",
      half: "Half", full: "Full", favesTitle: "House favourites — ask for today's price:",
    },
    quote: {
      line: "“Good food brings people together.”",
      badges: ["Halal", "Dine-in", "Takeaway", "Delivery"],
    },
    gallery: { eyebrow: "Gallery", title: "A look around" },
    faq: {
      eyebrow: "FAQ", title: "Good to know",
      items: [
        { q: "Is Curry & Kebab halal?", a: "Yes — we are a halal restaurant. Everything from our karahi to our biryani is prepared halal." },
        { q: "What kind of food do you serve?", a: "Authentic Pakistani and North Indian cuisine: chicken karahi seared in a steel wok, dum biryani, chicken korma, kebabs, and naan baked fresh in the tandoor." },
        { q: "Where are you located?", a: "Shop 19, Soi 50 (Charoen Krung 36 Alley), Bang Rak — a short walk from BTS Saphan Taksin, opposite Central Bangrak, near the Shangri-La and lebua hotels by the Chao Phraya river." },
        { q: "What are your opening hours?", a: "Open every day, 11:00 AM to 11:00 PM." },
        { q: "Do you take reservations?", a: "Yes — book a table right on this page and we confirm by phone, or call 061 053 8058." },
        { q: "Do you offer takeaway or delivery?", a: "Yes — dine-in, takeaway and delivery are all available." },
      ],
    },
    visit: {
      title: "Let's Connect",
      addrL: "Address",
      addr1: "Shop 19, Soi 50, Charoen Krung 36 Alley,",
      addr2: "Khwaeng Bang Rak, Bang Rak, Bangkok 10500",
      addr3: "Opposite Central Bangrak · few steps from Saphan Taksin BTS",
      hoursL: "Timings", hours: "Open daily · 11:00 AM – 11:00 PM",
      contactL: "Contact Us",
      dir: "Get directions", copy: "Copy address", copied: "Copied ✓",
      followL: "Follow us",
      pins: ["Curry & Kebab", "BTS Saphan Taksin", "Central Bangrak"],
      rights: "© 2026 Curry & Kebab Restaurant", credit: "Site by Saang Studio",
    },
    sticky: { call: "Call", book: "Book a table" },
  },
  th: {
    nav: { story: "เรื่องราวของเรา", menu: "เมนู", gallery: "แกลเลอรี", visit: "การเดินทาง", resv: "จองโต๊ะ", cta: "จองโต๊ะ" },
    hero: { alt: "หน้าร้าน Curry & Kebab Restaurant ถนนเจริญกรุง บางรัก" },
    resv: {
      eyebrow: "จองโต๊ะ", title: "จองโต๊ะล่วงหน้า",
      body: "จองล่วงหน้าแล้วมาถึงก็นั่งได้เลย ทุกการจองเราจะโทรยืนยันให้แน่นอน",
      name: "ชื่อของคุณ", phone: "เบอร์โทรศัพท์", party: "จำนวนคน", date: "วันที่", time: "เวลา", notes: "ข้อความเพิ่มเติม (ไม่บังคับ)",
      submit: "ส่งการจอง", sending: "กำลังส่ง…",
      okT: "ได้รับการจองแล้ว!", okB: "ขอบคุณค่ะ เราจะโทรกลับเพื่อยืนยันโต๊ะของคุณโดยเร็วที่สุด",
      errT: "ส่งไม่สำเร็จ", errB: "ระบบขัดข้อง กรุณาโทรจองโดยตรงที่",
      phoneLine: "สะดวกโทรมากกว่า? โทรหาเราที่", errReq: "กรุณากรอกข้อมูลนี้",
      errPhone: "กรุณากรอกเบอร์โทรที่ถูกต้อง", errParty: "ต้องเป็น 1–40", errDate: "เลือกวันนี้หรือวันในอนาคต",
      partyUnit: (n) => n + " คน",
    },
    story: {
      eyebrow: "เรื่องราวของเรา", title: "อาหารดี บรรยากาศดี",
      body1: "Curry & Kebab คือครัวฮาลาลของครอบครัว ใจกลางบางรัก เดินไม่กี่ก้าวจาก BTS สะพานตากสิน ตรงข้ามเซ็นทรัล บางรัก เราทำอาหารปากีสถานและอินเดียต้นตำรับ ทั้งการาฮีผัดไฟแรงในกระทะเหล็ก บิรยานีอบชั้นแบบช้าๆ และนานร้อนๆ จากเตาทันดูร์ส่งตรงถึงโต๊ะ",
      body2: "รสชาติจัดจ้าน การต้อนรับอบอุ่น ที่ที่คุณจะอยากกลับมาอีก นั่งทานที่ร้าน สั่งกลับบ้าน หรือเดลิเวอรีก็ได้",
    },
    menu: {
      eyebrow: "เมนูของเรา", title: "การาฮีเด็ดประจำสัปดาห์",
      body: "การาฮีคือเมนูเด็ดของเรา ไก่ผัดไฟแรงในกระทะเหล็กกับมะเขือเทศ ขิง และพริกเขียว ขนาดครึ่งทานได้สองคน ขนาดเต็มสำหรับทั้งโต๊ะ",
      half: "ครึ่ง", full: "เต็ม", favesTitle: "เมนูขึ้นชื่อของร้าน สอบถามราคาได้ที่ร้าน:",
    },
    quote: {
      line: "“อาหารดีๆ ทำให้ผู้คนมาอยู่ด้วยกัน”",
      badges: ["ฮาลาล", "ทานที่ร้าน", "สั่งกลับบ้าน", "เดลิเวอรี"],
    },
    gallery: { eyebrow: "แกลเลอรี", title: "ชมบรรยากาศร้าน" },
    faq: {
      eyebrow: "คำถามที่พบบ่อย", title: "เรื่องน่ารู้ก่อนมา",
      items: [
        { q: "ร้าน Curry & Kebab เป็นร้านฮาลาลไหม?", a: "ใช่ค่ะ เราเป็นร้านอาหารฮาลาล ทุกเมนูตั้งแต่ไก่การาฮีจนถึงข้าวหมกบิรยานีปรุงแบบฮาลาลทั้งหมด" },
        { q: "ที่ร้านมีอาหารแบบไหนบ้าง?", a: "อาหารปากีสถานและอินเดียเหนือต้นตำรับ — ไก่การาฮีผัดกระทะเหล็กไฟแรง ข้าวหมกบิรยานี แกงกอร์มาไก่ เคบับ และนานอบสดจากเตาทันดูร์" },
        { q: "ร้านอยู่ตรงไหน?", a: "ร้านที่ 19 ซอย 50 (ซอยเจริญกรุง 36) เขตบางรัก เดินไม่กี่นาทีจาก BTS สะพานตากสิน ตรงข้ามเซ็นทรัลบางรัก ใกล้โรงแรมแชงกรี-ลาและเลอบัว ริมแม่น้ำเจ้าพระยา" },
        { q: "ร้านเปิดกี่โมง?", a: "เปิดทุกวัน 11:00 – 23:00 น." },
        { q: "รับจองโต๊ะไหม?", a: "รับค่ะ จองผ่านหน้าเว็บนี้ได้เลย เราจะโทรยืนยันการจอง หรือโทร 061 053 8058" },
        { q: "มีสั่งกลับบ้านหรือเดลิเวอรีไหม?", a: "มีครบค่ะ ทั้งนั่งทานที่ร้าน สั่งกลับบ้าน และเดลิเวอรี" },
      ],
    },
    visit: {
      title: "ติดต่อเรา",
      addrL: "ที่อยู่",
      addr1: "ร้านที่ 19 ซอย 50 ซอยเจริญกรุง 36",
      addr2: "แขวงบางรัก เขตบางรัก กรุงเทพฯ 10500",
      addr3: "ตรงข้ามเซ็นทรัล บางรัก · เดินไม่กี่ก้าวจาก BTS สะพานตากสิน",
      hoursL: "เวลาเปิด", hours: "เปิดทุกวัน · 11:00 – 23:00 น.",
      contactL: "ติดต่อ",
      dir: "นำทาง", copy: "คัดลอกที่อยู่", copied: "คัดลอกแล้ว ✓",
      followL: "ติดตามเรา",
      pins: ["Curry & Kebab", "BTS สะพานตากสิน", "เซ็นทรัล บางรัก"],
      rights: "© 2026 Curry & Kebab Restaurant", credit: "เว็บไซต์โดย Saang Studio",
    },
    sticky: { call: "โทร", book: "จองโต๊ะ" },
  },
};
window.__DICT = DICT;
window.__REDUCED = REDUCED;

/* ----- utils --------------------------------------------------------------- */
function goto(id) {
  const el = document.getElementById(id);
  if (!el) return;
  if (window.__lenis) window.__lenis.scrollTo(el, { offset: -64 });
  else el.scrollIntoView({ behavior: REDUCED ? "auto" : "smooth" });
}
const pad2 = (n) => String(n).padStart(2, "0");
const todayIso = () => { const n = new Date(); return `${n.getFullYear()}-${pad2(n.getMonth() + 1)}-${pad2(n.getDate())}`; };
/* half-hourly slots inside opening hours (last seating 22:00) */
const TIME_SLOTS = [];
for (let h = 11; h <= 22; h++) { TIME_SLOTS.push(pad2(h) + ":00"); if (h < 22) TIME_SLOTS.push(pad2(h) + ":30"); }

/* ----- Reveal -------------------------------------------------------------- */
function Reveal({ children, className = "", tag = "div", id, style }) {
  const ref = useRef(null);
  const [shown, setShown] = useState(REDUCED);
  useEffect(() => {
    if (REDUCED || shown) return;
    const el = ref.current;
    if (!el || !("IntersectionObserver" in window)) { setShown(true); return; }
    const io = new IntersectionObserver(
      (es) => es.forEach((e) => { if (e.isIntersecting) { setShown(true); io.disconnect(); } }),
      { root: null, rootMargin: "0px 0px -20% 0px", threshold: 0.01 }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [shown]);
  const Tag = tag;
  return <Tag id={id} ref={ref} style={style} className={"reveal " + (shown ? "is-in " : "") + className}>{children}</Tag>;
}

/* ----- Nav ----------------------------------------------------------------- */
function Nav({ t, lang, toggleLang }) {
  const [scrolled, setScrolled] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 24);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  const links = ["story", "menu", "gallery", "visit", "resv"];
  const ids = { story: "story", menu: "menu", gallery: "gallery", visit: "visit", resv: "reserve" };
  return (
    <nav className="nav" data-scrolled={scrolled}>
      <a className="nav__brand" href="#top" onClick={(e) => { e.preventDefault(); goto("top"); }}>
        <img className="nav__logo" src="assets/logo.png" alt="" width="44" height="44" />
        <span className="nav__name">Curry <em>&amp;</em> Kebab</span>
      </a>
      <div className="nav__links">
        {links.map((k) => <a key={k} href={"#" + ids[k]} onClick={(e) => { e.preventDefault(); goto(ids[k]); }}>{t.nav[k]}</a>)}
      </div>
      <div className="nav__right">
        <button className="langtoggle" onClick={toggleLang} aria-label="Switch language">{lang === "th" ? "EN" : "ไทย"}</button>
        <button className="btn btn--accent" onClick={() => goto("reserve")}>{t.nav.cta}</button>
      </div>
    </nav>
  );
}

/* ----- Hero: food film ending on the logo card; photo fallback (JHOL) ------- */
const PORTRAIT_Q = "(max-width: 767px)";
function Hero({ t }) {
  const videoRef = useRef(null);
  const [failed, setFailed] = useState(false);
  // The 16:9 film cover-cropped into a phone hero would slice the end-card
  // wordmark, so phones get a re-cut 1:2 master: food shots centre-cropped to
  // portrait, end card letterboxed on its own black so the wordmark survives.
  const [small, setSmall] = useState(() => !!(window.matchMedia && window.matchMedia(PORTRAIT_Q).matches));
  useEffect(() => {
    if (!window.matchMedia) return;
    const mq = window.matchMedia(PORTRAIT_Q);
    const onChange = e => setSmall(e.matches);
    mq.addEventListener ? mq.addEventListener("change", onChange) : mq.addListener(onChange);
    return () => { mq.removeEventListener ? mq.removeEventListener("change", onChange) : mq.removeListener(onChange); };
  }, []);
  useEffect(() => {
    if (REDUCED) return;
    const v = videoRef.current;
    if (!v) return;
    setFailed(false);
    v.muted = true;
    const p = v.play();
    if (p && p.catch) p.catch(() => setFailed(true));
  }, [small]);
  const showPhoto = REDUCED || failed;
  const film = small ? "assets/hero-portrait" : "assets/hero";
  return (
    <header className="hero" id="top" data-media={showPhoto ? "photo" : "video"} data-cut={small ? "portrait" : "wide"}>
      {showPhoto ? (
        <img src="assets/storefront-night.jpg" alt={t.hero.alt} fetchPriority="high" />
      ) : (
        <video key={film} ref={videoRef} className="hero__video" autoPlay muted loop playsInline
          poster={small ? "assets/hero-portrait-poster.jpg" : "assets/hero-poster.jpg"}
          preload="auto" aria-label={t.hero.alt}
          onError={() => setFailed(true)}>
          <source src={film + ".webm"} type="video/webm" />
          <source src={film + ".mp4"} type="video/mp4" />
        </video>
      )}
    </header>
  );
}

/* ----- Reservations (below hero — the one section allowed to POST) ---------- */
function Reservations({ t }) {
  const [status, setStatus] = useState("idle"); // idle | sending | ok | error
  const [errors, setErrors] = useState({});
  const r = t.resv;

  const onSubmit = async (e) => {
    e.preventDefault();
    if (status === "sending") return;
    const f = e.target;
    const name = f.name.value.trim();
    const phone = f.phone.value.trim();
    const party = Number(f.party.value);
    const date = f.date.value;
    const time = f.time.value;
    const notes = f.notes.value.trim();
    const next = {};
    if (!name) next.name = r.errReq;
    if (!phone) next.phone = r.errReq;
    else if (!/^[+0-9 ()-]{6,20}$/.test(phone)) next.phone = r.errPhone;
    if (!Number.isInteger(party) || party < 1 || party > 40) next.party = r.errParty;
    if (!date) next.date = r.errReq;
    else if (date < todayIso()) next.date = r.errDate;
    if (!time) next.time = r.errReq;
    setErrors(next);
    if (Object.keys(next).length) {
      const firstKey = ["name", "phone", "party", "date", "time"].find((k) => next[k]);
      const elt = firstKey && document.getElementById("r-" + firstKey);
      if (elt) elt.focus();
      return;
    }
    if (f.website.value) { setStatus("ok"); return; } // honeypot: swallow bots silently
    setStatus("sending");
    try {
      const res = await fetch(RES_ENDPOINT, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          apikey: SUPA_KEY,
          Authorization: "Bearer " + SUPA_KEY,
          Prefer: "return=minimal",
        },
        body: JSON.stringify({
          name, phone, party_size: party, res_date: date, res_time: time,
          notes: notes || null, site: RES_SITE,
        }),
      });
      if (!res.ok) throw new Error("HTTP " + res.status);
      setStatus("ok");
    } catch {
      setStatus("error");
    }
  };

  return (
    <section className="band" id="reserve">
      <div className="wrap">
        <Reveal className="resv">
          <p className="eyebrow">{r.eyebrow}</p>
          <h2 className="h-display splash">{r.title}</h2>
          <p className="muted" style={{ maxWidth: 520, margin: "16px auto 0" }}>{r.body}</p>

          {status === "ok" ? (
            <div className="form__success" role="status">
              <h3>{r.okT}</h3>
              <p style={{ margin: 0 }}>{r.okB}</p>
            </div>
          ) : (
            <form className="form" onSubmit={onSubmit} noValidate>
              {status === "error" && (
                <div className="form__error" role="alert" style={{ marginTop: 0 }}>
                  <h3>{r.errT}</h3>
                  <p style={{ margin: 0 }}>{r.errB} <a href={PHONE_TEL} style={{ color: "var(--accent)" }}>{PHONE}</a></p>
                </div>
              )}
              <div className="two">
                <div className="field" data-invalid={errors.name ? "true" : undefined}>
                  <label htmlFor="r-name">{r.name}</label>
                  <input id="r-name" name="name" type="text" autoComplete="name" />
                  <div className="field__err">{errors.name || ""}</div>
                </div>
                <div className="field" data-invalid={errors.phone ? "true" : undefined}>
                  <label htmlFor="r-phone">{r.phone}</label>
                  <input id="r-phone" name="phone" type="tel" autoComplete="tel" />
                  <div className="field__err">{errors.phone || ""}</div>
                </div>
              </div>
              <div className="three">
                <div className="field" data-invalid={errors.party ? "true" : undefined}>
                  <label htmlFor="r-party">{r.party}</label>
                  <select id="r-party" name="party" defaultValue="2">
                    {Array.from({ length: 40 }, (_, i) => i + 1).map((n) => (
                      <option key={n} value={n}>{r.partyUnit(n)}</option>
                    ))}
                  </select>
                  <div className="field__err">{errors.party || ""}</div>
                </div>
                <div className="field" data-invalid={errors.date ? "true" : undefined}>
                  <label htmlFor="r-date">{r.date}</label>
                  <input id="r-date" name="date" type="date" min={todayIso()} defaultValue={todayIso()} />
                  <div className="field__err">{errors.date || ""}</div>
                </div>
                <div className="field" data-invalid={errors.time ? "true" : undefined}>
                  <label htmlFor="r-time">{r.time}</label>
                  <select id="r-time" name="time" defaultValue="19:00">
                    {TIME_SLOTS.map((s) => <option key={s} value={s}>{s}</option>)}
                  </select>
                  <div className="field__err">{errors.time || ""}</div>
                </div>
              </div>
              <div className="field">
                <label htmlFor="r-notes">{r.notes}</label>
                <textarea id="r-notes" name="notes" rows="2" />
              </div>
              <input className="hp" type="text" name="website" tabIndex="-1" autoComplete="off" aria-hidden="true" />
              <button className="btn btn--accent btn--block" type="submit" disabled={status === "sending"}>
                {status === "sending" ? r.sending : r.submit}
              </button>
            </form>
          )}
          <p className="resv__phone">{r.phoneLine} <a href={PHONE_TEL}>{PHONE}</a></p>
        </Reveal>
      </div>
    </section>
  );
}

/* ----- Story ----------------------------------------------------------------- */
function Story({ t }) {
  return (
    <section className="band" id="story">
      <div className="wrap split">
        <Reveal tag="figure" className="split__fig">
          <img src="assets/storefront-day.jpg" alt="Curry & Kebab storefront by day, Charoen Krung" loading="lazy" />
        </Reveal>
        <Reveal className="split__body">
          <p className="eyebrow">{t.story.eyebrow}</p>
          <h2 className="h-display splash">{t.story.title}</h2>
          <p style={{ marginTop: 18 }}>{t.story.body1}</p>
          <p className="muted">{t.story.body2}</p>
        </Reveal>
      </div>
    </section>
  );
}

/* ----- Menu ------------------------------------------------------------------ */
function Menu({ t, lang }) {
  return (
    <section className="band" id="menu" style={{ paddingTop: 0 }}>
      <div className="wrap">
        <div className="split split--flip">
          <Reveal className="split__body" style={{ order: 0 }}>
            <p className="eyebrow">{t.menu.eyebrow}</p>
            <h2 className="h-display splash">{t.menu.title}</h2>
            <p style={{ marginTop: 18 }}>{t.menu.body}</p>
          </Reveal>
          <Reveal className="dishes" tag="div">
            {KARAHI.map((k, i) => (
              <div className="dish" key={i}>
                <img src={k.img} alt={k.en} loading="lazy" />
                <div className="dish__body">
                  <div className="dish__name">{lang === "th" ? k.th : k.en}</div>
                  <div className="dish__price">{t.menu.half} {k.half} · {t.menu.full} {k.full}</div>
                </div>
              </div>
            ))}
          </Reveal>
        </div>
        <Reveal className="faves">
          <span className="muted" style={{ fontSize: 14 }}>{t.menu.favesTitle}</span>
          {FAVES.map((f, i) => <span className="fave" key={i}>{lang === "th" ? f.th : f.en}</span>)}
        </Reveal>
      </div>
    </section>
  );
}

/* ----- Quote band ------------------------------------------------------------ */
function QuoteBand({ t }) {
  return (
    <section className="band band--green">
      <div className="wrap">
        <Reveal className="quote">
          <blockquote>{t.quote.line}</blockquote>
          <div className="badges">
            {t.quote.badges.map((b, i) => <span className="badge" key={i}>{b}</span>)}
          </div>
        </Reveal>
      </div>
    </section>
  );
}

/* ----- Gallery ---------------------------------------------------------------- */
function Gallery({ t }) {
  return (
    <section className="band" id="gallery">
      <div className="wrap">
        <Reveal>
          <p className="eyebrow">{t.gallery.eyebrow}</p>
          <h2 className="h-display splash" style={{ marginBottom: 26 }}>{t.gallery.title}</h2>
        </Reveal>
        <Reveal className="gallery" tag="div">
          <figure><img src="assets/dining.jpg" alt="Guests enjoying karahi, biryani and naan at Curry & Kebab" loading="lazy" /></figure>
          <figure><img src="assets/storefront-night.jpg" alt="Curry & Kebab at night" loading="lazy" /></figure>
        </Reveal>
      </div>
    </section>
  );
}

/* ----- FAQ (visible twin of the FAQPage JSON-LD in index.html) ---------------- */
function Faq({ t }) {
  return (
    <section className="band" id="faq" style={{ paddingTop: 0 }}>
      <div className="wrap" style={{ maxWidth: 880 }}>
        <Reveal>
          <p className="eyebrow">{t.faq.eyebrow}</p>
          <h2 className="h-display splash" style={{ marginBottom: 30 }}>{t.faq.title}</h2>
        </Reveal>
        <Reveal className="faqlist" tag="dl">
          {t.faq.items.map((it, i) => (
            <div className="faqitem" key={i}>
              <dt>{it.q}</dt>
              <dd>{it.a}</dd>
            </div>
          ))}
        </Reveal>
      </div>
    </section>
  );
}

/* ----- Visit / footer (JHOL "Let's Connect") ---------------------------------- */
function StylizedMap({ t }) {
  const pins = [
    { x: 52, y: 38, label: t.visit.pins[0], main: true },
    { x: 40, y: 74, label: t.visit.pins[1] },
    { x: 66, y: 56, label: t.visit.pins[2] },
  ];
  return (
    <div className="mapcanvas" aria-label="Stylized map of Charoen Krung around the restaurant">
      <svg viewBox="0 0 100 70" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
        <rect width="100" height="70" fill="#E9EFE4" />
        {/* Chao Phraya river */}
        <path d="M0 62 C 20 58, 30 66, 48 64 S 80 70, 100 64 L100 70 L0 70 Z" fill="#BBD7E6" />
        {/* Charoen Krung road */}
        <path d="M8 0 C 24 24, 40 44, 44 70" stroke="#fff" strokeWidth="3.4" fill="none" />
        {/* Soi 50 / Charoen Krung 36 */}
        <path d="M30 34 L 78 44" stroke="#fff" strokeWidth="2.2" fill="none" />
        {/* BTS line */}
        <path d="M0 52 C 30 56, 60 52, 100 40" stroke="#9BB89F" strokeWidth="2" strokeDasharray="4 2.4" fill="none" />
        <rect x="60" y="48" width="16" height="12" rx="1.5" fill="#DCE4D2" />
      </svg>
      {pins.map((p, i) => (
        <button key={i} className="pin" style={{ left: p.x + "%", top: p.y + "%" }} aria-label={p.label}>
          <svg className="pin__dot" viewBox="0 0 24 24" aria-hidden="true">
            <path d="M12 2C7.6 2 4 5.6 4 10c0 6 8 12 8 12s8-6 8-12c0-4.4-3.6-8-8-8Z" fill={p.main ? "var(--red)" : "var(--accent)"} />
            <circle cx="12" cy="10" r="3" fill="#fff" />
          </svg>
          <span className="pin__label">{p.label}</span>
        </button>
      ))}
    </div>
  );
}

function Visit({ t }) {
  const [copied, setCopied] = useState(false);
  const v = t.visit;
  const address = `${v.addr1} ${v.addr2}`;
  const copy = async () => {
    try {
      if (navigator.clipboard && navigator.clipboard.writeText) await navigator.clipboard.writeText(address);
      else throw new Error("no clipboard");
    } catch {
      const el = document.getElementById("addr-line");
      if (el && window.getSelection) {
        const range = document.createRange(); range.selectNodeContents(el);
        const sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range);
      }
    }
    setCopied(true); setTimeout(() => setCopied(false), 2000);
  };
  return (
    <footer className="band band--green" id="visit">
      <div className="wrap">
        <Reveal>
          <h2 className="h-display" style={{ marginBottom: 36 }}>{v.title}</h2>
        </Reveal>
        <Reveal className="visit" tag="div">
          <div>
            <div className="visit__block">
              <div className="visit__label">{v.addrL}</div>
              <p id="addr-line">{v.addr1}<br />{v.addr2}</p>
              <p className="muted" style={{ fontSize: 14 }}>{v.addr3}</p>
              <div style={{ display: "flex", gap: 10, flexWrap: "wrap", marginTop: 12 }}>
                <a className="btn btn--outline" href={MAPS_URL} target="_blank" rel="noopener noreferrer">{v.dir}</a>
                <button className="copybtn" data-copied={copied} onClick={copy}>{copied ? v.copied : v.copy}</button>
              </div>
            </div>
            <div className="visit__block">
              <div className="visit__label">{v.hoursL}</div>
              <p>{v.hours}</p>
            </div>
            <div className="visit__block">
              <div className="visit__label">{v.contactL}</div>
              <p><a href={PHONE_TEL}>{PHONE}</a></p>
            </div>
            <div className="visit__block">
              <div className="visit__label">{v.followL}</div>
              <div className="socials">
                <a href={IG_URL} target="_blank" rel="noopener noreferrer">Instagram</a>
                <a href={FB_URL} target="_blank" rel="noopener noreferrer">Facebook</a>
                <a href={TIKTOK_URL} target="_blank" rel="noopener noreferrer">TikTok</a>
              </div>
            </div>
          </div>
          <StylizedMap t={t} />
        </Reveal>
        <div className="footer__bar">
          <span>{v.rights}</span>
          <span>{v.credit}</span>
        </div>
      </div>
    </footer>
  );
}

/* ----- Sticky (mobile): Call + Book ------------------------------------------- */
function StickyCta({ t }) {
  return (
    <div className="sticky" role="region" aria-label="Quick actions">
      <a className="call" href={PHONE_TEL}>📞 {t.sticky.call}</a>
      <button className="book" onClick={() => goto("reserve")}>🍽 {t.sticky.book}</button>
    </div>
  );
}

/* ----- App --------------------------------------------------------------------- */
function App() {
  const [lang, setLang] = useState(initialLang);
  const t = DICT[lang];
  useEffect(() => { loadAnalytics(); }, []);
  useEffect(() => {
    try { localStorage.setItem("site-lang", lang); } catch {}
    try {
      const params = new URLSearchParams(window.location.search);
      if (lang === DEFAULT_LANG) params.delete("lang");
      else params.set("lang", lang);
      const qs = params.toString();
      window.history.replaceState(null, "", window.location.pathname + (qs ? "?" + qs : "") + window.location.hash);
    } catch {}
    enhanceSeo(lang);
  }, [lang]);
  useEffect(() => {
    if (REDUCED || typeof Lenis === "undefined") return;
    const lenis = new Lenis({ duration: 1.1, smoothWheel: true });
    window.__lenis = lenis;
    let raf;
    const loop = (time) => { lenis.raf(time); raf = requestAnimationFrame(loop); };
    raf = requestAnimationFrame(loop);
    return () => { cancelAnimationFrame(raf); lenis.destroy(); window.__lenis = null; };
  }, []);
  const toggleLang = useCallback(() => setLang((l) => (l === "th" ? "en" : "th")), []);
  return (
    <div className="has-sticky">
      <Nav t={t} lang={lang} toggleLang={toggleLang} />
      <Hero t={t} />
      <Reservations t={t} />
      <Story t={t} />
      <Menu t={t} lang={lang} />
      <QuoteBand t={t} />
      <Gallery t={t} />
      <Faq t={t} />
      <Visit t={t} />
      <StickyCta t={t} />
    </div>
  );
}
ReactDOM.createRoot(document.getElementById("root")).render(<App />);
