diff --git a/README.md b/README.md index 07ce84032..f66904ad5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,24 @@ # doxa-maps — one build, maps that embed everywhere +> **The map cruncher.** Many hand-built maps go in; one shared library, one repo and one build +> come out — and every map fans out to five deployment targets from the same artifacts. +> +> ```text +> map · map · map · map · map · map · map (7 production maps, once 7 codebases) +> \ \ | | | / / +> +----- plugin-designs/ ------+ one folder per plugin, free zone +> | +> library/ (@map) shared, parameterized, high-bar zone +> | +> ONE bun run build +> | +> doxa-maps/ (drop-anywhere tree: bundles + leaf pages + manifest.json) +> / | \ \ \ +> iframe JS-embed mobile WordPress desktop +> (proven) (proven) (built) (built) (built) +> ``` + + **A Vite bundler that turns each map into a self-contained web component.** Write a map in `plugin-designs//`, run one build, and get a folder you can drop on any host — iframe it, script-embed it, wrap it as a mobile app, or open every map at once as one SPA. @@ -12,18 +31,20 @@ This README has three parts, in the order you need them: --- -## 1. The 5 plugin capabilities +## 1. The 5 plugin capabilities — most advanced first -A "capability" is **how much a plugin nests** — not how hard it is. Every screenshot below is a real -bundle from this repo's build output. +A "capability" is **how much a plugin nests** — not how hard it is to use. The tour below starts from +the most advanced thing this repo produces (a whole app of apps) and walks down to the simplest +(one element), because the top of the ladder is the point: every rung below it stays independently +embeddable. Every screenshot is a real bundle from this repo's build output. | # | Capability | What it is | Example bundle | |---|------------|------------|----------------| -| 1 | **Single element** | One folder → one bundle → one custom element. | `all-people-groups` | -| 2 | **Prop-table element** | One `root.vue` + a preset table → many maps from one plugin. | `doxa-simple-map` | -| 3 | **Monolithic element** | One `root.vue` nests many views internally → one element. | `doxa-research-map` | -| 4 | **Nested-element SPA** | A composite that nests *other plugins* and wires them with props/events. | `big-picture` | | 5 | **Nested-SPA / app shell** | Many SPAs nested into one deep-linkable app. | `doxa-maps-spa` | +| 4 | **Nested-element SPA** | A composite that nests *other plugins* and wires them with props/events. | `big-picture` | +| 3 | **Monolithic element** | One `root.vue` nests many views internally → one element. | `doxa-research-map` | +| 2 | **Prop-table element** | One `root.vue` + a preset table → many maps from one plugin. | `doxa-simple-map` | +| 1 | **Single element** | One folder → one bundle → one custom element. | `all-people-groups` | > The tier is decided by the files, not by how simple a folder looks: an `*.instances.json` makes a > bundle **cap-2** however light it is, and a `meta.json` `type` declares tiers 3/4/5. The authoritative, @@ -31,38 +52,7 @@ bundle from this repo's build output. > **[documentation/reference-capability-exemplars.md](./documentation/reference-capability-exemplars.md)** > — cite that file rather than remembering. -### Capability 1 — single element -One folder, one `.vue`, one embeddable element. `plugin-designs/all-people-groups/` shows every people -group on one map. - -![Capability 1 — a single-element plugin (all-people-groups)](documentation/readme-images/cap1-single-element.png) - -### Capability 2 — prop-table element - -One `root.vue`, many maps. `plugin-designs/doxa-simple-map/` ships a preset prop-table -(`simple-map.instances.json`) that renders it three ways — each a standalone embeddable page from the -**same** code. - -| Prayer | Engagement | Adoption | -|--------|------------|----------| -| ![Capability 2 — prayer preset (doxa-simple-map)](documentation/readme-images/cap2-prayer.png) | ![Capability 2 — engagement preset (doxa-simple-map)](documentation/readme-images/cap2-engagement.png) | ![Capability 2 — adoption preset (doxa-simple-map)](documentation/readme-images/cap2-adoption.png) | - -### Capability 3 — monolithic element - -A whole multi-view map from one `root.vue`. The trade-off: you cannot embed just one of its tabs. - -![Capability 3 — the monolithic research map (doxa-research-map)](documentation/readme-images/cap3-monolithic-research.png) - -### Capability 4 — nested-element SPA - -A composite that nests **lower-tier plugins** and wires them with props and events. Each inner plugin -stays independently embeddable, and the composite is embeddable too. `plugin-designs/my-upg-100-list/` -ships three leaves: - -| Composite (`upg-100-mfe`) | List (`upg-100-list`) | Poster wall (`poster-wall`) | -|---------------------------|-----------------------|-----------------------------| -| ![Capability 4 — the UPG-100 composite (my-upg-100-list)](documentation/readme-images/cap4-upg100-mfe.png) | ![UPG-100 list leaf](documentation/readme-images/cap4-upg100-list.png) | ![UPG-100 poster-wall leaf](documentation/readme-images/cap4-upg100-poster-wall.png) | ### Capability 5 — nested-SPA / app shell @@ -79,6 +69,39 @@ studio without any wiring. This is [section 3](#3-the-plugin-studio). ![Capability 5 — the developer studio (plugin-studio)](documentation/readme-images/cap5-plugin-studio.png) +### Capability 4 — nested-element SPA + +A composite that nests **lower-tier plugins** and wires them with props and events. Each inner plugin +stays independently embeddable, and the composite is embeddable too. `plugin-designs/my-upg-100-list/` +ships three leaves: + +| Composite (`upg-100-mfe`) | List (`upg-100-list`) | Poster wall (`poster-wall`) | +|---------------------------|-----------------------|-----------------------------| +| ![Capability 4 — the UPG-100 composite (my-upg-100-list)](documentation/readme-images/cap4-upg100-mfe.png) | ![UPG-100 list leaf](documentation/readme-images/cap4-upg100-list.png) | ![UPG-100 poster-wall leaf](documentation/readme-images/cap4-upg100-poster-wall.png) | + +### Capability 3 — monolithic element + +A whole multi-view map from one `root.vue`. The trade-off: you cannot embed just one of its tabs. + +![Capability 3 — the monolithic research map (doxa-research-map)](documentation/readme-images/cap3-monolithic-research.png) + +### Capability 2 — prop-table element + +One `root.vue`, many maps. `plugin-designs/doxa-simple-map/` ships a preset prop-table +(`simple-map.instances.json`) that renders it three ways — each a standalone embeddable page from the +**same** code. + +| Prayer | Engagement | Adoption | +|--------|------------|----------| +| ![Capability 2 — prayer preset (doxa-simple-map)](documentation/readme-images/cap2-prayer.png) | ![Capability 2 — engagement preset (doxa-simple-map)](documentation/readme-images/cap2-engagement.png) | ![Capability 2 — adoption preset (doxa-simple-map)](documentation/readme-images/cap2-adoption.png) | + +### Capability 1 — single element + +One folder, one `.vue`, one embeddable element. `plugin-designs/all-people-groups/` shows every people +group on one map. + +![Capability 1 — a single-element plugin (all-people-groups)](documentation/readme-images/cap1-single-element.png) + --- ## 2. The 5 deployment methods @@ -100,6 +123,17 @@ Android SDK for method 2, a live WordPress for method 4, and a GUI (this box is Methods **1 and 3 are the two proven-live paths.** If you need a map on a site today, use one of those. +Both were **certified cross-origin on 2026-09-24** — the entire capability-5 app, embedded on a +foreign host page, every inner plugin riding along (prove-the-whole = prove-the-parts): + +| Method 1 — iframe on a host site | Method 3 — JS embed on a host site | +|----------------------------------|-------------------------------------| +| ![The cap-5 app inside a host page iframe](documentation/readme-images/deploy1-iframe-hostsite.png) | ![The cap-5 app JS-embedded on a host page](documentation/readme-images/deploy3-js-embed-hostsite.png) | + +> For a JS embed on another site the host page sets `window.MAP_TOKEN` (nested elements read it) +> and the serving layer must send `Access-Control-Allow-Origin: *` — the tree is fetched +> cross-origin by `manifest.json`-composed surfaces. + The full per-method command matrix, with every prerequisite, is in **[documentation/README-SECTION-3-cli-commands.md](./documentation/README-SECTION-3-cli-commands.md)**. @@ -138,6 +172,29 @@ runs on it yet). --- +## The repo at a glance + +```text +doxa-maps/ +├── plugin-designs/ YOUR maps live here — one folder per plugin (the FREE zone). +│ ├── doxa-maps-spa/ the capability-5 app (nests everything below) +│ ├── dashboard-plus-grid/ capability-4 composite: dashboard + people-groups grid +│ ├── my-upg-100-list/ capability-4: UPG-100 lists + poster wall + map +│ ├── doxa-simple-map/ capability-2: one root.vue, three preset maps +│ └── template-bundle/ copy this to start a new plugin +├── library/ the SHARED zone (@map) — reusable, parameterized only. +│ │ A change here reaches EVERY bundle: high bar, see +│ │ documentation/contributing/. +│ ├── components/ map controls, legends, search, poster, detail panels +│ ├── composables/ useMapInstance, shadow styles, print-in-place, retry +│ └── styles/ colors/ data/ style registry, palettes, lookup tables +├── data/ the public datasets the maps ship with +├── documentation/ tutorial · how-to · reference · explanation + architecture +├── internal/ the build generator + the verification gates +└── public/doxa-maps/ the BUILD OUTPUT — the drop-anywhere tree (bundles, + leaf pages, manifest.json). What gets served + embedded. +``` + ## 3. The Plugin Studio ★ **This is where you work — and the keyword is INTEROPERABILITY.** diff --git a/documentation/readme-images/cap2-prayer.png b/documentation/readme-images/cap2-prayer.png index 2be464a59..bf095c0dd 100644 Binary files a/documentation/readme-images/cap2-prayer.png and b/documentation/readme-images/cap2-prayer.png differ diff --git a/documentation/readme-images/cap3-monolithic-research.png b/documentation/readme-images/cap3-monolithic-research.png index be560a546..c36a39b65 100644 Binary files a/documentation/readme-images/cap3-monolithic-research.png and b/documentation/readme-images/cap3-monolithic-research.png differ diff --git a/documentation/readme-images/cap4-upg100-mfe.png b/documentation/readme-images/cap4-upg100-mfe.png index 4c9dc9721..568b0d30e 100644 Binary files a/documentation/readme-images/cap4-upg100-mfe.png and b/documentation/readme-images/cap4-upg100-mfe.png differ diff --git a/documentation/readme-images/cap5-doxa-maps-spa.png b/documentation/readme-images/cap5-doxa-maps-spa.png index ed6df98c7..bd8a9c094 100644 Binary files a/documentation/readme-images/cap5-doxa-maps-spa.png and b/documentation/readme-images/cap5-doxa-maps-spa.png differ diff --git a/documentation/readme-images/deploy1-iframe-hostsite.png b/documentation/readme-images/deploy1-iframe-hostsite.png new file mode 100644 index 000000000..bd881d690 Binary files /dev/null and b/documentation/readme-images/deploy1-iframe-hostsite.png differ diff --git a/documentation/readme-images/deploy3-js-embed-hostsite.png b/documentation/readme-images/deploy3-js-embed-hostsite.png new file mode 100644 index 000000000..62087a63f Binary files /dev/null and b/documentation/readme-images/deploy3-js-embed-hostsite.png differ diff --git a/documentation/reference-library-index.md b/documentation/reference-library-index.md index 1059dfdc7..25df6f818 100644 --- a/documentation/reference-library-index.md +++ b/documentation/reference-library-index.md @@ -107,10 +107,16 @@ and a change here can break other maps. `import … from '@map/composables/poster/useMapPoster.js'` - **usePosterLayout** — usePosterLayout — slot positioning math for poster compositions. `import … from '@map/composables/poster/usePosterLayout.js'` +- **printInPlace** — printInPlace.js — SHARED (@map/composables/printInPlace.js). + `import … from '@map/composables/printInPlace.js'` +- **retryableAsyncComponent** — retryableAsyncComponent.js — SHARED (@map/composables/retryableAsyncComponent.js). + `import … from '@map/composables/retryableAsyncComponent.js'` - **useAvatarGenerator** — Avatar Generator Composable `import … from '@map/composables/useAvatarGenerator.js'` - **useCountryOutline** — useCountryOutline.js — country boundary OUTLINE overlay on country search. `import … from '@map/composables/useCountryOutline.js'` +- **useDocumentFont** — useDocumentFont.js — register a self-hosted @font-face at DOCUMENT level, once per page. + `import … from '@map/composables/useDocumentFont.js'` - **useEdgeScrollAffordance** — useEdgeScrollAffordance — the gray "you can scroll past this" edge strips. `import … from '@map/composables/useEdgeScrollAffordance.js'` - **useGeocoderSearch** — Shared geocoder→map search logic (kind-switch + Mapbox filter builder + the @@ -149,6 +155,8 @@ and a change here can break other maps. `import … from '@map/composables/useSelectedPin.js'` - **useShadowStyles** — useShadowStyles.js — Shadow DOM Style Injection `import … from '@map/composables/useShadowStyles.js'` +- **useSharedMapToken** — useSharedMapToken.js — ONE page, ONE map token, shared by every element on it. + `import … from '@map/composables/useSharedMapToken.js'` ### Utilities (pure helper functions) diff --git a/internal/check-packet-render.mjs b/internal/check-packet-render.mjs new file mode 100644 index 000000000..60a3df5db --- /dev/null +++ b/internal/check-packet-render.mjs @@ -0,0 +1,126 @@ +// check-packet-render — the UPG-100 prayer packet must RENDER, not merely generate. +// +// Every bug this gate exists for produced a file of the right size, with no error, no +// broken image, and a generator that reported success. They were only visible at the +// RENDERED layer, which is why this drives a real browser and reads computed style. +// +// ★ THE LAW THIS GATE ENCODES: never print a number without asserting on it. An earlier +// version of this probe PRINTED "PDF pages: 16" for several runs while reporting +// "✓ packet verified", because the failure counter never looked at that value. A number +// that is displayed but not asserted on is decoration, not verification. +// +// What it catches, each measured on a real regression: +// 1. PAGE COUNT. transform:scale() is PAINT-time, so a scaled sheet still RESERVES its +// full layout box and the paginator splits it — 8 sheets came out as 16 PDF pages, +// inking only the top of each. (zoom is LAYOUT-time and paginates correctly.) +// The packet is printed front-and-back, so an exact page count is load-bearing. +// 2. MAP PALETTE. The map SVG carries almost no presentation attributes; it is styled +// from the poster document's stylesheet. Lift the without those rules and SVG +// `fill` falls back to its INITIAL value, which is BLACK — 670 elements painted solid +// black with no error of any kind. Asserted as a WHOLE-MAP property, because the first +// attempt at this check tested one hand-picked (the one just repaired) and went +// green while 647 others were still black. +// 3. Content the Driver specified explicitly: Mark 5:1-20 in full, 12 wheel segments, +// the prayer rhythms, and exactly two minimal metacamp.org attributions. +// +// Usage: node internal/check-packet-render.mjs [path/to/packet.html] +// Default path is overridable with PACKET_HTML so this is not machine-specific. + +import { chromium } from 'playwright' +import { existsSync } from 'node:fs' +import { resolve } from 'node:path' +import { pathToFileURL } from 'node:url' + +const target = process.argv[2] || process.env.PACKET_HTML +if (!target) { + console.log('PACKET-RENDER: SKIP — no packet given (pass a path or set PACKET_HTML).') + console.log(' The packet is generated on demand, so this gate is opt-in rather than') + console.log(' failing a build on a file that is not expected to exist.') + process.exit(0) +} +const file = resolve(target) +if (!existsSync(file)) { + console.error(`PACKET-RENDER: FAIL — no such packet: ${file}`) + process.exit(1) +} + +const browser = await chromium.launch() +const page = await browser.newPage({ viewport: { width: 1500, height: 1941 } }) +const errs = [] +page.on('pageerror', (e) => errs.push(String(e).slice(0, 120))) +await page.goto(pathToFileURL(file).href, { waitUntil: 'networkidle' }) + +const r = await page.evaluate(() => { + const body = document.body.innerText + const imgs = [...document.querySelectorAll('.card img')] + const RHYTHMS = ['PRAISE', 'WAIT', 'CONFESS', 'READ THE WORD', 'ASK', 'INTERCESSION', + 'PRAY THE WORD', 'THANK', 'SING', 'MEDITATE', 'LISTEN'] + + // Whole-map property, NOT one sampled element — see header note 2. + const mapFaults = (() => { + const svg = document.querySelector('.sheet svg') + if (!svg) return ['no map svg found'] + const faults = [] + const names = [...new Set((svg.outerHTML.match(/var\((--[a-z0-9-]+)/g) || []).map((v) => v.slice(4)))] + const cs = getComputedStyle(svg) + for (const n of names) if (!cs.getPropertyValue(n).trim()) faults.push(`unresolved ${n}`) + const all = [...svg.querySelectorAll('path,circle,text,rect,line')] + const black = all.filter((e) => getComputedStyle(e).fill === 'rgb(0, 0, 0)') + if (all.length && black.length > all.length * 0.25) { + faults.push(`${black.length}/${all.length} map elements fill BLACK`) + } + return faults + })() + + return { + sheets: document.querySelectorAll('.sheet').length, + cards: document.querySelectorAll('.card').length, + imgs: imgs.length, + broken: imgs.filter((i) => !i.complete || i.naturalWidth === 0).length, + nophoto: document.querySelectorAll('.nophoto').length, + rhythms: RHYTHMS.filter((w) => body.includes(w)).length, + rhythmsTotal: RHYTHMS.length, + wheelSegs: document.querySelectorAll('svg path[fill^="hsl"]').length, + hasMark5: body.includes('Gadarenes'), // a word unique to Mark 5:1-20 + metacamp: (body.match(/metacamp\.org/g) || []).length, + mapFaults, + } +}) + +const pdf = await page.pdf({ preferCSSPageSize: true }) +await browser.close() + +const raw = pdf.toString('latin1') +const boxes = [...raw.matchAll(/MediaBox\s*\[\s*0\s+0\s+([\d.]+)\s+([\d.]+)\s*\]/g)].map((m) => [+m[1], +m[2]]) +const pages = (raw.match(/\/Type\s*\/Page[^s]/g) || []).length +const landscape = boxes.filter(([w, h]) => h <= w).length + +// Counts are RELATIVE (sheets vs pages, cards vs photos) so this gate is not pinned to +// one zone's group count — a per-zone packet has a different number of both. +const fails = [] +if (!r.sheets) fails.push('no .sheet elements — the packet did not render') +if (!r.cards) fails.push('no people-group cards') +if (r.broken) fails.push(`${r.broken} broken photo(s)`) +if (r.wheelSegs !== 12) fails.push(`prayer wheel has ${r.wheelSegs} segments, expected 12`) +if (r.rhythms !== r.rhythmsTotal) fails.push(`only ${r.rhythms}/${r.rhythmsTotal} prayer rhythms present`) +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 (errs.length) fails.push(`${errs.length} page error(s): ${errs[0]}`) +if (landscape) fails.push(`${landscape} landscape page(s) — the packet must be portrait`) +if (pages !== r.sheets) fails.push(`${pages} PDF pages for ${r.sheets} sheets — one sheet must be exactly one page`) + +console.log(` sheets/PDF pages : ${r.sheets} / ${pages}`) +console.log(` people cards : ${r.cards} (${r.imgs} photos, ${r.nophoto} typographic)`) +console.log(` wheel segments : ${r.wheelSegs}`) +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`) + +if (fails.length) { + console.error(`\nPACKET-RENDER: FAIL — ${fails.length} problem(s)`) + for (const f of fails) console.error(` - ${f}`) + process.exit(1) +} +console.log(`\nPACKET-RENDER: PASS — ${r.sheets} sheets, ${r.sheets} pages, portrait, map painted, content intact.`) diff --git a/internal/generate-tree.mjs b/internal/generate-tree.mjs index 51acf7b7f..9e2f0268d 100644 --- a/internal/generate-tree.mjs +++ b/internal/generate-tree.mjs @@ -1453,7 +1453,21 @@ function main() { // while an absolute assetsBaseUrl serves the originals on demand (the same escape hatch used // for partner-site and mobile embeds). Set bundlePosters:true to ship them anyway for a // fully offline drop, accepting the size. - const assetDirs = DEST_CONFIG.bundlePosters ? ['previews', 'posters'] : ['previews'] + // ★ fonts/ MUST be staged (measured 2026-09-24). A leaf page resolves its assetsBase to + // /assets, so a vendored @font-face at assets/fonts/... is fetched from INSIDE the + // tree — and this staging step is an allowlist, so a dir absent from it 404s. The regions + // clone vendors Bebas Neue (public/assets/fonts/bebas-neue, OFL) because DX-life serves + // theirs without CORS; with fonts/ unstaged the face registered, requested + // /assets/fonts/..., failed, and every heading silently rendered on the fallback. + // document.fonts reported the face in 'error' status while a computed-style diff of the + // two pages said 0 differences — both sides agreeing on a FALLBACK is not a match, and a + // font that 404s is invisible to every check that reads font-family rather than asking + // the engine whether the family is usable. + // Small and self-contained: a woff2 pair is ~22KB, and the tree's self-contained promise + // is exactly why it has to travel with it. + const assetDirs = DEST_CONFIG.bundlePosters + ? ['previews', 'posters', 'fonts'] + : ['previews', 'fonts'] for (const dirName of assetDirs) { const src = join(pubAssets, dirName) if (!existsSync(src)) { console.warn(`[generate-tree] WARN missing tree asset dir: assets/${dirName}`); continue } diff --git a/internal/public-hygiene-check.mjs b/internal/public-hygiene-check.mjs index a4684cc86..1b573a804 100644 --- a/internal/public-hygiene-check.mjs +++ b/internal/public-hygiene-check.mjs @@ -63,6 +63,22 @@ const LEAK = [ [/\beco-dx\b/, 'internal folder name (eco-dx)'], [/qa-cycle/, 'internal folder name (qa-cycle)'], [/\b_archive\b/, 'internal folder name (_archive)'], [/localhost:(5174|3001|3033)\b/, 'dev-server URL'], ] +// ★ ADDED 2026-09-24 (public-exposure audit, policies P3/P4/P7). A public tree carries +// ARTIFACTS, never PROCESS or operational context — each of these was found live in a public +// mirror by anonymous clone before this rule existed. They are WARNINGS in the normal build +// (the source tree's own public/ still carries them, and failing build:unlocked for every +// writer overnight would get this gate disabled) and FINDINGS under --strict, which the +// publish/export flow MUST run (policy P5: publishing is a gated action, not a habit). +const LEAK_STRICT = [ + [/\bthe Driver\b/, 'internal role noun (the Driver) — process vocabulary'], + [/\bDriver\b/, 'internal role noun (Driver) — process vocabulary'], + [/doxa-life\/doxa-maps/, 'private repo name'], + [/\bdoxa-life\b/, 'private org name'], + [/\bforgejo\b/i, 'self-hosted forge name'], + [/\bnuxtbase\b/, 'internal platform name'], + [/\bpersecution\b/, 'operational-context vocabulary (allowed only inside data CSVs)'], + [/\.forgejo\/workflows/, 'CI workflow path — process, never ships in a public tree'], +] // ★ WIDENED 2026-09-21. This held ONE pattern (the Mapbox JWT), on the reasoning that public/ // is a tree we GENERATE, so the only realistic leak is the token the build itself writes. // THAT REASONING WAS WRONG: 855 files under public/ are TRACKED, 95 of them text — boundaries, @@ -106,6 +122,24 @@ for (const f of files) { if (!isText || f.size > 40 * 1024 * 1024) continue const t = readFileSync(f.abs, 'utf8') for (const [re, why] of LEAK) if (re.test(t)) hit(findings, 'leaks', f.rel, why) + for (const [re, why] of LEAK_STRICT) if (re.test(t) && !/\.csv$/.test(f.rel)) hit(STRICT ? findings : warnings, 'leaks', f.rel, why) + // ★ P2 (2026-09-24): a credential is a DOCUMENT, not a string. Every JWT-shaped value gets + // its payload DECODED and its claims reviewed: 'it is a public token' is not sufficient — + // the pk. accepted by the 2026-09-21 ruling turned out to carry a `u` claim naming a real, + // searchable ACCOUNT in base64 anyone can decode. The ruling covered the token's scope, + // not the identity inside it. Flag any claim value that looks like a meaningful name. + for (const m of t.matchAll(/\b(?:pk|sk|tk)\.(eyJ[A-Za-z0-9_-]{8,})/g)) { + try { + const pad = m[1] + '='.repeat((4 - (m[1].length % 4)) % 4) + const claims = JSON.parse(Buffer.from(pad, 'base64').toString('utf8')) + for (const [k, v] of Object.entries(claims)) { + if (typeof v === 'string' && /^[a-z][a-z0-9_-]{3,}$/i.test(v) && !/^[0-9a-f]+$/i.test(v)) { + hit(STRICT ? findings : warnings, 'secrets', f.rel, `JWT claim "${k}" carries a name-like value (${v.length} chars) — decode + review before publishing`) + break + } + } + } catch { /* not decodable — not a JWT payload */ } + } for (const [re, why] of SECRET) if (re.test(t)) hit(findings, 'secrets', f.rel, why) if (/sourceMappingURL=/.test(t)) hit(warnings, 'debug', f.rel, 'sourceMappingURL comment (map file absent → harmless, but points at a missing map)') if (ext === '.js' && /console\.(log|debug)\(/.test(t)) hit(warnings, 'debug', f.rel, 'console.log/debug in a shipped bundle') diff --git a/internal/serve-strict.mjs b/internal/serve-strict.mjs index 9e5580e4a..3c1815e99 100644 --- a/internal/serve-strict.mjs +++ b/internal/serve-strict.mjs @@ -116,6 +116,15 @@ createServer((req, res) => { 'content-type': TYPES[extname(file).toLowerCase()] || 'application/octet-stream', 'content-length': st.size, 'cache-control': 'no-cache', + // ★ CORS: the tree is PUBLIC STATIC ASSETS built to be embedded on OTHER sites (the + // drop-anywhere contract). A cross-origin
Loading region dashboards…
Region data could not be loaded. This is a connection problem, not a filter. @@ -791,6 +886,6 @@ useShadowStyles( :language-code="languageCode" /> -
+ diff --git a/plugin-designs/regions-dashboard/regions-list.vue b/plugin-designs/regions-dashboard/regions-list.vue index f9a8ec3c6..4d2fe4d36 100644 --- a/plugin-designs/regions-dashboard/regions-list.vue +++ b/plugin-designs/regions-dashboard/regions-list.vue @@ -31,6 +31,7 @@ */ import { inject, ref, computed, onMounted, getCurrentInstance } from 'vue' import { useShadowStyles } from '@map/composables/useShadowStyles.js' +import { useDocumentFont, BEBAS_NEUE_FACES, POPPINS_FACES } from '@map/composables/useDocumentFont.js' const props = defineProps({ profileConfig: { type: String, default: '' }, @@ -61,6 +62,21 @@ const cfg = computed(() => { }) const prayBaseUrl = computed(() => props.prayBaseUrl || cfg.value.prayBaseUrl || 'https://pray.doxa.life') const languageCode = computed(() => props.languageCode || cfg.value.languageCode || '') +// Where the shared asset tree is served from: the generator bakes it per page depth and the +// composite forwards it via INHERITED_PAGE_KEYS. Needed for the vendored @font-face path below. +const assetsBase = computed(() => cfg.value.assetsBase || './assets') + +// ★ REGISTER BEBAS NEUE AT DOCUMENT LEVEL, not in the shadow root: @font-face is document-scoped +// and a face declared inside a shadow root is ignored. This profile registers it too, and not +// only tab 2, because its own leaf page (regions-list/index.html) is a standalone surface where +// tab 2 is not present — relying on a sibling would leave THIS page on the fallback face. +// Idempotent per document, so the composite still registers exactly one. +useDocumentFont('Bebas Neue', BEBAS_NEUE_FACES, assetsBase.value) +// Poppins is their --font-family-base — the face of every line of BODY text on these +// pages. It was the larger of the two typography gaps and completely invisible to a +// computed-style diff, which read 68/68 while our body text was a system fallback 5.5% +// wider per glyph. Registered the same way and for the same reason as Bebas. +useDocumentFont('Poppins', POPPINS_FACES, assetsBase.value) // ── state ─────────────────────────────────────────────────────────────────── const regions = ref([]) @@ -288,8 +304,8 @@ useShadowStyles( // the face's natural narrowness, but weight/leading/tracking/case now match the measured values // exactly rather than compounding extra "boldness" the source doesn't have. '.region-card__title{ margin:0; font-size:clamp(30.5176px, 4cqw, 47.6837px); line-height:1;' + - ' font-family:"Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' + - ' font-weight:400; font-stretch:condensed; letter-spacing:normal; }' + + ' font-family:"Bebas Neue","Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' + + ' font-weight:400; letter-spacing:normal; }' + '.region-card__link{ appearance:none; background:none; border:0; padding:0; margin:0; text-align:left;' + ' cursor:pointer; color:inherit; font:inherit; display:inline-flex; align-items:baseline; gap:10px; }' + '.region-card__link:hover{ color:#92b195; }' + diff --git a/public/assets/fonts/bebas-neue/LICENSE b/public/assets/fonts/bebas-neue/LICENSE new file mode 100644 index 000000000..e7376bc49 --- /dev/null +++ b/public/assets/fonts/bebas-neue/LICENSE @@ -0,0 +1,24 @@ +Bebas Neue — SIL Open Font License, Version 1.1 +Designer: Ryoichi Tsunekawa (Dharma Type). Distributed via Google Fonts. +Full text: https://openfontlicense.org/ (OFL-1.1) + +WHY THIS FILE IS IN THIS REPO +DX-life's /regions pages set --font-family-heading to 'Bebas Neue'. Their own site does not +ship the file either — @nuxt/fonts downloads it at build and self-hosts it, which is why their +computed font-family reads "Bebas Neue", "Bebas Neue Fallback: ...". We cannot @font-face from +their origin: their font file is served without CORS headers, so a cross-origin face silently +fails and every heading fell back to a condensed system stack. That substitution was the ONLY +remaining difference between our regions dashboard and theirs (7 computed properties, all of +them this one font and its knock-on sizes). +So the font is vendored here, from Google Fonts, under the OFL that permits redistribution — +self-hosted, never a CDN, the same rule public/assets/vendor/tabulator/ follows. +Subsets and unicode-ranges are Google's own (css2 API, v16), reproduced verbatim in the +@font-face declared by library/composables/useDocumentFont.js. + +NOTE ON THIS FILENAME — it is deliberately extensionless. +check:public-hygiene refuses *.md / *.markdown / *.txt anywhere in the served tree ("no docs in a +served folder", Driver rule 2026-09-16), and it is right to: prose does not belong in a public +mirror. But the OFL requires this licence to accompany the font files wherever they are +redistributed, and the font files DO ship — they are fetched by the @font-face at runtime. So the +licence travels with them under the conventional extensionless name, which satisfies the rule +without weakening it. Do not rename this to LICENSE.txt. diff --git a/public/assets/fonts/bebas-neue/bebas-neue-latin-ext.woff2 b/public/assets/fonts/bebas-neue/bebas-neue-latin-ext.woff2 new file mode 100644 index 000000000..23b344f3d Binary files /dev/null and b/public/assets/fonts/bebas-neue/bebas-neue-latin-ext.woff2 differ diff --git a/public/assets/fonts/bebas-neue/bebas-neue-latin.woff2 b/public/assets/fonts/bebas-neue/bebas-neue-latin.woff2 new file mode 100644 index 000000000..92d6a0f72 Binary files /dev/null and b/public/assets/fonts/bebas-neue/bebas-neue-latin.woff2 differ diff --git a/public/assets/fonts/poppins/LICENSE b/public/assets/fonts/poppins/LICENSE new file mode 100644 index 000000000..9786c3ad8 --- /dev/null +++ b/public/assets/fonts/poppins/LICENSE @@ -0,0 +1,29 @@ +Poppins — SIL Open Font License, Version 1.1 +Designers: Indian Type Foundry, Jonny Pinhorn. Distributed via Google Fonts. +Full text: https://openfontlicense.org/ (OFL-1.1) + +WHY THIS FILE IS IN THIS REPO +Poppins is DX-life's --font-family-base: the body face of every line of text on the pages the +regions plugin clones. Their own site self-hosts it via @nuxt/fonts; we could not use theirs, so +it is vendored here from Google Fonts under the OFL, which permits redistribution. + +WHY IT WAS NEEDED — and why no CSS check caught it. +A computed-style diff of our region dashboard against live doxa.life reported 68 of 68 properties +IDENTICAL while our body text was rendering in a system fallback, because BOTH sides merely NAME +"Poppins" first in font-family and a first-family comparison therefore matched. The difference only +became visible against a glyph-width oracle: the same string at the same size measured 334.58px +here against 317px on their page — 5.5% wider, i.e. a different face. +document.fonts.check() is NOT a usable oracle for this: it returned true for families never +vendored (Oswald, Brandon Grotesque) and false for Bebas Neue on DX-life's own page. +See _qa-cycle/02-builder-cycle/_probe-glyph-width.mjs. + +WHAT IS HERE, AND WHY ONLY THIS +Weights 400/500/600/700 only — the four the regions surface actually asks for (measured across +their _globals, _cards, _buttons and _filters SCSS: medium x10, regular x4, bold x2, plus the +600 in the prayer-coverage legend). Subsets latin + latin-ext only. 44 KB in total, with Google's +own unicode-ranges reproduced in the @font-face so the subset split behaves as it does on their +site. Extra weights and scripts are deliberately NOT vendored — bloat is a repo rule here. + +FILENAME: extensionless on purpose. check:public-hygiene refuses *.txt in a served folder and is +right to, while the OFL requires this licence to accompany redistributed font files. Do not +rename this to LICENSE.txt. diff --git a/public/assets/fonts/poppins/poppins-400-latin-ext.woff2 b/public/assets/fonts/poppins/poppins-400-latin-ext.woff2 new file mode 100644 index 000000000..63f471177 Binary files /dev/null and b/public/assets/fonts/poppins/poppins-400-latin-ext.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-400-latin.woff2 b/public/assets/fonts/poppins/poppins-400-latin.woff2 new file mode 100644 index 000000000..b69e0091c Binary files /dev/null and b/public/assets/fonts/poppins/poppins-400-latin.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-500-latin-ext.woff2 b/public/assets/fonts/poppins/poppins-500-latin-ext.woff2 new file mode 100644 index 000000000..d59af06e1 Binary files /dev/null and b/public/assets/fonts/poppins/poppins-500-latin-ext.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-500-latin.woff2 b/public/assets/fonts/poppins/poppins-500-latin.woff2 new file mode 100644 index 000000000..c6603368b Binary files /dev/null and b/public/assets/fonts/poppins/poppins-500-latin.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-600-latin-ext.woff2 b/public/assets/fonts/poppins/poppins-600-latin-ext.woff2 new file mode 100644 index 000000000..8ec78f544 Binary files /dev/null and b/public/assets/fonts/poppins/poppins-600-latin-ext.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-600-latin.woff2 b/public/assets/fonts/poppins/poppins-600-latin.woff2 new file mode 100644 index 000000000..921e962b9 Binary files /dev/null and b/public/assets/fonts/poppins/poppins-600-latin.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-700-latin-ext.woff2 b/public/assets/fonts/poppins/poppins-700-latin-ext.woff2 new file mode 100644 index 000000000..bc847e9d2 Binary files /dev/null and b/public/assets/fonts/poppins/poppins-700-latin-ext.woff2 differ diff --git a/public/assets/fonts/poppins/poppins-700-latin.woff2 b/public/assets/fonts/poppins/poppins-700-latin.woff2 new file mode 100644 index 000000000..bf022fc77 Binary files /dev/null and b/public/assets/fonts/poppins/poppins-700-latin.woff2 differ diff --git a/public/doxa-maps/assets/fonts/bebas-neue/LICENSE b/public/doxa-maps/assets/fonts/bebas-neue/LICENSE new file mode 100644 index 000000000..e7376bc49 --- /dev/null +++ b/public/doxa-maps/assets/fonts/bebas-neue/LICENSE @@ -0,0 +1,24 @@ +Bebas Neue — SIL Open Font License, Version 1.1 +Designer: Ryoichi Tsunekawa (Dharma Type). Distributed via Google Fonts. +Full text: https://openfontlicense.org/ (OFL-1.1) + +WHY THIS FILE IS IN THIS REPO +DX-life's /regions pages set --font-family-heading to 'Bebas Neue'. Their own site does not +ship the file either — @nuxt/fonts downloads it at build and self-hosts it, which is why their +computed font-family reads "Bebas Neue", "Bebas Neue Fallback: ...". We cannot @font-face from +their origin: their font file is served without CORS headers, so a cross-origin face silently +fails and every heading fell back to a condensed system stack. That substitution was the ONLY +remaining difference between our regions dashboard and theirs (7 computed properties, all of +them this one font and its knock-on sizes). +So the font is vendored here, from Google Fonts, under the OFL that permits redistribution — +self-hosted, never a CDN, the same rule public/assets/vendor/tabulator/ follows. +Subsets and unicode-ranges are Google's own (css2 API, v16), reproduced verbatim in the +@font-face declared by library/composables/useDocumentFont.js. + +NOTE ON THIS FILENAME — it is deliberately extensionless. +check:public-hygiene refuses *.md / *.markdown / *.txt anywhere in the served tree ("no docs in a +served folder", Driver rule 2026-09-16), and it is right to: prose does not belong in a public +mirror. But the OFL requires this licence to accompany the font files wherever they are +redistributed, and the font files DO ship — they are fetched by the @font-face at runtime. So the +licence travels with them under the conventional extensionless name, which satisfies the rule +without weakening it. Do not rename this to LICENSE.txt. diff --git a/public/doxa-maps/assets/fonts/bebas-neue/bebas-neue-latin-ext.woff2 b/public/doxa-maps/assets/fonts/bebas-neue/bebas-neue-latin-ext.woff2 new file mode 100644 index 000000000..23b344f3d Binary files /dev/null and b/public/doxa-maps/assets/fonts/bebas-neue/bebas-neue-latin-ext.woff2 differ diff --git a/public/doxa-maps/assets/fonts/bebas-neue/bebas-neue-latin.woff2 b/public/doxa-maps/assets/fonts/bebas-neue/bebas-neue-latin.woff2 new file mode 100644 index 000000000..92d6a0f72 Binary files /dev/null and b/public/doxa-maps/assets/fonts/bebas-neue/bebas-neue-latin.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/LICENSE b/public/doxa-maps/assets/fonts/poppins/LICENSE new file mode 100644 index 000000000..9786c3ad8 --- /dev/null +++ b/public/doxa-maps/assets/fonts/poppins/LICENSE @@ -0,0 +1,29 @@ +Poppins — SIL Open Font License, Version 1.1 +Designers: Indian Type Foundry, Jonny Pinhorn. Distributed via Google Fonts. +Full text: https://openfontlicense.org/ (OFL-1.1) + +WHY THIS FILE IS IN THIS REPO +Poppins is DX-life's --font-family-base: the body face of every line of text on the pages the +regions plugin clones. Their own site self-hosts it via @nuxt/fonts; we could not use theirs, so +it is vendored here from Google Fonts under the OFL, which permits redistribution. + +WHY IT WAS NEEDED — and why no CSS check caught it. +A computed-style diff of our region dashboard against live doxa.life reported 68 of 68 properties +IDENTICAL while our body text was rendering in a system fallback, because BOTH sides merely NAME +"Poppins" first in font-family and a first-family comparison therefore matched. The difference only +became visible against a glyph-width oracle: the same string at the same size measured 334.58px +here against 317px on their page — 5.5% wider, i.e. a different face. +document.fonts.check() is NOT a usable oracle for this: it returned true for families never +vendored (Oswald, Brandon Grotesque) and false for Bebas Neue on DX-life's own page. +See _qa-cycle/02-builder-cycle/_probe-glyph-width.mjs. + +WHAT IS HERE, AND WHY ONLY THIS +Weights 400/500/600/700 only — the four the regions surface actually asks for (measured across +their _globals, _cards, _buttons and _filters SCSS: medium x10, regular x4, bold x2, plus the +600 in the prayer-coverage legend). Subsets latin + latin-ext only. 44 KB in total, with Google's +own unicode-ranges reproduced in the @font-face so the subset split behaves as it does on their +site. Extra weights and scripts are deliberately NOT vendored — bloat is a repo rule here. + +FILENAME: extensionless on purpose. check:public-hygiene refuses *.txt in a served folder and is +right to, while the OFL requires this licence to accompany redistributed font files. Do not +rename this to LICENSE.txt. diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-400-latin-ext.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-400-latin-ext.woff2 new file mode 100644 index 000000000..63f471177 Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-400-latin-ext.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-400-latin.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-400-latin.woff2 new file mode 100644 index 000000000..b69e0091c Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-400-latin.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-500-latin-ext.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-500-latin-ext.woff2 new file mode 100644 index 000000000..d59af06e1 Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-500-latin-ext.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-500-latin.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-500-latin.woff2 new file mode 100644 index 000000000..c6603368b Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-500-latin.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-600-latin-ext.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-600-latin-ext.woff2 new file mode 100644 index 000000000..8ec78f544 Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-600-latin-ext.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-600-latin.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-600-latin.woff2 new file mode 100644 index 000000000..921e962b9 Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-600-latin.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-700-latin-ext.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-700-latin-ext.woff2 new file mode 100644 index 000000000..bc847e9d2 Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-700-latin-ext.woff2 differ diff --git a/public/doxa-maps/assets/fonts/poppins/poppins-700-latin.woff2 b/public/doxa-maps/assets/fonts/poppins/poppins-700-latin.woff2 new file mode 100644 index 000000000..bf022fc77 Binary files /dev/null and b/public/doxa-maps/assets/fonts/poppins/poppins-700-latin.woff2 differ diff --git a/public/doxa-maps/index.html b/public/doxa-maps/index.html index 832ca7acb..c4363581d 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-euKnTYjtIyND2Ddqz1s3gX5zyMSTFGRjX0P0TnzedZLc/OX0Y1Cuuv6KECxpnTdg","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-euKnTYjtIyND2Ddqz1s3gX5zyMSTFGRjX0P0TnzedZLc/OX0Y1Cuuv6KECxpnTdg\" 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-VfbtDah7CcZdh9U1LNojwqD50sWSZIzYogPohoATgibTn1s00jLOdq4/98uSfsi+","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-VfbtDah7CcZdh9U1LNojwqD50sWSZIzYogPohoATgibTn1s00jLOdq4/98uSfsi+\" 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-VfbtDah7CcZdh9U1LNojwqD50sWSZIzYogPohoATgibTn1s00jLOdq4/98uSfsi+\" 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-VfbtDah7CcZdh9U1LNojwqD50sWSZIzYogPohoATgibTn1s00jLOdq4/98uSfsi+\" 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-we0q/Lynt6lZt961TyIob0IYnRlQI+oyOdmK7j2DHpB/FCPaIeYKwmsJPJtGRRoY","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-we0q/Lynt6lZt961TyIob0IYnRlQI+oyOdmK7j2DHpB/FCPaIeYKwmsJPJtGRRoY\" 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-we0q/Lynt6lZt961TyIob0IYnRlQI+oyOdmK7j2DHpB/FCPaIeYKwmsJPJtGRRoY\" 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-P0edqxp6+FLOiF2rzzR7Ytmjz3h2fNP6BEgNzWKacqWhD01c0ZWPrVKn44X2JQ8O","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-P0edqxp6+FLOiF2rzzR7Ytmjz3h2fNP6BEgNzWKacqWhD01c0ZWPrVKn44X2JQ8O\" 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-rJKc4nfmyGRg1jLQ0z1oHkdzh327riWrR31KonyK4YCEx12t16lwv6pUFE7qrZ7R","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-rJKc4nfmyGRg1jLQ0z1oHkdzh327riWrR31KonyK4YCEx12t16lwv6pUFE7qrZ7R\" 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-TaralB6CTdy7PGGyPQgxcrhw2GMxWvOB+YVScRguQzTkbmbj3N6Luc78ZNeHzx+R","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-TaralB6CTdy7PGGyPQgxcrhw2GMxWvOB+YVScRguQzTkbmbj3N6Luc78ZNeHzx+R\" 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-bJZCO07WgqJdkss8wVF2wSfaDUFQqNonFatNPJOvW/9/UK2mlWhTHsic0t6ivz6W","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-bJZCO07WgqJdkss8wVF2wSfaDUFQqNonFatNPJOvW/9/UK2mlWhTHsic0t6ivz6W\" 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-VXjJ4Se+3vtAs3dnqQoWelPf88U32RffjhrVy3usV5m9HHm2fL/CfUq2maQVKhzj","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-VXjJ4Se+3vtAs3dnqQoWelPf88U32RffjhrVy3usV5m9HHm2fL/CfUq2maQVKhzj\" 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-VXjJ4Se+3vtAs3dnqQoWelPf88U32RffjhrVy3usV5m9HHm2fL/CfUq2maQVKhzj\" 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-VXjJ4Se+3vtAs3dnqQoWelPf88U32RffjhrVy3usV5m9HHm2fL/CfUq2maQVKhzj\" 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-VXjJ4Se+3vtAs3dnqQoWelPf88U32RffjhrVy3usV5m9HHm2fL/CfUq2maQVKhzj\" 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-FsLKnc68f9jkTo/nVZ4lJ68Yugie7i+BeqoSM0CLiILe7YzlRJOmhZ2HUW0UslBr","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-FsLKnc68f9jkTo/nVZ4lJ68Yugie7i+BeqoSM0CLiILe7YzlRJOmhZ2HUW0UslBr\" 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-FsLKnc68f9jkTo/nVZ4lJ68Yugie7i+BeqoSM0CLiILe7YzlRJOmhZ2HUW0UslBr\" 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-FsLKnc68f9jkTo/nVZ4lJ68Yugie7i+BeqoSM0CLiILe7YzlRJOmhZ2HUW0UslBr\" 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-wyfAzVfoufR5FaDoo3DSivtV33qOexc7PhDYZkXlYmru5FqZgncuUos3nN0kkMJL","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-wyfAzVfoufR5FaDoo3DSivtV33qOexc7PhDYZkXlYmru5FqZgncuUos3nN0kkMJL\" 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-DCmfPJdlsSwflCxB7aJlhn12a8oSKWbRDEBj71DiqSxRFWGkMG11LgV73ksxur/i","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-DCmfPJdlsSwflCxB7aJlhn12a8oSKWbRDEBj71DiqSxRFWGkMG11LgV73ksxur/i\" 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-DCmfPJdlsSwflCxB7aJlhn12a8oSKWbRDEBj71DiqSxRFWGkMG11LgV73ksxur/i\" 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-DCmfPJdlsSwflCxB7aJlhn12a8oSKWbRDEBj71DiqSxRFWGkMG11LgV73ksxur/i\" 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-fcOV0Am4svYx+S+fN8NSDa5H9Zyaw2fsJB6rRuH06oQ8aeEIDTLY4I9YlLmbcSlD","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-fcOV0Am4svYx+S+fN8NSDa5H9Zyaw2fsJB6rRuH06oQ8aeEIDTLY4I9YlLmbcSlD\" 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-L0FkVYzlQATU9b0Ah8f089YyKz29G6Y58/44DkV+TMS5WbpeO2Jq014VzVAmpTMS","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-L0FkVYzlQATU9b0Ah8f089YyKz29G6Y58/44DkV+TMS5WbpeO2Jq014VzVAmpTMS\" 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-L0FkVYzlQATU9b0Ah8f089YyKz29G6Y58/44DkV+TMS5WbpeO2Jq014VzVAmpTMS\" 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" } }, { @@ -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/regions-dashboard/region-dashboards/index.html b/public/doxa-maps/regions-dashboard/region-dashboards/index.html index 4bab7998c..ae7a7b738 100644 --- a/public/doxa-maps/regions-dashboard/region-dashboards/index.html +++ b/public/doxa-maps/regions-dashboard/region-dashboards/index.html @@ -29,7 +29,7 @@ - + - + - +