Commit graph

1099 commits

Author SHA1 Message Date
doxa-maps-88655c70
814124b3f6 Plugin studio: its own words in every locale
Some checks are pending
build-gate / build (push) Waiting to run
Choosing a language in the studio translated the plugins it mounts but
left the studio itself in English: the title, the count, the filter box,
Collapse all, the section names, Copy embed, the loading and error
messages, and the rail's own Collapse button and aria labels.

The studio's strings now live in library/i18n/locales/<lc>/studio.json
for all 11 locales, held to key parity by check:i18n-parity like every
other catalog. The studio has no vue-i18n instance, so it looks strings
up directly (locale, then English, per key). The Explorer tree takes
them through a `strings` prop. VerticalTabsLayout gains an optional
`labels` prop for its five words; hosts that do not pass it keep the
English defaults, so doxa-maps-spa is unchanged until it opts in.

Verified by switching the studio through every language its switcher
offers (en, es, fr, pt, ar, ru): no English left in the studio chrome.
Technical names (CAP n, element tags, tier machine names in tooltips)
stay as they are.
2026-09-25 04:10:49 +00:00
doxa-maps-88655c70
193127e257 Stop publishing the template scaffold's placeholder pages
Some checks are pending
build-gate / build (push) Waiting to run
template-bundle is the scaffold DX copies to start a plugin: its source
ships, but it is not a plugin. The dev manifest already skips it by
name (discover.mjs), while the build published it, so the built studio
listed template-map-a and template-map-b as two blank-looking plugins
that the dev studio never showed.

generate-tree now skips it by the same name rule, so the built
manifest lists exactly what dev lists: 21 bundles, 33 profiles. The
committed tree is brought in line without a full regeneration: the
entry is removed from manifest.json and from the staging page's baked
copy, the two leaf pages are deleted, and the archived gallery is
regenerated from the manifest. It is not added to gatedProfiles, which
is the private-data list.

