Every map translated its own chrome but left the basemap's country, city
and street names in the style default (English on Mapbox, Latin + native
on OpenFreeMap) in all eleven locales. A reader in Arabic saw an Arabic
legend over an English map.
library/maps/localize-labels.js uses each engine's supported mechanism:
- Mapbox: map.setLanguage(lang). It also localizes the Standard style's
IMPORTED basemap, which a layer rewrite cannot reach.
- MapLibre: basemap symbol layers point at
['coalesce', name:<lang>, name:latin, name]. English restores each
layer's original expression, re-captured on every style load so a theme
switch never restores the previous style's labels.
Only layers whose text-field reads an OSM name field are touched. A
plugin's own label layers (zone titles, group names) are never rewritten.
useMapInstance applies it on every style.load and when the reader's
language changes (ProfileLoader's 'lang', else profile-config locale, else
?lang / <html lang>). The Prayer Walk Planner drops its private copy for
the shared one.
Measured on the Prayer Progress map, ar/zh/ru/hi:
- Mapbox getLanguage() == locale in all four.
- MapLibre: 13/14 symbol layers localized, and the plugin's own label
layer untouched.
Read in the Arabic screenshots: القاهرة, الإسكندرية, السويس, الأردن on
both engines. check:gallery-embed (+ :maplibre) and
check:prayer-walk-planner pass. The shared change reaches all 11 bundles
that compile initializeMap, and all are rebuilt here. check:shared-reached
marker basemap-labels-localized: 10/10 consumers.
The table offered 107 groups a next step the map did not show. The candidate is
now computed once, in the pin build beside the curated bridge, and both surfaces
read it — the same rule that put the tier in one place. 1,167 candidates across
the research index; 107 of them reach Doxa groups the curated bridge misses.
The map's sheet renders it as its own weaker panel, and the words carry the
limit: "Possibly a way in — worth checking locally", with the language, how
many groups there speak it and what share of the country they are, and "not
ranked and not recommended". No strength, because none can honestly be
computed. Never for a sign language.
A REGRESSION I INTRODUCED AND ALMOST SHIPPED. The edit that wired this used
pinCandidates without declaring it — a string replace that matched nothing and
said nothing. The ReferenceError died inside the custom-element boundary, so the
map never mounted AND the page reported zero errors. My probe then failed with a
30-second timeout that I had caused separately, by passing playwright's timeout
as the waitForFunction ARG rather than its options, which had been silently
using the default in eleven scripts. Two faults stacked: a broken page that
looked error-free, and a probe too impatient to say so.
Zero page errors is not the same as working, and that is the only reason this
was caught: __grState never appeared, which no assertion about pins could have
explained away. Verified after the fix: 2,107 pins, gap cuts to 370, the
candidate panel renders for Abai Sungai naming Malay, and 25 Deaf sheets still
show no bridge at all.
E RESIDUALS, from rega's in-gallery re-sweep. All three lived in SHARED
components, which is why a grep of my own plugin found nothing:
SearchFilterHeader its label map was a literal object of English defaults —
the fallbacks every host gets, since no host passes
`labels`. Filters, Selected, World and the chip remove
button now come from the catalogue; a host's own labels
still win, which is what the prop is for.
ResearchMapFilterPanel the two "Any" options and the "{n} active" pill.
DashboardFacetRail the Expand/Collapse aria template, which repeats per row:
one untranslated template is dozens of English strings in
a screen reader.
dashboard.config.js the facet SECTION titles (People, Place, …). The earlier
pass converted `label:` and missed `title:`.
★ AND A LOOKUP BUG THAT LOOKED EXACTLY LIKE A MISSING TRANSLATION. 'Any' and
'0 active' stayed English after being catalogued, because the panel asks for
`controls.<key>` while I had authored them under `dashboard.<key>`. The
catalogue had the words; the component was looking one namespace over. Keys now
live where the panel reads, and its lookup falls back across both, so a string
authored in either place renders.
MEASURED after, ru, on the built tree: 0 English chrome strings on Simple
Research and 0 on Big Picture.
One probe correction worth keeping: a substring match on 'People'/'World'
flagged 'South Asian Peoples' and 'Arab World' inside aria labels whose frame
was already translated — data, not chrome. Whole-string matching separates them.
TOUCH TARGET (regions' phone sweep): .rd-proj "🌐 Globe" measured 74x23 at
390x844, one pixel under WCAG 2.5.8 AA. min-height:24px under
@media (pointer:coarse) — stating the 24 rather than growing padding, because
the height is padding plus a line box the renderer derives from the font and the
arithmetic lands at 23.7. Mouse sizing untouched. MEASURED after: 73.5x34.
Every bundle embedding the changed shared components is rebuilt in this commit —
a shared fix is not landed until its consumers are.
composition/_switcher.scss has three rules and the clone carried two. The third —
`.switcher > :nth-last-child(n + 6), .switcher > :nth-last-child(n + 6) ~ *
{ flex-basis: 100% }` — gives every child a full row once there are six or more,
instead of letting them share. It is now ported onto .rd-switcher.
It changes nothing today and that is measured, not assumed: the rendered page has
3 direct children at widths 372/372/372, the same three goal cards their
RegionDetail.vue lays out, so a threshold of 6 is out of reach. It is carried
because the brief is an exact clone, and because a fifth or sixth goal card would
otherwise diverge silently from doxa.life — a one-line rule now, or a puzzle later
about why six cards behave differently here.
The other two rules in that file are deliberately NOT ported, which is a
measurement rather than an oversight: .switcher-item{display:flex} never applies
because their switcher's children are `.stack | card`, not `.switcher-item`, and
.three-part-switcher belongs to a component the regions page does not use.
The probe proves both halves, because a rule that cannot be shown to fire is
decoration. It reads the real switcher, asserts no child has flex-basis:100% at
the live count, then clones cards INTO it until there are six and reads the basis
back as 100% before restoring it. Two of my own errors are recorded in it: a first
run drove the page with ?region=africa, got the chooser, and reported "no
.rd-switcher" — a reading of the wrong page state, since the selection comes from
the hash; and a second hardcoded "+2 clones = 6" from a static tag-count that said
4, so it only reached 5 and called the rule dead when its own arithmetic was wrong.
It now derives the clones to add from the count it just measured.
A row of phone screenshots (390x844, taken after each map finished loading)
under the production gallery: Regions, UPG-100 List, Prayer Progress, Cities
with Internet and Gospel Resources. Big Picture is desktop-only by ruling, and
Simple Research is left out until its phone layout is finished: its "Tap a
coloured dot" prompt still covers the Progress legend at 390px. The README
says both plainly.
repo reported it: the Capacitor app launches public/index.html, which
mounts <plugin-studio>. A recipient opening the phone app meets the DEV
STUDIO rather than the map the README promises. Reproduced in the synced
Android assets before touching anything.
RULED (Kingdom-agent, card DECISION-MOBILE-EXAMPLE-IS-UPG100-NOT-SIMPLE-
RESEARCH, done): the ONE mobile-app showcase is the UPG-100 list.
⚠ THE CARD'S OWN BLUF SAYS "Simple Research" AND IS STALE. It predates
that ruling, which is recorded DONE in the same backlog and says Simple
Research "was never mobile-friendly". Building to the card's wording
would have shipped the app the ruling rejects, so this builds to the
ruling and the card is annotated with why.
WHY IT RUNS AFTER cap sync AND WRITES ONLY INTO THE ANDROID ASSETS.
public/index.html has THREE consumers, which capacitor.config.json
documents: Railway/CDN, Capacitor, and local staging. The studio is
CORRECT for the CDN root and for `bun run serve` — only the phone build
wants a different landing page. Rewriting the shared root would fix the
app by breaking the other two. cap sync copies webDir into
assets/public/, so overwriting the COPY changes what the app opens and
leaves the served tree untouched. The studio is kept at studio.html
rather than deleted; a developer with the apk still wants it.
WHY IT COPIES THE BUILT LEAF RATHER THAN HAND-WRITING A HOST PAGE.
The leaf carries an SRI integrity hash. A hand-written page needs its
own correct hash, and a stale one makes the browser REFUSE the script
with no console error: the element never upgrades and the app is a blank
screen. Copying the artifact the build already verified means there is
no second copy of that hash to drift — confirmed, the hash still
matches after the rewrite. Only the relative depth changes, and the
script asserts every rewritten reference resolves to a file that exists
rather than trusting its own regex.
MEASURED by serving the ANDROID assets tree (not public/, which is the
whole point) on a private port, with the app-root marker checked before
any number was read:
390x844 mounted+upgraded, studio absent, 27 poster cards, 0 4xx, 0 errors
1280x900 same
check:app-start-page is mutation-tested: restoring the studio page as
the app root makes it exit 1 naming <plugin-studio>.
manifest.json, the staging page's baked copy and the regenerated
gallery, by the same rule generate-tree now uses (map profiles only).
check:sri passes; no non-map snippet carries a preload.
The card asked for Vue to be bundled inline to make maps faster. It
already is: vite.config.js externalises only jspdf, and every IIFE
carries its Vue runtime. The measurement (internal/measure-map-load.mjs,
cold, 10 Mbps / 60 ms, served built tree) found one serial runtime
fetch before first paint, on partner pages only. A page that pastes the
embed snippet has no leaf page to fetch mapbox-gl.js (~480 KB gz), so the
bundle requested it after it had downloaded, run and mounted. Leaf pages
already load it in parallel.
Map profiles' script snippets now start with a preload for the engine,
at the URL read from useMapInstance.js ENGINES.mapbox.js so the two can
never differ. It has no crossorigin attribute because
ensureEngineScript injects a plain <script>, and a mismatched preload is
not reused. Non-map profiles get no preload. A comment tells a partner
who switches to provider "maplibre" to delete the line.
Partner embed, before -> after (2 cold runs):
doxa-simple-map engine start 1.06 -> 0.20 s, done 2.26 -> 0.95 s,
style start 2.73 -> 1.94 s
regions-mfe engine start 2.67 -> 0.11 s, done 3.25 -> 1.13 s
(style start unchanged: the composite mounts its map late)
gospel resources engine start 0.81 -> 0.27 s
One engine request per run: the preload is reused, never a double
download. The time until tiles are loaded is dominated by tiles, data
and drawing, and moved within noise.
Held, per dx-shepherd: no shared Vue, no inlined mapbox-gl, and lighter
basemaps are for the Driver to decide.
The doxa.life script embed was blocked on a server change: the live root
manifest.json answers without Access-Control-Allow-Origin, and the gallery
fetch()es it cross-origin, so no tab could mount. Measured on nuxtbase,
though, the same static layer DOES send ACAO on every path inside a folder
of the tree (doxa-maps-spa/<anything>, even a 404; assets/…).
So generate-tree writes a byte-identical manifest copy at
doxa-maps-spa/manifest.json in the same step as the root one (they cannot
diverge). The gallery reads it FIRST when the tree is on another origin (no
CORS error in the host's console) and the root first when same-origin.
Script URLs still resolve against the tree root, never the copy's folder.
check:gallery-embed:live-cors serves the tree with the live rule
(serve-strict LIVE_CORS=1: no ACAO on the root manifest only).
- The current gallery under it: "did not boot within 60s" (negative
control, reproducing doxa.life).
- With this change: js 14, iframe 14, neighbour 14, linked 4. The default
gate also passes all phases.
check:embed-targets, check:drop and check:public-hygiene pass with the copy
present.
The server-side CORS rule (serving/traefik-embed-headers.yaml) is still the
right long-term fix, and it needs server access. This unblocks the embed
without it once the tree is published.
- A gallery of the seven production maps (Big Picture, Simple Research,
Regions, UPG-100 List, Prayer Progress, Cities with Internet, Gospel
Resources) plus the Research Gallery, each shot from its own standalone page
after its map finished loading. Five new shot definitions in readme-shots.
- One 5 x 5 table: every capability ships every way (iframe, mobile and JS
embed today; WordPress and desktop proven, not yet in production).
- The "map cruncher" converge/diverge picture, folded.
- Repository layout shows the plugin-designs and shared library trees, and a
folded "plugin or library?" section states the rule: write your app in its
own plugin; put code in the library only if it is reusable and parameterized.
- Stale shots retaken from the latest builds.
Closes the README parts of DOCS-README-FOR-A-VISIONARY and BUILD-README-COMPLETE-HANDOFF.
Every conflict was a build artifact — another agent had pushed a full rebuild of
all 23 bundles. Minified .js and baked-SRI leaf pages are generated files: merging
their text is meaningless and can produce a bundle that matches neither side, so
they are resolved by taking one side and then rebuilding from the merged source.
The incoming diff carries 68 source files, among them library/composables/
useMapInstance.js and the dashboard/gospel catalogues in 10 locales, so the
rebuild is all 23 bundles and not only the ones I touched — regenerating the tree
without rebuilding has shipped a stale .js here before.
The curated bridge covers 168 of Doxa's 369 with nothing, because it comes from
a curated subset rather than from the data. A candidate IS computable for the
rest: the largest language in their country carrying both Scripture and the film.
WHAT I COULD NOT COMPUTE, AND THEREFORE DO NOT CLAIM. Ranking those candidates
needs a notion of "is this a lingua franca", and neither available proxy
survives contact. By GROUP COUNT, Oman picks Swahili — two groups — for an
Arabic-speaking people. By SHARE OF PINNED POPULATION, a 10% floor REFUSES Urdu
in Pakistan at 7.3%, the national language, because few Pakistani groups are
pinned as Urdu speakers. That is the same bad-denominator mistake this map
already fixed on the choropleth. So no strength is invented: the candidate ships
with its group count and its share, under column names that say "candidate",
and a reader who knows Pakistan can see Urdu at 7.3% and know what to do with it.
A score would have thrown it away on their behalf.
AND THE ERROR CAUGHT BEFORE SHIPPING. The candidate logic was offering 89 Deaf
groups a SPOKEN language — Deaf Afghans to Turkmen, Deaf Arubans to Dutch. That
is not a weak bridge, it is not a bridge, and a mobiliser acting on it would be
sent to do something that cannot reach the people. This build already refuses to
search GRN for sign languages because GRN publishes audio; the candidate logic
had inherited the gap. Now: 92 Deaf groups are offered none, and their gap is
named as the access problem it is.
Of the 369 with nothing: 168 curated bridge, 107 computed candidate, 92 Deaf,
2 with neither.
inheritAttrs:false on the studio root, so a host's attributes (style,
class, id) stay on the <plugin-studio> element instead of also landing
on the layout inside it: the trap that made the Prayer Walk Planner
76px tall in the gallery. Layout verified unchanged in four hosts on the
built bundle: its own page (shell 1280x900, body 836), a bare partner
snippet (85vh default), a host that sets its own inline height (the
style stays on the host; the shell root has no style attribute), and
the app-shell mounter from BUG-EMBEDDED-PLUGIN-STUDIO-BLACK-SCREEN,
display:block forced on the host: body 656px, rail and tree rendered.
That bug no longer reproduces on the dx-maps side.
internal/measure-map-load.mjs: per production map, the cold, throttled
waterfall (bundle, engine script, style, data, basemap up, visible
tiles loaded) on a served built tree, from the leaf page or, with
--embed, from a partner page that pastes only the embed snippet. It
showed that Vue is already inlined in every bundle and that the engine
waits on the bundle only on partner pages.
The access table could say "nothing found" and stop. For 168 of Doxa's 369
that is not the whole truth: a language already spoken widely in their country
has Scripture and the film. Afitti in Sudan has nothing of their own; Sudanese
Arabic, spoken by 65 groups around them, has both. That is the difference
between no way in and a way in that is not their heart language, and it is the
next action a mobiliser can take — which is why it belongs in the EXPORT and not
only in the map's sheet, where it has been sitting unexportable.
Four columns: the bridge language, its ISO code, the strength, and how many
groups in that country speak it. Keyed by code|country, because the same
language bridges differently in different places.
IT CLOSES NO GAP AND THE DEFINITION SAYS SO. A bridge is a way in, not a
translation. These 168 are still counted in "nothing found", because they still
have nothing in the language they think in, and the first believers among them
still cannot be trained from a text that does not exist.
The other 201 have nothing AND no bridge. That number is worth having on its own:
it is the isolated set, where neither their own language nor anything spoken
around them carries the gospel.
Verified end to end: the CSV exports 2,107 rows carrying Bridge language, Bridge
ISO, Bridge strength and Groups speaking it, and the Afitti row reads Arabic,
Sudanese.
library/maps/rtl-text.js installed the RTL text plugin for MAPBOX only
(window.mapboxgl, from api.mapbox.com). So every MapLibre map, i.e. every
keyless embed, drew RTL place names as disconnected, reversed glyphs.
Measured on the Prayer Walk Planner's printed Arabic sheet.
Under it was a second gap. Bundle entries call the installer at LOAD
time, before useMapInstance has lazily injected a MapLibre engine, so even
an engine-aware installer found nothing to install into.
- rtl-text.js is engine-aware. Mapbox is unchanged (same URL, same lazy
call). MapLibre gets the same plugin package from jsDelivr, keyless and
not api.mapbox.com, so keyless pages still make zero Mapbox requests.
New ensureRtlTextPlugin(gl) waits until it is LOADED, for callers that
capture a single frame (print).
- useMapInstance installs it the moment an engine global exists, the one
point every map passes through.
- The planner drops its private copy for the shared helper.
Measured: a print map of downtown Cairo (MapLibre, keyless) shows shaped
Arabic street and place names (شارع التحرير, مسجد الطباخ …); plugin
status "loaded", fetched from jsDelivr. check:gallery-embed passes js,
iframe, neighbour and linked (14 tabs); :maplibre passes;
check:prayer-walk-planner passes.
The shared change reaches all 11 bundles that compile initializeMap, and
all are rebuilt here. check:shared-reached gains an rtl-text-maplibre
marker (10/10 consumers carry it; cities-internet, gospel-resources and
my-upg-100-list boot maps another way and are not affected).
Also: the new Experimental bundle pplr-data-maps loads the Mapbox geocoder
under maplibre (pplrData.js:38). It is an owned, printed MAPBOX_KNOWN entry
routed to its owner; delete it in their fix commit.
About thirty browser gates could pass while describing another worktree's build.
This machine runs several checkouts and a default port belongs to whichever
server started first — measured 2026-09-24, :4173 was another worktree's, and an
hour of probing reported confidently on code nobody here had touched. check:sri
cannot see it: a stale tree and its stale leaf agree perfectly.
Twenty gates now call requireThisTree() right after they resolve their target and
exit 75 on a mismatch — EX_TEMPFAIL, the code this repo already uses for "not
measured", which the wave runner reports as ⊘ rather than ✗. Exiting 1 would turn
a mis-pointed probe into a red product gate, which is the failure this whole
family of checks exists to prevent.
★ NARROWER THAN THE PROBLEM STATEMENT, ON PURPOSE. Three classes do NOT get it,
because there it would be wrong rather than missing:
self-serving gates start their own server on an OS-assigned port
file:// gates read this checkout directly — no server to be wrong about
remote targets verify-map-paint defaults to the LIVE site, and "does
production match what we built" is a deploy question asked
deliberately. MEASURED mid-rollout: it reported NOT
MEASURED against the live host, which was the guard being
wrong, not the gate. It now enforces on localhost only.
TWO THINGS THE ROLLOUT TAUGHT, both caught by running rather than reading:
· node --check passes a file that will die on ReferenceError. Adding a ROOT
constant to sixteen gates that do not import resolve/dirname/fileURLToPath
parsed perfectly and crashed on first run — the same shape that broke
check-locale-reachable twice. The imports are added under aliased names so
they cannot collide with a gate's own.
· Five gates take their URL from argv with no default and are left for a
per-file pass rather than patched blind.
CONTROLS: against another worktree's server a guarded gate prints NOT MEASURED
naming the mismatch and exits 75; against this checkout's server it runs and
reports normally; against the live site it proceeds.
TWO FIXES THAT BOTH SHOW UP ONLY WHEN THESE PLUGINS ARE SOMEBODY ELSE'S GUEST.
1. ATTRIBUTE FALLTHROUGH. These SFCs mount AS custom elements, so a style the
host puts on the tag lands on the component's own root element. MEASURED with
a host style of "display:flex; position:relative; height:100%": that exact
string appeared verbatim on the inner root div of people-groups-grid,
stack-demo, research-map-plus-grid and dashboard-plus-grid. The gallery sets
one on every tab, and the same fallthrough collapsed the Prayer Walk Planner
to 76px. inheritAttrs:false on each root — a host styling the TAG is styling
the box, not our internals.
AFTER: the inner root carries no inline style on any of the four, and each
still renders full height (2997-4010px in the harness), so the guard did not
become the collapse it prevents.
Worth knowing for the next probe: shadowRoot.firstElementChild is a <style>
node — Vue injects styles first — so a probe that trusts it measures nothing
and reports "no fallthrough, nothing to fix". Take the first non-style child.
2. THE RESIDUAL CHROME the in-gallery re-measure found: the dashboard title,
Align Dashboard and its tooltip, World, Vertical Legend, groups in scope, the
per-row aria templates (Filter by / Remove filter / groups), and the map
drawer's aria label. Catalogued in all 11 locales and read through tStatic,
reusing existing wording where the catalogue already had the word.
The aria TEMPLATES matter more than their size suggests: they repeat per row, so
one untranslated template is dozens of English strings in a screen-reader's ear.
resources-list and charts turned out to be almost entirely explanatory prose.
Their actual chrome is four strings — two page titles, the loading line and
Close — and those are now in all eleven locales. The blanket dir=ltr is off both
roots, so Arabic gets its layout, and each English prose block declares its own
direction where it sits.
That completes option (b) across all five gospel profiles. What stays English is
what carries doctrinal and statistical precision: which pools were searched, what
"nothing found" does and does not claim, why Open Bible Stories is not
Scripture. A machine translation of those would read as authoritative while
being wrong, and one translated line in each locale says so in the reader's own
language.
Verified on a served built tree whose identity I proved first — the Russian title
appears once served and once on disk. en/ar/ru on both leaves: heading
translated, 0 clipped, dir=rtl only for Arabic, no page errors and nothing 4xx.
A composite shell copies a whitelist of keys from its own profile-config down to
each map it nests. `provider` was not on any of the three lists, so a nested pane
read it as undefined and fell back to Mapbox: a partner who asked for MapLibre,
and who holds no Mapbox token, got Mapbox maps in every inner pane. Same shape as
the missing `urlSync` here before it -- an inner map cannot honour a key the shell
never passed down.
The report named country-posters-mfe. All three composites had it: regions-mfe and
upg-100-mfe were missing `provider` too, so fixing only the one named would have
left two thirds of the defect in place.
Verification is a probe, not an inspection, and it needs a control. "0 requests to
api.mapbox.com" passes trivially on a page where nothing mounted -- the most likely
way this measurement goes wrong -- so each composite is loaded twice: once with
provider=maplibre expecting zero, once with provider omitted expecting more than
zero. The second run is what proves the first measured anything; a run that
renders no canvas is reported UNPROVEN rather than passing.
It also cannot be measured on a leaf page. Every leaf loads its map peer in its
own <head> (regions-mfe/index.html line 13 is a hardcoded api.mapbox.com script),
so on a leaf zero mapbox requests is unreachable whatever provider says. The
question only has meaning on a host that picks the peer itself, which is what a
partner embedding the .js actually does.
The last commit recorded "in RTL the leftmost data column clips against the
edge" as known debt. Measured rather than eyeballed, that is wrong. Counting
cells falling outside the table holder: English and Arabic are IDENTICAL — 13 of
20 columns off-screen in both, scrollWidth 3146 against clientWidth 1400 either
way.
A twenty-column table is wider than 1,400px, and RTL mirrors which edge the
overflow sits on. What I saw in the Arabic screenshot was English's right-edge
overflow appearing on the left, which is what mirroring means. I read a picture
instead of measuring, and filed the result as debt — which would have sent the
next person chasing a defect that does not exist.
Whether twenty columns is right for a 1,400px screen is a real question. It is a
design question, and it is the same question in English.
The PPLR-validate-data maps (religion, ROP tree, language families, semantic
trees, PG name locales, ROP sunburst, all pins, cross-system ID rosetta,
translation coverage, index) shipped only as a frozen 339 KB IIFE on the old
dx-life research page, registering <doxa-map> and colliding with doxa-simple-map.
They now build from source as plugin-designs/pplr-data-maps: one monolithic
<pplr-data-maps> with a view switcher. The Research Gallery discovers it into
Experimental.
Data: build-data.mjs derives data/pplr from the source's 78 MB: 7.3 MB of pins
without the 17 locale names, which load from pg-names.json only when a pin's
detail card opens, plus a precomputed coverage table. The tree stages it inside
itself; the web-root mirror is ignored like the other data/ mirrors.
Measured on a served built tree in catalog layout: the leaf page, all ten views
script-embedded on another origin, and the Gallery tab each render with 0 page
errors and 0 failed requests. Coverage shades 241 of 251 countries.
Their tape system supersedes mine and is the better invariant, so it wins whole.
I had made both stickers self-centre on the card's edge; they pin both by
--card-pad-top and --tape-overlap, which makes the OVERLAP onto the banner the
constant instead of the centre. That is what the Driver's rule actually asks for
-- extra height then grows into empty space rather than walking over the artwork
-- and one variable now governs both stickers so neither can drift from the other.
Kept only the orthogonal part of mine: the (pointer:coarse) block that lifts
.pbtn, .pstock and .tape-h to the 24px WCAG 2.5.8 minimum. Dropped my padding
override on .tape-h -- their rule sizes and places that sticker, and re-stating
its padding here would fight a geometry the Driver has already approved. Also
took their new .pstock.is-on state.
Fixed a comment of mine that the merge made false: it claimed both tapes
self-centre, which stopped being true in this very commit.
Rebuilt all 23 bundles, not just the ones I touched. The incoming diff brought
library/i18n/index.js and three shared library components, which reach nearly
every bundle -- and regenerating the tree without rebuilding has shipped a stale
.js here once before. tree + fix:sri after; check:sri PASS across 54 script
references on 55 leaf pages.
PPLR renamed the aCountry fields upstream (GENCalpha3 -> GENCAlpha3, CtryNm -> Name,
ctryNmDisp -> DisplayName, ISOalpha2 -> ISOAlpha2). The query asked for the old
names, ArcGIS answered HTTP 200 with { error }, and the loop read that as "no
features". Every view that draws country polygons silently lost them. The
query now asks for the new names and gives each feature the old ones back, and an
{ error } body throws.
Paging also stopped after one page: f=geojson carries exceededTransferLimit under
data.properties, so only 200 of 251 countries loaded. A full page now means ask
for more.
Coverage: the status no longer claims a 6,000-row sample, and a __pplrCoverage()
handle reports how many polygons matched a coverage row.