
/* ===== ui.jsx ===== */
/* global React */
const { useState, useEffect, useRef, useCallback } = React;

/* ---------------- hooks ---------------- */

// reveal-on-scroll
function useReveal() {
  useEffect(() => {
    const els = document.querySelectorAll(".reveal:not(.in)");
    if (!("IntersectionObserver" in window)) {
      els.forEach((e) => e.classList.add("in"));
      return;
    }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((en) => {
          if (en.isIntersecting) {
            en.target.classList.add("in");
            io.unobserve(en.target);
          }
        });
      },
      { threshold: 0.14, rootMargin: "0px 0px -8% 0px" }
    );
    els.forEach((e) => io.observe(e));
    return () => io.disconnect();
  });
}

// count-up when in view
function useCountUp(end, { duration = 1600, decimals = 0 } = {}) {
  const ref = useRef(null);
  const [val, setVal] = useState(0);
  const done = useRef(false);
  useEffect(() => {
    const node = ref.current;
    if (!node) return;
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((en) => {
          if (en.isIntersecting && !done.current) {
            done.current = true;
            const start = performance.now();
            const tick = (now) => {
              const p = Math.min((now - start) / duration, 1);
              const eased = 1 - Math.pow(1 - p, 3);
              setVal(end * eased);
              if (p < 1) requestAnimationFrame(tick);
            };
            requestAnimationFrame(tick);
          }
        });
      },
      { threshold: 0.4 }
    );
    io.observe(node);
    return () => io.disconnect();
  }, [end, duration]);
  return [ref, decimals ? val.toFixed(decimals) : Math.round(val)];
}

/* ---------------- icons ---------------- */
const Arrow = ({ className = "" }) => (
  <svg className={"arr " + className} width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" aria-hidden="true">
    <path d="M5 12h14M13 6l6 6-6 6" strokeLinecap="round" strokeLinejoin="round" />
  </svg>
);

// AIM Global emblem — abstract meridian globe
const Emblem = ({ size = 30, color = "var(--accent)" }) => (
  <svg width={size} height={size} viewBox="0 0 40 40" fill="none" aria-hidden="true">
    <circle cx="20" cy="20" r="18" stroke={color} strokeWidth="1.1" />
    <ellipse cx="20" cy="20" rx="8" ry="18" stroke={color} strokeWidth="1.1" />
    <path d="M2.6 14.5h34.8M2.6 25.5h34.8" stroke={color} strokeWidth="1.1" />
    <circle cx="20" cy="20" r="2.4" fill={color} />
  </svg>
);

function Logo({ variant = "dark", size = 30 }) {
  const wordColor = variant === "light" ? "var(--cream-100)" : "var(--emerald-800)";
  return (
    <a href="#top" className="logo" aria-label="AIM Global home"
       style={{ display: "inline-flex", alignItems: "center", gap: 13 }}>
      <Emblem size={size} />
      <span style={{ display: "flex", flexDirection: "column", lineHeight: 1 }}>
        <span style={{ fontFamily: "var(--serif)", fontSize: size * 0.82, fontWeight: 500, letterSpacing: "0.01em", color: wordColor, whiteSpace: "nowrap" }}>
          AIM<span style={{ fontStyle: "italic", fontWeight: 400 }}> Global</span>
        </span>
        <span style={{ fontFamily: "var(--sans)", fontSize: size * 0.26, fontWeight: 600, letterSpacing: "0.34em", textTransform: "uppercase", marginTop: 4, color: variant === "light" ? "rgba(246,243,234,0.6)" : "var(--ink-400)" }}>
          Citizenship Advisory
        </span>
      </span>
    </a>
  );
}

/* ---------------- shared bits ---------------- */
const Eyebrow = ({ children, center, style }) => (
  <span className={"eyebrow" + (center ? " center" : "")} style={style}>{children}</span>
);

function Button({ children, variant = "", onClick, href, arrow = true, className = "" }) {
  const cls = "btn " + (variant ? "btn--" + variant : "") + " " + className;
  const inner = (
    <>
      <span>{children}</span>
      {arrow && <Arrow />}
    </>
  );
  if (href) return <a className={cls} href={href} onClick={onClick}>{inner}</a>;
  return <button className={cls} onClick={onClick}>{inner}</button>;
}

/* ---------------- audience toggle ---------------- */
function AudienceToggle({ value, onChange, theme = "dark" }) {
  const light = theme === "light";
  const opts = [
    { id: "private", label: "Private Clients" },
    { id: "advisor", label: "Advisors & Partners" },
  ];
  return (
    <div className="aud-toggle" data-theme={theme} role="tablist" aria-label="Audience">
      <span className="aud-pill" style={{ transform: value === "advisor" ? "translateX(100%)" : "translateX(0)" }} />
      {opts.map((o) => (
        <button
          key={o.id}
          role="tab"
          aria-selected={value === o.id}
          className={"aud-opt" + (value === o.id ? " active" : "")}
          onClick={() => onChange(o.id)}
        >
          {o.label}
        </button>
      ))}
    </div>
  );
}

/* ---------------- nav ---------------- */
function Nav({ audience, setAudience, onQuiz }) {
  const [scrolled, setScrolled] = useState(false);
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const onScroll = () => setScrolled(window.scrollY > 40);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  const links = [
    ["Programs", "#programs"],
    ["Explore", "#explorer"],
    ["Jurisdictions", "#jurisdictions"],
    ["Approach", "#approach"],
    ["Insights", "#insights"],
  ];
  return (
    <header className={"nav" + (scrolled ? " nav--solid" : "")}>
      <div className="nav__inner wrap">
        <Logo variant={scrolled ? "dark" : "light"} size={30} />
        <nav className="nav__links">
          {links.map(([l, h]) => (
            <a key={l} href={h} className="nav__link">{l}</a>
          ))}
        </nav>
        <div className="nav__right">
          <div className="nav__toggle-wrap">
            <AudienceToggle value={audience} onChange={setAudience} theme={scrolled ? "light" : "dark"} />
          </div>
          <Button variant={scrolled ? "" : "gold"} onClick={onQuiz} className="nav__cta">Eligibility Check</Button>
          <button className="nav__burger" aria-label="Menu" onClick={() => setOpen((o) => !o)}>
            <span /><span /><span />
          </button>
        </div>
      </div>
      {open && (
        <div className="nav__mobile">
          {links.map(([l, h]) => (
            <a key={l} href={h} onClick={() => setOpen(false)}>{l}</a>
          ))}
          <div style={{ marginTop: 18 }}>
            <AudienceToggle value={audience} onChange={setAudience} theme="light" />
          </div>
        </div>
      )}
    </header>
  );
}

