Where the gap concentrates: the same 2,901 groups, ranked by country

The ranked gap list answered "which single group is largest". A mobiliser also asks
"where would a team go", and that answer concentrates hard: the top 10 countries hold
56.6% of all 2,901 groups with nothing, and Papua New Guinea alone holds 469 of them.

The gap sheet now switches between the two. Country rows show groups AND people, because
the two rankings disagree and the disagreement is the useful part — Papua New Guinea has
469 groups with nothing and 2.2M people; China has 125 groups and 150.3M. Which number
matters depends on whether you are sending translators or broadcasters, and the sheet
says so rather than picking for the reader.

Verified against an offline count from the same index: the rendered first row reads
"Papua New Guinea 469 groups with nothing · 2,187,150 people", matching exactly, with the
By country tab active.

Two sources researched this tick and both recorded as rejected rather than shipped:

WordProject is real and permitted — robots.txt allows everything except /database/ and
/scripts/, and it lists 78 Bible languages. But only 7 of its directory codes are
literally an ISO 639-3 this map carries, ZERO of those reach a group with nothing, and
matching the other 71 would mean joining on the English language name, which this map
refuses everywhere else because names collide across unrelated languages. Taking that
risk to duplicate Scripture links that already exist is a bad trade.

GodTools, added last change, reaches 0 tier-0 groups of its 105 ISO-joined languages.

Both are depth for languages already served. The remaining 2,901 groups are not short of
a source nobody has looked at — GRN, YouVersion, api.bible, DBL, Deaf Bible Society and
DOOR all need a credential or an agreement, and everything open has now been mined.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 13:58:42 +00:00
parent e0fd61eb3e
commit a4d8363179
10 changed files with 111 additions and 37 deletions

View file

