// app.jsx — Origin Advisory

const { useEffect, useRef, useState } = React;
const { Icons } = window;

// ── Images ─────────────────────────────────────────────────────────────────
const FALLBACK_BG = "#1a3a5c";
const HERO_PHOTOS = [
  "alex-azabache-8weolGgaa9w-unsplash.jpg",
  "roxanne-desgagnes-Jc-XDKM6bZk-unsplash.jpg",
  "hendrik-cornelissen-jpTT_SAU034-unsplash.jpg",
  "tomas-malik-CKEmZAw0Z8c-unsplash.jpg",
  "jerry-kavan-i9eaAR4dWi8-unsplash.jpg",
];
const BREAK_IMG = "siarhei-palishchuk-kSUDzuVslS0-unsplash.jpg"; // Buddha statue
const HILLS_IMG = "dinuka-lankaloka-iduEaeBB_rQ-unsplash.jpg";   // Tea hills

// ── Map data ───────────────────────────────────────────────────────────────
const REGION_DATA = {
  "Colombo":      { intensity: "high", modal: "western"    },
  "Gampaha":      { intensity: "high", modal: "western"    },
  "Kalutara":     { intensity: "high", modal: "western"    },
  "Jaffna":       { intensity: "high", modal: "northern"   },
  "Kilinochchi":  { intensity: "high", modal: "northern"   },
  "Mullaitivu":   { intensity: "high", modal: "northern"   },
  "Mannar":       { intensity: "high", modal: "northern"   },
  "Vavuniya":     { intensity: "high", modal: "northern"   },
  "Galle":        { intensity: "high", modal: "southern"   },
  "Matara":       { intensity: "high", modal: "southern"   },
  "Hambantota":   { intensity: "high", modal: "hambantota" },
  "Kandy":        { intensity: "med",  modal: "central"    },
  "Matale":       { intensity: "med",  modal: "central"    },
  "Nuwara Eliya": { intensity: "med",  modal: "central"    },
};

const MODAL_CONTENT = {
  western: {
    name:     "Western Province",
    image:    "farhath-firous-PwHQahVJ_ag-unsplash.jpg",
    category: "Data Coverage",
    title:    "Macroeconomic & Fiscal Data",
    desc:     "Sri Lanka's economic engine. We track every IMF programme review, central bank announcement, and fiscal indicator from the country's commercial and political capital.",
    covered: [
      "Live IMF Extended Fund Facility tracking",
      "Central Bank of Sri Lanka announcements",
      "Treasury bond and currency data",
      "Fiscal performance indicators",
    ],
  },
  northern: {
    name:     "Northern Province",
    image:    "daniel-klein-Qx8_d5dGhrs-unsplash.jpg",
    category: "Data Coverage",
    title:    "Post-Conflict Development Data",
    desc:     "Reconstruction funding, agricultural recovery programmes, and donor activity in Sri Lanka's most complex development context.",
    covered: [
      "Active reconstruction programmes by donor",
      "Agricultural recovery initiatives",
      "Civil society and reconciliation funding flows",
      "Northern-specific FCDO and ADB activity",
    ],
  },
  southern: {
    name:     "Southern Province",
    image:    "javier-saint-jean-r4pIcB2reug-unsplash.jpg",
    category: "Data Coverage",
    title:    "Tourism & SME Sector Data",
    desc:     "Tourism economy indicators and small business finance — the engines of post-crisis recovery in coastal Sri Lanka.",
    covered: [
      "Tourism arrivals and revenue data",
      "SME finance programme tracking",
      "Coastal infrastructure projects",
      "Climate adaptation funding",
    ],
  },
  hambantota: {
    name:     "Hambantota Region",
    image:    "siarhei-palishchuk-kSUDzuVslS0-unsplash.jpg",
    category: "Data Coverage",
    title:    "Infrastructure & Sovereign Debt Data",
    desc:     "Belt & Road Initiative tracking, port concessions, and sovereign debt analysis from one of South Asia's most strategically watched districts.",
    covered: [
      "BRI project status and financing",
      "Port concession terms",
      "Sovereign debt restructuring",
      "Chinese and Indian infrastructure activity",
    ],
  },
  central: {
    name:     "Central Province",
    image:    "dinuka-lankaloka-iduEaeBB_rQ-unsplash.jpg",
    category: "Data Coverage",
    title:    "Agriculture & Rural Development Data",
    desc:     "Smallholder agriculture, tea sector economics, and rural development programmes across Sri Lanka's hill country.",
    covered: [
      "Tea sector trade and pricing data",
      "Smallholder agriculture programmes",
      "Rural development funding flows",
      "Climate-smart agriculture initiatives",
    ],
  },
};

// South Asia country overlay config
const SA_COUNTRIES = {
  "India":      { level: "coming", tooltip: "India — Coverage coming soon"  },
  "Pakistan":   { level: "2026",   tooltip: "Pakistan — Launching 2026"     },
  "Bangladesh": { level: "2026",   tooltip: "Bangladesh — Launching 2026"   },
  "Nepal":      { level: "faint",  tooltip: null },
  "Bhutan":     { level: "faint",  tooltip: null },
  "Maldives":   { level: "faint",  tooltip: null },
};

// ── National-scope article detection ──────────────────────────────────────
const NATIONAL_CITY_NAMES = [
  "Sri Lanka", "India", "Pakistan", "Bangladesh", "Nepal", "Bhutan", "Maldives", "Afghanistan",
  "Myanmar", "Thailand", "Vietnam", "Cambodia", "Laos", "Malaysia", "Singapore", "Indonesia",
  "Philippines", "Brunei", "East Timor", "China", "Japan", "South Korea", "North Korea",
  "Mongolia", "Taiwan", "Kazakhstan", "Uzbekistan", "Turkmenistan", "Tajikistan", "Kyrgyzstan",
  "Turkey", "Syria", "Lebanon", "Israel", "Jordan", "Saudi Arabia", "Yemen", "Oman",
  "United Arab Emirates", "Qatar", "Kuwait", "Bahrain", "Iraq", "Iran", "Cyprus",
  "Palestine", "Armenia", "Azerbaijan", "Georgia", "South Asia", "Asia",
];

const COUNTRY_CAPITALS = {
  // South Asia
  "Sri Lanka":            [6.927,   79.861],
  "India":                [28.614,  77.209],
  "Pakistan":             [33.729,  73.094],
  "Bangladesh":           [23.811,  90.412],
  "Nepal":                [27.701,  85.314],
  "Bhutan":               [27.472,  89.639],
  "Maldives":             [4.175,   73.509],
  "Afghanistan":          [34.528,  69.178],
  // Southeast Asia
  "Myanmar":              [19.745,  96.129],
  "Thailand":             [13.754, 100.501],
  "Vietnam":              [21.028, 105.854],
  "Cambodia":             [11.562, 104.916],
  "Laos":                 [17.967, 102.600],
  "Malaysia":             [3.149,  101.697],
  "Singapore":            [1.352,  103.820],
  "Indonesia":            [-6.208, 106.847],
  "Philippines":          [14.599, 120.984],
  "Brunei":               [4.940,  114.948],
  "East Timor":           [-8.556, 125.578],
  // East Asia
  "China":                [39.916, 116.391],
  "Japan":                [35.689, 139.692],
  "South Korea":          [37.564, 126.978],
  "North Korea":          [39.039, 125.754],
  "Mongolia":             [47.906, 106.923],
  "Taiwan":               [25.047, 121.514],
  "Hong Kong":            [22.319, 114.169],
  // Central Asia
  "Kazakhstan":           [51.160,  71.446],
  "Uzbekistan":           [41.299,  69.240],
  "Turkmenistan":         [37.936,  58.379],
  "Tajikistan":           [38.560,  68.774],
  "Kyrgyzstan":           [42.870,  74.590],
  // West Asia / Middle East
  "Turkey":               [39.921,  32.854],
  "Syria":                [33.510,  36.291],
  "Lebanon":              [33.889,  35.500],
  "Israel":               [31.768,  35.214],
  "Jordan":               [31.952,  35.934],
  "Saudi Arabia":         [24.687,  46.722],
  "Yemen":                [15.369,  44.191],
  "Oman":                 [23.614,  58.593],
  "United Arab Emirates": [24.453,  54.377],
  "Qatar":                [25.286,  51.533],
  "Kuwait":               [29.370,  47.978],
  "Bahrain":              [26.215,  50.586],
  "Iraq":                 [33.341,  44.401],
  "Iran":                 [35.696,  51.423],
  "Cyprus":               [35.166,  33.373],
  "Palestine":            [31.899,  35.204],
  "Armenia":              [40.182,  44.515],
  "Azerbaijan":           [40.407,  49.867],
  "Georgia":              [41.694,  44.834],
};

function isNationalScope(article) {
  return !article.city || NATIONAL_CITY_NAMES.includes(article.city);
}

const ARTICLE_ZOOM_THRESHOLD = 7;

