diff --git a/internal/check-packet-render.mjs b/internal/check-packet-render.mjs
index 6a0173efd..2291be92f 100644
--- a/internal/check-packet-render.mjs
+++ b/internal/check-packet-render.mjs
@@ -58,6 +58,24 @@ const lang = await page.evaluate(() => document.documentElement.lang || 'en')
// the bug it is, and no other assertion here would notice.
const RTL = new Set(['ar', 'he', 'fa', 'ur', 'ps', 'sd', 'yi'])
const dir = await page.evaluate(() => document.documentElement.dir || 'ltr')
+// SCRIPT FONT. Some languages need a face the system stack does not carry — Hindi
+// renders as tofu boxes in Georgia/Palatino/serif. When the packet declares one,
+// it must actually LOAD; a declared-but-failed @font-face is silent, and the page
+// then prints as empty rectangles with every other assertion still green.
+//
+// ⚠ This asserts the FONT LOADED, not that glyphs are correct. A general tofu
+// detector was attempted and abandoned: comparing shaped width against a missing
+// font false-positives on every system-covered script (Cyrillic, CJK, Arabic all
+// legitimately measure the same in both stacks), and an ink-profile heuristic
+// scored CORRECTLY-RENDERED Devanagari the same as tofu, because its baseline bar
+// gives a uniform column profile. A detector that blocks working packets is worse
+// than none. Read the pixels for that; this covers the mechanical half.
+const fontStatus = await page.evaluate(async () => {
+ await document.fonts.ready
+ const faces = [...document.fonts]
+ return { declared: faces.length, loaded: faces.filter((f) => f.status === 'loaded').length,
+ names: faces.map((f) => `${f.family}:${f.status}`) }
+})
// The catalog sits beside the generator that produced this packet, so derive it
// from the packet's own path rather than hardcoding a machine-specific location.
const catalogPath = process.env.PACKET_STRINGS
@@ -145,6 +163,10 @@ if (r.rhythms !== r.rhythmsTotal) fails.push(`only ${r.rhythms}/${r.rhythmsTotal
if (!r.hasMark5) fails.push('Mark 5:1-20 is NOT printed in full (Driver requirement for point 4)')
if (r.metacamp !== 2) fails.push(`${r.metacamp} metacamp.org attributions, expected exactly 2`)
if (r.mapFaults.length) fails.push(`map: ${r.mapFaults.join('; ')}`)
+if (fontStatus.declared && fontStatus.loaded !== fontStatus.declared) {
+ fails.push(`a declared @font-face did not load (${fontStatus.names.join(', ')}) — `
+ + 'text in that script will print as empty boxes')
+}
if (RTL.has(lang) && dir !== 'rtl') fails.push(`lang="${lang}" is right-to-left but dir="${dir}"`)
if (!RTL.has(lang) && dir === 'rtl') fails.push(`lang="${lang}" is not RTL but dir="rtl"`)
if (translated === 0) {
@@ -162,6 +184,7 @@ console.log(` Mark 5 in full : ${r.hasMark5}`)
console.log(` metacamp.org : ${r.metacamp}`)
console.log(` map : ${r.mapFaults.length ? r.mapFaults.join('; ') : 'palette resolves, not black'}`)
console.log(` page size : ${boxes[0] ? boxes[0].join(' x ') : '?'} pt`)
+console.log(` script font : ${fontStatus.declared ? fontStatus.names.join(', ') : 'system stack'}`)
console.log(` language : ${lang} (dir=${dir}) (${r.rhythms}/${r.rhythmsTotal} rhythms in ${lang}`
+ `${translated === null ? '' : `, ${translated} strings translated`})`)
diff --git a/internal/check-print-geometry.mjs b/internal/check-print-geometry.mjs
new file mode 100644
index 000000000..7ce5df5c2
--- /dev/null
+++ b/internal/check-print-geometry.mjs
@@ -0,0 +1,95 @@
+// check-print-geometry — the THREE print paths must each produce their OWN page
+// geometry, from the UPG-100 page, in JavaScript.
+//
+// Driver: "make sure poster generation works and the aspect ratio of the print
+// dialog can be custom based on your print point, so print dialog works for
+// poster, for vertical packet, and for horizontal cards... not using python but
+// all working in the jamstack maps."
+//
+// One gate over all three because the failure they share is the same: a print
+// path silently falling back to letter. Chromium does that SILENTLY for any
+// @page size it does not recognise ('A6 portrait' -> 612x792pt, no error), so
+// every assertion here is on the MEASURED MediaBox, never on the stylesheet.
+//
+// Usage: node internal/check-print-geometry.mjs
+// PACKET_ORIGIN (default http://127.0.0.1:4898) — a static server over the repo root
+import { chromium } from 'playwright'
+
+const ORIGIN = process.env.PACKET_ORIGIN || 'http://127.0.0.1:4898'
+const API = process.env.PACKET_API || 'https://pray.doxa.life'
+const ISO3 = process.env.PACKET_ISO3 || 'LAO'
+
+// Each path, its expected page in POINTS (72pt = 1in), and its orientation.
+const PATHS = [
+ { id: 'packet (vertical)', w: 612, h: 792, orient: 'portrait' },
+ { id: 'cards (poker)', w: 180, h: 252, orient: 'portrait', stock: 'poker' },
+ { id: 'cards (landscape)', w: 252, h: 180, orient: 'landscape', stock: 'pokerLandscape' },
+ { id: 'poster', w: null, h: null, orient: 'portrait' }, // raster, not paginated
+]
+
+const b = await chromium.launch()
+const pg = await b.newPage({ viewport: { width: 1200, height: 900 } })
+const errs = []; pg.on('pageerror', (e) => errs.push(String(e).slice(0, 120)))
+await pg.goto(ORIGIN + '/')
+
+const fails = []
+const line = (id, got, want) => console.log(` ${id.padEnd(20)} ${got}${want ? ` (want ${want})` : ''}`)
+
+// ── 1 + 2: the paginated paths, measured through page.pdf ────────────────────
+for (const p of PATHS.filter((x) => x.w)) {
+ const html = await pg.evaluate(async ({ ORIGIN, API, ISO3, stock }) => {
+ const norm = await import(`${ORIGIN}/plugin-designs/my-upg-100-list/data/normalize.js`)
+ const strings = await fetch(`${ORIGIN}/library/scripts/posters/packet-strings.json`).then((r) => r.json())
+ const recs = norm.normalizeRecords(norm.extractRecords(await (await fetch(
+ `${API}/api/people-groups/list?fields=slug,name,country_code,population,religion,image_url,primary_language&lang=en`)).json()))
+ const groups = recs.filter((r) => String(r.country_code) === ISO3).slice(0, 6)
+ if (stock) {
+ const { createCardRenderer } = await import(`${ORIGIN}/library/packet/card-render.js`)
+ return createCardRenderer({ strings, lang: 'en', stock }).buildDeck({ id: 'x', label: 'X' }, groups)
+ }
+ const { createPacketRenderer } = await import(`${ORIGIN}/library/packet/packet-render.js`)
+ return createPacketRenderer({ strings, lang: 'en' }).buildPacket({ id: 'x', label: 'X' }, groups, new Map(), '')
+ }, { ORIGIN, API, ISO3, stock: p.stock })
+
+ await pg.setContent(html, { waitUntil: 'load' })
+ await pg.waitForTimeout(1200)
+ const pdf = await pg.pdf({ preferCSSPageSize: true })
+ const raw = pdf.toString('latin1')
+ const box = [...raw.matchAll(/MediaBox\s*\[\s*0\s+0\s+([\d.]+)\s+([\d.]+)\s*\]/g)].map((m) => [+m[1], +m[2]])[0] || [0, 0]
+ const [w, h] = box.map((v) => Math.round(v))
+ line(p.id, `${w} x ${h} pt`, `${p.w} x ${p.h}`)
+ if (Math.abs(w - p.w) > 2 || Math.abs(h - p.h) > 2) {
+ fails.push(`${p.id}: page is ${w}x${h}pt, expected ${p.w}x${p.h}pt`
+ + (Math.abs(w - 612) < 2 && Math.abs(h - 792) < 2 && p.w !== 612
+ ? ' — this is LETTER: the @page size silently fell back' : ''))
+ }
+ const landscape = w > h
+ if ((p.orient === 'landscape') !== landscape) {
+ fails.push(`${p.id}: orientation is ${landscape ? 'landscape' : 'portrait'}, expected ${p.orient}`)
+ }
+}
+
+// ── 3: the poster, which rasterises rather than paginating ───────────────────
+const poster = await pg.evaluate(async ({ ORIGIN }) => {
+ const { rasterPoster, looksRendered } = await import(`${ORIGIN}/library/packet/poster-raster.js`)
+ const res = await fetch(`${ORIGIN}/library/scripts/posters/out/posters/laos.html`)
+ if (!res.ok) return { error: `poster HTML ${res.status}` }
+ const blob = await rasterPoster(await res.text(), { scale: 1 })
+ const bmp = await createImageBitmap(blob)
+ return { w: bmp.width, h: bmp.height, ok: await looksRendered(blob), bytes: blob.size }
+}, { ORIGIN })
+if (poster.error) fails.push(`poster: ${poster.error}`)
+else {
+ line('poster', `${poster.w} x ${poster.h} px`, 'portrait, painted')
+ if (poster.w >= poster.h) fails.push(`poster rasterised ${poster.w}x${poster.h} — expected portrait`)
+ if (!poster.ok) fails.push('poster rasterised blank/black')
+}
+
+await b.close()
+if (errs.length) fails.push(`${errs.length} page error(s): ${errs[0]}`)
+if (fails.length) {
+ console.error(`\nPRINT-GEOMETRY: FAIL — ${fails.length} problem(s)`)
+ for (const f of fails) console.error(' - ' + f)
+ process.exit(1)
+}
+console.log('\nPRINT-GEOMETRY: PASS — every print path produces its own page geometry.')
diff --git a/library/data/countries.json b/library/data/countries.json
new file mode 100644
index 000000000..88889ed20
--- /dev/null
+++ b/library/data/countries.json
@@ -0,0 +1,970 @@
+[
+{
+"value": "AFG",
+"label": "Afghanistan"
+},
+{
+"value": "ALB",
+"label": "Albania"
+},
+{
+"value": "DZA",
+"label": "Algeria"
+},
+{
+"value": "ASM",
+"label": "American Samoa"
+},
+{
+"value": "AND",
+"label": "Andorra"
+},
+{
+"value": "AGO",
+"label": "Angola"
+},
+{
+"value": "AIA",
+"label": "Anguilla"
+},
+{
+"value": "ATG",
+"label": "Antigua and Barbuda"
+},
+{
+"value": "ARG",
+"label": "Argentina"
+},
+{
+"value": "ARM",
+"label": "Armenia"
+},
+{
+"value": "ABW",
+"label": "Aruba"
+},
+{
+"value": "AUS",
+"label": "Australia"
+},
+{
+"value": "AUT",
+"label": "Austria"
+},
+{
+"value": "AZE",
+"label": "Azerbaijan"
+},
+{
+"value": "BHR",
+"label": "Bahrain"
+},
+{
+"value": "BGD",
+"label": "Bangladesh"
+},
+{
+"value": "BRB",
+"label": "Barbados"
+},
+{
+"value": "BLR",
+"label": "Belarus"
+},
+{
+"value": "BEL",
+"label": "Belgium"
+},
+{
+"value": "BLZ",
+"label": "Belize"
+},
+{
+"value": "BEN",
+"label": "Benin"
+},
+{
+"value": "BMU",
+"label": "Bermuda"
+},
+{
+"value": "BTN",
+"label": "Bhutan"
+},
+{
+"value": "BOL",
+"label": "Bolivia"
+},
+{
+"value": "BES",
+"label": "Bonaire, Sint Eustatius, and Saba"
+},
+{
+"value": "BIH",
+"label": "Bosnia and Herzegovina"
+},
+{
+"value": "BWA",
+"label": "Botswana"
+},
+{
+"value": "BRA",
+"label": "Brazil"
+},
+{
+"value": "BRN",
+"label": "Brunei"
+},
+{
+"value": "BGR",
+"label": "Bulgaria"
+},
+{
+"value": "BFA",
+"label": "Burkina Faso"
+},
+{
+"value": "BDI",
+"label": "Burundi"
+},
+{
+"value": "CPV",
+"label": "Cabo Verde"
+},
+{
+"value": "KHM",
+"label": "Cambodia"
+},
+{
+"value": "CMR",
+"label": "Cameroon"
+},
+{
+"value": "CAN",
+"label": "Canada"
+},
+{
+"value": "CYM",
+"label": "Cayman Islands"
+},
+{
+"value": "CAF",
+"label": "Central African Republic"
+},
+{
+"value": "TCD",
+"label": "Chad"
+},
+{
+"value": "CHL",
+"label": "Chile"
+},
+{
+"value": "CHN",
+"label": "China"
+},
+{
+"value": "CXR",
+"label": "Christmas Island"
+},
+{
+"value": "CCK",
+"label": "Cocos (Keeling) Islands"
+},
+{
+"value": "COL",
+"label": "Colombia"
+},
+{
+"value": "COM",
+"label": "Comoros"
+},
+{
+"value": "COK",
+"label": "Cook Islands"
+},
+{
+"value": "CRI",
+"label": "Costa Rica"
+},
+{
+"value": "CIV",
+"label": "Côte d’Ivoire"
+},
+{
+"value": "HRV",
+"label": "Croatia"
+},
+{
+"value": "CUB",
+"label": "Cuba"
+},
+{
+"value": "CUW",
+"label": "Curaçao"
+},
+{
+"value": "CYP",
+"label": "Cyprus"
+},
+{
+"value": "DNK",
+"label": "Denmark"
+},
+{
+"value": "DJI",
+"label": "Djibouti"
+},
+{
+"value": "DMA",
+"label": "Dominica"
+},
+{
+"value": "DOM",
+"label": "Dominican Republic"
+},
+{
+"value": "ECU",
+"label": "Ecuador"
+},
+{
+"value": "EGY",
+"label": "Egypt"
+},
+{
+"value": "SLV",
+"label": "El Salvador"
+},
+{
+"value": "GNQ",
+"label": "Equatorial Guinea"
+},
+{
+"value": "ERI",
+"label": "Eritrea"
+},
+{
+"value": "EST",
+"label": "Estonia"
+},
+{
+"value": "SWZ",
+"label": "Eswatini"
+},
+{
+"value": "ETH",
+"label": "Ethiopia"
+},
+{
+"value": "FLK",
+"label": "Falkland Islands (Islas Malvinas)"
+},
+{
+"value": "FRO",
+"label": "Faroe Islands"
+},
+{
+"value": "FJI",
+"label": "Fiji"
+},
+{
+"value": "FIN",
+"label": "Finland"
+},
+{
+"value": "FRA",
+"label": "France"
+},
+{
+"value": "GUF",
+"label": "French Guiana"
+},
+{
+"value": "PYF",
+"label": "French Polynesia"
+},
+{
+"value": "GAB",
+"label": "Gabon"
+},
+{
+"value": "GEO",
+"label": "Georgia"
+},
+{
+"value": "DEU",
+"label": "Germany"
+},
+{
+"value": "GHA",
+"label": "Ghana"
+},
+{
+"value": "GIB",
+"label": "Gibraltar"
+},
+{
+"value": "GRC",
+"label": "Greece"
+},
+{
+"value": "GRL",
+"label": "Greenland"
+},
+{
+"value": "GRD",
+"label": "Grenada"
+},
+{
+"value": "GLP",
+"label": "Guadeloupe"
+},
+{
+"value": "GUM",
+"label": "Guam"
+},
+{
+"value": "GTM",
+"label": "Guatemala"
+},
+{
+"value": "GGY",
+"label": "Guernsey"
+},
+{
+"value": "GIN",
+"label": "Guinea"
+},
+{
+"value": "GNB",
+"label": "Guinea-Bissau"
+},
+{
+"value": "GUY",
+"label": "Guyana"
+},
+{
+"value": "HTI",
+"label": "Haiti"
+},
+{
+"value": "HND",
+"label": "Honduras"
+},
+{
+"value": "HKG",
+"label": "Hong Kong"
+},
+{
+"value": "HUN",
+"label": "Hungary"
+},
+{
+"value": "ISL",
+"label": "Iceland"
+},
+{
+"value": "IND",
+"label": "India"
+},
+{
+"value": "IDN",
+"label": "Indonesia"
+},
+{
+"value": "IRN",
+"label": "Iran"
+},
+{
+"value": "IRQ",
+"label": "Iraq"
+},
+{
+"value": "IRL",
+"label": "Ireland"
+},
+{
+"value": "IMN",
+"label": "Isle of Man"
+},
+{
+"value": "ISR",
+"label": "Israel"
+},
+{
+"value": "ITA",
+"label": "Italy"
+},
+{
+"value": "JAM",
+"label": "Jamaica"
+},
+{
+"value": "JPN",
+"label": "Japan"
+},
+{
+"value": "JEY",
+"label": "Jersey"
+},
+{
+"value": "JOR",
+"label": "Jordan"
+},
+{
+"value": "KAZ",
+"label": "Kazakhstan"
+},
+{
+"value": "KEN",
+"label": "Kenya"
+},
+{
+"value": "KIR",
+"label": "Kiribati"
+},
+{
+"value": "KOS",
+"label": "Kosovo"
+},
+{
+"value": "KWT",
+"label": "Kuwait"
+},
+{
+"value": "KGZ",
+"label": "Kyrgyzstan"
+},
+{
+"value": "LAO",
+"label": "Laos"
+},
+{
+"value": "LVA",
+"label": "Latvia"
+},
+{
+"value": "LBN",
+"label": "Lebanon"
+},
+{
+"value": "LSO",
+"label": "Lesotho"
+},
+{
+"value": "LBR",
+"label": "Liberia"
+},
+{
+"value": "LBY",
+"label": "Libya"
+},
+{
+"value": "LIE",
+"label": "Liechtenstein"
+},
+{
+"value": "LTU",
+"label": "Lithuania"
+},
+{
+"value": "LUX",
+"label": "Luxembourg"
+},
+{
+"value": "MAC",
+"label": "Macao"
+},
+{
+"value": "MDG",
+"label": "Madagascar"
+},
+{
+"value": "MWI",
+"label": "Malawi"
+},
+{
+"value": "MYS",
+"label": "Malaysia"
+},
+{
+"value": "MLI",
+"label": "Mali"
+},
+{
+"value": "MLT",
+"label": "Malta"
+},
+{
+"value": "MTQ",
+"label": "Martinique"
+},
+{
+"value": "MRT",
+"label": "Mauritania"
+},
+{
+"value": "MUS",
+"label": "Mauritius"
+},
+{
+"value": "MYT",
+"label": "Mayotte"
+},
+{
+"value": "MEX",
+"label": "Mexico"
+},
+{
+"value": "MDA",
+"label": "Moldova"
+},
+{
+"value": "MCO",
+"label": "Monaco"
+},
+{
+"value": "MNG",
+"label": "Mongolia"
+},
+{
+"value": "MNE",
+"label": "Montenegro"
+},
+{
+"value": "MSR",
+"label": "Montserrat"
+},
+{
+"value": "MAR",
+"label": "Morocco"
+},
+{
+"value": "MOZ",
+"label": "Mozambique"
+},
+{
+"value": "MMR",
+"label": "Myanmar"
+},
+{
+"value": "NAM",
+"label": "Namibia"
+},
+{
+"value": "NRU",
+"label": "Nauru"
+},
+{
+"value": "NPL",
+"label": "Nepal"
+},
+{
+"value": "NCL",
+"label": "New Caledonia"
+},
+{
+"value": "NZL",
+"label": "New Zealand"
+},
+{
+"value": "NIC",
+"label": "Nicaragua"
+},
+{
+"value": "NER",
+"label": "Niger"
+},
+{
+"value": "NGA",
+"label": "Nigeria"
+},
+{
+"value": "NIU",
+"label": "Niue"
+},
+{
+"value": "NFK",
+"label": "Norfolk Island"
+},
+{
+"value": "PRK",
+"label": "North Korea"
+},
+{
+"value": "MKD",
+"label": "North Macedonia"
+},
+{
+"value": "NOR",
+"label": "Norway"
+},
+{
+"value": "OMN",
+"label": "Oman"
+},
+{
+"value": "PAK",
+"label": "Pakistan"
+},
+{
+"value": "PLW",
+"label": "Palau"
+},
+{
+"value": "PSE",
+"label": "Palestine"
+},
+{
+"value": "PAN",
+"label": "Panama"
+},
+{
+"value": "PNG",
+"label": "Papua New Guinea"
+},
+{
+"value": "PRY",
+"label": "Paraguay"
+},
+{
+"value": "PER",
+"label": "Peru"
+},
+{
+"value": "POL",
+"label": "Poland"
+},
+{
+"value": "PRT",
+"label": "Portugal"
+},
+{
+"value": "PRI",
+"label": "Puerto Rico"
+},
+{
+"value": "QAT",
+"label": "Qatar"
+},
+{
+"value": "REU",
+"label": "Reunion"
+},
+{
+"value": "ROU",
+"label": "Romania"
+},
+{
+"value": "RWA",
+"label": "Rwanda"
+},
+{
+"value": "BLM",
+"label": "Saint Barthelemy"
+},
+{
+"value": "SHN",
+"label": "Saint Helena, Ascension, and Tristan da Cunha"
+},
+{
+"value": "KNA",
+"label": "Saint Kitts and Nevis"
+},
+{
+"value": "LCA",
+"label": "Saint Lucia"
+},
+{
+"value": "MAF",
+"label": "Saint Martin"
+},
+{
+"value": "SPM",
+"label": "Saint Pierre and Miquelon"
+},
+{
+"value": "VCT",
+"label": "Saint Vincent and the Grenadines"
+},
+{
+"value": "WSM",
+"label": "Samoa"
+},
+{
+"value": "SMR",
+"label": "San Marino"
+},
+{
+"value": "STP",
+"label": "Sao Tome and Principe"
+},
+{
+"value": "SAU",
+"label": "Saudi Arabia"
+},
+{
+"value": "SEN",
+"label": "Senegal"
+},
+{
+"value": "SRB",
+"label": "Serbia"
+},
+{
+"value": "SYC",
+"label": "Seychelles"
+},
+{
+"value": "SLE",
+"label": "Sierra Leone"
+},
+{
+"value": "SGP",
+"label": "Singapore"
+},
+{
+"value": "SXM",
+"label": "Sint Maarten"
+},
+{
+"value": "SVK",
+"label": "Slovakia"
+},
+{
+"value": "SVN",
+"label": "Slovenia"
+},
+{
+"value": "SOM",
+"label": "Somalia"
+},
+{
+"value": "ZAF",
+"label": "South Africa"
+},
+{
+"value": "KOR",
+"label": "South Korea"
+},
+{
+"value": "SSD",
+"label": "South Sudan"
+},
+{
+"value": "ESP",
+"label": "Spain"
+},
+{
+"value": "LKA",
+"label": "Sri Lanka"
+},
+{
+"value": "SDN",
+"label": "Sudan"
+},
+{
+"value": "SUR",
+"label": "Suriname"
+},
+{
+"value": "SJM",
+"label": "Svalbard"
+},
+{
+"value": "SWE",
+"label": "Sweden"
+},
+{
+"value": "CHE",
+"label": "Switzerland"
+},
+{
+"value": "SYR",
+"label": "Syria"
+},
+{
+"value": "TWN",
+"label": "Taiwan"
+},
+{
+"value": "TJK",
+"label": "Tajikistan"
+},
+{
+"value": "TZA",
+"label": "Tanzania"
+},
+{
+"value": "THA",
+"label": "Thailand"
+},
+{
+"value": "BHS",
+"label": "The Bahamas"
+},
+{
+"value": "VGB",
+"label": "the British Virgin Islands"
+},
+{
+"value": "CZE",
+"label": "the Czech Republic"
+},
+{
+"value": "COD",
+"label": "the Democratic Republic of the Congo"
+},
+{
+"value": "FSM",
+"label": "the Federated States of Micronesia"
+},
+{
+"value": "GMB",
+"label": "The Gambia"
+},
+{
+"value": "MDV",
+"label": "the Maldives"
+},
+{
+"value": "MHL",
+"label": "the Marshall Islands"
+},
+{
+"value": "NLD",
+"label": "the Netherlands"
+},
+{
+"value": "MNP",
+"label": "the Northern Mariana Islands"
+},
+{
+"value": "PHL",
+"label": "the Philippines"
+},
+{
+"value": "PCN",
+"label": "the Pitcairn Islands"
+},
+{
+"value": "COG",
+"label": "the Republic of the Congo"
+},
+{
+"value": "RUS",
+"label": "the Russian Federation"
+},
+{
+"value": "SLB",
+"label": "the Solomon Islands"
+},
+{
+"value": "TCA",
+"label": "the Turks and Caicos Islands"
+},
+{
+"value": "ARE",
+"label": "the United Arab Emirates"
+},
+{
+"value": "GBR",
+"label": "the United Kingdom"
+},
+{
+"value": "USA",
+"label": "the United States"
+},
+{
+"value": "VIR",
+"label": "the Virgin Islands"
+},
+{
+"value": "TLS",
+"label": "Timor-Leste"
+},
+{
+"value": "TGO",
+"label": "Togo"
+},
+{
+"value": "TKL",
+"label": "Tokelau"
+},
+{
+"value": "TON",
+"label": "Tonga"
+},
+{
+"value": "TTO",
+"label": "Trinidad and Tobago"
+},
+{
+"value": "TUN",
+"label": "Tunisia"
+},
+{
+"value": "TUR",
+"label": "Turkiye"
+},
+{
+"value": "TKM",
+"label": "Turkmenistan"
+},
+{
+"value": "TUV",
+"label": "Tuvalu"
+},
+{
+"value": "UGA",
+"label": "Uganda"
+},
+{
+"value": "UKR",
+"label": "Ukraine"
+},
+{
+"value": "URY",
+"label": "Uruguay"
+},
+{
+"value": "UZB",
+"label": "Uzbekistan"
+},
+{
+"value": "VUT",
+"label": "Vanuatu"
+},
+{
+"value": "VAT",
+"label": "Vatican City"
+},
+{
+"value": "VEN",
+"label": "Venezuela"
+},
+{
+"value": "VNM",
+"label": "Vietnam"
+},
+{
+"value": "WLF",
+"label": "Wallis and Futuna"
+},
+{
+"value": "ESH",
+"label": "Western Sahara"
+},
+{
+"value": "YEM",
+"label": "Yemen"
+},
+{
+"value": "ZMB",
+"label": "Zambia"
+},
+{
+"value": "ZWE",
+"label": "Zimbabwe"
+}
+]
\ No newline at end of file
diff --git a/library/packet/card-render.js b/library/packet/card-render.js
index b5e0609ae..7593550c7 100644
--- a/library/packet/card-render.js
+++ b/library/packet/card-render.js
@@ -37,10 +37,17 @@ import { qrSvg } from './qr.js';
// sleeves and boxes are built around, and the one people already associate with a
// deck you hold and deal out. Bleed is included in the page so a trimmed card
// keeps its full-bleed portrait.
+// ★ LANDSCAPE IS NOT JUST TRANSPOSED DIMENSIONS. The portrait card stacks the
+// photo ABOVE the text (portrait occupies the top 46%); at 3.5 x 2.5in that same
+// band is a strip too short to carry a face or leave room for the rows beneath it.
+// A landscape card puts the photo BESIDE the text instead — so `side: true` is a
+// LAYOUT switch the CSS reads, not a cosmetic flag.
export const STOCK = {
- poker: { w: 2.5, h: 3.5, label: 'Poker (2.5 x 3.5 in)' },
- postcard: { w: 4, h: 6, label: 'Postcard (4 x 6 in)' },
- a6: { w: 4.13, h: 5.83, label: 'A6 (105 x 148 mm)' }, // explicit in, NOT "A6"
+ poker: { w: 2.5, h: 3.5, label: 'Card 2.5 × 3.5 in' },
+ pokerLandscape: { w: 3.5, h: 2.5, label: 'Card 3.5 × 2.5 in (landscape)', side: true },
+ postcard: { w: 4, h: 6, label: 'Postcard 4 × 6 in' },
+ postcardLandscape: { w: 6, h: 4, label: 'Postcard 6 × 4 in (landscape)', side: true },
+ a6: { w: 4.13, h: 5.83, label: 'A6 105 × 148 mm' }, // explicit in, NOT "A6"
};
const PX = 96; // CSS px per inch
@@ -73,6 +80,10 @@ export function createCardRenderer({ strings, lang = 'en', stock = 'poker',
const S = STOCK[stock] || STOCK.poker;
const W = Math.round(S.w * PX); // card box in CSS px
const H = Math.round(S.h * PX);
+ // Photo BESIDE the text rather than above it. Driven by the stock, not by
+ // comparing W to H: a square stock would be ambiguous, and the choice is a
+ // deliberate property of the format rather than an inference from its numbers.
+ const SIDE = S.side === true;
// The live profile URL for this group, in this language. English has no prefix
// (the generator's own rule); every other language does.
@@ -93,26 +104,36 @@ export function createCardRenderer({ strings, lang = 'en', stock = 'poker',
page-break-after:always;break-after:page}
.card:last-child{page-break-after:auto;break-after:auto}
- /* PORTRAIT: full-bleed across the top. A face is what makes this a prayer card
- rather than a data tile — it is the largest thing on the card by design. */
- .ph{position:absolute;left:0;top:0;width:${W}px;height:${Math.round(H * 0.46)}px;
- object-fit:cover;display:block;background:#ddd6c6}
- .ph-none{position:absolute;left:0;top:0;width:${W}px;height:${Math.round(H * 0.46)}px;
- background:linear-gradient(160deg,#e6dfcd,#cfc6ad);display:flex;
- align-items:center;justify-content:center;
+ /* THE PHOTO. Portrait: full-bleed across the TOP, the largest thing on the card
+ by design — a face is what makes this a prayer card rather than a data tile.
+ Landscape: full-bleed down the LEFT instead, because a 46% band on a 2.5in-tall
+ card is a strip too short to carry a face AND leave room for the rows. */
+ .ph,.ph-none{position:absolute;left:0;top:0;display:block;
+ width:${SIDE ? Math.round(W * 0.42) : W}px;
+ height:${SIDE ? H : Math.round(H * 0.46)}px}
+ .ph{object-fit:cover;background:#ddd6c6}
+ .ph-none{background:linear-gradient(160deg,#e6dfcd,#cfc6ad);display:flex;
+ align-items:center;justify-content:center;text-align:center;padding:6px;
font:italic 11px Georgia,serif;color:#8c8170}
/* A soft scrim under the name so it stays legible over any photo. */
- .scrim{position:absolute;left:0;top:${Math.round(H * 0.30)}px;width:${W}px;
- height:${Math.round(H * 0.16)}px;
+ .scrim{position:absolute;left:0;
+ width:${SIDE ? Math.round(W * 0.42) : W}px;
+ top:${SIDE ? Math.round(H * 0.58) : Math.round(H * 0.30)}px;
+ height:${SIDE ? Math.round(H * 0.42) : Math.round(H * 0.16)}px;
background:linear-gradient(to bottom,rgba(24,22,18,0),rgba(24,22,18,.72))}
- .nm{position:absolute;left:14px;right:14px;top:${Math.round(H * 0.355)}px;
- font:700 19px Georgia,serif;color:#fff;line-height:1.05;
+ .nm{position:absolute;
+ left:${SIDE ? 10 : 14}px;
+ ${SIDE ? `width:${Math.round(W * 0.42) - 20}px;` : `right:14px;`}
+ top:${SIDE ? Math.round(H * 0.74) : Math.round(H * 0.355)}px;
+ font:700 ${SIDE ? 15 : 19}px Georgia,serif;color:#fff;line-height:1.05;
text-shadow:0 1px 3px rgba(0,0,0,.5)}
- .nm.long{font-size:15px}
+ .nm.long{font-size:${SIDE ? 12 : 15}px}
- .body{position:absolute;left:14px;right:14px;top:${Math.round(H * 0.48)}px;
- bottom:${Math.round(H * 0.20)}px;overflow:hidden}
+ .body{position:absolute;right:14px;overflow:hidden;
+ left:${SIDE ? Math.round(W * 0.42) + 12 : 14}px;
+ top:${SIDE ? 12 : Math.round(H * 0.48)}px;
+ bottom:${SIDE ? Math.round(H * 0.30) : Math.round(H * 0.20)}px}
.row{display:flex;gap:6px;font:400 9.5px Georgia,serif;line-height:1.5;margin-bottom:1px}
.k{color:#8c8170;min-width:52px;flex:0 0 auto}
.v{color:#3b3a34;font-weight:600}
@@ -120,7 +141,8 @@ export function createCardRenderer({ strings, lang = 'en', stock = 'poker',
/* FOOTER: the QR is the card's whole reason to survive the day it is handed
over — it carries the reader to the live profile in their own language. */
- .ft{position:absolute;left:14px;right:14px;bottom:10px;
+ .ft{position:absolute;right:14px;bottom:10px;
+ left:${SIDE ? Math.round(W * 0.42) + 12 : 14}px;
display:flex;align-items:flex-end;gap:8px}
.qr{flex:0 0 auto;line-height:0}
.ftx{flex:1 1 auto;min-width:0}
diff --git a/library/packet/packet-render.js b/library/packet/packet-render.js
index 3931dbd5f..6e22c1499 100644
--- a/library/packet/packet-render.js
+++ b/library/packet/packet-render.js
@@ -26,7 +26,23 @@
// so a new Arabic-script locale is one entry here and not a schema change.
const RTL = new Set(['ar', 'he', 'fa', 'ur', 'ps', 'sd', 'yi']);
-export function createPacketRenderer({ strings, lang = 'en' }) {
+// Scripts with no coverage in the packet's Georgia/Palatino/serif stack, mapped to
+// the vendored face that does cover them. MEASURED by rendering each script at 48px
+// and reading the PIXELS: Latin, Cyrillic, Arabic and CJK all shape correctly with
+// system fonts; DEVANAGARI renders as tofu boxes.
+// ⚠ A width heuristic got this exactly backwards — it reported Arabic and Chinese
+// broken and Hindi fine, because Devanagari tofu boxes happen to match real glyph
+// advance. Never infer glyph coverage from measurements; look at the pixels.
+const SCRIPT_FONT = {
+ hi: { family: 'Noto Serif Devanagari', dir: 'noto-serif-devanagari',
+ file: 'noto-serif-devanagari-400-devanagari.woff2' },
+ mr: { family: 'Noto Serif Devanagari', dir: 'noto-serif-devanagari',
+ file: 'noto-serif-devanagari-400-devanagari.woff2' }, // Marathi, same script
+ ne: { family: 'Noto Serif Devanagari', dir: 'noto-serif-devanagari',
+ file: 'noto-serif-devanagari-400-devanagari.woff2' }, // Nepali, same script
+};
+
+export function createPacketRenderer({ strings, lang = 'en', fontData = null }) {
const EN = strings.en;
const L = strings[lang] || {};
const t = (k) => (L[k] !== undefined ? L[k] : EN[k]);
@@ -56,8 +72,28 @@ export function createPacketRenderer({ strings, lang = 'en' }) {
const sheet = (inner, cls = '') =>
`
${inner}
`;
+ // The script font for THIS language, if it needs one. `fontData` is a base64
+ // woff2 the caller supplies: the packet must stay a SELF-CONTAINED file that
+ // prints offline (the Node generator writes standalone HTML with no server, and
+ // photos are already inlined for the same reason), so a URL would break exactly
+ // where the packet is most used. Absent fontData, the rule is simply omitted and
+ // the stack falls back to serif — a legible wrong font beats a broken document.
+ const SF = SCRIPT_FONT[lang] || null;
+ const fontFace = (SF && fontData)
+ ? `@font-face{font-family:'${SF.family}';font-style:normal;font-weight:400;`
+ + `font-display:swap;src:url(data:font/woff2;base64,${fontData}) format('woff2')}`
+ : '';
+ // Prepend the script face to EVERY element's stack in one rule rather than
+ // editing 22 hardcoded Georgia declarations. The browser falls through per
+ // CHARACTER, so Latin text in a Hindi packet still renders in Georgia.
+ const scriptStack = SF
+ ? `.sheet,.sheet *{font-family:'${SF.family}',Georgia,'Palatino Linotype',serif !important}`
+ : '';
+
function css() {
return `
+ ${fontFace}
+ ${scriptStack}
@page{size:letter portrait;margin:0}
html,body{margin:0;background:#fff;-webkit-print-color-adjust:exact;print-color-adjust:exact}
.sheet{position:relative;width:${W}px;height:${H}px;background:#f7f4ec;overflow:hidden;
diff --git a/library/scripts/posters/packet-strings.json b/library/scripts/posters/packet-strings.json
index 125a4b3e2..2778e9343 100644
--- a/library/scripts/posters/packet-strings.json
+++ b/library/scripts/posters/packet-strings.json
@@ -4,7 +4,7 @@
"how_to_translate": "Copy the \"en\" block, change the key to the ISO code, translate the values. Keep {placeholders} exactly as they are — they are substituted at render time. Do not translate scripture REFERENCES (book names are resolved by the reader's own Bible); DO translate the prayer text.",
"note_on_scripture": "point4_passage is the ONLY full scripture text in the packet (Mark 5:1-20, printed in full at the Driver's instruction). A translation MUST supply its own rendering in a public-domain or properly-licensed version FOR THAT LANGUAGE. ⚠ Do NOT translate it yourself and do NOT leave a machine rendering in place: this is printed scripture in a prayer packet. A language block that omits point4_passage falls back to the English WEB text per-key, which is honest — an untrusted translation of Mark 5 is not.",
"note_on_references": "Scripture REFERENCES (Matthew 27:27, Mark 5:1-20) are deliberately left in English in every language block, so a reader can look them up in their own Bible regardless of the packet's language. ⚠ MEASURED TRADEOFF, not an oversight: on a rendered French page the English book names are visibly foreign next to fully-translated prayer text. If a language wants localised book names, add a `books` map to that block and teach the renderer to use it — do NOT translate the refs inline, because the same strings identify the passage for any future lookup.",
- "note_on_scripts": "MEASURED 2026-09-24 by rendering each script at 48px and READING THE PIXELS: Latin, Cyrillic (ru), Arabic (ar) and CJK (zh) all shape correctly in the packet's Georgia/serif stack. DEVANAGARI (hi) renders as TOFU BOXES — this box has zero Devanagari fonts installed (fc-list). So hi is BLOCKED on vendoring a Devanagari font (e.g. Noto Serif Devanagari) into public/assets/fonts and adding it to the packet's font stack; adding a hi block before then would ship a page of empty rectangles. ⚠ A WIDTH HEURISTIC GOT THIS BACKWARDS: comparing rendered advance against a missing-font baseline reported ar and zh as broken and hi as fine — the exact opposite of the truth, because Devanagari tofu boxes happen to match real glyph advance. Read the pixels.",
+ "note_on_scripts": "ALL 11 app locales now ship. Script coverage MEASURED by rendering at 48px and READING THE PIXELS: Latin, Cyrillic (ru), Arabic (ar) and CJK (zh) shape correctly with the system Georgia/Palatino/serif stack. DEVANAGARI (hi) does NOT — it renders as tofu boxes — so Noto Serif Devanagari is vendored (OFL) and base64-INLINED into hi packets by the renderer's SCRIPT_FONT map. Inlined, not linked, because a packet is a standalone file that must print offline; ~165KB on a 3.5MB packet, and only for that language. mr and ne map to the same face.\n⚠ TWO MEASUREMENTS LIED ABOUT THIS, so do not automate it cheaply: (1) comparing shaped width against a missing font reported ar and zh BROKEN and hi FINE — exactly backwards, because Devanagari tofu boxes match real glyph advance; (2) an ink-column-profile heuristic scored CORRECTLY-RENDERED Devanagari the same as tofu (3 vs 2 distinct heights), because its baseline bar gives a uniform profile. check:packet-render therefore asserts only that a DECLARED @font-face LOADED — the mechanical half — and leaves glyph correctness to a human reading a screenshot. A detector that blocks working packets is worse than none.",
"note_on_rtl": "ar is right-to-left. The renderer emits dir on from an RTL set (packet-render.js), and scripture REFERENCES are wrapped in dir=ltr + unicode-bidi:isolate. Without that isolation the bidi algorithm reorders a reference that STARTS WITH A DIGIT: '1 Peter 5:6-11' rendered as 'Peter 5:6-11 1'. Note the reference LIST then reads right-to-left on an RTL page (first item rightmost) — that is correct for an RTL reader, not a defect."
},
"en": {
@@ -1430,5 +1430,321 @@
]
}
]
+ },
+ "hi": {
+ "map_page_kicker": "अनछुए जनसमूह",
+ "map_page_sub": "{count} अनछुए जनसमूहों की प्रार्थना सूची",
+ "list_header": "{count} में से {from}–{to} · उनके नाम लेकर प्रार्थना करें",
+ "people_suffix": "लोग",
+ "no_photo": "कोई चित्र उपलब्ध नहीं",
+ "deck_title": "प्रार्थना कार्ड",
+ "card_country": "देश",
+ "card_population": "जनसंख्या",
+ "card_language": "भाषा",
+ "card_religion": "धर्म",
+ "card_cta": "उनके लिए प्रार्थना करें",
+ "points_title": "12 प्रार्थना बिंदु",
+ "points_intro": "परमेश्वर के साथ एक घंटा, हर बिंदु के लिए पाँच मिनट। इस पैकेट के जनसमूहों के लिए ये बारह बिंदु प्रार्थना करें।",
+ "points_attrib": "प्रार्थना लय Metacamp से प्रेरित · metacamp.org",
+ "wheel_title": "प्रार्थना चक्र",
+ "wheel_intro": "परमेश्वर के साथ एक घंटा बिताना? यह आपके सोचने से आसान है। इसे 5 मिनट के हिस्सों में बाँटें और हर हिस्से में इनमें से एक पर ध्यान दें।",
+ "wheel_minutes": "मिनट",
+ "wheel_attrib": "प्रार्थना चक्र · Metacamp मॉड्यूल 1 · metacamp.org",
+ "privacy_note": "चिह्नों की स्थिति अनुमानित है · गोपनीयता के लिए जनसमूहों के स्थान स्रोत पर सामान्यीकृत किए गए हैं",
+ "rhythms": {
+ "praise": "स्तुति",
+ "wait": "प्रतीक्षा",
+ "confess": "अंगीकार",
+ "read": "वचन पढ़ना",
+ "ask": "माँगना",
+ "intercede": "मध्यस्थता",
+ "pray_word": "वचन से प्रार्थना",
+ "thank": "धन्यवाद",
+ "sing": "गायन",
+ "meditate": "मनन",
+ "listen": "सुनना",
+ "praise_end": "स्तुति"
+ },
+ "points": [
+ {
+ "n": 1,
+ "rhythm": "praise",
+ "sub": "दो क्रूसों का सुसमाचार",
+ "text": "कि वे मसीह को राजा मानकर ग्रहण करें, उसके क्रूस से क्षमा पाएँ — और अपना क्रूस उठाएँ, सताव के क्षेत्र में दुख उठाने को तैयार। मेम्ने को उसके दुख का प्रतिफल मिले, मुझसे आरंभ करके।",
+ "refs": [
+ "Matthew 27:27",
+ "Matthew 16:24–25"
+ ]
+ },
+ {
+ "n": 2,
+ "rhythm": "wait",
+ "sub": "पवित्र आत्मा और छुटकारा",
+ "text": "कि वे आत्मा से भरें और साहसी गवाह बनें; अकेले न रहें; जल और आत्मा से बपतिस्मा पाएँ; बुराई की हर पकड़ टूट जाए। कि पड़ोसी जातियों के विश्वासी मज़दूरों के रूप में उठ खड़े हों।",
+ "refs": [
+ "Acts 1:8",
+ "John 14:26"
+ ]
+ },
+ {
+ "n": 3,
+ "rhythm": "confess",
+ "sub": "",
+ "text": "हम इस अविश्वास को मानते हैं कि यह परमेश्वर के लिए बहुत बड़ा है। कि वह संसार के अनछुए लोगों के लिए हमारे प्रेम और विश्वास को फिर से जगाए।",
+ "refs": [
+ "John 3:16",
+ "1 John 1:9"
+ ]
+ },
+ {
+ "n": 4,
+ "rhythm": "read",
+ "sub": "पिता का प्रेम",
+ "text": "कि वे उद्धार पाएँ और जानें कि उनका स्वर्गीय पिता उनसे प्रेम करता है — और उनके द्वारा वह उनके परिवार, मित्रों, पड़ोसियों, सहकर्मियों, सहपाठियों, उन सबके पास उद्धार पहुँचाए जिन्हें वे व्यक्तिगत रूप से जानते हैं।",
+ "refs": [
+ "Mark 5:1–20",
+ "Acts 16:31"
+ ],
+ "print_passage": true
+ },
+ {
+ "n": 5,
+ "rhythm": "ask",
+ "sub": "बपतिस्मा",
+ "text": "स्थानीय लोगों में नए बपतिस्मा हों, और हर बपतिस्मा पाया हुआ व्यक्ति दूसरों को बपतिस्मा देने के लिए प्रशिक्षित हो, और यह बढ़ता जाए।",
+ "refs": [
+ "Acts 16",
+ "Acts 10"
+ ]
+ },
+ {
+ "n": 6,
+ "rhythm": "intercede",
+ "sub": "मज़दूर और चरवाहे",
+ "text": "मज़दूर भेजे जाएँ जो उस जाति से गहरा प्रेम करें जिससे वे कभी नहीं मिले; और भीतर से अच्छे चरवाहे उठाए जाएँ।",
+ "refs": [
+ "Luke 10:2",
+ "John 10:11–15"
+ ]
+ },
+ {
+ "n": 7,
+ "rhythm": "pray_word",
+ "sub": "शांति के जन और घर",
+ "text": "कि मज़दूर और स्थानीय अगुवे ग्रहणशील व्यक्ति और शांति के घर को पाएँ, और उन्हें भली भाँति चेला बनाएँ।",
+ "refs": [
+ "Luke 10:5–7",
+ "Matthew 10:5–14"
+ ]
+ },
+ {
+ "n": 8,
+ "rhythm": "thank",
+ "sub": "विश्राम",
+ "text": "परमेश्वर का धन्यवाद कि उसने विश्राम दिया है; कि वे मसीह में उस विश्राम में प्रवेश करें, और उनका आत्मिक परिवार एक सुरक्षित स्थान हो।",
+ "refs": [
+ "Mark 4:26–29",
+ "Hebrews 4:9–11"
+ ]
+ },
+ {
+ "n": 9,
+ "rhythm": "sing",
+ "sub": "",
+ "text": "कि वह जनसमूह आराधकों से भर जाए — हर जगह आराधना, स्वतंत्रता में और बेड़ियों में भी।",
+ "refs": [
+ "Acts 16:25–26",
+ "Colossians 3:16"
+ ]
+ },
+ {
+ "n": 10,
+ "rhythm": "meditate",
+ "sub": "प्रभु भोज",
+ "text": "कि वे मिलकर प्रभु भोज लें और उसकी मृत्यु, गाड़े जाने और पुनरुत्थान के भार को समझें।",
+ "refs": [
+ "Matthew 26:26–30",
+ "1 Corinthians 11:23–26"
+ ]
+ },
+ {
+ "n": 11,
+ "rhythm": "listen",
+ "sub": "जवाबदेही",
+ "text": "केवल यह नहीं कि वे सुनें, परन्तु यह कि उनके जीवन में ऐसे लोग हों जो उन्हें वचन पढ़ने, प्रार्थना करने, संगति में लगे रहने और दुख का सही उत्तर देने में सहायता करें — उस शत्रु का सामना करते हुए जो सारे संसार में विश्वासियों के पीछे पड़ा है।",
+ "refs": [
+ "1 Peter 5:6–11",
+ "Hebrews 10:24–25"
+ ]
+ },
+ {
+ "n": 12,
+ "rhythm": "praise_end",
+ "sub": "महान · और महान · सबसे महान",
+ "text": "पहले से स्तुति — उद्धार के लिए; और महान, बढ़ते हुए आत्मिक परिवारों के लिए; सबसे महान, समर्पित जीवनों और उठाए गए स्थानीय कलीसिया-रोपकों के लिए जो पूरे जनसमूह में स्वस्थ कलीसियाएँ बढ़ाते हैं, चाहे मूल्य कुछ भी हो।",
+ "refs": [
+ "Acts 2:42–47",
+ "Matthew 28:19–20"
+ ]
+ }
+ ]
+ },
+ "zh": {
+ "map_page_kicker": "未得之民",
+ "map_page_sub": "為 {count} 個未得之民的禱告名單",
+ "list_header": "{count} 之中的 {from}–{to} · 按名為他們禱告",
+ "people_suffix": "人",
+ "no_photo": "沒有照片",
+ "deck_title": "禱告卡",
+ "card_country": "國家",
+ "card_population": "人口",
+ "card_language": "語言",
+ "card_religion": "宗教",
+ "card_cta": "為他們禱告",
+ "points_title": "12 個禱告要點",
+ "points_intro": "與神同在一小時,每個要點五分鐘。為這份禱告冊中的族群禱告這十二個要點。",
+ "points_attrib": "禱告節奏取材自 Metacamp · metacamp.org",
+ "wheel_title": "禱告輪",
+ "wheel_intro": "與神同在一小時?比你想的容易。把它分成五分鐘一段,每一段專注其中一個要點。",
+ "wheel_minutes": "分鐘",
+ "wheel_attrib": "禱告輪 · Metacamp 第一單元 · metacamp.org",
+ "privacy_note": "標記位置為概略位置 · 族群所在地已於資料來源處作概化處理以保護隱私",
+ "rhythms": {
+ "praise": "讚美",
+ "wait": "等候",
+ "confess": "認罪",
+ "read": "讀神的話",
+ "ask": "祈求",
+ "intercede": "代禱",
+ "pray_word": "以神的話禱告",
+ "thank": "感謝",
+ "sing": "歌唱",
+ "meditate": "默想",
+ "listen": "聆聽",
+ "praise_end": "讚美"
+ },
+ "points": [
+ {
+ "n": 1,
+ "rhythm": "praise",
+ "sub": "兩個十字架的福音",
+ "text": "願他們接受基督為王,藉著祂的十字架得赦免——也背起自己的十字架,甘願在逼迫之地受苦。願羔羊得著祂受苦的賞賜,從我開始。",
+ "refs": [
+ "Matthew 27:27",
+ "Matthew 16:24–25"
+ ]
+ },
+ {
+ "n": 2,
+ "rhythm": "wait",
+ "sub": "聖靈與釋放",
+ "text": "願他們被聖靈充滿,成為剛強的見證人;不至孤單;受水和聖靈的洗;一切邪惡的轄制都被打破。願鄰近族群中的信徒被興起作工人。",
+ "refs": [
+ "Acts 1:8",
+ "John 14:26"
+ ]
+ },
+ {
+ "n": 3,
+ "rhythm": "confess",
+ "sub": "",
+ "text": "我們承認自己的不信,以為這事對神太難。願祂重新點燃我們對普世未得之民的愛與信心。",
+ "refs": [
+ "John 3:16",
+ "1 John 1:9"
+ ]
+ },
+ {
+ "n": 4,
+ "rhythm": "read",
+ "sub": "天父的愛",
+ "text": "願他們得救,並知道天父愛他們——也願神藉著他們,把救恩帶給他們的家人、朋友、鄰舍、同事、同學,以及他們所認識的每一個人。",
+ "refs": [
+ "Mark 5:1–20",
+ "Acts 16:31"
+ ],
+ "print_passage": true
+ },
+ {
+ "n": 5,
+ "rhythm": "ask",
+ "sub": "洗禮",
+ "text": "願當地有新的受洗者,並且每一位受洗的人都被裝備去為別人施洗,不斷繁增。",
+ "refs": [
+ "Acts 16",
+ "Acts 10"
+ ]
+ },
+ {
+ "n": 6,
+ "rhythm": "intercede",
+ "sub": "工人與牧人",
+ "text": "願工人被差派去深愛一群素未謀面的人;也願好牧人從他們中間被興起。",
+ "refs": [
+ "Luke 10:2",
+ "John 10:11–15"
+ ]
+ },
+ {
+ "n": 7,
+ "rhythm": "pray_word",
+ "sub": "平安之子與平安之家",
+ "text": "願工人與當地帶領者尋得那樂意接待的人和平安之家,並好好門訓他們。",
+ "refs": [
+ "Luke 10:5–7",
+ "Matthew 10:5–14"
+ ]
+ },
+ {
+ "n": 8,
+ "rhythm": "thank",
+ "sub": "安息",
+ "text": "感謝神所賜的安息;願他們在基督裡進入那安息,也願他們的屬靈家庭是安全的地方。",
+ "refs": [
+ "Mark 4:26–29",
+ "Hebrews 4:9–11"
+ ]
+ },
+ {
+ "n": 9,
+ "rhythm": "sing",
+ "sub": "",
+ "text": "願這族群充滿敬拜者——處處敬拜,在自由中,也在捆鎖中。",
+ "refs": [
+ "Acts 16:25–26",
+ "Colossians 3:16"
+ ]
+ },
+ {
+ "n": 10,
+ "rhythm": "meditate",
+ "sub": "聖餐",
+ "text": "願他們一同領受聖餐,深深體會祂的死、埋葬與復活的份量。",
+ "refs": [
+ "Matthew 26:26–30",
+ "1 Corinthians 11:23–26"
+ ]
+ },
+ {
+ "n": 11,
+ "rhythm": "listen",
+ "sub": "彼此守望",
+ "text": "不只願他們聆聽,更願他們身邊有人幫助他們讀神的話、禱告、委身群體,並在苦難中正確回應——抵擋那在全世界追逼信徒的仇敵。",
+ "refs": [
+ "1 Peter 5:6–11",
+ "Hebrews 10:24–25"
+ ]
+ },
+ {
+ "n": 12,
+ "rhythm": "praise_end",
+ "sub": "大 · 更大 · 最大",
+ "text": "先行讚美——為著得救的人;更大的,為著不斷繁增的屬靈家庭;最大的,為著獻上的生命,以及被興起的本地植堂者,無論代價如何,都在整個族群中繁增健康的教會。",
+ "refs": [
+ "Acts 2:42–47",
+ "Matthew 28:19–20"
+ ]
+ }
+ ]
}
}
\ No newline at end of file
diff --git a/library/scripts/posters/packet.mjs b/library/scripts/posters/packet.mjs
index 29a53e0e0..51e602a1b 100644
--- a/library/scripts/posters/packet.mjs
+++ b/library/scripts/posters/packet.mjs
@@ -74,7 +74,30 @@ const fill = (s, vars) => String(s).replace(/\{(\w+)\}/g, (_, k) => (vars[k] ??
// the SAME packet from the SAME code. Changing a page here changes it in both.
const { createPacketRenderer } = await import(
join(__dirname, '..', '..', 'packet', 'packet-render.js'));
-const R = createPacketRenderer({ strings: STRINGS, lang: LANG });
+// SCRIPT FONT, inlined. Some languages need a face the system stack does not
+// carry — Hindi renders as tofu boxes in Georgia/Palatino/serif. The packet is a
+// standalone file meant to print offline, so the font is base64'd INTO it rather
+// than linked, exactly as the photos are. Absent, the renderer omits the rule and
+// falls back to serif rather than failing.
+const SCRIPT_FONT_FILE = {
+ hi: 'noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2',
+ mr: 'noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2',
+ ne: 'noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2',
+};
+let fontData = null;
+const fontRel = SCRIPT_FONT_FILE[LANG];
+if (fontRel) {
+ const fp = join(__dirname, '..', '..', '..', 'public', 'assets', 'fonts', fontRel);
+ if (existsSync(fp)) {
+ fontData = readFileSync(fp).toString('base64');
+ } else {
+ // Loud, not silent: a missing font here means the packet prints as empty
+ // rectangles, which looks like a content bug rather than a missing asset.
+ console.warn(`[packet] ⚠ ${LANG} needs ${fontRel} and it is MISSING — `
+ + 'the packet will render as tofu boxes. Vendor the font first.');
+ }
+}
+const R = createPacketRenderer({ strings: STRINGS, lang: LANG, fontData });
const { mapPage, listPages, pointsPage, wheelPage, css, mapCss, PER_PAGE, W, H } = R;
// ── DATA ────────────────────────────────────────────────────────────────────
diff --git a/library/utils/countries-meta.js b/library/utils/countries-meta.js
new file mode 100644
index 000000000..3517fb89a
--- /dev/null
+++ b/library/utils/countries-meta.js
@@ -0,0 +1,283 @@
+// countries-meta.js — a PORT of DX-life's config/countries-meta.ts (326 lines), the rollup
+// source of truth behind /regions, /regions/ and /regions/.
+//
+// WHY IT IS HERE AND NOT IN THE PLUGIN FOLDER: two named consumers already had their own
+// copy of this grouping logic — plugin-designs/regions-dashboard/regions-list.vue (tab 1)
+// and .../region-dashboards.vue (tab 2). Two copies of a rollup is two chances for the
+// region page and the dashboard to disagree about the same number, which is exactly the
+// failure this file's own header warns about on their side ("the region totals, the
+// per-country rows and the map's in-region pins all agree").
+//
+// WHAT CHANGED IN THE PORT, and nothing else:
+// * TypeScript -> JavaScript. The interfaces become JSDoc; no logic was rewritten.
+// * `import { COUNTRIES } from '../app/utils/countries'` becomes library/data/countries.json,
+// which is their same 242-entry {value,label} array in their order.
+// Everything else — slugify's replace chain, FULL_PRAYER_COVERAGE_COUNT, addToStats's
+// conditions, fitView's WORLD_DIM/VIEW_W/VIEW_H/MAX_ZOOM/MIN_ZOOM/NEIGHBOUR_PADDING,
+// lngExtent's largest-gap walk, WORLD_VIEW, and both sort orders — is theirs verbatim.
+//
+// THE TWO THINGS THE OLD COPIES GOT WRONG, both fixed by porting rather than patching:
+// 1. They slugged a country from `g.name`, the LOCALIZED label off the API. Theirs slugs
+// from the canonical ENGLISH name (ENGLISH_NAME_BY_CODE), deliberately: their comment
+// says "a French page is still /fr/regions/india, not /fr/regions/inde". Since our
+// dashboard writes the slug into the URL hash, the old behaviour made a deep link
+// locale-dependent and so different from theirs.
+// 2. They dropped center/zoom/bounds entirely ("minus the map bounds/zoom"), so our maps
+// derived their own frame. Theirs computes a fit view per country AND per region.
+
+import COUNTRIES from '../data/countries.json'
+
+/** Fields requested from the prayer API's people-groups list for these summaries. */
+export const PEOPLE_GROUP_LIST_FIELDS = 'country_code,wagf_region,latitude,longitude,population,adopted_by_churches,people_committed,engagement_status'
+
+/** A people group counts as fully covered once this many people commit to daily prayer. */
+export const FULL_PRAYER_COVERAGE_COUNT = 100
+
+/**
+ * @typedef {Object} PeopleGroupLite
+ * @property {{value:string,label:string}|null} [country_code]
+ * @property {{value:string,label:string}|null} [wagf_region]
+ * @property {string|number|null} [latitude]
+ * @property {string|number|null} [longitude]
+ * @property {string|number|null} [population]
+ * @property {number|null} [adopted_by_churches]
+ * @property {string|number|null} [people_committed]
+ * @property {{value:string,label:string}|null} [engagement_status]
+ */
+
+/**
+ * Map bounds [[west, south], [east, north]] framing a set of pins. East may exceed 180 when
+ * the box crosses the antimeridian (e.g. Oceania), which Mapbox accepts as a continuous
+ * longitude range.
+ * @typedef {[[number,number],[number,number]]} MapBounds
+ */
+
+/**
+ * Progress figures shared by country and region summaries: count (people groups), population,
+ * adopted (by >=1 church), engaged (engagement_status 'engaged'), withPrayer (>=1 committed),
+ * withFullPrayer (>=FULL_PRAYER_COVERAGE_COUNT), peopleCommitted (the total).
+ * @typedef {{count:number,population:number,adopted:number,engaged:number,withPrayer:number,withFullPrayer:number,peopleCommitted:number}} ProgressStats
+ */
+
+// ISO-3 -> canonical English name, the basis for slugs. Sourced from the same country list
+// the rest of the app uses so slugs stay consistent and locale-independent (a French page is
+// still /fr/regions/india, not /fr/regions/inde).
+const ENGLISH_NAME_BY_CODE = new Map(COUNTRIES.map(c => [c.value, c.label]))
+
+export function slugify(name) {
+ return String(name || '')
+ .normalize('NFD').replace(/[̀-ͯ]/g, '') // strip diacritics
+ .toLowerCase()
+ .replace(/['’.]/g, '') // drop apostrophes / periods (Côte d'Ivoire -> cote-divoire)
+ .replace(/&/g, ' and ')
+ .replace(/[^a-z0-9]+/g, '-') // any run of non-alphanumerics -> single hyphen
+ .replace(/^-+|-+$/g, '') // trim leading/trailing hyphens
+}
+
+// Slug for a country code. Prefers the canonical English name; falls back to a provided
+// label (and finally the raw code) for any code not in the master list.
+export function countrySlug(code, fallbackLabel) {
+ return slugify(ENGLISH_NAME_BY_CODE.get(code) || fallbackLabel || code)
+}
+
+// Slug for a WAGF region. Derived from the stable value rather than the localized label so
+// every locale shares one URL (e.g. "middle_east" -> "middle-east"). No region -> "other".
+export function regionSlug(value) {
+ return slugify(value) || 'other'
+}
+
+// EXPORTED BEYOND THEIR SURFACE — the only two, and why. Theirs keeps toNumber and
+// emptyStats module-private because their pages never need a cross-region total: DX-life has
+// no "all regions" view. Ours does, because the no-selection chooser is one of the two
+// sanctioned additions, and it needs a zero accumulator (emptyStats) to roll every region up
+// and a coordinate parser (toNumber) for the map's points array. Nothing else is added, and
+// no logic differs.
+export function toNumber(value) {
+ const n = typeof value === 'string' ? parseFloat(value) : (value ?? 0)
+ return Number.isNaN(n) ? 0 : n
+}
+
+export function emptyStats() {
+ return { count: 0, population: 0, adopted: 0, engaged: 0, withPrayer: 0, withFullPrayer: 0, peopleCommitted: 0 }
+}
+
+function addToStats(stats, p) {
+ const committed = toNumber(p.people_committed)
+ stats.count++
+ stats.population += toNumber(p.population)
+ if ((p.adopted_by_churches ?? 0) > 0) stats.adopted++
+ if (p.engagement_status?.value === 'engaged') stats.engaged++
+ if (committed > 0) stats.withPrayer++
+ if (committed >= FULL_PRAYER_COVERAGE_COUNT) stats.withFullPrayer++
+ stats.peopleCommitted += committed
+}
+
+function addPin(bounds, p) {
+ const lat = typeof p.latitude === 'string' ? parseFloat(p.latitude) : p.latitude
+ const lng = typeof p.longitude === 'string' ? parseFloat(p.longitude) : p.longitude
+ if (lat != null && lng != null && !Number.isNaN(lat) && !Number.isNaN(lng)) {
+ bounds.lats.push(lat)
+ bounds.lngs.push(lng)
+ }
+}
+
+function round(n) {
+ return Math.round(n * 100) / 100
+}
+
+// Web-Mercator latitude in radians, used by the fit-bounds zoom math.
+function latRad(lat) {
+ const s = Math.sin((lat * Math.PI) / 180)
+ return Math.log((1 + s) / (1 - s)) / 2
+}
+
+// Compute a map center + zoom that frames the given bounding box. Zooms out slightly beyond
+// a tight fit so surrounding countries remain in view (the whole point of the country page's
+// map). The viewport dimensions are an approximation of the map slot; exact framing isn't
+// important because the map keeps its own pan/zoom controls.
+function fitView(minLng, minLat, maxLng, maxLat) {
+ const WORLD_DIM = 512
+ const VIEW_W = 1000
+ const VIEW_H = 560
+ const MAX_ZOOM = 6
+ const MIN_ZOOM = 1.6
+ const NEIGHBOUR_PADDING = 0.6 // zoom out this much so neighbours stay visible
+
+ const latFraction = Math.max((latRad(maxLat) - latRad(minLat)) / Math.PI, 1e-6)
+ let lngDiff = maxLng - minLng
+ if (lngDiff < 0) lngDiff += 360
+ const lngFraction = Math.max(lngDiff / 360, 1e-6)
+
+ const latZoom = Math.log2(VIEW_H / WORLD_DIM / latFraction)
+ const lngZoom = Math.log2(VIEW_W / WORLD_DIM / lngFraction)
+
+ let zoom = Math.min(latZoom, lngZoom, MAX_ZOOM) - NEIGHBOUR_PADDING
+ zoom = Math.max(MIN_ZOOM, Math.min(zoom, MAX_ZOOM))
+
+ return {
+ center: [round((minLng + maxLng) / 2), round((minLat + maxLat) / 2)],
+ zoom: round(zoom),
+ }
+}
+
+// Longitude extent covering all pins along the shortest arc: sorts the longitudes, finds the
+// largest gap between neighbours (including the wrap-around gap past 180), and spans
+// everything else. When the extent crosses the antimeridian the east edge exceeds 180 so the
+// range stays continuous.
+function lngExtent(lngs) {
+ const sorted = [...lngs].sort((a, b) => a - b)
+ const last = sorted.length - 1
+ let gapStart = last
+ let maxGap = sorted[0] + 360 - sorted[last]
+ for (let i = 0; i < last; i++) {
+ const gap = sorted[i + 1] - sorted[i]
+ if (gap > maxGap) {
+ maxGap = gap
+ gapStart = i
+ }
+ }
+ if (gapStart === last) return [sorted[0], sorted[last]]
+ return [sorted[gapStart + 1], sorted[gapStart] + 360]
+}
+
+// Default whole-world view for a set of records with no coordinates.
+const WORLD_VIEW = { center: [20, 10], zoom: 1.8, bounds: null }
+
+function viewFor(pins) {
+ if (pins.lats.length === 0) return WORLD_VIEW
+ const [west, east] = lngExtent(pins.lngs)
+ const south = Math.min(...pins.lats)
+ const north = Math.max(...pins.lats)
+ return { ...fitView(west, south, east, north), bounds: [[west, south], [east, north]] }
+}
+
+/**
+ * Group people-group records into one summary per country, sorted by name.
+ * @param {PeopleGroupLite[]} posts
+ */
+export function summarizeCountries(posts) {
+ const groups = new Map()
+
+ for (const p of posts) {
+ const code = p.country_code?.value
+ if (!code) continue
+
+ let g = groups.get(code)
+ if (!g) {
+ g = { name: p.country_code.label, region: { value: '', label: '' }, pins: { lats: [], lngs: [] }, stats: emptyStats() }
+ groups.set(code, g)
+ }
+ // A few records carry no region; the country takes the first region seen.
+ if (!g.region.value && p.wagf_region?.value) g.region = p.wagf_region
+ addToStats(g.stats, p)
+ addPin(g.pins, p)
+ }
+
+ const out = []
+ for (const [code, g] of groups) {
+ const view = viewFor(g.pins)
+ out.push({
+ slug: countrySlug(code, g.name),
+ code,
+ name: g.name,
+ region: g.region,
+ center: view.center,
+ zoom: view.zoom,
+ bounds: view.bounds,
+ ...g.stats,
+ })
+ }
+
+ out.sort((a, b) => a.name.localeCompare(b.name))
+ return out
+}
+
+/**
+ * Group people-group records into one summary per WAGF region, sorted by label. Each people
+ * group rolls up under its country's region, so the region totals, the per-country rows and
+ * the map's in-region pins all agree.
+ * @param {PeopleGroupLite[]} posts
+ */
+export function summarizeRegions(posts) {
+ const countries = summarizeCountries(posts)
+ const regionSlugByCountry = new Map(countries.map(c => [c.code, regionSlug(c.region.value)]))
+
+ const groups = new Map()
+
+ for (const c of countries) {
+ const slug = regionSlugByCountry.get(c.code)
+ let g = groups.get(slug)
+ if (!g) {
+ g = { value: c.region.value, label: c.region.label, countries: [], pins: { lats: [], lngs: [] }, stats: emptyStats() }
+ groups.set(slug, g)
+ }
+ g.countries.push(c)
+ }
+
+ for (const p of posts) {
+ const code = p.country_code?.value
+ if (!code) continue
+ const g = groups.get(regionSlugByCountry.get(code))
+ addToStats(g.stats, p)
+ addPin(g.pins, p)
+ }
+
+ const out = []
+ for (const [slug, g] of groups) {
+ const view = viewFor(g.pins)
+ out.push({
+ slug,
+ value: g.value,
+ label: g.label,
+ countryCount: g.countries.length,
+ center: view.center,
+ zoom: view.zoom,
+ bounds: view.bounds,
+ countries: g.countries,
+ ...g.stats,
+ })
+ }
+
+ out.sort((a, b) => a.label.localeCompare(b.label))
+ return out
+}
diff --git a/package.json b/package.json
index 492bf8414..f97bad1cf 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"build:unlocked": "bun run gates:pre && bun run library-index && bun run check:library-index && bun run clean && bun run token && bun run build:bundles && bun run tree && bun run gates:post && bun run gallery",
"check:card-deck": "node internal/check-card-deck.mjs",
"check:poster-raster": "node internal/check-poster-raster.mjs",
+ "check:print-geometry": "node internal/check-print-geometry.mjs",
"check:packet-browser": "node internal/check-packet-browser.mjs",
"check:packet-render": "node internal/check-packet-render.mjs",
"check:template-literal": "node internal/template-literal-check.mjs",
diff --git a/plugin-designs/my-upg-100-list/poster-wall.vue b/plugin-designs/my-upg-100-list/poster-wall.vue
index b5378eb50..097b60b8e 100644
--- a/plugin-designs/my-upg-100-list/poster-wall.vue
+++ b/plugin-designs/my-upg-100-list/poster-wall.vue
@@ -271,9 +271,16 @@ const exportState = ref(null) // { label, phase, message, pct, kind }
// is what commercial card printers, sleeves and boxes are built around.
// @page takes these as explicit inches — a NAMED size silently falls back to letter
// in Chromium, which is the exact bug this deck is being fixed for.
+// Both orientations, because the Driver asked for "horizontal cards" as well as
+// vertical ones. A landscape stock is not merely transposed: the renderer moves
+// the photo BESIDE the text, since a 46%-height band on a 2.5in-tall card is too
+// short to carry a face. The labels come from the renderer's own STOCK table so
+// this list cannot drift from what the renderer actually supports.
const DECK_STOCKS = [
{ id: 'poker', label: 'Card 2.5 × 3.5 in' },
+ { id: 'pokerLandscape', label: 'Card 3.5 × 2.5 in (landscape)' },
{ id: 'postcard', label: 'Postcard 4 × 6 in' },
+ { id: 'postcardLandscape', label: 'Postcard 6 × 4 in (landscape)' },
{ id: 'a6', label: 'A6 105 × 148 mm' },
]
const deckStock = ref('poker')
diff --git a/plugin-designs/regions-dashboard/region-dashboards.vue b/plugin-designs/regions-dashboard/region-dashboards.vue
index 1d37f4efc..9094df262 100644
--- a/plugin-designs/regions-dashboard/region-dashboards.vue
+++ b/plugin-designs/regions-dashboard/region-dashboards.vue
@@ -40,6 +40,7 @@ import { splitLastWord } from './src/useLastWordHighlight.js'
// order its CVD separation was validated in — the legend must render in it for that to hold.
import { PALETTE as REGION_PALETTE, WAGF_REGIONS, REGION_ORDER } from '@map/colors/wagf-region.js'
import RegionCountryMap from './src/RegionCountryMap.vue'
+import { summarizeRegions, slugify, emptyStats, toNumber, PEOPLE_GROUP_LIST_FIELDS } from '@map/utils/countries-meta.js'
const props = defineProps({
profileConfig: { type: String, default: '' },
@@ -118,85 +119,9 @@ const sel = ref({
// A SUPERSET of tab 1's list: name + slug are added for the map popups and the country
// people-group list. Every ROLLUP field is unchanged, so the two tabs still roll up
// identically -- the extra fields are read by the map, never by addToStats().
-const FIELDS = 'name,slug,country_code,wagf_region,latitude,longitude,population,adopted_by_churches,people_committed,engagement_status'
-
-function toNumber(v) {
- const n = typeof v === 'string' ? parseFloat(v) : (v ?? 0)
- return Number.isNaN(n) ? 0 : n
-}
-
-function slugify(name) {
- return String(name || '')
- .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
- .toLowerCase()
- .replace(/['\u2019.]/g, '')
- .replace(/&/g, ' and ')
- .replace(/[^a-z0-9]+/g, '-')
- .replace(/^-+|-+$/g, '')
-}
-
-const FULL_PRAYER_COVERAGE_COUNT = 100
-
-function emptyStats() {
- return { count: 0, population: 0, adopted: 0, engaged: 0, withPrayer: 0, withFullPrayer: 0, peopleCommitted: 0 }
-}
-
-function addToStats(stats, p) {
- const committed = toNumber(p.people_committed)
- stats.count++
- stats.population += toNumber(p.population)
- if ((p.adopted_by_churches ?? 0) > 0) stats.adopted++
- if (p.engagement_status?.value === 'engaged') stats.engaged++
- if (committed > 0) stats.withPrayer++
- if (committed >= FULL_PRAYER_COVERAGE_COUNT) stats.withFullPrayer++
- stats.peopleCommitted += committed
-}
-
-function summarizeRegions(posts) {
- const countryGroups = new Map()
- for (const p of posts) {
- const code = p.country_code?.value
- if (!code) continue
- let g = countryGroups.get(code)
- if (!g) {
- g = { name: p.country_code.label, region: { value: '', label: '' }, stats: emptyStats() }
- countryGroups.set(code, g)
- }
- if (!g.region.value && p.wagf_region?.value) g.region = p.wagf_region
- addToStats(g.stats, p)
- }
-
- const countries = []
- for (const [code, g] of countryGroups) {
- countries.push({ slug: slugify(g.name) || slugify(code) || code.toLowerCase(), code, name: g.name, region: g.region, ...g.stats })
- }
- countries.sort((a, b) => a.name.localeCompare(b.name))
-
- const regionSlugByCountry = new Map(countries.map((c) => [c.code, slugify(c.region.value) || 'other']))
- const regionGroups = new Map()
- for (const c of countries) {
- const slug = regionSlugByCountry.get(c.code)
- let g = regionGroups.get(slug)
- if (!g) {
- g = { value: c.region.value, label: c.region.label, countries: [], stats: emptyStats() }
- regionGroups.set(slug, g)
- }
- g.countries.push(c)
- }
- for (const p of posts) {
- const code = p.country_code?.value
- if (!code) continue
- const g = regionGroups.get(regionSlugByCountry.get(code))
- if (g) addToStats(g.stats, p)
- }
-
- const out = []
- for (const [slug, g] of regionGroups) {
- out.push({ slug, value: g.value, label: g.label, countryCount: g.countries.length, countries: g.countries, ...g.stats })
- }
- out.sort((a, b) => (a.label || 'Other').localeCompare(b.label || 'Other'))
- return out
-}
+// Their PEOPLE_GROUP_LIST_FIELDS plus the two this tab's map needs (name, slug) — derived
+// from the shared list so the two can never drift apart.
+const FIELDS = 'name,slug,' + PEOPLE_GROUP_LIST_FIELDS
function formatNumber(n) {
// THEIRS, RegionDetail.vue:16: `n.toLocaleString(`${locale.value}-u-nu-latn`)` — locale
diff --git a/plugin-designs/regions-dashboard/regions-list.vue b/plugin-designs/regions-dashboard/regions-list.vue
index 726213d4c..c05b54179 100644
--- a/plugin-designs/regions-dashboard/regions-list.vue
+++ b/plugin-designs/regions-dashboard/regions-list.vue
@@ -39,6 +39,7 @@ import { useRegionsI18n } from './src/useRegionsI18n.js'
import { RTL_LOCALES } from '@map/i18n/index.js'
import { useI18n } from 'vue-i18n'
import { useDocumentFont, BEBAS_NEUE_FACES, POPPINS_FACES } from '@map/composables/useDocumentFont.js'
+import { summarizeRegions, slugify, emptyStats, toNumber, PEOPLE_GROUP_LIST_FIELDS } from '@map/utils/countries-meta.js'
const props = defineProps({
profileConfig: { type: String, default: '' },
@@ -108,91 +109,15 @@ const loading = ref(true)
const loadFailed = ref(false)
// ── same field list the source page's PEOPLE_GROUP_LIST_FIELDS asks for ─────
-const FIELDS = 'country_code,wagf_region,latitude,longitude,population,adopted_by_churches,people_committed,engagement_status'
-
-function toNumber(v) {
- const n = typeof v === 'string' ? parseFloat(v) : (v ?? 0)
- return Number.isNaN(n) ? 0 : n
-}
-
-function slugify(name) {
- return String(name || '')
- .normalize('NFD').replace(/[\u0300-\u036f]/g, '') // strip combining diacritics
- .toLowerCase()
- .replace(/['\u2019.]/g, '') // drop apostrophes / periods
- .replace(/&/g, ' and ')
- .replace(/[^a-z0-9]+/g, '-')
- .replace(/^-+|-+$/g, '')
-}
-
-const FULL_PRAYER_COVERAGE_COUNT = 100
-
-function emptyStats() {
- return { count: 0, population: 0, adopted: 0, engaged: 0, withPrayer: 0, withFullPrayer: 0, peopleCommitted: 0 }
-}
-
-function addToStats(stats, p) {
- const committed = toNumber(p.people_committed)
- stats.count++
- stats.population += toNumber(p.population)
- if ((p.adopted_by_churches ?? 0) > 0) stats.adopted++
- if (p.engagement_status?.value === 'engaged') stats.engaged++
- if (committed > 0) stats.withPrayer++
- if (committed >= FULL_PRAYER_COVERAGE_COUNT) stats.withFullPrayer++
- stats.peopleCommitted += committed
-}
-
-// Group people-group records into one summary per country, then roll countries up into one
-// summary per WAGF region — same two-pass shape as countries-meta.ts's summarizeCountries() +
-// summarizeRegions(), minus the map bounds/zoom (this element has no map).
-function summarizeRegions(posts) {
- const countryGroups = new Map()
- for (const p of posts) {
- const code = p.country_code?.value
- if (!code) continue
- let g = countryGroups.get(code)
- if (!g) {
- g = { name: p.country_code.label, region: { value: '', label: '' }, stats: emptyStats() }
- countryGroups.set(code, g)
- }
- if (!g.region.value && p.wagf_region?.value) g.region = p.wagf_region
- addToStats(g.stats, p)
- }
-
- const countries = []
- for (const [code, g] of countryGroups) {
- countries.push({ slug: slugify(g.name) || slugify(code) || code.toLowerCase(), code, name: g.name, region: g.region, ...g.stats })
- }
- countries.sort((a, b) => a.name.localeCompare(b.name))
-
- const regionSlugByCountry = new Map(countries.map((c) => [c.code, slugify(c.region.value) || 'other']))
- const regionGroups = new Map()
- for (const c of countries) {
- const slug = regionSlugByCountry.get(c.code)
- let g = regionGroups.get(slug)
- if (!g) {
- g = { value: c.region.value, label: c.region.label, countries: [], stats: emptyStats() }
- regionGroups.set(slug, g)
- }
- g.countries.push(c)
- }
- for (const p of posts) {
- const code = p.country_code?.value
- if (!code) continue
- const g = regionGroups.get(regionSlugByCountry.get(code))
- if (g) addToStats(g.stats, p)
- }
-
- const out = []
- for (const [slug, g] of regionGroups) {
- out.push({ slug, value: g.value, label: g.label, countryCount: g.countries.length, countries: g.countries, ...g.stats })
- }
- out.sort((a, b) => (a.label || 'Other').localeCompare(b.label || 'Other'))
- return out
-}
+// Their PEOPLE_GROUP_LIST_FIELDS, imported rather than restated — one list, one source.
+const FIELDS = PEOPLE_GROUP_LIST_FIELDS
function formatNumber(n) {
- return Number(n || 0).toLocaleString('en-US')
+ // Theirs, RegionDetail.vue:16: n.toLocaleString(`${locale}-u-nu-latn`) — locale grouping
+ // separators with Latin digits. Hardcoding en-US made de/fr/es wrong (they group with '.'
+ // or a thin space). Same fix already applied in region-dashboards.vue.
+ const v = Number(n || 0)
+ try { return v.toLocaleString(`${i18nLocale.value || 'en'}-u-nu-latn`) } catch (_) { return v.toLocaleString('en-US') }
}
function summaryLine(region) {
diff --git a/public/assets/fonts/noto-serif-devanagari/LICENSE b/public/assets/fonts/noto-serif-devanagari/LICENSE
new file mode 100644
index 000000000..cd2cc5c94
--- /dev/null
+++ b/public/assets/fonts/noto-serif-devanagari/LICENSE
@@ -0,0 +1,93 @@
+Copyright 2022 The Noto Project Authors (https://github.com/notofonts/devanagari)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/assets/fonts/noto-serif-devanagari/README.md b/public/assets/fonts/noto-serif-devanagari/README.md
new file mode 100644
index 000000000..68253f5db
--- /dev/null
+++ b/public/assets/fonts/noto-serif-devanagari/README.md
@@ -0,0 +1,24 @@
+# Noto Serif Devanagari — the Devanagari subset only
+
+**Why vendored:** the prayer packet's font stack is Georgia / Palatino Linotype /
+serif, none of which carry Devanagari. Measured by rendering Devanagari at 48px
+and READING THE PIXELS, Hindi came out as **tofu boxes** — this machine has zero
+Devanagari fonts (`fc-list`). A `hi` translation without this file would ship a
+page of empty rectangles.
+
+**Licence:** SIL Open Font License 1.1 (see `LICENSE`). Free to bundle and serve.
+
+**Subset:** `U+0900-097F` plus the Devanagari extensions Google's own subsetting
+selects — the *devanagari* range ONLY, matching how `poppins/` ships `latin` and
+`latin-ext` separately so a page downloads only what it needs. 124 KB, against
+6-8 KB for a Latin subset; that is the cost of the script's glyph count, and it
+loads only on a Hindi packet.
+
+**Source:** the woff2 Google Fonts serves for
+`Noto+Serif+Devanagari:wght@400;700` with a modern browser UA. Note the 400 and
+700 faces resolve to the SAME file — it is a variable font, so one file covers
+both weights and vendoring two would duplicate 124 KB for nothing.
+
+To refresh: request that CSS with a Chrome user-agent (without one Google serves
+unsubsetted TTF), take the `@font-face` block whose `unicode-range` contains
+`U+0900`, and download its woff2.
diff --git a/public/assets/fonts/noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2 b/public/assets/fonts/noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2
new file mode 100644
index 000000000..713584ed3
Binary files /dev/null and b/public/assets/fonts/noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2 differ
diff --git a/public/doxa-maps/assets/fonts/noto-serif-devanagari/LICENSE b/public/doxa-maps/assets/fonts/noto-serif-devanagari/LICENSE
new file mode 100644
index 000000000..cd2cc5c94
--- /dev/null
+++ b/public/doxa-maps/assets/fonts/noto-serif-devanagari/LICENSE
@@ -0,0 +1,93 @@
+Copyright 2022 The Noto Project Authors (https://github.com/notofonts/devanagari)
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/public/doxa-maps/assets/fonts/noto-serif-devanagari/README.md b/public/doxa-maps/assets/fonts/noto-serif-devanagari/README.md
new file mode 100644
index 000000000..68253f5db
--- /dev/null
+++ b/public/doxa-maps/assets/fonts/noto-serif-devanagari/README.md
@@ -0,0 +1,24 @@
+# Noto Serif Devanagari — the Devanagari subset only
+
+**Why vendored:** the prayer packet's font stack is Georgia / Palatino Linotype /
+serif, none of which carry Devanagari. Measured by rendering Devanagari at 48px
+and READING THE PIXELS, Hindi came out as **tofu boxes** — this machine has zero
+Devanagari fonts (`fc-list`). A `hi` translation without this file would ship a
+page of empty rectangles.
+
+**Licence:** SIL Open Font License 1.1 (see `LICENSE`). Free to bundle and serve.
+
+**Subset:** `U+0900-097F` plus the Devanagari extensions Google's own subsetting
+selects — the *devanagari* range ONLY, matching how `poppins/` ships `latin` and
+`latin-ext` separately so a page downloads only what it needs. 124 KB, against
+6-8 KB for a Latin subset; that is the cost of the script's glyph count, and it
+loads only on a Hindi packet.
+
+**Source:** the woff2 Google Fonts serves for
+`Noto+Serif+Devanagari:wght@400;700` with a modern browser UA. Note the 400 and
+700 faces resolve to the SAME file — it is a variable font, so one file covers
+both weights and vendoring two would duplicate 124 KB for nothing.
+
+To refresh: request that CSS with a Chrome user-agent (without one Google serves
+unsubsetted TTF), take the `@font-face` block whose `unicode-range` contains
+`U+0900`, and download its woff2.
diff --git a/public/doxa-maps/assets/fonts/noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2 b/public/doxa-maps/assets/fonts/noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2
new file mode 100644
index 000000000..713584ed3
Binary files /dev/null and b/public/doxa-maps/assets/fonts/noto-serif-devanagari/noto-serif-devanagari-400-devanagari.woff2 differ
diff --git a/public/doxa-maps/index.html b/public/doxa-maps/index.html
index e59d54e83..4c33792f4 100644
--- a/public/doxa-maps/index.html
+++ b/public/doxa-maps/index.html
@@ -130,7 +130,7 @@ header.top .sub { margin-top: 3px; font: 12px/1.4 var(--sans); color: var(--mut)
+ window.FORGE.manifest = {"schema":"doxa-maps-manifest@1","generatedFrom":"plugin-designs/*/*.vue","embedBase":".","bundleCount":21,"profileCount":32,"bundles":[{"bundle":"all-people-groups","scriptUrl":"./all-people-groups/all-people-groups.js","output":"doxa-maps/all-people-groups/all-people-groups.js","integrity":"sha384-cqk7oshuPW5FdnbZ/DyfBBXN2FObAI+V1jrbxmZR2xzNiBtWG65PETQ5/w5PCDB2","elements":["all-people-groups"],"profiles":[{"profile":"all-people-groups","file":"all-people-groups.vue","description":"All 12,379 people groups (peoplegroups.org) in four colors — reached,","element":"all-people-groups","type":"single","isMap":true,"iframeUrl":"./all-people-groups/all-people-groups/index.html","scriptUrl":"./all-people-groups/all-people-groups.js","embed":{"iframe":"\u003ciframe src=\"./all-people-groups/all-people-groups/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./all-people-groups/all-people-groups.js\" integrity=\"sha384-cqk7oshuPW5FdnbZ/DyfBBXN2FObAI+V1jrbxmZR2xzNiBtWG65PETQ5/w5PCDB2\" crossorigin=\"anonymous\">\u003c/script>\n\u003call-people-groups profile-config='{\"profile\":\"all-people-groups\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/all-people-groups>"},"bundle":"all-people-groups"}]},{"bundle":"big-picture","scriptUrl":"./big-picture/big-picture.js","output":"doxa-maps/big-picture/big-picture.js","integrity":"sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h","elements":["bp-big-picture","bp-research-header","people-groups-grid"],"composes":["bp-research-header","people-groups-grid"],"profiles":[{"profile":"big-picture","file":"big-picture.vue","description":"The Cat-4 composite that PAIRS the two real capability elements of the","element":"bp-big-picture","type":"nested-elements-mfe","isMap":false,"iframeUrl":"./big-picture/big-picture/index.html","scriptUrl":"./big-picture/big-picture.js","embed":{"iframe":"\u003ciframe src=\"./big-picture/big-picture/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./big-picture/big-picture.js\" integrity=\"sha384-v0b+aZ7TBxmgUIBBLdKEm6HzwjY3jAGyOFNsKeKCKY4EIRpiWRTcNLavAitHRt8h\" crossorigin=\"anonymous\">\u003c/script>\n\u003cbp-big-picture profile-config='{\"profile\":\"big-picture\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/bp-big-picture>"},"instances":[{"id":"big-picture","title":"Big Picture — research header ↔ people-groups grid (composite)","apiBaseUrl":"","props":{"profile":"big-picture","provider":"maplibre","instanceId":"bp-big-picture"},"element":"bp-big-picture","iframeUrl":"./big-picture/big-picture/big-picture/index.html","embed":{"iframe":"\u003ciframe src=\"./big-picture/big-picture/big-picture/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"frontier","title":"Big Picture — frontier accent","apiBaseUrl":"","props":{"profile":"big-picture","provider":"maplibre","instanceId":"bp-big-picture-frontier","accent":"#22d3ee","title":"Big Picture — Frontier Focus"},"element":"bp-big-picture","iframeUrl":"./big-picture/big-picture/frontier/index.html","embed":{"iframe":"\u003ciframe src=\"./big-picture/big-picture/frontier/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}],"bundle":"big-picture"}]},{"bundle":"cities-internet","scriptUrl":"./cities-internet/cities-internet.js","output":"doxa-maps/cities-internet/cities-internet.js","integrity":"sha384-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R","elements":["cities-internet-map"],"profiles":[{"profile":"connectivity-list","file":"connectivity-list.vue","description":"Countries where DX people groups live, grouped by region, showing measured internet penetration","element":"cities-internet-map","type":"single","isMap":false,"iframeUrl":"./cities-internet/connectivity-list/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/cities-internet-map>"}},{"profile":"connectivity-map","file":"connectivity-map.vue","description":"Every country shaded by measured internet penetration, with its named carriers and dominant","element":"cities-internet-map","type":"single","isMap":true,"iframeUrl":"./cities-internet/connectivity-map/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/connectivity-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"connectivity-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/cities-internet-map>"}},{"profile":"data-tables","file":"data-tables.vue","description":"Four validated research tables, one tab each, with sortable and filterable columns, CSV and JSON","element":"cities-internet-map","type":"single","isMap":false,"iframeUrl":"./cities-internet/data-tables/index.html","scriptUrl":"./cities-internet/cities-internet.js","embed":{"iframe":"\u003ciframe src=\"./cities-internet/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./cities-internet/cities-internet.js\" integrity=\"sha384-tNig8gXdCocWVKD5iXR6iRqiAr7msWFtcFKzYif0L0G5i/RM+HI+SO8oR5c6O+5R\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccities-internet-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/cities-internet-map>"}}]},{"bundle":"country-posters","scriptUrl":"./country-posters/country-posters.js","output":"doxa-maps/country-posters/country-posters.js","integrity":"sha384-kbTr53oyqrMpgr620HR4W8vLXxT4Ulo5EeCoHebbMjGns5rwNvJP2BLuPyo5XVJj","elements":["country-posters-map"],"profiles":[{"profile":"countries-map","file":"countries-map.vue","description":"The researcher's workbench retargeted from people groups to COUNTRIES — region","element":"country-posters-map","type":"single","isMap":true,"iframeUrl":"./country-posters/countries-map/index.html","scriptUrl":"./country-posters/country-posters.js","embed":{"iframe":"\u003ciframe src=\"./country-posters/countries-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-kbTr53oyqrMpgr620HR4W8vLXxT4Ulo5EeCoHebbMjGns5rwNvJP2BLuPyo5XVJj\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"countries-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/country-posters-map>"}},{"profile":"country-poster-wall","file":"country-poster-wall.vue","description":"One printable card per country, grouped into COLLAPSIBLE regional sections so the","element":"country-posters-map","type":"single","isMap":false,"iframeUrl":"./country-posters/country-poster-wall/index.html","scriptUrl":"./country-posters/country-posters.js","embed":{"iframe":"\u003ciframe src=\"./country-posters/country-poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./country-posters/country-posters.js\" integrity=\"sha384-kbTr53oyqrMpgr620HR4W8vLXxT4Ulo5EeCoHebbMjGns5rwNvJP2BLuPyo5XVJj\" crossorigin=\"anonymous\">\u003c/script>\n\u003ccountry-posters-map profile-config='{\"profile\":\"country-poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/country-posters-map>"}}]},{"bundle":"dashboard-plus-grid","scriptUrl":"./dashboard-plus-grid/dashboard-plus-grid.js","output":"doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js","integrity":"sha384-YbJgljynZ4t3/Ro44oHQy9ihl9/5o7j4IKts8MNRfA73Cb5cyD7U52DklXjzpLpG","elements":["dashboard-plus-grid"],"composes":["research-dashboard","people-groups-grid"],"profiles":[{"profile":"dashboard-plus-grid","file":"dashboard-plus-grid.vue","description":"Both children are IMPORTED DIRECTLY (compiled into this ONE bundle — no runtime bundle","element":"dashboard-plus-grid","type":"nested-elements-mfe","isMap":true,"iframeUrl":"./dashboard-plus-grid/dashboard-plus-grid/index.html","scriptUrl":"./dashboard-plus-grid/dashboard-plus-grid.js","embed":{"iframe":"\u003ciframe src=\"./dashboard-plus-grid/dashboard-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./dashboard-plus-grid/dashboard-plus-grid.js\" integrity=\"sha384-YbJgljynZ4t3/Ro44oHQy9ihl9/5o7j4IKts8MNRfA73Cb5cyD7U52DklXjzpLpG\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdashboard-plus-grid profile-config='{\"profile\":\"dashboard-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/dashboard-plus-grid>"},"instances":[{"id":"default","title":"default","apiBaseUrl":"","props":{"profile":"dashboard-plus-grid"},"element":"dashboard-plus-grid","iframeUrl":"./dashboard-plus-grid/dashboard-plus-grid/default/index.html","embed":{"iframe":"\u003ciframe src=\"./dashboard-plus-grid/dashboard-plus-grid/default/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"demo-map","scriptUrl":"./demo-map/demo-map.js","output":"doxa-maps/demo-map/demo-map.js","integrity":"sha384-QiUJUgeFn27tiHKVhAYtJuIWo+1z5xsCUP1Nj4m1H4cFUV3vUA7ADTP58Ky3oVtQ","elements":["demo-map"],"profiles":[{"profile":"demo-map","file":"demo-map.vue","description":"Simple two-tab demo map. Tab 1 colors pins by religion (shared","element":"demo-map","type":"parameterized","isMap":true,"iframeUrl":"./demo-map/demo-map/index.html","scriptUrl":"./demo-map/demo-map.js","embed":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./demo-map/demo-map.js\" integrity=\"sha384-QiUJUgeFn27tiHKVhAYtJuIWo+1z5xsCUP1Nj4m1H4cFUV3vUA7ADTP58Ky3oVtQ\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdemo-map profile-config='{\"profile\":\"demo-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/demo-map>"},"instances":[{"id":"maplibre","title":"Demo map — MapLibre (token-free)","apiBaseUrl":"","props":{"profile":"demo-map","provider":"maplibre","instanceId":"demo-maplibre"},"element":"demo-map","iframeUrl":"./demo-map/demo-map/maplibre/index.html","embed":{"iframe":"\u003ciframe src=\"./demo-map/demo-map/maplibre/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}],"bundle":"demo-map"}]},{"bundle":"doxa-maps-spa","scriptUrl":"./doxa-maps-spa/doxa-maps-spa.js","output":"doxa-maps/doxa-maps-spa/doxa-maps-spa.js","integrity":"sha384-Ce+DKsLgwfej+rgoWeaWFuMMbR3Y4H+8io/lVL+lQfX0O3iGKwDLf4remDkHGCrP","elements":["doxa-maps-spa"],"composes":["bp-big-picture","doxa-research-map","doxa-simple-map","cities-internet-map","gospel-resources-map","my-upg-100-list-map"],"profiles":[{"profile":"doxa-maps-spa","file":"doxa-maps-spa.vue","description":"The DX-maps app itself: one element whose vertical-tab rail nests every other","element":"doxa-maps-spa","type":"nested-mfe-spa","isMap":false,"iframeUrl":"./doxa-maps-spa/doxa-maps-spa/index.html","scriptUrl":"./doxa-maps-spa/doxa-maps-spa.js","embed":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-maps-spa/doxa-maps-spa.js\" integrity=\"sha384-Ce+DKsLgwfej+rgoWeaWFuMMbR3Y4H+8io/lVL+lQfX0O3iGKwDLf4remDkHGCrP\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-maps-spa profile-config='{\"profile\":\"doxa-maps-spa\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-maps-spa>"},"instances":[{"id":"app","title":"DX-Maps SPA — Big Picture + every research map (app-shell)","apiBaseUrl":"","props":{"profile":"doxa-maps-spa","provider":"maplibre","instanceId":"doxa-maps-spa-app","title":"DX-Maps"},"element":"doxa-maps-spa","iframeUrl":"./doxa-maps-spa/doxa-maps-spa/app/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-maps-spa/doxa-maps-spa/app/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"doxa-research-map","scriptUrl":"./doxa-research-map/doxa-research-map.js","output":"doxa-maps/doxa-research-map/doxa-research-map.js","integrity":"sha384-oecoezttPmpRe5GNG/Yko6IkEbtLjCD7PD7qghU1ZigprnfLKhbf0eBEiEbE24iR","elements":["doxa-research-map"],"profiles":[{"profile":"research-map","file":"research-map.vue","description":"The researcher's workbench: every piece a missiologist, demographer or","element":"doxa-research-map","type":"monolithic","isMap":true,"iframeUrl":"./doxa-research-map/research-map/index.html","scriptUrl":"./doxa-research-map/doxa-research-map.js","embed":{"iframe":"\u003ciframe src=\"./doxa-research-map/research-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-research-map/doxa-research-map.js\" integrity=\"sha384-oecoezttPmpRe5GNG/Yko6IkEbtLjCD7PD7qghU1ZigprnfLKhbf0eBEiEbE24iR\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-research-map profile-config='{\"profile\":\"research-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-research-map>"},"instances":[{"id":"maplibre","title":"Research map — MapLibre (token-free, no search bar)","apiBaseUrl":"","props":{"profile":"research-map","provider":"maplibre","instanceId":"research-map-maplibre"},"element":"doxa-research-map","iframeUrl":"./doxa-research-map/research-map/maplibre/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-research-map/research-map/maplibre/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"doxa-simple-map","scriptUrl":"./doxa-simple-map/doxa-simple-map.js","output":"doxa-maps/doxa-simple-map/doxa-simple-map.js","integrity":"sha384-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W","elements":["doxa-map","doxa-simple-map"],"profiles":[{"profile":"doxa-simple-map","file":"doxa-simple-map.vue","description":"The simple world map, shipped as three presets off ONE prop table —","element":"doxa-map","type":"parameterized","isMap":true,"iframeUrl":"./doxa-simple-map/doxa-simple-map/index.html","scriptUrl":"./doxa-simple-map/doxa-simple-map.js","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-simple-map/doxa-simple-map.js\" integrity=\"sha384-hzEui48BqpW8VhRthz3daMgAdw4w4M3gtBre4ce4sXuAmS+39pMRW9reNAvpFb+W\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-map profile-config='{\"profile\":\"doxa-simple-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-map>"},"instances":[{"id":"prayer","title":"Prayer Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-prayer","dataSource":"pray-tools","tabs":[{"id":"prayer","colorStrategy":"prayer","legend":"prayer","popup":"prayer"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/prayer/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/prayer/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"engagement","title":"Engagement Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-engagement","dataSource":"pray-tools","tabs":[{"id":"engagement","colorStrategy":"engagement","legend":"engagement","popup":"engagement"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/engagement/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/engagement/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"adoption","title":"Adoption Map","apiBaseUrl":"https://pray.doxa.life","props":{"profile":"doxa-simple-map","instanceId":"embed-adoption","dataSource":"pray-tools","tabs":[{"id":"adoption","colorStrategy":"adoption","legend":"adoption","popup":"adoption"}]},"element":"doxa-map","iframeUrl":"./doxa-simple-map/doxa-simple-map/adoption/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-simple-map/doxa-simple-map/adoption/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"doxa-vision-card","scriptUrl":"./doxa-vision-card/doxa-vision-card.js","output":"doxa-maps/doxa-vision-card/doxa-vision-card.js","integrity":"sha384-2mza3cltN5LQQIrlENG/IVL6Icd9zzcFve3E8R8VWuCdYOGW9uGKFvxcbu6eFfwn","elements":["doxa-vision-card"],"profiles":[{"profile":"doxa-vision-card","file":"doxa-vision-card.vue","description":"The Capability-1 example: ONE folder -> ONE bundle -> ONE element.","element":"doxa-vision-card","type":"parameterized","isMap":false,"iframeUrl":"./doxa-vision-card/doxa-vision-card/index.html","scriptUrl":"./doxa-vision-card/doxa-vision-card.js","embed":{"iframe":"\u003ciframe src=\"./doxa-vision-card/doxa-vision-card/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./doxa-vision-card/doxa-vision-card.js\" integrity=\"sha384-2mza3cltN5LQQIrlENG/IVL6Icd9zzcFve3E8R8VWuCdYOGW9uGKFvxcbu6eFfwn\" crossorigin=\"anonymous\">\u003c/script>\n\u003cdoxa-vision-card profile-config='{\"profile\":\"doxa-vision-card\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/doxa-vision-card>"},"instances":[{"id":"default","title":"Vision card — reach at a glance","apiBaseUrl":"","props":{"profile":"doxa-vision-card","provider":"maplibre","instanceId":"vision-card-default"},"element":"doxa-vision-card","iframeUrl":"./doxa-vision-card/doxa-vision-card/default/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-vision-card/doxa-vision-card/default/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"frontier","title":"Vision card — frontier accent","apiBaseUrl":"","props":{"profile":"doxa-vision-card","provider":"maplibre","instanceId":"vision-card-frontier","accent":"#22d3ee","title":"The last frontier of the gospel.","tagline":"Frontier people groups — where fewer than one in a thousand follow Jesus.","stats":[{"value":"1,910","label":"Frontier groups","note":"under 0.1% believers"},{"value":"2.1B","label":"People","note":"living in frontier settings"},{"value":"0.1%","label":"Christian","note":"or fewer, in each group"}]},"element":"doxa-vision-card","iframeUrl":"./doxa-vision-card/doxa-vision-card/frontier/index.html","embed":{"iframe":"\u003ciframe src=\"./doxa-vision-card/doxa-vision-card/frontier/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}],"bundle":"doxa-vision-card"}]},{"bundle":"golden-path-check","scriptUrl":"./golden-path-check/golden-path-check.js","output":"doxa-maps/golden-path-check/golden-path-check.js","integrity":"sha384-1V/sT5SkV0U9yTCK9ELIOFfU1ZIJkInIF6nG3UllJ4dxax3RsEAupSGLJEFK6e14","elements":["golden-path-check"],"profiles":[{"profile":"golden-path-check","file":"golden-path-check.vue","description":"A live self-test of the develop-a-plugin-and-see-it-live path: it reads the built","element":"golden-path-check","type":"single","isMap":false,"iframeUrl":"./golden-path-check/golden-path-check/index.html","scriptUrl":"./golden-path-check/golden-path-check.js","embed":{"iframe":"\u003ciframe src=\"./golden-path-check/golden-path-check/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./golden-path-check/golden-path-check.js\" integrity=\"sha384-1V/sT5SkV0U9yTCK9ELIOFfU1ZIJkInIF6nG3UllJ4dxax3RsEAupSGLJEFK6e14\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgolden-path-check profile-config='{\"profile\":\"golden-path-check\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/golden-path-check>"}}]},{"bundle":"gospel-resources","scriptUrl":"./gospel-resources/gospel-resources.js","output":"doxa-maps/gospel-resources/gospel-resources.js","integrity":"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI","elements":["gospel-resources-map"],"profiles":[{"profile":"charts","file":"charts.vue","description":"The gap by LANGUAGE (ISO 639-3) rather than by people group — because one translation serves every","element":"gospel-resources-map","type":"single","isMap":false,"iframeUrl":"./gospel-resources/charts/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}},{"profile":"data-tables","file":"data-tables.vue","description":"Two validated tables: what resources exist per people group, and whether that access is keeping","element":"gospel-resources-map","type":"single","isMap":false,"iframeUrl":"./gospel-resources/data-tables/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}},{"profile":"resources-list","file":"resources-list.vue","description":"Which DX people groups have scripture in their own language, and which have none. The zero-resource","element":"gospel-resources-map","type":"single","isMap":false,"iframeUrl":"./gospel-resources/resources-list/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}},{"profile":"resources-map","file":"resources-map.vue","description":"Every country shaded by how much of its unengaged population has NO gospel resource in their own","element":"gospel-resources-map","type":"single","isMap":true,"iframeUrl":"./gospel-resources/resources-map/index.html","scriptUrl":"./gospel-resources/gospel-resources.js","embed":{"iframe":"\u003ciframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-4pOAnuuOovcKXSQANV9jnLu1ExxiwQMuC4aU5MtAsd959tYmdUj05u4tT57Z/rKI\" crossorigin=\"anonymous\">\u003c/script>\n\u003cgospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/gospel-resources-map>"}}]},{"bundle":"my-upg-100-list","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","output":"doxa-maps/my-upg-100-list/my-upg-100-list.js","integrity":"sha384-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra","elements":["my-upg-100-list-map"],"composes":["my-upg-100-list/upg-100-list","my-upg-100-list/poster-wall"],"profiles":[{"profile":"poster-wall","file":"poster-wall.vue","description":"A wall of printable poster cards, one per 100-list entry, each with a \"Show on map\" action that","element":"my-upg-100-list-map","type":"single","isMap":true,"iframeUrl":"./my-upg-100-list/poster-wall/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe src=\"./my-upg-100-list/poster-wall/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"poster-wall\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/my-upg-100-list-map>"}},{"profile":"upg-100-list","file":"upg-100-list.vue","description":"A personal unreached-people-groups list painted as true boundary polygons —","element":"my-upg-100-list-map","type":"single","isMap":true,"iframeUrl":"./my-upg-100-list/upg-100-list/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe src=\"./my-upg-100-list/upg-100-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/my-upg-100-list-map>"}},{"profile":"upg-100-mfe","file":"upg-100-mfe.vue","description":"The standalone, shareable and embeddable composite of the my-upg-100-list family — it nests the map","element":"my-upg-100-list-map","type":"nested-elements-mfe","isMap":true,"iframeUrl":"./my-upg-100-list/upg-100-mfe/index.html","scriptUrl":"./my-upg-100-list/my-upg-100-list.js","embed":{"iframe":"\u003ciframe src=\"./my-upg-100-list/upg-100-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./my-upg-100-list/my-upg-100-list.js\" integrity=\"sha384-cs9Yz6dE0cp2ChVeNB9btNOXrqNk+xl0r4374AkMHRiEW7x+u3wlLDBv9TU469Ra\" crossorigin=\"anonymous\">\u003c/script>\n\u003cmy-upg-100-list-map profile-config='{\"profile\":\"upg-100-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/my-upg-100-list-map>"}}]},{"bundle":"people-groups-grid","scriptUrl":"./people-groups-grid/people-groups-grid.js","output":"doxa-maps/people-groups-grid/people-groups-grid.js","integrity":"sha384-ss5PmeSkKuxafBwvxZG3r7roP0OB/oVGwFbfrHnydEhjbtrlB4eisdsPXEyCJF37","elements":["people-groups-grid"],"profiles":[{"profile":"people-groups-grid","file":"people-groups-grid.vue","description":"The people-groups grid as an owned custom element: card list, search and four","element":"people-groups-grid","type":"parameterized","isMap":false,"iframeUrl":"./people-groups-grid/people-groups-grid/index.html","scriptUrl":"./people-groups-grid/people-groups-grid.js","embed":{"iframe":"\u003ciframe src=\"./people-groups-grid/people-groups-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./people-groups-grid/people-groups-grid.js\" integrity=\"sha384-ss5PmeSkKuxafBwvxZG3r7roP0OB/oVGwFbfrHnydEhjbtrlB4eisdsPXEyCJF37\" crossorigin=\"anonymous\">\u003c/script>\n\u003cpeople-groups-grid profile-config='{\"profile\":\"people-groups-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/people-groups-grid>"},"instances":[{"id":"people-groups-grid","title":"People Groups Grid — standalone (own search + 4 dropdowns)","apiBaseUrl":"","props":{"profile":"people-groups-grid","provider":"maplibre","instanceId":"people-groups-grid","pray-base-url":"https://pray.doxa.life"},"element":"people-groups-grid","iframeUrl":"./people-groups-grid/people-groups-grid/people-groups-grid/index.html","embed":{"iframe":"\u003ciframe src=\"./people-groups-grid/people-groups-grid/people-groups-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}},{"id":"driven","title":"People Groups Grid — externally driven (controls off, props/events)","apiBaseUrl":"","props":{"profile":"people-groups-grid","provider":"maplibre","instanceId":"people-groups-grid-driven","pray-base-url":"https://pray.doxa.life","show-search":"false","show-filters":"false"},"element":"people-groups-grid","iframeUrl":"./people-groups-grid/people-groups-grid/driven/index.html","embed":{"iframe":"\u003ciframe src=\"./people-groups-grid/people-groups-grid/driven/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"plugin-studio","scriptUrl":"./plugin-studio/plugin-studio.js","output":"doxa-maps/plugin-studio/plugin-studio.js","integrity":"sha384-2RlLFRU2XdjBN7w+FPguj8INgj57JFg4IFT2AmdefGnR9QQLehbiiAffV45LcsFH","elements":["plugin-studio"],"composes":["bp-big-picture","people-groups-grid","doxa-research-map","doxa-simple-map","cities-internet-map","gospel-resources-map","my-upg-100-list-map","doxa-vision-card","all-people-groups","research-dashboard"],"profiles":[{"profile":"plugin-studio","file":"plugin-studio.vue","description":"The developer's viewing surface: a sidebar that is the composability ladder","element":"plugin-studio","type":"nested-mfe-spa","isMap":false,"iframeUrl":"./plugin-studio/plugin-studio/index.html","scriptUrl":"./plugin-studio/plugin-studio.js","embed":{"iframe":"\u003ciframe src=\"./plugin-studio/plugin-studio/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./plugin-studio/plugin-studio.js\" integrity=\"sha384-2RlLFRU2XdjBN7w+FPguj8INgj57JFg4IFT2AmdefGnR9QQLehbiiAffV45LcsFH\" crossorigin=\"anonymous\">\u003c/script>\n\u003cplugin-studio profile-config='{\"profile\":\"plugin-studio\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/plugin-studio>"},"instances":[{"id":"studio","title":"Plugin Studio — every element by capability 5 -> 1 (developer studio)","apiBaseUrl":"","props":{"profile":"plugin-studio","provider":"maplibre","instanceId":"plugin-studio","title":"Plugin Studio"},"element":"plugin-studio","iframeUrl":"./plugin-studio/plugin-studio/studio/index.html","embed":{"iframe":"\u003ciframe src=\"./plugin-studio/plugin-studio/studio/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"prayer-walk-planner","scriptUrl":"./prayer-walk-planner/prayer-walk-planner.js","output":"doxa-maps/prayer-walk-planner/prayer-walk-planner.js","integrity":"sha384-F0+r4avX8xkXp0hBpU5EBPU0InMRCObIn2Ieh9HJNn1r2amk8/qHmJ68WuNfsirC","elements":["prayer-walk-planner"],"profiles":[{"profile":"prayer-walk-planner","file":"prayer-walk-planner.vue","description":"A simple polygon-drawing tool for planning prayer-walking routes.","element":"prayer-walk-planner","type":"single","isMap":true,"iframeUrl":"./prayer-walk-planner/prayer-walk-planner/index.html","scriptUrl":"./prayer-walk-planner/prayer-walk-planner.js","embed":{"iframe":"\u003ciframe src=\"./prayer-walk-planner/prayer-walk-planner/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./prayer-walk-planner/prayer-walk-planner.js\" integrity=\"sha384-F0+r4avX8xkXp0hBpU5EBPU0InMRCObIn2Ieh9HJNn1r2amk8/qHmJ68WuNfsirC\" crossorigin=\"anonymous\">\u003c/script>\n\u003cprayer-walk-planner profile-config='{\"profile\":\"prayer-walk-planner\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/prayer-walk-planner>"}}]},{"bundle":"regions-dashboard","scriptUrl":"./regions-dashboard/regions-dashboard.js","output":"doxa-maps/regions-dashboard/regions-dashboard.js","integrity":"sha384-CFvBQbLzGcJKwekfNfjZoR+LtYaMBsbDLaumMmvyKWdtDMN8Eer5XSMzYLHrQy8w","elements":["regions-dashboard"],"composes":["regions-dashboard/regions-list","regions-dashboard/region-dashboards"],"profiles":[{"profile":"region-dashboards","file":"region-dashboards.vue","description":"The dashboard half of the Driver's 2-tab Regions element: given a region (or a","element":"regions-dashboard","type":"single","isMap":true,"iframeUrl":"./regions-dashboard/region-dashboards/index.html","scriptUrl":"./regions-dashboard/regions-dashboard.js","embed":{"iframe":"\u003ciframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-CFvBQbLzGcJKwekfNfjZoR+LtYaMBsbDLaumMmvyKWdtDMN8Eer5XSMzYLHrQy8w\" crossorigin=\"anonymous\">\u003c/script>\n\u003cregions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/regions-dashboard>"}},{"profile":"regions-list","file":"regions-list.vue","description":"A refactor of DX-life's /regions index page (app/pages/regions/index.vue) into","element":"regions-dashboard","type":"single","isMap":true,"iframeUrl":"./regions-dashboard/regions-list/index.html","scriptUrl":"./regions-dashboard/regions-dashboard.js","embed":{"iframe":"\u003ciframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-CFvBQbLzGcJKwekfNfjZoR+LtYaMBsbDLaumMmvyKWdtDMN8Eer5XSMzYLHrQy8w\" crossorigin=\"anonymous\">\u003c/script>\n\u003cregions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/regions-dashboard>"}},{"profile":"regions-mfe","file":"regions-mfe.vue","description":"The composite the Driver asked for: ONE element carrying the SELECT VIEW switcher","element":"regions-dashboard","type":"nested-elements-mfe","isMap":true,"iframeUrl":"./regions-dashboard/regions-mfe/index.html","scriptUrl":"./regions-dashboard/regions-dashboard.js","embed":{"iframe":"\u003ciframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-CFvBQbLzGcJKwekfNfjZoR+LtYaMBsbDLaumMmvyKWdtDMN8Eer5XSMzYLHrQy8w\" crossorigin=\"anonymous\">\u003c/script>\n\u003cregions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/regions-dashboard>"}}]},{"bundle":"research-dashboard","scriptUrl":"./research-dashboard/research-dashboard.js","output":"doxa-maps/research-dashboard/research-dashboard.js","integrity":"sha384-VvOYWBcDd7NtnB1eveECKX88ZHIpGnOf79tVdCUqGZzXLgoZPc+oTTPIC3ajaYsn","elements":["research-dashboard-map"],"profiles":[{"profile":"research-dashboard","file":"research-dashboard.vue","description":"Coordinated-Multiple-Views research dashboard for 10/40-window UPGs (live pray.doxa.life): docked facet rail + map-as-hero + linked table/charts, brushing-and-linking over one in-memory store.","element":"research-dashboard-map","type":"monolithic","isMap":true,"iframeUrl":"./research-dashboard/research-dashboard/index.html","scriptUrl":"./research-dashboard/research-dashboard.js","embed":{"iframe":"\u003ciframe src=\"./research-dashboard/research-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./research-dashboard/research-dashboard.js\" integrity=\"sha384-VvOYWBcDd7NtnB1eveECKX88ZHIpGnOf79tVdCUqGZzXLgoZPc+oTTPIC3ajaYsn\" crossorigin=\"anonymous\">\u003c/script>\n\u003cresearch-dashboard-map profile-config='{\"profile\":\"research-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/research-dashboard-map>"},"bundle":"research-dashboard"}]},{"bundle":"research-map-plus-grid","scriptUrl":"./research-map-plus-grid/research-map-plus-grid.js","output":"doxa-maps/research-map-plus-grid/research-map-plus-grid.js","integrity":"sha384-sOkaMV0UFr2uNXfoJpRvaD+gHD6xFlkmpR7MzEwwFdarYdid8aN9Nj1Y8TqQJuFo","elements":["research-map-plus-grid"],"composes":["doxa-research-map","people-groups-grid"],"profiles":[{"profile":"research-map-plus-grid","file":"research-map-plus-grid.vue","description":"The monolithic 7-tab research map (cap-3) on top, the People Groups grid beneath,","element":"research-map-plus-grid","type":"nested-elements-mfe","isMap":false,"iframeUrl":"./research-map-plus-grid/research-map-plus-grid/index.html","scriptUrl":"./research-map-plus-grid/research-map-plus-grid.js","embed":{"iframe":"\u003ciframe src=\"./research-map-plus-grid/research-map-plus-grid/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./research-map-plus-grid/research-map-plus-grid.js\" integrity=\"sha384-sOkaMV0UFr2uNXfoJpRvaD+gHD6xFlkmpR7MzEwwFdarYdid8aN9Nj1Y8TqQJuFo\" crossorigin=\"anonymous\">\u003c/script>\n\u003cresearch-map-plus-grid profile-config='{\"profile\":\"research-map-plus-grid\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/research-map-plus-grid>"},"instances":[{"id":"default","title":"default","apiBaseUrl":"","props":{"profile":"research-map-plus-grid"},"element":"research-map-plus-grid","iframeUrl":"./research-map-plus-grid/research-map-plus-grid/default/index.html","embed":{"iframe":"\u003ciframe src=\"./research-map-plus-grid/research-map-plus-grid/default/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"stack-demo","scriptUrl":"./stack-demo/stack-demo.js","output":"doxa-maps/stack-demo/stack-demo.js","integrity":"sha384-gnwM6s+puf1K60Nl7crQcC2QC+9BxwpkaTx4OepLZ8VUCraZc+aRlWpsb6nj6Gyf","elements":["stack-demo"],"composes":["research-dashboard-map","people-groups-grid"],"profiles":[{"profile":"stack-demo","file":"stack-demo.vue","description":"Groups TWO independent elements TOP-TO-BOTTOM so you can embed the pair on a page and","element":"stack-demo","type":"nested-elements-mfe","isMap":false,"iframeUrl":"./stack-demo/stack-demo/index.html","scriptUrl":"./stack-demo/stack-demo.js","embed":{"iframe":"\u003ciframe src=\"./stack-demo/stack-demo/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./stack-demo/stack-demo.js\" integrity=\"sha384-gnwM6s+puf1K60Nl7crQcC2QC+9BxwpkaTx4OepLZ8VUCraZc+aRlWpsb6nj6Gyf\" crossorigin=\"anonymous\">\u003c/script>\n\u003cstack-demo profile-config='{\"profile\":\"stack-demo\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/stack-demo>"},"instances":[{"id":"pair","title":"pair","apiBaseUrl":"","props":{"profile":"stack-demo"},"element":"stack-demo","iframeUrl":"./stack-demo/stack-demo/pair/index.html","embed":{"iframe":"\u003ciframe src=\"./stack-demo/stack-demo/pair/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>"}}]}]},{"bundle":"template-bundle","scriptUrl":"./template-bundle/template-bundle.js","output":"doxa-maps/template-bundle/template-bundle.js","integrity":"sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX","elements":["template-bundle"],"profiles":[{"profile":"template-map-a","file":"template-map-a.vue","description":"Minimal example profile A — a colored placeholder, no Mapbox or data.","element":"template-bundle","type":"single","isMap":false,"iframeUrl":"./template-bundle/template-map-a/index.html","scriptUrl":"./template-bundle/template-bundle.js","embed":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-a/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-a\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/template-bundle>"},"bundle":"template-bundle"},{"profile":"template-map-b","file":"template-map-b.vue","description":"Minimal example profile B — a second profile in the same bundle.","element":"template-bundle","type":"single","isMap":false,"iframeUrl":"./template-bundle/template-map-b/index.html","scriptUrl":"./template-bundle/template-bundle.js","embed":{"iframe":"\u003ciframe src=\"./template-bundle/template-map-b/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen>\u003c/iframe>","script":"\u003cscript>window.MAP_APP_API_URL=\"https://pray.doxa.life\";\u003c/script>\n\u003cscript src=\"./template-bundle/template-bundle.js\" integrity=\"sha384-k+G5UlkZ99QmuecCCWUrjz998JBJnQT3hsDG0A+IVmiX9F3CNswmHXXzYhjnUTDX\" crossorigin=\"anonymous\">\u003c/script>\n\u003ctemplate-bundle profile-config='{\"profile\":\"template-map-b\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'>\u003c/template-bundle>"},"bundle":"template-bundle"}]}]};
\n\n"
+ "script": "\n\n"
}
},
{
@@ -674,7 +674,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "",
- "script": "\n\n"
+ "script": "\n\n"
}
},
{
@@ -688,7 +688,7 @@
"scriptUrl": "./my-upg-100-list/my-upg-100-list.js",
"embed": {
"iframe": "",
- "script": "\n\n"
+ "script": "\n\n"
}
}
]
@@ -838,7 +838,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
- "integrity": "sha384-G5kRzcJ2eKbMi2QatgYKKtrwZOo133Mr0ymaMwFJ/i6PWsCUq71lSXNnAeBwnWoN",
+ "integrity": "sha384-CFvBQbLzGcJKwekfNfjZoR+LtYaMBsbDLaumMmvyKWdtDMN8Eer5XSMzYLHrQy8w",
"elements": [
"regions-dashboard"
],
@@ -858,7 +858,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "",
- "script": "\n\n"
+ "script": "\n\n"
}
},
{
@@ -872,7 +872,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "",
- "script": "\n\n"
+ "script": "\n\n"
}
},
{
@@ -886,7 +886,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "",
- "script": "\n\n"
+ "script": "\n\n"
}
}
]
diff --git a/public/doxa-maps/my-upg-100-list/my-upg-100-list.js b/public/doxa-maps/my-upg-100-list/my-upg-100-list.js
index 1b2b28c73..0b5ee047e 100644
--- a/public/doxa-maps/my-upg-100-list/my-upg-100-list.js
+++ b/public/doxa-maps/my-upg-100-list/my-upg-100-list.js
@@ -1,46 +1,46 @@
-(function(){"use strict";var ip;var cn=typeof document<"u"?document.currentScript:null;const Mx="modulepreload",Nx=function(e){return"/"+e},Ix={},to=function(t,o,n){let a=Promise.resolve();function r(s){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s}return a.then(s=>{for(const i of s||[])i.status==="rejected"&&r(i.reason);return t().catch(r)})};/**
+(function(){"use strict";var bp;var cn=typeof document<"u"?document.currentScript:null;const zx="modulepreload",Hx=function(e){return"/"+e},Vx={},to=function(t,o,n){let a=Promise.resolve();function r(s){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=s,window.dispatchEvent(i),!i.defaultPrevented)throw s}return a.then(s=>{for(const i of s||[])i.status==="rejected"&&r(i.reason);return t().catch(r)})};/**
* @vue/shared v3.5.42
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
-**/function Pr(e){const t=Object.create(null);for(const o of e.split(","))t[o]=1;return o=>o in t}const Xe={},un=[],oo=()=>{},Ei=()=>!1,va=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),ka=e=>e.startsWith("onUpdate:"),ut=Object.assign,Mr=(e,t)=>{const o=e.indexOf(t);o>-1&&e.splice(o,1)},pp=Object.prototype.hasOwnProperty,ze=(e,t)=>pp.call(e,t),we=Array.isArray,No=e=>On(e)==="[object Map]",Wo=e=>On(e)==="[object Set]",Ci=e=>On(e)==="[object Date]",Ae=e=>typeof e=="function",Qe=e=>typeof e=="string",Gt=e=>typeof e=="symbol",Ve=e=>e!==null&&typeof e=="object",Li=e=>(Ve(e)||Ae(e))&&Ae(e.then)&&Ae(e.catch),Ti=Object.prototype.toString,On=e=>Ti.call(e),gp=e=>On(e).slice(8,-1),wa=e=>On(e)==="[object Object]",xa=e=>Qe(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Dn=Pr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),$a=e=>{const t=Object.create(null);return o=>t[o]||(t[o]=e(o))},fp=/-\w/g,_t=$a(e=>e.replace(fp,t=>t.slice(1).toUpperCase())),hp=/\B([A-Z])/g,jt=$a(e=>e.replace(hp,"-$1").toLowerCase()),Sa=$a(e=>e.charAt(0).toUpperCase()+e.slice(1)),Nr=$a(e=>e?`on${Sa(e)}`:""),no=(e,t)=>!Object.is(e,t),Aa=(e,...t)=>{for(let o=0;o{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:n,value:o})},Ea=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Mi=e=>{const t=Qe(e)?Number(e):NaN;return isNaN(t)?e:t};let Ni;const Ca=()=>Ni||(Ni=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function nt(e){if(we(e)){const t={};for(let o=0;o{if(o){const n=o.split(bp);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Ne(e){let t="";if(Qe(e))t=e;else if(we(e))for(let o=0;oIo(o,t))}const Fi=e=>!!(e&&e.__v_isRef===!0),ge=e=>Qe(e)?e:e==null?"":we(e)||Ve(e)&&(e.toString===Ti||!Ae(e.toString))?Fi(e)?ge(e.value):JSON.stringify(e,Oi,2):String(e),Oi=(e,t)=>Fi(t)?Oi(e,t.value):No(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((o,[n,a],r)=>(o[Ir(n,r)+" =>"]=a,o),{})}:Wo(t)?{[`Set(${t.size})`]:[...t.values()].map(o=>Ir(o))}:Gt(t)?Ir(t):Ve(t)&&!we(t)&&!wa(t)?String(t):t,Ir=(e,t="")=>{var o;return Gt(e)?`Symbol(${(o=e.description)!=null?o:t})`:e};/**
+**/function Pr(e){const t=Object.create(null);for(const o of e.split(","))t[o]=1;return o=>o in t}const et={},un=[],oo=()=>{},Ei=()=>!1,va=e=>e.charCodeAt(0)===111&&e.charCodeAt(1)===110&&(e.charCodeAt(2)>122||e.charCodeAt(2)<97),ka=e=>e.startsWith("onUpdate:"),dt=Object.assign,Mr=(e,t)=>{const o=e.indexOf(t);o>-1&&e.splice(o,1)},wp=Object.prototype.hasOwnProperty,Ve=(e,t)=>wp.call(e,t),xe=Array.isArray,No=e=>Fn(e)==="[object Map]",Wo=e=>Fn(e)==="[object Set]",Ci=e=>Fn(e)==="[object Date]",$e=e=>typeof e=="function",tt=e=>typeof e=="string",Gt=e=>typeof e=="symbol",Ye=e=>e!==null&&typeof e=="object",Li=e=>(Ye(e)||$e(e))&&$e(e.then)&&$e(e.catch),Ti=Object.prototype.toString,Fn=e=>Ti.call(e),xp=e=>Fn(e).slice(8,-1),wa=e=>Fn(e)==="[object Object]",xa=e=>tt(e)&&e!=="NaN"&&e[0]!=="-"&&""+parseInt(e,10)===e,Dn=Pr(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Aa=e=>{const t=Object.create(null);return o=>t[o]||(t[o]=e(o))},Ap=/-\w/g,_t=Aa(e=>e.replace(Ap,t=>t.slice(1).toUpperCase())),Sp=/\B([A-Z])/g,jt=Aa(e=>e.replace(Sp,"-$1").toLowerCase()),Sa=Aa(e=>e.charAt(0).toUpperCase()+e.slice(1)),Nr=Aa(e=>e?`on${Sa(e)}`:""),no=(e,t)=>!Object.is(e,t),$a=(e,...t)=>{for(let o=0;o{Object.defineProperty(e,t,{configurable:!0,enumerable:!1,writable:n,value:o})},Ea=e=>{const t=parseFloat(e);return isNaN(t)?e:t},Mi=e=>{const t=tt(e)?Number(e):NaN;return isNaN(t)?e:t};let Ni;const Ca=()=>Ni||(Ni=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:typeof global<"u"?global:{});function rt(e){if(xe(e)){const t={};for(let o=0;o{if(o){const n=o.split(Ep);n.length>1&&(t[n[0].trim()]=n[1].trim())}}),t}function Ne(e){let t="";if(tt(e))t=e;else if(xe(e))for(let o=0;oRo(o,t))}const Oi=e=>!!(e&&e.__v_isRef===!0),pe=e=>tt(e)?e:e==null?"":xe(e)||Ye(e)&&(e.toString===Ti||!$e(e.toString))?Oi(e)?pe(e.value):JSON.stringify(e,Fi,2):String(e),Fi=(e,t)=>Oi(t)?Fi(e,t.value):No(t)?{[`Map(${t.size})`]:[...t.entries()].reduce((o,[n,a],r)=>(o[Rr(n,r)+" =>"]=a,o),{})}:Wo(t)?{[`Set(${t.size})`]:[...t.values()].map(o=>Rr(o))}:Gt(t)?Rr(t):Ye(t)&&!xe(t)&&!wa(t)?String(t):t,Rr=(e,t="")=>{var o;return Gt(e)?`Symbol(${(o=e.description)!=null?o:t})`:e};/**
* @vue/reactivity v3.5.42
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
-**/let ht;class Di{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!t&&ht&&(ht.active?(this.parent=ht,this.index=(ht.scopes||(ht.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,o;if(this.scopes){const n=this.scopes.slice();for(t=0,o=n.length;t0&&--this._on===0){if(ht===this)ht=this.prevScope;else{let t=ht;for(;t;){if(t.prevScope===this){t.prevScope=this.prevScope;break}t=t.prevScope}}this.prevScope=void 0}}stop(t){if(this._active){this._active=!1;let o,n;for(o=0,n=this.effects.length;o0)return;if(Gn){let t=Gn;for(Gn=void 0;t;){const o=t.next;t.next=void 0,t.flags&=-9,t=o}}let e;for(;Bn;){let t=Bn;for(Bn=void 0;t;){const o=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(n){e||(e=n)}t=o}}if(e)throw e}function Ki(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function zi(e){let t,o=e.depsTail,n=o;for(;n;){const a=n.prevDep;n.version===-1?(n===o&&(o=a),Gr(n),$p(n)):t=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=a}e.deps=t,e.depsTail=o}function Br(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Hi(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Hi(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===jn)||(e.globalVersion=jn,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Br(e))))return;e.flags|=2;const t=e.dep,o=et,n=Yt;et=e,Yt=!0;try{Ki(e);const a=e.fn(e._value);(t.version===0||no(a,e._value))&&(e.flags|=128,e._value=a,t.version++)}catch(a){throw t.version++,a}finally{et=o,Yt=n,zi(e),e.flags&=-3}}function Gr(e,t=!1){const{dep:o,prevSub:n,nextSub:a}=e;if(n&&(n.nextSub=a,e.prevSub=void 0),a&&(a.prevSub=n,e.nextSub=void 0),o.subs===e&&(o.subs=n,!n&&o.computed)){o.computed.flags&=-5;for(let r=o.computed.deps;r;r=r.nextDep)Gr(r,!0)}!t&&!--o.sc&&o.map&&o.map.delete(o.key)}function $p(e){const{prevDep:t,nextDep:o}=e;t&&(t.nextDep=o,e.prevDep=void 0),o&&(o.prevDep=t,e.nextDep=void 0)}let Yt=!0;const Vi=[];function ao(){Vi.push(Yt),Yt=!1}function ro(){const e=Vi.pop();Yt=e===void 0?!0:e}function Wi(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const o=et;et=void 0;try{t()}finally{et=o}}}let jn=0;class Sp{constructor(t,o){this.sub=t,this.dep=o,this.version=o.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class jr{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!et||!Yt||et===this.computed)return;let o=this.activeLink;if(o===void 0||o.sub!==et)o=this.activeLink=new Sp(et,this),et.deps?(o.prevDep=et.depsTail,et.depsTail.nextDep=o,et.depsTail=o):et.deps=et.depsTail=o,Yi(o);else if(o.version===-1&&(o.version=this.version,o.nextDep)){const n=o.nextDep;n.prevDep=o.prevDep,o.prevDep&&(o.prevDep.nextDep=n),o.prevDep=et.depsTail,o.nextDep=void 0,et.depsTail.nextDep=o,et.depsTail=o,et.deps===o&&(et.deps=n)}return o}trigger(t){this.version++,jn++,this.notify(t)}notify(t){Or();try{for(let o=this.subs;o;o=o.prevSub)o.sub.notify()&&o.sub.dep.notify()}finally{Dr()}}}function Yi(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let n=t.deps;n;n=n.nextDep)Yi(n)}const o=e.dep.subs;o!==e&&(e.prevSub=o,o&&(o.nextSub=e)),e.dep.subs=e}}const La=new WeakMap,Yo=Symbol(""),Ur=Symbol(""),Un=Symbol("");function Pt(e,t,o){if(Yt&&et){let n=La.get(e);n||La.set(e,n=new Map);let a=n.get(o);a||(n.set(o,a=new jr),a.map=n,a.key=o),a.track()}}function yo(e,t,o,n,a,r){const s=La.get(e);if(!s){jn++;return}const i=l=>{l&&l.trigger()};if(Or(),t==="clear")s.forEach(i);else{const l=we(e),c=l&&xa(o);if(l&&o==="length"){const u=Number(n);s.forEach((p,f)=>{(f==="length"||f===Un||!Gt(f)&&f>=u)&&i(p)})}else switch((o!==void 0||s.has(void 0))&&i(s.get(o)),c&&i(s.get(Un)),t){case"add":l?c&&i(s.get("length")):(i(s.get(Yo)),No(e)&&i(s.get(Ur)));break;case"delete":l||(i(s.get(Yo)),No(e)&&i(s.get(Ur)));break;case"set":No(e)&&i(s.get(Yo));break}}Dr()}function Ap(e,t){const o=La.get(e);return o&&o.get(t)}function dn(e){const t=Oe(e);return t===e?t:(Pt(t,"iterate",Un),Ut(e)?t:t.map(qt))}function Ta(e){return Pt(e=Oe(e),"iterate",Un),e}function so(e,t){return ko(e)?gn(vo(e)?qt(t):t):qt(t)}const Ep={__proto__:null,[Symbol.iterator](){return Kr(this,Symbol.iterator,e=>so(this,e))},concat(...e){return dn(this).concat(...e.map(t=>we(t)?dn(t):t))},entries(){return Kr(this,"entries",e=>(e[1]=so(this,e[1]),e))},every(e,t){return _o(this,"every",e,t,void 0,arguments)},filter(e,t){return _o(this,"filter",e,t,o=>o.map(n=>so(this,n)),arguments)},find(e,t){return _o(this,"find",e,t,o=>so(this,o),arguments)},findIndex(e,t){return _o(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return _o(this,"findLast",e,t,o=>so(this,o),arguments)},findLastIndex(e,t){return _o(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return _o(this,"forEach",e,t,void 0,arguments)},includes(...e){return zr(this,"includes",e)},indexOf(...e){return zr(this,"indexOf",e)},join(e){return dn(this).join(e)},lastIndexOf(...e){return zr(this,"lastIndexOf",e)},map(e,t){return _o(this,"map",e,t,void 0,arguments)},pop(){return Kn(this,"pop")},push(...e){return Kn(this,"push",e)},reduce(e,...t){return qi(this,"reduce",e,t)},reduceRight(e,...t){return qi(this,"reduceRight",e,t)},shift(){return Kn(this,"shift")},some(e,t){return _o(this,"some",e,t,void 0,arguments)},splice(...e){return Kn(this,"splice",e)},toReversed(){return dn(this).toReversed()},toSorted(e){return dn(this).toSorted(e)},toSpliced(...e){return dn(this).toSpliced(...e)},unshift(...e){return Kn(this,"unshift",e)},values(){return Kr(this,"values",e=>so(this,e))}};function Kr(e,t,o){const n=Ta(e),a=n[t]();return n!==e&&!Ut(e)&&(a._next=a.next,a.next=()=>{const r=a._next();return r.done||(r.value=o(r.value)),r}),a}const Cp=Array.prototype;function _o(e,t,o,n,a,r){const s=Ta(e),i=s!==e&&!Ut(e),l=s[t];if(l!==Cp[t]){const p=l.apply(e,r);return i?qt(p):p}let c=o;s!==e&&(i?c=function(p,f){return o.call(this,so(e,p),f,e)}:o.length>2&&(c=function(p,f){return o.call(this,p,f,e)}));const u=l.call(s,c,n);return i&&a?a(u):u}function qi(e,t,o,n){const a=Ta(e),r=a!==e&&!Ut(e);let s=o,i=!1;a!==e&&(r?(i=n.length===0,s=function(c,u,p){return i&&(i=!1,c=so(e,c)),o.call(this,c,so(e,u),p,e)}):o.length>3&&(s=function(c,u,p){return o.call(this,c,u,p,e)}));const l=a[t](s,...n);return i?so(e,l):l}function zr(e,t,o){const n=Oe(e);Pt(n,"iterate",Un);const a=n[t](...o);return(a===-1||a===!1)&&Ra(o[0])?(o[0]=Oe(o[0]),n[t](...o)):a}function Kn(e,t,o=[]){ao(),Or();const n=Oe(e)[t].apply(e,o);return Dr(),ro(),n}const Lp=Pr("__proto__,__v_isRef,__isVue"),Ji=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Gt));function Tp(e){Gt(e)||(e=String(e));const t=Oe(this);return Pt(t,"has",e),t.hasOwnProperty(e)}class Xi{constructor(t=!1,o=!1){this._isReadonly=t,this._isShallow=o}get(t,o,n){if(o==="__v_skip")return t.__v_skip;const a=this._isReadonly,r=this._isShallow;if(o==="__v_isReactive")return!a;if(o==="__v_isReadonly")return a;if(o==="__v_isShallow")return r;if(o==="__v_raw")return n===(a?r?nl:ol:r?tl:el).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const s=we(t);if(!a){let l;if(s&&(l=Ep[o]))return l;if(o==="hasOwnProperty")return Tp}const i=Reflect.get(t,o,at(t)?t:n);if((Gt(o)?Ji.has(o):Lp(o))||(a||Pt(t,"get",o),r))return i;if(at(i)){const l=s&&xa(o)?i:i.value;return a&&Ve(l)?Vr(l):l}return Ve(i)?a?Vr(i):pn(i):i}}class Zi extends Xi{constructor(t=!1){super(!1,t)}set(t,o,n,a){let r=t[o];const s=we(t)&&xa(o);if(!this._isShallow){const c=ko(r);if(!Ut(n)&&!ko(n)&&(r=Oe(r),n=Oe(n)),!s&&at(r)&&!at(n))return c||(r.value=n),!0}const i=s?Number(o)e,Pa=e=>Reflect.getPrototypeOf(e);function Rp(e,t,o){return function(...n){const a=this.__v_raw,r=Oe(a),s=No(r),i=e==="entries"||e===Symbol.iterator&&s,l=e==="keys"&&s,c=a[e](...n),u=o?Hr:t?gn:qt;return!t&&Pt(r,"iterate",l?Ur:Yo),ut(Object.create(c),{next(){const{value:p,done:f}=c.next();return f?{value:p,done:f}:{value:i?[u(p[0]),u(p[1])]:u(p),done:f}}})}}function Ma(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Fp(e,t){const o={get(a){const r=this.__v_raw,s=Oe(r),i=Oe(a);e||(no(a,i)&&Pt(s,"get",a),Pt(s,"get",i));const{has:l}=Pa(s),c=t?Hr:e?gn:qt;if(l.call(s,a))return c(r.get(a));if(l.call(s,i))return c(r.get(i));r!==s&&r.get(a)},get size(){const a=this.__v_raw;return!e&&Pt(Oe(a),"iterate",Yo),a.size},has(a){const r=this.__v_raw,s=Oe(r),i=Oe(a);return e||(no(a,i)&&Pt(s,"has",a),Pt(s,"has",i)),a===i?r.has(a):r.has(a)||r.has(i)},forEach(a,r){const s=this,i=s.__v_raw,l=Oe(i),c=t?Hr:e?gn:qt;return!e&&Pt(l,"iterate",Yo),i.forEach((u,p)=>a.call(r,c(u),c(p),s))}};return ut(o,e?{add:Ma("add"),set:Ma("set"),delete:Ma("delete"),clear:Ma("clear")}:{add(a){const r=Oe(this),s=Pa(r),i=Oe(a),l=!t&&!Ut(a)&&!ko(a)?i:a;return s.has.call(r,l)||no(a,l)&&s.has.call(r,a)||no(i,l)&&s.has.call(r,i)||(r.add(l),yo(r,"add",l,l)),this},set(a,r){!t&&!Ut(r)&&!ko(r)&&(r=Oe(r));const s=Oe(this),{has:i,get:l}=Pa(s);let c=i.call(s,a);c||(a=Oe(a),c=i.call(s,a));const u=l.call(s,a);return s.set(a,r),c?no(r,u)&&yo(s,"set",a,r):yo(s,"add",a,r),this},delete(a){const r=Oe(this),{has:s,get:i}=Pa(r);let l=s.call(r,a);l||(a=Oe(a),l=s.call(r,a)),i&&i.call(r,a);const c=r.delete(a);return l&&yo(r,"delete",a,void 0),c},clear(){const a=Oe(this),r=a.size!==0,s=a.clear();return r&&yo(a,"clear",void 0,void 0),s}}),["keys","values","entries",Symbol.iterator].forEach(a=>{o[a]=Rp(a,e,t)}),o}function Na(e,t){const o=Fp(e,t);return(n,a,r)=>a==="__v_isReactive"?!e:a==="__v_isReadonly"?e:a==="__v_raw"?n:Reflect.get(ze(o,a)&&a in n?o:n,a,r)}const Op={get:Na(!1,!1)},Dp={get:Na(!1,!0)},Bp={get:Na(!0,!1)},Gp={get:Na(!0,!0)},el=new WeakMap,tl=new WeakMap,ol=new WeakMap,nl=new WeakMap;function jp(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function pn(e){return ko(e)?e:Ia(e,!1,Pp,Op,el)}function Up(e){return Ia(e,!1,Np,Dp,tl)}function Vr(e){return Ia(e,!0,Mp,Bp,ol)}function Fx(e){return Ia(e,!0,Ip,Gp,nl)}function Ia(e,t,o,n,a){if(!Ve(e)||e.__v_raw&&!(t&&e.__v_isReactive)||e.__v_skip||!Object.isExtensible(e))return e;const r=a.get(e);if(r)return r;const s=jp(gp(e));if(s===0)return e;const i=new Proxy(e,s===2?n:o);return a.set(e,i),i}function vo(e){return ko(e)?vo(e.__v_raw):!!(e&&e.__v_isReactive)}function ko(e){return!!(e&&e.__v_isReadonly)}function Ut(e){return!!(e&&e.__v_isShallow)}function Ra(e){return e?!!e.__v_raw:!1}function Oe(e){const t=e&&e.__v_raw;return t?Oe(t):e}function qo(e){return!ze(e,"__v_skip")&&Object.isExtensible(e)&&Pi(e,"__v_skip",!0),e}const qt=e=>Ve(e)?pn(e):e,gn=e=>Ve(e)?Vr(e):e;function at(e){return e?e.__v_isRef===!0:!1}function de(e){return al(e,!1)}function fn(e){return al(e,!0)}function al(e,t){return at(e)?e:new Kp(e,t)}class Kp{constructor(t,o){this.dep=new jr,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=o?t:Oe(t),this._value=o?t:qt(t),this.__v_isShallow=o}get value(){return this.dep.track(),this._value}set value(t){const o=this._rawValue,n=this.__v_isShallow||Ut(t)||ko(t);t=n?t:Oe(t),no(t,o)&&(this._rawValue=t,this._value=n?t:qt(t),this.dep.trigger())}}function _e(e){return at(e)?e.value:e}const zp={get:(e,t,o)=>t==="__v_raw"?e:_e(Reflect.get(e,t,o)),set:(e,t,o,n)=>{const a=e[t];return at(a)&&!at(o)?(a.value=o,!0):Reflect.set(e,t,o,n)}};function rl(e){return vo(e)?e:new Proxy(e,zp)}function Hp(e){const t=we(e)?new Array(e.length):{};for(const o in e)t[o]=sl(e,o);return t}class Vp{constructor(t,o,n){this._object=t,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0,this._key=Gt(o)?o:String(o),this._raw=Oe(t);let a=!0,r=t;if(!we(t)||Gt(this._key)||!xa(this._key))do a=!Ra(r)||Ut(r);while(a&&(r=r.__v_raw));this._shallow=a}get value(){let t=this._object[this._key];return this._shallow&&(t=_e(t)),this._value=t===void 0?this._defaultValue:t}set value(t){if(this._shallow&&at(this._raw[this._key])){const o=this._object[this._key];if(at(o)){o.value=t;return}}this._object[this._key]=t}get dep(){return Ap(this._raw,this._key)}}class Wp{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function Yp(e,t,o){return at(e)?e:Ae(e)?new Wp(e):Ve(e)&&arguments.length>1?sl(e,t,o):de(e)}function sl(e,t,o){return new Vp(e,t,o)}class qp{constructor(t,o,n){this.fn=t,this.setter=o,this._value=void 0,this.dep=new jr(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=jn-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!o,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&et!==this)return Ui(this,!0),!0}get value(){const t=this.dep.track();return Hi(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function Jp(e,t,o=!1){let n,a;return Ae(e)?n=e:(n=e.get,a=e.set),new qp(n,a,o)}const Fa={},Oa=new WeakMap;let Jo;function Xp(e,t=!1,o=Jo){if(o){let n=Oa.get(o);n||Oa.set(o,n=[]),n.push(e)}}function Zp(e,t,o=Xe){const{immediate:n,deep:a,once:r,scheduler:s,augmentJob:i,call:l}=o,c=k=>a?k:Ut(k)||a===!1||a===0?wo(k,1):wo(k);let u,p,f,w,x=!1,y=!1;if(at(e)?(p=()=>e.value,x=Ut(e)):vo(e)?(p=()=>c(e),x=!0):we(e)?(y=!0,x=e.some(k=>vo(k)||Ut(k)),p=()=>e.map(k=>{if(at(k))return k.value;if(vo(k))return c(k);if(Ae(k))return l?l(k,2):k()})):Ae(e)?t?p=l?()=>l(e,2):e:p=()=>{if(f){ao();try{f()}finally{ro()}}const k=Jo;Jo=u;try{return l?l(e,3,[w]):e(w)}finally{Jo=k}}:p=oo,t&&a){const k=p,$=a===!0?1/0:a;p=()=>wo(k(),$)}const d=Bi(),g=()=>{u.stop(),d&&d.active&&Mr(d.effects,u)};if(r&&t){const k=t;t=(...$)=>{const v=k(...$);return g(),v}}let b=y?new Array(e.length).fill(Fa):Fa;const h=k=>{if(!(!(u.flags&1)||!u.dirty&&!k))if(t){const $=u.run();if(k||a||x||(y?$.some((v,C)=>no(v,b[C])):no($,b))){f&&f();const v=Jo;Jo=u;try{const C=[$,b===Fa?void 0:y&&b[0]===Fa?[]:b,w];b=$,l?l(t,3,C):t(...C)}finally{Jo=v}}}else u.run()};return i&&i(h),u=new Gi(p),u.scheduler=s?()=>s(h,!1):h,w=k=>Xp(k,!1,u),f=u.onStop=()=>{const k=Oa.get(u);if(k){if(l)l(k,4);else for(const $ of k)$();Oa.delete(u)}},t?n?h(!0):b=u.run():s?s(h.bind(null,!0),!0):u.run(),g.pause=u.pause.bind(u),g.resume=u.resume.bind(u),g.stop=g,g}function wo(e,t=1/0,o){if(t<=0||!Ve(e)||e.__v_skip||(o=o||new Map,(o.get(e)||0)>=t))return e;if(o.set(e,t),t--,at(e))wo(e.value,t,o);else if(we(e))for(let n=0;n{wo(n,t,o)});else if(wa(e)){for(const n in e)wo(e[n],t,o);for(const n of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,n)&&wo(e[n],t,o)}return e}/**
+**/let ht;class Di{constructor(t=!1){this.detached=t,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this._warnOnRun=!0,this.__v_skip=!0,!t&&ht&&(ht.active?(this.parent=ht,this.index=(ht.scopes||(ht.scopes=[])).push(this)-1):(this._active=!1,this._warnOnRun=!1))}get active(){return this._active}pause(){if(this._active){this._isPaused=!0;let t,o;if(this.scopes){const n=this.scopes.slice();for(t=0,o=n.length;t0&&--this._on===0){if(ht===this)ht=this.prevScope;else{let t=ht;for(;t;){if(t.prevScope===this){t.prevScope=this.prevScope;break}t=t.prevScope}}this.prevScope=void 0}}stop(t){if(this._active){this._active=!1;let o,n;for(o=0,n=this.effects.length;o0)return;if(Gn){let t=Gn;for(Gn=void 0;t;){const o=t.next;t.next=void 0,t.flags&=-9,t=o}}let e;for(;Bn;){let t=Bn;for(Bn=void 0;t;){const o=t.next;if(t.next=void 0,t.flags&=-9,t.flags&1)try{t.trigger()}catch(n){e||(e=n)}t=o}}if(e)throw e}function Ki(e){for(let t=e.deps;t;t=t.nextDep)t.version=-1,t.prevActiveLink=t.dep.activeLink,t.dep.activeLink=t}function zi(e){let t,o=e.depsTail,n=o;for(;n;){const a=n.prevDep;n.version===-1?(n===o&&(o=a),Gr(n),Rp(n)):t=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=a}e.deps=t,e.depsTail=o}function Br(e){for(let t=e.deps;t;t=t.nextDep)if(t.dep.version!==t.version||t.dep.computed&&(Hi(t.dep.computed)||t.dep.version!==t.version))return!0;return!!e._dirty}function Hi(e){if(e.flags&4&&!(e.flags&16)||(e.flags&=-17,e.globalVersion===jn)||(e.globalVersion=jn,!e.isSSR&&e.flags&128&&(!e.deps&&!e._dirty||!Br(e))))return;e.flags|=2;const t=e.dep,o=ot,n=Yt;ot=e,Yt=!0;try{Ki(e);const a=e.fn(e._value);(t.version===0||no(a,e._value))&&(e.flags|=128,e._value=a,t.version++)}catch(a){throw t.version++,a}finally{ot=o,Yt=n,zi(e),e.flags&=-3}}function Gr(e,t=!1){const{dep:o,prevSub:n,nextSub:a}=e;if(n&&(n.nextSub=a,e.prevSub=void 0),a&&(a.prevSub=n,e.nextSub=void 0),o.subs===e&&(o.subs=n,!n&&o.computed)){o.computed.flags&=-5;for(let r=o.computed.deps;r;r=r.nextDep)Gr(r,!0)}!t&&!--o.sc&&o.map&&o.map.delete(o.key)}function Rp(e){const{prevDep:t,nextDep:o}=e;t&&(t.nextDep=o,e.prevDep=void 0),o&&(o.prevDep=t,e.nextDep=void 0)}let Yt=!0;const Vi=[];function ao(){Vi.push(Yt),Yt=!1}function ro(){const e=Vi.pop();Yt=e===void 0?!0:e}function Wi(e){const{cleanup:t}=e;if(e.cleanup=void 0,t){const o=ot;ot=void 0;try{t()}finally{ot=o}}}let jn=0;class Ip{constructor(t,o){this.sub=t,this.dep=o,this.version=o.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class jr{constructor(t){this.computed=t,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(t){if(!ot||!Yt||ot===this.computed)return;let o=this.activeLink;if(o===void 0||o.sub!==ot)o=this.activeLink=new Ip(ot,this),ot.deps?(o.prevDep=ot.depsTail,ot.depsTail.nextDep=o,ot.depsTail=o):ot.deps=ot.depsTail=o,Yi(o);else if(o.version===-1&&(o.version=this.version,o.nextDep)){const n=o.nextDep;n.prevDep=o.prevDep,o.prevDep&&(o.prevDep.nextDep=n),o.prevDep=ot.depsTail,o.nextDep=void 0,ot.depsTail.nextDep=o,ot.depsTail=o,ot.deps===o&&(ot.deps=n)}return o}trigger(t){this.version++,jn++,this.notify(t)}notify(t){Fr();try{for(let o=this.subs;o;o=o.prevSub)o.sub.notify()&&o.sub.dep.notify()}finally{Dr()}}}function Yi(e){if(e.dep.sc++,e.sub.flags&4){const t=e.dep.computed;if(t&&!e.dep.subs){t.flags|=20;for(let n=t.deps;n;n=n.nextDep)Yi(n)}const o=e.dep.subs;o!==e&&(e.prevSub=o,o&&(o.nextSub=e)),e.dep.subs=e}}const La=new WeakMap,Yo=Symbol(""),Ur=Symbol(""),Un=Symbol("");function Pt(e,t,o){if(Yt&&ot){let n=La.get(e);n||La.set(e,n=new Map);let a=n.get(o);a||(n.set(o,a=new jr),a.map=n,a.key=o),a.track()}}function yo(e,t,o,n,a,r){const s=La.get(e);if(!s){jn++;return}const i=l=>{l&&l.trigger()};if(Fr(),t==="clear")s.forEach(i);else{const l=xe(e),u=l&&xa(o);if(l&&o==="length"){const c=Number(n);s.forEach((p,g)=>{(g==="length"||g===Un||!Gt(g)&&g>=c)&&i(p)})}else switch((o!==void 0||s.has(void 0))&&i(s.get(o)),u&&i(s.get(Un)),t){case"add":l?u&&i(s.get("length")):(i(s.get(Yo)),No(e)&&i(s.get(Ur)));break;case"delete":l||(i(s.get(Yo)),No(e)&&i(s.get(Ur)));break;case"set":No(e)&&i(s.get(Yo));break}}Dr()}function Op(e,t){const o=La.get(e);return o&&o.get(t)}function dn(e){const t=Be(e);return t===e?t:(Pt(t,"iterate",Un),Ut(e)?t:t.map(qt))}function Ta(e){return Pt(e=Be(e),"iterate",Un),e}function so(e,t){return ko(e)?gn(vo(e)?qt(t):t):qt(t)}const Fp={__proto__:null,[Symbol.iterator](){return Kr(this,Symbol.iterator,e=>so(this,e))},concat(...e){return dn(this).concat(...e.map(t=>xe(t)?dn(t):t))},entries(){return Kr(this,"entries",e=>(e[1]=so(this,e[1]),e))},every(e,t){return _o(this,"every",e,t,void 0,arguments)},filter(e,t){return _o(this,"filter",e,t,o=>o.map(n=>so(this,n)),arguments)},find(e,t){return _o(this,"find",e,t,o=>so(this,o),arguments)},findIndex(e,t){return _o(this,"findIndex",e,t,void 0,arguments)},findLast(e,t){return _o(this,"findLast",e,t,o=>so(this,o),arguments)},findLastIndex(e,t){return _o(this,"findLastIndex",e,t,void 0,arguments)},forEach(e,t){return _o(this,"forEach",e,t,void 0,arguments)},includes(...e){return zr(this,"includes",e)},indexOf(...e){return zr(this,"indexOf",e)},join(e){return dn(this).join(e)},lastIndexOf(...e){return zr(this,"lastIndexOf",e)},map(e,t){return _o(this,"map",e,t,void 0,arguments)},pop(){return Kn(this,"pop")},push(...e){return Kn(this,"push",e)},reduce(e,...t){return qi(this,"reduce",e,t)},reduceRight(e,...t){return qi(this,"reduceRight",e,t)},shift(){return Kn(this,"shift")},some(e,t){return _o(this,"some",e,t,void 0,arguments)},splice(...e){return Kn(this,"splice",e)},toReversed(){return dn(this).toReversed()},toSorted(e){return dn(this).toSorted(e)},toSpliced(...e){return dn(this).toSpliced(...e)},unshift(...e){return Kn(this,"unshift",e)},values(){return Kr(this,"values",e=>so(this,e))}};function Kr(e,t,o){const n=Ta(e),a=n[t]();return n!==e&&!Ut(e)&&(a._next=a.next,a.next=()=>{const r=a._next();return r.done||(r.value=o(r.value)),r}),a}const Dp=Array.prototype;function _o(e,t,o,n,a,r){const s=Ta(e),i=s!==e&&!Ut(e),l=s[t];if(l!==Dp[t]){const p=l.apply(e,r);return i?qt(p):p}let u=o;s!==e&&(i?u=function(p,g){return o.call(this,so(e,p),g,e)}:o.length>2&&(u=function(p,g){return o.call(this,p,g,e)}));const c=l.call(s,u,n);return i&&a?a(c):c}function qi(e,t,o,n){const a=Ta(e),r=a!==e&&!Ut(e);let s=o,i=!1;a!==e&&(r?(i=n.length===0,s=function(u,c,p){return i&&(i=!1,u=so(e,u)),o.call(this,u,so(e,c),p,e)}):o.length>3&&(s=function(u,c,p){return o.call(this,u,c,p,e)}));const l=a[t](s,...n);return i?so(e,l):l}function zr(e,t,o){const n=Be(e);Pt(n,"iterate",Un);const a=n[t](...o);return(a===-1||a===!1)&&Ia(o[0])?(o[0]=Be(o[0]),n[t](...o)):a}function Kn(e,t,o=[]){ao(),Fr();const n=Be(e)[t].apply(e,o);return Dr(),ro(),n}const Bp=Pr("__proto__,__v_isRef,__isVue"),Ji=new Set(Object.getOwnPropertyNames(Symbol).filter(e=>e!=="arguments"&&e!=="caller").map(e=>Symbol[e]).filter(Gt));function Gp(e){Gt(e)||(e=String(e));const t=Be(this);return Pt(t,"has",e),t.hasOwnProperty(e)}class Xi{constructor(t=!1,o=!1){this._isReadonly=t,this._isShallow=o}get(t,o,n){if(o==="__v_skip")return t.__v_skip;const a=this._isReadonly,r=this._isShallow;if(o==="__v_isReactive")return!a;if(o==="__v_isReadonly")return a;if(o==="__v_isShallow")return r;if(o==="__v_raw")return n===(a?r?nl:ol:r?tl:el).get(t)||Object.getPrototypeOf(t)===Object.getPrototypeOf(n)?t:void 0;const s=xe(t);if(!a){let l;if(s&&(l=Fp[o]))return l;if(o==="hasOwnProperty")return Gp}const i=Reflect.get(t,o,st(t)?t:n);if((Gt(o)?Ji.has(o):Bp(o))||(a||Pt(t,"get",o),r))return i;if(st(i)){const l=s&&xa(o)?i:i.value;return a&&Ye(l)?Vr(l):l}return Ye(i)?a?Vr(i):pn(i):i}}class Zi extends Xi{constructor(t=!1){super(!1,t)}set(t,o,n,a){let r=t[o];const s=xe(t)&&xa(o);if(!this._isShallow){const u=ko(r);if(!Ut(n)&&!ko(n)&&(r=Be(r),n=Be(n)),!s&&st(r)&&!st(n))return u||(r.value=n),!0}const i=s?Number(o)e,Pa=e=>Reflect.getPrototypeOf(e);function Hp(e,t,o){return function(...n){const a=this.__v_raw,r=Be(a),s=No(r),i=e==="entries"||e===Symbol.iterator&&s,l=e==="keys"&&s,u=a[e](...n),c=o?Hr:t?gn:qt;return!t&&Pt(r,"iterate",l?Ur:Yo),dt(Object.create(u),{next(){const{value:p,done:g}=u.next();return g?{value:p,done:g}:{value:i?[c(p[0]),c(p[1])]:c(p),done:g}}})}}function Ma(e){return function(...t){return e==="delete"?!1:e==="clear"?void 0:this}}function Vp(e,t){const o={get(a){const r=this.__v_raw,s=Be(r),i=Be(a);e||(no(a,i)&&Pt(s,"get",a),Pt(s,"get",i));const{has:l}=Pa(s),u=t?Hr:e?gn:qt;if(l.call(s,a))return u(r.get(a));if(l.call(s,i))return u(r.get(i));r!==s&&r.get(a)},get size(){const a=this.__v_raw;return!e&&Pt(Be(a),"iterate",Yo),a.size},has(a){const r=this.__v_raw,s=Be(r),i=Be(a);return e||(no(a,i)&&Pt(s,"has",a),Pt(s,"has",i)),a===i?r.has(a):r.has(a)||r.has(i)},forEach(a,r){const s=this,i=s.__v_raw,l=Be(i),u=t?Hr:e?gn:qt;return!e&&Pt(l,"iterate",Yo),i.forEach((c,p)=>a.call(r,u(c),u(p),s))}};return dt(o,e?{add:Ma("add"),set:Ma("set"),delete:Ma("delete"),clear:Ma("clear")}:{add(a){const r=Be(this),s=Pa(r),i=Be(a),l=!t&&!Ut(a)&&!ko(a)?i:a;return s.has.call(r,l)||no(a,l)&&s.has.call(r,a)||no(i,l)&&s.has.call(r,i)||(r.add(l),yo(r,"add",l,l)),this},set(a,r){!t&&!Ut(r)&&!ko(r)&&(r=Be(r));const s=Be(this),{has:i,get:l}=Pa(s);let u=i.call(s,a);u||(a=Be(a),u=i.call(s,a));const c=l.call(s,a);return s.set(a,r),u?no(r,c)&&yo(s,"set",a,r):yo(s,"add",a,r),this},delete(a){const r=Be(this),{has:s,get:i}=Pa(r);let l=s.call(r,a);l||(a=Be(a),l=s.call(r,a)),i&&i.call(r,a);const u=r.delete(a);return l&&yo(r,"delete",a,void 0),u},clear(){const a=Be(this),r=a.size!==0,s=a.clear();return r&&yo(a,"clear",void 0,void 0),s}}),["keys","values","entries",Symbol.iterator].forEach(a=>{o[a]=Hp(a,e,t)}),o}function Na(e,t){const o=Vp(e,t);return(n,a,r)=>a==="__v_isReactive"?!e:a==="__v_isReadonly"?e:a==="__v_raw"?n:Reflect.get(Ve(o,a)&&a in n?o:n,a,r)}const Wp={get:Na(!1,!1)},Yp={get:Na(!1,!0)},qp={get:Na(!0,!1)},Jp={get:Na(!0,!0)},el=new WeakMap,tl=new WeakMap,ol=new WeakMap,nl=new WeakMap;function Xp(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}function pn(e){return ko(e)?e:Ra(e,!1,jp,Wp,el)}function Zp(e){return Ra(e,!1,Kp,Yp,tl)}function Vr(e){return Ra(e,!0,Up,qp,ol)}function Yx(e){return Ra(e,!0,zp,Jp,nl)}function Ra(e,t,o,n,a){if(!Ye(e)||e.__v_raw&&!(t&&e.__v_isReactive)||e.__v_skip||!Object.isExtensible(e))return e;const r=a.get(e);if(r)return r;const s=Xp(xp(e));if(s===0)return e;const i=new Proxy(e,s===2?n:o);return a.set(e,i),i}function vo(e){return ko(e)?vo(e.__v_raw):!!(e&&e.__v_isReactive)}function ko(e){return!!(e&&e.__v_isReadonly)}function Ut(e){return!!(e&&e.__v_isShallow)}function Ia(e){return e?!!e.__v_raw:!1}function Be(e){const t=e&&e.__v_raw;return t?Be(t):e}function qo(e){return!Ve(e,"__v_skip")&&Object.isExtensible(e)&&Pi(e,"__v_skip",!0),e}const qt=e=>Ye(e)?pn(e):e,gn=e=>Ye(e)?Vr(e):e;function st(e){return e?e.__v_isRef===!0:!1}function de(e){return al(e,!1)}function fn(e){return al(e,!0)}function al(e,t){return st(e)?e:new Qp(e,t)}class Qp{constructor(t,o){this.dep=new jr,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=o?t:Be(t),this._value=o?t:qt(t),this.__v_isShallow=o}get value(){return this.dep.track(),this._value}set value(t){const o=this._rawValue,n=this.__v_isShallow||Ut(t)||ko(t);t=n?t:Be(t),no(t,o)&&(this._rawValue=t,this._value=n?t:qt(t),this.dep.trigger())}}function _e(e){return st(e)?e.value:e}const eg={get:(e,t,o)=>t==="__v_raw"?e:_e(Reflect.get(e,t,o)),set:(e,t,o,n)=>{const a=e[t];return st(a)&&!st(o)?(a.value=o,!0):Reflect.set(e,t,o,n)}};function rl(e){return vo(e)?e:new Proxy(e,eg)}function tg(e){const t=xe(e)?new Array(e.length):{};for(const o in e)t[o]=sl(e,o);return t}class og{constructor(t,o,n){this._object=t,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0,this._key=Gt(o)?o:String(o),this._raw=Be(t);let a=!0,r=t;if(!xe(t)||Gt(this._key)||!xa(this._key))do a=!Ia(r)||Ut(r);while(a&&(r=r.__v_raw));this._shallow=a}get value(){let t=this._object[this._key];return this._shallow&&(t=_e(t)),this._value=t===void 0?this._defaultValue:t}set value(t){if(this._shallow&&st(this._raw[this._key])){const o=this._object[this._key];if(st(o)){o.value=t;return}}this._object[this._key]=t}get dep(){return Op(this._raw,this._key)}}class ng{constructor(t){this._getter=t,this.__v_isRef=!0,this.__v_isReadonly=!0,this._value=void 0}get value(){return this._value=this._getter()}}function ag(e,t,o){return st(e)?e:$e(e)?new ng(e):Ye(e)&&arguments.length>1?sl(e,t,o):de(e)}function sl(e,t,o){return new og(e,t,o)}class rg{constructor(t,o,n){this.fn=t,this.setter=o,this._value=void 0,this.dep=new jr(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=jn-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!o,this.isSSR=n}notify(){if(this.flags|=16,!(this.flags&8)&&ot!==this)return Ui(this,!0),!0}get value(){const t=this.dep.track();return Hi(this),t&&(t.version=this.dep.version),this._value}set value(t){this.setter&&this.setter(t)}}function sg(e,t,o=!1){let n,a;return $e(e)?n=e:(n=e.get,a=e.set),new rg(n,a,o)}const Oa={},Fa=new WeakMap;let Jo;function ig(e,t=!1,o=Jo){if(o){let n=Fa.get(o);n||Fa.set(o,n=[]),n.push(e)}}function lg(e,t,o=et){const{immediate:n,deep:a,once:r,scheduler:s,augmentJob:i,call:l}=o,u=v=>a?v:Ut(v)||a===!1||a===0?wo(v,1):wo(v);let c,p,g,k,w=!1,_=!1;if(st(e)?(p=()=>e.value,w=Ut(e)):vo(e)?(p=()=>u(e),w=!0):xe(e)?(_=!0,w=e.some(v=>vo(v)||Ut(v)),p=()=>e.map(v=>{if(st(v))return v.value;if(vo(v))return u(v);if($e(v))return l?l(v,2):v()})):$e(e)?t?p=l?()=>l(e,2):e:p=()=>{if(g){ao();try{g()}finally{ro()}}const v=Jo;Jo=c;try{return l?l(e,3,[k]):e(k)}finally{Jo=v}}:p=oo,t&&a){const v=p,x=a===!0?1/0:a;p=()=>wo(v(),x)}const d=Bi(),h=()=>{c.stop(),d&&d.active&&Mr(d.effects,c)};if(r&&t){const v=t;t=(...x)=>{const A=v(...x);return h(),A}}let f=_?new Array(e.length).fill(Oa):Oa;const m=v=>{if(!(!(c.flags&1)||!c.dirty&&!v))if(t){const x=c.run();if(v||a||w||(_?x.some((A,S)=>no(A,f[S])):no(x,f))){g&&g();const A=Jo;Jo=c;try{const S=[x,f===Oa?void 0:_&&f[0]===Oa?[]:f,k];f=x,l?l(t,3,S):t(...S)}finally{Jo=A}}}else c.run()};return i&&i(m),c=new Gi(p),c.scheduler=s?()=>s(m,!1):m,k=v=>ig(v,!1,c),g=c.onStop=()=>{const v=Fa.get(c);if(v){if(l)l(v,4);else for(const x of v)x();Fa.delete(c)}},t?n?m(!0):f=c.run():s?s(m.bind(null,!0),!0):c.run(),h.pause=c.pause.bind(c),h.resume=c.resume.bind(c),h.stop=h,h}function wo(e,t=1/0,o){if(t<=0||!Ye(e)||e.__v_skip||(o=o||new Map,(o.get(e)||0)>=t))return e;if(o.set(e,t),t--,st(e))wo(e.value,t,o);else if(xe(e))for(let n=0;n{wo(n,t,o)});else if(wa(e)){for(const n in e)wo(e[n],t,o);for(const n of Object.getOwnPropertySymbols(e))Object.prototype.propertyIsEnumerable.call(e,n)&&wo(e[n],t,o)}return e}/**
* @vue/runtime-core v3.5.42
* (c) 2018-present Yuxi (Evan) You and Vue contributors
* @license MIT
-**/const zn=[];let Wr=!1;function Ox(e,...t){if(Wr)return;Wr=!0,ao();const o=zn.length?zn[zn.length-1].component:null,n=o&&o.appContext.config.warnHandler,a=Qp();if(n)hn(n,o,11,[e+t.map(r=>{var s,i;return(i=(s=r.toString)==null?void 0:s.call(r))!=null?i:JSON.stringify(r)}).join(""),o&&o.proxy,a.map(({vnode:r})=>`at <${sc(o,r.type)}>`).join(`
+**/const zn=[];let Wr=!1;function qx(e,...t){if(Wr)return;Wr=!0,ao();const o=zn.length?zn[zn.length-1].component:null,n=o&&o.appContext.config.warnHandler,a=cg();if(n)hn(n,o,11,[e+t.map(r=>{var s,i;return(i=(s=r.toString)==null?void 0:s.call(r))!=null?i:JSON.stringify(r)}).join(""),o&&o.proxy,a.map(({vnode:r})=>`at <${sc(o,r.type)}>`).join(`
`),a]);else{const r=[`[Vue warn]: ${e}`,...t];a.length&&r.push(`
-`,...eg(a)),console.warn(...r)}ro(),Wr=!1}function Qp(){let e=zn[zn.length-1];if(!e)return[];const t=[];for(;e;){const o=t[0];o&&o.vnode===e?o.recurseCount++:t.push({vnode:e,recurseCount:0});const n=e.component&&e.component.parent;e=n&&n.vnode}return t}function eg(e){const t=[];return e.forEach((o,n)=>{t.push(...n===0?[]:[`
-`],...tg(o))}),t}function tg({vnode:e,recurseCount:t}){const o=t>0?`... (${t} recursive calls)`:"",n=e.component?e.component.parent==null:!1,a=` at <${sc(e.component,e.type,n)}`,r=">"+o;return e.props?[a,...og(e.props),r]:[a+r]}function og(e){const t=[],o=Object.keys(e);return o.slice(0,3).forEach(n=>{t.push(...il(n,e[n]))}),o.length>3&&t.push(" ..."),t}function il(e,t,o){return Qe(t)?(t=JSON.stringify(t),o?t:[`${e}=${t}`]):typeof t=="number"||typeof t=="boolean"||t==null?o?t:[`${e}=${t}`]:at(t)?(t=il(e,Oe(t.value),!0),o?t:[`${e}=Ref<`,t,">"]):Ae(t)?[`${e}=fn${t.name?`<${t.name}>`:""}`]:(t=Oe(t),o?t:[`${e}=`,t])}function hn(e,t,o,n){try{return n?e(...n):e()}catch(a){Hn(a,t,o)}}function Jt(e,t,o,n){if(Ae(e)){const a=hn(e,t,o,n);return a&&Li(a)&&a.catch(r=>{Hn(r,t,o)}),a}if(we(e)){const a=[];for(let r=0;r>>1,a=Nt[n],r=Vn(a);r=Vn(o)?Nt.push(e):Nt.splice(ag(t),0,e),e.flags|=1,cl()}}function cl(){Da||(Da=ll.then(pl))}function rg(e){if(!we(e))Ro&&e.id===-1?Ro.splice(bn+1,0,e):e.flags&1||(mn.push(e),e.flags|=1);else for(let t=0;tVn(o)-Vn(n));if(mn.length=0,Ro){for(let o=0;oe.id==null?e.flags&2?-1:1/0:e.id;function pl(e){try{for(io=0;io{n._d&&Ja(-1);const r=Ba(t),s=$o.length;let i;try{i=e(...a)}finally{for(let l=$o.length;l>s;l--)us();Ba(r),n._d&&Ja(1)}return i};return n._n=!0,n._c=!0,n._d=!0,n}function fl(e,t){if(At===null)return e;const o=er(At),n=e.dirs||(e.dirs=[]);for(let a=0;a1)return o&&Ae(t)?t.call(n&&n.proxy):t}}function sg(){return!!(Ao()||Qo)}const ig=Symbol.for("v-scx"),lg=()=>Ie(ig);function cg(e,t){return qr(e,null,t)}function lt(e,t,o){return qr(e,t,o)}function qr(e,t,o=Xe){const{immediate:n,deep:a,flush:r,once:s}=o,i=ut({},o),l=t&&n||!t&&r!=="post";let c;if(kn){if(r==="sync"){const w=lg();c=w.__watcherHandles||(w.__watcherHandles=[])}else if(!l){const w=()=>{};return w.stop=oo,w.resume=oo,w.pause=oo,w}}const u=Ct;i.call=(w,x,y)=>Jt(w,u,x,y);let p=!1;r==="post"?i.scheduler=w=>{Rt(w,u&&u.suspense)}:r!=="sync"&&(p=!0,i.scheduler=(w,x)=>{x?w():Yr(w)}),i.augmentJob=w=>{t&&(w.flags|=4),p&&(w.flags|=2,u&&(w.id=u.uid,w.i=u))};const f=Zp(e,t,i);return kn&&(c?c.push(f):l&&f()),f}function ug(e,t,o){const n=this.proxy,a=Qe(e)?e.includes(".")?hl(n,e):()=>n[e]:e.bind(n,n);let r;Ae(t)?r=t:(r=t.handler,o=t);const s=na(this),i=qr(a,r.bind(n),o);return s(),i}function hl(e,t){const o=t.split(".");return()=>{let n=e;for(let a=0;ae.__isTeleport,Zo=e=>e&&(e.disabled||e.disabled===""),dg=e=>e&&(e.defer||e.defer===""),bl=e=>typeof SVGElement<"u"&&e instanceof SVGElement,yl=e=>typeof MathMLElement=="function"&&e instanceof MathMLElement,Jr=(e,t)=>{const o=e&&e.to;return Qe(o)?t?t(o):null:o},pg={name:"Teleport",__isTeleport:!0,process(e,t,o,n,a,r,s,i,l,c){const{mc:u,pc:p,pbc:f,o:{insert:w,querySelector:x,createText:y,createComment:d,parentNode:g}}=c,b=Zo(t.props);let{dynamicChildren:h}=t;const k=(C,T,R)=>{C.shapeFlag&16&&u(C.children,T,R,a,r,s,i,l)},$=(C=t)=>{const T=Zo(C.props),R=C.target=Jr(C.props,x),H=Xr(R,C,y,w);R&&(s!=="svg"&&bl(R)?s="svg":s!=="mathml"&&yl(R)&&(s="mathml"),a&&a.isCE&&(a.ce._teleportTargets||(a.ce._teleportTargets=new Set)).add(R),T||(k(C,R,H),Wn(C,!1)))},v=C=>{const T=()=>{if(Fo.get(C)===T){if(Fo.delete(C),Zo(C.props)){const R=g(C.el)||o;k(C,R,C.anchor),Wn(C,!0)}$(C)}};Fo.set(C,T),Rt(T,r)};if(e==null){const C=t.el=y(""),T=t.anchor=y("");if(w(C,o,n),w(T,o,n),dg(t.props)||r&&r.pendingBranch){v(t);return}b&&(k(t,o,T),Wn(t,!0)),$()}else{t.el=e.el;const C=t.anchor=e.anchor,T=Fo.get(e);if(T){T.flags|=8,Fo.delete(e),v(t);return}t.targetStart=e.targetStart;const R=t.target=e.target,H=t.targetAnchor=e.targetAnchor,Y=Zo(e.props),ae=Y?o:R,P=Y?C:H;if(s==="svg"||bl(R)?s="svg":(s==="mathml"||yl(R))&&(s="mathml"),h?(f(e.dynamicChildren,h,ae,a,r,s,i),cs(e,t,!0)):l||p(e,t,ae,P,a,r,s,i,!1),b)Y?t.props&&e.props&&t.props.to!==e.props.to&&(t.props.to=e.props.to):ja(t,o,C,c,1);else if((t.props&&t.props.to)!==(e.props&&e.props.to)){const A=Jr(t.props,x);A&&(t.target=A,ja(t,A,null,c,0))}else Y&&ja(t,R,H,c,1);Wn(t,b)}},remove(e,t,o,{um:n,o:{remove:a}},r){const{shapeFlag:s,children:i,anchor:l,targetStart:c,targetAnchor:u,target:p,props:f}=e,w=Zo(f),x=r||!w,y=Fo.get(e);if(y&&(y.flags|=8,Fo.delete(e)),p&&(a(c),a(u)),r&&a(l),!y&&(w||p)&&s&16)for(let d=0;d1){for(const o of e)if(o.type!==po){t=o;break}}return t}function _l(e){if(!za(e))return Ga(e.type)&&e.children?hg(e.children):e;if(e.component)return e.component.subTree;const{shapeFlag:t,children:o}=e;if(o){if(t&16)return o[0];if(t&32&&Ae(o.default))return o.default()}}function Qr(e,t){if(e.shapeFlag&6&&e.component){e.transition=t;const o=e.component.subTree;Qr(Ga(o.type)&&_l(o)||o,t)}else e.shapeFlag&128?(e.ssContent.transition=t.clone(e.ssContent),e.ssFallback.transition=t.clone(e.ssFallback)):e.transition=t}function Yn(e,t){return Ae(e)?ut({name:e.name},t,{setup:e}):e}function es(e){e.ids=[e.ids[0]+e.ids[2]+++"-",0,0]}function vl(e,t){let o;return!!((o=Object.getOwnPropertyDescriptor(e,t))&&!o.configurable)}const Ua=new WeakMap;function qn(e,t,o,n,a=!1){if(we(e)){e.forEach((y,d)=>qn(y,t&&(we(t)?t[d]:t),o,n,a));return}if(yn(n)&&!a){n.shapeFlag&512&&n.type.__asyncResolved&&n.component.subTree.component&&qn(e,t,o,n.component.subTree);return}const r=n.shapeFlag&4?er(n.component):n.el,s=a?null:r,{i,r:l}=e,c=t&&t.r,u=i.refs===Xe?i.refs={}:i.refs,p=i.setupState,f=Oe(p),w=p===Xe?Ei:y=>vl(u,y)?!1:ze(f,y),x=(y,d)=>!(d&&vl(u,d));if(c!=null&&c!==l){if(kl(t),Qe(c))u[c]=null,w(c)&&(p[c]=null);else if(at(c)){const y=t;x(c,y.k)&&(c.value=null),y.k&&(u[y.k]=null)}}if(Ae(l))hn(l,i,12,[s,u]);else{const y=Qe(l),d=at(l);if(y||d){const g=()=>{if(e.f){const b=y?w(l)?p[l]:u[l]:x()||!e.k?l.value:u[e.k];if(a)we(b)&&Mr(b,r);else if(we(b))b.includes(r)||b.push(r);else if(y)u[l]=[r],w(l)&&(p[l]=u[l]);else{const h=[r];x(l,e.k)&&(l.value=h),e.k&&(u[e.k]=h)}}else y?(u[l]=s,w(l)&&(p[l]=s)):d&&(x(l,e.k)&&(l.value=s),e.k&&(u[e.k]=s))};if(s){const b=()=>{g(),Ua.delete(e)};b.id=-1,Ua.set(e,b),Rt(b,o)}else kl(e),g()}}}function kl(e){const t=Ua.get(e);t&&(t.flags|=8,Ua.delete(e))}const wl=e=>e.nodeType===8;Ca().requestIdleCallback,Ca().cancelIdleCallback;function mg(e,t){if(wl(e)&&e.data==="["){let o=1,n=e.nextSibling;for(;n;){if(n.nodeType===1){if(t(n)===!1)break}else if(wl(n))if(n.data==="]"){if(--o===0)break}else n.data==="["&&o++;n=n.nextSibling}}else t(e)}const yn=e=>!!e.type.__asyncLoader;function bg(e){Ae(e)&&(e={loader:e});const{loader:t,loadingComponent:o,errorComponent:n,delay:a=200,hydrate:r,timeout:s,suspensible:i=!0,onError:l}=e;let c=null,u,p=0;const f=()=>(p++,c=null,w()),w=()=>{let x;return c||(x=c=t().catch(y=>{if(y=y instanceof Error?y:new Error(String(y)),l)return new Promise((d,g)=>{l(y,()=>d(f()),()=>g(y),p+1)});throw y}).then(y=>x!==c&&c?c:(y&&(y.__esModule||y[Symbol.toStringTag]==="Module")&&(y=y.default),u=y,y)))};return Yn({name:"AsyncComponentWrapper",__asyncLoader:w,__asyncHydrate(x,y,d){const g=x.isConnected;let b=!1;(y.bu||(y.bu=[])).push(()=>b=!0);const h=()=>{b||!x.parentNode||g&&!x.isConnected||d()},k=r?()=>{const $=r(h,v=>mg(x,v));$&&(y.bum||(y.bum=[])).push($)}:h;u?k():w().then(()=>!y.isUnmounted&&k())},get __asyncResolved(){return u},setup(){const x=Ct;if(es(x),u)return()=>Ka(u,x);const y=$=>{c=null,Hn($,x,13,!n)};if(i&&x.suspense||kn)return w().then($=>()=>Ka($,x)).catch($=>(y($),()=>n?He(n,{error:$}):null));const d=de(!1),g=de(),b=de(!!a);let h,k;return Va(()=>{h!=null&&clearTimeout(h),k!=null&&clearTimeout(k)}),a&&(k=setTimeout(()=>{x.isUnmounted||(b.value=!1)},a)),s!=null&&(h=setTimeout(()=>{if(!x.isUnmounted&&!d.value&&!g.value){const $=new Error(`Async component timed out after ${s}ms.`);y($),g.value=$}},s)),w().then(()=>{x.isUnmounted||(d.value=!0,x.parent&&za(x.parent.vnode)&&x.parent.update())}).catch($=>{if(x.isUnmounted){c=null;return}y($),g.value=$}),()=>{if(d.value&&u)return Ka(u,x);if(g.value&&n)return He(n,{error:g.value});if(o&&!b.value)return Ka(o,x)}}})}function Ka(e,t){const{ref:o,props:n,children:a,ce:r}=t.vnode,s=He(e,n,a);return s.ref=o,s.ce=r,delete t.vnode.ce,s}const za=e=>e.type.__isKeepAlive;function yg(e,t){xl(e,"a",t)}function _g(e,t){xl(e,"da",t)}function xl(e,t,o=Ct){const n=e.__wdc||(e.__wdc=()=>{let a=o;for(;a;){if(a.isDeactivated)return;a=a.parent}return e()});if(Ha(t,n,o),o){let a=o.parent;for(;a&&a.parent;)za(a.parent.vnode)&&vg(n,t,o,a),a=a.parent}}function vg(e,t,o,n){const a=Ha(t,e,n,!0);Va(()=>{Mr(n[t],a)},o)}function Ha(e,t,o=Ct,n=!1){if(o){const a=o[e]||(o[e]=[]),r=t.__weh||(t.__weh=(...s)=>{ao();const i=na(o),l=Jt(t,o,e,s);return i(),ro(),l});return n?a.unshift(r):a.push(r),r}}const xo=e=>(t,o=Ct)=>{(!kn||e==="sp")&&Ha(e,(...n)=>t(...n),o)},$l=xo("bm"),co=xo("m"),kg=xo("bu"),wg=xo("u"),uo=xo("bum"),Va=xo("um"),xg=xo("sp"),$g=xo("rtg"),Sg=xo("rtc");function Ag(e,t=Ct){Ha("ec",e,t)}const Eg="components",Sl=Symbol.for("v-ndc");function Al(e){return Qe(e)?Cg(Eg,e,!1)||e:e||Sl}function Cg(e,t,o=!0,n=!1){const a=At||Ct;if(a){const r=a.type;{const i=rc(r,!1);if(i&&(i===t||i===_t(t)||i===Sa(_t(t))))return r}const s=El(a[e]||r[e],t)||El(a.appContext[e],t);return!s&&n?r:s}}function El(e,t){return e&&(e[t]||e[_t(t)]||e[Sa(_t(t))])}function Et(e,t,o,n){let a;const r=o,s=we(e);if(s||Qe(e)){const i=s&&vo(e);let l=!1,c=!1;i&&(l=!Ut(e),c=ko(e),e=Ta(e)),a=new Array(e.length);for(let u=0,p=e.length;u