Twelve packet pages and seventy prayer cards, rendered from the current
renderers (people-based knows-Christ page, page-filling grids and charts,
sharp card photos) and shot in the fleet's shared browser: pages at 1500px
reduced to 1200px wide, cards at 480x672, WebP q80, 2.8 MB. They ship under
previews/examples/middle-east-core/, which the existing previews staging
copies into the tree, and the popup's pages and deck examples read them.
Middle East Core is the packet covering the most people in unengaged groups
(IMB GSEC 0-1).
At 390 the desktop offsets landed on each composite's own top chrome: Regions' and UPG-100's view
selector and Gospel's search box (probe, en and ar). A tab's pos can now carry `narrow`, laid over
it while the shell is narrow (max-width:760px): Regions and UPG-100 put the button at the START of
their strip, which is empty on a phone; Gospel takes the corner above its zoom column. The same
probe then reports no interactive element under the button on any tab, 1440 and 390, en and ar.
The one-time pointer still lies over the selector or search box until first opened; it takes no
clicks (pointer-events:none).
The stored poster (data/posters) and its wall preview predated the ink pin
badge: 57 pin numbers drawn white on the zone hue, 1.44:1. They are what the
UPG-100 info popup shows and opens ("Open full size"). Regenerated from the
current poster-render in its stored 3x4 format (1500x2000) and published;
the pin numbers are now white on #1c1c17, 17.10:1. The preview and the
1500px download are made from a 3000px shot of the regenerated sheet with
make-previews' own sizes and qualities (a 3x4 sheet now makes a 420x560
thumbnail; the wall letterboxes it). The other 27 stored zone posters are
fallback-only and follow after the demo.
A geometry probe (every visible interactive element in every shadow root, intersected with the
button's box, 7 tabs in en and ar at 1440) found the default position covering each composite's Share
button exactly, which no screenshot showed. They take side 56px like the map tabs. With that, the
probe reports no element under the button on any tab in either direction; before this branch it
reported Zoom in/out under it on Prayer Progress, Cities and Gospel and Share on Big Picture.
landing-preconditions step 4 named country-posters, my-upg-100-list and
regions-dashboard; each built with BUNDLE, then tree, then fix:sri. No source of
another plugin changed -- their bundles catch up with the shared library they
already import.
The rule lives in library/packet/posterSheets.js so the browser and the Node
generator cannot disagree, which puts it in every bundle that generates a country
sheet. landing-preconditions step 4 named country-map, country-posters and
regions-dashboard; each built with BUNDLE, then tree, then fix:sri.
No source of country-posters changed -- only its compiled bundle catches up with
the library it already imported.
mapInfo.tab.research-map-plus-grid.examplePacket ("Example packet: Middle
East Core"), the titleKey the gallery puts on that tab's pages example so
the packet reads as an example zone. The gallery bundle carries the mapInfo
area, so it is rebuilt.
Big Picture's export is a spreadsheet, so its example is a kind:'table' excerpt (sr): the six largest
groups by population, of 2,107, with the live table's own column keys (dashboard.people_group,
.country, .region, .population), so the popup header and the table's header cannot drift.
Cost, measured: those keys bring the 'dashboard' area into the gallery bundle, which grows from
369,635 to 420,172 bytes (123,536 to 136,998 gzipped, +13.5 KB). The alternative, header keys
duplicated into 'mapInfo' in eleven locales, is a translation job for after the demo.
The page reused the life calendar's grid and counted its shaded boxes in
weeks ("0.61% · 32 weeks"), under a week axis and a year axis, with a lede
calling it "one human life in weeks". On this page a box is an equal share
of the list's PEOPLE, so the same 5,200 boxes are now drawn with no week or
year axis, and the legend states each side in people: how many follow
Jesus and how many do not yet, each with its share (the two percentages
sum to 100 as printed), plus how many people one box stands for. Numbers
are formatted in the reader's locale.
The labels become "Followers of Jesus" / "Not yet following Jesus" and the
lede drops the weeks, in all eleven languages; the served strings file is
re-pinned. check:evangelical-share asserted the weeks figure; it now
asserts the people totals, the bare percentage and the absence of weeks
and axes, with the life page (which keeps its axes) as the negative
control. check:packet-variants found the grid by an exact class string
and now matches the class prefix.
The pane re-forward is compiled into this bundle, and landing-preconditions
step 4 named regions-dashboard as the only consumer the library change reaches.
Built with BUNDLE=regions-dashboard, then tree, then fix:sri.
Each production tab reads mapInfo.tab.<tabId>.lead and .takeaway, and Regions also its note, with
the India/China reasoning beside it. Only files already in the tree are listed as examples (the
UPG-100 poster); the rest join when their shots land. A tab's button renders only when its lead
key resolves in the bundled catalogue, so a key that has not landed yet hides the button instead of
opening a popup of raw key names.
Three defects on country-map/country-map, which is production -- DX Life's
country pages. Found by auditing the element rather than from a report.
1. ANY profile-config change refetched all 2,106 records. The watch getter
returned a NEW ARRAY literal, and Vue compares a watch value with Object.is,
so a fresh array always reads as changed; both computeds read the injected
cfg, so the effect re-ran on every config change and the callback then fired
unconditionally. MEASURED with real Vue: flipping isDark refetched the whole
endpoint and redrew the map, and so did a host adding pinHint. A joined
STRING compares by value, so only a real language or country change refetches
-- which must still refetch, because the API localises the names.
This was latent waste until the composite re-forward made config writes
routine. It is now on every upstream locale switch, so the two changes are
complementary rather than independent.
2. A SLOW EARLIER RESPONSE OVERWROTE A NEWER ONE. loadPoints is async and had no
sequencing at all -- 0 abort or generation tokens in the file -- so two runs
could be in flight and the one that STARTED first could FINISH last. Both
assigned points, so the map showed the previous country's pins with no error
and nothing for the reader to notice. Every run now takes a ticket and a stale
ticket writes nothing: not the points, not loadFailed, not the spinner the
current run is still using. fetch is left to complete rather than aborted,
because the response is cached for the next switch back.
3. `loading` WAS WRITE-ONLY: assigned in three places and read in none, zero
template references. A reader on a country page waited on an empty map -- no
outline, no pins, no message -- which is indistinguishable from a country that
genuinely has no people groups, and this element fetches the whole list before
it can draw one pin. "Loading results..." was already in the catalogue in all
eleven locales, so saying so cost no new string and no parity debt.
check:country-map-refetch guards all three. It drives the REAL reactive chain
with real Vue and takes the watch key expression OUT OF THE SHIPPED SFC, because
bug 1 is entirely a question of what Vue considers changed and is invisible to
any test that fakes reactivity or re-types the watch.
Mutation-tested, 5 mutations, all caught by name. The first version of the gate
MISSED one: a bare /if \(!current\(\)\) return/ grep matched the guard in the
catch block, so removing only the guard before the points write still passed.
The assertions are positional now -- the guard must sit immediately before the
assignment it protects -- and the catch guard is asserted separately. That hole
existed because the behavioural half of the test reproduced the guard's shape
inside the gate instead of reading the component, which is the self-consistent
oracle this repo's other geometry gate explicitly warns against.
Registered in scripts and gates:pre, verified by re-parsing: one key, 107
checks. Bundle rebuilt (BUNDLE=country-map), then tree, then fix:sri.
A key in a map's own area either renders blank in the gallery or, named literally, pulls that map's
whole catalogue into the gallery bundle (internal/i18n-areas.mjs). Measured with areasForBundle:
development's doxa-maps-spa carries buttons, grid, messages; this branch carries buttons, grid,
mapInfo, messages, and no upgMap. The UPG-100 entry reads mapInfo.tab.upg-100.lead and .takeaway,
which upg100 adds with every tab's strings.
The library MapInfo (an info button, a one-time pointer and a popup of real, pre-generated output)
is mounted in each pane whose tab has an entry in src/tabInfo.js, with the gallery's live locale and
its assets base. UPG-100 is the first entry, with its poster; its packet pages and card deck join
when their files are in the tree, because a missing file would show "not available" to the audience.
Regions and the other tabs follow as their owners' keys land. profile-config info:false removes the
buttons.