Object.assign(window, {
  useReveal, useCountUp, Arrow, Emblem, Logo, Eyebrow, Button, AudienceToggle, Nav,
});


/* ===== data.jsx ===== */
/* Shared content — audience-keyed copy + program/jurisdiction data */

const CONTENT = {
  private: {
    navLabel: "Private Clients",
    hero: {
      eyebrow: "Global Citizenship & Residency",
      head: ["The world,", "on your", "terms."],
      headItalic: 2, // index of line rendered in italic serif
      lede:
        "AIM Global advises individuals and families on second citizenship, residency by investment, and global mobility — quietly, and with absolute discretion. One relationship. Every jurisdiction.",
      primary: "Begin your eligibility check",
      secondary: "Speak with an advisor",
      slot: { id: "hero-private", placeholder: "Editorial: family at a coastal villa, golden hour" },
      caption: "Algarve, Portugal — Residence strategy",
    },
    stats: [
      { value: 38, suffix: "+", label: "Jurisdictions advised" },
      { value: 4200, suffix: "", label: "Families served", fmt: "comma" },
      { value: 98, suffix: "%", label: "Application approval rate" },
      { value: 22, suffix: "yrs", label: "Of combined practice" },
    ],
    programsIntro:
      "Three routes to the same outcome — optionality. We match your circumstances to the program that fits, not the other way around.",
    ctaBand: {
      head: "A second passport begins with a single conversation.",
      sub: "Take the two-minute eligibility check. We follow with a confidential, no-obligation assessment from a dedicated advisor.",
      btn: "Start eligibility check",
    },
  },
  advisor: {
    navLabel: "Advisors & Partners",
    hero: {
      eyebrow: "Partner Practice · For Advisors",
      head: ["Mobility", "your clients", "trust."],
      headItalic: 0,
      lede:
        "AIM Global is the residency-and-citizenship desk behind family offices, private banks, and wealth advisors. White-glove execution, co-branded delivery, and transparent referral economics — your relationship stays yours.",
      primary: "Explore the partner program",
      secondary: "Request the partner deck",
      slot: { id: "hero-advisor", placeholder: "Editorial: advisor and client in a private office" },
      caption: "Co-advised mandate — UAE & Portugal",
    },
    stats: [
      { value: 320, suffix: "+", label: "Active partner firms" },
      { value: 60, suffix: "+", label: "Countries covered" },
      { value: 72, suffix: "hrs", label: "Avg. mandate turnaround" },
      { value: 100, suffix: "%", label: "Relationship retained by you" },
    ],
    programsIntro:
      "A single desk for every cross-border mobility question your clients raise — delivered under your brand, on your timeline.",
    ctaBand: {
      head: "Give your clients the world. Keep the relationship.",
      sub: "Run an eligibility check on a live client scenario, or request the full partner pack with commercials, SLAs, and co-branded materials.",
      btn: "Run a client scenario",
    },
  },
};

const PROGRAMS = [
  {
    key: "citizenship",
    num: "01",
    title: "Citizenship by Investment",
    tag: "Second passport",
    horizon: "3–8 months",
    from: "From $200,000",
    desc:
      "Full citizenship and a second passport through government-approved investment. Visa-free access, generational security, and a true plan B.",
    points: ["Visa-free to 140+ destinations", "Heritable, lifelong status", "No residency requirement"],
    slot: { id: "prog-citizenship", placeholder: "Caribbean coastline / passport detail" },
  },
  {
    key: "residency",
    num: "02",
    title: "Residency & Golden Visas",
    tag: "Right to live",
    horizon: "2–6 months",
    from: "From €250,000",
    desc:
      "Residence rights in the world's most desirable jurisdictions through real estate, funds, or capital transfer — a path to citizenship over time.",
    points: ["Settle, work and study freely", "Route to permanent residence", "Family included as standard"],
    slot: { id: "prog-residency", placeholder: "European architecture / golden hour terrace" },
  },
  {
    key: "nomad",
    num: "03",
    title: "Digital Nomad Visas",
    tag: "Live anywhere",
    horizon: "4–10 weeks",
    from: "From $0 capital",
    desc:
      "Live legally in a new country while you work remotely. The lightest-touch route to testing a jurisdiction before you commit.",
    points: ["Income-based, low threshold", "Favourable tax treatment", "Renewable & flexible"],
    slot: { id: "prog-nomad", placeholder: "Laptop on a sunlit balcony, sea view" },
  },
];

// Jurisdictions for the showcase + explorer
const JURISDICTIONS = [
  { name: "Portugal", region: "Europe", program: "Golden / D7 Residency", mobility: 188, route: "residency", from: "€250k", goals: ["mobility", "lifestyle", "education", "eu"], timeline: "fast", budget: "mid", slot: "jur-portugal" },
  { name: "Malta", region: "Europe", program: "Citizenship & Residency", mobility: 190, route: "citizenship", from: "€690k", goals: ["mobility", "eu", "business"], timeline: "med", budget: "high", slot: "jur-malta" },
  { name: "Greece", region: "Europe", program: "Golden Visa", mobility: 188, route: "residency", from: "€250k", goals: ["lifestyle", "eu", "mobility"], timeline: "fast", budget: "mid", slot: "jur-greece" },
  { name: "UAE", region: "Middle East", program: "Golden Visa", mobility: 185, route: "residency", from: "$205k", goals: ["business", "tax", "lifestyle"], timeline: "fast", budget: "mid", slot: "jur-uae" },
  { name: "Caribbean", region: "Americas", program: "Citizenship by Investment", mobility: 156, route: "citizenship", from: "$200k", goals: ["mobility", "tax", "speed"], timeline: "fast", budget: "low", slot: "jur-caribbean" },
  { name: "Spain", region: "Europe", program: "Non-Lucrative / Nomad", mobility: 194, route: "nomad", from: "Income", goals: ["lifestyle", "eu", "remote"], timeline: "med", budget: "low", slot: "jur-spain" },
  { name: "Singapore", region: "Asia", program: "Global Investor Prog.", mobility: 195, route: "residency", from: "$2.5m", goals: ["business", "education", "tax"], timeline: "slow", budget: "high", slot: "jur-singapore" },
  { name: "Cyprus", region: "Europe", program: "Permanent Residency", mobility: 178, route: "residency", from: "€300k", goals: ["eu", "tax", "lifestyle"], timeline: "med", budget: "mid", slot: "jur-cyprus" },
];

