Show the groups with nothing found and nobody praying

213 people groups have no Scripture, audio, film, Bible video or Door43
resource in any pool checked AND zero people praying on doxa.life's
counter — 4.67M people, largest Songhai (BFA, 259,000), Kuka (TCD,
250,000), Kimr (SDN, 221,000). That intersection is the sharpest thing
in the data and nothing surfaced it.

The gap sheet gains a fourth tab whose rows are links straight to each
group's prayer page, so the answer to "nobody is praying" is one tap,
not a search. The sheet says plainly that "nobody praying" means zero
on doxa.life's counter, not a claim nobody in the world prays for them.

Two fixes found while proving it: the tab is built from the lazily
fetched prayer index, so opening the gap list now loads that index —
without it the tab only ever appeared if you had clicked a pin first.
And the new branch opened its own v-if chain, which left the old
"largest groups" list rendering underneath it, 80 rows where 40 belong.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 16:54:14 +00:00
parent 5c579a124a
commit 4f5136211c
9 changed files with 87 additions and 37 deletions

View file

@ -308,6 +308,27 @@ const gapList = computed(() => {
// Papua New Guinea has 469 groups with nothing but only 2.2M people, China has 125 groups
// and 150.3M. Ranking by either one alone hides the other question.
const gapBy = ref('groups')
// THE SHARPEST INTERSECTION THIS DATA HOLDS: no gospel resource found in any pool checked,
// AND nobody praying. 213 people groups, 4.67M people. Every other view here ends at "this
// group has nothing"; this one ends at a page where the reader can do something about it,
// which is why the rows are links rather than fly-to buttons.
//
// "NOBODY PRAYING" MEANS ZERO ON DOXA.LIFE'S COUNTER. It is not a claim that nobody in the
// world prays for these people — an unknowable thing — and the sheet says so rather than
// letting the number imply it.
const gapNoPrayer = computed(() => {
if (!showGapList.value) return []
const out = []
for (let i = 0; i < pinRows.value.length; i++) {
const r = pinRows.value[i]
if (r[6] !== 0) continue
const e = prayIndex.value[prayKey(r[0], r[3])]
if (!e || e.praying > 0) continue
out.push({ i, name: r[0], iso3: r[3], code: r[4], pop: r[5], url: e.u })
}
return out.sort((a, b) => b.pop - a.pop).slice(0, 40)
})
// A THIRD AXIS: the RATE. The country tab ranks by how many groups a country holds and
// shows population beside it; neither answers "what share of this place is unreached".
// Measured, the three disagree completely and each is somebody's question:
@ -346,7 +367,15 @@ const gapCountries = computed(() => {
.slice(0, 40)
})
function openGapList() { showGapList.value = true; selected.value = null; selectedGroup.value = null }
function openGapList() {
// The prayer index is fetched lazily and the "Nobody praying" tab is built from it, so
// opening this list without having first clicked a pin left the tab hidden entirely —
// the view existed and was unreachable unless you happened to take the long way round.
ensurePeopleGroups()
showGapList.value = true
selected.value = null
selectedGroup.value = null
}
function closeGapList() { showGapList.value = false }
// Fly first, THEN open: openPin sets `selected`, which swaps the sheet away from this list,
// so doing it the other way round loses the map movement behind a re-render.
@ -905,7 +934,7 @@ useShadowStyles(
'margin-bottom:6px;border-radius:8px;' +
'border:1px solid rgba(0,0,0,.10);background:rgba(0,0,0,.02);color:inherit;font:inherit}' +
'.is-dark .gm-pg{border-color:rgba(255,255,255,.10);background:rgba(255,255,255,.03)}' +
'.gm-pg-btn{cursor:pointer}' +
'.gm-pg-btn{cursor:pointer;text-decoration:none;color:inherit}' +
// The drill-in control on a resource row: a bare button that inherits the shell's
// look, so the row still reads as one tap-target for the name + language line.
'.gm-pg-open{display:block;width:100%;text-align:left;padding:0;margin:0;border:0;' +
@ -1066,13 +1095,21 @@ useShadowStyles(
<!-- ITEM 0 — the ranked gap: the largest groups with nothing found anywhere. -->
<template v-if="showGapList && !selected">
<div class="gm-sh-head">
<span class="gm-sh-name">{{ gapBy === 'groups' ? 'Largest groups with nothing found' : 'Where the gap concentrates' }}</span>
<span class="gm-sh-name">{{ gapBy === 'groups' ? 'Largest groups with nothing found' : gapBy === 'noprayer' ? 'Nothing found, and nobody praying' : 'Where the gap concentrates' }}</span>
<button type="button" class="gm-x" @click="closeGapList">Close</button>
</div>
<p class="gm-note" style="margin-top:0">
{{ fmt(pinTally.none) }} people groups have no Scripture, audio, film, Bible video or
Door43 resource in the pools checked — {{ fmt(gapPeople) }} people.
<template v-if="gapBy === 'regions'">
<template v-if="gapBy === 'noprayer'">
The groups with nothing found in any pool checked AND nobody praying —
{{ fmt(gapNoPrayer.length) }} shown, largest first. Tapping one opens their prayer
page on doxa.life.
<br><b>"Nobody praying" means zero on doxa.life's counter</b>, not a claim that
nobody in the world prays for them. Only the {{ fmt(Object.keys(prayIndex).length) }}
groups doxa.life publishes a page for can appear here at all.
</template>
<template v-else-if="gapBy === 'regions'">
The five UN regions, ranked by the SHARE of their groups with nothing — the one
question the other two tabs cannot answer. Oceania is half unreached by group
and small by population; Asia is the reverse. Rate, count and population each
@ -1103,11 +1140,24 @@ useShadowStyles(
@click="gapBy = 'countries'">By country</button>
<button type="button" class="gm-gtab" :class="{ on: gapBy === 'regions' }"
@click="gapBy = 'regions'">By region</button>
<button v-if="gapNoPrayer.length" type="button" class="gm-gtab" :class="{ on: gapBy === 'noprayer' }"
@click="gapBy = 'noprayer'">Nobody praying</button>
</div>
<!-- Links, not fly-to buttons: every row here HAS a prayer page, so the useful
action is to open it rather than to look at the dot again. -->
<template v-if="gapBy === 'noprayer'">
<a v-for="g in gapNoPrayer" :key="g.i" class="gm-pg gm-pg-btn" :href="g.url"
target="_blank" rel="noopener noreferrer">
<span class="gm-pg-n">{{ g.name }}</span>
<span class="gm-pg-l">{{ fmt(g.pop) }} people · {{ g.iso3 }}<code class="gm-code">{{ g.code || 'no code' }}</code></span>
<span class="gm-pg-rel">nothing found, and nobody praying — open their prayer page ↗</span>
</a>
</template>
<!-- Not buttons: a region is not a place this map can fly to, and a control that
looks clickable and does nothing is worse than a plain row. -->
<template v-if="gapBy === 'regions'">
<template v-else-if="gapBy === 'regions'">
<div v-for="r in gapRegions" :key="r.region" class="gm-pg">
<span class="gm-pg-n">{{ r.region }}</span>
<span class="gm-pg-l"><b>{{ r.pct }}%</b> of its {{ fmt(r.groups) }} groups have nothing

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -26,7 +26,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <gospel-resources-map> custom element. -->
<script src="../gospel-resources.js" integrity="sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -591,7 +591,7 @@
"bundle": "gospel-resources",
"scriptUrl": "./gospel-resources/gospel-resources.js",
"output": "doxa-maps/gospel-resources/gospel-resources.js",
"integrity": "sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl",
"integrity": "sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ",
"elements": [
"gospel-resources-map"
],
@ -611,7 +611,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/charts/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"charts\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -625,7 +625,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/data-tables/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"data-tables\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -639,7 +639,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/gospel-dashboard/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"gospel-dashboard\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -653,7 +653,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/resources-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
},
{
@ -667,7 +667,7 @@
"scriptUrl": "./gospel-resources/gospel-resources.js",
"embed": {
"iframe": "<iframe src=\"./gospel-resources/resources-map/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-3KTr1qhgnkpG3VcebvpMExKEBfsMuHLimRhzbv8FBWxHoV+nyVe9vUqI6pcV4gZl\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./gospel-resources/gospel-resources.js\" integrity=\"sha384-2MupWXvGxYisiI5Vd6ZW5b+99/GRVn1tzRuWsLPlcGp9gZVByVvoXMK6VNb3Z+LJ\" crossorigin=\"anonymous\"></script>\n<gospel-resources-map profile-config='{\"profile\":\"resources-map\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></gospel-resources-map>"
}
}
]