const { useState, useEffect, useRef, useMemo } = React;

// ============ CONFIG ============
const SHOP = "TAKARA SCALE AGENCY";
const EMAIL = "inquiry@takarascale.jp"; // <-- replace with your real inbox
const SHIP_FROM_JPY = 1620; // lowest flat per-kit Air Packet rate (Zone 1) — used for "from" appeal

// ============ DATA ============
// status: "in"  -> In stock  -> Buy button
//         "out" -> Out of stock -> Waiting list
// (Stock states are placeholders for now — update `status` per kit later.)
// Products are loaded from products.json (single source, shared with product.php).
let PRODUCTS = [];
async function loadProducts() {
  try {
    const res = await fetch("products.json?v=" + Date.now(), { cache: "no-store" });
    const data = await res.json();
    const list = Array.isArray(data) ? data : (data && data.products) || [];
    if (Array.isArray(list) && list.length) { PRODUCTS = list; return true; }
  } catch (e) {}
  return false;
}
const productById = (id) => PRODUCTS.find((p) => p.id === id) || null;

// ---- shipping zones + rate table (shared with paypal-lib.php via shipping.json) ----
let SHIP = { zones: [{ id: 4, name: "Zone 4", blurb: "United States" }], rates: [[1000, 1620, 1830, 2500, 3090, 3260], [2000, 2620, 3030, 4300, 5190, 5860]] };
async function loadShipping() {
  try { const r = await fetch("shipping.json?v=" + Date.now()); const d = await r.json(); if (d && d.zones) SHIP = d; return true; } catch (e) {}
  return false;
}
// flat per-kit shipping in JPY; a packet holds up to 2 kits (mirrors the PHP)
function shippingJPY(kits, zoneId) {
  if (kits <= 0) return 0;
  const zi = Math.max(1, Math.min(5, zoneId));
  const rowFor = (g) => SHIP.rates.find((r) => g <= r[0]) || SHIP.rates[SHIP.rates.length - 1];
  const full = rowFor(2000)[zi], half = rowFor(1000)[zi];
  return Math.floor(kits / 2) * full + (kits % 2 ? half : 0);
}

// ---- PayPal public config (client id / mode / gating) ----
let PAYPAL = { mode: "sandbox", public_enabled: false, configured: false, client_id: "" };
async function loadPaypal() {
  try { const r = await fetch("paypal-config.php?v=" + Date.now()); PAYPAL = await r.json(); return true; } catch (e) {}
  return false;
}

// ---- cart store (localStorage), with a tiny pub/sub so components re-render ----
const cart = {
  items: (() => { try { return JSON.parse(localStorage.getItem("tsa_cart") || "[]"); } catch (e) { return []; } })(),
  subs: [],
  _save() { try { localStorage.setItem("tsa_cart", JSON.stringify(this.items)); } catch (e) {} this.subs.forEach((f) => f()); },
  count() { return this.items.reduce((n, i) => n + i.qty, 0); },
  qtyOf(id) { const i = this.items.find((x) => x.id === id); return i ? i.qty : 0; },
  add(id, q = 1) { const i = this.items.find((x) => x.id === id); if (i) i.qty = Math.min(20, i.qty + q); else this.items.push({ id, qty: q }); this._save(); },
  setQty(id, q) { q = Math.max(0, Math.min(20, q)); if (q === 0) this.items = this.items.filter((x) => x.id !== id); else { const i = this.items.find((x) => x.id === id); if (i) i.qty = q; } this._save(); },
  remove(id) { this.items = this.items.filter((x) => x.id !== id); this._save(); },
  clear() { this.items = []; this._save(); },
};
function useCart() {
  const [, f] = useState(0);
  useEffect(() => { const l = () => f((n) => n + 1); cart.subs.push(l); return () => { cart.subs = cart.subs.filter((x) => x !== l); }; }, []);
  return cart;
}
// precise charge amount in the active currency (mirrors pp_amount in PHP, for display)
function chargeAmount(jpy, currency) {
  const c = CURRENCIES[currency] || CURRENCIES.JPY;
  if (currency === "JPY") return { value: Math.round(jpy), display: "¥" + Math.round(jpy).toLocaleString("en-US") };
  const v = jpy * c.perYen;
  const r = Math.round(v * 100) / 100;
  return { value: r, display: c.sym + r.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }) };
}
// is checkout shown to this visitor? (live+enabled+configured, or owner test via ?shop=1)
function checkoutVisible() {
  const owner = /[?&]shop=1\b/.test(location.search);
  return PAYPAL.configured && (PAYPAL.public_enabled || owner);
}

const SCALES = ["All", "1/20", "1/24"];
const AVAIL = [
  { k: "all",     label: "All" },
  { k: "in",      label: "In stock" },
  { k: "inquiry", label: "Enquire" },
  { k: "out",     label: "Sold" },
];
// ============ CURRENCY ============
// FX vs JPY — units of foreign currency per ¥1. These are fallbacks; live rates
// (fetched from open.er-api.com, JPY base) overwrite perYen at runtime when available.
const CURRENCIES = {
  JPY: { sym: "¥",  perYen: 1,        label: "¥ JPY" },
  USD: { sym: "$",  perYen: 1 / 150,  label: "$ USD" },
  EUR: { sym: "€",  perYen: 1 / 165,  label: "€ EUR" },
  GBP: { sym: "£",  perYen: 1 / 190,  label: "£ GBP" },
  AUD: { sym: "A$", perYen: 1 / 100,  label: "A$ AUD" },
  CAD: { sym: "C$", perYen: 1 / 110,  label: "C$ CAD" },
};
const CURRENCY_KEYS = Object.keys(CURRENCIES);
// map an ISO country code to one of our currencies (for IP auto-localisation)
const EU_CC = ["DE","FR","IT","ES","NL","BE","AT","IE","PT","FI","GR","LU","SK","SI","EE","LV","LT","CY","MT","HR"];
function countryToCur(cc) {
  cc = (cc || "").toUpperCase();
  if (cc === "JP") return "JPY";
  if (cc === "GB") return "GBP";
  if (cc === "AU" || cc === "NZ") return "AUD";
  if (cc === "CA") return "CAD";
  if (cc === "US") return "USD";
  if (EU_CC.includes(cc)) return "EUR";
  return "USD";
}
let activeCur = "JPY";  // set synchronously by <App> on each render
let ratesLive = false;  // true once live FX rates have been applied