const unsplash = (id, w = 1400, h = 1800) =>
  `https://images.unsplash.com/photo-${id}?auto=format&fit=crop&w=${w}&h=${h}&q=82`;

const IMAGE_SLOTS = {
  "hero-private": unsplash("1763656260388-2fa02fe953f1", 1400, 1750),
  "hero-advisor": unsplash("1556761175-b413da4baf72", 1400, 1750),
  "prog-citizenship": unsplash("1450101499163-c8848c66ca85", 1000, 760),
  "prog-residency": unsplash("1763656260396-3e60dfa57366", 1000, 760),
  "prog-nomad": unsplash("1497366754035-f200968a6e72", 1000, 760),
  "jur-portugal": unsplash("1749511380076-38ff3b3a7d6e", 900, 1200),
  "jur-malta": unsplash("1523906834658-6e24ef2386f9", 900, 1200),
  "jur-greece": unsplash("1533105079780-92b9be482077", 900, 1200),
  "jur-uae": unsplash("1512453979798-5ea266f8880c", 900, 1200),
  "jur-caribbean": unsplash("1507525428034-b723cf961d3e", 900, 1200),
  "jur-spain": unsplash("1543783207-ec64e4d95325", 900, 1200),
  "jur-singapore": unsplash("1525625293386-3f8f99389edd", 900, 1200),
  "jur-cyprus": unsplash("1507525428034-b723cf961d3e", 900, 1200),
  "quote-private": unsplash("1494790108377-be9c29b29330", 240, 240),
  "quote-advisor": unsplash("1507003211169-0a1dd7228f2d", 240, 240),
};

const IMAGE_POSITIONS = {
  "hero-private": "52% 50%",
  "hero-advisor": "50% 45%",
  "prog-citizenship": "52% 55%",
  "prog-residency": "50% 52%",
  "prog-nomad": "48% 52%",
  "jur-portugal": "54% 50%",
  "jur-greece": "50% 54%",
  "jur-uae": "52% 48%",
  "jur-caribbean": "50% 54%",
  "jur-spain": "50% 48%",
  "jur-singapore": "50% 46%",
};

function imageSrc(id) { return IMAGE_SLOTS[id] || ""; }
function imagePosition(id) { return IMAGE_POSITIONS[id] || "50% 50%"; }

// Explorer taxonomy
const EXPLORER_GOALS = [
  { id: "mobility", label: "Visa-free travel" },
  { id: "tax", label: "Tax optimisation" },
  { id: "lifestyle", label: "Lifestyle & family" },
  { id: "business", label: "Business & markets" },
  { id: "education", label: "Education access" },
  { id: "eu", label: "EU access" },
];
const EXPLORER_BUDGET = [
  { id: "low", label: "Under $300k" },
  { id: "mid", label: "$300k – $1m" },
  { id: "high", label: "$1m +" },
];
const EXPLORER_TIMELINE = [
  { id: "fast", label: "Under 6 months" },
  { id: "med", label: "6 – 18 months" },
  { id: "slow", label: "Flexible" },
];

const TESTIMONIALS = {
  private: {
    quote:
      "They treated a deeply personal decision with the seriousness it deserved. Two passports later, my family has options we never imagined — and one advisor who knew every detail.",
    name: "A. Reinhardt",
    role: "Founder · Private client, Zurich",
  },
  advisor: {
    quote:
      "AIM became our mobility desk overnight. Clients see one seamless service; we keep the relationship and the economics. It is the cleanest partnership in this market.",
    name: "M. Okonkwo",
    role: "Partner · Multi-family office, London",
  },
};

const PARTNERS = ["FORBES", "FINANCIAL TIMES", "BLOOMBERG", "ROBB REPORT", "MONOCLE"];

Object.assign(window, {
  CONTENT, PROGRAMS, JURISDICTIONS, EXPLORER_GOALS, EXPLORER_BUDGET, EXPLORER_TIMELINE, TESTIMONIALS, PARTNERS,
  IMAGE_SLOTS, imageSrc, imagePosition,
});


/* ===== hero.jsx ===== */
/* Hero, trust strip, animated stats */
const { useState: useStateH, useEffect: useEffectH } = React;

function Hero({ audience, onQuiz }) {
  const c = CONTENT[audience].hero;
  const [shown, setShown] = useStateH(false);
  useEffectH(() => {
    setShown(false);
    const t = setTimeout(() => setShown(true), 30);
    return () => clearTimeout(t);
  }, [audience]);

  return (
    <section className="hero surface-emerald-900 grain" id="top">
      <div className="hero__bg-orb" aria-hidden="true" />
      <div className="wrap hero__grid">
        <div className="hero__copy">
          <div className={"hero__eyebrow " + (shown ? "is-in" : "")}>
            <Eyebrow style={{ color: "var(--accent)" }}>{c.eyebrow}</Eyebrow>
          </div>
          <h1 className="display hero__head" aria-label={c.head.join(" ")}>
            {c.head.map((line, i) => (
              <span className="hero__line-mask" key={i}>
                <span
                  className={"hero__line" + (shown ? " is-in" : "") + (i === c.headItalic ? " serif-accent" : "")}
                  style={{ transitionDelay: (0.15 + i * 0.11) + "s" }}
                >
                  {line}
                </span>
              </span>
            ))}
          </h1>
          <p className={"lede hero__lede" + (shown ? " is-in" : "")} style={{ color: "rgba(246,243,234,0.78)" }}>
            {c.lede}
          </p>
          <div className={"hero__cta" + (shown ? " is-in" : "")}>
            <Button variant="gold" onClick={onQuiz}>{c.primary}</Button>
            <a href="#programs" className="link-arrow on-dark">{c.secondary} <Arrow /></a>
          </div>
        </div>

        <div className={"hero__media" + (shown ? " is-in" : "")}>
          <div className="hero__media-frame grain">
            <image-slot
              id={c.slot.id}
              shape="rect"
              fit="cover"
              src={imageSrc(c.slot.id)}
              position={imagePosition(c.slot.id)}
              placeholder={c.slot.placeholder}
              style={{ width: "100%", height: "100%" }}
            ></image-slot>
          </div>
          <div className="hero__caption">
            <span className="hero__caption-dot" />
            {c.caption}
          </div>
        </div>
      </div>

      <StatStrip audience={audience} />
    </section>
  );
}