Verified: check:sri and check-manifest-covers-discovery pass, the
staging page renders with no errors, and a sweep of the built studio
shows 31 plugins and no template rows.
2026-09-25 04:05:59 +00:00
doxa-maps-88655c70
6494b97993 library: PosterWall and SemanticTreeLegend no longer require i18n to render
Some checks are pending
build-gate / build (push) Waiting to run
Both called useI18n() UNGUARDED. That function THROWS ("Need to install with
`app.use` function") on any host that never installed vue-i18n, and seven
bundles here do not — big-picture, doxa-maps-spa, doxa-vision-card,
golden-path-check, people-groups-grid, plugin-studio, stack-demo. In a library
component this widely consumed, an unguarded call is a BLANK PAGE waiting for
the first bare host that renders it: exactly how library FilterDropdown blanked
people-groups-grid, a production plugin, fixed by studio in 6603dc17.

★ THIS IS PREVENTIVE, NOT A FIX FOR AN OBSERVED BREAK, and the distinction
matters. MEASURED: no bare bundle currently consumes either component, so the
blank page cannot be reproduced today — which matches the shepherd's framing
("LIVE today: none; latent debt"). What is fixed is the embedding hazard, and
the gift policy asks for plugins that work "standalone AND embedded".

⚠ THE FALLBACK IS DERIVED, NOT A TABLE. Hardcoding English for these eleven
keys would put a second copy of the `en` catalog inside the component, and two
copies of one fact drift — the exact trap behind the embedBase asset-path bug
this week. Humanizing the key's last segment gives "buttons.prayerList" ->
"Prayer List", "buttons.cardDeck" -> "Card Deck", "legend.columns.name" ->
"Name": readable English that CANNOT go stale, rather than a raw key shown to a
reader. te() answers false so every caller falls through to its own default.

VERIFIED: the fallback produces the correct English for every real key these
components use; with i18n present nothing changes —
check:legend-info-localized PASS (es/ar/zh still translate, 0 blank),
check:poster-card-fits PASS across 5 widths x 3 locales,
check:a11y-button-names PASS, SRI consistent.

I also confirmed 6603dc17 is an ancestor of my HEAD before pushing, per the
shepherd's warning: the FilterDropdown fix is intact in my tree, not reverted.
2026-09-25 04:02:56 +00:00
doxa-maps-88655c70
8810046bee Regenerate the gallery index after taking main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:59:14 +00:00
doxa-maps-88655c70
1e3a0b0b27 Take main
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:59:04 +00:00
doxa-maps-88655c70
d77ca860b3 Arabic did not get a translation, it got a scrambled English one
createAppI18n sets document.documentElement.dir=rtl for Arabic, which is right
for a translated app. Every string in these five profiles is hard-coded English
— measured at ?lang=ar: identical word count to ?lang=en and zero non-Latin
characters — so the flip gained nothing and cost a great deal. On the built
tree it clipped the legend title's first letter against the left edge, turned
30-59% into 59%-30, moved every colour swatch to the far side of its label,
threw sentence-ending periods to the START of lines, and dropped the zoom
controls on top of the search box. Not untranslated: scrambled.

The five profile roots declare dir=ltr until their strings are translated, and
the comment says to take it off on that day. The host page's own dir is left
alone, as createAppI18n already intends.

Arabic now renders exactly as English does: 12,379 of 12,379 pins, full
choropleth, nothing clipped.
2026-09-25 03:58:39 +00:00
doxa-maps-88655c70
14f8388172 Merge main into bundler-2/work
Some checks are pending
build-gate / build (push) Waiting to run
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:57:29 +00:00
doxa-maps-88655c70
344990d186 Research map: centre the active tab without scrolling the host page
On mount the map centred its active tab with

    btn.scrollIntoView({ inline: 'center', block: 'nearest', behavior: 'smooth' })

and 'nearest' does not mean "do not scroll the page". It means "scroll every
scrollable ancestor by the least amount needed", and on a partner page the HOST
is an ancestor. Measured by the embed sweep: a page carrying this map below the
fold jumps to y≈640 the moment it mounts — on a phone, where Simple Research is
the default tab. The reader asked for nothing and the page moved under them.

The tab strip now scrolls ITSELF: compute the offset that centres the active
button inside its own box, clamp it to the strip's own scroll range, and write
scrollLeft. Same shape as SemanticTreeLegend's bringRowToTop, which scrolls the
container it found rather than asking the platform to walk ancestors. An element
that only ever writes its own scrollLeft cannot move anybody's page.

The exemption goes in the SAME commit, per that gate's contract — main never
carries a stale exemption or a red gate on someone else's bug. Both
NEIGHBOUR_KNOWN entries (research-map, research-map-plus-grid) are deleted here.

MEASURED after, with the exemption gone: check:gallery-embed PASS, and the
neighbour phase now reports "research-map leaves the host page alone" and
"research-map-plus-grid leaves the host page alone" on its own evidence — js 13
tabs, iframe 13 tabs, neighbour 13 tabs. The two KNOWN lines still printed belong
to regions-dashboard and are theirs to clear.
2026-09-25 03:57:20 +00:00
doxa-maps-88655c70
5418a79cd8 Take main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:50:20 +00:00
doxa-maps-88655c70
851aa93e16 The manifest published an element name nothing defines
gospel-dashboard.vue declared @element gospel-resources-dashboard. index.js
registers exactly one custom element, gospel-resources-map, and every profile is
selected through its profile-config — which is why all five generated leaves
embed gospel-resources-map and work. The manifest reads that JSDoc line, so the
catalogue offered consumers a tag that resolves to nothing: copy the snippet
into a page and get an empty box. Invisible in dev, where the leaf HTML is what
loads, and only findable on a built tree.

Also retires the numbers shipped alongside it, which described a different
dataset: 3,457 groups with nothing became 1,613 and 347 million people became
208 million when the GRN sweep finished, and the tables profile still called
itself Eight tables over 3,550 resources when it carries eleven over 8,437
across 4,231 languages. The manifest is what a consumer of this map reads first.
2026-09-25 03:50:02 +00:00
doxa-maps-88655c70
7d4be4e86a Reference: the "I want to…" table first, wrong facts fixed, history to a log
Some checks are pending
build-gate / build (push) Waiting to run
- The quick "I want to…" table moved from the bottom to the top, with
  BUILD_ALLOW=1 and the no-key MapLibre option.
- Wrong facts corrected: `bun run dev` serves the Plugin Studio at `/`, not
  the gallery; the build-config `token` row said "leave empty", the reverse of
  the baked public-token decision; `check:shots` now compares bundle hashes.
- Two table rows stranded after a code block rendered as stray pipes; they
  now sit in the preconditions table they belong to.
- Removed a `cd` into a retired branch folder, platform host names and an
  internal catalog repo name.
- The dated measurements moved to a "Notes and history" log. 7,255 -> 3,719
  words. Every link, anchor and script name checked.
2026-09-25 03:48:26 +00:00
doxa-maps-88655c70
d5c283f474 Mobile drawer: an open rail sits above the plugin's own layers
Some checks are pending
build-gate / build (push) Waiting to run
On a phone, opening the studio's rail over the research dashboard left
the dashboard's filter row painted across the drawer. Mounted content
shares the drawer's stacking context, and the dashboard raises its
filter bar to z-index 55 while the drawer sat at 20.

While the drawer is open on a phone it now sits at z-index 1000: at
that moment it is the only thing you can use. Desktop and plugin modal
scrims keep their existing order. Checked at 390px on dashboard-plus-grid
in the studio: the drawer covers the dashboard completely. doxa-maps-spa
shares this layout and gets the same fix.
2026-09-25 03:47:16 +00:00
doxa-maps-88655c70
879f08ba08 upg-100 legend: the title was hardcoded English in every locale
Some checks are pending
build-gate / build (push) Waiting to run
MEASURED on the live Chinese render: the legend header read "My UPG 100 List —
Legend" in full English, inside an otherwise localized panel — beside translated
tabs (区域 · UPG-100 名单) and a translated column header (人口). It was a plain
string literal, and SemanticTreeLegend passes `title` through RAW, so the caller
owns localizing it.

The Driver's gift policy names this exactly: "BEAUTIFUL: no clipped text, no
placeholders, no English leaking." An English title on the plugin chosen as the
mobile showcase is that.

★ FOUND BY SEPARATING CHROME FROM DATA, which is the part worth keeping. A naive
Latin-script sweep of the Chinese render returned 31 hits and would have been a
wildly overstated report. Rendering the same elements in en and zh and keeping
only those that are IDENTICAL narrows it to 24, and those fall in three classes:
  · Mapbox attribution (© Mapbox, Improve this map) — third-party, a LICENCE
    obligation, must not be touched
  · zone and region names (Middle East Core, Sudan) — API DATA; translating
    place names is a domain decision, not a bug
  · the legend title and "UUPGs" — product UI strings, and the only real leaks
So 31 hits reduce to 2, one of which is already carded.

★ AND THE HOUSE STYLE ALREADY EXISTED, so no terminology was invented. This
catalog renders "Doxa Regions" as "Doxa 区域" / "مناطق Doxa" / "Регионы Doxa" —
the proper noun stays Latin, the frame translates. "UPG 100" is treated the same
way across all 11 locales, following a convention the catalog demonstrates
rather than coining acronyms (the trap I stopped at on the UUPGs header).

COMPUTED, not a constant: a locale change must move it. A const captured at
setup keeps the mounting language forever, which is the subtler half of the same
bug. That also forced the definition below useI18n(), since a computed
referencing t() above it throws at setup.

VERIFIED at the rendered layer: en "My UPG 100 List", es "Mi lista UPG 100",
zh "我的 UPG 100 名单", ar "قائمة UPG 100 الخاصة بي" (RTL), ru "Мой список UPG 100"
— 0 clipped in all five, including the longest Russian form.
check:legend-info-localized PASS, check:poster-card-fits PASS, SRI consistent.
2026-09-25 03:46:06 +00:00
doxa-maps-88655c70
a5134c770b Regenerate the gallery index after taking main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:45:06 +00:00
doxa-maps-88655c70
6433d7f92e Take main before landing
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:45:03 +00:00
doxa-maps-88655c70
4fc261f8df Say which film it is: three languages were labelled JESUS Film without one
Five codes still carried the retired ?languageId= URL, which nothing can verify.
Asking Arclight what each language actually has:

  ars  Arabic, Najdi        The Savior                 (8 groups; film was their ONLY resource)
  gjk  Kachi Koli           Magdalena - Director's Cut
  mzn  Mazanderani          Magdalena - Director's Cut
  lia  Limba, West-Central  no feature at all
  jos  Jordanian Sign Lang  left alone, see below

counts.featureFilm >= 1 says a feature exists, not that it is JESUS. Three of
these had a real JESUS Film Project film in their own language under a different
title, and the map called all of them the JESUS Film — the same class of lie as
calling Open Bible Stories Scripture. They now ship under their own names, with
verified deep links; lia's film is dropped, because no component carries it and
a link nobody can check is worse than none.

jos is left exactly as it was. Arclight files iso3 jos under a row whose own
name reads Lebanese Sign Language, while the JESUS Film sits at a different id
named Jordanian Sign Language. The ISO join and the publisher's label disagree,
and resolving that is a data question, not a guess to make inside a fetcher.
2026-09-25 03:44:39 +00:00
doxa-maps-88655c70
5aeff81368 Regenerate after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:44:18 +00:00
doxa-maps-88655c70
7f71ed0ffc Merge commit '580dff6369ae6dce621131a6b112ae2f095af94a' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:44:13 +00:00
doxa-maps-88655c70
90ceac36ba PinHint must not require vue-i18n either, and a gate so the next one is caught
The shepherd reported a P0 I caused: db36397b made library FilterDropdown call
useI18n() unconditionally, which throws "Need to install with `app.use`" on any
host that never installed vue-i18n. people-groups-grid deliberately has none, so
the grid rendered blank standalone, in the studio and in every composite nesting
it -- one of the seven production plugins going to DX. Studio fixed that
instance in 6603dc17.

That fix was NOT on my branch. 6603dc17 was not an ancestor of my HEAD and my
tree still carried the unguarded call, so my next push would have reverted it.
Merged before doing anything else.

PinHint carried the same unguarded call, added by me in 9ba6ad7a. Guarded the
same way: host translations when present, the English literal otherwise, with
te() answering false so callers fall through to their own defaults.

The gate the shepherd asked for is _chk-library-i18n-optional.mjs. It scans
library/components for useI18n() calls that are not behind a guard, and it
separates LIVE from LATENT, because a wall of identical failures gets ignored
while a list of one gets fixed: it computes which bundles install no i18n and
which of those actually import each offender.

Measured: 17 library components still call useI18n() unguarded, 7 bundles
install no i18n (big-picture, doxa-maps-spa, doxa-vision-card, golden-path-check,
people-groups-grid, plugin-studio, stack-demo), and LIVE is currently none --
no bare host renders an unguarded component today. The 17 are debt, not safety.
They are left for their owners rather than rewritten from here, since library/
reaches every bundle.

Two of my own errors on the way, both caught before they reached anyone. I first
reported 14 bare bundles, because I grepped for createI18n when the install is
usually createAppI18n from @map/i18n -- country-posters does install it. And the
gate first reported PeopleGroupDetail as LIVE in people-groups-grid on the
strength of a COMMENT mentioning it: a substring match cannot tell an import from
prose, which is precisely the defect I reported in check:shared-reached a day
earlier and then rebuilt myself. It now strips comments and requires a real
import statement or an element tag.
2026-09-25 03:43:56 +00:00
doxa-maps-88655c70
580dff6369 Studio first run: dev lists what the build lists, and the self-test finds its manifest
Some checks are pending
build-gate / build (push) Waiting to run
Two gaps found by opening every plugin in the studio under the dev
server:

- key-cities is gated (vite.doxa-maps-build-config.json gatedProfiles)
  out of every public surface the build emits, but the dev manifest
  listed it anyway, so the dev studio and the Research Gallery's
  Experimental shelf showed a plugin the built tree deliberately hides.
  The dev model now applies the same gate: 21 bundles, 33 profiles, as
  in the built manifest. readBuildConfig() does not carry gatedProfiles,
  so the gate is read from the config file, as generate-tree does.
- golden-path-check takes its tree base from a tree-base attribute, not
  from profile-config. The studio only passed the latter, so inside the
  studio the check guessed the tree from the page URL, fetched
  /manifest.json, got a 404 and showed an empty self-test. The studio
  now sets tree-base on every element it mounts; elements that do not
  read it ignore it.

Dev sweep after: all 31 listed plugins mount with content, no page
errors, no failed requests. Rebuilt the plugin-studio bundle and
re-baked its SRI.
2026-09-25 03:43:38 +00:00
doxa-maps-88655c70
0907e6ade3 Rebuild the Research Gallery and regenerate the tree after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:38:14 +00:00
doxa-maps-88655c70
2dce74ce67 Merge remote-tracking branch 'origin/main' into rega/research-gallery-embed
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:37:50 +00:00
doxa-maps-88655c70
86fb6e750b Research Gallery: a good guest on the partner's page
Embedded below the fold on a phone-width host page, the gallery disturbed
the page it was a guest on:

- The host's inherited typography flowed into the gallery. Shadow DOM
  isolates selectors, not inheritance, so a host with body{font:20px/3;
  letter-spacing:2px} re-flowed every rail row and legend (root measured at
  20px / 60px / 2px). :host now resets the inherited text properties to the
  gallery's own defaults (16px / normal / normal). Direction still inherits,
  so RTL hosts stay RTL.
- Nested maps wrote the host's URL and title. The gallery now tells every
  child urlSync:false. Regions needs to honour it (routed).

check:gallery-embed gains a good-neighbour phase: phone width, gallery
below the fold, hostile host CSS, the host's own #anchor and <title>. After
load and after every tab it asserts no host scroll, an unchanged URL, an
unchanged title, and no inherited typography. Two nested-plugin violations
are listed, owned and printed in NEIGHBOUR_KNOWN until their owners fix
them (same contract as MAPBOX_KNOWN):
- research-map.vue:647's scrollIntoView jumps the host to y=640 on load on
  a phone.
- Regions' unconditional URL/title sync.

A boot timeout is now a named FAIL instead of an uncaught crash. The gate
caught a real one on the way: a rebuilt bundle whose leaf page still
pinned the old SRI hash.
2026-09-25 03:37:26 +00:00
doxa-maps-88655c70
de7388cbf7 Take main before landing
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:33:49 +00:00
doxa-maps-88655c70
c3d1df30dc The click-sheet verifier had not run in months, and was aimed at the wrong files
It crashed on ENOENT — app/doxa-maps/gospel-resources/assets/tables/, a tree
deleted when data moved out of public/. Nothing noticed, because nothing ran it:
it had no npm script, so it was neither discoverable nor wired anywhere.

The dead path was the smaller half. Its other input was a private by-country
copy under plugin-designs/, and its people-group table was the curated 2,106-row
subset that resources-map.vue stopped fetching some time ago — it says so in its
own comments; the country sheet is built from the 12,379-pin index now. Repaired
in place it would have gone green about a surface it was no longer looking at.

Both inputs are the live tables. The country join is ISO3 instead of a substring
test against a comma-joined name blob, which the file's own comment admitted was
under-counting because country names contain commas. It reads 242 of 242
countries resolving to a pin and 8,435 sheet links, through the same flattener
the sheet renders from, and its self-test now requires four corruptions to go
red rather than two.

Also gives it and the stale-sweep gate an npm script, and corrects the link-rot
entry, which still told readers that jesusfilm.org and fivefish.org can never be
status-checked — the thing that gate stopped believing two commits ago.
2026-09-25 03:33:31 +00:00
doxa-maps-88655c70
ace9c396ff CLI matrix: one status per method that matches the README; history to a log
Some checks are pending
build-gate / build (push) Waiting to run
The per-method sections now lead with install + commands + the one thing that
matters, and the dated measurements move to a "Notes and history" log at the
end. Statuses match the README: iframe and JS embed production, mobile built,
WordPress/Disciple Tools and Tauri proofs of concept. The Tauri artifact sizes
disagreed within the doc (67/60 MB vs 65/58 MB); the log records both runs.
The package.json scripts block that check:quoted-scripts compares is untouched.
2,590 -> 1,922 words.
2026-09-25 03:31:05 +00:00
doxa-maps-88655c70
08dca05f63 Golden path: a six-step table first, the incident notes in a log at the bottom
Some checks are pending
build-gate / build (push) Waiting to run
The first doc a DX developer follows now opens with one table (what you do,
the command, what "done" looks like), then one short section per step with
its command. The nine dated measurement notes move, unabridged in substance,
to a "Notes and history" log at the end. 2,612 -> 1,501 words.

Also corrected two stale facts: the deployment status table now matches the
README (WordPress and Tauri are proofs of concept; mobile is built), and the
Experimental shelf is described as it works, not by a 2026-09-21 row list
that no longer matches.
2026-09-25 03:29:39 +00:00
doxa-maps-88655c70
64ef820f62 Take main before landing
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:29:23 +00:00
doxa-maps-88655c70
7f8ff4c4f8 A filtered export should arrive under its own name
Click a country on the map, follow through to the table, export: you get 264 of
8,438 rows, correctly filtered, in a file called
gospel-resources-every-link-and-who-it-reaches.csv. Do it for a second country
and you get the same filename again, so the second download overwrites the first
or lands as "(1)". The rows were right and the file was unidentifiable, which
is the whole failure for the person this export is for — someone pulling a list
per country to hand to a team.

The filter now travels in meta.title, which is what the download is named from,
so China arrives as -people-s-republic-of-china.csv and German as
-german-standard.csv. Done here rather than by teaching the shared DataTable a
new prop, keeping the decision this file already made about a component with
other consumers, and it fixes the heading in the same move: it read "every
link, and who it reaches" over a table showing one language's six.
2026-09-25 03:29:09 +00:00
doxa-maps-88655c70
0b2e07c830 Merge commit 'bf7e0e64640a633a4bdb132867861d65e694c19d' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:28:15 +00:00
doxa-maps-88655c70
4f7e2342f1 Port their img reset: our photos had max-width none where theirs is 100%
base/_reset.scss's `img, picture { max-width: 100%; display: block }` is a
document-level element rule, so it never reached our shadow root. Measured on a
COUNTRY page, where the uupg photos actually render: their img.uupg__image has
max-width 100%, ours had NONE, so an image wider than its grid cell overflows
our card where theirs is constrained. display already matched, and
height/border-radius/object-fit already match because our .uupg__image class
sets them (100px, 7.5px, cover), so max-width was the whole gap.

_globals.scss:80's `img { border-radius: var(--border-radius-lg) }` is
deliberately NOT ported. On their site it rounds every image because every image
there is theirs; inside our root it would also round the poster-wall previews,
part of a sanctioned addition their page does not have. The elements that ARE
clones already carry the right radius from their own class.

An earlier run reported img as 'absent from BOTH pages' -- `.uupg__image` IS the
img, not a wrapper, so a descendant selector matched nothing. That is the third
time a mis-selector has masqueraded as agreement here, so the probe now fails
loudly on anything it did not actually exercise instead of counting it as a pass.
2026-09-25 03:28:12 +00:00
doxa-maps-88655c70
bf7e0e6464 Dev server: serve assets from data/ and under the tree base, so a fresh clone's plugins get their data
Some checks are pending
build-gate / build (push) Waiting to run
Opening every plugin in the studio under the dev server on a fresh
worktree, 13 of them mounted with empty data: every table, boundary and
people-groups file 404'd. Two causes.

- The dev asset lookup read public/assets (a mirror generate-tree
  writes, absent on a fresh clone) and plugin-designs/*/data, but never
  data/, the tracked source that mirror is copied from. It now falls
  back to data/<rel>, which is what DX gets when they clone.
- Plugins nested in the studio resolve assets against the tree base the
  studio hands them (/__plugin-gallery/), as they do against <tree>/assets
  in the built tree, but only /assets/ was served. Both prefixes are
  served now.

After: the same sweep shows the data 404s gone from all 13.
2026-09-25 03:27:29 +00:00
doxa-maps-88655c70
8415e8e112 README: clone the public repo; no links the giveaway export deletes
Some checks are pending
build-gate / build (push) Waiting to run
- The clone line pointed at the private source repo. It now clones the public
  giveaway, doxa-open/doxa-maps, which clones anonymously (checked with
  ls-remote and no credentials), so a DX newcomer can onboard from the README.
- The README linked AGENT-BRIEF.md, which the giveaway export removes; it now
  points to documentation/CONTRIBUTING.md.
- README-longform's two SPA images were written relative to the wrong folder
  and rendered broken; fixed.
2026-09-25 03:27:17 +00:00
doxa-maps-88655c70
f40ce70e64 Take main before landing
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:20:00 +00:00
doxa-maps-88655c70
3c77ced7ff Regenerate the gallery index after taking main 2026-09-25 03:19:57 +00:00
doxa-maps-88655c70
ec9b7caf17 Take main before landing
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 03:19:35 +00:00
doxa-maps-88655c70
6603dc176b FilterDropdown: translate when the host has i18n, never require it
Some checks are pending
build-gate / build (push) Waiting to run
db36397b made the dropdown call useI18n() unconditionally. That throws
"Need to install with `app.use` function" on any host that never
installed vue-i18n, and people-groups-grid deliberately doesn't (its
labels come through its `t` prop). The grid rendered 2 DOM nodes and a
page error: standalone, in the plugin studio, and inside every
composite that nests it (research-map-plus-grid, stack-demo).

The dropdown now uses the host's translations when it has them and the
English strings otherwise. Verified: people-groups-grid renders with no
error again, and region-dashboards in French still shows "Aucune option
trouvée".
2026-09-25 03:19:23 +00:00
doxa-maps-88655c70
a71f676fc3 Give the country sheet back the number its own footnote depends on
The two curated figures — growing & unreached, deaths a day — sit under a
footnote saying how much narrower their basis is than the pins. It read "covers
391 of these 391 groups — a narrower set than the pins" for every one of the 242
countries, because the script that recomputes country rows from the pins
overwrote the curated group count with the pin count and the footnote went on
reading it. Always false, and false about the one thing it existed to qualify.

The real numbers were still in the commit that introduced the overwrite and are
restored on the rows: China 158 of 391, India 856 of 2,146. They are recorded
once now and never rewritten, because the pins change every build and a curated
analysis does not.

The same block also drew both figures for the 72 countries that have no curated
row at all — "Est. deaths/day 0", which is not a measurement. It is hidden for
them instead. A first pass at the fix handed those 72 a basis equal to their pin
count, inventing the number the footnote exists to qualify; null now means there
is no curated set here.
2026-09-25 03:19:00 +00:00
doxa-maps-88655c70
e32034039d poster cards: assert the preview's alt, which is the card's only name
Some checks are pending
build-gate / build (push) Waiting to run
MEASURED while sweeping my own gates for the count-only pattern: a poster card's
TEXT reads "116 UPG · Show on map" — a count and an action, no zone. The
identity lives entirely in the image's alt ("Northwest India — prayer poster…").

So the wall is CORRECT AS BUILT — all 27 previews carry a descriptive alt, and
sighted and screen-reader readers both learn which poster is which. No defect.
But that alt was doing load-bearing work nothing asserted: remove it and the
wall becomes 27 anonymous pictures for a screen-reader user while every
geometry, clipping, hover and reachability assertion here stays green.

★ I ALMOST ASSERTED THE WRONG THING. The obvious move on seeing "no zone name in
the card text" is to require one. That would have been me inventing a
requirement nobody stated — the same line I drew on the card-stock labels and
the UUPGs header. Measuring first showed the identity was already there, in the
attribute rather than the text, so the right assertion protects what exists
instead of mandating what I would have preferred.

MUTATION-TESTED: emptying the alt binding fails all 15 width x locale
combinations with "27 poster preview(s) have no alt text", exit 1. Restored,
gate green, SRI consistent.
2026-09-25 03:18:31 +00:00
doxa-maps-88655c70
4046abd551 Take main before landing
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 03:10:25 +00:00
doxa-maps-88655c70
320b75cb74 Name the two pools the group sheet was searching in silence
Open a group in the gap and the sheet listed five pools that found nothing —
Scripture, film, FCBH audio, Bible Stories, Zume — and said nothing at all about
Global Recordings Network or Scripture Earth. For 1,220 groups GRN is the only
resource there is, so silence about it at the moment of decision does not read
as "searched and empty", it reads as "nobody asked". Both now have an absent
case, with the pool named.

The GRN chip asks the data whether the pool was really searched, and that is the
part worth keeping. The sweep ran against the gap first and stopped there, so
3,202 codes that already carry resources have never been asked — the first
version of this chip told German, which certainly does have Words of Life, that
none was found. The build had recorded the truth per code all along: a swept
code lists grn under checked, an unswept one carries "grn (not yet swept)".
The chip reads that instead of assuming the sweep was universal.
2026-09-25 03:10:11 +00:00
doxa-maps-88655c70
de3d8dda22 README: show the iframe and JS-embed snippets, shaped like manifest.json ships them
Some checks are pending
build-gate / build (push) Waiting to run
Host as a placeholder and the SRI hash elided (it changes every build), with a
pointer to manifest.json for the exact copy, so the README cannot go stale.
2026-09-25 03:09:07 +00:00
doxa-maps-88655c70
cbbebffb95 Docs: every build line a reader copies now carries BUILD_ALLOW=1
Some checks are pending
build-gate / build (push) Waiting to run
The build refuses (exit 75) without BUILD_ALLOW=1, so a DX developer
following the tutorial, the golden path or the CLI matrix hit a refusal on
the first build. The eight runnable lines now include the flag, and the
golden path says why it exists and how to build one plugin without it.
Also: the golden path said "From doxa-maps-plugin-studio/", a retired branch
name; a clone lands in the repo root.
2026-09-25 03:07:52 +00:00
doxa-maps-88655c70
ff0be67bca README: name what is on the Experimental shelf; retake eight stale shots
Some checks are pending
build-gate / build (push) Waiting to run
The Research Gallery tree said only "every other bundle". It now lists the
five the shell actually shows today, in its own order: prayer-walk-planner,
upg-neighbor, all-people-groups, country-posters, doxa-vision-card
(22 bundles - 7 curated tabs - 10 in NOT_EXPERIMENTAL).

Eight screenshots were shot from bundles that have since been rebuilt;
retaken from the current build and checked by eye.
2026-09-25 03:05:09 +00:00
doxa-maps-88655c70
2087074e9d Stop telling readers the audio sweep is unfinished — it finished
Some checks are pending
build-gate / build (push) Waiting to run
The legend's caveat is the paragraph a reader uses to decide what "nothing
found" is worth. It said GRN was being swept a batch at a time and part of the
pool was still unread. That was true while the crawl ran and false the moment it
ended, and it stayed on the page through four commits that knew better. Found by
reading a screenshot, which is the only thing that could have found it.

Corrected on all three surfaces, and scoped honestly rather than upgraded: the
sweep finished for every one of the 1,192 languages in the gap, so "nothing
found" no longer rests on an unread pool — but codes that already carry a
resource were not all asked, because the sweep was spent on the gap first. Both
halves are now on the page.

A gate holds it. The DATA decides whether the claim is allowed: while codes are
genuinely undecided it passes, and it starts failing the moment they are all
decided, so it re-arms itself for the next slow pool. It does not flag a claim
rendered under a v-if on the pending count, because that form expires on its own
and is the one to copy. Its first version asked about every code on the map
instead of the gap, measured 3,202 undecided and would have let the stale
sentence stand.
2026-09-25 02:57:50 +00:00
doxa-maps-88655c70
8a3d836e44 packets: assert a people-group card actually NAMES someone
Some checks are pending
build-gate / build (push) Waiting to run
The gate counted .card elements and checked the photo/typographic split. A card
with a photo and a BLANK NAME satisfied all of it: the total held, the split
held, the page arithmetic held. Counting proves the element exists; the spec
says pages 2..n carry "the people groups, 20 per page, with photos", and a slot
that names nobody is not an entry in a prayer list.

★ MY FIRST VERSION FAILED ITS OWN MUTATION TEST, and the reason is the useful
part. It tested card.textContent. Blanking every name still left 18 characters
per card, because the typographic fallback ("no photo available") lives INSIDE
.card — so the card was not empty, it just said nothing about a people group.
The assertion passed a packet where all 95 names were gone.

Now it measures .nm, the name line the renderer writes (packet-render.js:229).
Same lesson as the nophoto count one level up, and as the consumersOf substring
test: match the thing you mean, not a container that happens to include it.

MEASURED on laos: 95 names, 0 blank, shortest "1. Aheu" — a threshold of 3 sits
far below any real entry and above an empty one.

MUTATION-TESTED against the mutation that defeated the first version: blanking
<div class="nm"> fails with "95 of 95 people-group card(s) have no NAME",
exit 1. Restored, and check:packets-all stays green across all five shapes.
2026-09-25 02:51:50 +00:00
doxa-maps-88655c70
1b08b3fcac Regenerate after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 02:50:48 +00:00
doxa-maps-88655c70
c7942ae7f7 Merge commit '28b0e505b788e47e028287144bb8039c3a68fc25' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 02:50:22 +00:00
doxa-maps-88655c70
baa5e72e5c The title and strapline sat 10px tight, because we recreated a reset as a class
Their base/_reset.scss zeroes margins with
`body,h1,h2,h3,h4,p,figure,blockquote,dl,dd { margin: 0 }` -- a bare ELEMENT
selector, specificity 0-0-1. Their `.stack > * + *` is 0-1-0, so the stack wins
and their heading gets its 10px.

We recreated that reset as class rules, `.rd-title{margin:0}` and
`.rd-summary{margin:0}`. Those are ALSO 0-1-0, because `*` contributes nothing
to specificity, so `.rd-head > * + *` and `.rd-title` tied and source order
decided it -- the later class rule won and the stack margin was discarded.

Measured on live /regions/africa, their head is a(0px), h1(10px), p(10px).
Ours was h1(0px), p(0px): the title and strapline sat flush against the back
link, 10px tight, on every region and country page. Porting the reset as the
element selector it actually is, and deleting the two class-level margin:0
declarations, restores their cascade exactly -- both now read 10px.

Found by sweeping for the defect CLASS rather than the instance: for every
`> * + *` container in our sheets, any child that is not the first and still
computes margin-top 0 has lost the same race. That turned up three candidates,
and measuring THEIR page settled the third -- their .region-goal is 0px too, so
our .rd-goal matching at 0px was never a defect. A heuristic found the
suspects; only the live measurement could tell a defect from a faithful copy.

This is the fourth of their global rules that the shadow boundary changed the
behaviour of, after box-sizing, the list bullet and prefers-reduced-motion --
but the first where the rule DID reach us and the port altered its specificity.
Copying a global element rule as a scoped class rule is not a faithful port even
when the declaration is identical.

reset-vs-live now reports 0 differences; region-vs-live 4/4, list-vs-live 21/21,
table rows 4/4, SRI passes.
2026-09-25 02:50:05 +00:00
doxa-maps-88655c70
28b0e505b7 Point the landing recipe at the rebase hazard it can create
Some checks are pending
build-gate / build (push) Waiting to run
Landing by rebase leaves the old branch tip orphaned, and reconciling it with a
-s ours merge is how this branch got one. The rule against rebasing over such a
merge is further down the file; the recipe that creates the condition now says
so where the work happens.
2026-09-25 02:50:05 +00:00