// Overwrite perYen from an {CUR: per-1-JPY} map (skips JPY / bad values)
function applyRates(rates) {
  let any = false;
  for (const k of CURRENCY_KEYS) {
    if (k === "JPY") continue;
    if (typeof rates[k] === "number" && rates[k] > 0) { CURRENCIES[k].perYen = rates[k]; any = true; }
  }
  if (any) ratesLive = true;
  return any;
}

// Fetch live FX once a day (cached in localStorage); falls back silently on failure
async function loadRates() {
  const now = Date.now();
  try {
    const cached = JSON.parse(localStorage.getItem("tsa_rates") || "null");
    if (cached && cached.r && now - cached.t < 12 * 3600 * 1000) return applyRates(cached.r);
  } catch (e) {}
  try {
    const res = await fetch("https://open.er-api.com/v6/latest/JPY");
    const data = await res.json();
    if (data && data.rates) {
      const ok = applyRates(data.rates);
      try { localStorage.setItem("tsa_rates", JSON.stringify({ t: now, r: data.rates })); } catch (e) {}
      return ok;
    }
  } catch (e) {}
  return false;
}

// POST a form submission to the server mailer (contact.php). Throws on failure.
async function sendInquiry(payload) {
  const res = await fetch("contact.php", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(payload),
  });
  let data = {};
  try { data = await res.json(); } catch (e) {}
  if (!res.ok || !data.ok) throw new Error(data.error || "Could not send. Please email us directly.");
  return data;
}

const yen = (n) => "¥" + n.toLocaleString("en-US");
// money() formats a JPY amount in the currently-selected currency (≈ = approx)
function money(n) {
  const c = CURRENCIES[activeCur] || CURRENCIES.JPY;
  if (activeCur === "JPY") return yen(n);
  const v = n * c.perYen;
  const r = v >= 50 ? Math.round(v / 5) * 5 : Math.round(v);
  return c.sym + r.toLocaleString("en-US");
}
const statusLabel = (s) => s === "in" ? "In stock" : s === "inquiry" ? "Enquire" : "Sold out";

// total photos a product has (front + decal + box[] + runner[] + note images)
function imgCount(p) {
  const im = p.images || {};
  let n = 0;
  if (im.front) n++;
  if (im.decal) n++;
  if (Array.isArray(im.box)) n += im.box.length;
  if (Array.isArray(im.runner)) n += im.runner.length;
  if (Array.isArray(p.notes)) n += p.notes.filter((nt) => nt && nt.img).length;
  if (n === 0 && p.img) n = 1; // legacy single-image products
  return n;
}