function StatNumber({ value, suffix, fmt }) {
  const [ref, val] = useCountUp(value, { duration: 1700 });
  const display = fmt === "comma" ? Number(val).toLocaleString() : val;
  return (
    <span ref={ref} className="stat__num">
      {display}
      <span className="stat__suffix">{suffix}</span>
    </span>
  );
}

function StatStrip({ audience }) {
  const stats = CONTENT[audience].stats;
  return (
    <div className="wrap">
      <div className="stat-strip">
        {stats.map((s, i) => (
          <div className="stat" key={audience + i}>
            <StatNumber value={s.value} suffix={s.suffix} fmt={s.fmt} />
            <span className="stat__label">{s.label}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

function TrustStrip() {
  return (
    <section className="trust">
      <div className="wrap trust__inner">
        <span className="trust__label">As featured in</span>
        <div className="trust__logos">
          {PARTNERS.map((p) => (
            <span className="trust__logo" key={p}>{p}</span>
          ))}
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { Hero, StatStrip, TrustStrip });


/* ===== programs.jsx ===== */
/* Programs + Approach */

function Programs({ audience, onQuiz }) {
  const intro = CONTENT[audience].programsIntro;
  return (
    <section className="section programs" id="programs">
      <div className="wrap">
        <div className="programs__head reveal">
          <div>
            <Eyebrow>The Routes</Eyebrow>
            <h2 className="h-section" style={{ marginTop: 22, maxWidth: 720 }}>
              Three doors to the same <span className="serif-accent">freedom</span>.
            </h2>
          </div>
          <p className="lede" style={{ maxWidth: 380 }}>{intro}</p>
        </div>

        <div className="programs__grid">
          {PROGRAMS.map((p, i) => (
            <article className={"prog reveal d" + (i + 1)} key={p.key}>
              <div className="prog__media grain">
                <image-slot id={p.slot.id} shape="rect" fit="cover" placeholder={p.slot.placeholder}
                  src={imageSrc(p.slot.id)}
                  position={imagePosition(p.slot.id)}
                  style={{ width: "100%", height: "100%" }}></image-slot>
                <span className="prog__num">{p.num}</span>
              </div>
              <div className="prog__body">
                <div className="prog__tag">{p.tag}</div>
                <h3 className="h-card">{p.title}</h3>
                <p className="prog__desc">{p.desc}</p>
                <ul className="prog__points">
                  {p.points.map((pt) => (
                    <li key={pt}><span className="prog__check" />{pt}</li>
                  ))}
                </ul>
                <div className="prog__meta">
                  <div><span className="prog__meta-k">Timeline</span><span className="prog__meta-v">{p.horizon}</span></div>
                  <div><span className="prog__meta-k">Investment</span><span className="prog__meta-v">{p.from}</span></div>
                </div>
                <a href="#explorer" className="link-arrow prog__link">Explore options <Arrow /></a>
              </div>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

const APPROACH_STEPS = [
  { n: "01", t: "Discovery", d: "A confidential conversation about your goals, family, tax posture and timeline — no obligation, ever." },
  { n: "02", t: "Strategy", d: "We model the routes that fit and present a clear, side-by-side recommendation with real costs and timelines." },
  { n: "03", t: "Execution", d: "A dedicated advisor manages filings, due diligence and counsel end-to-end across every jurisdiction." },
  { n: "04", t: "Stewardship", d: "Renewals, second routes, and the wider plan for your wealth and mobility — for the long term." },
];

function Approach({ audience }) {
  return (
    <section className="section approach surface-cream" id="approach">
      <div className="wrap approach__grid">
        <div className="approach__intro reveal">
          <Eyebrow>The AIM Approach</Eyebrow>
          <h2 className="h-section" style={{ marginTop: 22 }}>
            An advisory practice,<br /><span className="serif-accent">not a brokerage.</span>
          </h2>
          <p className="lede" style={{ marginTop: 26, color: "var(--ink-700)" }}>
            Most firms sell a program. We hold a relationship. Every recommendation is independent,
            every fee is transparent, and every client is served by one advisor who knows the whole picture —
            from a first passport to the structures that protect a family for generations.
          </p>
          <div className="approach__signals">
            <div className="approach__signal"><span className="approach__signal-k">Independent</span><span>No allegiance to any single jurisdiction.</span></div>
            <div className="approach__signal"><span className="approach__signal-k">Discreet</span><span>Bank-grade confidentiality, end to end.</span></div>
          </div>
        </div>
        <ol className="approach__steps">
          {APPROACH_STEPS.map((s, i) => (
            <li className={"approach__step reveal d" + (i + 1)} key={s.n}>
              <span className="approach__step-n">{s.n}</span>
              <div>
                <h3 className="approach__step-t">{s.t}</h3>
                <p className="approach__step-d">{s.d}</p>
              </div>
            </li>
          ))}
        </ol>
      </div>
    </section>
  );
}

Object.assign(window, { Programs, Approach });


/* ===== explorer.jsx ===== */
/* Interactive eligibility/program explorer + Jurisdictions showcase */
const { useState: useStateE, useMemo: useMemoE } = React;

function scoreJur(j, goals, budget, timeline) {
  let s = 0;
  const overlap = goals.filter((g) => j.goals.includes(g)).length;
  s += overlap * 3;
  if (budget && j.budget === budget) s += 2;
  if (budget && budgetRank(j.budget) <= budgetRank(budget)) s += 1;
  if (timeline && j.timeline === timeline) s += 2;
  return s;
}
function budgetRank(b) { return { low: 1, mid: 2, high: 3 }[b] || 2; }

function Chip({ active, onClick, children }) {
  return (
    <button className={"chip" + (active ? " chip--on" : "")} onClick={onClick} type="button">
      <span className="chip__box" />{children}
    </button>
  );
}

function Explorer({ onQuiz }) {
  const [goals, setGoals] = useStateE(["mobility", "eu"]);
  const [budget, setBudget] = useStateE("mid");
  const [timeline, setTimeline] = useStateE("fast");

  const toggleGoal = (id) =>
    setGoals((g) => (g.includes(id) ? g.filter((x) => x !== id) : [...g, id]));

  const ranked = useMemoE(() => {
    return JURISDICTIONS
      .map((j) => ({ ...j, _s: scoreJur(j, goals, budget, timeline) }))
      .sort((a, b) => b._s - a._s);
  }, [goals, budget, timeline]);

  const matches = ranked.filter((j) => j._s >= 4);
  const top = (matches.length ? matches : ranked).slice(0, 5);
  const routeLabel = { citizenship: "Citizenship", residency: "Residency", nomad: "Nomad Visa" };

  return (
    <section className="section explorer surface-dark grain" id="explorer">
      <div className="wrap">
        <div className="explorer__head reveal">
          <Eyebrow style={{ color: "var(--accent)" }}>Eligibility Explorer</Eyebrow>
          <h2 className="h-section" style={{ marginTop: 22, color: "var(--cream-100)", maxWidth: 760 }}>
            Tell us what matters. <span className="serif-accent" style={{ color: "var(--gold-300)" }}>See where the world opens up.</span>
          </h2>
        </div>

        <div className="explorer__panel reveal d1">
          <div className="explorer__controls">
            <div className="ctrl">
              <span className="ctrl__label">What are you optimising for?</span>
              <div className="chips">
                {EXPLORER_GOALS.map((g) => (
                  <Chip key={g.id} active={goals.includes(g.id)} onClick={() => toggleGoal(g.id)}>{g.label}</Chip>
                ))}
              </div>
            </div>
            <div className="ctrl ctrl--row">
              <div className="ctrl__col">
                <span className="ctrl__label">Budget</span>
                <div className="seg">
                  {EXPLORER_BUDGET.map((b) => (
                    <button key={b.id} type="button"
                      className={"seg__opt" + (budget === b.id ? " on" : "")}
                      onClick={() => setBudget(b.id)}>{b.label}</button>
                  ))}
                </div>
              </div>
              <div className="ctrl__col">
                <span className="ctrl__label">Timeline</span>
                <div className="seg">
                  {EXPLORER_TIMELINE.map((t) => (
                    <button key={t.id} type="button"
                      className={"seg__opt" + (timeline === t.id ? " on" : "")}
                      onClick={() => setTimeline(t.id)}>{t.label}</button>
                  ))}
                </div>
              </div>
            </div>
            <div className="explorer__cta">
              <span className="explorer__count">
                <strong>{matches.length || ranked.length}</strong> {matches.length ? "strong matches" : "routes to explore"}
              </span>
              <Button variant="gold" onClick={onQuiz}>Build my personalised plan</Button>
            </div>
          </div>

          <div className="explorer__results">
            <div className="results__head">
              <span>Jurisdiction</span>
              <span>Route</span>
              <span className="results__hide-sm">Passport rank</span>
              <span>From</span>
            </div>
            {top.map((j, i) => {
              const fit = Math.min(100, 40 + j._s * 9);
              return (
                <div className="result" key={j.name} style={{ transitionDelay: i * 40 + "ms" }}>
                  <div className="result__name">
                    <span className="result__rank">{String(i + 1).padStart(2, "0")}</span>
                    <span>
                      <span className="result__country">{j.name}</span>
                      <span className="result__region">{j.region} · {j.program}</span>
                    </span>
                  </div>
                  <div className="result__route"><span className={"route-pill route-pill--" + j.route}>{routeLabel[j.route]}</span></div>
                  <div className="result__mob results__hide-sm">
                    <span className="result__mob-val">{j.mobility}</span>
                    <span className="result__bar"><span style={{ width: (j.mobility / 200 * 100) + "%" }} /></span>
                  </div>
                  <div className="result__from">{j.from}</div>
                  <div className="result__fit" title={fit + "% fit"}><span style={{ height: fit + "%" }} /></div>
                </div>
              );
            })}
          </div>
        </div>
      </div>
    </section>
  );
}

/* ---------------- Jurisdictions showcase ---------------- */
const FEATURED = ["Portugal", "UAE", "Caribbean", "Greece", "Singapore", "Spain"];

function Jurisdictions() {
  const items = FEATURED.map((n) => JURISDICTIONS.find((j) => j.name === n)).filter(Boolean);
  return (
    <section className="section jur" id="jurisdictions">
      <div className="wrap">
        <div className="jur__head reveal">
          <div>
            <Eyebrow>Where We Operate</Eyebrow>
            <h2 className="h-section" style={{ marginTop: 22 }}>
              A curated atlas of <span className="serif-accent">opportunity</span>.
            </h2>
          </div>
          <p className="lede muted" style={{ maxWidth: 360 }}>
            Thirty-eight jurisdictions, continuously vetted for stability, mobility and value. A selection of where our clients are moving now.
          </p>
        </div>
        <div className="jur__grid">
          {items.map((j, i) => (
            <article className={"jur-card reveal d" + ((i % 3) + 1)} key={j.name}>
              <div className="jur-card__media grain">
                <image-slot id={j.slot} shape="rect" fit="cover" placeholder={j.name + " — editorial landscape"}
                  src={imageSrc(j.slot)}
                  position={imagePosition(j.slot)}
                  style={{ width: "100%", height: "100%" }}></image-slot>
              </div>
              <div className="jur-card__overlay">
                <div className="jur-card__top">
                  <span className="jur-card__region">{j.region}</span>
                  <span className="jur-card__mob">#{j.mobility} <span>mobility</span></span>
                </div>
                <div className="jur-card__bottom">
                  <h3 className="jur-card__name">{j.name}</h3>
                  <span className="jur-card__prog">{j.program} · from {j.from}</span>
                </div>
              </div>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

Object.assign(window, { Explorer, Jurisdictions });


/* ===== story.jsx ===== */
/* Testimonial, CTA band, Insights, Footer */

function Testimonial({ audience }) {
  const t = TESTIMONIALS[audience];
  return (
    <section className="section testimonial surface-emerald-900 grain">
      <div className="wrap testimonial__inner">
        <div className="testimonial__mark">&ldquo;</div>
        <blockquote className="testimonial__quote reveal">{t.quote}</blockquote>
        <div className="testimonial__by reveal d1">
          <image-slot id={"quote-" + audience} shape="circle" placeholder="Portrait"
            src={imageSrc("quote-" + audience)}
            position={imagePosition("quote-" + audience)}
            style={{ width: 56, height: 56, flexShrink: 0 }}></image-slot>
          <div>
            <div className="testimonial__name">{t.name}</div>
            <div className="testimonial__role">{t.role}</div>
          </div>
        </div>
      </div>
    </section>
  );
}

function CtaBand({ audience, onQuiz }) {
  const c = CONTENT[audience].ctaBand;
  return (
    <section className="section cta-band">
      <div className="wrap cta-band__inner reveal">
        <div className="cta-band__orb" aria-hidden="true" />
        <Eyebrow center style={{ color: "var(--accent)", justifyContent: "center" }}>Begin</Eyebrow>
        <h2 className="h-section cta-band__head">{c.head}</h2>
        <p className="lede cta-band__sub">{c.sub}</p>
        <div className="cta-band__btns">
          <Button variant="gold" onClick={onQuiz}>{c.btn}</Button>
          <a href="#programs" className="link-arrow on-dark">View all programs <Arrow /></a>
        </div>
        <div className="cta-band__assure">
          <span><span className="dot" /> No obligation</span>
          <span><span className="dot" /> Strictly confidential</span>
          <span><span className="dot" /> Two minutes</span>
        </div>
      </div>
    </section>
  );
}

const INSIGHTS = [
  { tag: "Analysis", date: "May 2026", title: "The 2026 mobility map: where strength is shifting", read: "6 min" },
  { tag: "Jurisdiction", date: "Apr 2026", title: "Portugal after the Golden Visa reform — what still works", read: "4 min" },
  { tag: "Wealth", date: "Apr 2026", title: "Citizenship as an asset class: structuring for the long view", read: "8 min" },
];

function Insights() {
  return (
    <section className="section insights surface-cream" id="insights">
      <div className="wrap">
        <div className="insights__head reveal">
          <div>
            <Eyebrow>Intelligence</Eyebrow>
            <h2 className="h-section" style={{ marginTop: 22 }}>From the desk.</h2>
          </div>
          <a href="#insights" className="link-arrow">All insights <Arrow /></a>
        </div>
        <div className="insights__grid">
          {INSIGHTS.map((a, i) => (
            <article className={"insight reveal d" + (i + 1)} key={a.title}>
              <div className="insight__meta">
                <span className="insight__tag">{a.tag}</span>
                <span className="insight__date">{a.date}</span>
              </div>
              <h3 className="insight__title">{a.title}</h3>
              <div className="insight__foot">
                <span>{a.read} read</span>
                <Arrow />
              </div>
            </article>
          ))}
        </div>
      </div>
    </section>
  );
}

function Footer({ audience, setAudience }) {
  const cols = [
    { h: "Programs", links: ["Citizenship by Investment", "Residency & Golden Visas", "Digital Nomad Visas", "Compare all routes"] },
    { h: "Firm", links: ["Our approach", "Advisors", "Partner program", "Press & insights"] },
    { h: "Regions", links: ["Europe", "Middle East", "Caribbean", "Asia-Pacific"] },
  ];
  return (
    <footer className="footer surface-emerald-900 on-dark">
      <div className="wrap">
        <div className="footer__top">
          <div className="footer__brand">
            <Logo variant="light" size={32} />
            <p className="footer__tag">
              Considered advice on global citizenship, residency and mobility — for individuals,
              families and the advisors who serve them.
            </p>
            <div className="footer__news">
              <span className="footer__news-label">The AIM Briefing — monthly intelligence</span>
              <form className="footer__form" onSubmit={(e) => e.preventDefault()}>
                <input type="email" placeholder="Your email" aria-label="Email" required />
                <button type="submit" aria-label="Subscribe"><Arrow /></button>
              </form>
            </div>
          </div>
          <div className="footer__cols">
            {cols.map((col) => (
              <div className="footer__col" key={col.h}>
                <h4>{col.h}</h4>
                <ul>{col.links.map((l) => <li key={l}><a href="#programs">{l}</a></li>)}</ul>
              </div>
            ))}
          </div>
        </div>
        <hr className="rule" />
        <div className="footer__offices">
          {["London", "Dubai", "Singapore", "Lisbon", "Zurich"].map((o) => (
            <span key={o}>{o}</span>
          ))}
        </div>
        <hr className="rule" />
        <div className="footer__bottom">
          <span>© 2026 AIM Global. Licensed advisory. All rights reserved.</span>
          <div className="footer__legal">
            <a href="#top">Privacy</a><a href="#top">Terms</a><a href="#top">Regulatory</a>
          </div>
        </div>
      </div>
    </footer>
  );
}

Object.assign(window, { Testimonial, CtaBand, Insights, Footer });


/* ===== quiz.jsx ===== */
/* Eligibility quiz modal */
const { useState: useStateQ, useEffect: useEffectQ } = React;

const QUIZ_STEPS = [
  {
    key: "intent",
    q: "What are you looking to secure?",
    sub: "There are no wrong answers — this simply orients us.",
    type: "single",
    options: [
      { id: "citizenship", label: "A second citizenship & passport", hint: "Lifelong, heritable status" },
      { id: "residency", label: "Residency in a new country", hint: "The right to live & a path onward" },
      { id: "nomad", label: "To live & work remotely abroad", hint: "Lightest-touch route" },
      { id: "unsure", label: "I'm exploring my options", hint: "We'll guide you" },
    ],
  },
  {
    key: "goals",
    q: "What matters most?",
    sub: "Select all that apply.",
    type: "multi",
    options: [
      { id: "mobility", label: "Visa-free travel" },
      { id: "tax", label: "Tax efficiency" },
      { id: "lifestyle", label: "Lifestyle & family" },
      { id: "business", label: "Business & markets" },
      { id: "education", label: "Education access" },
      { id: "eu", label: "EU access" },
    ],
  },
  {
    key: "budget",
    q: "What investment are you considering?",
    sub: "Indicative only — many routes are flexible.",
    type: "single",
    options: [
      { id: "low", label: "Under $300,000", hint: "Incl. income-based routes" },
      { id: "mid", label: "$300,000 – $1,000,000" },
      { id: "high", label: "$1,000,000 +" },
    ],
  },
  {
    key: "timeline",
    q: "When would you like to be settled?",
    type: "single",
    options: [
      { id: "fast", label: "Within 6 months" },
      { id: "med", label: "6 – 18 months" },
      { id: "slow", label: "I'm flexible" },
    ],
  },
];

function scoreForQuiz(j, a) {
  let s = 0;
  const goals = a.goals || [];
  s += goals.filter((g) => j.goals.includes(g)).length * 3;
  if (a.intent && a.intent !== "unsure" && j.route === a.intent) s += 4;
  if (a.budget && budgetRank(j.budget) <= budgetRank(a.budget)) s += 2;
  if (a.budget === j.budget) s += 1;
  if (a.timeline && j.timeline === a.timeline) s += 2;
  return s;
}

function QuizModal({ open, onClose }) {
  const [step, setStep] = useStateQ(0);
  const [answers, setAnswers] = useStateQ({ goals: [] });
  const [contact, setContact] = useStateQ({ name: "", email: "" });
  const [submitted, setSubmitted] = useStateQ(false);
  const total = QUIZ_STEPS.length;
  const isResult = step >= total;

  useEffectQ(() => {
    document.body.style.overflow = open ? "hidden" : "";
    return () => { document.body.style.overflow = ""; };
  }, [open]);

  useEffectQ(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    if (open) window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [open, onClose]);

  if (!open) return null;

  const cur = QUIZ_STEPS[step];
  const progress = isResult ? 100 : Math.round((step / total) * 100);

  const choose = (id) => {
    if (cur.type === "multi") {
      setAnswers((a) => {
        const arr = a.goals || [];
        return { ...a, goals: arr.includes(id) ? arr.filter((x) => x !== id) : [...arr, id] };
      });
    } else {
      setAnswers((a) => ({ ...a, [cur.key]: id }));
      setTimeout(() => setStep((s) => s + 1), 240);
    }
  };

  const canNext = cur && (cur.type === "multi" ? (answers.goals || []).length > 0 : !!answers[cur.key]);

  // ranked results
  const ranked = JURISDICTIONS
    .map((j) => ({ ...j, _s: scoreForQuiz(j, answers) }))
    .sort((a, b) => b._s - a._s)
    .slice(0, 3);
  const routeLabel = { citizenship: "Citizenship", residency: "Residency", nomad: "Nomad Visa" };

  return (
    <div className="modal" role="dialog" aria-modal="true" aria-label="Eligibility check">
      <div className="modal__scrim" onClick={onClose} />
      <div className="modal__panel grain">
        <button className="modal__close" onClick={onClose} aria-label="Close">
          <svg width="20" height="20" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="1.5" fill="none"><path d="M6 6l12 12M18 6L6 18" strokeLinecap="round"/></svg>
        </button>

        <div className="modal__rail">
          <Logo variant="light" size={28} />
          <div className="modal__rail-mid">
            <Eyebrow style={{ color: "var(--accent)" }}>Eligibility Check</Eyebrow>
            <p className="modal__rail-copy">
              A confidential, two-minute orientation. We follow with a tailored assessment from a dedicated advisor — no obligation.
            </p>
          </div>
          <div className="modal__rail-foot">
            {["Confidential", "No obligation", "Advisor-reviewed"].map((x) => (
              <span key={x}><span className="dot" />{x}</span>
            ))}
          </div>
        </div>

        <div className="modal__body">
          <div className="modal__progress">
            <div className="modal__progress-bar" style={{ width: progress + "%" }} />
          </div>

          {!isResult && (
            <div className="quiz-step" key={step}>
              <span className="quiz-step__count">Question {step + 1} of {total}</span>
              <h3 className="quiz-step__q">{cur.q}</h3>
              {cur.sub && <p className="quiz-step__sub">{cur.sub}</p>}
              <div className={"quiz-opts" + (cur.type === "multi" ? " quiz-opts--multi" : "")}>
                {cur.options.map((o) => {
                  const active = cur.type === "multi" ? (answers.goals || []).includes(o.id) : answers[cur.key] === o.id;
                  return (
                    <button key={o.id} type="button" className={"quiz-opt" + (active ? " on" : "")} onClick={() => choose(o.id)}>
                      <span className="quiz-opt__main">
                        <span className="quiz-opt__label">{o.label}</span>
                        {o.hint && <span className="quiz-opt__hint">{o.hint}</span>}
                      </span>
                      <span className="quiz-opt__mark" />
                    </button>
                  );
                })}
              </div>
              <div className="quiz-nav">
                {step > 0 ? (
                  <button className="quiz-back" onClick={() => setStep((s) => s - 1)}>← Back</button>
                ) : <span />}
                {cur.type === "multi" && (
                  <Button variant="gold" onClick={() => canNext && setStep((s) => s + 1)} className={canNext ? "" : "is-disabled"}>Continue</Button>
                )}
              </div>
            </div>
          )}

          {isResult && !submitted && (
            <div className="quiz-result">
              <span className="quiz-step__count">Your indicative matches</span>
              <h3 className="quiz-step__q">Three routes worth a conversation.</h3>
              <p className="quiz-step__sub">Based on your answers. A dedicated advisor will refine these with you.</p>
              <div className="quiz-matches">
                {ranked.map((j, i) => (
                  <div className="qmatch" key={j.name}>
                    <span className="qmatch__rank">{String(i + 1).padStart(2, "0")}</span>
                    <div className="qmatch__info">
                      <span className="qmatch__name">{j.name}</span>
                      <span className="qmatch__prog">{j.program}</span>
                    </div>
                    <span className={"route-pill route-pill--" + j.route}>{routeLabel[j.route]}</span>
                    <span className="qmatch__from">{j.from}</span>
                  </div>
                ))}
              </div>
              <form className="quiz-form" onSubmit={(e) => { e.preventDefault(); setSubmitted(true); }}>
                <p className="quiz-form__label">Where shall we send your full assessment?</p>
                <div className="quiz-form__row">
                  <input required placeholder="Full name" value={contact.name} onChange={(e) => setContact({ ...contact, name: e.target.value })} />
                  <input required type="email" placeholder="Email address" value={contact.email} onChange={(e) => setContact({ ...contact, email: e.target.value })} />
                </div>
                <Button variant="gold" className="quiz-form__submit">Send my assessment</Button>
              </form>
            </div>
          )}

          {submitted && (
            <div className="quiz-done">
              <div className="quiz-done__seal"><Emblem size={48} /></div>
              <h3 className="quiz-step__q">Thank you, {contact.name.split(" ")[0] || "and welcome"}.</h3>
              <p className="quiz-step__sub" style={{ maxWidth: 380 }}>
                Your indicative assessment is on its way to {contact.email || "your inbox"}. A dedicated advisor will reach out within one business day — in complete confidence.
              </p>
              <Button variant="ghost" arrow={false} onClick={onClose}>Close</Button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { QuizModal });


/* ===== app.jsx ===== */
/* global React, ReactDOM */
const { useState: useStateA, useEffect: useEffectA } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#c7b07b",
  "ground": "#0e2a23",
  "headlineFont": "Cormorant Garamond",
  "density": "regular"
}/*EDITMODE-END*/;

const HEADLINE_FONTS = {
  "Cormorant Garamond": "'Cormorant Garamond', Georgia, serif",
  "Playfair Display": "'Playfair Display', Georgia, serif",
  "Fraunces": "'Fraunces', Georgia, serif",
};
const DENSITY_Y = { compact: "60px", regular: "clamp(72px, 9vw, 150px)", spacious: "clamp(96px, 12vw, 190px)" };

function App() {
  const [audience, setAudience] = useStateA("private");
  const [quizOpen, setQuizOpen] = useStateA(false);
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const openQuiz = () => setQuizOpen(true);

  useReveal();

  // apply tweaks to :root
  useEffectA(() => {
    const r = document.documentElement.style;
    r.setProperty("--accent", t.accent);
    r.setProperty("--emerald-800", t.ground);
    r.setProperty("--serif", HEADLINE_FONTS[t.headlineFont] || HEADLINE_FONTS["Cormorant Garamond"]);
    r.setProperty("--section-y", DENSITY_Y[t.density] || DENSITY_Y.regular);
  }, [t.accent, t.ground, t.headlineFont, t.density]);

  // re-run reveal observers whenever audience swaps content
  useEffectA(() => {
    const t2 = setTimeout(() => {
      document.querySelectorAll(".reveal:not(.in)").forEach((el) => {
        const rect = el.getBoundingClientRect();
        if (rect.top < window.innerHeight * 0.92) el.classList.add("in");
      });
    }, 60);
    return () => clearTimeout(t2);
  }, [audience]);

  // Browsers resolve URL hashes before the React-rendered sections exist.
  useEffectA(() => {
    const scrollToHash = (behavior = "auto") => {
      const id = decodeURIComponent(window.location.hash.replace(/^#/, ""));
      if (!id) return;
      const target = document.getElementById(id);
      if (!target) return;
      requestAnimationFrame(() => target.scrollIntoView({ block: "start", behavior }));
    };

    let alive = true;
    const timers = [];
    const queueHashScroll = (behavior = "auto") => {
      [120, 420, 900, 1600].forEach((delay) => {
        timers.push(setTimeout(() => alive && scrollToHash(behavior), delay));
      });
    };

    if (window.location.hash) queueHashScroll("auto");
    if (document.fonts && document.fonts.ready) {
      document.fonts.ready.then(() => alive && scrollToHash("auto"));
    }
    const onLoad = () => scrollToHash("auto");
    const onHashChange = () => queueHashScroll("smooth");
    window.addEventListener("load", onLoad, { once: true });
    window.addEventListener("hashchange", onHashChange);
    return () => {
      alive = false;
      timers.forEach(clearTimeout);
      window.removeEventListener("load", onLoad);
      window.removeEventListener("hashchange", onHashChange);
    };
  }, []);

  return (
    <>
      <Nav audience={audience} setAudience={setAudience} onQuiz={openQuiz} />
      <main>
        <Hero audience={audience} onQuiz={openQuiz} />
        <TrustStrip />
        <Programs audience={audience} onQuiz={openQuiz} />
        <Explorer onQuiz={openQuiz} />
        <Jurisdictions />
        <Approach audience={audience} />
        <Testimonial audience={audience} />
        <Insights />
        <CtaBand audience={audience} onQuiz={openQuiz} />
      </main>
      <Footer audience={audience} setAudience={setAudience} />
      <QuizModal open={quizOpen} onClose={() => setQuizOpen(false)} />

      <TweaksPanel>
        <TweakSection label="Brand" />
        <TweakColor label="Accent" value={t.accent}
          options={["#c7b07b", "#b8965a", "#a98b54", "#caa64b"]}
          onChange={(v) => setTweak("accent", v)} />
        <TweakColor label="Ground" value={t.ground}
          options={["#0e2a23", "#0b1f33", "#1a1a1a", "#10312a"]}
          onChange={(v) => setTweak("ground", v)} />
        <TweakSection label="Typography" />
        <TweakSelect label="Headline font" value={t.headlineFont}
          options={["Cormorant Garamond", "Playfair Display", "Fraunces"]}
          onChange={(v) => setTweak("headlineFont", v)} />
        <TweakSection label="Layout" />
        <TweakRadio label="Density" value={t.density}
          options={["compact", "regular", "spacious"]}
          onChange={(v) => setTweak("density", v)} />
      </TweaksPanel>
    </>
  );
}

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