// ── HDI Data ────────────────────────────────────────────────────────────────
// Source of truth: data/asia-hdi-2024.json (update that file when new HDR data is released)
// Values from UNDP Human Development Report 2025 (reports 2023 data).
// Verified against official HDR 2025 Statistical Annex Table 1 (PDF).
const HDI_COUNTRIES = {
  // South Asia
  "LK": { value: 0.776, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "MV": { value: 0.766, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "BT": { value: 0.698, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "BD": { value: 0.685, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "IN": { value: 0.685, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "NP": { value: 0.622, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "PK": { value: 0.544, tier: "low",       source: "UNDP HDR 2025 — 2023 data" },
  "AF": { value: 0.496, tier: "low",       source: "UNDP HDR 2025 — 2023 data" },
  // Southeast Asia
  "MM": { value: 0.609, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "TH": { value: 0.798, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "VN": { value: 0.766, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "KH": { value: 0.606, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "LA": { value: 0.617, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "MY": { value: 0.819, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "SG": { value: 0.946, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "ID": { value: 0.728, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "PH": { value: 0.720, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "BN": { value: 0.837, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "TL": { value: 0.634, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  // East Asia
  "CN": { value: 0.797, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "JP": { value: 0.925, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "KR": { value: 0.937, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "HK": { value: 0.955, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  // KP (North Korea): HDI not published by UNDP — omitted; country shows without colour
  "MN": { value: 0.747, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "TW": { value: 0.926, tier: "very_high", source: "Est. — secondary source (UNDP does not publish for Taiwan)" },
  // Central Asia
  "KZ": { value: 0.837, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "UZ": { value: 0.740, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "TM": { value: 0.764, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "TJ": { value: 0.691, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "KG": { value: 0.720, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  // West Asia / Middle East
  "TR": { value: 0.853, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "SY": { value: 0.564, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "LB": { value: 0.752, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "IL": { value: 0.919, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "JO": { value: 0.754, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "SA": { value: 0.900, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "YE": { value: 0.470, tier: "low",       source: "UNDP HDR 2025 — 2023 data" },
  "OM": { value: 0.858, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "AE": { value: 0.940, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "QA": { value: 0.886, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "KW": { value: 0.852, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "BH": { value: 0.899, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "IQ": { value: 0.695, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "IR": { value: 0.799, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "CY": { value: 0.913, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "PS": { value: 0.674, tier: "medium",    source: "UNDP HDR 2025 — 2023 data" },
  "AM": { value: 0.811, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
  "AZ": { value: 0.789, tier: "high",      source: "UNDP HDR 2025 — 2023 data" },
  "GE": { value: 0.844, tier: "very_high", source: "UNDP HDR 2025 — 2023 data" },
};

const HDI_LK_PROVINCES = {
  "Western":       { value: 0.821, tier: "very_high", source: "DCS Sri Lanka 2019" },
  "Southern":      { value: 0.785, tier: "high",      source: "DCS Sri Lanka 2019" },
  "North Western": { value: 0.764, tier: "high",      source: "DCS Sri Lanka 2019" },
  "Central":       { value: 0.755, tier: "high",      source: "DCS Sri Lanka 2019" },
  "Sabaragamuwa":  { value: 0.751, tier: "high",      source: "DCS Sri Lanka 2019" },
  "North Central": { value: 0.738, tier: "high",      source: "DCS Sri Lanka 2019" },
  "Uva":           { value: 0.724, tier: "high",      source: "DCS Sri Lanka 2019" },
  "Eastern":       { value: 0.696, tier: "medium",    source: "DCS Sri Lanka 2019" },
  "Northern":      { value: 0.687, tier: "medium",    source: "DCS Sri Lanka 2019" },
};

// GeoJSON is at district level — map each to its province for HDI lookup
const DISTRICT_TO_PROVINCE = {
  "Colombo": "Western",      "Gampaha": "Western",      "Kalutara": "Western",
  "Kandy":   "Central",      "Matale":  "Central",      "Nuwara Eliya": "Central",
  "Galle":   "Southern",     "Matara":  "Southern",     "Hambantota": "Southern",
  "Jaffna":  "Northern",     "Kilinochchi": "Northern", "Mannar": "Northern",
  "Vavuniya":"Northern",     "Mullaitivu":  "Northern",
  "Trincomalee": "Eastern",  "Batticaloa":  "Eastern",  "Ampara": "Eastern",
  "Kurunegala": "North Western", "Puttalam": "North Western",
  "Anuradhapura": "North Central", "Polonnaruwa": "North Central",
  "Badulla": "Uva",          "Moneragala": "Uva",
  "Ratnapura": "Sabaragamuwa", "Kegalle": "Sabaragamuwa",
};

const COUNTRY_TO_ISO = {
  // South Asia
  "India": "IN", "Pakistan": "PK", "Bangladesh": "BD",
  "Nepal": "NP", "Bhutan": "BT",  "Maldives": "MV", "Sri Lanka": "LK",
  "Afghanistan": "AF",
  // Southeast Asia
  "Myanmar": "MM", "Burma": "MM",
  "Thailand": "TH", "Vietnam": "VN", "Cambodia": "KH", "Laos": "LA",
  "Malaysia": "MY", "Singapore": "SG", "Indonesia": "ID", "Philippines": "PH",
  "Brunei": "BN", "Brunei Darussalam": "BN",
  "East Timor": "TL", "Timor-Leste": "TL",
  // East Asia
  "China": "CN", "Japan": "JP", "South Korea": "KR", "Korea": "KR",
  "North Korea": "KP", "Mongolia": "MN",
  "Taiwan": "TW", "Taiwan*": "TW",
  "Hong Kong": "HK", "Hong Kong S.A.R.": "HK", "Hong Kong SAR China": "HK",
  // Central Asia
  "Kazakhstan": "KZ", "Uzbekistan": "UZ", "Turkmenistan": "TM",
  "Tajikistan": "TJ", "Kyrgyzstan": "KG",
  // West Asia / Middle East
  "Turkey": "TR", "Türkiye": "TR",
  "Syria": "SY", "Lebanon": "LB", "Israel": "IL", "Jordan": "JO",
  "Saudi Arabia": "SA", "Yemen": "YE", "Oman": "OM",
  "United Arab Emirates": "AE", "Qatar": "QA", "Kuwait": "KW",
  "Bahrain": "BH", "Iraq": "IQ", "Iran": "IR",
  "Cyprus": "CY", "Palestine": "PS", "West Bank": "PS",
  "Armenia": "AM", "Azerbaijan": "AZ", "Georgia": "GE",
};

const COUNTRY_FALLBACK_KEY = {
  "Sri Lanka": "default_LK", "Pakistan": "default_PK",
  "Bangladesh": "default_BD", "India": "default_IN", "Nepal": "default_NP",
};

// Countries that have sub-national HDI shading (either via admin1 GeoJSON or lazy-loaded)
const SUBNATIONAL_COUNTRIES = new Set([
  'IN', 'PK', 'BD', 'LK', 'NP', 'BT', 'MV',
  'CN', 'ID', 'PH', 'VN', 'TH', 'MM', 'IR', 'TR',
  'KH', 'LA', 'MY', 'KZ', 'UZ', 'MN', 'KG', 'TJ', 'AF',
]);
// Countries requiring lazy-loaded GeoJSON (South Asia uses pre-loaded admin1)
const LAZY_SUBNAT = {
  'CN': { iso3: 'CHN', name: 'China',       bbox: [[ 18,  73], [ 53, 135]] },
  'ID': { iso3: 'IDN', name: 'Indonesia',   bbox: [[-11,  95], [  6, 141]] },
  'PH': { iso3: 'PHL', name: 'Philippines', bbox: [[  5, 116], [ 22, 127]] },
  'VN': { iso3: 'VNM', name: 'Vietnam',     bbox: [[  8, 102], [ 24, 110]] },
  'TH': { iso3: 'THA', name: 'Thailand',    bbox: [[  5,  97], [ 21, 106]] },
  'MM': { iso3: 'MMR', name: 'Myanmar',     bbox: [[  9,  92], [ 28, 102]] },
  'IR': { iso3: 'IRN', name: 'Iran',        bbox: [[ 25,  44], [ 40,  64]] },
  'TR': { iso3: 'TUR', name: 'Turkey',      bbox: [[ 36,  26], [ 42,  45]] },
  'KH': { iso3: 'KHM', name: 'Cambodia',   bbox: [[ 10, 102], [ 15, 108]] },
  'LA': { iso3: 'LAO', name: 'Laos',        bbox: [[ 14, 100], [ 23, 107]] },
  'MY': { iso3: 'MYS', name: 'Malaysia',    bbox: [[  1, 100], [  8, 120]] },
  'KZ': { iso3: 'KAZ', name: 'Kazakhstan',  bbox: [[ 41,  51], [ 56,  88]] },
  'UZ': { iso3: 'UZB', name: 'Uzbekistan',  bbox: [[ 37,  56], [ 46,  74]] },
  'MN': { iso3: 'MNG', name: 'Mongolia',    bbox: [[ 41,  87], [ 52, 120]] },
  'KG': { iso3: 'KGZ', name: 'Kyrgyzstan',  bbox: [[ 39,  69], [ 44,  81]] },
  'TJ': { iso3: 'TJK', name: 'Tajikistan',  bbox: [[ 36,  67], [ 41,  75]] },
  'AF': { iso3: 'AFG', name: 'Afghanistan', bbox: [[ 29,  60], [ 39,  75]] },
};

// Sub-national HDI data (Global Data Lab Subnational HDI 2023 / UNDP national reports)
const HDI_IN_STATES = {
  "Kerala": 0.782, "Goa": 0.761, "Delhi": 0.746, "Chandigarh": 0.776,
  "Himachal Pradesh": 0.725, "Punjab": 0.724, "Haryana": 0.708, "Tamil Nadu": 0.708,
  "Maharashtra": 0.696, "Karnataka": 0.682, "Uttarakhand": 0.684, "Telangana": 0.669,
  "Gujarat": 0.672, "Andhra Pradesh": 0.649, "West Bengal": 0.641,
  "Jammu and Kashmir": 0.679, "Ladakh": 0.671,
  "Andaman and Nicobar": 0.740, "Lakshadweep": 0.734, "Puducherry": 0.730,
  "Dadra and Nagar Haveli and Daman and Diu": 0.695,
  "Sikkim": 0.716, "Mizoram": 0.624, "Manipur": 0.600, "Nagaland": 0.605,
  "Arunachal Pradesh": 0.600, "Meghalaya": 0.586, "Tripura": 0.608,
  "Chhattisgarh": 0.613, "Assam": 0.613, "Odisha": 0.606, "Madhya Pradesh": 0.603,
  "Rajasthan": 0.629, "Jharkhand": 0.599, "Uttar Pradesh": 0.596, "Bihar": 0.574,
};

const HDI_PK_PROVINCES = {
  "Punjab": 0.567, "Sindh": 0.546, "Khyber Pakhtunkhwa": 0.510,
  "Balochistan": 0.475, "Islamabad Capital Territory": 0.624,
  "Azad Kashmir": 0.612, "Gilgit-Baltistan": 0.589,
};

const HDI_BD_DIVISIONS = {
  "Dhaka": 0.701, "Chittagong": 0.673, "Khulna": 0.674,
  "Rajshahi": 0.654, "Sylhet": 0.642, "Barisal": 0.625, "Rangpur": 0.611,
};

// Nepal uses old 14-zone boundaries in the GeoJSON
const HDI_NP_ZONES = {
  "Bagmati": 0.644, "Gandaki": 0.590, "Narayani": 0.572, "Lumbini": 0.549,
  "Dhawalagiri": 0.557, "Rapti": 0.508, "Bheri": 0.507, "Karnali": 0.488,
  "Seti": 0.511, "Mahakali": 0.520, "Mechi": 0.558, "Sagarmatha": 0.554,
  "Janakpur": 0.530, "Bhojpur": 0.543,
};

const HDI_BT_DZONGKHAGS = {
  "Thimphu": 0.748, "Paro": 0.719, "Punakha": 0.695, "Chhukha": 0.700,
  "Wangdi Phodrang": 0.672, "Bumthang": 0.680, "Gasa": 0.628, "Ha": 0.650,
  "Tongsa": 0.659, "Lhuntshi": 0.582, "Mongar": 0.593, "Tashigang": 0.574,
  "Pemagatsel": 0.601, "Shemgang": 0.575, "Samdrup Jongkhar": 0.638,
  "Samchi": 0.615, "Chirang": 0.632, "Daga": 0.607, "Geylegphug": 0.640,
  "Tashi Yangtse": 0.578,
};

const HDI_MV_ATOLLS = {
  "Malé": 0.819, "Kaafu": 0.788, "Addu": 0.768, "Baa": 0.756,
  "Alifu Alifu": 0.749, "Alifu Dhaalu": 0.744, "Vaavu": 0.750,
  "Lhaviyani": 0.742, "Gaafu Dhaalu": 0.741, "Gaafu Alifu": 0.738,
  "Gnaviyani": 0.735, "Noonu": 0.730, "Faafu": 0.728, "Raa": 0.727,
  "Shaviyani": 0.719, "Meemu": 0.720, "Thaa": 0.712, "Laamu": 0.718,
  "Dhaalu": 0.723, "Haa Dhaalu": 0.704, "Haa Alifu": 0.698,
};

// Sub-national HDI for lazy-loaded countries (Global Data Lab SHDI 2023)
const SUBNAT_HDI = {
  'CN': {
    "Beijing Municipality": 0.876, "Shanghai Municipality": 0.872,
    "Tianjin Municipality": 0.832, "Zhejiang Province": 0.818,
    "Jiangsu Province": 0.815, "Guangzhou Province": 0.793,
    "Fujian Province": 0.800, "Liaoning Province": 0.784,
    "Shandong Province": 0.786, "Chongqing Municipality": 0.773,
    "Inner Mongolia Autonomous Region": 0.775, "Jilin Province": 0.762,
    "Heilongjiang Province": 0.757, "Shaanxi Province": 0.766,
    "Hubei Province": 0.779, "Shanxi Province": 0.756,
    "Henan Province": 0.753, "Hebei Province": 0.762,
    "Anhui Province": 0.749, "Hunan Province": 0.762,
    "Sichuan Province": 0.761, "Jiangxi Province": 0.753,
    "Guangxi Zhuang Autonomous Region": 0.722, "Hainan Province": 0.741,
    "Ningxia Ningxia Hui Autonomous Region": 0.748,
    "Xinjiang Uyghur Autonomous Region": 0.742,
    "Qinghai Province": 0.697, "Gansu Province": 0.693,
    "Guizhou Province": 0.701, "Yunnan Province": 0.688,
    "Tibet Autonomous Region": 0.583,
    "Hong Kong Special Administrative Region": 0.956,
    "Macau Special Administrative Region": 0.930,
  },
  'ID': {
    "Jakarta Special Capital Region": 0.823, "Special Region of Yogyakarta": 0.798,
    "Bali": 0.762, "East Kalimantan": 0.755, "Riau Islands": 0.762,
    "Bangka-Belitung Islands": 0.726, "North Sulawesi": 0.721,
    "North Kalimantan": 0.717, "Banten": 0.712, "Central Kalimantan": 0.710,
    "South Kalimantan": 0.713, "West Sumatra": 0.712, "Riau": 0.714,
    "East Java": 0.706, "West Java": 0.710, "Central Java": 0.715,
    "South Sulawesi": 0.710, "Jambi": 0.704, "Aceh": 0.690,
    "North Sumatra": 0.693, "Central Sulawesi": 0.679, "Gorontalo": 0.678,
    "South Sumatra": 0.686, "Bengkulu": 0.681, "Southeast Sulawesi": 0.685,
    "Lampung": 0.682, "West Kalimantan": 0.675, "West Sulawesi": 0.654,
    "West Nusa Tenggara": 0.648, "East Nusa Tenggara": 0.631,
    "North Maluku": 0.666, "Maluku": 0.664, "West Papua": 0.618, "Papua": 0.572,
  },
  'PH': {
    "NCR": 0.826, "Calabarzon": 0.749, "Central Visayas": 0.720,
    "Central Luzon": 0.712, "CAR": 0.697, "Davao Region": 0.699,
    "Western Visayas": 0.682, "Northern Mindanao": 0.691,
    "Ilocos Region": 0.686, "Mimaropa": 0.649, "Cagayan Valley": 0.648,
    "Eastern Visayas": 0.654, "Caraga": 0.634, "Soccsksargen": 0.659,
    "Zamboanga Peninsula": 0.631, "Bicol Region": 0.640, "ARMM": 0.539,
  },
  'VN': {
    "Ho Chi Minh": 0.847, "Hà Nội": 0.837, "Đà Nẵng": 0.816,
    "Hải Phòng": 0.782, "Bà Rịa–Vũng Tàu": 0.780, "Bình Dương": 0.774,
    "Cần Thơ": 0.745, "Vĩnh Phúc": 0.741, "Đồng Nai": 0.741,
    "Bắc Ninh": 0.743, "Khánh Hòa": 0.736, "Quảng Ninh": 0.729,
    "Hưng Yên": 0.730, "Hải Dương": 0.720, "Lâm Đồng": 0.718,
    "Long An": 0.712, "Thái Nguyên": 0.705, "Tây Ninh": 0.705,
    "Bình Phước": 0.704, "An Giang": 0.703, "Thừa Thiên Huế": 0.700,
    "Bắc Giang": 0.700, "Kiên Giang": 0.700, "Bình Thuận": 0.706,
    "Tiền Giang": 0.710, "Ninh Bình": 0.690, "Phú Thọ": 0.688,
    "Nam Định": 0.685, "Hà Nam": 0.680, "Sóc Trăng": 0.681,
    "Thái Bình": 0.677, "Ninh Thuận": 0.682, "Phú Yên": 0.678,
    "Cà Mau": 0.678, "Quảng Nam": 0.672, "Hậu Giang": 0.673,
    "Bạc Liêu": 0.673, "Tuyên Quang": 0.670, "Hòa Bình": 0.665,
    "Quảng Ngãi": 0.667, "Trà Vinh": 0.668, "Bến Tre": 0.698,
    "Vĩnh Long": 0.698, "Đồng Tháp": 0.695, "Yên Bái": 0.660,
    "Quảng Trị": 0.661, "Hà Tĩnh": 0.657, "Lạng Sơn": 0.653,
    "Quảng Bình": 0.655, "Đắk Lắk": 0.654, "Bình Định": 0.697,
    "Nghệ An": 0.645, "Gia Lai": 0.650, "Kon Tum": 0.644, "Đắk Nông": 0.641,
    "Thanh Hóa": 0.638, "Bắc Kạn": 0.636, "Lào Cai": 0.632,
    "Cao Bằng": 0.593, "Sơn La": 0.614, "Điện Biên": 0.590,
    "Lai Châu": 0.584, "Hà Giang": 0.560,
  },
  'TH': {
    "Bangkok": 0.835, "Phuket Province": 0.802, "Chon Buri Province": 0.786,
    "Nonthaburi Province": 0.776, "Rayong Province": 0.775,
    "Pathum Thani Province": 0.768, "Samut Prakan Province": 0.754,
    "Samut Sakhon Province": 0.751, "Saraburi Province": 0.750,
    "Prachin Buri Province": 0.742, "Chiang Mai Province": 0.738,
    "Songkhla Province": 0.733, "Ang Thong Province": 0.717,
    "Sing Buri Province": 0.719, "Nakhon Ratchasima Province": 0.714,
    "Lopburi Province": 0.721, "Khon Kaen Province": 0.704,
    "Udon Thani Province": 0.704, "Maha Sarakham Province": 0.685,
    "Nakhon Sawan Province": 0.700, "Roi Et Province": 0.675,
    "Ubon Ratchathani Province": 0.680, "Surin Province": 0.672,
    "Chiang Rai Province": 0.689, "Si Sa Ket Province": 0.661,
    "Yala Province": 0.660, "Narathiwat Province": 0.654,
    "Pattani Province": 0.643, "Mae Hong Son Province": 0.637,
  },
  'MM': {
    "Yangon": 0.643, "Mandalay": 0.580, "Bago": 0.562,
    "Mon": 0.568, "Tanitharyi": 0.563, "Kachin": 0.560,
    "Ayeyarwady": 0.547, "Kayah": 0.545, "Magway": 0.548,
    "Saigang": 0.549, "Kayin": 0.534,
    "Shan": 0.528, "Rakhine": 0.510, "Chin": 0.499,
  },
  'IR': {
    "Tehran": 0.832, "Alborz": 0.810, "Isfahan": 0.794,
    "Qom": 0.796, "Yazd": 0.786, "Fars": 0.765,
    "Mazandaran": 0.768, "Semnan": 0.762, "Qazvin": 0.762,
    "Gilan": 0.753, "Bushehr": 0.749, "Markazi": 0.748,
    "East Azerbaijan": 0.747, "Zanjan": 0.737,
    "Razavi Khorasan": 0.741, "Ardabil": 0.721,
    "West Azerbaijan": 0.725, "Hamadan": 0.726,
    "Kermanshah": 0.722, "North Khorasan": 0.719,
    "Khuzestan": 0.730, "Golestan": 0.708, "Lorestan": 0.700,
    "Ilam": 0.710, "Chaharmahal and Bakhtiari": 0.714,
    "Kurdistan": 0.706, "Kohgiluyeh and Boyer-Ahmad": 0.694,
    "South Khorasan": 0.694, "Hormozgan": 0.692,
    "Sistan and Baluchestan": 0.595,
  },
  'TR': {
    "İstanbul": 0.834, "Ankara": 0.820, "İzmir": 0.803,
    "Kocaeli": 0.795, "Eskişehir": 0.791, "Tekirdağ": 0.783,
    "Antalya": 0.773, "Muğla": 0.771, "Bursa": 0.771,
    "Yalova": 0.767, "Denizli": 0.763, "Çanakkale": 0.763,
    "Aydın": 0.758, "Edirne": 0.756, "Sakarya": 0.762,
    "Kırklareli": 0.751, "Kayseri": 0.757, "Konya": 0.748,
    "Mersin": 0.748, "Balıkesir": 0.748, "Bolu": 0.745,
    "Manisa": 0.745, "Adana": 0.745, "Gaziantep": 0.740,
    "Trabzon": 0.738, "Düzce": 0.738, "Samsun": 0.730,
    "Karabük": 0.735, "Zonguldak": 0.726, "Kastamonu": 0.720,
    "Elazığ": 0.702, "Batman": 0.621, "Mardin": 0.621,
    "Diyarbakır": 0.659, "Siirt": 0.601, "Bitlis": 0.590,
    "Şırnak": 0.571, "Ağrı": 0.567, "Muş": 0.578,
    "Van": 0.610, "Şanlıurfa": 0.614, "Hakkâri": 0.575,
  },
  'KH': {
    "Phnom Penh": 0.703, "Siem Reap": 0.573, "Kandal": 0.589,
    "Preah Sihanouk": 0.566, "Koh Kong": 0.553, "Kep": 0.556,
    "Kampot": 0.557, "Battambang": 0.561, "Kampong Cham": 0.556,
    "Kampong Thom": 0.551, "Takeo": 0.547, "Kampong Chhnang": 0.547,
    "Prey Veng": 0.547, "Svay Rieng": 0.544, "Pailin": 0.544,
    "Tbong Khmum": 0.541, "Pursat": 0.540, "Kampong Speu": 0.540,
    "Kratie": 0.536, "Bantey Meanchey": 0.535, "Stung Treng": 0.520,
    "Preah Vihear": 0.513, "Oddar Meanchey": 0.501,
    "Mondulkiri": 0.501, "Ratanakiri Province": 0.479,
  },
  'LA': {
    "Vientiane Capital": 0.720, "Vientiane": 0.600, "Champasak": 0.598,
    "Luang Prabang": 0.592, "Bolikhamsai": 0.582, "Xaignabouli": 0.583,
    "Khammouane": 0.570, "Bokeo": 0.565, "Xiangkhouang": 0.556,
    "Attapeu": 0.549, "Xaisomboun": 0.543, "Houaphan": 0.543,
    "Oudomxay": 0.543, "Savannakhet": 0.572, "Salavan": 0.535,
    "Luang Namtha": 0.547, "Xekong": 0.521, "Phongsaly": 0.513,
  },
  'MY': {
    "Kuala Lumpur": 0.882, "Putrajaya": 0.855, "Selangor": 0.826,
    "Penang": 0.800, "Labuan": 0.800, "Johor": 0.773,
    "Negeri Sembilan": 0.757, "Malacca": 0.755, "Perak": 0.736,
    "Sarawak": 0.718, "Terengganu": 0.718, "Pahang": 0.725,
    "Perlis": 0.726, "Kedah": 0.706, "Kelantan": 0.691, "Sabah": 0.680,
  },
  'KZ': {
    "Astana": 0.857, "Almaty": 0.846, "Mangystau Region": 0.795,
    "Karaganda Region": 0.793, "Atyrau Region": 0.789,
    "Pavlodar Region": 0.789, "East Kazakhstan Region": 0.780,
    "Kostanay Region": 0.779, "North Kazakhstan Region": 0.775,
    "Aktobe Region": 0.773, "West Kazakhstan Region": 0.771,
    "Akmola Region": 0.770, "Almaty Region": 0.742,
    "Jambyl Region": 0.733, "Kyzylorda Region": 0.731,
    "South Kazakhstan Region": 0.715,
  },
  'UZ': {
    "Tashkent": 0.774, "Tashkent Region": 0.694,
    "Andijan Region": 0.686, "Fergana Region": 0.677,
    "Navoiy Region": 0.674, "Namangan Region": 0.673,
    "Bukhara Region": 0.659, "Samarqand Region": 0.655,
    "Sirdaryo Region": 0.652, "Jizzakh Region": 0.639,
    "Xorazm Region": 0.630, "Qashqadaryo Region": 0.617,
    "Surxondaryo Region": 0.608, "Republic of Karakalpakstan": 0.590,
  },
  'MN': {
    "Ulaanbaatar": 0.784, "Orkhon": 0.755, "Darkhan-Uul": 0.742,
    "Töv": 0.710, "Dornogovi": 0.700, "Ömnögovi": 0.702,
    "Selenge": 0.702, "Govisumber": 0.689, "Dornod": 0.682,
    "Sükhbaatar": 0.682, "Övörkhangai": 0.681, "Khentii": 0.675,
    "Bulgan": 0.676, "Dundgovi": 0.674, "Arkhangai": 0.673,
    "Bayankhongor": 0.672, "Hovsgel": 0.665, "Zavkhan": 0.660,
    "Uvs": 0.660, "Govi-Altai": 0.657, "Khovd": 0.655,
    "Bayan-Ölgii": 0.638,
  },
  'KG': {
    "Chuy Region": 0.706, "Issyk-Kul Region": 0.662,
    "Talas Region": 0.643, "Jalal-Abad Region": 0.648,
    "Naryn Region": 0.634, "Osh Region": 0.627, "Batken Region": 0.605,
  },
  'TJ': {
    "Dushanbe": 0.694, "Districts of Republican Subordination": 0.620,
    "Sughd Region": 0.611, "Gorno-Badakhshan Autonomous Region": 0.580,
    "Khatlon Region": 0.579,
  },
  'AF': {
    "Kabul": 0.508, "Balkh": 0.465, "Panjshir": 0.446,
    "Herat": 0.454, "Nangarhar": 0.438, "Kandahar": 0.434,
    "Parwan": 0.427, "Kapisa": 0.414, "Baghlan": 0.419,
    "Jowzjan": 0.416, "Badakhshan": 0.411, "Kunduz": 0.417,
    "Laghman": 0.403, "Takhar": 0.401, "Bamyan": 0.400,
    "Samangan": 0.399, "Khost": 0.399, "Logar": 0.406,
    "Kunar": 0.394, "Paktia": 0.396, "Nimruz": 0.395,
    "Wardak": 0.387, "Farah": 0.373, "Sar-e Pol": 0.374,
    "Nuristan": 0.365, "Faryab": 0.363, "Helmand": 0.361,
    "Paktika": 0.355, "Zabul": 0.354, "Ghanzi": 0.350,
    "Badghis": 0.344, "Daykundi": 0.342, "Ghor": 0.340,
    "Uruzgan": 0.320,
  },
};

// Natural Earth uses different spellings for some Pakistan provinces
const PK_NAME_MAP = {
  "Baluchistan": "Balochistan", "Sind": "Sindh",
  "K.P.": "Khyber Pakhtunkhwa", "F.C.T.": "Islamabad Capital Territory",
  "Northern Areas": "Gilgit-Baltistan", "F.A.T.A.": "Khyber Pakhtunkhwa",
};

const ISO_COUNTRY = {
  IN: "India", PK: "Pakistan", BD: "Bangladesh",
  NP: "Nepal", BT: "Bhutan", MV: "Maldives", LK: "Sri Lanka",
};
const ISO_REGION_TYPE = {
  IN: "State / UT", PK: "Province", BD: "Division",
  NP: "Zone", BT: "Dzongkhag", MV: "Atoll", LK: "Province",
};

// ── Story ranking ──────────────────────────────────────────────────────────
const SOURCE_TIERS = {
  // International majors
  'reuters.com': 0.90, 'bbc.com': 0.90, 'bbc.co.uk': 0.90, 'ft.com': 0.90,
  'apnews.com': 0.90, 'theguardian.com': 0.88, 'nytimes.com': 0.88,
  'wsj.com': 0.88, 'aljazeera.com': 0.85, 'bloomberg.com': 0.88,
  'economist.com': 0.87, 'foreignpolicy.com': 0.84,
  // Quality regional
  'thehindu.com': 0.80, 'dawn.com': 0.78, 'thedailystar.net': 0.76,
  'dhakatribune.com': 0.74, 'thediplomat.com': 0.78, 'scmp.com': 0.76,
  'bangkokpost.com': 0.72, 'straitstimes.com': 0.78, 'thejakartapost.com': 0.74,
  'nepaltimes.com': 0.72, 'kuenselonline.com': 0.68, 'ndtv.com': 0.72,
  'hindustantimes.com': 0.70, 'businessstandard.com': 0.68, 'aninews.in': 0.65,
  // Reliable local
  'adaderana.lk': 0.62, 'economynext.com': 0.60, 'dailymirror.lk': 0.60,
  'newsfirst.lk': 0.58, 'sundaytimes.lk': 0.60, 'sundayobserver.lk': 0.55,
  'lankabusinessonline.com': 0.58, 'colombopage.com': 0.55,
  'dailynews.lk': 0.55, 'island.lk': 0.57,
};
const DEFAULT_SOURCE_SCORE = 0.35;

function getSourceAuthority(url) {
  try {
    const domain = new URL(url).hostname.replace(/^www\./, '');
    return SOURCE_TIERS[domain] || DEFAULT_SOURCE_SCORE;
  } catch { return DEFAULT_SOURCE_SCORE; }
}

function calculateImportanceScore(article, allArticles) {
  const now    = Date.now();
  const ageHrs = (now - new Date(article.published).getTime()) / 3600000;
  const recency    = Math.exp(-0.693 * ageHrs / 24);      // half-life 24h
  const authority  = getSourceAuthority(article.url);
  const pubTime    = new Date(article.published).getTime();
  const nearby = allArticles.filter(a =>
    a.id !== article.id && a.country === article.country &&
    Math.abs(new Date(a.published).getTime() - pubTime) < 172800000
  ).length;
  const coverage = Math.min(nearby / 5, 1);
  const entity   = (article.significance || 1) / 4;
  return 0.40 * recency + 0.25 * authority + 0.20 * coverage + 0.10 * entity;
}

function isTrendingArticle(article) {
  const ageHrs = (Date.now() - new Date(article.published).getTime()) / 3600000;
  return ageHrs < 12 && (article.significance || 1) >= 3;
}

// Returns articles published within cutoffHours, sorted by significance then date.
// No padding — callers show fewer items rather than surfacing stale stories.
function freshEditionPool(articles, cutoffHours = 48) {
  const cutoff = Date.now() - cutoffHours * 3600000;
  return articles
    .filter(a => new Date(a.published).getTime() >= cutoff)
    .sort((a, b) => (b.significance || 1) - (a.significance || 1) || new Date(b.published) - new Date(a.published));
}

function getCoverageCount(article, allArticles) {
  const pubTime = new Date(article.published).getTime();
  return allArticles.filter(a =>
    a.id !== article.id && a.country === article.country &&
    Math.abs(new Date(a.published).getTime() - pubTime) < 172800000
  ).length;
}

function hdiColor(value) {
  if (value >= 0.800) return { fill: '#2d6a4f', tier: 'very_high' };
  if (value >= 0.700) return { fill: '#52b788', tier: 'high' };
  if (value >= 0.550) return { fill: '#f4d35e', tier: 'medium' };
  if (value >= 0.400) return { fill: '#e07a5f', tier: 'low' };
  return { fill: '#9d2d1f', tier: 'very_low' };
}

function hdiTierLabel(tier) {
  return ({
    very_high: 'VERY HIGH HUMAN DEVELOPMENT',
    high:      'HIGH HUMAN DEVELOPMENT',
    medium:    'MEDIUM HUMAN DEVELOPMENT',
    low:       'LOW HUMAN DEVELOPMENT',
    very_low:  'VERY LOW HUMAN DEVELOPMENT',
  })[tier] || '';
}

function makeCountryAggIcon(count) {
  return window.L.divIcon({
    html: `<div class="country-agg-marker">${count}</div>`,
    className: '',
    iconSize: [32, 32],
    iconAnchor: [16, 16],
  });
}

function makeArtIcon(art) {
  const cfgs = {
    4: { outer: 14, inner: 8, hex: '#e53e3e', outerOp: 0.30, pulse: true  },
    3: { outer: 12, inner: 7, hex: '#C8531C', outerOp: 0.25, pulse: false },
    2: { outer: 10, inner: 6, hex: '#d69e2e', outerOp: 0.20, pulse: false },
    1: { outer:  8, inner: 5, hex: '#38a169', outerOp: 0.15, pulse: false },
  };
  const c = cfgs[art.significance] || cfgs[2];
  const sz = c.outer * 2; const isz = c.inner * 2;
  return window.L.divIcon({
    html: `<div class="art-dot${c.pulse ? ' art-dot-pulse' : ''}" style="width:${sz}px;height:${sz}px;position:relative;"><div style="position:absolute;inset:0;border-radius:50%;background:${c.hex};opacity:${c.outerOp};"></div><div style="position:absolute;width:${isz}px;height:${isz}px;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:50%;background:${c.hex};border:1.5px solid #fff;box-shadow:0 2px 6px rgba(0,0,0,0.28);"></div></div>`,
    className: '', iconSize: [sz, sz], iconAnchor: [sz / 2, sz / 2],
  });
}

function makeLikedIcon() {
  return window.L.divIcon({
    html: `<div class="art-dot art-dot-pulse" style="width:32px;height:32px;position:relative;"><div style="position:absolute;inset:0;border-radius:50%;background:#E8526C;opacity:0.30;"></div><div style="position:absolute;width:16px;height:16px;top:50%;left:50%;transform:translate(-50%,-50%);border-radius:50%;background:#E8526C;border:2px solid #fff;box-shadow:0 2px 8px rgba(232,82,108,0.55);"></div></div>`,
    className: '', iconSize: [32, 32], iconAnchor: [16, 16],
  });
}

// ── Page data ──────────────────────────────────────────────────────────────
const FEATURES = [
  {
    num:  "01",
    title: "Ask anything",
    desc:  "Plain English search across IMF reports, FCDO programmes, World Bank projects, central bank announcements, and government policy.",
  },
  {
    num:  "02",
    title: "We retrieve",
    desc:  "AI scans 25+ live data sources updated daily — donor platforms, policy portals, central bank feeds, and multilateral project databases.",
  },
  {
    num:  "03",
    title: "You get sourced answers",
    desc:  "Cited, structured, and ready to use. Every output includes the source document, access date, and direct link. No hallucinations.",
  },
];

const USE_CASES = [
  { title: "Boutique consultancies",        desc: "Pitch faster on FCDO and ADB tenders with instant programme intelligence and donor flow analysis.",          image: "alex-azabache-8weolGgaa9w-unsplash.jpg" },
  { title: "NGOs and implementing partners",desc: "Stay ahead of policy changes, track regulatory shifts, and identify funding opportunities across South Asia.", image: null },
  { title: "Donor offices and DFIs",        desc: "Brief teams in minutes, not days. Compare programmes across countries with structured AI analysis.",           image: null },
  { title: "Researchers and analysts",      desc: "Cited sources, structured outputs, and direct access to the source documents. Built for serious work.",        image: "tomas-malik-CKEmZAw0Z8c-unsplash.jpg" },
];

const TIERS = [
  {
    num:    "01", title: "Individual", popular: false,
    price:  "£150", period: "/month",
    desc:   "For consultants, analysts, and researchers working solo.",
    cta:    "Start free trial",
    features: ["Full platform access", "Weekly intelligence briefings", "Citation-ready outputs", "Email support"],
  },
  {
    num:    "02", title: "Organisation", popular: true,
    price:  "£600", period: "/month",
    desc:   "For consultancies, NGOs, and implementing partners.",
    cta:    "Start free trial",
    features: ["Everything in Individual", "Up to 10 team members", "Custom keyword alerts", "Shared workspaces", "Priority support", "Custom briefing schedules"],
  },
  {
    num:    "03", title: "Enterprise", popular: false,
    price:  "Custom", period: "pricing",
    desc:   "For donor offices, DFIs, and large institutions.",
    cta:    "Talk to us",
    features: ["Everything in Organisation", "Unlimited team members", "Custom data integrations", "Dedicated analyst support", "Bespoke research reports", "White-label outputs"],
  },
];

const INSIGHTS = [
  {
    tag:   "Economic Analysis",
    title: "Sri Lanka's IMF Programme: Live Mid-Programme Review",
    desc:  "Auto-generated weekly analysis of Sri Lanka's IMF Extended Fund Facility — fiscal performance, monetary indicators, conditionality compliance. Updated as new data publishes.",
    image: "farhath-firous-PwHQahVJ_ag-unsplash.jpg",
  },
  {
    tag:   "Policy Brief",
    title: "FCDO Sri Lanka: Active Portfolio Snapshot",
    desc:  "Live tracking of every FCDO programme in Sri Lanka — funding levels, sector breakdown, key personnel, contract timelines. Updated within hours of new awards.",
    image: "daniel-klein-Qx8_d5dGhrs-unsplash.jpg",
  },
  {
    tag:   "Research Note",
    title: "Democracy and Development in Sri Lanka",
    desc:  "Long-form analysis of Sri Lanka's political economy and the executive presidency's impact on development outcomes. Drawing on Venugopal, Sen, and Huntington.",
    image: "javier-saint-jean-r4pIcB2reug-unsplash.jpg",
  },
];

const FAQ = [
  { q: "What sources does the platform cover?",    a: "Origin Advisory pulls from IMF, World Bank, FCDO, ADB, UNDP, central bank publications, government policy portals, and 20+ additional data sources across South Asia. Every output includes full source citations." },
  { q: "How current is the data?",                 a: "Most sources update daily. IMF and multilateral databases update on release schedules. Government sources update within 24-48 hours. Every data point shows a last-updated timestamp." },
  { q: "Can we get a custom enterprise setup?",    a: "Yes. Enterprise plans include custom data integrations, bespoke reporting formats, dedicated analyst support, and white-label outputs. Contact us to discuss your requirements." },
  { q: "What happens after the 14-day trial?",     a: "You'll be asked to choose a plan. There is no automatic billing — we confirm terms manually before any payment is taken." },
  { q: "Do you offer discounts for NGOs?",         a: "Yes. We offer a 30% discount for registered NGOs and civil society organisations. Contact us with your registration details." },
  { q: "How does this compare to Devex?",          a: "Devex covers global funding opportunities and aid sector news. Origin Advisory is a data intelligence tool — it tracks active programme data, policy shifts, and economic conditions in real time across South Asia. They're complementary." },
];

// ── Mock data ──────────────────────────────────────────────────────────────
const MOCK_REPORTS = [
  { id:1, title:"Colombo Development Brief", category:"City Brief", tag:"city-brief", location:"Colombo", author:"Origin Research Team", date:"April 2025", summary:"A structured overview of Colombo's development landscape — active donor programmes, urban infrastructure gaps, youth employment challenges, and key civil society actors operating in the Western Province." },
  { id:2, title:"Kandy Youth Employment Brief", category:"District Brief", tag:"district-brief", location:"Kandy, Central Province", author:"Priya Gunawardena", date:"March 2025", summary:"Analysis of youth unemployment in the Kandy district, drawing on Central Bank labour statistics, UNDP youth mapping data, and interviews with vocational training providers." },
  { id:3, title:"Jaffna Education & Livelihoods Brief", category:"City Brief", tag:"city-brief", location:"Jaffna, Northern Province", author:"Arjun Sivanesan", date:"March 2025", summary:"A district-level brief covering post-conflict education recovery, livelihoods programming in Northern Province, and an overview of active NGO and donor presence in Jaffna." },
  { id:4, title:"Girls' Education in Sri Lanka", category:"Sector Report", tag:"sector-report", location:"National", author:"Origin Research Team", date:"February 2025", summary:"A national sector review covering gender parity in education, secondary and tertiary enrolment trends, barriers to girls' retention in conflict-affected districts, and a mapping of funders active in this space." },
  { id:5, title:"Youth Employment in Sri Lanka", category:"Sector Report", tag:"sector-report", location:"National", author:"Nalini Weerasinghe", date:"February 2025", summary:"Structured analysis of youth NEET rates, skills gaps identified by employers, TVET effectiveness, and a summary of multilateral and bilateral programmes targeting youth employment nationally." },
  { id:6, title:"Donor Funding Landscape for Sri Lankan NGOs", category:"Donor Brief", tag:"donor-brief", location:"National", author:"Origin Research Team", date:"January 2025", summary:"An overview of the active donor landscape for Sri Lankan civil society — covering FCDO, EU, USAID, GIZ, ADB, and UN agencies. Includes funding trends, sector priorities, and upcoming call windows." },
  { id:7, title:"Bangladesh Climate Resilience Funding Brief", category:"Sector Report", tag:"sector-report", location:"National", author:"Origin Research Team", date:"May 2025", summary:"An analysis of active donor funding for climate adaptation and resilience programmes in Bangladesh — covering UNDP, USAID, BRAC, and emerging green finance mechanisms targeting coastal communities and agricultural systems." },
  { id:8, title:"Kerala Women's SME Development Brief", category:"District Brief", tag:"district-brief", location:"Kerala, India", author:"Priya Nair", date:"April 2025", summary:"A district-level analysis of women-led small and medium enterprise development in Kerala, drawing on NABARD data, SHG programme outcomes, and an overview of state and national funding mechanisms available to women entrepreneurs." },
  { id:9, title:"Nepal Post-Earthquake Livelihoods Brief", category:"District Brief", tag:"district-brief", location:"Sindhupalchok & Gorkha, Nepal", author:"Rohan Gurung", date:"March 2025", summary:"A structured review of livelihoods recovery programming in Nepal's most earthquake-affected districts — covering ADB, World Bank, and bilateral donor activity, agricultural and construction sector recovery, and remaining gaps in the humanitarian-development transition." },
];

const MOCK_ORGS = [
  { id:1, name:"Galle Women's Cooperative", location:"Galle", sector:"Women's Empowerment", type:"NGO", founded:2018, desc:"Supports women-led micro-enterprises in the Southern Province through skills training, market linkages, and access to finance.", lat:6.0535, lng:80.2210 },
  { id:2, name:"Jaffna Youth Development Centre", location:"Jaffna", sector:"Youth Employment", type:"NGO", founded:2015, desc:"Vocational and digital skills training for youth aged 18–30 in the Northern Province, with placement partnerships across Colombo's tech sector.", lat:9.6615, lng:80.0255 },
  { id:3, name:"Colombo Urban Resilience Project", location:"Colombo", sector:"Climate Resilience", type:"Programme", founded:2021, desc:"EU-funded programme addressing urban flood risk, waste management, and green infrastructure in Colombo's underserved communities.", lat:6.9271, lng:79.8612 },
  { id:4, name:"Kandy Heritage Livelihoods Trust", location:"Kandy", sector:"Livelihoods", type:"NGO", founded:2012, desc:"Preserves traditional craft skills and creates livelihoods pathways for artisans in the Central Province through tourism linkages and export markets.", lat:7.2906, lng:80.6337 },
  { id:5, name:"Batticaloa Education Collective", location:"Batticaloa", sector:"Education", type:"NGO", founded:2019, desc:"Community-led schools support programme improving learning outcomes for Tamil-medium students in the Eastern Province, including teacher training and resource provision.", lat:7.7170, lng:81.7000 },
  { id:6, name:"Trinco Fisheries Development Fund", location:"Trincomalee", sector:"Livelihoods", type:"Programme", founded:2020, desc:"ADB-supported programme strengthening fishing community resilience, cold chain infrastructure, and market access for fishing households in Trincomalee.", lat:8.5874, lng:81.2152 },
];

const TRAINING_MODULES = [
  { num:"01", title:"Introduction to Origin Advisory", desc:"Platform overview, mission, and how Origin's research model works. Understand the relationship between AI tools, human review, and published outputs." },
  { num:"02", title:"How to Write an Origin Brief", desc:"Structure, length, tone, and quality standards. Learn the Origin format — from city briefs to sector reports — and study published examples." },
  { num:"03", title:"Research and Sources", desc:"Finding credible data: multilateral databases, government portals, NGO reports, academic literature. Citation standards and source verification." },
  { num:"04", title:"Development Themes", desc:"Core concepts in international development — HDI, SDGs, livelihoods, governance, education, gender, and climate — applied to South Asian contexts." },
  { num:"05", title:"AI and Research Ethics", desc:"How AI tools assist (not replace) research. Bias, hallucination, attribution, and the ethical responsibilities of a human reviewer." },
  { num:"06", title:"Submission and Review Process", desc:"How to submit a draft, what the review process looks like, and how feedback is incorporated before publication. Timeline expectations." },
];

const PILOT_OPTIONS = [
  { title:"Donor Mapping Sprint", what:"A focused scan of funders active in your sector and geography, structured as a 2-page report with priority recommendations.", output:"Donor landscape brief (PDF)", timeline:"5–7 working days" },
  { title:"Funding Readiness Scan", what:"AI-assisted assessment of your organisation's funding readiness, with a structured report on strengths, gaps, and recommended next steps.", output:"Readiness report + action checklist", timeline:"3–5 working days" },
  { title:"Impact Summary", what:"A structured one-page summary of your programme's outcomes and impact — written for donor audiences.", output:"Impact summary document", timeline:"3–5 working days" },
  { title:"M&E Starter Framework", what:"A simple monitoring and evaluation framework tailored to your programme — with indicators, data sources, and reporting cadence.", output:"M&E framework (1–2 pages)", timeline:"5–7 working days" },
  { title:"NGO Map Profile", what:"A verified profile page for your organisation on Origin's Development Map — visible to donors, researchers, and the public.", output:"Map listing + profile card", timeline:"2–3 working days" },
];

const ADVISORY_CARDS = [
  { title:"Grant Readiness Pack", who:"NGOs and charities approaching funders for the first time", what:"Organisation summary, programme narrative, theory of change draft, impact evidence review, funder eligibility checklist", cta:"Request this pack" },
  { title:"Donor Mapping Pack", who:"Organisations with a specific sector or geography ready to approach funders", what:"Shortlist of 8–12 matched funders, application windows, contact guidance, fit assessment for each", cta:"Request this pack" },
  { title:"Impact Summary", who:"Organisations needing a clear, donor-ready summary of their outcomes", what:"A one-page structured impact document with data, narrative, and SDG alignment — ready to send to funders", cta:"Request this pack" },
  { title:"M&E Starter Framework", who:"Programmes without formal monitoring in place", what:"Custom indicator set, data collection plan, simple reporting template aligned to donor expectations", cta:"Request this pack" },
  { title:"Funding Strategy", who:"Organisations planning 12–24 months of fundraising", what:"Prioritised funding pipeline, sector positioning advice, 12-month outreach plan, diversification recommendations", cta:"Request this pack" },
  { title:"Research Brief", who:"NGOs, academics, or donors needing structured intelligence on a location or sector", what:"A 3–5 page Origin brief — cited, structured, and ready to use in reports, pitches, or donor submissions", cta:"Request this pack" },
];

const CHAT_PROMPTS = [
  "What documents do I need before approaching donors?",
  "What indicators should I track for a livelihoods project?",
  "Find NGOs working in education in Jaffna",
  "How do I improve my funding readiness score?",
];

const CHAT_RESPONSES = {
  "What documents do I need before approaching donors?": "Before approaching most institutional donors, you'll typically need:\n\n**Core documents:**\n• Registration certificate (with your country's NGO/charity regulator)\n• Audited accounts for the last 2–3 years\n• Board list with brief bios\n• Safeguarding policy\n• Organisational chart\n\n**Programme documents:**\n• Theory of change or logical framework\n• Programme narrative (3–5 pages)\n• Budget breakdown and financial controls policy\n• Impact evidence — data, case studies, or evaluations\n\n**Optional but strengthening:**\n• Annual report\n• Existing donor references or endorsement letters\n\nOrigin's Grant Readiness Pack helps you prepare all of these in formats matched to specific funders.",
  "What indicators should I track for a livelihoods project?": "Standard indicators for livelihoods programmes, aligned to FCDO and UN agency frameworks:\n\n**Output indicators:**\n• Number of beneficiaries trained / supported\n• % female / youth / marginalised group\n• Number of businesses or cooperatives formed\n\n**Outcome indicators:**\n• % reporting increased income (vs baseline)\n• % with new or improved employment (3/6 month follow-up)\n• Household food security score change (HDDS or FIES)\n• Access to savings mechanisms or financial services\n\n**Impact indicators:**\n• Poverty headcount change (if data allows)\n• SDG 1 and SDG 8 alignment\n\nFor a simple M&E framework tailored to your programme, Origin can produce a starter pack in 5–7 working days.",
  "Find NGOs working in education in Jaffna": "**NGOs active in education in Jaffna (Northern Province):**\n\n1. **Jaffna Education Collective** — teacher training, Tamil-medium curriculum support, secondary retention programmes\n2. **Save the Children Sri Lanka** — early childhood development, school feeding, PSS integration\n3. **NRC Sri Lanka** — education in emergencies (transitional), catch-up learning for over-age students\n4. **UNICEF Northern Province** — WASH in schools, school safety, girls' education\n5. **Sevalanka Foundation** — vocational training and livelihood-linked education\n\n*This is a sample from Origin's development map. A full Jaffna education sector brief is available, including funding flows and open programme opportunities.*",
  "How do I improve my funding readiness score?": "Your funding readiness score reflects how prepared your organisation is to successfully apply to and manage grants from institutional donors. To improve it:\n\n**Governance (high impact):**\n• Ensure audited accounts are up to date and filed\n• Maintain an active, diverse board with clear terms of reference\n• Have a documented safeguarding policy in place\n\n**Programme quality:**\n• Develop a simple theory of change or results chain\n• Collect baseline data before programmes start\n• Document outcomes with real numbers, not just activity counts\n\n**Communication:**\n• Produce a clear 1-page organisational summary\n• Have a programme narrative ready in English (even if you operate in another language)\n\n**Quick wins:**\n• Register with the UN Partner Portal (Gateway)\n• Create a Devex profile\n• Join BOND or your national INGO network\n\nOrigin's Funding Readiness Scan gives you a personalised scorecard with specific, prioritised actions.",
};
function getRoute() {
  const h = window.location.hash;
  if (!h || h === "#" || h === "#/") return "/";
  return h.startsWith("#") ? h.slice(1) : h;
}

function useRoute() {
  const [route, setRoute] = useState(getRoute);
  useEffect(() => {
    const handler = () => { setRoute(getRoute()); window.scrollTo(0, 0); };
    window.addEventListener("hashchange", handler);
    return () => window.removeEventListener("hashchange", handler);
  }, []);
  return route;
}

// ── Utilities ──────────────────────────────────────────────────────────────
function useReveal() {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const obs = new IntersectionObserver(
      entries => entries.forEach(e => { if (e.isIntersecting) { el.classList.add("in"); obs.unobserve(el); } }),
      { threshold: 0.06 }
    );
    obs.observe(el);
    return () => obs.disconnect();
  }, []);
  return ref;
}

function Reveal({ children, className = "", as: As = "div" }) {
  const ref = useReveal();
  return <As ref={ref} className={`reveal ${className}`}>{children}</As>;
}

// ── Country entity helpers ─────────────────────────────────────────────────
const KNOWN_COUNTRIES = [
  "Democratic Republic of Congo","Trinidad and Tobago","Dominican Republic",
  "United Arab Emirates","United Kingdom","South Korea","North Korea",
  "South Sudan","South Africa","Saudi Arabia","East Timor","Ivory Coast",
  "Burkina Faso","Sri Lanka","Costa Rica","El Salvador","Hong Kong",
  "Afghanistan","Bangladesh","Maldives","Malaysia","Indonesia","Singapore",
  "Philippines","Mongolia","Cambodia","Maldives","Palestine","Lebanon",
  "Morocco","Algeria","Tunisia","Zimbabwe","Tanzania","Ethiopia",
  "Cameroon","Mozambique","Somalia","Senegal","Bolivia","Ecuador",
  "Venezuela","Guatemala","Honduras","Nicaragua","Paraguay","Uruguay",
  "Armenia","Azerbaijan","Georgia","Ukraine","Serbia","Poland",
  "Turkey","Jordan","Cyprus","Kuwait","Bahrain","Qatar","Oman","Yemen",
  "Syria","Iraq","Iran","Israel","Egypt","Libya","Sudan","Angola",
  "Zambia","Uganda","Rwanda","Ghana","Guinea","Niger","Chad","Mali",
  "Kenya","Nigeria","Jamaica","Panama","Haiti","Colombia","Chile",
  "Argentina","Mexico","Peru","Brazil","Vietnam","Thailand","Myanmar",
  "Laos","Brunei","Taiwan","Japan","China","India","Nepal","Pakistan",
  "Bhutan","Russia","Germany","France","Greece","Indonesia","Uganda",
];

function renderWithCountryLinks(text, onCountryTap) {
  if (!text || !onCountryTap) return text;
  const sorted = [...KNOWN_COUNTRIES].sort((a, b) => b.length - a.length);
  const escaped = sorted.map(c => c.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
  const regex = new RegExp(`\\b(${escaped.join('|')})\\b`, 'g');
  const parts = [];
  let last = 0, match;
  while ((match = regex.exec(text)) !== null) {
    if (match.index > last) parts.push(text.slice(last, match.index));
    const country = match[0];
    parts.push(
      React.createElement('span', {
        key: match.index,
        className: 'em-country',
        onClick: e => { e.stopPropagation(); onCountryTap(country); },
      }, country)
    );
    last = match.index + match[0].length;
  }
  if (last < text.length) parts.push(text.slice(last));
  return parts.length > 0 ? parts : text;
}

// ── Country Sheet ──────────────────────────────────────────────────────────
function CountrySheet({ country, articles, onClose }) {
  const recentStories = (articles || [])
    .filter(a => a.country === country)
    .sort((a, b) => new Date(b.published) - new Date(a.published))
    .slice(0, 5);

  const [followed, setFollowed] = useState(false);

  const handleFollow = () => {
    const prefs = loadFeedPrefs();
    const weights = { ...(prefs.tagWeights || {}) };
    const tag = `country:${country}`;
    weights[tag] = Math.min(10, (weights[tag] || 0) + 0.4);
    saveFeedPrefs({ ...prefs, tagWeights: weights });
    setFollowed(true);
  };

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

  return (
    <div className="country-sheet-backdrop" onClick={onClose}>
      <div className="country-sheet" onClick={e => e.stopPropagation()}>
        <div className="country-sheet-header">
          <span className="country-sheet-name">{country}</span>
          <button className="country-sheet-close" onClick={onClose} aria-label="Close">
            <svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <path d="M1 1 L11 11 M11 1 L1 11" />
            </svg>
          </button>
        </div>
        <button
          className={`country-sheet-follow${followed ? ' followed' : ''}`}
          onClick={handleFollow}
          disabled={followed}
        >
          {followed ? `Following ${country}` : `Follow ${country}`}
        </button>
        {recentStories.length > 0 && (
          <div className="country-sheet-stories">
            <div className="country-sheet-stories-label">Recent stories</div>
            {recentStories.map(a => (
              <div key={a.id} className="country-sheet-story">
                <div className="country-sheet-story-title">{a.title}</div>
                <div className="country-sheet-story-meta">{relativeTime(a.published)}</div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
}

function relativeTime(isoStr) {
  if (!isoStr) return "";
  const diff = Date.now() - new Date(isoStr).getTime();
  const mins = Math.round(diff / 60000);
  if (mins < 60) return `${mins}m ago`;
  const hrs = Math.round(diff / 3600000);
  if (hrs < 24) return `${hrs}h ago`;
  return `${Math.round(diff / 86400000)}d ago`;
}

function colorToHex(c) {
  return { red: "#e53e3e", orange: "#C8531C", yellow: "#d69e2e", green: "#38a169" }[c] || "#C8531C";
}

// Small inline SVGs
const TierIcon = ({ type }) => {
  if (type === "individual") return (
    <svg className="tier-icon" viewBox="0 0 36 36" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="18" cy="12" r="6" />
      <path d="M5 32 C5 23 31 23 31 32" />
    </svg>
  );
  if (type === "organisation") return (
    <svg className="tier-icon" viewBox="0 0 36 36" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="18" cy="10" r="5" />
      <path d="M6 30 C6 23 14 23 14 30" />
      <path d="M22 30 C22 23 30 23 30 30" />
      <path d="M11 30 C11 24 25 24 25 30" />
    </svg>
  );
  return (
    <svg className="tier-icon" viewBox="0 0 36 36" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
      <rect x="4" y="18" width="28" height="14" />
      <path d="M10 18 V12 H26 V18" />
      <rect x="13" y="5" width="10" height="7" />
      <path d="M12 24 V30 M18 24 V30 M24 24 V30" />
    </svg>
  );
};

const Check = () => (
  <svg className="tier-feature-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
    <path d="M2 8 L6 12 L14 4" />
  </svg>
);

function LiveIndicator({ count, national, citySpecific, activeFilter, lastUpdated }) {
  const suffix =
    activeFilter === '24h'  ? ' — LAST 24H' :
    activeFilter === 'high' ? ' — HIGH SIGNIFICANCE' : '';
  return (
    <div className="live-indicator">
      <span className="live-dot" />
      <span className="live-label">LIVE</span>
      <span className="live-sep">·</span>
      <span className="live-count">{count} ARTICLES{suffix}</span>
      <span className="live-sep">·</span>
      <span className="live-count">{national} NATIONAL</span>
      <span className="live-sep">·</span>
      <span className="live-count">{citySpecific} CITY-SPECIFIC</span>
      <span className="live-sep">·</span>
      <span className="live-time">UPDATED {relativeTime(lastUpdated).toUpperCase()}</span>
    </div>
  );
}

// ── Nav items ──────────────────────────────────────────────────────────────
const NAV_ITEMS = [
  { label: "Map",          href: "#/"             },
  { label: "Edition",      href: "#/"             },
  { label: "Archive",      href: "#/"             },
  { label: "Intelligence", href: "#/intelligence" },
  { label: "Contact",      href: "#/contact"      },
];

// ── Layout ─────────────────────────────────────────────────────────────────
function SideRail({ onMenuOpen }) {
  return (
    <div className="side-rail">
      <button className="hamburger" onClick={onMenuOpen} aria-label="Open navigation menu">
        <span /><span /><span />
      </button>
      <span className="side-rail-text" aria-hidden="true">ORIGIN ADVISORY</span>
    </div>
  );
}

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

  return (
    <div className={`menu-overlay ${open ? "open" : ""}`} onClick={onClose}>
      <div className="menu-inner" onClick={e => e.stopPropagation()}>
        <button className="menu-close" onClick={onClose} aria-label="Close menu">
          <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round">
            <path d="M2 2 L16 16 M16 2 L2 16" />
          </svg>
        </button>
        <nav className="menu-nav" aria-label="Primary navigation">
          {NAV_ITEMS.map((item, i) => (
            <a key={item.label} href={item.href} className="menu-link"
              style={{ transitionDelay: open ? `${80 + i * 55}ms` : "0ms" }} onClick={onClose}>
              <span className="menu-num">0{i + 1}</span>
              {item.label}
            </a>
          ))}
        </nav>
        <div className="menu-foot">
          <a href="mailto:hello@originadvisory.com" className="menu-email">hello@originadvisory.com</a>
        </div>
      </div>
    </div>
  );
}

function Nav({ route }) {
  const [scrolled, setScrolled] = useState(false);
  const [overHero, setOverHero] = useState(route === "/");
  useEffect(() => {
    const onScroll = () => {
      setScrolled(window.scrollY > 24);
      setOverHero(route === "/" && window.scrollY < window.innerHeight * 0.75);
    };
    window.addEventListener("scroll", onScroll);
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, [route]);
  return (
    <nav className={`nav ${scrolled ? "scrolled" : ""} ${overHero && !scrolled ? "over-hero" : ""}`}>
      <div className="nav-inner">
        <a href="#/" className="brand">
          <img src="./assets:logo.webp" alt="" className="brand-logo" />
          <span className="brand-name">Origin Advisory</span>
        </a>
      </div>
    </nav>
  );
}

function Footer() {
  return (
    <footer className="footer">
      <div className="container">
        <div className="footer-grid">
          <div className="footer-brand">
            <span className="name">Origin Advisory</span>
            <p>AI-powered development intelligence across Asia, delivered every morning.</p>
          </div>
          <div className="footer-col">
            <h5>Navigate</h5>
            <ul>
              <li><a href="#/">Map</a></li>
              <li><a href="#/#edition">Today's Edition</a></li>
              <li><a href="#/feed">Your Feed</a></li>
              <li><a href="#/#archive">Archive</a></li>
              <li><a href="#/intelligence">Intelligence</a></li>
              <li><a href="#/contact">Contact</a></li>
            </ul>
          </div>
          <div className="footer-col">
            <h5>Contact</h5>
            <ul>
              <li><a href="mailto:hello@originadvisory.com">hello@originadvisory.com</a></li>
              <li><span>London, United Kingdom</span></li>
              <li><span>Colombo, Sri Lanka</span></li>
            </ul>
          </div>
        </div>
        <div className="footer-bottom">
          <span>© 2026 Origin Advisory. All rights reserved.</span>
          <span>Privacy · Terms</span>
        </div>
      </div>
    </footer>
  );
}

// ── Hero ───────────────────────────────────────────────────────────────────
function Hero({ articles = [] }) {
  const [idx, setIdx]           = useState(0);
  const [scrollY, setScrollY]   = useState(0);
  const [email, setEmail]       = useState('');
  const [submitted, setSubmitted] = useState(false);

  useEffect(() => {
    const id = setInterval(() => setIdx(i => (i + 1) % HERO_PHOTOS.length), 6000);
    return () => clearInterval(id);
  }, []);
  useEffect(() => {
    const onScroll = () => setScrollY(window.scrollY);
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, []);

  const topStory = freshEditionPool(articles)[0];

  const today = new Date().toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' });

  const handleSubmit = e => {
    e.preventDefault();
    if (email.trim()) setSubmitted(true);
  };

  return (
    <section className="hero">
      <div className="hero-bg">
        {HERO_PHOTOS.map((src, i) => (
          <div key={src} className={`hero-slide ${i === idx ? "active" : ""}`}
            style={{ backgroundImage: `url(${src})`, backgroundColor: FALLBACK_BG,
              transform: `translateY(${scrollY * 0.28}px) scale(${1 + scrollY * 0.00015})` }} />
        ))}
        <div className="hero-overlay" />
      </div>
      <div className="hero-content">
        <div className="container">
          {topStory && (
            <Reveal>
              <div className="hero-ticker">
                <span className="hero-ticker-label">LATEST</span>
                <span className="hero-ticker-text">{topStory.title}</span>
              </div>
            </Reveal>
          )}
          <Reveal><div className="hero-eyebrow"><span>{today}</span></div></Reveal>
          <Reveal>
            <h1>Asia's development story, <span className="italic-accent">this morning.</span></h1>
          </Reveal>
          <Reveal>
            <p className="hero-sub">
              AI-powered intelligence on Asia's funding flows, policy changes, and economic recovery — built for the people doing serious work in the region.
            </p>
          </Reveal>
          <Reveal>
            {submitted ? (
              <div className="hero-subscribed">
                <span className="hero-subscribed-check">✓</span>
                You're on the list. Look out for your first edition.
              </div>
            ) : (
              <form className="hero-subscribe" onSubmit={handleSubmit}>
                <input
                  type="email"
                  className="hero-subscribe-input"
                  placeholder="Your email address"
                  value={email}
                  onChange={e => setEmail(e.target.value)}
                  required
                />
                <button type="submit" className="hero-subscribe-btn">
                  Subscribe <span className="arr"><Icons.Arrow /></span>
                </button>
              </form>
            )}
            <p className="hero-sub-note">Free. No spam. Unsubscribe anytime.</p>
          </Reveal>
        </div>
      </div>
      <div className="hero-dots">
        {HERO_PHOTOS.map((_, i) => (
          <button key={i} className={`hero-dot ${i === idx ? "active" : ""}`}
            onClick={() => setIdx(i)} aria-label={`Go to slide ${i + 1}`} />
        ))}
      </div>
    </section>
  );
}

// ── Home: Intelligence CTA ─────────────────────────────────────────────────
function HomeIntelCTA() {
  return (
    <section className="section" style={{padding:"80px 0", background:"var(--ink)"}}>
      <div className="container about-text-centered">
        <Reveal>
          <span className="eyebrow" style={{color:"rgba(242,234,217,0.6)"}}>Origin Intelligence</span>
          <h2 style={{marginTop:16, color:"#fff", maxWidth:"36ch", margin:"16px auto"}}>
            Get a 60-second AI draft of your funding scan, donor shortlist, or project brief — then request a free human-reviewed version from our team.
          </h2>
          <a href="#/intelligence" className="btn btn-primary" style={{fontSize:"16px", padding:"16px 32px", marginTop:40, display:"inline-block"}}>
            Try Origin Intelligence <span className="arr"><Icons.Arrow /></span>
          </a>
          <p style={{fontSize:"13px", color:"rgba(242,234,217,0.45)", marginTop:14, letterSpacing:"0.01em"}}>
            Free human review during pilot phase. No credit card. No commitment.
          </p>
        </Reveal>
      </div>
    </section>
  );
}

// ── Today's Edition ────────────────────────────────────────────────────────
const EDITION_STREAK_KEY = 'origin_edition_streak';

function getEditionStreak() {
  try {
    const raw = localStorage.getItem(EDITION_STREAK_KEY);
    if (!raw) return 0;
    const { streak, lastDate } = JSON.parse(raw);
    const today = new Date().toDateString();
    const yesterday = new Date(Date.now() - 86400000).toDateString();
    if (lastDate === today) return streak;
    if (lastDate === yesterday) return streak; // will be bumped on render
    return 0; // streak broken
  } catch { return 0; }
}

function bumpEditionStreak() {
  try {
    const today = new Date().toDateString();
    const raw = localStorage.getItem(EDITION_STREAK_KEY);
    const prev = raw ? JSON.parse(raw) : null;
    if (prev?.lastDate === today) return prev.streak; // already counted today
    const yesterday = new Date(Date.now() - 86400000).toDateString();
    const streak = prev?.lastDate === yesterday ? (prev.streak || 0) + 1 : 1;
    localStorage.setItem(EDITION_STREAK_KEY, JSON.stringify({ streak, lastDate: today }));
    return streak;
  } catch { return 0; }
}

function editionGreeting() {
  const h = new Date().getHours();
  if (h < 12) return 'Good morning';
  if (h < 17) return 'Good afternoon';
  return 'Good evening';
}

function TodaysEdition({ articles, onArticleClick, lastUpdated, editionSections }) {
  const top5 = freshEditionPool(articles || []).slice(0, 5);

  if (!top5.length) return null;

  const [lead, ...rest] = top5;
  const today = new Date().toLocaleDateString('en-GB', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
  const sigLabel = { 4: 'Critical', 3: 'Significant', 2: 'Moderate', 1: 'Local' };
  const sigColor = { 4: '#e53e3e', 3: '#C8531C', 2: '#d69e2e', 1: '#38a169' };

  const streak = bumpEditionStreak();
  const greeting = editionGreeting();

  return (
    <section className="edition-section" id="edition">
      <div className="container">
        <div className="edition-masthead">
          <MiraBlockMark size={36} state="idle" />
          <div className="edition-masthead-left">
            <h2 className="edition-greeting">{greeting}</h2>
            <time className="edition-date">Your edition for {today}</time>
          </div>
          <div className="edition-masthead-meta">
            {streak > 0 && <span className="edition-streak">{streak}-day streak</span>}
            {lastUpdated && <span className="edition-updated">Updated {relativeTime(lastUpdated)}</span>}
          </div>
          <div className="edition-masthead-line" />
        </div>
        <div className="edition-grid">
          <div className="edition-lead" onClick={() => onArticleClick && onArticleClick(lead)}>
            <span className="edition-num">01</span>
            <div className="edition-lead-body">
              <div className="edition-lead-meta">
                <span className="edition-tag" style={{ color: sigColor[lead.significance] || sigColor[2] }}>
                  {sigLabel[lead.significance] || 'Moderate'}
                </span>
                <span className="edition-lead-region">{isNationalScope(lead) ? lead.country : lead.city}</span>
                {(() => {
                  const srcCount = 1 + (lead.sources?.length || 0);
                  if (srcCount < 2) return null;
                  const oldest = lead.sources?.[0]?.published;
                  return (
                    <span className="edition-coverage-badge">
                      {srcCount >= 4 && <span className="edition-big-story">Big story</span>}
                      {srcCount} sources{oldest ? ` · ${relativeTime(oldest)}` : ''}
                    </span>
                  );
                })()}
              </div>
              <h2 className="edition-lead-title">{lead.title}</h2>
              <p className="edition-lead-summary">{(lead.summary || '').slice(0, 220)}{(lead.summary || '').length > 220 ? '…' : ''}</p>
              <div className="edition-read">Read full story →</div>
            </div>
          </div>
          <div className="edition-sidebar">
            {rest.map((art, i) => {
              const srcCount = 1 + (art.sources?.length || 0);
              const oldest = art.sources?.[0]?.published;
              return (
              <div key={art.id} className="edition-item" onClick={() => onArticleClick && onArticleClick(art)}>
                <span className="edition-num edition-num-sm">{String(i + 2).padStart(2, '0')}</span>
                <div className="edition-item-body">
                  <div className="edition-item-meta">
                    <span className="edition-tag" style={{ color: sigColor[art.significance] || sigColor[2] }}>
                      {sigLabel[art.significance] || 'Moderate'}
                    </span>
                    <span className="edition-item-region">{isNationalScope(art) ? art.country : art.city}</span>
                    <span className="edition-item-time">{relativeTime(art.published)}</span>
                  </div>
                  <div className="edition-item-title-row">
                    <h4 className="edition-item-title">{art.title}</h4>
                    {srcCount >= 2 && (
                      <span className="edition-coverage-badge edition-coverage-badge-sm">
                        {srcCount >= 4 && <span className="edition-big-story">Big story</span>}
                        {srcCount} sources{oldest ? ` · ${relativeTime(oldest)}` : ''}
                      </span>
                    )}
                  </div>
                </div>
              </div>
              );
            })}
            <div className="edition-see-all">
              <a href="#/" className="btn btn-outline" style={{ fontSize: '10px' }}>
                View all stories on the map <span className="arr"><Icons.Arrow /></span>
              </a>
            </div>
          </div>
        </div>
        {editionSections && editionSections.length > 0 && (
          <div className="edition-sections">
            {editionSections.map((sec, si) => {
              const secArts = (articles || []).filter(a => sec.story_ids?.includes(a.id));
              if (!secArts.length) return null;
              return (
                <div key={si} className="edition-named-section">
                  <div className="edition-named-section-head">
                    <span className="edition-named-section-title">{sec.title}</span>
                    {sec.subtitle && <span className="edition-named-section-sub">{sec.subtitle}</span>}
                  </div>
                  <div className="edition-named-section-list">
                    {secArts.map(art => (
                      <div key={art.id} className="edition-named-item" onClick={() => onArticleClick && onArticleClick(art)}>
                        <div className="edition-named-item-region">{isNationalScope(art) ? art.country : art.city}</div>
                        <div className="edition-named-item-title">{art.title}</div>
                      </div>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </div>
    </section>
  );
}

// ── Home: Archive ──────────────────────────────────────────────────────────
const ARCHIVE_EDITIONS = [
  { date: "8 May 2026",  headline: "Bangladesh factory output contracts; Nepal hits remittance record",       countries: "BD, NP",     count: 14 },
  { date: "7 May 2026",  headline: "Pakistan IMF tranche approved; Sri Lanka tourism arrivals up 23%",        countries: "PK, LK",     count: 11 },
  { date: "6 May 2026",  headline: "India-China border talks resume; Myanmar ceasefire holding by thread",    countries: "IN, CN, MM",  count: 16 },
  { date: "5 May 2026",  headline: "ADB launches $400M climate adaptation fund for South Asia",               countries: "Regional",    count: 9  },
  { date: "2 May 2026",  headline: "Afghanistan aid pipeline under review; Tajikistan joins SCO framework",   countries: "AF, TJ",     count: 8  },
  { date: "1 May 2026",  headline: "Sri Lanka posts first fiscal surplus since crisis; Bhutan hydro deal signed", countries: "LK, BT", count: 12 },
  { date: "30 Apr 2026", headline: "Vietnam FDI inflows surge; Cambodia garment sector reform stalls",        countries: "VN, KH",     count: 10 },
];

function HomeArchive() {
  return (
    <section className="archive-section" id="archive">
      <div className="container">
        <div className="section-head" style={{ marginBottom: 0 }}>
          <Reveal>
            <span className="eyebrow">Past Editions</span>
            <h2 style={{ marginTop: 20 }}>The archive</h2>
            <p style={{ color: 'rgba(242,234,217,0.55)', fontSize: '16px', maxWidth: '52ch', marginTop: 16 }}>Every morning's intelligence, preserved.</p>
          </Reveal>
        </div>
        <div className="archive-grid">
          {ARCHIVE_EDITIONS.map((ed, i) => (
            <Reveal key={i} className="archive-card">
              <time className="archive-card-date">{ed.date}</time>
              <h4 className="archive-card-headline">{ed.headline}</h4>
              <div className="archive-card-footer">
                <span className="archive-card-meta">{ed.count} stories · {ed.countries}</span>
                <span className="archive-card-cta">Read edition →</span>
              </div>
            </Reveal>
          ))}
        </div>
        <Reveal>
          <div className="archive-subscribe-cta">
            <p>Don't miss tomorrow's edition.</p>
            <a href="#/" className="btn btn-primary" style={{ marginTop: 20, display: 'inline-flex' }}>
              Subscribe free <span className="arr"><Icons.Arrow /></span>
            </a>
          </div>
        </Reveal>
      </div>
    </section>
  );
}

// ── Floating AI Assistant ──────────────────────────────────────────────────
function FloatingAI() {
  const [open, setOpen]         = useState(false);
  const [selected, setSelected] = useState(null);
  const [userInput, setUserInput] = useState('');
  const [response, setResponse] = useState(null);
  const [thinking, setThinking] = useState(false);

  const handlePrompt = prompt => {
    setSelected(prompt);
    setThinking(true);
    setTimeout(() => {
      setResponse(CHAT_RESPONSES[prompt] || "Thanks for your question — our team will follow up shortly.");
      setThinking(false);
    }, 1200);
  };

  const handleSubmit = e => {
    e.preventDefault();
    if (userInput.trim()) { handlePrompt(userInput.trim()); setUserInput(''); }
  };

  const reset = () => { setSelected(null); setResponse(null); setThinking(false); setUserInput(''); };
  const close = () => { setOpen(false); reset(); };

  const renderBody = () => {
    if (!selected) return (
      <>
        <p className="fai-intro">Ask anything about development funding in South Asia.</p>
        <div className="fai-prompts">
          {CHAT_PROMPTS.map(p => (
            <button key={p} className="fai-prompt-btn" onClick={() => handlePrompt(p)}>{p}</button>
          ))}
        </div>
      </>
    );
    if (thinking) return (
      <div className="fai-thinking">
        <div className="fai-dots"><span /><span /><span /></div>
        <p>Searching Origin's database…</p>
      </div>
    );
    return (
      <div className="fai-result">
        <p className="fai-q">{selected}</p>
        <div className="fai-response">
          {(response || '').split('\n').map((line, i) => <p key={i}>{line}</p>)}
        </div>
        <button className="fai-reset-btn" onClick={reset}>← Ask another question</button>
      </div>
    );
  };

  return (
    <>
      <button className={`fai-toggle${open ? ' fai-open' : ''}`} onClick={() => setOpen(o => !o)}
        aria-label={open ? 'Close AI assistant' : 'Open AI assistant'}>
        {open
          ? <svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M2 2 L16 16 M16 2 L2 16" /></svg>
          : <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" /></svg>
        }
      </button>
      {open && (
        <div className="fai-card">
          <div className="fai-header">
            <div className="fai-header-left">
              <span className="fai-header-dot" />
              <span className="fai-header-title">Origin AI</span>
            </div>
            <button className="fai-close" onClick={close} aria-label="Close">
              <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M1 1 L13 13 M13 1 L1 13" /></svg>
            </button>
          </div>
          <div className="fai-body">{renderBody()}</div>
          {!selected && (
            <form className="fai-footer" onSubmit={handleSubmit}>
              <input type="text" className="fai-input" placeholder="Or type your question…"
                value={userInput} onChange={e => setUserInput(e.target.value)} />
              <button type="submit" className="fai-send" aria-label="Send">
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M2 8 L14 8 M10 4 L14 8 L10 12" /></svg>
              </button>
            </form>
          )}
        </div>
      )}
    </>
  );
}

// ── Map Section ────────────────────────────────────────────────────────────
function MapSection({ onRegionClick, mapData, onArticleClick, onListOpen, onNatModalOpen }) {
  const mapRef              = useRef(null);
  const mapInst             = useRef(null);
  const cbRef               = useRef(onRegionClick);
  const artCbRef            = useRef(onArticleClick);
  const listOpenRef         = useRef(onListOpen);
  const clusterRef          = useRef(null);
  const countryMarkersRef   = useRef(null);
  const applyLayersRef      = useRef(null);
  const countryLayerRef     = useRef(null);
  const admin1LayerRef      = useRef(null);
  const slLayerRef          = useRef(null);
  const lazyLayerCacheRef  = useRef({});
  const applyLazyRef       = useRef(null);
  const subnationalDataRef = useRef(null);
  const subnatSetterRef    = useRef(null);
  const [mapZoom, setMapZoom]         = useState(5);
  const [hintVisible, setHintVisible] = useState(true);
  const [subnatLoading, setSubnatLoading] = useState(null);
  const [subnatNote, setSubnatNote]       = useState(null);
  subnatSetterRef.current = setSubnatNote;
  useEffect(() => {
    if (!hintVisible) return;
    const t = setTimeout(() => setHintVisible(false), 5000);
    return () => clearTimeout(t);
  }, [hintVisible]);
  const [slData, setSlData]             = useState(null);
  const [worldData, setWorldData]       = useState(null);
  const [admin1Data, setAdmin1Data]     = useState(null);
  const [mapReady, setMapReady]         = useState(false);
  const [activeFilter, setActiveFilter] = useState('all');
  const [likedIds, setLikedIds] = useState(() => {
    try {
      return new Set(JSON.parse(localStorage.getItem('originadvisory_likes') || '[]').map(l => l.id));
    } catch { return new Set(); }
  });

  useEffect(() => {
    const onLike = () => {
      try {
        setLikedIds(new Set(JSON.parse(localStorage.getItem('originadvisory_likes') || '[]').map(l => l.id)));
      } catch {}
    };
    window.addEventListener('originadvisory:like', onLike);
    return () => window.removeEventListener('originadvisory:like', onLike);
  }, []);

  useEffect(() => { cbRef.current = onRegionClick; },    [onRegionClick]);
  useEffect(() => { artCbRef.current = onArticleClick; }, [onArticleClick]);
  useEffect(() => { listOpenRef.current = onListOpen; },  [onListOpen]);

  useEffect(() => {
    fetch("sri-lanka-provinces.geojson")
      .then(r => { if (!r.ok) throw new Error(); return r.json(); })
      .then(setSlData).catch(() => {});
  }, []);

  useEffect(() => {
    fetch("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson")
      .then(r => { if (!r.ok) throw new Error(); return r.json(); })
      .then(setWorldData).catch(() => {});
  }, []);

  useEffect(() => {
    fetch("south-asia-admin1.geojson")
      .then(r => { if (!r.ok) throw new Error(); return r.json(); })
      .then(setAdmin1Data).catch(() => {});
  }, []);

  // Effect 1: map + static HDI layers (only re-runs when GeoJSON data changes)
  useEffect(() => {
    if (!mapRef.current || !window.L || !slData || !worldData) return;
    if (mapInst.current) { mapInst.current.remove(); mapInst.current = null; }
    setMapReady(false);
    countryLayerRef.current = null;
    admin1LayerRef.current  = null;
    slLayerRef.current      = null;

    const map = window.L.map(mapRef.current, {
      scrollWheelZoom: true, touchZoom: true,
      zoomSnap: 0.25, zoomDelta: 0.5, wheelPxPerZoomLevel: 80,
      zoomControl: true, attributionControl: true, minZoom: 3, maxZoom: 12,
    });
    mapInst.current = map;

    // shapesPane sits below default markerPane (600) so dots always render on top
    map.createPane('shapesPane');
    map.getPane('shapesPane').style.zIndex = 399;

    window.L.tileLayer("https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png", {
      attribution: "&copy; OpenStreetMap &copy; CARTO", subdomains: "abcd", maxZoom: 19,
    }).addTo(map);

    // ── Style helpers ──────────────────────────────────────────────────────────
    const countryStyleFor = iso => {
      const hdi = iso && HDI_COUNTRIES[iso];
      if (hdi) {
        const col = hdiColor(hdi.value);
        return { fillColor: col.fill, fillOpacity: 0.6, color: col.fill, weight: 1.5, opacity: 1 };
      }
      return { fillColor: '#0A2540', fillOpacity: 0.04, color: '#0A2540', weight: 0.5, opacity: 0.18 };
    };

    const styleDistrict = feature => {
      const district = feature.properties.woe_name;
      const province = DISTRICT_TO_PROVINCE[district];
      const hdi      = province && HDI_LK_PROVINCES[province];
      const col      = hdi ? hdiColor(hdi.value) : { fill: '#94a3b8' };
      return { fillColor: col.fill, fillOpacity: 0.6, color: '#1a1a1a', weight: 1, opacity: 0.6 };
    };

    const getAdmin1HDI = feature => {
      const iso     = feature.properties.iso_a2;
      const rawName = feature.properties.name;
      const name    = iso === 'PK' ? (PK_NAME_MAP[rawName] || rawName) : rawName;
      let value;
      if      (iso === 'IN') value = HDI_IN_STATES[name];
      else if (iso === 'PK') value = HDI_PK_PROVINCES[name];
      else if (iso === 'BD') value = HDI_BD_DIVISIONS[name];
      else if (iso === 'NP') value = HDI_NP_ZONES[name];
      else if (iso === 'BT') value = HDI_BT_DZONGKHAGS[name];
      else if (iso === 'MV') value = HDI_MV_ATOLLS[name];
      if (value === undefined) { const c = HDI_COUNTRIES[iso]; value = c ? c.value : null; }
      return { name, iso, value };
    };

    const styleAdmin1 = feature => {
      const { value } = getAdmin1HDI(feature);
      const col = value ? hdiColor(value) : { fill: '#94a3b8' };
      return { fillColor: col.fill, fillOpacity: 0.6, color: '#1a1a1a', weight: 1, opacity: 0.6 };
    };

    // ── Country layer (world.geojson filtered to Asia) ────────────────────────
    const asiaFeatures = worldData.features.filter(f => COUNTRY_TO_ISO[f.properties.name]);
    const countryLayer = window.L.geoJSON({ type: "FeatureCollection", features: asiaFeatures }, {
      pane: 'shapesPane',
      interactive: true,
      style: feature => countryStyleFor(COUNTRY_TO_ISO[feature.properties.name]),
      onEachFeature: (feature, lyr) => {
        const iso  = COUNTRY_TO_ISO[feature.properties.name];
        const hdi  = iso && HDI_COUNTRIES[iso];
        const name = feature.properties.name;
        if (hdi) {
          const col  = hdiColor(hdi.value);
          const html = `<div class="hdi-tt"><b>${name}</b><br>HDI: ${hdi.value.toFixed(3)}<br><span class="hdi-tt-tier" style="color:${col.fill}">${hdiTierLabel(col.tier)}</span><br><span class="hdi-tt-rank">${hdi.source}</span></div>`;
          lyr.bindTooltip(html, { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 });
        } else {
          lyr.bindTooltip(name, { className: 'region-tooltip', direction: 'top', sticky: true, opacity: 1 });
        }
        lyr.on('mouseover', () => { const s = countryStyleFor(iso); lyr.setStyle({ ...s, fillOpacity: 0.88, weight: 2 }); });
        lyr.on('mouseout',  () => lyr.setStyle(countryStyleFor(iso)));
      },
    });
    countryLayerRef.current = countryLayer;

    // ── Admin1 layer (states/provinces for IN, PK, BD, NP, BT, MV) ───────────
    let admin1Layer = null;
    if (admin1Data) {
      admin1Layer = window.L.geoJSON(admin1Data, {
        pane: 'shapesPane',
        style: styleAdmin1,
        onEachFeature: (feature, lyr) => {
          const { name, iso, value } = getAdmin1HDI(feature);
          const country = ISO_COUNTRY[iso] || iso;
          if (value) {
            const col  = hdiColor(value);
            const html = `<div class="hdi-tt"><b>${name}</b><br><span class="hdi-tt-country">${country}</span>HDI: ${value.toFixed(3)}<br><span class="hdi-tt-tier" style="color:${col.fill}">${hdiTierLabel(col.tier)}</span></div>`;
            lyr.bindTooltip(html, { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 });
          } else {
            lyr.bindTooltip(`<div class="hdi-tt"><b>${name}</b><br><span class="hdi-tt-country">${country}</span></div>`, { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 });
          }
          lyr.on('mouseover', () => { const s = styleAdmin1(feature); lyr.setStyle({ ...s, fillOpacity: 0.88, weight: 2, color: '#000000', opacity: 0.85 }); });
          lyr.on('mouseout',  () => lyr.setStyle(styleAdmin1(feature)));
        },
      });
      admin1LayerRef.current = admin1Layer;
    }

    // ── Sri Lanka district layer (sri-lanka-provinces.geojson) ────────────────
    const slLayer = window.L.geoJSON(slData, {
      pane: 'shapesPane',
      style: styleDistrict,
      onEachFeature: (feature, lyr) => {
        const district = feature.properties.woe_name;
        const province = DISTRICT_TO_PROVINCE[district];
        const hdi      = province && HDI_LK_PROVINCES[province];
        const provData = REGION_DATA[district];
        if (hdi) {
          const col  = hdiColor(hdi.value);
          const html = `<div class="hdi-tt"><b>${district}</b><br><span class="hdi-tt-country">Sri Lanka · ${province} Province</span>HDI: ${hdi.value.toFixed(3)}<br><span class="hdi-tt-tier" style="color:${col.fill}">${hdiTierLabel(col.tier)}</span></div>`;
          lyr.bindTooltip(html, { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 });
        } else {
          lyr.bindTooltip(`<div class="hdi-tt"><b>${district}</b><br><span class="hdi-tt-country">Sri Lanka</span></div>`, { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 });
        }
        lyr.on('mouseover', () => { const s = styleDistrict(feature); lyr.setStyle({ ...s, fillOpacity: 0.88, weight: 2.2, color: '#000000', opacity: 0.85 }); });
        lyr.on('mouseout',  () => lyr.setStyle(styleDistrict(feature)));
        if (provData) lyr.on('click', () => cbRef.current(provData.modal));
      },
    });
    slLayerRef.current = slLayer;

    // ── Zoom-based layer switching ─────────────────────────────────────────────
    // Country fill is ALWAYS visible as a base layer at every zoom level.
    // At zoom ≥ 6, sub-national layers (admin1, SL districts) render on top.
    const ZOOM_THRESHOLD = 6;
    const applyZoom = () => {
      const z = map.getZoom();
      // Country base layer — always present
      if (!map.hasLayer(countryLayer)) countryLayer.addTo(map);
      if (z < ZOOM_THRESHOLD) {
        if (admin1Layer && map.hasLayer(admin1Layer)) map.removeLayer(admin1Layer);
        if (map.hasLayer(slLayer))                   map.removeLayer(slLayer);
      } else {
        if (admin1Layer && !map.hasLayer(admin1Layer)) admin1Layer.addTo(map);
        if (!map.hasLayer(slLayer))                   slLayer.addTo(map);
      }
    };
    map.on('zoomend', () => {
      const z = map.getZoom();
      setMapZoom(z);
      applyZoom();
      if (applyLayersRef.current) applyLayersRef.current(z);
    });

    map.fitBounds([[-10, 25], [55, 145]], { padding: [24, 24] });
    applyZoom();
    setMapZoom(map.getZoom());
    setMapReady(true);

    return () => { if (mapInst.current) { mapInst.current.remove(); mapInst.current = null; } setMapReady(false); };
  }, [slData, worldData, admin1Data]);

  // Effect 2: article markers (re-runs only when filter or data changes — no map rebuild)
  useEffect(() => {
    if (!mapReady || !mapInst.current || !window.L || !window.L.markerClusterGroup) return;
    if (clusterRef.current)        { mapInst.current.removeLayer(clusterRef.current);      clusterRef.current = null; }
    if (countryMarkersRef.current) { mapInst.current.removeLayer(countryMarkersRef.current); countryMarkersRef.current = null; }

    const all = mapData?.articles || [];
    const now = Date.now();
    const likedIdsSnap = (() => {
      try { return new Set(JSON.parse(localStorage.getItem('originadvisory_likes') || '[]').map(l => l.id)); }
      catch { return new Set(); }
    })();
    const filtered =
      activeFilter === '24h'   ? all.filter(a => now - new Date(a.published).getTime() < 86400000) :
      activeFilter === 'high'  ? all.filter(a => a.significance >= 3) :
      activeFilter === 'liked' ? all.filter(a => likedIdsSnap.has(a.url || a.title)) : all;
    const isLikedView = activeFilter === 'liked';

    // ── City-specific cluster (zoom ≥ 7) ──────────────────────────────────────
    const SIG_COLOURS = { 4: '#e53e3e', 3: '#C8531C', 2: '#d69e2e', 1: '#38a169' };
    const cluster = window.L.markerClusterGroup({
      iconCreateFunction: c => {
        if (isLikedView) return window.L.divIcon({
          html: `<div class="cluster-bubble" style="background:#E8526C;box-shadow:0 3px 12px #E8526C66;">${c.getChildCount()}</div>`,
          className: '', iconSize: null,
        });
        const maxSig = c.getAllChildMarkers().reduce((mx, m) => Math.max(mx, m.options.significance || 1), 1);
        const bg = SIG_COLOURS[maxSig] || '#C8531C';
        return window.L.divIcon({
          html: `<div class="cluster-bubble" style="background:${bg};box-shadow:0 3px 12px ${bg}66;">${c.getChildCount()}</div>`,
          className: '', iconSize: null,
        });
      },
      spiderfyOnMaxZoom: false,
      showCoverageOnHover: false,
      zoomToBoundsOnClick: true,
      maxClusterRadius: 50,
    });
    filtered.filter(a => !isNationalScope(a) && a.lat && a.lon).forEach(art => {
      const marker = window.L.marker([art.lat, art.lon], { icon: isLikedView ? makeLikedIcon() : makeArtIcon(art), significance: art.significance });
      marker.bindTooltip(art.title, { className: 'region-tooltip', direction: 'top', sticky: true, opacity: 1 });
      marker.on('click', () => { artCbRef.current && artCbRef.current(art); });
      cluster.addLayer(marker);
    });
    clusterRef.current = cluster;

    // ── Country aggregate markers (zoom < 7) ──────────────────────────────────
    const byCountry = {};
    filtered.forEach(a => {
      if (!a.country) return;
      if (!byCountry[a.country]) byCountry[a.country] = [];
      byCountry[a.country].push(a);
    });
    const cmGroup = window.L.layerGroup();
    Object.entries(byCountry).forEach(([country, arts]) => {
      const cap = COUNTRY_CAPITALS[country];
      if (!cap) return;
      const m = window.L.marker(cap, { icon: makeCountryAggIcon(arts.length) });
      m.bindTooltip(`<b>${country}</b>: ${arts.length} articles tracked`, { className: 'region-tooltip', direction: 'top', sticky: true, opacity: 1 });
      m.on('click', () => { listOpenRef.current && listOpenRef.current({ title: country, articles: arts }); });
      cmGroup.addLayer(m);
    });
    countryMarkersRef.current = cmGroup;

    // ── Apply correct layers for current zoom ─────────────────────────────────
    const applyLayers = z => {
      if (!mapInst.current) return;
      if (z < ARTICLE_ZOOM_THRESHOLD) {
        if (clusterRef.current && mapInst.current.hasLayer(clusterRef.current))
          mapInst.current.removeLayer(clusterRef.current);
        if (countryMarkersRef.current && !mapInst.current.hasLayer(countryMarkersRef.current))
          countryMarkersRef.current.addTo(mapInst.current);
      } else {
        if (countryMarkersRef.current && mapInst.current.hasLayer(countryMarkersRef.current))
          mapInst.current.removeLayer(countryMarkersRef.current);
        if (clusterRef.current && !mapInst.current.hasLayer(clusterRef.current))
          clusterRef.current.addTo(mapInst.current);
      }
    };
    applyLayersRef.current = applyLayers;
    applyLayers(mapInst.current.getZoom());
  }, [mapReady, mapData, activeFilter, likedIds]);

  // Effect 3: lazy-load sub-national GeoJSON layers for LAZY_SUBNAT countries
  useEffect(() => {
    if (!mapReady || !mapInst.current || !window.L) return;
    const map = mapInst.current;
    lazyLayerCacheRef.current = {};

    const loadCountry = iso2 => {
      if (lazyLayerCacheRef.current[iso2] !== undefined) return;
      lazyLayerCacheRef.current[iso2] = null;
      const { iso3, name: countryName } = LAZY_SUBNAT[iso2];
      const hdiData = SUBNAT_HDI[iso2] || {};
      fetch(`data/geojson/subnational/${iso3}.geojson`)
        .then(r => r.ok ? r.json() : Promise.reject())
        .then(geoJson => {
          const styleFor = name => {
            const v = hdiData[name];
            const c = v != null ? hdiColor(v) : { fill: '#94a3b8' };
            return { fillColor: c.fill, fillOpacity: 0.72, color: '#1a1a1a', weight: 1, opacity: 0.6 };
          };
          const layer = window.L.geoJSON(geoJson, {
            pane: 'shapesPane',
            style: feat => styleFor((feat.properties.shapeName || '').trim()),
            onEachFeature: (feat, lyr) => {
              const name = (feat.properties.shapeName || '').trim();
              lyr.on('mouseover', () => lyr.setStyle({ ...styleFor(name), fillOpacity: 0.88, weight: 2, color: '#000000', opacity: 0.85 }));
              lyr.on('mouseout',  () => lyr.setStyle(styleFor(name)));
              const v = hdiData[name];
              if (v != null) {
                const col = hdiColor(v);
                lyr.bindTooltip(
                  `<div class="hdi-tt"><b>${name}</b><br><span class="hdi-tt-country">${countryName}</span>HDI: ${v.toFixed(3)}<br><span class="hdi-tt-tier" style="color:${col.fill}">${hdiTierLabel(col.tier)}</span></div>`,
                  { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 }
                );
              } else {
                lyr.bindTooltip(`<div class="hdi-tt"><b>${name}</b><br><span class="hdi-tt-country">${countryName}</span></div>`, { className: 'hdi-tooltip-wrap', direction: 'top', sticky: true, opacity: 1 });
              }
            },
          });
          lazyLayerCacheRef.current[iso2] = layer;
          if (map.getZoom() >= 6) layer.addTo(map);
        })
        .catch(() => { lazyLayerCacheRef.current[iso2] = 'error'; });
    };

    const applyLazy = z => {
      Object.keys(LAZY_SUBNAT).forEach(iso2 => {
        const layer = lazyLayerCacheRef.current[iso2];
        if (z >= 6) {
          if (layer === undefined) loadCountry(iso2);
          else if (layer && layer !== 'error' && !map.hasLayer(layer)) layer.addTo(map);
        } else {
          if (layer && layer !== 'error' && map.hasLayer(layer)) map.removeLayer(layer);
        }
      });
    };

    const onZoom = () => applyLazy(map.getZoom());
    map.on('zoomend', onZoom);
    applyLazyRef.current = applyLazy;
    applyLazy(map.getZoom());

    return () => { map.off('zoomend', onZoom); applyLazyRef.current = null; };
  }, [mapReady]);

  const articles = mapData?.articles || [];
  const now      = Date.now();
  const likedCount = likedIds.size;
  const likedIdsArr = (() => {
    try { return new Set(JSON.parse(localStorage.getItem('originadvisory_likes') || '[]').map(l => l.id)); }
    catch { return new Set(); }
  })();
  const filtered =
    activeFilter === '24h'   ? articles.filter(a => now - new Date(a.published).getTime() < 86400000) :
    activeFilter === 'high'  ? articles.filter(a => a.significance >= 3) :
    activeFilter === 'liked' ? articles.filter(a => likedIdsArr.has(a.url || a.title)) : articles;
  const filteredCount    = filtered.length;
  const nationalArticles = filtered.filter(isNationalScope);
  const cityCount        = filteredCount - nationalArticles.length;

  const FILTERS = [
    { key: 'all',   label: 'ALL' },
    { key: '24h',   label: 'LAST 24H' },
    { key: 'high',  label: 'HIGH SIGNIFICANCE ONLY' },
    { key: 'liked', label: likedCount ? `MY LIKES (${likedCount})` : 'MY LIKES' },
  ];

  return (
    <section className="map-section">
      <div className="container">
        <Reveal className="section-center">
          <span className="eyebrow">Live Intelligence Map</span>
          <h2 style={{ marginTop: 20 }}>Where the news is happening</h2>
          <p className="head-sub">Region shading shows the UN Human Development Index by country and province. Story markers show where we track active coverage — zoom in to explore.</p>
        </Reveal>
      </div>
      <Reveal className="map-centered">
        {articles.length > 0 && (
          <LiveIndicator count={filteredCount} national={nationalArticles.length} citySpecific={cityCount} activeFilter={activeFilter} lastUpdated={mapData.last_updated} />
        )}
        <div style={{ position: "relative", marginTop: 16 }}>
          <div className="map-frame" ref={mapRef} />
          {hintVisible && (
            <div className="map-zoom-hint">
              <span>Zoom in to see city and district-level data</span>
              <button className="map-zoom-hint-close" onClick={() => setHintVisible(false)} aria-label="Dismiss">×</button>
            </div>
          )}
        </div>
        {nationalArticles.length > 0 && (
          <div className="nat-btn-row">
            <button className="nat-articles-btn" onClick={onNatModalOpen}>
              <span className="live-dot nat-btn-dot" />
              <span className="nat-btn-label">NATIONAL ARTICLES</span>
              <span className="nat-btn-sep">·</span>
              <span className="nat-btn-count">{nationalArticles.length} STORIES</span>
              <span className="nat-btn-sep">·</span>
              <span className="nat-btn-cta">VIEW ALL →</span>
            </button>
          </div>
        )}
        <div className="map-filters">
          {FILTERS.map(f => (
            <button key={f.key} className={`map-filter-btn${activeFilter === f.key ? ' active' : ''}`}
              onClick={() => setActiveFilter(f.key)}>
              {f.label}
            </button>
          ))}
        </div>
        {activeFilter === 'liked' && likedCount === 0 && (
          <p className="map-likes-empty">Like stories in the feed above to pin them here.</p>
        )}
        <div className="map-legend-wrap">
          <div className="map-legend-label">UN Human Development Index</div>
          <div className="map-legend-row">
            <div className="map-legend-item"><span className="map-legend-swatch" style={{background:'rgba(45,106,79,0.75)',borderColor:'#2d6a4f'}} /> Very High (≥0.80)</div>
            <div className="map-legend-item"><span className="map-legend-swatch" style={{background:'rgba(82,183,136,0.75)',borderColor:'#52b788'}} /> High (0.70–0.79)</div>
            <div className="map-legend-item"><span className="map-legend-swatch" style={{background:'rgba(244,211,94,0.75)',borderColor:'#f4d35e'}} /> Medium (0.55–0.69)</div>
            <div className="map-legend-item"><span className="map-legend-swatch" style={{background:'rgba(224,122,95,0.75)',borderColor:'#e07a5f'}} /> Low (0.40–0.54)</div>
            <div className="map-legend-item"><span className="map-legend-swatch" style={{background:'rgba(157,45,31,0.75)',borderColor:'#9d2d1f'}} /> Very Low (&lt;0.40)</div>
          </div>
          <div className="map-legend-divider" />
          <div className="map-legend-row">
            <div className="map-legend-item"><span className="map-legend-dot dot-red"    /> Critical</div>
            <div className="map-legend-item"><span className="map-legend-dot dot-orange" /> Significant</div>
            <div className="map-legend-item"><span className="map-legend-dot dot-yellow" /> Moderate</div>
            <div className="map-legend-item"><span className="map-legend-dot dot-green"  /> Local</div>
          </div>
          <p className="map-attribution">Country data: UNDP Human Development Report 2025 (2023 values, latest available). Sub-national data: Global Data Lab Subnational HDI 2023; Department of Census and Statistics Sri Lanka 2019. The next UNDP HDR (covering 2024 data) is expected in mid-2026. Some countries show country-level data only where sub-national HDI is unpublished.</p>
        </div>
      </Reveal>
    </section>
  );
}

// ── Modal — rebuilt, z-index 1000 in CSS ──────────────────────────────────
function Modal({ region, onClose }) {
  useEffect(() => {
    if (!region) return;
    const onKey = e => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { window.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [region, onClose]);

  const c = region ? MODAL_CONTENT[region] : null;
  return (
    <div className={`modal-backdrop ${region ? "open" : ""}`} onClick={onClose}>
      {c && (
        <div className="modal" onClick={e => e.stopPropagation()}>
          <button className="modal-close" onClick={onClose} aria-label="Close">
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <path d="M1 1 L11 11 M11 1 L1 11" />
            </svg>
          </button>
          <img className="modal-image" src={c.image} alt={c.name} loading="eager"
            onError={e => { e.currentTarget.style.background = FALLBACK_BG; e.currentTarget.removeAttribute("src"); }} />
          <div className="modal-body">
            <span className="eyebrow" style={{ display: "block", marginBottom: 10 }}>{c.name}</span>
            <div className="modal-cat">{c.category}</div>
            <h3>{c.title}</h3>
            <p style={{ marginTop: 14, fontSize: "16px" }}>{c.desc}</p>
            {c.covered && (
              <div className="modal-covered">
                <div className="modal-covered-label">What we cover</div>
                <ul className="modal-covered-list">
                  {c.covered.map((item, i) => <li key={i} className="modal-covered-item">{item}</li>)}
                </ul>
              </div>
            )}
            <div className="modal-actions">
              <a href="#/contact" className="btn btn-primary" onClick={onClose}>
                Try the platform <span className="arr"><Icons.Arrow /></span>
              </a>
              <a href="#/contact" className="btn btn-outline" onClick={onClose}>
                Talk to us <span className="arr"><Icons.Arrow /></span>
              </a>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ── Article Modal (news dot click) ────────────────────────────────────────
function ArticleModal({ article, onClose, onBack, cityImages, allArticles }) {
  const [countrySheet, setCountrySheet] = useState(null);

  useEffect(() => {
    if (!article) return;
    setCountrySheet(null);
    const dismiss = onBack || onClose;
    const onKey = e => { if (e.key === "Escape") { if (countrySheet) setCountrySheet(null); else dismiss(); } };
    window.addEventListener("keydown", onKey);
    if (!onBack) document.body.style.overflow = "hidden";
    return () => {
      window.removeEventListener("keydown", onKey);
      if (!onBack) document.body.style.overflow = "";
    };
  }, [article, onClose, onBack]);

  const sigLabels = { 1: "Low", 2: "Moderate", 3: "Significant", 4: "Critical" };
  const sigClass  = { 1: "green", 2: "yellow", 3: "orange", 4: "red" };

  const getImg = art => {
    if (art.image_url) return art.image_url;
    if (!cityImages) return null;
    const fallbackKey = COUNTRY_FALLBACK_KEY[art.country] || 'default_LK';
    const imgs = cityImages[art.city] || cityImages[fallbackKey];
    return imgs ? imgs[0] : null;
  };

  return (
    <div className={`article-modal-backdrop ${article ? "open" : ""}`} onClick={onBack || onClose}>
      {article && (
        <div className="article-modal" onClick={e => e.stopPropagation()}>
          <button className="article-modal-close" onClick={onBack || onClose} aria-label="Close">
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <path d="M1 1 L11 11 M11 1 L1 11" />
            </svg>
          </button>
          {getImg(article) && (
            <img className="article-modal-img" src={getImg(article)} alt={article.city}
              loading="eager" onError={e => { e.currentTarget.style.display = 'none'; }} />
          )}
          <div className="article-modal-body">
            {onBack && (
              <button className="article-modal-back" onClick={onBack}>
                ‹ Back to list
              </button>
            )}
            <div className="article-meta">
              <span className={`significance-badge badge-${sigClass[article.significance]}`}>
                {sigLabels[article.significance]}
              </span>
              <span className="article-meta-city">{isNationalScope(article) ? 'National' : article.city}</span>
              <span className="article-meta-time">{relativeTime(article.published)}</span>
            </div>
            <h3>{renderWithCountryLinks(article.title, setCountrySheet)}</h3>
            <p>{renderWithCountryLinks(article.summary, setCountrySheet)}</p>
            <a href={article.url} className="article-modal-link" target="_blank" rel="noopener noreferrer">
              Read full article <Icons.ArrowSmall />
            </a>
          </div>
          {countrySheet && (
            <CountrySheet
              country={countrySheet}
              articles={allArticles}
              onClose={() => setCountrySheet(null)}
            />
          )}
        </div>
      )}
    </div>
  );
}

// ── Article List Modal ─────────────────────────────────────────────────────
function ArticleListModal({ modal, onClose, onArticleClick, noEsc }) {
  const [tab, setTab]           = useState('all');
  const [sortMode, setSortMode] = useState('importance');

  useEffect(() => {
    if (!modal) return;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = ''; };
  }, [modal]);

  useEffect(() => {
    if (!modal || noEsc) return;
    const onKey = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => { window.removeEventListener('keydown', onKey); };
  }, [modal, onClose, noEsc]);

  useEffect(() => { setTab('all'); }, [modal?.title]);

  if (!modal) return null;

  const { title, articles } = modal;
  const sorted = sortMode === 'importance'
    ? [...articles].sort((a, b) => calculateImportanceScore(b, articles) - calculateImportanceScore(a, articles))
    : [...articles].sort((a, b) => new Date(b.published) - new Date(a.published));
  const hasNational = sorted.some(isNationalScope);
  const hasCity     = sorted.some(a => !isNationalScope(a));
  const showTabs    = hasNational && hasCity;

  const displayed =
    tab === 'national' ? sorted.filter(isNationalScope) :
    tab === 'city'     ? sorted.filter(a => !isNationalScope(a)) : sorted;

  const sourceDomain = url => { try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return ''; } };

  return (
    <div className="list-modal-backdrop open" onClick={onClose}>
      <div className="list-modal" onClick={e => e.stopPropagation()}>
        <div className="list-modal-header">
          <div className="list-modal-eyebrow">{title}</div>
          <h2 className="list-modal-heading">{articles.length} articles tracked</h2>
          <button className="list-modal-close" onClick={onClose} aria-label="Close">
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <path d="M1 1 L11 11 M11 1 L1 11" />
            </svg>
          </button>
        </div>
        <div className="list-modal-sort-row">
          <button className={`list-sort-btn${sortMode === 'importance' ? ' active' : ''}`} onClick={() => setSortMode('importance')}>Most important</button>
          <button className={`list-sort-btn${sortMode === 'recent' ? ' active' : ''}`} onClick={() => setSortMode('recent')}>Most recent</button>
        </div>
        {showTabs && (
          <div className="list-modal-tabs">
            {[['all','All'],['national','National'],['city','City-specific']].map(([k,l]) => (
              <button key={k} className={`list-modal-tab${tab === k ? ' active' : ''}`} onClick={() => setTab(k)}>{l}</button>
            ))}
          </div>
        )}
        <div className="list-modal-scroll">
          {displayed.map(art => {
            const trending = isTrendingArticle(art);
            const coverage = getCoverageCount(art, articles);
            return (
              <div key={art.id} className="list-article-item" onClick={() => onArticleClick(art)}>
                <span className="list-article-dot" style={{ background: colorToHex(art.color) }} />
                <div className="list-article-content">
                  <div className="list-article-headline">
                    {art.title}
                    {trending && <span className="article-badge badge-trending">Trending</span>}
                    {coverage >= 3 && <span className="article-badge badge-coverage">{coverage} sources</span>}
                  </div>
                  <div className="list-article-meta">
                    {sourceDomain(art.url)} · {relativeTime(art.published)} · {isNationalScope(art) ? 'National' : art.city}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── National Articles Modal (all national-scope, country sub-tabs) ────────
function NationalArticlesModal({ open, articles, onClose, onArticleClick, noEsc }) {
  const [tab, setTab]           = useState('all');
  const [sortMode, setSortMode] = useState('importance');

  useEffect(() => {
    if (!open) return;
    document.body.style.overflow = 'hidden';
    return () => { document.body.style.overflow = ''; };
  }, [open]);

  useEffect(() => {
    if (!open || noEsc) return;
    const onKey = e => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose, noEsc]);

  useEffect(() => { if (open) setTab('all'); }, [open]);

  if (!open || !articles.length) return null;

  const byCountry = {};
  articles.forEach(a => {
    if (!byCountry[a.country]) byCountry[a.country] = [];
    byCountry[a.country].push(a);
  });
  const countries = Object.entries(byCountry)
    .sort((a, b) => b[1].length - a[1].length)
    .map(([c, arts]) => ({ country: c, count: arts.length }));

  const sorted = sortMode === 'importance'
    ? [...articles].sort((a, b) => calculateImportanceScore(b, articles) - calculateImportanceScore(a, articles))
    : [...articles].sort((a, b) => new Date(b.published) - new Date(a.published));
  const displayed = tab === 'all' ? sorted : sorted.filter(a => a.country === tab);

  const sourceDomain = url => { try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return ''; } };

  return (
    <div className="list-modal-backdrop open" onClick={onClose}>
      <div className="list-modal" onClick={e => e.stopPropagation()}>
        <div className="list-modal-header">
          <div className="list-modal-eyebrow">ALL NATIONAL ARTICLES</div>
          <h2 className="list-modal-heading">Regional development news</h2>
          <button className="list-modal-close" onClick={onClose} aria-label="Close">
            <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
              <path d="M1 1 L11 11 M11 1 L1 11" />
            </svg>
          </button>
        </div>
        <div className="list-modal-sort-row">
          <button className={`list-sort-btn${sortMode === 'importance' ? ' active' : ''}`} onClick={() => setSortMode('importance')}>Most important</button>
          <button className={`list-sort-btn${sortMode === 'recent' ? ' active' : ''}`} onClick={() => setSortMode('recent')}>Most recent</button>
        </div>
        <div className="list-modal-tabs nat-modal-tabs">
          <button className={`list-modal-tab${tab === 'all' ? ' active' : ''}`} onClick={() => setTab('all')}>
            All ({articles.length})
          </button>
          {countries.map(({ country, count }) => (
            <button key={country} className={`list-modal-tab${tab === country ? ' active' : ''}`} onClick={() => setTab(country)}>
              {country.toUpperCase()} ({count})
            </button>
          ))}
        </div>
        <div className="list-modal-scroll">
          {displayed.map(art => {
            const trending = isTrendingArticle(art);
            const coverage = getCoverageCount(art, articles);
            return (
              <div key={art.id} className="list-article-item" onClick={() => onArticleClick(art)}>
                <span className="list-article-dot" style={{ background: colorToHex(art.color) }} />
                <div className="list-article-content">
                  <div className="list-article-headline">
                    {art.title}
                    {trending && <span className="article-badge badge-trending">Trending</span>}
                    {coverage >= 3 && <span className="article-badge badge-coverage">{coverage} sources</span>}
                  </div>
                  <div className="list-article-meta">
                    {art.country} · {sourceDomain(art.url)} · {relativeTime(art.published)}
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// ── Page: Home ─────────────────────────────────────────────────────────────
function HomePage({ onRegionClick, mapData, onArticleClick, onListOpen, onNatModalOpen, cityImages }) {
  const articles = mapData?.articles || [];
  return (
    <React.Fragment>
      <Hero articles={articles} />
      <HomeFeedSection articles={articles} onArticleClick={onArticleClick} />
      <PersonalisedSection articles={articles} cityImages={cityImages} />
      <MapSection onRegionClick={onRegionClick} mapData={mapData} onArticleClick={onArticleClick} onListOpen={onListOpen} onNatModalOpen={onNatModalOpen} />
      <TodaysEdition articles={articles} onArticleClick={onArticleClick} lastUpdated={mapData?.last_updated} editionSections={mapData?.edition_sections} />
    </React.Fragment>
  );
}

// ── Platform: UI Mockup ────────────────────────────────────────────────────
function PlatformMockup() {
  return (
    <div className="platform-mockup">
      <div className="mockup-topbar">
        <span className="mockup-dot" /><span className="mockup-dot" /><span className="mockup-dot" />
        <span className="mockup-topbar-label">Origin Advisory</span>
      </div>
      <div className="mockup-query-row">
        <svg width="13" height="13" viewBox="0 0 14 14" fill="none" stroke="var(--ink-soft)" strokeWidth="1.5" strokeLinecap="round">
          <circle cx="6" cy="6" r="4.5" /><path d="M9.5 9.5 L13 13" />
        </svg>
        <span>What is the current FCDO Sri Lanka budget allocation?</span>
      </div>
      <div className="mockup-timer">Retrieved in 2.1 seconds from 3 sources</div>
      <div className="mockup-result">
        <div className="mockup-result-title">FCDO Sri Lanka Bilateral Programme 2025–26</div>
        <div className="mockup-result-body">£47.2M across 12 active programmes. Economic recovery (38%), governance (24%), climate adaptation (22%).</div>
        <div className="mockup-source">FCDO.gov.uk · Accessed 5 May 2026</div>
      </div>
      <div className="mockup-result">
        <div className="mockup-result-title">Active FCDO contractors (Sri Lanka)</div>
        <div className="mockup-result-body">Crown Agents, Adam Smith International, Oxford Policy Management — 7 active contracts above £500k.</div>
        <div className="mockup-source">FCDO Contracts Database · Accessed 5 May 2026</div>
      </div>
    </div>
  );
}

// ── Page: Platform ─────────────────────────────────────────────────────────
function PlatformPage() {
  return (
    <main style={{ paddingTop: "96px" }}>

      {/* 1 — Hook with mockup */}
      <section className="section">
        <div className="container">
          <div className="platform-hook">
            <Reveal className="platform-hook-text">
              <span className="eyebrow">The Platform</span>
              <h2 style={{ marginTop: 20, maxWidth: "12ch" }}>Research that takes hours, in seconds</h2>
              <p className="section-intro" style={{ marginTop: 28 }}>
                Development professionals spend 8+ hours a week pulling data from scattered sources —
                donor websites, IMF reports, government announcements, tender platforms. Origin Advisory
                aggregates it all and uses AI to deliver instant, sourced answers to the questions you
                actually need answered.
              </p>
              <a href="#/contact" className="btn btn-primary" style={{ marginTop: 36 }}>
                Try it free <span className="arr"><Icons.Arrow /></span>
              </a>
            </Reveal>
            <Reveal><PlatformMockup /></Reveal>
          </div>
        </div>
      </section>

      {/* 2 — How it works flow, dark navy background */}
      <section style={{ background: "var(--ink)", padding: "120px 0" }}>
        <div className="container">
          <Reveal>
            <span className="eyebrow" style={{ color: "var(--accent-soft)" }}>How it works</span>
            <h2 style={{ color: "#fff", marginTop: 20, marginBottom: 72 }}>Three steps to an answer</h2>
          </Reveal>
          <div className="how-it-works-flow">
            {FEATURES.map(f => (
              <Reveal key={f.num} className="flow-step">
                <div className="flow-step-num">{f.num}</div>
                <h3 style={{ color: "#fff" }}>{f.title}</h3>
                <p style={{ color: "rgba(242,234,217,0.65)" }}>{f.desc}</p>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      {/* 3 — Who uses it (asymmetric 2×2) */}
      <section className="section">
        <div className="container">
          <Reveal>
            <div className="section-head" style={{ maxWidth: 600 }}>
              <span className="eyebrow">Who Uses It</span>
              <h2 style={{ marginTop: 20 }}>Built for the people doing the work</h2>
            </div>
          </Reveal>
        </div>
        <div className="use-cases-asymmetric">
          <Reveal className="uca-img-card">
            <img src={USE_CASES[0].image} alt={USE_CASES[0].title} loading="lazy"
              onError={e => { e.currentTarget.style.opacity = "0"; }} />
            <div className="uca-img-content">
              <h3>{USE_CASES[0].title}</h3>
              <p>{USE_CASES[0].desc}</p>
            </div>
          </Reveal>
          <Reveal className="uca-text-card">
            <h3>{USE_CASES[1].title}</h3>
            <p>{USE_CASES[1].desc}</p>
          </Reveal>
          <Reveal className="uca-text-card">
            <h3>{USE_CASES[2].title}</h3>
            <p>{USE_CASES[2].desc}</p>
          </Reveal>
          <Reveal className="uca-img-card">
            <img src={USE_CASES[3].image} alt={USE_CASES[3].title} loading="lazy"
              onError={e => { e.currentTarget.style.opacity = "0"; }} />
            <div className="uca-img-content">
              <h3>{USE_CASES[3].title}</h3>
              <p>{USE_CASES[3].desc}</p>
            </div>
          </Reveal>
        </div>
      </section>

      {/* 4 — Quote strip */}
      <div className="img-break">
        <img src={HILLS_IMG} alt="Sri Lanka tea hills" loading="lazy" />
        <div className="img-break-overlay">
          <p className="img-break-quote">
            "Origin Advisory replaces the 8 hours I used to spend digging through PDFs every Monday."
          </p>
          <p style={{ color: "rgba(242,234,217,0.5)", fontSize: "14px", marginTop: 16, fontStyle: "normal", fontWeight: 400 }}>
            — Senior analyst, UK boutique consultancy
          </p>
        </div>
      </div>

      {/* 5 — CTA bar */}
      <div className="cta-bar">
        <div className="container">
          <div className="cta-bar-inner">
            <h2>See how teams use Origin Advisory</h2>
            <a href="#/access" className="btn-white-outline">View pricing <span className="arr"><Icons.Arrow /></span></a>
          </div>
        </div>
      </div>

    </main>
  );
}

// ── Page: Access ───────────────────────────────────────────────────────────
function AccessPage() {
  const [openFaq, setOpenFaq] = useState(null);
  const tierIconTypes = ["individual", "organisation", "enterprise"];

  return (
    <main style={{ paddingTop: "96px" }}>

      {/* Header + Tiers */}
      <section className="section">
        <div className="container">
          <Reveal className="access-header">
            <span className="eyebrow">Access</span>
            <h2>How to access</h2>
            <p>Three ways to bring Origin Advisory into your work. Every plan starts with 14 days free.</p>
          </Reveal>

          <div className="tiers-grid">
            {TIERS.map((t, i) => (
              <Reveal key={t.num} className={t.popular ? "tier-card-featured" : "tier-card-flat"}>
                {t.popular && <span className="tier-most-popular">Most popular</span>}
                <TierIcon type={tierIconTypes[i]} />
                <h3>{t.title}</h3>
                <span className="tier-price-large">
                  {t.price}
                  <span style={{ fontSize: "14px", fontWeight: 400, color: "var(--ink-soft)", letterSpacing: 0 }}> {t.period}</span>
                </span>
                <p className="tier-desc">{t.desc}</p>
                <ul className="tier-features">
                  {t.features.map((f, j) => (
                    <li key={j} className="tier-feature"><Check /> {f}</li>
                  ))}
                </ul>
                <a href="#/contact" className={`btn ${t.popular ? "btn-primary" : "btn-outline"}`}
                  style={{ width: "100%", justifyContent: "center", marginTop: "auto" }}>
                  {t.cta} <span className="arr"><Icons.Arrow /></span>
                </a>
              </Reveal>
            ))}
          </div>

          <Reveal>
            <p style={{ marginTop: 48, fontSize: "14px", color: "var(--ink-soft)", textAlign: "center" }}>
              All plans include a 14-day free trial. No card required to start.
            </p>
          </Reveal>
        </div>
      </section>

      {/* FAQ */}
      <section className="faq-section" style={{ background: "var(--bg-2)" }}>
        <div className="container">
          <Reveal>
            <span className="eyebrow">FAQ</span>
            <h2 style={{ marginTop: 20 }}>Common questions</h2>
          </Reveal>
          <div className="faq-grid">
            {FAQ.map((item, i) => (
              <div key={i} className="faq-item">
                <button className="faq-question" onClick={() => setOpenFaq(openFaq === i ? null : i)}>
                  {item.q}
                  <svg className={`faq-icon ${openFaq === i ? "open" : ""}`}
                    viewBox="0 0 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round">
                    <path d="M10 3 V17 M3 10 H17" />
                  </svg>
                </button>
                <div className={`faq-answer ${openFaq === i ? "open" : ""}`}>
                  <div className="faq-answer-inner">{item.a}</div>
                </div>
              </div>
            ))}
          </div>
        </div>
      </section>

      {/* Bottom CTA */}
      <section className="section" style={{ padding: "100px 0" }}>
        <div className="container about-text-centered">
          <Reveal>
            <h2>Try it before you commit</h2>
            <p style={{ fontSize: "17px", marginTop: 20, marginBottom: 40 }}>14-day free trial. No card required.</p>
            <a href="#/contact" className="btn btn-primary">Request access <span className="arr"><Icons.Arrow /></span></a>
          </Reveal>
        </div>
      </section>

    </main>
  );
}

// ── Page: Insights ─────────────────────────────────────────────────────────
// ── Page: Contact ──────────────────────────────────────────────────────────
function ContactPage() {
  return (
    <main>
      <section className="contact-section" style={{ paddingTop: "160px", paddingBottom: "120px" }}>
        <div className="container">
          <div className="contact-page-grid">
            <Reveal className="contact-page-left">
              <span className="eyebrow" style={{ marginBottom: 16 }}>Get in touch</span>
              <h2>Try it before you commit</h2>
              <p>14 days free access. We'll set up your account and walk you through the platform.</p>
              <div className="contact-details">
                <div className="contact-detail-item">
                  <Icons.Mail />
                  <a href="mailto:hello@originadvisory.com">hello@originadvisory.com</a>
                </div>
                <div className="contact-detail-item">
                  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round">
                    <path d="M7 1 C4 1 1.5 3.3 1.5 6.3 C1.5 9.8 7 13 7 13 S12.5 9.8 12.5 6.3 C12.5 3.3 10 1 7 1Z" />
                    <circle cx="7" cy="6" r="2" />
                  </svg>
                  London, United Kingdom
                </div>
                <div className="contact-detail-item">
                  <svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round">
                    <path d="M7 1 C4 1 1.5 3.3 1.5 6.3 C1.5 9.8 7 13 7 13 S12.5 9.8 12.5 6.3 C12.5 3.3 10 1 7 1Z" />
                    <circle cx="7" cy="6" r="2" />
                  </svg>
                  Colombo, Sri Lanka
                </div>
              </div>
              <div className="contact-prefer-email">
                Prefer email? <a href="mailto:hello@originadvisory.com">hello@originadvisory.com</a>
              </div>
            </Reveal>

            <Reveal>
              <form className="contact-form" onSubmit={e => e.preventDefault()}>
                <div className="field field-full"><label>Name</label><input type="text" placeholder="Your name" /></div>
                <div className="field field-full"><label>Email</label><input type="email" placeholder="your@email.com" /></div>
                <div className="field"><label>Organisation</label><input type="text" placeholder="Your organisation" /></div>
                <div className="field"><label>Role</label><input type="text" placeholder="Your role or title" /></div>
                <div className="field field-full">
                  <label>What are you trying to do?</label>
                  <textarea placeholder="Tell us about your work and what you're trying to solve..." rows="5" />
                </div>
                <button type="submit" className="btn btn-primary field-full">
                  Request access <span className="arr"><Icons.Arrow /></span>
                </button>
              </form>
            </Reveal>
          </div>
        </div>
      </section>
    </main>
  );
}

// ── Page: Funding Scan (redirect) ──────────────────────────────────────────
function FundingScanPage() {
  useEffect(() => { window.location.hash = "#/intelligence"; }, []);
  return null;
}

// ── Page: For NGOs ──────────────────────────────────────────────────────────
function ForNGOsPage() {
  return (
    <main style={{ paddingTop:"96px" }}>
      <section className="section">
        <div className="container">
          <Reveal>
            <span className="eyebrow">For NGOs, charities & social enterprises</span>
            <h2 style={{ maxWidth:"20ch", marginTop:12 }}>Become funder-ready. Find the right donors.</h2>
            <p className="section-sub" style={{ maxWidth:"56ch" }}>Origin helps civil society organisations in South Asia access the intelligence, tools, and support they need to secure sustainable funding and demonstrate credible impact.</p>
            <div style={{ display:"flex", gap:16, flexWrap:"wrap", marginTop:36 }}>
              <a href="#/intelligence" className="btn btn-primary">Explore Intelligence Tools <span className="arr"><Icons.Arrow /></span></a>
              <a href="#/pilot" className="btn btn-outline">Apply for a Pilot Project</a>
            </div>
          </Reveal>
        </div>
      </section>

      <section className="section" style={{ background:"var(--bg-2)", paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal><h3 style={{ marginBottom:48 }}>How NGOs use Origin</h3></Reveal>
          <div className="ngo-use-grid">
            {[
              { icon:"→", title:"Become funder-ready", desc:"Our Funding Readiness Scan identifies gaps in your governance, evidence, and documentation — and tells you exactly what to fix before approaching donors." },
              { icon:"→", title:"Find funding opportunities", desc:"Origin maps active donor programmes, open grant windows, and funding trends across South Asia. Know which funders are active in your sector before you apply." },
              { icon:"→", title:"Improve impact reporting", desc:"We help you build simple, credible monitoring frameworks that produce the outcome data donors actually want to see — without complex systems." },
              { icon:"→", title:"Stronger donor pitches", desc:"Origin's team helps you translate your work into funder language — clear narratives, theory of change, and outcome summaries that stand out in competitive rounds." },
              { icon:"→", title:"Get listed on the map", desc:"Add your organisation to Origin's Development Map — visible to donors, researchers, diaspora funders, and partner organisations across South Asia." },
              { icon:"→", title:"Request pilot support", desc:"Origin is offering free pilot projects to a small number of organisations. Apply for a donor mapping sprint, readiness scan, or impact summary — no cost, just feedback." },
            ].map((item,i) => (
              <Reveal key={i} className="ngo-use-card">
                <div className="ngo-use-icon">{item.icon}</div>
                <h4>{item.title}</h4>
                <p>{item.desc}</p>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section">
        <div className="container">
          <Reveal><span className="eyebrow">Advisory products</span><h2 style={{ marginTop:12, marginBottom:48 }}>What you receive</h2></Reveal>
          <div className="advisory-grid">
            {ADVISORY_CARDS.map((card,i) => (
              <Reveal key={i} className="advisory-card">
                <div className="advisory-card-title">{card.title}</div>
                <div className="advisory-card-who"><span>For:</span> {card.who}</div>
                <p className="advisory-card-desc">{card.what}</p>
                <a href="#/services" className="advisory-card-cta">{card.cta} <Icons.ArrowSmall /></a>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ background:"var(--ink)", paddingTop:80, paddingBottom:80 }}>
        <div className="container" style={{ textAlign:"center" }}>
          <Reveal>
            <h2 style={{ color:"#fff", marginBottom:16 }}>Ready to start?</h2>
            <p style={{ color:"rgba(255,255,255,0.65)", maxWidth:"44ch", margin:"0 auto 36px" }}>Origin is working with a small cohort of South Asian NGOs. Applications are reviewed on a rolling basis.</p>
            <div style={{ display:"flex", gap:16, justifyContent:"center", flexWrap:"wrap" }}>
              <a href="#/intelligence" className="btn btn-primary">Explore Intelligence Tools <span className="arr"><Icons.Arrow /></span></a>
              <a href="#/pilot" className="btn" style={{ border:"1px solid rgba(255,255,255,0.3)", color:"#fff" }}>Apply for a Pilot Project</a>
            </div>
          </Reveal>
        </div>
      </section>
    </main>
  );
}

// ── Page: For Funders ───────────────────────────────────────────────────────
function ForFundersPage() {
  const [interested, setInterested] = useState(false);
  return (
    <main style={{ paddingTop:"96px" }}>
      <section className="section">
        <div className="container">
          <Reveal>
            <span className="eyebrow">For donors, foundations & CSR teams</span>
            <div className="coming-soon-pill">Coming soon</div>
            <h2 style={{ maxWidth:"22ch", marginTop:12 }}>Discover credible Sri Lanka-focused organisations</h2>
            <p className="section-sub" style={{ maxWidth:"54ch" }}>Origin is building a verified intelligence layer for donors who want to find, assess, and support South Asian civil society — with confidence.</p>
          </Reveal>
        </div>
      </section>

      <section className="section" style={{ background:"var(--bg-2)", paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal><h3 style={{ marginBottom:48 }}>What funders will be able to do</h3></Reveal>
          <div className="ngo-use-grid">
            {[
              { title:"Explore organisations by location and sector", desc:"Search and filter a verified map of NGOs, CBOs, and social enterprises across Sri Lanka and South Asia — by geography, sector, scale, and registration status." },
              { title:"Find credible, vetted organisations", desc:"Every organisation on Origin's map has been reviewed against baseline criteria. Save time on initial due diligence and spend it on relationship-building." },
              { title:"Request shortlists and due diligence support", desc:"Tell us your priorities — sector, geography, scale — and Origin will produce a shortlist of organisations matched to your funding criteria." },
              { title:"Commission regional intelligence reports", desc:"Need a sector overview before you design a programme? Origin produces structured research briefs on demand — cited, structured, and ready to use." },
              { title:"Discover projects via the map", desc:"Origin's Development Map shows where work is happening, what it's about, and who is doing it. A live layer of civil society activity across South Asia." },
              { title:"Register diaspora and CSR interest", desc:"For diaspora groups and corporate CSR teams, Origin will provide curated lists of verified organisations accepting donations or partnerships." },
            ].map((item,i) => (
              <Reveal key={i} className="ngo-use-card">
                <h4>{item.title}</h4>
                <p>{item.desc}</p>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal>
            <h3 style={{ marginBottom:16 }}>Sample intelligence output</h3>
            <p style={{ color:"var(--ink-soft)", marginBottom:48, maxWidth:"52ch" }}>An example of the structured intelligence Origin produces for funders. Northern Sri Lanka education sector, Q1 2026.</p>
          </Reveal>
          <Reveal className="intel-sample-output">
            <div className="intel-sample-header">
              <div className="intel-sample-tag">Sector Brief · Q1 2026</div>
              <h4 className="intel-sample-title">Northern Sri Lanka — Education Sector</h4>
              <div className="intel-sample-meta">Origin Research Team · January 2026 · Confidential to subscriber</div>
            </div>
            <div className="intel-sample-body">
              <div className="intel-sample-section">
                <h5>Funding landscape overview</h5>
                <p>External development finance for education in Northern Province reached an estimated $28M in 2025 — an 18% increase on the prior year, driven primarily by UNICEF's expanded crisis-sensitive programming and ADB's TVET scale-up. FCDO remains the leading bilateral actor with active programmes in girls' secondary education and teacher training.</p>
              </div>
              <div className="intel-sample-section">
                <h5>Key developments this quarter</h5>
                <ul>
                  <li>UNICEF announced a $4.2M call for civil society partners on crisis-sensitive education — deadline March 2026</li>
                  <li>EU Civil Society Fund opened for expressions of interest — education and livelihoods focus</li>
                  <li>3 Colombo-based NGOs expanded to Northern Province for the first time — competitive landscape shifting</li>
                  <li>Government of Sri Lanka released revised education sector plan — donor alignment expected in H1 2026</li>
                </ul>
              </div>
              <div className="intel-sample-section">
                <h5>Recommended focus areas for new funding</h5>
                <p>Girls' secondary retention in Kilinochchi and Mullaitivu remains below national average despite existing programming. There is a documented gap in post-secondary vocational pathways — only 2 current programmes address this at scale.</p>
              </div>
            </div>
            <div className="intel-sample-footer">Origin Advisory · Subscriber Intelligence Brief · Not for public distribution</div>
          </Reveal>
        </div>
      </section>

      <section className="section" style={{ background:"var(--bg-2)", paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal>
            <h3 style={{ marginBottom:16 }}>Intelligence packages for funders</h3>
            <p style={{ color:"var(--ink-soft)", marginBottom:48, maxWidth:"52ch" }}>Three ways to work with Origin — from a single research brief to an ongoing intelligence subscription.</p>
          </Reveal>
          <div className="funder-tiers-grid">
            {[
              { name:"Quick Brief", price:"£500", timeline:"7 working days", desc:"A single structured research brief on a sector, district, or organisation — cited, formatted, and ready to use in grant design or due diligence.", features:["3–5 page research brief","Full citations and source list","Key actor mapping","Funder-ready executive summary"], cta:"Request a brief", featured:false },
              { name:"Deep Dive", price:"£2,500", timeline:"3 weeks", desc:"A comprehensive sector or geography study — funding landscape, actor analysis, gap mapping, and strategic recommendations for programme design.", features:["15–25 page sector report","Stakeholder mapping (donors + implementers)","Funding gap analysis","Shortlist of implementers for partnership","3 follow-up queries (30 days)"], cta:"Start a Deep Dive", featured:true },
              { name:"Ongoing Intelligence", price:"Custom", timeline:"Monthly or quarterly", desc:"A retained relationship with Origin — regular intelligence briefs, monitoring of your priority sectors, and on-demand research support.", features:["Monthly or quarterly briefings","Priority sector and geography monitoring","On-demand research queries","Donor activity alerts","Annual sector review report"], cta:"Discuss a retainer", featured:false },
            ].map((tier,i) => (
              <Reveal key={i} className={`funder-tier-card${tier.featured ? " funder-tier-featured" : ""}`}>
                {tier.featured && <div className="funder-tier-badge">Most popular</div>}
                <div className="funder-tier-name">{tier.name}</div>
                <div className="funder-tier-price">{tier.price}</div>
                <div className="funder-tier-timeline">{tier.timeline}</div>
                <p className="funder-tier-desc">{tier.desc}</p>
                <ul className="funder-tier-features">
                  {tier.features.map((f,j) => <li key={j}><Check />{f}</li>)}
                </ul>
                <a href="mailto:hello@originadvisory.com" className={`btn${tier.featured ? " btn-primary" : " btn-outline"}`} style={{ marginTop:"auto", display:"block" }}>{tier.cta} <span className="arr"><Icons.Arrow /></span></a>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ paddingTop:80, paddingBottom:100 }}>
        <div className="container">
          <div className="funder-cta-grid">
            <Reveal className="funder-cta-left">
              <h2>Register funder interest</h2>
              <p style={{ color:"var(--ink-soft)", maxWidth:"40ch", marginTop:16, marginBottom:32 }}>Leave your details and we'll be in touch when the funder portal launches. We're working with a small number of founding funders ahead of the public launch.</p>
              <a href="mailto:hello@originadvisory.com" className="btn btn-primary">Discuss a partnership <span className="arr"><Icons.Arrow /></span></a>
            </Reveal>
            <Reveal>
              {!interested ? (
                <form className="scan-form" onSubmit={e => { e.preventDefault(); setInterested(true); }}>
                  <div className="scan-form-grid" style={{ gridTemplateColumns:"1fr" }}>
                    <div className="scan-field"><label>Name *</label><input type="text" required placeholder="Your name" /></div>
                    <div className="scan-field"><label>Organisation *</label><input type="text" required placeholder="Foundation, CSR team, or individual" /></div>
                    <div className="scan-field"><label>Email *</label><input type="email" required placeholder="your@email.com" /></div>
                    <div className="scan-field"><label>Sector interests</label><input type="text" placeholder="e.g. Education, Health, Women's Empowerment" /></div>
                    <div className="scan-field"><label>Geographic focus</label><input type="text" placeholder="e.g. Northern Sri Lanka, South Asia broadly" /></div>
                  </div>
                  <button type="submit" className="btn btn-primary" style={{ marginTop:24 }}>Register interest <span className="arr"><Icons.Arrow /></span></button>
                </form>
              ) : (
                <div className="scan-success">
                  <div className="scan-success-icon">✓</div>
                  <h3>Thank you</h3>
                  <p>We'll be in touch ahead of the funder portal launch. In the meantime, feel free to email us at <a href="mailto:hello@originadvisory.com">hello@originadvisory.com</a>.</p>
                </div>
              )}
            </Reveal>
          </div>
        </div>
      </section>
    </main>
  );
}

// ── Page: Research Network ──────────────────────────────────────────────────
function ResearchNetworkPage() {
  const [submitted, setSubmitted] = useState(false);
  return (
    <main style={{ paddingTop:"96px" }}>
      <section className="section">
        <div className="container">
          <Reveal>
            <span className="eyebrow">Become an Origin Researcher</span>
            <h2 style={{ maxWidth:"22ch", marginTop:12 }}>Build your development research portfolio</h2>
            <p className="section-sub" style={{ maxWidth:"54ch" }}>Origin's Research Network connects students and early-career researchers with real publication opportunities. Your work is AI-assisted, human-reviewed, and published under your name.</p>
          </Reveal>
        </div>
      </section>

      <section className="section" style={{ background:"var(--bg-2)", paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal><h3 style={{ marginBottom:48 }}>What you get as a contributor</h3></Reveal>
          <div className="benefits-grid">
            {[
              { num:"01", title:"Published credit", desc:"Your name, university, and profile appear on every brief you contribute to. Real published research — not just a certificate." },
              { num:"02", title:"Contributor profile", desc:"A public Origin contributor page listing your published briefs, areas of expertise, and academic background." },
              { num:"03", title:"Completion certificate", desc:"A formal Origin Research Network certificate for each training module completed and brief published." },
              { num:"04", title:"LinkedIn feature", desc:"Origin features active contributors on LinkedIn — tagged posts highlighting your published work to our network of donors, NGOs, and development professionals." },
              { num:"05", title:"Portfolio building", desc:"A growing portfolio of real development research — city briefs, sector reports, organisation profiles — that demonstrates analytical capability to future employers." },
              { num:"06", title:"Development sector exposure", desc:"Work with real data, real organisations, and real development challenges in Sri Lanka and South Asia. Understand how the sector works from the inside." },
            ].map((b,i) => (
              <Reveal key={i} className="benefit-card">
                <div className="benefit-num">{b.num}</div>
                <h4>{b.title}</h4>
                <p>{b.desc}</p>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section">
        <div className="container">
          <Reveal><h3 style={{ marginBottom:12 }}>How it works</h3>
            <p style={{ color:"var(--ink-soft)", maxWidth:"52ch", marginBottom:48 }}>Three stages from application to publication.</p>
          </Reveal>
          <div className="how-steps">
            {[
              { n:"1", title:"Apply and onboard", desc:"Submit a short application. Accepted researchers complete the 6-module training programme at their own pace — typically 4–6 hours total." },
              { n:"2", title:"Research and write", desc:"Choose a brief topic from Origin's editorial queue or propose your own. Use Origin's AI research tools and structured template to draft your brief." },
              { n:"3", title:"Review and publish", desc:"Your draft is reviewed by Origin's editorial team. Feedback is returned within 7 days. Accepted briefs are published with full attribution within 2 weeks." },
            ].map((s,i) => (
              <Reveal key={i} className="how-step">
                <div className="how-step-num">{s.n}</div>
                <div className="how-step-body"><h4>{s.title}</h4><p>{s.desc}</p></div>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal>
            <h3 style={{ marginBottom:12 }}>Editorial standards</h3>
            <p style={{ color:"var(--ink-soft)", maxWidth:"52ch", marginBottom:48 }}>Origin holds its research to the standards expected of professional development intelligence. Every brief published on our platform meets the following criteria.</p>
          </Reveal>
          <div className="editorial-standards-grid">
            {[
              { num:"A", title:"Accuracy and citation", desc:"All factual claims must be supported by a cited source — multilateral databases, government portals, peer-reviewed literature, or credible NGO reports. Uncited assertions are not publishable under Origin's editorial policy." },
              { num:"B", title:"Methodology disclosure", desc:"Each brief must include a methodology note — what sources were used, how they were selected, and any significant limitations. Readers should understand how the research was conducted." },
              { num:"C", title:"Author transparency", desc:"Contributors publish under their real name, university, and year of study. Ghost-writing, AI-only authorship, and anonymous publication are not permitted. All AI assistance must be disclosed in the methods section." },
              { num:"D", title:"Editorial independence", desc:"Origin's editorial team reviews all briefs independently. Funders, partner organisations, and featured NGOs have no editorial input into published content. Findings are reported as the evidence shows." },
              { num:"E", title:"Conflict of interest", desc:"Contributors must disclose any personal or professional relationship with organisations or funders featured in their research. Briefs are reassigned where a conflict of interest is identified." },
              { num:"F", title:"Corrections policy", desc:"Factual errors identified post-publication are corrected within 48 hours and noted at the top of the corrected document. Significant errors may result in full retraction. Origin does not silently update published content." },
            ].map((s,i) => (
              <Reveal key={i} className="editorial-standard-card">
                <div className="editorial-standard-num">{s.num}</div>
                <h4>{s.title}</h4>
                <p>{s.desc}</p>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ background:"var(--bg-2)", paddingTop:80, paddingBottom:80 }}>
        <div className="container">
          <Reveal><h3 style={{ marginBottom:48 }}>Training modules</h3></Reveal>
          <div className="modules-grid">
            {TRAINING_MODULES.map((m,i) => (
              <Reveal key={i} className="module-card">
                <div className="module-num">{m.num}</div>
                <h4>{m.title}</h4>
                <p>{m.desc}</p>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ paddingTop:80, paddingBottom:100 }}>
        <div className="container">
          <div className="network-signup-grid">
            <Reveal className="network-signup-left">
              <h2>Apply to join</h2>
              <p style={{ color:"var(--ink-soft)", marginTop:16, maxWidth:"36ch" }}>Applications are open on a rolling basis. We review applications weekly and aim to respond within 5 working days.</p>
            </Reveal>
            <Reveal>
              {!submitted ? (
                <form className="scan-form" onSubmit={e => { e.preventDefault(); setSubmitted(true); window.scrollTo(0,0); }}>
                  <div className="scan-form-grid">
                    <div className="scan-field"><label>Full name *</label><input required type="text" placeholder="Your full name" /></div>
                    <div className="scan-field"><label>University *</label><input required type="text" placeholder="Your university or institution" /></div>
                    <div className="scan-field"><label>Degree / programme *</label><input required type="text" placeholder="e.g. MSc Development Studies" /></div>
                    <div className="scan-field"><label>Year of study</label>
                      <select>
                        <option value="">Select…</option>
                        <option>Year 1</option><option>Year 2</option><option>Year 3</option>
                        <option>Masters (Year 1)</option><option>Masters (Year 2)</option>
                        <option>PhD</option><option>Recent graduate</option>
                      </select>
                    </div>
                    <div className="scan-field"><label>Email *</label><input required type="email" placeholder="your@university.ac.uk" /></div>
                    <div className="scan-field"><label>LinkedIn profile</label><input type="url" placeholder="https://linkedin.com/in/yourprofile" /></div>
                    <div className="scan-field scan-field-full"><label>Topics of interest</label><input type="text" placeholder="e.g. Education, Youth Employment, Economic development, Gender" /></div>
                    <div className="scan-field scan-field-full"><label>Regions of knowledge</label><input type="text" placeholder="e.g. Northern Sri Lanka, Bangladesh, Nepal" /></div>
                    <div className="scan-field scan-field-full"><label>Why do you want to join? *</label><textarea required rows="4" placeholder="Tell us about your interest in development research and what you hope to contribute…" /></div>
                  </div>
                  <button type="submit" className="btn btn-primary" style={{ marginTop:32 }}>Submit application <span className="arr"><Icons.Arrow /></span></button>
                </form>
              ) : (
                <div className="scan-success">
                  <div className="scan-success-icon">✓</div>
                  <h3>Application received</h3>
                  <p>Thank you for applying to the Origin Research Network. We'll review your application and be in touch within 5 working days.</p>
                </div>
              )}
            </Reveal>
          </div>
        </div>
      </section>
    </main>
  );
}

// ── Page: Insights (extended) ───────────────────────────────────────────────
function InsightsPage() {
  const FILTERS = [
    { key:"all", label:"All" },
    { key:"city-brief", label:"City Briefs" },
    { key:"district-brief", label:"District Briefs" },
    { key:"sector-report", label:"Sector Reports" },
    { key:"donor-brief", label:"Donor Briefs" },
  ];
  const [activeFilter, setActiveFilter] = useState("all");
  const filtered = activeFilter === "all" ? MOCK_REPORTS : MOCK_REPORTS.filter(r => r.tag === activeFilter);

  return (
    <main style={{ paddingTop:"96px" }}>
      <section className="section" style={{ paddingBottom:0 }}>
        <div className="container">
          <Reveal className="insights-editorial-head">
            <span className="eyebrow">Insights</span>
            <h2>Research briefs & sector analysis</h2>
            <p className="section-sub">District-level intelligence, sector reports, and donor briefs for South Asia. Written by Origin researchers, AI-assisted and human-reviewed.</p>
          </Reveal>
          <Reveal>
            <div className="insights-filter-bar">
              {FILTERS.map(f => (
                <button key={f.key} className={`insights-filter-btn${activeFilter === f.key ? " active" : ""}`} onClick={() => setActiveFilter(f.key)}>{f.label}</button>
              ))}
            </div>
          </Reveal>
        </div>
      </section>

      <section style={{ paddingTop:48, paddingBottom:80 }}>
        <div className="container">
          <div className="report-cards-grid">
            {filtered.map(r => (
              <Reveal key={r.id} className="report-card">
                <div className="report-card-top">
                  <span className={`report-tag report-tag-${r.tag}`}>{r.category}</span>
                  <span className="report-location">{r.location}</span>
                </div>
                <h4 className="report-title">{r.title}</h4>
                <p className="report-summary">{r.summary}</p>
                <div className="report-card-footer">
                  <div className="report-meta">
                    <span className="report-author">{r.author}</span>
                    <span className="report-date">{r.date}</span>
                  </div>
                  <a href="#" className="read-more" onClick={e => e.preventDefault()}>Read more <Icons.ArrowSmall /></a>
                </div>
              </Reveal>
            ))}
            {filtered.length === 0 && <p style={{ color:"var(--muted)", gridColumn:"1/-1", paddingTop:32 }}>No reports in this category yet.</p>}
          </div>
        </div>
      </section>

      <div className="newsletter-bar">
        <div className="newsletter-inner">
          <Reveal>
            <h3>Get the weekly intelligence brief</h3>
            <p>Sent every Friday. Unsubscribe anytime.</p>
            <div className="newsletter-form">
              <input className="newsletter-input" type="email" placeholder="your@email.com" />
              <button className="btn-subscribe">Subscribe</button>
            </div>
          </Reveal>
        </div>
      </div>
    </main>
  );
}

// ── Page: Pilot Partnerships ────────────────────────────────────────────────
function PilotPage() {
  const [submitted, setSubmitted] = useState(false);
  return (
    <main style={{ paddingTop:"96px" }}>
      <section className="section">
        <div className="container">
          <Reveal>
            <span className="eyebrow">Limited availability</span>
            <h2 style={{ maxWidth:"22ch", marginTop:12 }}>Apply for a pilot project</h2>
            <p className="section-sub" style={{ maxWidth:"54ch" }}>Origin is accepting a small number of pilot and pro bono projects with South Asian NGOs, charities, and social enterprises. We want to test our tools with real organisations and real needs.</p>
          </Reveal>
        </div>
      </section>

      <section className="section" style={{ background:"var(--bg-2)", paddingTop:72, paddingBottom:80 }}>
        <div className="container">
          <Reveal><h3 style={{ marginBottom:12 }}>Pilot options — all free of charge</h3>
            <p style={{ color:"var(--ink-soft)", marginBottom:48, maxWidth:"48ch" }}>Choose one option per application. Additional support can be discussed after the pilot.</p>
          </Reveal>
          <div className="pilot-cards-grid">
            {PILOT_OPTIONS.map((p,i) => (
              <Reveal key={i} className="pilot-card">
                <h4>{p.title}</h4>
                <p>{p.what}</p>
                <div className="pilot-card-meta">
                  <div><span className="pilot-label">You receive:</span> {p.output}</div>
                  <div><span className="pilot-label">Timeline:</span> {p.timeline}</div>
                </div>
              </Reveal>
            ))}
          </div>
        </div>
      </section>

      <section className="section" style={{ paddingTop:72, paddingBottom:80 }}>
        <div className="container">
          <div className="pilot-ask-grid">
            <Reveal>
              <h3>What we ask in return</h3>
              <ul className="pilot-ask-list">
                {["Honest feedback on the process and output","A brief testimonial if the work was useful","Permission to list your organisation as a pilot case study (you can remain anonymous)","Permission to add your organisation to Origin's Development Map","A short call with our team after the project"].map((item,i) => (
                  <li key={i}><span>→</span>{item}</li>
                ))}
              </ul>
              <p style={{ color:"var(--ink-soft)", fontSize:14, marginTop:24 }}>We're not asking for payment, long-term commitment, or access to sensitive data. Just honest collaboration and feedback that helps us improve.</p>
            </Reveal>
            <Reveal>
              <div className="pilot-cta-box">
                <h3>Apply for pilot support</h3>
                <p>Applications are reviewed weekly. We aim to confirm within 5 working days.</p>
                {!submitted ? (
                  <form className="scan-form" style={{ marginTop:32 }} onSubmit={e => { e.preventDefault(); setSubmitted(true); }}>
                    <div className="scan-form-grid" style={{ gridTemplateColumns:"1fr" }}>
                      <div className="scan-field"><label>Organisation name *</label><input required type="text" placeholder="Your organisation" /></div>
                      <div className="scan-field"><label>Contact name *</label><input required type="text" placeholder="Your name" /></div>
                      <div className="scan-field"><label>Email *</label><input required type="email" placeholder="your@email.com" /></div>
                      <div className="scan-field"><label>Country</label><input type="text" placeholder="Sri Lanka" /></div>
                      <div className="scan-field"><label>Pilot option *</label>
                        <select required>
                          <option value="">Select a pilot option…</option>
                          {PILOT_OPTIONS.map((p,i) => <option key={i}>{p.title}</option>)}
                        </select>
                      </div>
                      <div className="scan-field"><label>Brief description of your work *</label><textarea required rows="3" placeholder="What does your organisation do and who do you work with?" /></div>
                    </div>
                    <button type="submit" className="btn btn-primary" style={{ marginTop:24 }}>Submit application <span className="arr"><Icons.Arrow /></span></button>
                  </form>
                ) : (
                  <div className="scan-success" style={{ marginTop:32 }}>
                    <div className="scan-success-icon">✓</div>
                    <h3>Application received</h3>
                    <p>We'll review your application and be in touch within 5 working days.</p>
                  </div>
                )}
              </div>
            </Reveal>
          </div>
        </div>
      </section>
    </main>
  );
}

// ── Intelligence constants ────────────────────────────────────────────────
const API_BACKED_TOOLS = new Set(["sector-snapshot", "donor-shortlist", "readiness"]);

const BUDGET_AMOUNTS = {
  "Under £10k":   8500,
  "£10k–£50k":  28000,
  "£50k–£250k": 85000,
  "£250k+":    180000,
  "Not sure yet": 25000,
};

const BUDGET_BREAKDOWN = [
  { label: "Programme staff",           pct: 0.45 },
  { label: "Activities and materials",  pct: 0.30 },
  { label: "Monitoring and evaluation", pct: 0.10 },
  { label: "Travel and logistics",      pct: 0.08 },
  { label: "Overhead",                  pct: 0.07 },
];

// ── Document shell wrapper ────────────────────────────────────────────────
function DocShell({ refNum, issued, title, submeta, children, dataSources }) {
  return (
    <div className="doc-shell">
      <div className="doc-header">
        <div className="doc-header-brand">
          <img src="./assets:logo.webp" alt="" className="doc-header-logo" />
          <span className="doc-header-wordmark">Origin Advisory</span>
        </div>
        <div className="doc-header-meta">
          <span>REF: ORG-{refNum}</span>
          <span>ISSUED: {issued}</span>
          <span>STATUS: DRAFT</span>
        </div>
      </div>
      <div className="doc-rule" />
      <div className="doc-watermark" aria-hidden="true">OA</div>
      <h1 className="doc-title">{title}</h1>
      <p className="doc-submeta">{submeta}</p>
      {dataSources}
      <div className="doc-body-inner">
        {children}
      </div>
      <div className="doc-rule" />
      <div className="doc-footer">
        <span>Origin Advisory · originadvisory.com · hello@originadvisory.com</span>
        <span>Page 1 of 1</span>
      </div>
    </div>
  );
}

// ── Human Review Modal ────────────────────────────────────────────────────
function HumanReviewModal({ open, onClose, toolLabel, country, sectors }) {
  const [name,     setName]     = useState("");
  const [org,      setOrg]      = useState("");
  const [email,    setEmail]    = useState("");
  const [phone,    setPhone]    = useState("");
  const [notes,    setNotes]    = useState("");
  const [submitted,setSubmitted] = useState(false);
  const [expanded, setExpanded]  = useState(false);

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

  const handleSubmit = e => {
    e.preventDefault();
    console.log("Human review request:", { name, org, email, phone, notes, toolLabel, country, sectors });
    setSubmitted(true);
  };

  if (!open) return null;

  return (
    <div className="hrm-backdrop" onClick={onClose}>
      <div className="hrm-modal" onClick={e => e.stopPropagation()}>
        <button className="hrm-close" onClick={onClose} aria-label="Close">
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M2 2 L14 14 M14 2 L2 14"/></svg>
        </button>

        {submitted ? (
          <div className="hrm-success">
            <div className="hrm-success-icon">✓</div>
            <h3>Got it.</h3>
            <p>We'll be in touch within 24 hours to confirm next steps. Your reviewed version will be ready in 5 working days.</p>
          </div>
        ) : (
          <>
            <h3 className="hrm-heading">Request your free human-reviewed version</h3>
            <p className="hrm-sub">We'll review your draft, add real funder data and regional context, and return a polished version within 5 working days. Free during our pilot phase.</p>

            <div className="hrm-context">
              <div className="hrm-context-row"><span className="hrm-context-label">Output type</span><span className="hrm-context-val">{toolLabel || "—"}</span></div>
              <div className="hrm-context-row"><span className="hrm-context-label">Country</span><span className="hrm-context-val">{country || "—"}</span></div>
              <div className="hrm-context-row"><span className="hrm-context-label">Sector(s)</span><span className="hrm-context-val">{(sectors && sectors.length) ? sectors.join(", ") : "—"}</span></div>
              <button className="hrm-expand-btn" onClick={() => setExpanded(x => !x)}>
                {expanded ? "Hide draft ↑" : "View your draft ↓"}
              </button>
              {expanded && <p className="hrm-draft-note">Your draft is shown in the results view. Submit this form and our team will review it against your inputs.</p>}
            </div>

            <form className="hrm-form" onSubmit={handleSubmit}>
              <div className="hrm-field">
                <label>Name *</label>
                <input type="text" required value={name} onChange={e => setName(e.target.value)} placeholder="Your name" />
              </div>
              <div className="hrm-field">
                <label>Organisation *</label>
                <input type="text" required value={org} onChange={e => setOrg(e.target.value)} placeholder="Organisation name" />
              </div>
              <div className="hrm-field">
                <label>Email *</label>
                <input type="email" required value={email} onChange={e => setEmail(e.target.value)} placeholder="your@email.com" />
              </div>
              <div className="hrm-field">
                <label>Phone <span className="hrm-optional">(optional)</span></label>
                <input type="tel" value={phone} onChange={e => setPhone(e.target.value)} placeholder="+44 or local number" />
              </div>
              <div className="hrm-field">
                <label>Anything specific you want us to focus on? <span className="hrm-optional">(optional)</span></label>
                <textarea rows="3" value={notes} onChange={e => setNotes(e.target.value)} placeholder="e.g. Northern Province only, specific funders to include…" />
              </div>
              <button type="submit" className="btn btn-primary hrm-submit">Request review →</button>
            </form>
          </>
        )}
      </div>
    </div>
  );
}

// ── Intelligence — Hinge-style flow ──────────────────────────────────────

const INTEL_BG = {
  "Sri Lanka":   "dinuka-lankaloka-iduEaeBB_rQ-unsplash.jpg",
  "India":       "tomas-malik-CKEmZAw0Z8c-unsplash.jpg",
  "Bangladesh":  "roxanne-desgagnes-Jc-XDKM6bZk-unsplash.jpg",
  "Nepal":       "hendrik-cornelissen-jpTT_SAU034-unsplash.jpg",
  "Pakistan":    "alex-azabache-8weolGgaa9w-unsplash.jpg",
  "Bhutan":      "siarhei-palishchuk-kSUDzuVslS0-unsplash.jpg",
  "Maldives":    "jerry-kavan-i9eaAR4dWi8-unsplash.jpg",
  "Afghanistan": "tomas-malik-CKEmZAw0Z8c-unsplash.jpg",
};
const INTEL_DEFAULT_BG  = "dinuka-lankaloka-iduEaeBB_rQ-unsplash.jpg";
const INTEL_ALL_BGS     = [...new Set([INTEL_DEFAULT_BG, ...Object.values(INTEL_BG)])];

const INTEL_TOOL_LABELS = {
  readiness:        "Funding Readiness Scan",
  "donor-shortlist":"Donor Shortlist",
  "project-brief":  "Project Brief",
  "sector-snapshot":"Sector Snapshot",
  "me-indicators":  "M&E Indicator Set",
  "pitch-review":   "Pitch Review",
  toc:              "Theory of Change",
};

const IQ_TOOLS    = [
  { id:"readiness",        label:"Funding Readiness Scan" },
  { id:"donor-shortlist",  label:"Donor Shortlist"        },
  { id:"project-brief",    label:"Project Brief"          },
  { id:"sector-snapshot",  label:"Sector Snapshot"        },
  { id:"me-indicators",    label:"M&E Indicator Set"      },
  { id:"pitch-review",     label:"Pitch Review"           },
  { id:"toc",              label:"Theory of Change"       },
];
const IQ_COUNTRIES = ["Sri Lanka","India","Bangladesh","Nepal","Pakistan","Bhutan","Maldives","Afghanistan","Other"];
const IQ_SECTORS   = ["Education","Health","Youth Employment","Women's Empowerment","Climate Resilience","SME Development","Livelihoods","Governance","Other"];
const IQ_FUNDING   = ["Under £10k","£10k–£50k","£50k–£250k","£250k+","Not sure yet"];

function IntelligencePage() {
  const TOTAL = 7;
  const [step,    setStep]    = useState(1);
  const [visible, setVisible] = useState(true);
  const [mode,    setMode]    = useState("q"); // "q" | "loading" | "result"

  // answers
  const [toolType, setToolType] = useState(null);
  const [country,  setCountry]  = useState(null);
  const [sectors,  setSectors]  = useState([]);
  const [orgName,  setOrgName]  = useState("");
  const [orgDesc,  setOrgDesc]  = useState("");
  const [funding,  setFunding]  = useState(null);
  const [focus,    setFocus]    = useState("");
  const [dynamic,  setDynamic]  = useState("");
  const [showReviewModal, setShowReviewModal] = useState(false);

  // pending chip selections
  const [pCountry, setPCountry] = useState(null);
  const [pSectors, setPSectors] = useState([]);

  // hybrid text inputs (one per hybrid-capable step)
  const [step1Text, setStep1Text] = useState("");
  const [step2Text, setStep2Text] = useState("");
  const [step3Text, setStep3Text] = useState("");
  const [step5Text, setStep5Text] = useState("");

  // API / loading
  const [apiData,      setApiData]      = useState(null);
  const [apiError,     setApiError]     = useState(false);
  const [loadingPhase, setLoadingPhase] = useState(0);
  const [loadingMsgs,  setLoadingMsgs]  = useState([]);

  // parsed intent from step-1 free text
  const [parsedIntent,     setParsedIntent]     = useState(null);
  const [showConfirmBanner,setShowConfirmBanner] = useState(false);

  const activeBg  = (country && INTEL_BG[country]) || INTEL_DEFAULT_BG;
  const refNum    = useRef(Math.floor(1000 + Math.random() * 9000));
  const issued    = new Date().toLocaleDateString("en-GB", { day:"numeric", month:"long", year:"numeric" });

  // Cycle loading messages every 2.5 s while loading
  useEffect(() => {
    if (mode !== "loading" || loadingMsgs.length === 0) return;
    const id = setInterval(() => {
      setLoadingPhase(p => {
        if (p >= loadingMsgs.length - 1) { clearInterval(id); return p; }
        return p + 1;
      });
    }, 2500);
    return () => clearInterval(id);
  }, [mode, loadingMsgs]);

  const fade = cb => { setVisible(false); setTimeout(() => { cb(); setVisible(true); }, 300); };

  const getLoadingMessages = tt => {
    const sec = sectors[0] || "sector";
    const cty = country   || "your country";
    if (tt === "sector-snapshot") return [
      "Querying IATI for active donors…",
      "Pulling World Bank indicators…",
      "Synthesising your draft…",
    ];
    if (tt === "donor-shortlist") return [
      `Finding donors active in ${cty} ${sec}…`,
      "Cross-referencing recent funding activity…",
      "Building your shortlist…",
    ];
    if (tt === "readiness") return [
      "Benchmarking against sector funding ranges…",
      "Reviewing your organisation profile…",
      "Generating your readiness assessment…",
    ];
    return ["Generating your 60-second draft…"];
  };

  const next = () => {
    if (step < TOTAL) { fade(() => setStep(s => s + 1)); }
    else {
      const isApi = API_BACKED_TOOLS.has(toolType);
      const msgs  = getLoadingMessages(toolType);
      setLoadingMsgs(msgs);
      setLoadingPhase(0);
      fade(() => {
        setMode("loading");
        if (isApi) {
          const ep = toolType === "readiness" ? "funding-readiness" : toolType;
          fetch(`/api/intelligence/${ep}`, {
            method:  "POST",
            headers: { "Content-Type": "application/json" },
            body:    JSON.stringify({ country, sectors, fundingNeed: funding, organisationDescription: orgDesc }),
          })
            .then(r => r.json())
            .then(data => { setApiData(data); setApiError(!data.apiSource); fade(() => setMode("result")); })
            .catch(() => { setApiData(null); setApiError(true); fade(() => setMode("result")); });
        } else {
          setTimeout(() => fade(() => setMode("result")), 3000);
        }
      });
    }
  };

  const back = () => { if (step > 1) { fade(() => setStep(s => s - 1)); setShowConfirmBanner(false); } };

  const reset = () => fade(() => {
    setStep(1); setMode("q"); setToolType(null); setCountry(null);
    setSectors([]); setOrgName(""); setOrgDesc(""); setFunding(null);
    setFocus(""); setDynamic(""); setPCountry(null); setPSectors([]);
    setStep1Text(""); setStep2Text(""); setStep3Text(""); setStep5Text("");
    setParsedIntent(null); setShowConfirmBanner(false);
    setApiData(null); setApiError(false); setLoadingPhase(0); setLoadingMsgs([]);
  });

  const dynConf = () => {
    if (toolType === "readiness")       return { q:"Do you have existing funder relationships?",    type:"chips", opts:["Yes — active funders","Some — past only","No — first time"] };
    if (toolType === "donor-shortlist") return { q:"Any funders you’ve already approached?",  type:"text",  ph:"e.g. FCDO, UN Women, Aga Khan Foundation (leave blank if none)" };
    if (toolType === "project-brief")   return { q:"What problem are you trying to solve?",         type:"area",  ph:"Describe the development challenge your programme addresses…" };
    if (toolType === "sector-snapshot") return { q:"Any specific focus within the sector?",          type:"text",  ph:"e.g. girls’ education, coastal communities (optional)" };
    if (toolType === "me-indicators")   return { q:"How long is your programme?",                    type:"chips", opts:["Under 6 months","6–12 months","1–2 years","Over 2 years"] };
    if (toolType === "pitch-review")    return { q:"Paste your pitch text",                          type:"area",  ph:"Paste the text you’d like reviewed — concept note, email pitch, or summary…" };
    if (toolType === "toc")             return { q:"What is your long-term impact goal?",            type:"text",  ph:"e.g. Reduced poverty among coastal fishing communities" };
    return { q:"Any additional context?", type:"text", ph:"Optional" };
  };

  // Step-1 free-text intent parsing
  const handleStep1Continue = async () => {
    if (toolType) { fade(() => setStep(2)); return; }
    if (!step1Text.trim()) return;
    try {
      const resp   = await fetch("/api/intelligence/parse-intent", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ rawText: step1Text }),
      });
      const parsed = await resp.json();
      if (parsed.tool) {
        setToolType(parsed.tool);
        let jumpStep = 2;
        if (parsed.country) {
          setCountry(parsed.country); setPCountry(parsed.country); setStep2Text(parsed.country);
          jumpStep = Math.max(jumpStep, 3);
        }
        if (parsed.sectors?.length > 0) {
          setSectors(parsed.sectors); setPSectors(parsed.sectors); setStep3Text(parsed.sectors.join(", "));
          jumpStep = Math.max(jumpStep, 4);
        }
        if (parsed.focus) setFocus(parsed.focus);
        setParsedIntent(parsed);
        setShowConfirmBanner(true);
        fade(() => setStep(jumpStep));
      } else {
        fade(() => setStep(2));
      }
    } catch { fade(() => setStep(2)); }
  };

  const renderConfirmBanner = () => {
    if (!showConfirmBanner || !parsedIntent) return null;
    return (
      <div className="iq-confirm-banner">
        <span className="iq-confirm-label">We understood:</span>
        {parsedIntent.tool && <span className="iq-confirm-tag">{INTEL_TOOL_LABELS[parsedIntent.tool]}</span>}
        {parsedIntent.country && <span className="iq-confirm-tag">{parsedIntent.country}</span>}
        {parsedIntent.sectors?.map(s => <span key={s} className="iq-confirm-tag">{s}</span>)}
        {parsedIntent.focus && <span className="iq-confirm-tag">{parsedIntent.focus}</span>}
        <button className="iq-confirm-edit" onClick={() => { setShowConfirmBanner(false); fade(() => setStep(1)); }}>Edit →</button>
      </div>
    );
  };

  const renderStep = () => {
    if (step === 1) return (
      <>
        <div className="iq-pilot-badge">PILOT PHASE — FREE HUMAN REVIEW</div>
        <h2 className="iq-headline">Get a draft in 60 seconds.<br />Get the real thing from our team — free during pilot.</h2>
        <p className="iq-sub">Describe what you need, or pick an option below.</p>
        <div className="iq-hybrid-wrap">
          <input className="iq-hybrid-input" type="text"
            placeholder="e.g. I need to find funders for a girls’ education project in Jaffna"
            value={step1Text}
            onChange={e => { setStep1Text(e.target.value); if (toolType) setToolType(null); }}
            onKeyDown={e => { if (e.key === "Enter" && (toolType || step1Text.trim())) handleStep1Continue(); }}
            autoFocus
          />
        </div>
        <div className="iq-tool-grid">
          {IQ_TOOLS.map(t => (
            <button key={t.id}
              className={`iq-tool-btn${toolType === t.id ? " iq-tool-btn-sel" : ""}`}
              onClick={() => { setToolType(t.id); setStep1Text(t.label); fade(() => setStep(2)); }}>
              {t.label}
            </button>
          ))}
        </div>
        <button className="iq-continue" disabled={!toolType && !step1Text.trim()} onClick={handleStep1Continue}>
          Continue →
        </button>
      </>
    );

    if (step === 2) return (
      <>
        {renderConfirmBanner()}
        <h2 className="iq-headline">Where are you working?</h2>
        <div className="iq-hybrid-wrap">
          <input className="iq-hybrid-input" type="text"
            placeholder="Type a country… or pick one below"
            value={step2Text}
            onChange={e => { setStep2Text(e.target.value); setPCountry(null); }}
            onKeyDown={e => { if (e.key === "Enter" && (pCountry || step2Text.trim())) { setCountry(pCountry || step2Text.trim()); next(); } }}
          />
        </div>
        <div className="iq-chips">
          {IQ_COUNTRIES.map(c => (
            <button key={c} className={`iq-chip${pCountry === c ? " sel" : ""}`}
              onClick={() => { setPCountry(c); setStep2Text(c); }}>{c}</button>
          ))}
        </div>
        <button className="iq-continue" disabled={!pCountry && !step2Text.trim()}
          onClick={() => { setCountry(pCountry || step2Text.trim()); next(); }}>Continue →</button>
      </>
    );

    if (step === 3) return (
      <>
        {renderConfirmBanner()}
        <h2 className="iq-headline">Which sector?</h2>
        <p className="iq-sub">Select all that apply, or describe below.</p>
        <div className="iq-hybrid-wrap">
          <input className="iq-hybrid-input" type="text"
            placeholder="Type sectors… or pick below (select multiple)"
            value={step3Text}
            onChange={e => setStep3Text(e.target.value)}
            onKeyDown={e => { if (e.key === "Enter" && (pSectors.length > 0 || step3Text.trim())) { setSectors(pSectors.length > 0 ? pSectors : [step3Text.trim()]); next(); } }}
          />
        </div>
        <div className="iq-chips">
          {IQ_SECTORS.map(s => (
            <button key={s} className={`iq-chip${pSectors.includes(s) ? " sel" : ""}`}
              onClick={() => {
                const upd = pSectors.includes(s) ? pSectors.filter(x => x !== s) : [...pSectors, s];
                setPSectors(upd); setStep3Text(upd.join(", "));
              }}>{s}</button>
          ))}
        </div>
        <button className="iq-continue" disabled={pSectors.length === 0 && !step3Text.trim()}
          onClick={() => { setSectors(pSectors.length > 0 ? pSectors : [step3Text.trim()]); next(); }}>Continue →</button>
      </>
    );

    if (step === 4) return (
      <>
        {renderConfirmBanner()}
        <h2 className="iq-headline">Tell us about your organisation</h2>
        <div className="iq-fields">
          <input className="iq-input" type="text" placeholder="Organisation name *"
            value={orgName} onChange={e => setOrgName(e.target.value)} autoFocus />
          <input className="iq-input" type="text" placeholder="One-line description (optional)"
            value={orgDesc} onChange={e => setOrgDesc(e.target.value)}
            onKeyDown={e => { if (e.key === "Enter" && orgName.trim()) next(); }} />
        </div>
        <button className="iq-continue" disabled={!orgName.trim()} onClick={next}>Continue →</button>
      </>
    );

    if (step === 5) return (
      <>
        {renderConfirmBanner()}
        <h2 className="iq-headline">What’s your funding need?</h2>
        <div className="iq-hybrid-wrap">
          <input className="iq-hybrid-input" type="text"
            placeholder="e.g. £25,000 or select a range below"
            value={step5Text}
            onChange={e => { setStep5Text(e.target.value); setFunding(null); }}
            onKeyDown={e => { if (e.key === "Enter" && (funding || step5Text.trim())) { if (!funding) setFunding(step5Text.trim()); next(); } }}
          />
        </div>
        <div className="iq-chips">
          {IQ_FUNDING.map(f => (
            <button key={f} className={`iq-chip${funding === f ? " sel" : ""}`}
              onClick={() => { setFunding(f); setStep5Text(f); setTimeout(next, 200); }}>{f}</button>
          ))}
        </div>
        <button className="iq-continue" disabled={!funding && !step5Text.trim()}
          onClick={() => { if (!funding && step5Text.trim()) setFunding(step5Text.trim()); next(); }}>Continue →</button>
      </>
    );

    if (step === 6) return (
      <>
        {renderConfirmBanner()}
        <h2 className="iq-headline">Anything specific to focus on?</h2>
        <p className="iq-sub">Optional — press Skip to continue.</p>
        <textarea className="iq-textarea" rows="3"
          placeholder="e.g. Northern Province only, girls’ education, diaspora funding…"
          value={focus} onChange={e => setFocus(e.target.value)}
          onKeyDown={e => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); next(); } }} />
        <div className="iq-btn-row">
          <button className="iq-skip" onClick={next}>Skip</button>
          <button className="iq-continue" style={{flex:1}} onClick={next}>Continue →</button>
        </div>
      </>
    );

    if (step === 7) {
      const { q, type, opts, ph } = dynConf();
      return (
        <>
          {renderConfirmBanner()}
          <h2 className="iq-headline">{q}</h2>
          {type === "chips" && (
            <div className="iq-chips">
              {opts.map(o => (
                <button key={o} className={`iq-chip${dynamic === o ? " sel" : ""}`}
                  onClick={() => { setDynamic(o); setTimeout(next, 200); }}>{o}</button>
              ))}
            </div>
          )}
          {type === "text" && <>
            <input className="iq-input" type="text" placeholder={ph}
              value={dynamic} onChange={e => setDynamic(e.target.value)}
              onKeyDown={e => { if (e.key === "Enter") next(); }} autoFocus />
            <button className="iq-continue" style={{marginTop:16}} onClick={next}>Generate →</button>
          </>}
          {type === "area" && <>
            <textarea className="iq-textarea" rows="5" placeholder={ph}
              value={dynamic} onChange={e => setDynamic(e.target.value)} autoFocus />
            <div className="iq-btn-row">
              <button className="iq-skip" onClick={next}>Skip</button>
              <button className="iq-continue" style={{flex:1}} onClick={next}>Generate →</button>
            </div>
          </>}
        </>
      );
    }
  };

  const renderResult = () => {
    const org  = orgName || "Sample Organisation";
    const cty  = country || "Sri Lanka";
    const sec  = sectors.length ? sectors.join(", ") : "Education";

    const isApi      = API_BACKED_TOOLS.has(toolType);
    const hasApiData = isApi && apiData?.apiSource;
    const fmtGBP     = n => "£" + Math.round(n).toLocaleString("en-GB");
    const budgetTotal = BUDGET_AMOUNTS[funding] || 25000;

    const docTitles = {
      "readiness":        `Funding Readiness Scan: ${org}`,
      "donor-shortlist":  `Donor Shortlist: ${sec.split(",")[0]} in ${cty}`,
      "project-brief":    `Project Brief: ${sec.split(",")[0]} Programme, ${cty}`,
      "sector-snapshot":  `Sector Snapshot: ${sec.split(",")[0]}, ${cty}`,
      "me-indicators":    `M&E Indicator Set: ${sec.split(",")[0]} Programme`,
      "pitch-review":     `Pitch Review: ${org}`,
      "toc":              `Theory of Change: ${org}`,
    };
    const docTitle   = docTitles[toolType] || (INTEL_TOOL_LABELS[toolType] || "Output");
    const docSubmeta = `Prepared for ${org} · ${cty} · ${sec}`;

    const wbIndicators = hasApiData && apiData.worldbank ? Object.entries(apiData.worldbank) : [];
    const apiDonors    = hasApiData && apiData.iati?.donors?.length > 0 ? apiData.iati.donors.slice(0, 4) : [];

    const dataSources = hasApiData ? (
      <div className="doc-data-sources">
        <div className="doc-ds-heading">DATA SOURCES</div>
        <p className="doc-ds-body">This draft uses live data from:</p>
        <ul className="doc-ds-list">
          {apiData.iati?.total > 0 && <li>IATI Datastore — {apiData.iati.total} active development projects in {cty} (last 24 months)</li>}
          <li>World Bank Open Data — {cty} development indicators</li>
          <li>AI-generated analysis and synthesis</li>
        </ul>
        {apiError && <p className="doc-ds-fallback">Live data partially unavailable — some sections use general knowledge.</p>}
      </div>
    ) : (isApi && apiError) ? (
      <div className="doc-data-sources doc-ds-fallback-only">
        <p className="doc-ds-fallback">Live data temporarily unavailable — output uses general knowledge only.</p>
      </div>
    ) : null;

    const bannerTitle = hasApiData
      ? "This is your AI draft, backed by live data from IATI and World Bank."
      : "This is your 60-second AI draft.";
    const bannerText  = hasApiData
      ? "It’s a starting point — not a finished deliverable. Our research team will verify the data, add Sri Lanka-specific context and current funder windows, and return a polished version within 5 working days. Free during pilot."
      : "It’s a starting point — not a finished deliverable. Our research team will review, fact-check, add real funder data and Sri Lanka-specific context, and return a polished version within 5 working days. Free during pilot.";

    return (
      <div className="ir-wrap">
        <div className="ir-draft-banner">
          <div className="ir-draft-banner-body">
            <p className="ir-draft-banner-title">{bannerTitle}</p>
            <p className="ir-draft-banner-text">{bannerText}</p>
          </div>
          <button className="ir-review-btn" onClick={() => setShowReviewModal(true)}>
            Request free human review →
          </button>
        </div>

        <DocShell refNum={refNum.current} issued={issued} title={docTitle} submeta={docSubmeta} dataSources={dataSources}>

          {toolType === "readiness" && (
            <div className="ir-body">
              <div className="ir-score-block">
                <div className="ir-score-label">Funding Readiness Score</div>
                <div className="ir-score"><span className="ir-score-num">64</span><span className="ir-score-denom">/100</span><span className="ir-score-tier">Developing</span></div>
                <div className="scan-progress-bar" style={{marginTop:10}}><div className="scan-progress-fill" style={{width:"64%"}} /></div>
              </div>
              {wbIndicators.length > 0 && (
                <div className="ir-card">
                  <div className="ir-card-title">Sector context ({cty})</div>
                  {apiData.iati?.total > 0 && <p style={{fontSize:13,color:"var(--ink-soft)",margin:"0 0 10px"}}><strong>{apiData.iati.total}</strong> active IATI projects in {cty} {sectors[0] || ""} (last 2 years).</p>}
                  {wbIndicators.slice(0,3).map(([id, ind]) => (
                    <div key={id} className="doc-wb-row">
                      <span className="doc-wb-label">{ind.label}</span>
                      <span className="doc-wb-value">{typeof ind.value === "number" ? ind.value.toLocaleString("en-GB",{maximumFractionDigits:1}) : ind.value} <span className="doc-wb-date">({ind.date})</span></span>
                    </div>
                  ))}
                  <p className="doc-source-note">Source: World Bank Open Data · IATI Datastore</p>
                </div>
              )}
              <div className="ir-grid-2">
                <div className="ir-card"><div className="ir-card-title">Strengths</div>
                  {["Clear organisational identity and geographic focus","Evidence of programme delivery and reach","Existing relationship with at least one donor","Documented registration and governance"].map((s,i) => <div key={i} className="scan-check-item scan-check-yes"><span>✓</span>{s}</div>)}
                </div>
                <div className="ir-card"><div className="ir-card-title">Gaps to address</div>
                  {["No current audited accounts on file","Theory of change not documented","Impact data qualitative only — no outcome indicators","Safeguarding policy not confirmed"].map((g,i) => <div key={i} className="scan-check-item scan-check-no"><span>✗</span>{g}</div>)}
                </div>
                <div className="ir-card"><div className="ir-card-title">Suggested funders</div>
                  {["FCDO — Small Grants Programme","EU Civil Society Fund Sri Lanka","UNDP Sri Lanka Small Grants","Aga Khan Foundation","Asia Foundation — Sri Lanka"].map((f,i) => <div key={i} className="scan-funder-tag">{f}</div>)}
                </div>
                <div className="ir-card"><div className="ir-card-title">Next steps</div>
                  {["Commission an audited set of accounts (priority — blocks most funders)","Draft a one-page theory of change","Set 3–5 outcome indicators for your programme","Contact UNDP Sri Lanka Small Grants (open Q3 2025)","Apply for Origin pilot support"].map((s,i) => <div key={i} className="scan-step"><span className="scan-step-num">{i+1}</span>{s}</div>)}
                </div>
              </div>
            </div>
          )}

          {toolType === "donor-shortlist" && (
            <div className="ir-body">
              <p className="ir-body-intro">Matched donors for <strong>{sec.split(",")[0]}</strong> work in <strong>{cty}</strong> · {funding || "£10k–£50k"}</p>
              {apiDonors.length > 0 && (
                <>
                  <div className="doc-section-heading">ACTIVE DONORS — IATI DATA</div>
                  {apiDonors.map((d,i) => (
                    <div key={i} className="ir-donor">
                      <div className="ir-donor-top">
                        <div><span className="ir-donor-name">{d.name}</span><span className="ir-donor-type">{d.type}</span></div>
                        <span className="ir-fit ir-fit-high">Active</span>
                      </div>
                      <div className="ir-donor-window">
                        {d.activities} activit{d.activities === 1 ? "y" : "ies"} tracked
                        {d.totalBudget > 0 ? ` · ${fmtGBP(d.totalBudget)} committed (IATI, 2024)` : ""}
                      </div>
                      <p className="ir-donor-note">Active in {cty} {sectors[0] || "sector"} per IATI Datastore.</p>
                    </div>
                  ))}
                  <div className="doc-section-heading" style={{marginTop:20}}>AI ANALYSIS — ADDITIONAL MATCHES</div>
                </>
              )}
              {[
                { name:"FCDO — Small Grants Programme",     type:"Bilateral",  fit:"High",        window:"Rolling (closes Sep 2025)", note:"Strong match. Actively funds NGOs in Sri Lanka. Requires audited accounts and safeguarding policy." },
                { name:"UN Women Sri Lanka",                     type:"UN Agency",  fit:"High",        window:"Q3 2025 open call",         note:"Direct alignment. Expression of Interest required before full proposal." },
                { name:"Aga Khan Foundation — South Asia",  type:"Foundation", fit:"Medium-High", window:"Annual (Nov 2025)",          note:"Good fit for economic empowerment. Prefers 3+ years operating history." },
                { name:"EU Civil Society Fund — Sri Lanka", type:"Bilateral",  fit:"Medium-High", window:"Open (Aug 2025)",            note:"Requires EU registration or local partner. Strong match for governance components." },
                { name:"Asia Foundation — Sri Lanka",       type:"Foundation", fit:"Medium",      window:"Rolling",                    note:"Active in gender and economic empowerment. Smaller grant sizes ($10–$50k)." },
                { name:"Oxfam Sri Lanka",                        type:"INGO",       fit:"Medium",      window:"Project-specific",           note:"Sub-granting via Oxfam programmes. Best approached through country office." },
              ].map((d,i) => (
                <div key={i} className="ir-donor">
                  <div className="ir-donor-top">
                    <div><span className="ir-donor-name">{d.name}</span><span className="ir-donor-type">{d.type}</span></div>
                    <span className={`ir-fit ir-fit-${d.fit.toLowerCase().replace(/[- ]/g,"")}`}>{d.fit} fit</span>
                  </div>
                  <div className="ir-donor-window">Open window: {d.window}</div>
                  <p className="ir-donor-note">{d.note}</p>
                </div>
              ))}
            </div>
          )}

          {toolType === "project-brief" && (
            <div className="ir-body">
              <div className="ir-doc-section"><h5>Executive Summary</h5>
                <p>This brief outlines a 12-month {sec.split(",")[0].toLowerCase()} programme in {cty}. The intervention addresses documented gaps in service delivery, combining capacity building, community engagement, and institutional partnerships to reach underserved populations.</p>
                <p>We are requesting {fmtGBP(budgetTotal)} to deliver measurable outcomes for a defined target group within the programme period.</p>
              </div>
              <div className="ir-doc-section"><h5>Context</h5>
                <p>Development indicators in {cty} show persistent challenges in {sec.split(",")[0].toLowerCase()}. External development finance in this sector has grown 18% year-on-year. Civil society organisations play a critical implementation role that remains systematically under-resourced.</p>
              </div>
              <div className="ir-doc-section"><h5>Theory of Change</h5>
                <p><strong>If</strong> target communities receive well-designed, evidence-based interventions, <strong>then</strong> individual-level outcomes will improve, <strong>leading to</strong> measurable progress against national development indicators.</p>
              </div>
              <div className="ir-doc-section"><h5>Budget (indicative)</h5>
                <table className="intel-brief-table"><tbody>
                  {BUDGET_BREAKDOWN.map((item,i) => (
                    <tr key={i}>
                      <td>{item.label}</td>
                      <td style={{textAlign:"center",color:"var(--ink-soft)"}}>{Math.round(item.pct*100)}%</td>
                      <td style={{textAlign:"right"}}>{fmtGBP(Math.round(budgetTotal*item.pct))}</td>
                    </tr>
                  ))}
                  <tr className="intel-brief-table-total">
                    <td><strong>Total budget</strong></td><td></td>
                    <td style={{textAlign:"right"}}><strong>{fmtGBP(budgetTotal)}</strong></td>
                  </tr>
                </tbody></table>
                {budgetTotal >= 180000 && (
                  <p className="doc-budget-note">Origin note: This budget is at the upper end for the described programme scope. Human-reviewed version will validate against comparable Sri Lanka programmes.</p>
                )}
              </div>
            </div>
          )}

          {toolType === "sector-snapshot" && (
            <div className="ir-body">
              {wbIndicators.length > 0 && (
                <div className="ir-snap-section">
                  <h5>Key development indicators ({cty})</h5>
                  {wbIndicators.map(([id, ind]) => (
                    <div key={id} className="doc-wb-row">
                      <span className="doc-wb-label">{ind.label}</span>
                      <span className="doc-wb-value">
                        {typeof ind.value === "number" ? ind.value.toLocaleString("en-GB",{maximumFractionDigits:1}) : ind.value}
                        {" "}<span className="doc-wb-date">({ind.date})</span>
                      </span>
                    </div>
                  ))}
                  <p className="doc-source-note">Source: World Bank Open Data</p>
                </div>
              )}
              <div className="ir-snap-section"><h5>Funding landscape</h5>
                <p>{sec.split(",")[0]} in {cty} receives significant external development finance{hasApiData && apiData.iati?.total > 0 ? `, with ${apiData.iati.total} active IATI-registered projects in the last 24 months.` : ", with major donors including FCDO, UNICEF, ADB, EU, and World Bank."} Combined external flows are estimated at $120–200M annually, with growth driven by post-crisis recovery and climate-resilience integration.</p>
              </div>
              {apiDonors.length > 0 && (
                <div className="ir-snap-section">
                  <h5>Most active donors (IATI data)</h5>
                  {apiDonors.map((d,i) => (
                    <div key={i} className="intel-snapshot-actor"><strong>{d.name}</strong> — {d.type}. {d.activities} activit{d.activities===1?"y":"ies"} tracked{d.totalBudget>0?`, ${fmtGBP(d.totalBudget)} committed`:""}.</div>
                  ))}
                  <p className="doc-source-note">Source: IATI Datastore</p>
                </div>
              )}
              <div className="ir-snap-section"><h5>Key actors</h5>
                {[{n:"FCDO",r:"Bilateral donor — sector-wide programming and civil society partnerships"},{n:"UNICEF",r:"UN agency — community-level implementation and systems strengthening"},{n:"ADB",r:"Multilateral — infrastructure and institutional capacity"},{n:"Local civil society",r:"Primary implementation — 40+ active NGOs tracked by Origin"}].map((a,i) => <div key={i} className="intel-snapshot-actor"><strong>{a.n}</strong> — {a.r}</div>)}
              </div>
              <div className="ir-snap-section"><h5>Trends</h5>
                <ul style={{margin:0,paddingLeft:18,color:"var(--ink-soft)",fontSize:14,lineHeight:1.8}}>
                  <li>Donor coordination improving — joint programming increasing</li>
                  <li>Localisation agenda driving sub-granting to smaller NGOs</li>
                  <li>Climate integration now required in most bilateral calls</li>
                  <li>Digital delivery components increasingly fundable post-COVID</li>
                </ul>
              </div>
              <div className="ir-snap-section"><h5>Open windows (May 2026)</h5>
                {[{d:"FCDO Sri Lanka",w:"Small Grants — closes Sep 2026"},{d:"UNICEF",w:"Civil society call — Q3 2026"},{d:"EU Civil Society Fund",w:"Expressions of Interest open"}].map((w,i) => <div key={i} className="intel-snapshot-window"><span className="intel-snapshot-donor">{w.d}</span>{w.w}</div>)}
              </div>
            </div>
          )}

          {toolType === "me-indicators" && (
            <div className="ir-body">
              <p className="ir-body-intro">Indicator set for <strong>{sec.split(",")[0]}</strong> programme in <strong>{cty}</strong> · FCDO-aligned framework</p>
              {[
                { level:"Output indicators", color:"output", items:[
                  {ind:"Number of beneficiaries reached",src:"Attendance registers",freq:"Monthly",target:"TBD by baseline"},
                  {ind:"% female / youth / marginalised group",src:"Registration data",freq:"Monthly",target:"≥ 50%"},
                  {ind:"Number of activities delivered",src:"Activity logs",freq:"Monthly",target:"Per work plan"},
                ]},
                { level:"Outcome indicators", color:"outcome", items:[
                  {ind:"% beneficiaries reporting measurable change (vs baseline)",src:"Endline survey",freq:"6-month follow-up",target:"≥ 55%"},
                  {ind:"% with improved access to services or income",src:"Follow-up survey",freq:"3-month follow-up",target:"≥ 50%"},
                  {ind:"Household food / income security score change",src:"Endline survey",freq:"Endline",target:"+25%"},
                ]},
                { level:"Impact indicators", color:"impact", items:[
                  {ind:"Poverty headcount change in target area",src:"Household survey",freq:"Annual",target:"5% reduction"},
                  {ind:"Alignment to national development plan",src:"Programme reporting",freq:"Annual",target:"Narrative"},
                ]},
              ].map((grp,i) => (
                <div key={i} className={`intel-indicator-group intel-indicator-${grp.color}`}>
                  <h5 className="intel-indicator-level">{grp.level}</h5>
                  {grp.items.map((item,j) => (
                    <div key={j} className="intel-indicator-row">
                      <div className="intel-indicator-name">{item.ind}</div>
                      <div className="intel-indicator-meta"><span>Source: {item.src}</span><span>Frequency: {item.freq}</span><span>Target: {item.target}</span></div>
                    </div>
                  ))}
                </div>
              ))}
            </div>
          )}

          {toolType === "pitch-review" && (
            <div className="ir-body">
              <div className="intel-review-section intel-review-strong"><h5>Strengths</h5><ul>
                <li>Clear problem statement with specific, verifiable need</li>
                <li>Geographic focus is a priority area for target donor</li>
                <li>Theory of change is present and logically structured</li>
                <li>Budget is proportionate and costs appear reasonable</li>
              </ul></div>
              <div className="intel-review-section intel-review-gaps"><h5>Gaps to address</h5><ul>
                <li><strong>No baseline data cited</strong> — institutional donors expect quantified baselines</li>
                <li><strong>Outcome indicators absent</strong> — activities listed but no measurable targets</li>
                <li><strong>Safeguarding section missing</strong> — required by most bilateral and UN donors</li>
                <li><strong>Sustainability not addressed</strong> — add 2–3 sentences on continuity beyond the grant period</li>
              </ul></div>
              <div className="intel-review-section intel-review-language"><h5>Language and framing</h5><ul>
                <li>Replace vague language with specific commitments ("train 150 women in…")</li>
                <li>Avoid passive voice — use active, concrete language in activities section</li>
                <li>Reference the donor’s current country strategy in your opening paragraph</li>
              </ul></div>
              <div className="intel-review-section intel-review-score"><h5>Overall</h5>
                <p>Strong foundation with a clear development logic. Main gaps are fixable before submission.</p>
                <div className="intel-review-rating"><div className="intel-rating-bar-wrap"><div className="intel-rating-bar" style={{width:"72%"}} /></div><span>72 / 100 — Good</span></div>
              </div>
            </div>
          )}

          {toolType === "toc" && (
            <div className="ir-body">
              {[
                {stage:"Inputs",      color:"toc-inputs",     items:["Funding and staff capacity","Partner NGO relationships","Training materials and tools","Community entry points and trust"]},
                {stage:"Activities",  color:"toc-activities", items:["Direct training and capacity building","Community mobilisation and outreach","Stakeholder engagement and coordination","Monitoring data collection"]},
                {stage:"Outputs",     color:"toc-outputs",    items:[`${org} community members reached`,"Partners trained and supported","Resources and tools distributed","Data collection completed"]},
                {stage:"Outcomes (1–2 years)", color:"toc-outcomes", items:["Measurable behaviour or status change among beneficiaries","Improved access to services or economic opportunities","Strengthened organisational or community capacity","Evidence base for sector learning"]},
                {stage:"Impact (3–5 years)",   color:"toc-impact",   items:[dynamic || "Long-term systemic change in target context","Contribution to national development indicators","Sustainable community-led practice beyond programme end"]},
              ].map((s,i) => (
                <div key={i} className={`toc-stage ${s.color}`}>
                  <div className="toc-stage-label">{s.stage}</div>
                  <ul className="toc-stage-items">{s.items.map((item,j) => <li key={j}>{item}</li>)}</ul>
                  {i < 4 && <div className="toc-arrow">↓</div>}
                </div>
              ))}
              <div className="intel-toc-assumptions" style={{marginTop:16}}>
                <h5>Key assumptions</h5>
                <ul>
                  <li>Target communities are accessible and willing to engage</li>
                  <li>Political and security environment remains stable</li>
                  <li>Staff capacity is maintained throughout the programme</li>
                  <li>Co-funding or matched contributions are secured where required</li>
                </ul>
              </div>
            </div>
          )}

        </DocShell>

        <div className="ir-human-review-cta">
          <h4 className="ir-hrc-heading">What you’ll get from human review</h4>
          <ul className="ir-hrc-list">
            <li><span className="ir-hrc-check">✓</span>Real funder names, grant sizes, and current windows (not generic categories)</li>
            <li><span className="ir-hrc-check">✓</span>Verified Sri Lanka and South Asia data — not LLM guesswork</li>
            <li><span className="ir-hrc-check">✓</span>Reviewed and edited by Origin’s research team — ready to use with funders</li>
          </ul>
          <button className="btn btn-primary ir-hrc-btn" onClick={() => setShowReviewModal(true)}>
            Request free human review →
          </button>
          <button className="ir-hrc-reset" onClick={reset}>Generate another draft →</button>
        </div>

        <p className="ir-limitation-note">This draft uses general AI knowledge and your inputs. It does not yet draw on Origin’s curated funder database or verified regional data. The human-reviewed version does.</p>
      </div>
    );
  };

  return (
    <div className="iq-page">
      <div className="iq-bg">
        {INTEL_ALL_BGS.map(img => (
          <div key={img} className={`iq-bg-slide${activeBg === img ? " active" : ""}`}
            style={{backgroundImage:`url(${img})`}} />
        ))}
        <div className="iq-bg-overlay" />
      </div>

      {mode === "q" && (
        <div className={`iq-card${visible ? " iq-show" : " iq-hide"}`}>
          <div className="iq-progress">
            <div className="iq-progress-track">
              <div className="iq-progress-fill" style={{width:`${(step/TOTAL)*100}%`}} />
            </div>
            <span className="iq-progress-label">Step {step} of {TOTAL}</span>
          </div>
          {step > 1 && <button className="iq-back" onClick={back}>← Back</button>}
          {renderStep()}
        </div>
      )}

      {mode === "loading" && (
        <div className={`iq-center-card${visible ? " iq-show" : " iq-hide"}`}>
          <div className="iq-spinner" />
          <p className="iq-loading-text">{loadingMsgs[loadingPhase] || "Generating your 60-second draft…"}</p>
          <p className="iq-loading-sub">AI-assisted · starting point for human review</p>
        </div>
      )}

      {mode === "result" && (
        <div className={`iq-result-card${visible ? " iq-show" : " iq-hide"}`}>
          {renderResult()}
        </div>
      )}

      <HumanReviewModal
        open={showReviewModal}
        onClose={() => setShowReviewModal(false)}
        toolLabel={INTEL_TOOL_LABELS[toolType] || "Output"}
        country={country}
        sectors={sectors}
      />
    </div>
  );
}


// ── Personalised Feed ──────────────────────────────────────────────────────

const SECTOR_KEYWORDS = {
  'sector:education':      ['education', 'school', 'university', 'student', 'learning', 'teacher', 'literacy'],
  'sector:climate':        ['climate', 'environment', 'flood', 'drought', 'cyclone', 'emissions', 'carbon', 'forest', 'adaptation'],
  'sector:health':         ['health', 'hospital', 'medical', 'disease', 'pandemic', 'vaccine', 'nutrition', 'maternal'],
  'sector:infrastructure': ['infrastructure', 'road', 'bridge', 'port', 'railway', 'transport', 'construction', 'highway'],
  'sector:economy':        ['gdp', 'economy', 'economic', 'growth', 'fiscal', 'budget', 'trade', 'finance', 'tax', 'debt', 'imf', 'inflation', 'remittance'],
  'sector:governance':     ['governance', 'corruption', 'election', 'parliament', 'minister', 'policy', 'reform', 'democracy'],
  'sector:security':       ['security', 'military', 'conflict', 'ceasefire', 'army', 'peace', 'insurgency', 'violence', 'war'],
  'sector:agriculture':    ['agriculture', 'farm', 'food', 'rice', 'crop', 'farmer', 'irrigation', 'livestock', 'harvest'],
  'sector:energy':         ['energy', 'power', 'electricity', 'solar', 'renewable', 'dam', 'fuel', 'grid', 'hydro'],
  'sector:humanitarian':   ['humanitarian', 'aid', 'refugee', 'displacement', 'shelter', 'unhcr', 'crisis', 'emergency'],
  'sector:livelihoods':    ['livelihood', 'employment', 'job', 'poverty', 'income', 'microfinance', 'entrepreneur'],
  'sector:diplomacy':      ['diplomatic', 'diplomacy', 'bilateral', 'summit', 'quad', 'saarc', 'geopolitics', 'foreign'],
};

function stripHtml(str) {
  return (str || '').replace(/<[^>]*>/g, '').replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').trim();
}

function getArticleTags(article) {
  const tags = [];
  if (article.country) tags.push(`country:${article.country}`);
  const text = `${article.title || ''} ${article.summary || ''}`.toLowerCase();
  for (const [sector, kws] of Object.entries(SECTOR_KEYWORDS)) {
    if (kws.some(kw => text.includes(kw))) tags.push(sector);
  }
  return tags;
}

const FEED_PREFS_KEY = 'origin_user_prefs';

function defaultFeedPrefs() {
  return { tagWeights: {}, totalSwipes: 0, likedCount: 0, lastUpdated: null, paused: false };
}

function loadFeedPrefs() {
  try {
    const raw = localStorage.getItem(FEED_PREFS_KEY);
    return raw ? { ...defaultFeedPrefs(), ...JSON.parse(raw) } : defaultFeedPrefs();
  } catch { return defaultFeedPrefs(); }
}

function saveFeedPrefs(prefs) {
  try {
    localStorage.setItem(FEED_PREFS_KEY, JSON.stringify({ ...prefs, lastUpdated: new Date().toISOString() }));
  } catch {}
}

function useFeedPrefs() {
  const [prefs, setPrefs] = useState(loadFeedPrefs);
  const update = updater => setPrefs(p => {
    const next = typeof updater === 'function' ? updater(p) : { ...p, ...updater };
    saveFeedPrefs(next);
    return next;
  });
  return [prefs, update];
}

function useIsMobile() {
  const [mob, setMob] = useState(() => window.innerWidth <= 768);
  useEffect(() => {
    const mq = window.matchMedia('(max-width: 768px)');
    const h = e => setMob(e.matches);
    mq.addListener(h);
    return () => mq.removeListener(h);
  }, []);
  return mob;
}

function scoreArticle(article, tags, prefs) {
  const tagScore = tags.reduce((s, t) => s + ((prefs.tagWeights || {})[t] || 0), 0);
  const ageHours = (Date.now() - new Date(article.published).getTime()) / 3600000;
  const recency = Math.max(0, 2 - ageHours / 12);
  return 5 + tagScore + recency;
}

function applySwipe(prefs, tags, isLike) {
  const weights = { ...(prefs.tagWeights || {}) };
  const delta = isLike ? 1 : -0.5;
  for (const tag of tags) {
    weights[tag] = Math.max(-5, Math.min(10, (weights[tag] || 0) + delta));
  }
  return {
    ...prefs,
    tagWeights: weights,
    totalSwipes: (prefs.totalSwipes || 0) + 1,
    likedCount: isLike ? (prefs.likedCount || 0) + 1 : (prefs.likedCount || 0),
  };
}

function buildFeedQueue(articles, prefs) {
  if (prefs.paused) {
    return [...articles]
      .sort((a, b) => new Date(b.published) - new Date(a.published))
      .slice(0, 40)
      .map(a => ({ article: a, tags: getArticleTags(a), score: 0, maxExposure: 0, isDiversity: false }));
  }
  const isColdStart = (prefs.totalSwipes || 0) < 5;
  const tagged = articles.map(a => {
    const tags = getArticleTags(a);
    const maxExposure = tags.reduce((m, t) => Math.max(m, Math.abs((prefs.tagWeights || {})[t] || 0)), 0);
    return { article: a, tags, score: scoreArticle(a, tags, prefs), maxExposure };
  });

  if (isColdStart) {
    const byCountry = {};
    for (const t of tagged) {
      const c = t.article.country || 'Other';
      if (!byCountry[c]) byCountry[c] = [];
      byCountry[c].push(t);
    }
    for (const c of Object.keys(byCountry)) byCountry[c].sort((a, b) => b.score - a.score);
    const result = [], used = new Set(), countries = Object.keys(byCountry);
    let i = 0;
    while (result.length < Math.min(40, articles.length) && i < articles.length * 3) {
      const c = countries[i % countries.length];
      const pick = (byCountry[c] || []).find(t => !used.has(t.article.id));
      if (pick) { used.add(pick.article.id); result.push({ ...pick, isDiversity: false }); }
      i++;
    }
    return result;
  }

  tagged.sort((a, b) => b.score - a.score);
  const mainPool = tagged.filter(t => t.maxExposure >= 2);
  const divPool  = tagged.filter(t => t.maxExposure < 2)
    .sort((a, b) => (b.article.significance || 1) - (a.article.significance || 1));

  const result = [];
  let mi = 0, di = 0;
  for (let pos = 0; pos < Math.min(40, articles.length); pos++) {
    const isDiversitySlot = pos > 0 && (pos + 1) % 4 === 0 && di < divPool.length;
    if (isDiversitySlot)          result.push({ ...divPool[di++], isDiversity: true });
    else if (mi < mainPool.length) result.push({ ...mainPool[mi++], isDiversity: false });
    else if (di < divPool.length)  result.push({ ...divPool[di++], isDiversity: false });
  }
  return result;
}

// ── Feed: Why-popup ────────────────────────────────────────────────────────
function FeedWhyPopup({ tags, prefs, isDiversity, onClose }) {
  const reasons = [];
  if (!isDiversity) {
    const sorted = [...tags]
      .filter(t => (prefs.tagWeights?.[t] || 0) > 0)
      .sort((a, b) => ((prefs.tagWeights || {})[b] || 0) - ((prefs.tagWeights || {})[a] || 0))
      .slice(0, 3);
    for (const tag of sorted) {
      const w = Math.round((prefs.tagWeights || {})[tag] || 0);
      const [type, val] = tag.split(':');
      if (type === 'country') reasons.push(`You've liked ${w} ${w === 1 ? 'story' : 'stories'} about ${val}`);
      else if (type === 'sector') reasons.push(`You follow ${val.charAt(0).toUpperCase() + val.slice(1)} topics`);
    }
    if (!reasons.length) reasons.push('Top story in your region today');
  }
  return (
    <div className="feed-why-popup" onClick={e => e.stopPropagation()}>
      <button className="feed-why-close" onClick={onClose}>×</button>
      {isDiversity ? (
        <>
          <strong>This is a discovery pick.</strong>
          <p>We show one of these every few stories so you don't miss what's happening across Asia.</p>
        </>
      ) : (
        <>
          <strong>You're seeing this because:</strong>
          <ul>{reasons.map((r, i) => <li key={i}>{r}</li>)}</ul>
        </>
      )}
    </div>
  );
}

// ── Feed: Mobile card (swipeable) ──────────────────────────────────────────
function FeedMobileCard({ item, prefs, onLike, onSkip, isTop, stackPos }) {
  const { article, tags, isDiversity } = item;
  const [whyOpen, setWhyOpen]   = useState(false);
  const [dragX, setDragX]       = useState(0);
  const [dragging, setDragging] = useState(false);
  const [flyDir, setFlyDir]     = useState(null);
  const startX = useRef(0);
  const THRESHOLD = 75;

  const doFly = dir => {
    setFlyDir(dir);
    if (navigator.vibrate) navigator.vibrate(dir === 'like' ? [40] : [20]);
    setTimeout(() => { dir === 'like' ? onLike() : onSkip(); }, 370);
  };

  const onTouchStart = e => {
    if (!isTop) return;
    startX.current = e.touches[0].clientX;
    setDragging(true);
    setFlyDir(null);
  };
  const onTouchMove = e => {
    if (!dragging || !isTop) return;
    setDragX(e.touches[0].clientX - startX.current);
  };
  const onTouchEnd = () => {
    setDragging(false);
    if      (dragX >  THRESHOLD) doFly('like');
    else if (dragX < -THRESHOLD) doFly('skip');
    else setDragX(0);
  };

  const tintAlpha = Math.min(Math.abs(dragX) / 160, 0.55);
  const tintColor = dragX > 20
    ? `rgba(56,161,105,${tintAlpha})`
    : dragX < -20 ? `rgba(229,62,62,${tintAlpha})` : 'transparent';

  const topStyle = {
    transform: flyDir === 'like' ? 'translateX(140vw) rotate(20deg)' :
               flyDir === 'skip' ? 'translateX(-140vw) rotate(-20deg)' :
               `translateX(${dragX}px) rotate(${dragX * 0.04}deg)`,
    transition: (dragging && !flyDir) ? 'none' : 'transform 0.37s cubic-bezier(.22,.61,.36,1), opacity 0.37s',
    opacity: flyDir ? 0.4 : 1,
    zIndex: 20,
    touchAction: 'none',
  };
  const bgStyle = {
    transform: `scale(${1 - stackPos * 0.035}) translateY(${stackPos * 14}px)`,
    zIndex: 20 - stackPos,
    opacity: 1 - stackPos * 0.18,
    pointerEvents: 'none',
    touchAction: 'none',
  };

  const sectorTags = tags.filter(t => t.startsWith('sector:')).map(t => t.replace('sector:', ''));
  const countryName = article.country || '';

  return (
    <div
      className="feed-mobile-card"
      style={isTop ? topStyle : bgStyle}
      onTouchStart={onTouchStart}
      onTouchMove={onTouchMove}
      onTouchEnd={onTouchEnd}
    >
      {isTop && <div className="feed-card-tint" style={{ background: tintColor }} />}
      {isTop && dragX > 30  && <div className="feed-card-swipe-label feed-label-like">LIKE ♥</div>}
      {isTop && dragX < -30 && <div className="feed-card-swipe-label feed-label-skip">SKIP ✕</div>}

      {isDiversity && <div className="feed-diversity-chip">Outside your usual</div>}

      {article.image_url ? (
        <div className="feed-mobile-img-wrap">
          <img src={article.image_url} alt="" className="feed-mobile-img"
            onError={e => { e.currentTarget.parentElement.style.display = 'none'; }} />
        </div>
      ) : (
        <div className="feed-mobile-img-placeholder" />
      )}

      <div className="feed-mobile-card-body">
        <div className="feed-card-meta-row">
          <span className="feed-card-country-tag">{countryName}</span>
          {sectorTags.slice(0, 2).map(s => (
            <span key={s} className="feed-card-sector-tag">{s}</span>
          ))}
        </div>
        <h3 className="feed-card-headline">{stripHtml(article.title)}</h3>
        <p className="feed-card-summary-text">
          {stripHtml(article.summary).slice(0, 200)}{stripHtml(article.summary).length > 200 ? '…' : ''}
        </p>
        <div className="feed-card-foot">
          <span className="feed-card-timestamp">{relativeTime(article.published)}</span>
          <div style={{ position: 'relative', display: 'inline-block' }}>
            <button className="feed-why-btn" onClick={e => { e.stopPropagation(); setWhyOpen(o => !o); }}>ⓘ</button>
            {whyOpen && <FeedWhyPopup tags={tags} prefs={prefs} isDiversity={isDiversity} onClose={() => setWhyOpen(false)} />}
          </div>
          <a href={article.url} target="_blank" rel="noopener noreferrer" className="feed-mobile-read-link">
            Read <Icons.ArrowSmall />
          </a>
        </div>
      </div>
    </div>
  );
}

// ── Feed: Desktop card (hover-buttons) ────────────────────────────────────
function FeedDesktopCard({ item, prefs, onLike, onSkip, isFirst, animDir }) {
  const { article, tags, isDiversity } = item;
  const [whyOpen, setWhyOpen] = useState(false);
  const sectorTags  = tags.filter(t => t.startsWith('sector:')).map(t => t.replace('sector:', ''));
  const countryName = article.country || '';
  const sigLabel    = { 4: 'Critical', 3: 'Significant', 2: 'Moderate', 1: 'Local' }[article.significance] || 'Moderate';

  return (
    <div className={`feed-desktop-card${animDir ? ` feed-desktop-fly-${animDir}` : ''}${isFirst ? ' feed-desktop-card-first' : ''}`}>
      {isDiversity && <div className="feed-diversity-chip feed-diversity-chip-desktop">Outside your usual</div>}
      <div className="feed-desktop-card-inner">
        {article.image_url && (
          <div className="feed-desktop-img-wrap">
            <img src={article.image_url} alt="" className="feed-desktop-img"
              onError={e => { e.currentTarget.parentElement.style.display = 'none'; }} />
          </div>
        )}
        <div className="feed-desktop-card-body">
          <div className="feed-card-meta-row">
            <span className="feed-card-country-tag">{countryName}</span>
            {sectorTags.slice(0, 2).map(s => <span key={s} className="feed-card-sector-tag">{s}</span>)}
            <span className="feed-card-sig-tag">{sigLabel}</span>
          </div>
          <h3 className="feed-card-headline feed-desktop-headline">{stripHtml(article.title)}</h3>
          <p className="feed-card-summary-text">
            {stripHtml(article.summary).slice(0, 260)}{stripHtml(article.summary).length > 260 ? '…' : ''}
          </p>
          <div className="feed-card-foot">
            <span className="feed-card-timestamp">{relativeTime(article.published)}</span>
            <div style={{ position: 'relative', display: 'inline-block' }}>
              <button className="feed-why-btn" onClick={e => { e.stopPropagation(); setWhyOpen(o => !o); }}>ⓘ</button>
              {whyOpen && <FeedWhyPopup tags={tags} prefs={prefs} isDiversity={isDiversity} onClose={() => setWhyOpen(false)} />}
            </div>
          </div>
        </div>
        <div className="feed-desktop-actions">
          <button className="feed-action-btn feed-like-btn" onClick={onLike} title="Like (→)">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3H14z"/><path d="M7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"/></svg>
            Like
          </button>
          <button className="feed-action-btn feed-skip-btn" onClick={onSkip} title="Skip (←)">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3H10z"/><path d="M17 2h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"/></svg>
            Skip
          </button>
          <a href={article.url} target="_blank" rel="noopener noreferrer" className="feed-action-btn feed-read-btn" title="Read (↵)">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
            Read
          </a>
        </div>
      </div>
    </div>
  );
}

// ── Home Feed: full-bleed swipeable card ──────────────────────────────────
// ── Home Feed: display card (image-top / text-bottom) ─────────────────────
function HomeFeedCard({ item }) {
  const { article, tags } = item;
  const sectorTags  = tags.filter(t => t.startsWith('sector:')).map(t => t.replace('sector:', ''));
  const countryName = article.country || '';
  const sigLabel    = { 4: 'CRITICAL', 3: 'SIGNIFICANT', 2: 'NOTABLE', 1: 'ROUTINE' }[article.significance] || 'NOTABLE';
  const source = (() => { try { return new URL(article.url).hostname.replace(/^www\./, ''); } catch { return ''; } })();

  return (
    <div className="hfc-card">
      {/* Image top 55% */}
      <div className="hfc-img-section">
        <img
          src={article.image_url || ''}
          alt=""
          className="hfc-img"
          draggable="false"
          onError={e => { e.currentTarget.style.display = 'none'; }}
        />
        <div className="hfc-img-gradient" />
        <div className="hfc-img-tags">
          {countryName && <span className="hfc-tag-country">{countryName}</span>}
          {sectorTags.slice(0, 2).map(s => <span key={s} className="hfc-tag-sector">{s}</span>)}
        </div>
      </div>
      {/* Text bottom 45% */}
      <div className="hfc-text-section">
        <div className="hfc-sig-label">{sigLabel}</div>
        <h3 className="hfc-headline">{stripHtml(article.title)}</h3>
        <p className="hfc-summary">
          {stripHtml(article.summary).slice(0, 300)}{stripHtml(article.summary).length > 300 ? '…' : ''}
        </p>
        <div className="hfc-separator" />
        <div className="hfc-meta">
          {source && <span>{source}</span>}
          {source && <span className="hfc-meta-sep">·</span>}
          <span>{relativeTime(article.published)}</span>
        </div>
      </div>
    </div>
  );
}


// ── Personalised For You Section ─────────────────────────────────────────────

const SECTOR_LABELS = {
  'sector:economy':        'fiscal policy',
  'sector:climate':        'climate finance',
  'sector:governance':     'governance',
  'sector:security':       'regional security',
  'sector:health':         'public health',
  'sector:education':      'education',
  'sector:infrastructure': 'infrastructure',
  'sector:agriculture':    'agriculture',
  'sector:energy':         'energy',
  'sector:humanitarian':   'humanitarian aid',
  'sector:livelihoods':    'livelihoods',
  'sector:diplomacy':      'geopolitics',
};

const PF_COUNTRY_IMG = {
  'Sri Lanka':   'dinuka-lankaloka-iduEaeBB_rQ-unsplash.jpg',
  'India':       'daniel-klein-Qx8_d5dGhrs-unsplash.jpg',
  'Pakistan':    'siarhei-palishchuk-kSUDzuVslS0-unsplash.jpg',
  'Bangladesh':  'alex-azabache-8weolGgaa9w-unsplash.jpg',
  'Nepal':       'tomas-malik-CKEmZAw0Z8c-unsplash.jpg',
  'Myanmar':     'tomas-malik-CKEmZAw0Z8c-unsplash.jpg',
  'Thailand':    'tomas-malik-CKEmZAw0Z8c-unsplash.jpg',
  'Vietnam':     'jerry-kavan-i9eaAR4dWi8-unsplash.jpg',
  'China':       'tomas-malik-CKEmZAw0Z8c-unsplash.jpg',
  'Japan':       'hendrik-cornelissen-jpTT_SAU034-unsplash.jpg',
};
const PF_FALLBACK_IMG = 'dinuka-lankaloka-iduEaeBB_rQ-unsplash.jpg';

// Cascading image component — tries remote URL, city-images, country defaults, then final fallback
function PFFallbackImg({ art, cityImages, className }) {
  const chain = React.useMemo(() => {
    const c = [];
    if (art.image_url) c.push(art.image_url);
    if (cityImages) {
      if (art.city && cityImages[art.city] && cityImages[art.city][0])
        c.push(cityImages[art.city][0]);
      const key = COUNTRY_FALLBACK_KEY[art.country] || 'default_LK';
      if (cityImages[key] && cityImages[key][0]) c.push(cityImages[key][0]);
    }
    if (PF_COUNTRY_IMG[art.country]) c.push(PF_COUNTRY_IMG[art.country]);
    c.push(PF_FALLBACK_IMG);
    // deduplicate while preserving order
    return [...new Set(c)];
  }, [art.id]);

  const [idx, setIdx] = useState(0);

  if (idx >= chain.length) return <div className={`pf-img-ph ${className || ''}`.trim()} />;

  return (
    <img
      className={className}
      src={chain[idx]}
      alt=""
      onError={() => setIdx(i => i + 1)}
    />
  );
}

function buildPersonalisedFeed(allArticles, likedArticles) {
  if (!likedArticles.length || !allArticles.length) return [];

  const likedIds  = new Set(likedArticles.map(a => a.id).filter(Boolean));
  const likedUrls = new Set(likedArticles.map(a => a.url).filter(Boolean));

  const countryCounts = {};
  const sectorCounts  = {};
  const sigCounts     = {};

  for (const liked of likedArticles) {
    if (liked.country) countryCounts[liked.country] = (countryCounts[liked.country] || 0) + 1;
    const tags = getArticleTags(liked);
    for (const tag of tags) {
      if (tag.startsWith('sector:')) sectorCounts[tag] = (sectorCounts[tag] || 0) + 1;
    }
    if (liked.significance) sigCounts[liked.significance] = (sigCounts[liked.significance] || 0) + 1;
  }

  const sevenDaysAgo = Date.now() - 7 * 24 * 3600 * 1000;

  const scored = allArticles
    .filter(a => !likedIds.has(a.id) && !likedUrls.has(a.url))
    .map(a => {
      let score = 0;
      const countryBonus   = a.country && countryCounts[a.country] ? 3 * countryCounts[a.country] : 0;
      let sectorBonus = 0;
      for (const tag of getArticleTags(a)) {
        if (tag.startsWith('sector:') && sectorCounts[tag]) sectorBonus += 2 * sectorCounts[tag];
      }
      const sigBonus    = a.significance && sigCounts[a.significance] ? 1 : 0;
      const recencyMult = new Date(a.published).getTime() > sevenDaysAgo ? 1.5 : 1.0;
      score = (countryBonus + sectorBonus + sigBonus) * recencyMult;
      return { article: a, score, countryBonus, sectorBonus, sigBonus, recencyMult };
    })
    .sort((a, b) => b.score !== a.score ? b.score - a.score : new Date(b.article.published) - new Date(a.article.published));

  // Algorithm verification — top 5 scoring breakdown visible in console
  console.group('[Origin] Personalised feed — top 5 scoring breakdown');
  scored.slice(0, 5).forEach(({ article, score, countryBonus, sectorBonus, sigBonus, recencyMult }) => {
    console.log(
      `[${score.toFixed(1)}] ${article.country} | ${article.title.slice(0, 60)}…`,
      `| country:+${countryBonus} sector:+${sectorBonus} sig:+${sigBonus} recency:×${recencyMult}`
    );
  });
  console.groupEnd();

  return scored.slice(0, 13).map(s => s.article);
}

function PFArticleModal({ article, cityImages, onClose, onLiked }) {
  const [liked, setLiked] = useState(false);

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

  useEffect(() => {
    if (!article) { setLiked(false); return; }
    try {
      const saved = JSON.parse(localStorage.getItem('originadvisory_likes') || '[]');
      setLiked(saved.some(l => l.id === article.id || l.url === article.url));
    } catch { setLiked(false); }
  }, [article]);

  if (!article) return null;

  const getImg = () => {
    if (article.image_url) return article.image_url;
    if (!cityImages) return null;
    const key  = COUNTRY_FALLBACK_KEY[article.country] || 'default_LK';
    const imgs = cityImages[article.city] || cityImages[key];
    return imgs ? imgs[0] : null;
  };

  const source = (() => { try { return new URL(article.url).hostname.replace(/^www\./, ''); } catch { return ''; } })();
  const sigLabels = { 1: 'Local', 2: 'Moderate', 3: 'Significant', 4: 'Critical' };
  const sigColors = { 1: '#38a169', 2: '#d69e2e', 3: '#C8531C', 4: '#e53e3e' };

  const handleLike = () => {
    if (liked) return;
    try {
      const existing = JSON.parse(localStorage.getItem('originadvisory_likes') || '[]');
      if (!existing.some(l => l.id === article.id)) {
        existing.push({ id: article.id, url: article.url, title: article.title,
          country: article.country, lat: article.lat, lon: article.lon,
          published: article.published, significance: article.significance,
          summary: article.summary, image_url: article.image_url });
        localStorage.setItem('originadvisory_likes', JSON.stringify(existing));
      }
    } catch {}
    window.dispatchEvent(new CustomEvent('originadvisory:like', { detail: { id: article.id } }));
    setLiked(true);
    if (onLiked) onLiked();
  };

  const img = getImg();

  return (
    <div className="pf-modal-backdrop open" onClick={onClose}>
      <div className="pf-modal" onClick={e => e.stopPropagation()}>
        <button className="pf-modal-close" onClick={onClose} aria-label="Close">
          <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
            <path d="M1 1 L11 11 M11 1 L1 11" />
          </svg>
        </button>
        {img && (
          <img className="pf-modal-img" src={img} alt=""
            onError={e => { e.currentTarget.style.display = 'none'; }} />
        )}
        <div className="pf-modal-body">
          <div className="pf-modal-meta">
            {article.country && (
              <span className="pf-modal-country">{article.country}</span>
            )}
            <span style={{ fontSize: 11, color: sigColors[article.significance] || '#C8531C',
              fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase' }}>
              {sigLabels[article.significance] || 'Moderate'}
            </span>
            <span className="pf-modal-time">{relativeTime(article.published)}</span>
          </div>
          <h3 className="pf-modal-headline">{stripHtml(article.title)}</h3>
          <p className="pf-modal-summary">{stripHtml(article.summary)}</p>
          <div className="pf-modal-actions">
            <a href={article.url} target="_blank" rel="noopener noreferrer" className="pf-modal-read-btn">
              Read on {source} →
            </a>
            <button className={`pf-modal-like-btn${liked ? ' pf-modal-liked' : ''}`} onClick={handleLike}>
              {liked ? '♥ Saved' : '♥ Save story'}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

function PersonalisedSection({ articles, cityImages }) {
  const [likedArticles, setLikedArticles] = useState(() => {
    try { return JSON.parse(localStorage.getItem('originadvisory_likes') || '[]'); } catch { return []; }
  });
  const [modalArticle, setModalArticle] = useState(null);
  const reshuffleTimer = useRef(null);

  useEffect(() => {
    const refresh = () => {
      try { setLikedArticles(JSON.parse(localStorage.getItem('originadvisory_likes') || '[]')); } catch {}
    };
    window.addEventListener('originadvisory:like', refresh);
    return () => window.removeEventListener('originadvisory:like', refresh);
  }, []);

  useEffect(() => () => { if (reshuffleTimer.current) clearTimeout(reshuffleTimer.current); }, []);

  const likedCount = likedArticles.length;
  if (likedCount < 3) return null;

  // Build user profile from liked articles
  const countryCounts = {};
  const sectorCounts  = {};
  for (const liked of likedArticles) {
    if (liked.country) countryCounts[liked.country] = (countryCounts[liked.country] || 0) + 1;
    const tags = getArticleTags(liked);
    for (const tag of tags) {
      if (tag.startsWith('sector:')) sectorCounts[tag] = (sectorCounts[tag] || 0) + 1;
    }
  }

  const topCountryEntry = Object.entries(countryCounts).sort((a, b) => b[1] - a[1])[0];
  const topSectorEntry  = Object.entries(sectorCounts).sort((a, b) => b[1] - a[1])[0];
  const uniqueCountries = Object.keys(countryCounts).length;
  const uniqueSectors   = Object.keys(sectorCounts).length;

  const feed         = buildPersonalisedFeed(articles, likedArticles);
  const featured     = feed[0] || null;
  const gridArticles = feed.slice(1, 4);
  const listArticles = feed.slice(4, 8);

  const sigLabels = { 1: 'Local', 2: 'Moderate', 3: 'Significant', 4: 'Critical' };
  const srcHost   = url => { try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return ''; } };

  const handleLiked = () => {
    if (reshuffleTimer.current) clearTimeout(reshuffleTimer.current);
    reshuffleTimer.current = setTimeout(() => {
      try { setLikedArticles(JSON.parse(localStorage.getItem('originadvisory_likes') || '[]')); } catch {}
    }, 1000);
  };

  const heading = uniqueCountries === 1 && topCountryEntry
    ? `More from ${topCountryEntry[0]}`
    : "More on what you're following";

  let subline;
  if (likedCount < 5) {
    subline = "Stories matched to what you've been reading. Like more to refine your feed.";
  } else if (topSectorEntry && topCountryEntry) {
    const sectorLabel = SECTOR_LABELS[topSectorEntry[0]] || topSectorEntry[0].replace('sector:', '');
    const otherThemes = Math.max(0, uniqueSectors - 1);
    const otherPart   = otherThemes > 0
      ? ` — plus ${otherThemes} other ${otherThemes === 1 ? 'theme' : 'themes'} you've engaged with`
      : '';
    subline = `Following your interest in ${sectorLabel} and ${topCountryEntry[0]}${otherPart}.`;
  } else {
    subline = "Stories matched to what you've been reading. Like more to refine your feed.";
  }

  return (
    <section className="pf-section">
      <div className="container">

        {/* ── Header ── */}
        <div className="pf-header">
          <span className="pf-eyebrow">For You</span>
          <h2 className="pf-heading">{heading}</h2>
          <p className="pf-sub">{subline}</p>
        </div>

        {feed.length === 0 ? (
          <p className="pf-empty">Not enough articles match your interests yet — check back after swiping more stories.</p>
        ) : (
          <>
            {/* ── Row 1: Featured ── */}
            {featured && (
              <div className="pf-featured" onClick={() => setModalArticle(featured)}>
                <div className="pf-feat-img-wrap">
                  <PFFallbackImg art={featured} cityImages={cityImages} className="pf-feat-img" />
                </div>
                <div className="pf-feat-body">
                  <div className="pf-feat-tags">
                    {featured.country && <span className="pf-tag-dark">{featured.country}</span>}
                    <span className="pf-tag-sig">{sigLabels[featured.significance] || 'Moderate'}</span>
                  </div>
                  <h3 className="pf-feat-headline">{stripHtml(featured.title)}</h3>
                  <p className="pf-feat-excerpt">{stripHtml(featured.summary)}</p>
                  <div className="pf-feat-meta">{srcHost(featured.url)} · {relativeTime(featured.published)}</div>
                  <button className="pf-read-btn" onClick={e => { e.stopPropagation(); setModalArticle(featured); }}>
                    Read more →
                  </button>
                </div>
              </div>
            )}

            {/* ── Row 2: 3-column grid ── */}
            {gridArticles.length > 0 && (
              <div className="pf-grid">
                {gridArticles.map((art, i) => (
                  <div key={art.id || i} className="pf-grid-card" onClick={() => setModalArticle(art)}>
                    <div className="pf-grid-img-wrap">
                      <PFFallbackImg art={art} cityImages={cityImages} className="pf-grid-img" />
                      {art.country && <span className="pf-grid-country-tag">{art.country}</span>}
                    </div>
                    <div className="pf-grid-body">
                      <div className="pf-grid-sig">{sigLabels[art.significance] || 'Moderate'}</div>
                      <h4 className="pf-grid-headline">{stripHtml(art.title)}</h4>
                      <p className="pf-grid-excerpt">{stripHtml(art.summary)}</p>
                      <div className="pf-grid-meta">{srcHost(art.url)} · {relativeTime(art.published)}</div>
                    </div>
                  </div>
                ))}
              </div>
            )}

            {/* ── Row 3: Editorial divider ── */}
            {listArticles.length > 0 && (
              <div className="pf-divider">
                <span className="pf-divider-label">More from your regions</span>
                <div className="pf-divider-rule" />
              </div>
            )}

            {/* ── Row 4: Horizontal list ── */}
            {listArticles.length > 0 && (
              <div className="pf-list">
                {listArticles.map((art, i) => (
                  <div key={art.id || i} className="pf-list-row" onClick={() => setModalArticle(art)}>
                    <PFFallbackImg art={art} cityImages={cityImages} className="pf-list-thumb" />
                    <div className="pf-list-body">
                      <div className="pf-list-tags">
                        {art.country && <span className="pf-tag-dark pf-tag-sm">{art.country}</span>}
                      </div>
                      <div className="pf-list-headline">{stripHtml(art.title)}</div>
                      <div className="pf-list-excerpt">{stripHtml(art.summary)}</div>
                      <div className="pf-list-meta">{srcHost(art.url)} · {relativeTime(art.published)}</div>
                    </div>
                  </div>
                ))}
              </div>
            )}

            {/* ── Row 5: Section footer ── */}
            <div className="pf-section-foot">
              <p className="pf-foot-hint">
                {feed.length < 8
                  ? 'Like more stories to expand your feed.'
                  : 'Want more? Like more stories above to refine your feed.'}
              </p>
              <button className="pf-foot-link"
                onClick={() => document.querySelector('.map-section')?.scrollIntoView({ behavior: 'smooth' })}>
                Explore all stories on the map →
              </button>
            </div>
          </>
        )}
      </div>

      {modalArticle && (
        <PFArticleModal
          article={modalArticle}
          cityImages={cityImages}
          onClose={() => setModalArticle(null)}
          onLiked={handleLiked}
        />
      )}
    </section>
  );
}

// ─────────────────────────────────────────────────────────────────────────────

function HomeFeedSection({ articles, onArticleClick }) {
  const [prefs, updatePrefs] = useFeedPrefs();
  const [queue, setQueue]   = useState([]);
  const queueBuilt          = useRef(false);
  const [currentIdx, setCurrentIdx] = useState(0);
  const [visited, setVisited]       = useState(new Set());
  const [exitDir,  setExitDir]      = useState(null);
  const [transitioning, setTransitioning] = useState(false);
  const [dragX, setDragX]     = useState(0);
  const [dragging, setDragging] = useState(false);
  const [resetKey, setResetKey] = useState(0);
  const cardWrapRef  = useRef(null);
  const dragStartX   = useRef(0);

  // Stable refs for keyboard handler
  const curRef   = useRef(0);
  const transRef = useRef(false);
  const qRef     = useRef([]);
  curRef.current   = currentIdx;
  transRef.current = transitioning;
  qRef.current     = queue;

  const buildQ = loadedPrefs => {
    let f = articles.filter(a => (a.significance || 1) >= 3);
    if (f.length < 20) f = articles.filter(a => (a.significance || 1) >= 2);
    return buildFeedQueue(f.slice(0, 40), loadedPrefs);
  };

  useEffect(() => {
    if (articles.length > 0 && !queueBuilt.current) {
      queueBuilt.current = true;
      setQueue(buildQ(loadFeedPrefs()));
    }
  }, [articles.length]);

  const navigate = (dir, isCommit = false) => {
    if (transRef.current) return;
    const idx = curRef.current;
    const q   = qRef.current;
    const isBack = dir === 'back';
    const newIdx = isBack ? idx - 1 : idx + 1;
    if (isBack && idx === 0) return;
    if (!isBack && idx >= q.length) return;

    transRef.current = true;
    setTransitioning(true);
    setExitDir(dir);
    setDragX(0);

    if (isCommit && q[idx]) {
      updatePrefs(p => applySwipe(p, q[idx].tags, dir === 'like'));
      setVisited(prev => new Set([...prev, idx]));
      if (dir === 'like') {
        const art = q[idx].article;
        const id  = art.url || art.title;
        try {
          const existing = JSON.parse(localStorage.getItem('originadvisory_likes') || '[]');
          if (!existing.some(l => l.id === id)) {
            existing.push({ id, url: art.url, title: art.title, country: art.country,
              lat: art.lat, lon: art.lon, published: art.published, significance: art.significance,
              summary: art.summary, image_url: art.image_url });
            localStorage.setItem('originadvisory_likes', JSON.stringify(existing));
          }
        } catch (_) {}
        window.dispatchEvent(new CustomEvent('originadvisory:like', { detail: { id } }));
      }
    }

    setTimeout(() => {
      setCurrentIdx(newIdx);
      setExitDir(null);
      setTransitioning(false);
    }, 420);
  };

  // Keyboard
  useEffect(() => {
    const h = e => {
      if (e.key === 'ArrowRight')               { e.preventDefault(); navigate('like', true); }
      else if (e.key === 'ArrowLeft')            { e.preventDefault(); navigate('skip', true); }
      else if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        const it = qRef.current[curRef.current];
        if (!it) return;
        if (onArticleClick) onArticleClick(it.article);
        else window.open(it.article.url, '_blank');
      }
    };
    window.addEventListener('keydown', h);
    return () => window.removeEventListener('keydown', h);
  }, []);

  // Pointer drag
  const onPointerDown = e => {
    if (transitioning) return;
    cardWrapRef.current?.setPointerCapture(e.pointerId);
    dragStartX.current = e.clientX;
    setDragging(true);
  };
  const onPointerMove = e => {
    if (!dragging || transitioning) return;
    setDragX(e.clientX - dragStartX.current);
  };
  const onPointerUp = () => {
    if (!dragging) return;
    setDragging(false);
    const threshold = (cardWrapRef.current?.offsetWidth || 720) * 0.3;
    if      (dragX >  threshold) navigate('like', true);
    else if (dragX < -threshold) navigate('skip', true);
    else setDragX(0);
  };

  const resetFeed = () => {
    const fresh = defaultFeedPrefs();
    updatePrefs(fresh);
    queueBuilt.current = false;
    const newQ = buildQ(fresh);
    queueBuilt.current = true;
    setQueue(newQ);
    setCurrentIdx(0);
    setVisited(new Set());
    setExitDir(null);
    setTransitioning(false);
    setDragX(0);
    setResetKey(k => k + 1);
  };

  const totalCards = queue.length;
  const isDone     = totalCards > 0 && currentIdx >= totalCards;
  const item       = queue[currentIdx];

  const tintAlpha = Math.min(Math.abs(dragX) / 200, 0.5);
  const tintColor = dragX > 20  ? `rgba(39,174,96,${tintAlpha})`
                  : dragX < -20 ? `rgba(192,57,43,${tintAlpha})` : 'transparent';

  const wrapStyle = exitDir ? {
    transform: exitDir === 'like'    ? 'translateX(130%) rotate(15deg)'  :
               exitDir === 'skip'   ? 'translateX(-130%) rotate(-15deg)' :
               exitDir === 'forward'? 'translateX(-40px) scale(0.96)'    :
                                      'translateX(40px) scale(0.96)',
    opacity: 0,
    transition: 'transform 0.38s ease-out, opacity 0.34s ease-out',
    pointerEvents: 'none',
  } : {
    transform: `translateX(${dragX}px) rotate(${dragX * 0.018}deg)`,
    transition: dragging ? 'none' : 'transform 0.22s ease',
    touchAction: 'none',
    cursor: dragging ? 'grabbing' : 'grab',
  };

  const handleRead = () => {
    if (!item) return;
    if (onArticleClick) onArticleClick(item.article);
    else window.open(item.article.url, '_blank');
  };

  return (
    <section className="home-feed-section">
      <div className="container">
        <div className="home-feed-heading">
          <span className="eyebrow">Your Feed</span>
          <h2 style={{ marginTop: 12 }}>What matters today</h2>
          <p>Stories from across Asia, ranked for relevance. Swipe to shape what you see next.</p>
        </div>
      </div>

      {!totalCards ? (
        <div className="hf-loading"><p>Loading stories…</p></div>
      ) : isDone ? (
        <div className="hf-empty">
          <span className="eyebrow" style={{ color: 'var(--accent)', display: 'block', marginBottom: 14 }}>All caught up</span>
          <h3>You've seen the most important stories</h3>
          <p>Check back tomorrow for fresh updates from across Asia. Or explore the full archive on the map below.</p>
          <div className="hf-empty-btns">
            <button className="hf-btn-reset" onClick={resetFeed}>Reset feed</button>
            <button
              className="hf-btn-map"
              onClick={() => document.querySelector('.map-section')?.scrollIntoView({ behavior: 'smooth' })}
            >Explore the map</button>
          </div>
        </div>
      ) : (
        <>
          <div className="hf-stage">
            {/* Left chevron */}
            <button
              className={`hf-chevron hf-chev-left${currentIdx === 0 ? ' hf-chev-disabled' : ''}`}
              onClick={() => navigate('back')}
              aria-label="Previous card"
            >
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
                <polyline points="15 18 9 12 15 6"/>
              </svg>
            </button>

            {/* Card wrap (drag + swipe target) */}
            <div
              ref={cardWrapRef}
              className="hf-card-wrap"
              style={wrapStyle}
              onPointerDown={onPointerDown}
              onPointerMove={onPointerMove}
              onPointerUp={onPointerUp}
              onPointerCancel={onPointerUp}
            >
              {dragX > 30  && !exitDir && <div className="hf-swipe-badge hf-badge-like">LIKE ♥</div>}
              {dragX < -30 && !exitDir && <div className="hf-swipe-badge hf-badge-skip">SKIP ✕</div>}
              <div className="hf-tint" style={{ background: tintColor }} />
              {item && <HomeFeedCard key={`hfc-${currentIdx}-${resetKey}`} item={item} />}
            </div>

            {/* Right chevron */}
            <button
              className={`hf-chevron hf-chev-right${currentIdx >= totalCards - 1 ? ' hf-chev-disabled' : ''}`}
              onClick={() => navigate('forward')}
              aria-label="Next card"
            >
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
                <polyline points="9 18 15 12 9 6"/>
              </svg>
            </button>
          </div>

          {/* Position indicator */}
          <div className="hf-indicators">
            {totalCards <= 20 && (
              <div className="hf-dots">
                {queue.map((_, i) => (
                  <div key={i} className={`hf-dot${i === currentIdx ? ' hf-dot-active' : visited.has(i) ? ' hf-dot-visited' : ''}`} />
                ))}
              </div>
            )}
            <span className="hf-pos-text">{currentIdx + 1} of {totalCards}</span>
          </div>

          {/* Action buttons */}
          <div className="hf-action-row">
            <button className="hf-act hf-act-skip" onClick={() => navigate('skip', true)}>
              <span className="hf-act-arr">←</span> Skip
            </button>
            <button className="hf-act hf-act-read" onClick={handleRead}>Read</button>
            <button className="hf-act hf-act-like" onClick={() => navigate('like', true)}>
              Like <span className="hf-act-arr">→</span>
            </button>
          </div>

          {/* Keyboard hints */}
          <div className="hf-kbd-hint">
            <kbd>←</kbd> Skip &nbsp;·&nbsp; <kbd>→</kbd> Like &nbsp;·&nbsp; <kbd>Enter</kbd> Read
          </div>
        </>
      )}
    </section>
  );
}

// ── Feed: Homepage CTA ─────────────────────────────────────────────────────
function FeedCTA() {
  const prefs    = loadFeedPrefs();
  const hasLikes = (prefs.likedCount || 0) > 0;
  return (
    <section className="feed-cta-section">
      <div className="container">
        <div className="feed-cta-inner">
          <div className="feed-cta-text">
            <span className="eyebrow">Your Feed</span>
            <h3 style={{ marginTop: 12 }}>Want more? Build your own feed.</h3>
            <p style={{ marginTop: 12, maxWidth: 440 }}>
              Swipe through stories from across Asia. The more you swipe,
              the better it gets at finding what matters to you.
            </p>
          </div>
          <div className="feed-cta-action">
            {hasLikes && (
              <span className="feed-cta-stat">
                You've liked {prefs.likedCount} {prefs.likedCount === 1 ? 'story' : 'stories'}
              </span>
            )}
            <a href="#/feed" className="btn btn-primary" style={{ marginTop: hasLikes ? 10 : 0 }}>
              {hasLikes ? 'Continue your feed' : 'Start swiping'}{' '}
              <span className="arr"><Icons.Arrow /></span>
            </a>
          </div>
        </div>
      </div>
    </section>
  );
}

// ── Feed: Main page ────────────────────────────────────────────────────────
function FeedPage({ articles }) {
  const [prefs, updatePrefs] = useFeedPrefs();
  const isMobile = useIsMobile();
  const [queue, setQueue]       = useState([]);
  const queueBuilt              = useRef(false);
  const [current, setCurrent]   = useState(0);    // mobile: current index
  const [swiped,  setSwiped]    = useState({});   // desktop: { idx: 'like'|'skip' }
  const [done,    setDone]      = useState({});   // desktop: fully removed
  const [showCtrl, setShowCtrl] = useState(false);

  useEffect(() => {
    if (articles.length > 0 && !queueBuilt.current) {
      queueBuilt.current = true;
      setQueue(buildFeedQueue(articles, loadFeedPrefs()));
    }
  }, [articles.length]);

  // Keyboard shortcuts (desktop)
  const firstActiveRef = useRef(-1);
  const queueRef       = useRef(queue);
  queueRef.current     = queue;

  firstActiveRef.current = queue.findIndex((_, i) => !done[i] && !swiped[i]);

  useEffect(() => {
    if (isMobile) return;
    const handler = e => {
      const fa = firstActiveRef.current;
      if (fa < 0) return;
      const item = queueRef.current[fa];
      if (!item) return;
      if (e.key === 'ArrowRight') {
        e.preventDefault();
        updatePrefs(p => applySwipe(p, item.tags, true));
        setSwiped(s => ({ ...s, [fa]: 'like' }));
        setTimeout(() => setDone(d => ({ ...d, [fa]: true })), 520);
      } else if (e.key === 'ArrowLeft') {
        e.preventDefault();
        updatePrefs(p => applySwipe(p, item.tags, false));
        setSwiped(s => ({ ...s, [fa]: 'skip' }));
        setTimeout(() => setDone(d => ({ ...d, [fa]: true })), 520);
      } else if (e.key === 'Enter') {
        const url = item.article?.url;
        if (url) window.open(url, '_blank');
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [isMobile]);

  const handleSwipeMobile  = (item, isLike) => {
    updatePrefs(p => applySwipe(p, item.tags, isLike));
    setCurrent(c => c + 1);
  };
  const handleSwipeDesktop = (item, idx, isLike) => {
    updatePrefs(p => applySwipe(p, item.tags, isLike));
    setSwiped(s => ({ ...s, [idx]: isLike ? 'like' : 'skip' }));
    setTimeout(() => setDone(d => ({ ...d, [idx]: true })), 520);
  };

  const isLoading   = !queueBuilt.current;
  const mobileDone  = current >= queue.length;
  const desktopDone = Object.keys(done).length >= queue.length && queue.length > 0;
  const isDone      = !isLoading && (isMobile ? mobileDone : desktopDone);

  const topTags = Object.entries(prefs.tagWeights || {})
    .filter(([, w]) => w > 0)
    .sort((a, b) => b[1] - a[1])
    .slice(0, 8);

  const firstActive = queue.findIndex((_, i) => !done[i] && !swiped[i]);

  return (
    <main className="feed-page">

      {/* ── Header ── */}
      <div className="feed-header">
        <div className="container">
          <div className="feed-header-inner">
            <div>
              <a href="#/" className="feed-back-link">← Home</a>
              <span className="eyebrow" style={{ display: 'block', marginTop: 14 }}>Your Feed</span>
              <h2 className="feed-title">Stories curated for you.</h2>
              <p className="feed-subtitle">The more you swipe, the better it gets.</p>
            </div>
            <button className="feed-settings-btn" onClick={() => setShowCtrl(o => !o)} aria-label="Feed settings">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                <circle cx="12" cy="12" r="3"/>
                <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/>
              </svg>
            </button>
          </div>
        </div>
      </div>

      {/* ── Controls panel ── */}
      {showCtrl && (
        <div className="feed-controls-panel">
          <div className="container">
            <div className="feed-controls-inner">
              <div>
                <h4 style={{ marginBottom: 12 }}>Your reading interests</h4>
                {topTags.length ? (
                  <div className="feed-interests-grid">
                    {topTags.map(([tag, w]) => (
                      <span key={tag} className="feed-interest-chip">
                        {tag.replace(/^(country|sector|city):/, '')}
                        <span className="feed-interest-weight">+{w.toFixed(1)}</span>
                      </span>
                    ))}
                  </div>
                ) : (
                  <p style={{ fontSize: 14, color: 'var(--muted)' }}>Swipe a few stories to see your interests appear here.</p>
                )}
              </div>
              <div className="feed-controls-btns">
                <button
                  className={`feed-ctrl-btn${prefs.paused ? ' feed-ctrl-active' : ''}`}
                  onClick={() => updatePrefs(p => ({ ...p, paused: !p.paused }))}
                >
                  {prefs.paused ? 'Resume personalisation' : 'Pause personalisation'}
                </button>
                <button
                  className="feed-ctrl-btn feed-ctrl-danger"
                  onClick={() => {
                    if (!window.confirm('Reset all your feed preferences? This cannot be undone.')) return;
                    const fresh = defaultFeedPrefs();
                    updatePrefs(fresh);
                    queueBuilt.current = false;
                    setQueue(buildFeedQueue(articles, fresh));
                    queueBuilt.current = true;
                    setCurrent(0);
                    setSwiped({});
                    setDone({});
                  }}
                >
                  Reset my preferences
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* ── Loading state ── */}
      {isLoading && (
        <div className="feed-empty">
          <p>Loading your feed…</p>
        </div>
      )}

      {/* ── All-caught-up state ── */}
      {isDone && (
        <div className="feed-done-state">
          <div className="feed-done-icon">✓</div>
          <h3>You're all caught up.</h3>
          <p>Come back tomorrow for more stories from across Asia.</p>
          <div className="feed-done-email-cta">
            <p>Get the top 5 stories in your inbox every morning.</p>
            <a href="#/" className="btn btn-primary" style={{ marginTop: 16, display: 'inline-flex' }}>
              Subscribe to the briefing <span className="arr" style={{ marginLeft: 8 }}><Icons.Arrow /></span>
            </a>
          </div>
        </div>
      )}

      {/* ── Mobile swipe deck ── */}
      {!isDone && isMobile && (
        <div className="feed-swipe-area">
          <div className="feed-progress-bar">
            <div className="feed-progress-fill" style={{ width: `${(current / Math.max(queue.length, 1)) * 100}%` }} />
          </div>
          <div className="feed-progress-text">Story {current + 1} of {queue.length}</div>

          <div className="feed-deck">
            {queue.slice(current, current + 3).map((item, i) => (
              <FeedMobileCard
                key={`${item.article.id}-${current + i}`}
                item={item}
                prefs={prefs}
                onLike={() => handleSwipeMobile(item, true)}
                onSkip={() => handleSwipeMobile(item, false)}
                isTop={i === 0}
                stackPos={i}
              />
            ))}
          </div>

          <div className="feed-mobile-action-row">
            <button className="feed-btn-skip" onClick={() => handleSwipeMobile(queue[current], false)} aria-label="Skip">
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round">
                <line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
              </svg>
            </button>
            <a href={queue[current]?.article.url} target="_blank" rel="noopener noreferrer"
               className="feed-btn-read" aria-label="Read story">
              <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
                <polyline points="15 3 21 3 21 9"/>
                <line x1="10" y1="14" x2="21" y2="3"/>
              </svg>
            </a>
            <button className="feed-btn-like" onClick={() => handleSwipeMobile(queue[current], true)} aria-label="Like">
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/>
              </svg>
            </button>
          </div>
        </div>
      )}

      {/* ── Desktop feed ── */}
      {!isDone && !isMobile && (
        <div className="feed-desktop-wrap">
          <div className="feed-keyboard-hint">
            <span><kbd>←</kbd> Skip</span>
            <span><kbd>→</kbd> Like</span>
            <span><kbd>Enter</kbd> Read</span>
          </div>
          {queue.map((item, idx) => {
            if (done[idx]) return null;
            return (
              <FeedDesktopCard
                key={item.article.id}
                item={item}
                prefs={prefs}
                onLike={() => handleSwipeDesktop(item, idx, true)}
                onSkip={() => handleSwipeDesktop(item, idx, false)}
                isFirst={idx === firstActive}
                animDir={swiped[idx] || null}
              />
            );
          })}
        </div>
      )}

      <p className="feed-privacy-note">
        Your preferences are stored locally on your device. We don't track or sell your reading data.
      </p>
    </main>
  );
}

// ── App ────────────────────────────────────────────────────────────────────
function App() {
  const route                             = useRoute();
  const [modalRegion, setModalRegion]     = useState(null);
  const [menuOpen, setMenuOpen]           = useState(false);
  const [mapData, setMapData]             = useState(null);
  const [articleModal, setArticleModal]   = useState(null); // { article, fromList } | null
  const [listModal, setListModal]         = useState(null); // { title, articles } | null
  const [natModal, setNatModal]           = useState(false);
  const [cityImages, setCityImages]       = useState(null);

  useEffect(() => { setModalRegion(null); }, [route]);

  useEffect(() => {
    fetch('map-data.json?t=' + Date.now()).then(r => r.json()).then(setMapData).catch(() => {});
  }, []);

  useEffect(() => {
    fetch("city-images.json").then(r => r.json()).then(setCityImages).catch(() => {});
  }, []);

  const allArticles     = mapData?.articles || [];
  const nationalArticles = allArticles.filter(isNationalScope);

  const handleArticleClick    = art   => setArticleModal({ article: art, fromList: false });
  const handleListOpen        = modal => { setListModal(modal); setNatModal(false); };
  const handleNatModalOpen    = ()    => { setNatModal(true); setListModal(null); };
  const handleArticleFromList = art   => setArticleModal({ article: art, fromList: true });
  const handleBackToList      = ()    => setArticleModal(null);
  const handleCloseArticle    = ()    => setArticleModal(null);
  const handleCloseList       = ()    => { setListModal(null); setArticleModal(null); };
  const handleCloseNat        = ()    => { setNatModal(false); setArticleModal(null); };

  const anyListOpen = !!(listModal || natModal);

  return (
    <React.Fragment>
      <SideRail onMenuOpen={() => setMenuOpen(true)} />
      <Nav route={route} />

      {route === "/"             && <HomePage onRegionClick={setModalRegion} mapData={mapData} onArticleClick={handleArticleClick} onListOpen={handleListOpen} onNatModalOpen={handleNatModalOpen} cityImages={cityImages} />}
      {route === "/feed"         && <FeedPage articles={allArticles} />}
      {route === "/intelligence" && <IntelligencePage />}
      {route === "/insights"     && <InsightsPage />}
      {route === "/contact"      && <ContactPage />}
      {route === "/pilot"        && <PilotPage />}

      <Footer />
      <FloatingAI />
      <Modal region={modalRegion} onClose={() => setModalRegion(null)} />
      <ArticleListModal
        modal={listModal}
        onClose={handleCloseList}
        onArticleClick={handleArticleFromList}
        noEsc={!!articleModal}
      />
      <NationalArticlesModal
        open={natModal}
        articles={nationalArticles}
        onClose={handleCloseNat}
        onArticleClick={handleArticleFromList}
        noEsc={!!articleModal}
      />
      <ArticleModal
        article={articleModal?.article}
        onClose={handleCloseArticle}
        onBack={articleModal?.fromList ? handleBackToList : null}
        cityImages={cityImages}
        allArticles={allArticles}
      />
      <MenuOverlay open={menuOpen} onClose={() => setMenuOpen(false)} />
    </React.Fragment>
  );
}

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