@ -164,6 +164,31 @@ door_international — both key-gated` as not.
**Jehovah's Witnesses publish extensively in sign language and are excluded regardless.**
The size of a gap is not a reason to relax the doctrinal filter.
## WordProject — real, allowed, and not worth joining
`robots.txt` permits everything except `/database/` and `/scripts/`, and
`wordproject.org/bibles/index.htm` is a real 30KB page listing **78** Bible languages. It was
rejected on the join, not on doctrine or access:
- Only **7** of its 78 directory codes are literally an ISO 639-3 this map carries
(`ace`, `ben`, `ceb`, `guj`, `lug`, `mar`, `tel`). The rest are house codes — `al` for
Albanian, `cr` Croatian, `cz` Czech, `dk` Danish, `gb` and `big5` for Chinese.
- **Zero** of those 7 reach a group that has nothing; all 78 are major languages already
covered here.
- Matching the other 71 would mean joining on the English language NAME, which this map
refuses everywhere else because names collide across unrelated languages. Taking that
risk to duplicate Scripture links that already exist is a bad trade.
Also noted for whenever it is revisited: the list includes the Douay-Rheims and the Si Gao
version, both of which carry the deuterocanon. The standing rule here is the one written for
eBible — a publication carrying the 66 books plus an appendix still delivers the 66 books,
and the type matters rather than the appendix — but where a 66-book edition of the same
language exists it should be preferred.
## GodTools / Cru — reaches nobody who has nothing
Integrated as gospel presentations (see the commit), but worth recording separately: of its
105 ISO-joined languages, **0** reach a group at tier 0. Like WordProject, it adds depth to
languages that are already served.
## Not a source at all
- **sign.bible** — a parked domain listed for sale on Epik. Answers 200.

View file

@ -288,6 +288,28 @@ const gapList = computed(() => {
out.sort((a, b) => b.pop - a.pop)
return out.slice(0, 40)
})
// WHERE, not just WHO. The ranked group list answers "which single group is largest";
// a mobiliser also asks "where would a team go", and the answer concentrates hard: the
// top 10 countries hold 56.6% of all 2,901 groups with nothing.
// Both axes are shown because they disagree, and the disagreement is the useful part —
// 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')
const gapCountries = computed(() => {
if (!showGapList.value) return []
const by = {}
for (const r of pinRows.value) {
if (r[6] !== 0) continue
const e = (by[r[3]] = by[r[3]] || { iso3: r[3], groups: 0, pop: 0 })
e.groups++
e.pop += r[5] || 0
}
return Object.values(by)
.map((e) => ({ ...e, country: byIso.value[e.iso3]?.country || e.iso3 }))
.sort((a, b) => b.groups - a.groups)
.slice(0, 40)
})
function openGapList() { 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,
@ -760,6 +782,12 @@ useShadowStyles(
// Same frame as the sign-language note: a caveat, visually separate from the chips so it
// never reads as another resource.
// Quiet, not alarming: it is a caveat on a real gap, not a dismissal of it.
'.gm-gswitch{display:flex;gap:6px;margin:0 0 8px}' +
'.gm-gtab{flex:1;font:600 11px inherit;padding:5px 8px;border-radius:999px;cursor:pointer;' +
'border:1px solid #d1d4cd;background:#fff;color:#3b463d}' +
'.gm-gtab.on{background:#92b195;border-color:#92b195;color:#fff}' +
'.is-dark .gm-gtab{background:transparent;border-color:rgba(255,255,255,.22);color:#e5e7eb}' +
'.is-dark .gm-gtab.on{background:#92b195;color:#10241a}' +
'.gm-pg-rel{display:block;margin-top:2px;font-size:10px;color:#6d766e;font-style:italic}' +
'.is-dark .gm-pg-rel{color:#9fb3a4}' +
'.gm-rel{margin:8px 0 0;padding:8px 10px;border:1px solid #d1d4cd;border-left:3px solid #92b195;' +
@ -941,13 +969,19 @@ 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">Largest groups with nothing found</span>
<span class="gm-sh-name">{{ gapBy === 'countries' ? 'Where the gap concentrates' : 'Largest groups with nothing found' }}</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. These are the 40
largest; tap one to fly there.
Door43 resource in the pools checked — {{ fmt(gapPeople) }} people.
<template v-if="gapBy === 'countries'">
The top 40 countries by how many of those groups they hold; tap one to open it.
The two columns disagree on purpose — Papua New Guinea has the most groups and
few people, China the reverse — and which one matters depends on whether you are
sending translators or broadcasters.
</template>
<template v-else>These are the 40 largest; tap one to fly there.</template>
<!-- This paragraph used to say audio had never been searched for any of them. That
stopped being true when Faith Comes By Hearing was added, and a stale caveat is
as misleading as a stale number. GRN is still unsearchable and still named. -->
@ -959,7 +993,22 @@ useShadowStyles(
macrolanguage with another variety that does have resources. It is how the standard
groups the codes, not a claim they are mutually intelligible.
</p>
<button v-for="g in gapList" :key="g.i" type="button" class="gm-pg gm-pg-btn" @click="gotoGap(g)">
<div class="gm-gswitch">
<button type="button" class="gm-gtab" :class="{ on: gapBy === 'groups' }"
@click="gapBy = 'groups'">Largest groups</button>
<button type="button" class="gm-gtab" :class="{ on: gapBy === 'countries' }"
@click="gapBy = 'countries'">By country</button>
</div>
<template v-if="gapBy === 'countries'">
<button v-for="c in gapCountries" :key="c.iso3" type="button" class="gm-pg gm-pg-btn"
@click="closeGapList(); openCountry(c.iso3)">
<span class="gm-pg-n">{{ c.country }}</span>
<span class="gm-pg-l">{{ fmt(c.groups) }} groups with nothing · {{ fmt(c.pop) }} people<code class="gm-code">{{ c.iso3 }}</code></span>
</button>
</template>
<button v-else v-for="g in gapList" :key="g.i" type="button" class="gm-pg gm-pg-btn" @click="gotoGap(g)">
<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 v-if="g.rel" class="gm-pg-rel"

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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1" 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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1" 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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1" 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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1" 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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX" crossorigin="anonymous"></script>
<script src="../gospel-resources.js" integrity="sha384-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1" 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

@ -556,7 +556,7 @@
"bundle": "gospel-resources",
"scriptUrl": "./gospel-resources/gospel-resources.js",
"output": "doxa-maps/gospel-resources/gospel-resources.js",
"integrity": "sha384-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX",
"integrity": "sha384-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1",
"elements": [
"gospel-resources-map"
],
@ -576,7 +576,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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX\" 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-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1\" 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>"
}
},
{
@ -590,7 +590,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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX\" 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-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1\" 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>"
}
},
{
@ -604,7 +604,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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX\" 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-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1\" 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>"
}
},
{
@ -618,7 +618,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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX\" 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-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1\" 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>"
}
},
{
@ -632,7 +632,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-p1tz0SlBvn8a/R7GVu/Ze6+gb3/V3lltm/AZrYnod0VTSISkUrVr7ao/ClpCYhNX\" 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-vj+71llBlK/GbEm9DfUG+nfVDUMgxjQ3tLT0aWgX1lWdv8th2zRA6UPdWz0O+TH1\" 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>"
}
}
]