// ============ MAILTO ============
function buildMailto({ subject, body }) {
  return `mailto:${EMAIL}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
}

// ============ reveal hook ============
function useReveal(dep) {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const els = ref.current.querySelectorAll(".card");
    if (!("IntersectionObserver" in window)) { els.forEach((el) => el.classList.add("in")); return; }
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }),
      { threshold: 0.08 }
    );
    els.forEach((el) => io.observe(el));
    // safety net: if the observer hasn't revealed a card shortly after mount, show them anyway
    const t = setTimeout(() => { els.forEach((el) => el.classList.add("in")); }, 1200);
    return () => { clearTimeout(t); io.disconnect(); };
  }, [dep]);
  return ref;
}

// close an overlay (modal / cart) when the browser/phone back button is pressed
function useBackClose(onClose) {
  useEffect(() => {
    let byPop = false;
    window.history.pushState({ ovl: 1 }, "");
    const onPop = () => { byPop = true; onClose(); };
    window.addEventListener("popstate", onPop);
    return () => {
      window.removeEventListener("popstate", onPop);
      if (!byPop) window.history.back(); // closed via button → pop our history entry
    };
  }, []);
}

// ============ MT FUJI SVG ============
function FujiSilhouette({ className }) {
  return (
    <svg className={className} viewBox="0 0 1440 220" preserveAspectRatio="xMidYMax meet" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
      <path d="M0 220 L470 220 L620 70 Q660 38 700 38 Q740 38 780 70 L930 220 L1440 220 L1440 220 L0 220 Z" fill="currentColor" opacity="0.9" />
      <path d="M636 86 Q700 56 764 86 L726 118 Q700 104 674 118 L636 86 Z" fill="#fff" />
    </svg>
  );
}

// ============ ICONS ============
const Icon = {
  japan: (
    <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4">
      <circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="3.4" fill="currentColor" stroke="none" />
    </svg>
  ),
  search: (
    <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4">
      <circle cx="10.5" cy="10.5" r="6.5" /><path d="M15.5 15.5L21 21" strokeLinecap="round" />
    </svg>
  ),
  box: (
    <svg className="ic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.4">
      <path d="M3 7.5L12 3l9 4.5v9L12 21l-9-4.5v-9z" strokeLinejoin="round" /><path d="M3 7.5L12 12l9-4.5M12 12v9" />
    </svg>
  ),
  cart: (
    <svg className="bic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
      <path d="M3 4h2l2.2 11.2a1 1 0 0 0 1 .8h8.5a1 1 0 0 0 1-.8L20.5 8H6.2" strokeLinecap="round" strokeLinejoin="round" />
      <circle cx="9.5" cy="20" r="1.3" fill="currentColor" stroke="none" /><circle cx="17.5" cy="20" r="1.3" fill="currentColor" stroke="none" />
    </svg>
  ),
  bell: (
    <svg className="bic" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
      <path d="M6 9a6 6 0 0 1 12 0c0 5 2 6 2 6H4s2-1 2-6Z" strokeLinejoin="round" /><path d="M10 19a2 2 0 0 0 4 0" strokeLinecap="round" />
    </svg>
  ),
};

// ============ TOP BAR ============
function NoticeBar() {
  const ref = useRef(null);
  // keep the sticky header offset (--notice-h) in sync with this bar's height
  useEffect(() => {
    const sync = () => {
      const h = ref.current ? ref.current.offsetHeight : 0;
      document.documentElement.style.setProperty("--notice-h", h + "px");
    };
    sync();
    window.addEventListener("resize", sync);
    return () => window.removeEventListener("resize", sync);
  }, []);
  return (
    <div
      ref={ref}
      role="status"
      style={{
        position: "sticky", top: 0, zIndex: 60,
        background: "#b3261e", color: "#fff", textAlign: "center",
        padding: "calc(9px + env(safe-area-inset-top)) 16px 9px", fontSize: "13.5px", lineHeight: 1.45, fontWeight: 600,
      }}
    >
      <span style={{ marginRight: 6 }}>⚠</span>
      Checkout is temporarily under maintenance — please place orders via the enquiry form (Ask).
    </div>
  );
}

function TopBar() {
  return (
    <div className="topbar">
      <div className="wrap">
        <span className="sun-mini"></span>
        <span>Based in Japan · <b>Flat tracked shipping from {money(SHIP_FROM_JPY)} / kit</b> · Worldwide</span>
      </div>
    </div>
  );
}

// ============ HEADER ============
function Header({ onContact, currency, setCurrency, onCart }) {
  const c = useCart();
  const n = c.count();
  return (
    <header className="site">
      <div className="wrap nav">
        <a href="#top" className="brand">
          <svg className="brand-logo" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
            <defs><clipPath id="blClip"><circle cx="20" cy="20" r="19" /></clipPath></defs>
            <g clipPath="url(#blClip)">
              <rect width="40" height="40" fill="#f4f2ec" />
              <circle cx="20" cy="15.5" r="7.4" fill="#cf2f2c" />
              <path d="M1 35 L20 17.5 L39 35 Z" fill="#2a2a2a" />
              <path d="M20 17.5 L15.6 21.5 L17.7 20.4 L20 22 L22.3 20.4 L24.4 21.5 Z" fill="#fff" />
            </g>
            <circle cx="20" cy="20" r="19" fill="none" stroke="#2a2a2a" strokeOpacity="0.12" strokeWidth="1.4" />
          </svg>
          <span className="bt">
            <span className="mark">TAKARA SCALE AGENCY</span>
            <span className="sub">Discontinued model kit export, from Japan</span>
          </span>
        </a>
        <nav className="nav-links">
          <a href="#stock">Stock</a>
          <a href="tools/">Tools</a>
          <a href="articles/">Articles</a>
          <a href="Shipping.html">Shipping</a>
          <a href="#ask">Ask</a>
        </nav>
        <div className="nav-right">
          <select
            className="cur-sel"
            value={currency}
            onChange={(e) => setCurrency(e.target.value)}
            aria-label="Display currency"
            style={{ background: "transparent", border: "1px solid rgba(0,0,0,0.18)", borderRadius: "8px", padding: "6px 8px", font: "inherit", fontSize: "13px", cursor: "pointer", color: "inherit" }}
          >
            {CURRENCY_KEYS.map((k) => <option key={k} value={k}>{CURRENCIES[k].label}</option>)}
          </select>
          <button className="btn btn--sm nav-ask" onClick={() => onContact(undefined)}>Ask</button>
          <button className="btn btn--sm btn--red" onClick={onCart} aria-label="Cart" style={{ position: "relative" }}>
            {Icon.cart} Cart{n > 0 ? ` · ${n}` : ""}
          </button>
        </div>
      </div>
    </header>
  );
}

// ============ HERO EMBLEM (Rising sun + Mt Fuji) ============
function HeroEmblem() {
  const cx = 260, cy = 250, r0 = 122, r1 = 244;
  const p = (r, ang) => `${(cx + r * Math.cos(ang)).toFixed(1)} ${(cy + r * Math.sin(ang)).toFixed(1)}`;
  const rays = Array.from({ length: 32 }, (_, i) => {
    const a = (i * (360 / 32)) * Math.PI / 180;
    const w = 0.045;
    return `M${p(r0, a - w)} L${p(r1, a)} L${p(r0, a + w)} Z`;
  });
  return (
    <svg className="emblem" viewBox="0 0 520 520" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
      <defs>
        <clipPath id="embClip"><circle cx="260" cy="250" r="240" /></clipPath>
      </defs>
      <g clipPath="url(#embClip)">
        <g fill="var(--accent)" opacity="0.17">
          {rays.map((d, i) => <path key={i} d={d} />)}
        </g>
        <circle cx="260" cy="250" r="120" fill="var(--accent)" />
        <path d="M-20 502 C 110 484 198 432 236 344 L248 338 L254 344 L260 320 L266 344 L272 338 L284 344 C 322 432 410 484 540 502 L540 542 L-20 542 Z" fill="var(--ink)" />
        <path d="M236 344 L248 338 L254 344 L260 320 L266 344 L272 338 L284 344 L292 360 L281 354 L272 362 L262 356 L252 363 L242 356 L232 361 Z" fill="#fff" />
      </g>
      <circle cx="260" cy="250" r="240" fill="none" stroke="var(--line)" strokeWidth="1.5" />
    </svg>
  );
}

// ============ HERO ============
function Hero({ ready }) {
  const inStock = PRODUCTS.filter((p) => p.status === "in").length;
  return (
    <section className="hero" id="top">
      <div className="hero-bg">
        <div className="hero-sun"></div>
        <div className="rays"></div>
        <div className="sun-glow"></div>
      </div>
      <div className="wrap hero-grid">
        <div className="hero-copy">
          <span className="eyebrow">Discontinued · Collector grade</span>
          <h1>Rare racing model kits, <em>found in Japan.</em></h1>
          <p className="lead">
            A rotating stock of out-of-production Tamiya, Fujimi and Hasegawa racing kits —
            Grand Prix, sports and GT. Buy what's in stock outright; for sold-out boxings,
            join the waiting list and we'll source the next one from the Japanese market.
          </p>
          <div className="hero-cta">
            <a href="#stock" className="btn">Browse the collection <span className="btn-arrow">↓</span></a>
          </div>
        </div>
      </div>
    </section>
  );
}

// ============ FEATURES ============
function Features() {
  const items = [
    { ic: Icon.japan, h: "Based in Japan", p: "We operate inside Japan — the world's deepest market for discontinued Tamiya, Fujimi and Hasegawa kits." },
    { ic: Icon.search, h: "Second-hand access", p: "Direct reach into the Japanese second-hand and collector market, where long-deleted kits are far easier to find than abroad." },
    { ic: Icon.box, h: "Condition first", p: "Unless noted, every kit is genuine, sealed and unbuilt. We send condition photos before you commit." },
  ];
  return (
    <section className="features" id="about">
      <div className="wrap feat-grid">
        {items.map((it, i) => (
          <div className="feat" key={i}>
            {it.ic}
            <h3>{it.h}</h3>
            <p>{it.p}</p>
          </div>
        ))}
      </div>
    </section>
  );
}

// ============ PRODUCT CARD ============
function Card({ p, onBuy, onWaitlist, onInquiry }) {
  const isIn      = p.status === "in";
  const isInquiry = p.status === "inquiry";
  const statusCls = isInquiry ? "status--inquiry" : `status--${p.status}`;
  const detail = `product.php?id=${p.id}`;
  return (
    <article className="card">
      <div className="card-media">
        <div className="media-tags">
          <span className="tag tag--scale">{p.scale}</span>
          <span className="tag tag--maker">{p.maker}</span>
        </div>
        <span className={`status ${statusCls}`}><span className="d"></span>{statusLabel(p.status)}</span>
        <a href={detail} aria-label={p.title}><img src={p.img} alt={`${p.maker} ${p.title} ${p.scale} model kit`} loading="lazy" /></a>
      </div>
      <div className="card-body">
        <span className="card-maker" style={{ fontSize: "13.5px", fontWeight: 700, color: "var(--accent)", letterSpacing: "0.04em" }}>{p.maker} · {p.scale}</span>
        <h3><a href={detail} style={{ color: "inherit", textDecoration: "none" }}>{p.title}</a></h3>
        <p className="sub">{p.sub}</p>
        <div className="specline">
          {p.specs.slice(0, 4).map((s) => <span key={s}>{s}</span>)}
          {p.specs.length > 4 && <span style={{ color: "var(--ink-faint)" }}>+{p.specs.length - 4} more</span>}
        </div>
        <a href={detail} style={{ display: "inline-block", marginTop: "12px", padding: "11px 22px", fontSize: "15px", fontWeight: 700, color: "#fff", background: "var(--accent)", borderRadius: "6px", textDecoration: "none", letterSpacing: "0.02em", boxShadow: "0 2px 10px oklch(0.55 0.2 27 / 0.28)" }}>View details &amp; photos →</a>
        <div className="card-foot">
          <div>
            <div className="price-k">{isIn ? "Price" : isInquiry ? "Guide price" : "Status"}</div>
            <div className={`price-v${isIn || isInquiry ? "" : " muted"}`}>{isIn || isInquiry ? money(p.price) : "Sold out"}</div>
          </div>
          {isIn      && <button className="btn btn--red btn--sm"   onClick={() => onBuy(p)}>{Icon.cart} Add to cart</button>}
          {isInquiry && <button className="btn btn--red btn--sm"   onClick={() => onInquiry(p)}>{Icon.cart} Add to cart</button>}
          {!isIn && !isInquiry && <button className="btn btn--ghost btn--sm" onClick={() => onWaitlist(p)}>{Icon.bell} Waiting list</button>}
        </div>
      </div>
    </article>
  );
}

// ============ STOCK ============
function Stock({ onBuy, onWaitlist, onInquiry, ready }) {
  const [scale, setScale] = useState("All");
  const [avail, setAvail] = useState("all");
  const [sort, setSort] = useState("featured");

  const list = useMemo(() => {
    let l = PRODUCTS.filter((p) => (scale === "All" || p.scale === scale) && (avail === "all" || p.status === avail));
    const arr = (p) => (p.arrived ? Date.parse(p.arrived) || 0 : 0); // newest arrival first if dated
    if (sort === "featured") {
      // Featured: available first → Rank S→A→B → most photos → newest arrival; sold/out at the bottom
      const av = (p) => (p.status === "out" ? 0 : 1);
      const rk = (p) => ({ S: 3, A: 2, B: 1 }[p.rank] || 0);
      l = [...l].sort((a, b) => av(b) - av(a) || rk(b) - rk(a) || imgCount(b) - imgCount(a) || arr(b) - arr(a) || PRODUCTS.indexOf(a) - PRODUCTS.indexOf(b));
    }
    else if (sort === "price-asc") l = [...l].sort((a, b) => a.price - b.price);
    else if (sort === "price-desc") l = [...l].sort((a, b) => b.price - a.price);
    else if (sort === "name") l = [...l].sort((a, b) => a.title.localeCompare(b.title));
    else if (sort === "stock") l = [...l].sort((a, b) => (a.status === b.status ? 0 : a.status === "in" ? -1 : 1));
    return l;
  }, [scale, avail, sort, ready]);

  // pagination — 10 per page
  const PER = 10;
  const [page, setPage] = useState(1);
  useEffect(() => { setPage(1); }, [scale, avail, sort]);
  const pages = Math.max(1, Math.ceil(list.length / PER));
  const cur = Math.min(page, pages);
  const pageList = list.slice((cur - 1) * PER, cur * PER);
  const goPage = (n) => { setPage(n); const el = document.getElementById("stock"); if (el) el.scrollIntoView({ behavior: "smooth", block: "start" }); };

  const ref = useReveal(scale + avail + sort + cur + ready);

  return (
    <section className="sec sec--stock" id="stock">
      <div className="wrap">
        <div className="sec-head">
          <span className="eyebrow eyebrow--dim">The Collection</span>
          <h2>Buy in stock, waitlist the rest</h2>
          <p>Genuine and, unless noted, sealed and unbuilt. In-stock kits ship now — sold-out boxings take a waiting list, and we'll source the next one from Japan.</p>
          <p style={{ marginTop: "12px", fontSize: "14.5px", fontWeight: 600 }}>
            <span aria-hidden="true">📦</span> Flat tracked worldwide shipping from <span style={{ color: "var(--accent)" }}>{money(SHIP_FROM_JPY)} / kit</span> · <a href="Shipping.html" style={{ textDecoration: "underline" }}>see rates →</a>
          </p>
        </div>

        <div className="toolbar">
          <div className="filter-groups">
            <div className="filters">
              <span className="f-label">Scale</span>
              {SCALES.map((s) => (
                <button key={s} className={`chip${scale === s ? " active" : ""}`} onClick={() => setScale(s)}>{s}</button>
              ))}
            </div>
            <div className="filters">
              <span className="f-label">Status</span>
              {AVAIL.map((a) => (
                <button key={a.k} className={`chip${avail === a.k ? " active" : ""}`} onClick={() => setAvail(a.k)}>{a.label}</button>
              ))}
            </div>
          </div>
          <div className="tools-right">
            <span className="count"><b>{list.length}</b> {list.length === 1 ? "kit" : "kits"}</span>
            <select className="sort" value={sort} onChange={(e) => setSort(e.target.value)}>
              <option value="featured">Featured</option>
              <option value="stock">In stock first</option>
              <option value="price-desc">Price: high → low</option>
              <option value="price-asc">Price: low → high</option>
              <option value="name">Name A–Z</option>
            </select>
          </div>
        </div>

        {!ready ? (
          <div style={{ textAlign: "center", padding: "70px 20px", color: "var(--ink-faint)" }} className="jost">
            Loading the collection…
          </div>
        ) : list.length > 0 ? (
          <React.Fragment>
            <div className="pgrid" ref={ref} key={scale + avail + sort + cur}>
              {pageList.map((p) => <Card key={p.id} p={p} onBuy={onBuy} onWaitlist={onWaitlist} onInquiry={onInquiry} />)}
            </div>
            {pages > 1 && (
              <div className="pager">
                <button disabled={cur === 1} onClick={() => goPage(cur - 1)}>← Prev</button>
                {Array.from({ length: pages }, (_, i) => i + 1).map((n) => (
                  <button key={n} className={n === cur ? "on" : ""} onClick={() => goPage(n)}>{n}</button>
                ))}
                <button disabled={cur === pages} onClick={() => goPage(cur + 1)}>Next →</button>
              </div>
            )}
          </React.Fragment>
        ) : (
          <EmptyState scale={scale} avail={avail} onRequest={() => onWaitlist(null)} />
        )}
      </div>
    </section>
  );
}

function EmptyState({ scale, avail, onRequest }) {
  const what = avail === "in" ? "in-stock" : avail === "out" ? "waiting-list" : "";
  return (
    <div style={{ textAlign: "center", padding: "70px 20px", border: "1px solid var(--line)", borderRadius: "5px", background: "var(--bg-soft)" }}>
      <div className="jost" style={{ fontSize: 11.5, letterSpacing: "0.16em", color: "var(--ink-faint)", textTransform: "uppercase" }}>No {scale !== "All" ? scale + " " : ""}{what} kits right now</div>
      <h3 className="serif" style={{ fontSize: 27, fontWeight: 700, margin: "14px 0 10px" }}>But we can find one.</h3>
      <p style={{ color: "var(--ink-2)", maxWidth: "42ch", margin: "0 auto 24px", fontSize: 15 }}>
        Tell us the exact kit you want and we'll source it from the Japanese market — or add you to the waiting list.
      </p>
      <button className="btn btn--red" onClick={onRequest}>Request a kit →</button>
    </div>
  );
}

// ============ SOURCING ============
function Sourcing({ onRequest }) {
  return (
    <section className="sourcing sec" id="ask">
      <FujiSilhouette className="fuji-faint" />
      <div className="wrap src-grid">
        <div className="src-left">
          <span className="eyebrow">Ask the Agency</span>
          <h2>Tell us what you're after.<br /><em>We'll find it, price it, ship it.</em></h2>
          <p>
            Found a kit in our stock, want something we don't list yet, or just have a question about
            condition or shipping? This is the one place to ask. We're based inside Japan with direct
            reach into the domestic second-hand market, collector channels and local shops — so deleted
            kits that rarely surface abroad are well within reach.
          </p>
          <div className="jp-line">
            Tell us the kit or question you have.<br />
            We'll source it from Japan's domestic market and quote you.
          </div>
        </div>
        <RequestForm onRequest={onRequest} />
      </div>
    </section>
  );
}

function RequestForm({ onRequest }) {
  const [f, setF] = useState({ name: "", email: "", country: "", scale: "1/20", model: "", notes: "", company: "" });
  const [sending, setSending] = useState(false);
  const [err, setErr] = useState(null);
  const upd = (k) => (e) => setF({ ...f, [k]: e.target.value });

  const submit = async (e) => {
    e.preventDefault();
    if (sending) return;
    setSending(true); setErr(null);
    const hasModel = f.model.trim();
    try {
      await sendInquiry({
        type: "sourcing",
        name: f.name, email: f.email, country: f.country,
        model: f.model, scale: f.scale, message: f.notes,
        company: f.company, // honeypot
      });
      onRequest({ label: hasModel ? f.model : "your inquiry", kind: "sourcing", ok: true });
    } catch (e2) {
      setErr(e2.message || "Could not send. Please email us directly.");
    } finally {
      setSending(false);
    }
  };

  return (
    <form className="form-card" onSubmit={submit}>
      <h3>Ask the agency</h3>
      <div className="fh-sub">Source a kit, check a price, or ask about shipping — we'll reply by email.</div>
      <div className="field-row">
        <div className="field"><label>Your name</label><input value={f.name} onChange={upd("name")} placeholder="Jane Doe" required /></div>
        <div className="field"><label>Email</label><input type="email" value={f.email} onChange={upd("email")} placeholder="you@email.com" required /></div>
      </div>
      <div className="field-row">
        <div className="field"><label>Country</label><input value={f.country} onChange={upd("country")} placeholder="e.g. Germany" /></div>
        <div className="field"><label>Scale</label>
          <select value={f.scale} onChange={upd("scale")}>
            <option>1/20</option><option>1/24</option><option>1/12</option><option>Any / unsure</option>
          </select>
        </div>
      </div>
      <div className="field"><label>Kit you're after <span style={{ color: "var(--ink-faint)", fontWeight: 400 }}>(optional)</span></label><input value={f.model} onChange={upd("model")} placeholder="e.g. Tamiya 1/20 Lotus 102B — or leave blank for a general question" /></div>
      <div className="field"><label>Your message</label><textarea rows="3" value={f.notes} onChange={upd("notes")} placeholder="Your question or request — stock, price, shipping, condition, budget…" required></textarea></div>
      <input type="text" name="company" value={f.company} onChange={upd("company")} tabIndex="-1" autoComplete="off" style={{ position: "absolute", left: "-9999px", width: 1, height: 1, opacity: 0 }} aria-hidden="true" />
      {err && <div className="form-note" style={{ color: "var(--accent)" }}>{err}</div>}
      <div className="modal-foot">
        <button type="submit" className="btn btn--red" disabled={sending}>{sending ? "Sending…" : "Send →"}</button>
        <span className="form-note">We'll reply to your email.</span>
      </div>
    </form>
  );
}

// ============ TRANSACTION MODAL (buy / waitlist / general) ============
const MODE = {
  buy: {
    k: "Order", heading: (p) => p.title, badge: "Buy",
    intro: (p) => `In stock now · ${money(p.price)}`,
    label: "Place order →",
    note: "We'll confirm stock, total and shipping by email.",
    msgLabel: "Message (optional)", msgReq: false,
    msgPlaceholder: "Any questions about condition, decals, shipping…",
  },
  inquiry: {
    k: "Enquiry", heading: (p) => p.title, badge: "Enquire",
    intro: (p) => `Guide price · ${money(p.price)}`,
    label: "Send enquiry →",
    note: "We'll confirm availability, final price and shipping by email.",
    msgLabel: "Message (optional)", msgReq: false,
    msgPlaceholder: "Any questions about condition, price, shipping, availability…",
  },
  waitlist: {
    k: "Waiting list", heading: (p) => p.title, badge: "Out of stock",
    intro: (p) => `Currently out of stock — join the waiting list`,
    label: "Join waiting list →",
    note: "We'll email you the moment one is sourced.",
    msgLabel: "Anything we should know? (optional)", msgReq: false,
    msgPlaceholder: "Target budget, condition you'd accept, alternative liveries…",
  },
  general: {
    k: "Contact", heading: () => "Send an inquiry", badge: null,
    intro: () => null,
    label: "Send inquiry →",
    note: null,
    msgLabel: "Message", msgReq: true,
    msgPlaceholder: "What are you looking for?",
  },
};

function TxModal({ mode, product, onClose, onSent }) {
  const [f, setF] = useState({ name: "", email: "", country: "", message: "", company: "" });
  const [sending, setSending] = useState(false);
  const [err, setErr] = useState(null);
  const upd = (k) => (e) => setF({ ...f, [k]: e.target.value });
  const cfg = MODE[mode];
  const isItem = !!product;
  useBackClose(onClose);

  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, []);

  const submit = async (e) => {
    e.preventDefault();
    if (sending) return;
    setSending(true); setErr(null);
    try {
      await sendInquiry({
        type: mode,
        name: f.name, email: f.email, country: f.country, message: f.message,
        item: product ? { maker: product.maker, title: product.title, scale: product.scale, price: product.price } : null,
        currency: activeCur,
        company: f.company, // honeypot
      });
      onSent({ label: isItem ? product.title : "your inquiry", kind: mode, ok: true });
    } catch (e2) {
      setErr(e2.message || "Could not send. Please email us directly.");
    } finally {
      setSending(false);
    }
  };

  return (
    <div className="modal-back" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal">
        <div className="modal-head">
          {isItem && <img className="modal-thumb" src={product.img} alt="" />}
          <div>
            <div className="mh-k">{cfg.k}</div>
            <h3>{cfg.heading(product || {})}</h3>
            {isItem && <div className="mh-sub">{product.maker} · {product.scale} · {cfg.intro(product)}</div>}
          </div>
          <button className="modal-x" onClick={onClose} aria-label="Close">×</button>
        </div>
        <form className="modal-body" onSubmit={submit}>
          <div className="field-row">
            <div className="field"><label>Your name</label><input value={f.name} onChange={upd("name")} placeholder="Jane Doe" required /></div>
            <div className="field"><label>Email</label><input type="email" value={f.email} onChange={upd("email")} placeholder="you@email.com" required /></div>
          </div>
          <div className="field"><label>Country</label><input value={f.country} onChange={upd("country")} placeholder="e.g. United Kingdom" /></div>
          <div className="field">
            <label>{cfg.msgLabel}</label>
            <textarea rows="3" value={f.message} onChange={upd("message")} placeholder={cfg.msgPlaceholder} required={cfg.msgReq}></textarea>
          </div>
          <input type="text" name="company" value={f.company} onChange={upd("company")} tabIndex="-1" autoComplete="off" style={{ position: "absolute", left: "-9999px", width: 1, height: 1, opacity: 0 }} aria-hidden="true" />
          {err && <div className="form-note" style={{ color: "var(--accent)" }}>{err}</div>}
          <div className="modal-foot">
            <button type="submit" className="btn btn--red" disabled={sending}>{sending ? "Sending…" : cfg.label}</button>
            <span className="form-note">{cfg.note || "We'll reply to your email shortly."}</span>
          </div>
        </form>
      </div>
    </div>
  );
}

// ============ SENT BANNER ============
const SENT = {
  buy:      { k: "Order received",   h: "Thank you — message sent", body: (l) => <>We've received your order request for <b>{l}</b>. We'll reply by email to confirm the total, condition photos and shipping to your country.</> },
  inquiry:  { k: "Enquiry sent",     h: "Thank you — message sent", body: (l) => <>We've received your enquiry about <b>{l}</b>. We'll reply by email with availability, final price and shipping details.</> },
  waitlist: { k: "On the list",      h: "Thank you — message sent", body: (l) => <>We've added your waiting-list request for <b>{l}</b>. We'll email you the moment one is sourced.</> },
  sourcing: { k: "Request sent",     h: "Thank you — message sent", body: (l) => <>We've received your request for <b>{l}</b>. We'll hunt it down across Japan and reply by email with availability and a price.</> },
  general:  { k: "Message sent",     h: "Thank you — message sent", body: (l) => <>We've received your message about <b>{l}</b>. We'll reply by email shortly.</> },
  order:    { k: "Order confirmed",  h: "Thank you — payment received", body: () => <>Your payment went through and <b>your order is confirmed</b>. We'll email your receipt and tracking as soon as it ships from Japan.</> },
};

function SentBanner({ label, kind, onClose }) {
  const cfg = SENT[kind] || SENT.inquiry;
  useBackClose(onClose);
  return (
    <div className="modal-back" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal">
        <div className="modal-head">
          <div>
            <div className="mh-k">{cfg.k}</div>
            <h3>{cfg.h}</h3>
          </div>
          <button className="modal-x" onClick={onClose}>×</button>
        </div>
        <div className="modal-body">
          <div className="ok-banner">{cfg.body(label)}</div>
          <div className="copy-row">
            <span>We'll reply to the email address you gave us — please check your inbox (and spam).</span>
          </div>
          <div className="modal-foot">
            <button className="btn btn--red" onClick={onClose}>Done</button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ============ INQUIRY CTA ============
function InquiryCTA({ onOpen }) {
  return (
    <section className="sec" id="inquiry">
      <div className="wrap" style={{ textAlign: "center", maxWidth: 680, margin: "0 auto" }}>
        <span className="eyebrow">Inquiry</span>
        <h2 className="serif" style={{ fontSize: "clamp(30px,4vw,46px)", fontWeight: 700, lineHeight: 1.2, margin: "14px 0 16px" }}>
          Found one in stock? It's yours.
        </h2>
        <p style={{ color: "var(--ink-2)", fontSize: 16, lineHeight: 1.85, maxWidth: "52ch", margin: "0 auto 30px" }}>
          In-stock kits can be bought outright — we confirm the total, condition photos and shipping by email.
          Anything sold out, join the waiting list, or send us a kit to hunt down across Japan.
        </p>
        <div style={{ display: "flex", gap: 14, justifyContent: "center", flexWrap: "wrap" }}>
          <button className="btn btn--red" onClick={() => onOpen(undefined)}>Send an inquiry →</button>
          <a href="#sourcing" className="btn btn--ghost">Request a specific model</a>
        </div>
      </div>
    </section>
  );
}

// ============ FOOTER ============
function Footer() {
  return (
    <footer className="site">
      <FujiSilhouette className="fuji-foot" />
      <div className="wrap">
        <div className="foot-grid">
          <div className="foot-brand">
            <span className="mark">TAKARA SCALE <b>AGENCY</b></span>
            <p>Discontinued & premium racing scale model kits, sourced from inside Japan. Built around 1/20 Formula 1, expanding across sports & GT.</p>
          </div>
          <div className="foot-links">
            <div className="foot-col">
              <h4>Browse</h4>
              <a href="#stock">The collection</a>
              <a href="tools/">Modeller's tools</a>
              <a href="Shipping.html">Shipping &amp; rates</a>
              <a href="#ask">Ask the agency</a>
            </div>
            <div className="foot-col">
              <h4>Contact</h4>
              <a href="#ask">Send an enquiry</a>
              <span>Based in Japan</span>
              <span>Ships worldwide</span>
            </div>
          </div>
        </div>
        <div className="foot-bottom">
          <span>© {new Date().getFullYear()} {SHOP} · Independent collector store</span>
          <span>TAMIYA · FUJIMI · HASEGAWA · 1/20 · 1/24</span>
        </div>
      </div>
    </footer>
  );
}

// ============ CART / CHECKOUT ============
let _ppLoadedCur = null;
function loadPaypalSdk(clientId, currency) {
  return new Promise((resolve) => {
    if (window.paypal && _ppLoadedCur === currency) return resolve(window.paypal);
    const old = document.getElementById("paypal-sdk");
    if (old) { old.remove(); try { window.paypal = undefined; } catch (e) {} }
    const s = document.createElement("script");
    s.id = "paypal-sdk";
    s.src = "https://www.paypal.com/sdk/js?client-id=" + encodeURIComponent(clientId) +
            "&currency=" + encodeURIComponent(currency) + "&intent=capture&components=buttons";
    s.onload = () => { _ppLoadedCur = currency; resolve(window.paypal); };
    s.onerror = () => resolve(null);
    document.body.appendChild(s);
  });
}

function CartDrawer({ currency, onClose, onInquire, onDone }) {
  const c = useCart();
  const zones = SHIP.zones || [];
  const [zone, setZone] = useState(() => (zones.find((z) => z.id === 4) ? 4 : (zones[0] ? zones[0].id : 4)));
  const [err, setErr] = useState(null);
  const [busy, setBusy] = useState(false);
  const ppRef = useRef(null);
  useBackClose(onClose);

  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, []);

  // resolve cart lines against products
  const lines = c.items.map((it) => ({ it, p: productById(it.id) })).filter((x) => x.p);
  const kits = lines.reduce((n, x) => n + x.it.qty, 0);
  // shipping weight units: 1/12 kits are large → count as 2 units (≈2 kg); others 1 unit
  const units = lines.reduce((n, x) => n + x.it.qty * (x.p.scale === "1/12" ? 2 : 1), 0);
  const subtotalJPY = lines.reduce((n, x) => n + x.p.price * x.it.qty, 0);
  const shipJPY = shippingJPY(units, zone);
  const totalJPY = subtotalJPY + shipJPY;
  const showCheckout = checkoutVisible();

  // render PayPal buttons when visible
  useEffect(() => {
    if (!showCheckout || lines.length === 0 || !ppRef.current) return;
    let cancelled = false;
    loadPaypalSdk(PAYPAL.client_id, currency).then((paypal) => {
      if (cancelled || !paypal || !ppRef.current) return;
      ppRef.current.innerHTML = "";
      try {
        paypal.Buttons({
          style: { layout: "vertical", shape: "rect", label: "paypal" },
          createOrder: async () => {
            setErr(null);
            const res = await fetch("paypal-create-order.php", {
              method: "POST", headers: { "Content-Type": "application/json" },
              body: JSON.stringify({ items: cart.items, currency, zone }),
            });
            const d = await res.json();
            if (!d.id) { setErr("Could not start checkout. Please try again."); throw new Error("create"); }
            return d.id;
          },
          onApprove: async (data) => {
            setBusy(true);
            try {
              const res = await fetch("paypal-capture-order.php", {
                method: "POST", headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ orderID: data.orderID }),
              });
              const d = await res.json();
              if (d.ok) { cart.clear(); onDone({ label: "your order", kind: "order", ok: true }); }
              else setErr("Payment didn't complete. You were not charged — please try again.");
            } catch (e) { setErr("Network error during payment."); }
            finally { setBusy(false); }
          },
          onError: () => setErr("PayPal had a problem. Please try again, or send an enquiry."),
        }).render(ppRef.current);
      } catch (e) { setErr("Could not load PayPal."); }
    });
    return () => { cancelled = true; };
  }, [currency, zone, showCheckout, lines.length]);

  return (
    <div className="modal-back" onMouseDown={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className="modal" style={{ maxWidth: 560 }}>
        <div className="modal-head">
          <div>
            <div className="mh-k">Cart</div>
            <h3>Your kits</h3>
          </div>
          <button className="modal-x" onClick={onClose} aria-label="Close">×</button>
        </div>
        <div className="modal-body">
          {lines.length === 0 ? (
            <div className="ok-banner">Your cart is empty. Add a kit from the collection.</div>
          ) : (
            <React.Fragment>
              {lines.map(({ it, p }) => (
                <div key={p.id} style={{ display: "flex", gap: 12, alignItems: "center", padding: "10px 0", borderBottom: "1px solid var(--line)" }}>
                  <img src={p.img} alt="" style={{ width: 54, height: 54, objectFit: "cover", borderRadius: 6, border: "1px solid var(--line)" }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: 600, fontSize: 14 }}>{p.title}</div>
                    <div style={{ fontSize: 12, color: "var(--ink-faint)" }}>{p.maker} · {p.scale} · {chargeAmount(p.price, currency).display}</div>
                  </div>
                  <div className="stepper" style={{ display: "inline-flex", alignItems: "center", border: "1px solid var(--line)", borderRadius: 6 }}>
                    <button onClick={() => cart.setQty(p.id, it.qty - 1)} style={{ width: 30, height: 32, border: 0, background: "transparent", cursor: "pointer", fontSize: 16 }}>−</button>
                    <b style={{ minWidth: 22, textAlign: "center" }}>{it.qty}</b>
                    <button onClick={() => cart.setQty(p.id, it.qty + 1)} style={{ width: 30, height: 32, border: 0, background: "transparent", cursor: "pointer", fontSize: 16 }}>+</button>
                  </div>
                  <button onClick={() => cart.remove(p.id)} aria-label="Remove" style={{ border: 0, background: "transparent", cursor: "pointer", color: "var(--ink-faint)", fontSize: 18 }}>×</button>
                </div>
              ))}

              <div className="field" style={{ marginTop: 16 }}>
                <label>Ship to</label>
                <select value={zone} onChange={(e) => setZone(Number(e.target.value))}>
                  {zones.map((z) => <option key={z.id} value={z.id}>{z.name} — {z.blurb}</option>)}
                </select>
              </div>

              <div style={{ marginTop: 14, fontSize: 14.5 }}>
                <div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0" }}><span>Subtotal ({kits} kit{kits > 1 ? "s" : ""})</span><b>{chargeAmount(subtotalJPY, currency).display}</b></div>
                <div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0" }}><span>Shipping (tracked)</span><b>{chargeAmount(shipJPY, currency).display}</b></div>
                <div style={{ display: "flex", justifyContent: "space-between", padding: "8px 0", borderTop: "1px solid var(--line)", marginTop: 6, fontSize: 17 }}><span>Total</span><b style={{ color: "var(--accent)" }}>{chargeAmount(totalJPY, currency).display}</b></div>
              </div>

              {err && <div className="form-note" style={{ color: "var(--accent)", marginTop: 8 }}>{err}</div>}
              {busy && <div className="form-note" style={{ marginTop: 8 }}>Finalising payment…</div>}

              {showCheckout ? (
                <div style={{ marginTop: 16 }}>
                  <div ref={ppRef}></div>
                  <p className="form-note" style={{ marginTop: 10 }}>
                    Charged in {currency} via PayPal{PAYPAL.mode !== "live" ? " (SANDBOX / test mode)" : ""}. Tracked worldwide shipping included.
                  </p>
                </div>
              ) : (
                <div style={{ marginTop: 16 }}>
                  <button className="btn btn--red" style={{ width: "100%" }} onClick={() => { onClose(); onInquire(); }}>
                    Checkout opening soon — send as enquiry →
                  </button>
                  <p className="form-note" style={{ marginTop: 10 }}>
                    Card checkout is being finalised. For now we confirm your order and total by email — we'll reply fast.
                  </p>
                </div>
              )}
            </React.Fragment>
          )}
        </div>
      </div>
    </div>
  );
}

// ============ APP ============
function App() {
  // modal = { mode, product } | null
  const [modal, setModal] = useState(null);
  const [sent, setSent] = useState(null);

  // currency — USD by default; auto-localise by IP once if there's no saved choice
  const hadSavedCur = useRef(false);
  const [currency, setCurrency] = useState(() => {
    try { const s = localStorage.getItem("tsa_cur"); if (s && CURRENCIES[s]) { hadSavedCur.current = true; return s; } } catch (e) {}
    return "USD";
  });
  activeCur = currency; // make selection available to module-level money()
  useEffect(() => { try { localStorage.setItem("tsa_cur", currency); } catch (e) {} }, [currency]);
  useEffect(() => {
    if (hadSavedCur.current) return;
    fetch("https://ipapi.co/json/").then((r) => r.json()).then((d) => {
      const cur = countryToCur(d && d.country_code);
      if (cur && CURRENCIES[cur]) setCurrency(cur);
    }).catch(() => {});
  }, []);

  // pull live FX rates once on load, then re-render so converted prices update
  const [, bumpRates] = useState(0);
  useEffect(() => { loadRates().then((ok) => { if (ok) bumpRates((n) => n + 1); }); }, []);

  // load products.json + shipping + paypal config before showing the store
  const [ready, setReady] = useState(false);
  useEffect(() => { Promise.all([loadProducts(), loadShipping(), loadPaypal()]).then(() => setReady(true)); }, []);

  const [cartOpen, setCartOpen] = useState(false);

  const openBuy      = (p) => setModal({ mode: "buy",      product: p });
  const openInquiry  = (p) => setModal({ mode: "inquiry",  product: p });
  const openWaitlist = (p) => setModal(p ? { mode: "waitlist", product: p } : { mode: "general", product: null });
  const openGeneral  = () => setModal({ mode: "general", product: null });
  const addToCart    = (p) => { cart.add(p.id); setCartOpen(true); };

  return (
    <React.Fragment>
      <NoticeBar />
      <TopBar />
      <Header onContact={openGeneral} currency={currency} setCurrency={setCurrency} onCart={() => setCartOpen(true)} />
      <Hero ready={ready} />
      <Stock onBuy={addToCart} onWaitlist={openWaitlist} onInquiry={addToCart} ready={ready} />
      <Features />
      <Sourcing onRequest={(r) => setSent(r)} />
      <Footer />

      {cartOpen && <CartDrawer currency={currency} onClose={() => setCartOpen(false)} onInquire={openGeneral} onDone={(r) => { setCartOpen(false); setSent(r); }} />}

      {modal && (
        <TxModal
          mode={modal.mode}
          product={modal.product}
          onClose={() => setModal(null)}
          onSent={(r) => { setModal(null); setSent(r); }}
        />
      )}
      {sent && <SentBanner label={sent.label} kind={sent.kind} onClose={() => setSent(null)} />}
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
