From 35c68048e80a04ca32c79a1f285a472d69bc4bcb Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Thu, 1 Oct 2026 00:08:29 +0000 Subject: [PATCH 01/25] cities-internet, key-cities: neutral source comments Rewrite code comments to drop quoted directives, person-tied dates, internal process and role names, and QA run references, keeping each technical fact intact. Comments only; behaviour unchanged. --- .../cities-internet/connectivity-map.vue | 22 +++++++++---------- .../cities-internet/data-tables.vue | 17 +++++++------- plugin-designs/key-cities/cities-map.vue | 4 ++-- plugin-designs/key-cities/data-tables.vue | 9 ++++---- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/plugin-designs/cities-internet/connectivity-map.vue b/plugin-designs/cities-internet/connectivity-map.vue index d11333c0b..eaba47ce4 100644 --- a/plugin-designs/cities-internet/connectivity-map.vue +++ b/plugin-designs/cities-internet/connectivity-map.vue @@ -13,7 +13,7 @@ function setGlobal(name, value) { * social platform on tap — the connectivity view of where DX people groups can be reached online. * * Every country shaded by measured internet penetration, with its NAMED carriers - * and dominant social platform on tap. Brief: "all these are missing maps." + * and dominant social platform on tap. One of the data maps this project adds. * * CLONE of gospel-resources/resources-map.vue with the data and bands swapped — * profiles duplicate, the pattern does not get re-derived. Same mobile-first @@ -131,8 +131,8 @@ const rows = ref([]) const selected = ref(null) const aiNoticeOpen = ref(true) // dismissible AI-research disclaimer in the detail sheet const err = ref(null) -// Starts TRUE: the notice must be on screen for the first frame, not switched on after a tick. rega -// measured 7-30s to the first data paint, and for that whole window the map was an empty basemap with +// Starts TRUE: the notice must be on screen for the first frame, not switched on after a tick. +// Measurement showed 7-30s to the first data paint, and for that whole window the map was an empty basemap with // nothing to say it was still working — indistinguishable from a broken embed. const loading = ref(true) @@ -181,7 +181,7 @@ const selectedAdm1 = ref(null) // The DOXA list from pray-tools, the same list Prayer Progress draws. It carries the slug the // detail panel fetches by. The shared pin table (tables/people-group-pins.json) has no slug, // so its dots could not open that panel. -// Dark slate with a white ring (2026-09-28: the red dots looked wrong on the mostly green +// Dark slate with a white ring (red dots looked wrong on the mostly green // map). A neutral dark reads on every fill here, the green/amber/red/grey bands AND India's pale-to-deep // blues, and it carries no "good/bad" meaning the way red does on a map whose red means under 25%. // The white ring keeps it apart from the deepest blue and from the dark basemap. @@ -190,7 +190,7 @@ const uiStore = inject('uiStore', null) const _lang = (() => { const v = inject('lang', null)?.value; return (v && v !== 'en') ? v : null })() // The reader's language for the ENGINE'S OWN labels (the canvas's "Map" name, "Zoom in", attribution…). // This map is built raw, not through useMapInstance, so it must hand the engine that locale itself; -// without it they stayed English under ar on both engines (rega, row 34). Same ladder as +// without it they stayed English under ar on both engines (measured). Same ladder as // useMapInstance's readerLang: ProfileLoader's lang, then profile-config, then ?lang=, then . const readerLang = () => { if (_lang) return _lang @@ -302,8 +302,8 @@ function paintGroups(map) { // installed it, so English from the shipped catalog is the fallback, never a blank or a key. // Carrier and platform names are data and are not translated. // C4 wave 3b: the guard is library/composables/useOptionalT.js now, and the fallback resolves through -// tStatic — the RUNTIME LOCALE first, English as the last resort. exp-shepherd's call as this -// plugin's owner (2026-09-26), on the same reasoning gospel-plugin gave: the bundle already carries +// tStatic — the RUNTIME LOCALE first, English as the last resort. This plugin owns that +// choice, on the same reasoning the other map plugins use: the bundle already carries // all 11 catalogues, so a partner embedding without vue-i18n being served English is a bug, not a // decision. The old enLookup was English-only, which is exactly what that served. // The local fill() is gone with the guard: fillParams applies the same interpolation, except it leaves @@ -565,7 +565,7 @@ onMounted(async () => { paintGroups(map) const geo = await adm1Loaded if (geo && !unmounted) paintAdm1(map, geo) - // ★ CLEARED HERE, NOT ON 'load'. rega measured 7-30s from mount to the first data paint under + // ★ CLEARED HERE, NOT ON 'load'. Measurement showed 7-30s from mount to the first data paint under // SwiftShader: the style fires 'load' early, while the shaded countries, the adm1 provinces and // the 2,105 dots all arrive later. Clearing on 'load' would retire the notice during exactly the // wait it exists to explain, leaving the reader with an empty basemap and no reason for it. @@ -603,7 +603,7 @@ const groupOpen = computed(() => !!uiStore?.selectedPeopleGroup) useShadowStyles( // ⚠ THE MAP MUST FILL ITS HOST, NOT THE VIEWPORT. This element had NO :host rule at all, so a // custom element defaults to display:inline with no height of its own, and .gm's absolute inset:0 - // resolved against whatever ancestor happened to be positioned. rega measured the result in the + // resolved against whatever ancestor happened to be positioned. The result was measured in the // /embed-test re-shoot at 1440: a 1198x900 canvas inside a 1198x560 embed card — ~340px, the // attribution among it, drawn below the card and clipped. Same class as the board's, and the same // fix it already carries: height:100% so a height-bounded slot decides, min-height as a floor so an @@ -612,7 +612,7 @@ useShadowStyles( // .gm below is position:absolute;inset:0, and absolute positioning resolves against the nearest // POSITIONED ancestor — which :host was not. So on a partner page .gm resolved against whatever the // PARTNER happened to have positioned, and the map sized itself to that rather than to this element. - // rega measured it in the hostbox run on ca07fc3c8: the element was CORRECT at 420px in a 420px box, + // It was measured in a host-box run: the element was CORRECT at 420px in a 420px box, // while the canvas inside it stayed 900px and painted the pill and the attribution 478px below the // box (538px in a 360px box), over the partner's own text. Giving the host a HEIGHT without making it // a CONTAINING BLOCK fixes the box and not what is drawn inside it. @@ -635,7 +635,7 @@ useShadowStyles( // control rail by construction: MapToolbar is inset-inline-end:3px with 32px // buttons, so ANY box reaching right:8px intersects it at every viewport. The // rail's z-index:10 beats this z-index:4, so the buttons stayed clickable and - // the defect read as "the legend underlaps the controls" — reported 3x. + // the defect read as "the legend underlaps the controls". // The fix imitates the shared SemanticTreeLegend, which has always done it // correctly: `left:8px; width:var(--map-legend-width,380px)`. Bounded and // left-anchored cannot reach the right rail, so the two can never overlap diff --git a/plugin-designs/cities-internet/data-tables.vue b/plugin-designs/cities-internet/data-tables.vue index bb2606369..ab09c8d5b 100644 --- a/plugin-designs/cities-internet/data-tables.vue +++ b/plugin-designs/cities-internet/data-tables.vue @@ -5,15 +5,14 @@ * @description Four validated research tables, one tab each, with sortable and filterable columns, CSV and JSON * export, and each table's caveats rendered in the UI rather than hidden in a footnote. * - * Four validated Tabulator tables from the flight's research corpora, each with + * Four validated Tabulator tables from the research corpora, each with * sortable/filterable columns, CSV+JSON export, and its caveats rendered IN the UI. * - * > design note 2026-08-01: "all the tabulator data and trees for all the multiple - * > various tabulator tables for all this data all organized in list format with - * > full granularity." + * Design intent: present all the tabulator data and trees for the various tables + * in list format, at full granularity. * * The tables are produced by library/scripts/research/build-tabulator-tables.mjs - * (the M+V of IEMV) — flat scalar rows, one row per fact, validated before write. + * — flat scalar rows, one row per fact, validated before write. * * WHY THE CAVEATS RENDER: a list view strips context. Someone reading * "Internet 12.6%" needs to know it is a NATIONAL figure, and someone reading a @@ -37,14 +36,14 @@ const assetsBase = computed(() => resolveAssetsBase(profileConfig.value)) // i18n is OPTIONAL: useI18n() throws on a host that never installed it, so English from the // shipped catalog is the fallback, never a blank or a key. Region and country names are data. // C4 wave 3b: the guard is library/composables/useOptionalT.js now, and the fallback resolves through -// tStatic — the RUNTIME LOCALE first, English as the last resort. exp-shepherd's call as this -// plugin's owner (2026-09-26), on the same reasoning gospel-plugin gave: the bundle already carries +// tStatic — the RUNTIME LOCALE first, English as the last resort. This plugin owns that +// choice, on the same reasoning the other map plugins use: the bundle already carries // all 11 catalogues, so a partner embedding without vue-i18n being served English is a bug, not a // decision. The old enLookup was English-only, which is exactly what that served. // The local fill() is gone with the guard: fillParams applies the same interpolation, except it leaves // a placeholder for a NULL value where this fill() rendered the string "null". const t = useOptionalT('connectivity') -// NUMERALS ARE HELD fleet-wide (2026-09-26): a locale-following format would switch +// NUMERALS ARE HELD by design: a locale-following format would switch // 'ar' to Arabic-Indic digits. Until that is decided, numbers keep the browser-default format they // had before this pass; only the TEXT is translated. const num = (n) => (Number(n) || 0).toLocaleString() @@ -90,7 +89,7 @@ const table = computed(() => cache.value[current.value?.file] || null) useShadowStyles( // ★ THE TABLE MUST STAY INSIDE ITS HOST BOX — the same defect and the same fix as connectivity-list. // With no :host rule the element is inline with no height, and .ct (position:absolute;inset:0) - // resolved against the partner page's positioned ancestor instead of this element. rega measured + // resolved against the partner page's positioned ancestor instead of this element. Measurement showed // content painting to y=1,698, which is 1,254px BELOW a 420px host box. // .ct already has overflow-y:auto, so containing it is enough to make the table scroll in the box. ':host{display:block;position:relative;width:100%;height:100%;min-height:320px}' + diff --git a/plugin-designs/key-cities/cities-map.vue b/plugin-designs/key-cities/cities-map.vue index d8221d40d..a80a5e34a 100644 --- a/plugin-designs/key-cities/cities-map.vue +++ b/plugin-designs/key-cities/cities-map.vue @@ -3,7 +3,7 @@ * cities-map.vue — THE KEY CITIES GLOBAL MAP. * * Every located population centre as a POINT, sized by how many people groups it - * holds. Brief: "all these are missing maps." + * holds. One of the data maps this project adds. * * POINTS, NOT COUNTRY SHADING — this is the one dataset with real coordinates * (1,646 clusters, 277 of them gazetteer-named), so shading a country would throw @@ -86,7 +86,7 @@ async function load() { const res = await fetch(`${assetsBase.value}/tables/key-cities.json`) if (!res.ok) throw new Error(`key-cities table not found (${res.status})`) rows.value = (await res.json()).rows || [] - // CITY-STAR LAYER (Brief: "better city display"). Fetched SEPARATELY — never a + // CITY-STAR LAYER (a richer city display). Fetched SEPARATELY — never a // static import, which would inline the table into the IIFE bundle (that trap // once took two bundles to 2.0/2.9MB). Failure here must NOT break the base map, // so it is caught: the city layer is an ENHANCEMENT, not a dependency. diff --git a/plugin-designs/key-cities/data-tables.vue b/plugin-designs/key-cities/data-tables.vue index 3e608f6e8..ffaf544e4 100644 --- a/plugin-designs/key-cities/data-tables.vue +++ b/plugin-designs/key-cities/data-tables.vue @@ -2,15 +2,14 @@ /** * data-tables.vue — the CITIES DATA list view: every research table, one tab each. * - * Four validated Tabulator tables from the flight's research corpora, each with + * Four validated Tabulator tables from the research corpora, each with * sortable/filterable columns, CSV+JSON export, and its caveats rendered IN the UI. * - * > design note 2026-08-01: "all the tabulator data and trees for all the multiple - * > various tabulator tables for all this data all organized in list format with - * > full granularity." + * Design intent: present all the tabulator data and trees for the various tables + * in list format, at full granularity. * * The tables are produced by library/scripts/research/build-tabulator-tables.mjs - * (the M+V of IEMV) — flat scalar rows, one row per fact, validated before write. + * — flat scalar rows, one row per fact, validated before write. * * WHY THE CAVEATS RENDER: a list view strips context. Someone reading * "Internet 12.6%" needs to know it is a NATIONAL figure, and someone reading a From c1a2f36983f5059d22894ea395080233c5769e72 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Thu, 1 Oct 2026 00:17:18 +0000 Subject: [PATCH 02/25] Wall of People Groups: comments state what was measured without naming who measured it Five comment lines in the Wall's sources named a fleet agent as the measurer. They now keep the measurement and drop the name. Comments only; no behaviour changes. --- .../wall-of-people-groups/scripts/check-wall-no-bg-photos.mjs | 2 +- plugin-designs/wall-of-people-groups/src/blessPoster.js | 4 ++-- plugin-designs/wall-of-people-groups/src/wall.css | 2 +- plugin-designs/wall-of-people-groups/src/wallCanvas.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugin-designs/wall-of-people-groups/scripts/check-wall-no-bg-photos.mjs b/plugin-designs/wall-of-people-groups/scripts/check-wall-no-bg-photos.mjs index d90e16f13..18c21c1c3 100644 --- a/plugin-designs/wall-of-people-groups/scripts/check-wall-no-bg-photos.mjs +++ b/plugin-designs/wall-of-people-groups/scripts/check-wall-no-bg-photos.mjs @@ -3,7 +3,7 @@ * check-wall-no-bg-photos — the Wall's store never downloads photos on its own. Headless. * * Until 2026-09-29 the store "tested" every photo after loading the list: 932 photos, 132 MB, on every - * visit, whether or not anyone zoomed in (rega measured 108–132 image requests in the first 60 s; 0 + * visit, whether or not anyone zoomed in (measured: 108–132 image requests in the first 60 s; 0 * after the fix). The canvas loads only the photos in view, so the store must load none. This runs the * REAL store (through Vite, so @map resolves) against a trimmed copy of the live list * (fixtures/pray-list-sample.json), with fetch stubbed to serve it and Image stubbed to COUNT every diff --git a/plugin-designs/wall-of-people-groups/src/blessPoster.js b/plugin-designs/wall-of-people-groups/src/blessPoster.js index ab52c2ccb..a8c28e41a 100644 --- a/plugin-designs/wall-of-people-groups/src/blessPoster.js +++ b/plugin-designs/wall-of-people-groups/src/blessPoster.js @@ -148,7 +148,7 @@ const ICONS = { * two cannot drift. Sizes in points, lengths in inches. * · the reference prints at the verse's size × 0.8, never below MIN_READABLE_PT (a reference that can't * be read is noise); verse and reference are only printed on sizes whose verses are readable; - * · a long name shrinks to fit its line (bold ≈ 0.72 em a character: measured wide on purpose, rega found + * · a long name shrinks to fit its line (bold ≈ 0.72 em a character: measured wide on purpose, since * 0.68 left "Kummara, Kumbara" 2 px over), down to MIN_READABLE_PT (or the preset's own name size if that * is smaller); still too long, it wraps, at the size that fits two lines, or more (up to four) at the floor. */ @@ -163,7 +163,7 @@ export function cardText(plan, name) { const words = String(name).split(/\s+/).filter(Boolean), longest = Math.max(1, ...words.map((w) => w.length)) const pt = wrap ? Math.max(floor, Math.min(nPt, (2 * inner) / (len * EM), inner / (longest * EM))) : Math.min(nPt, want) // Lines counted the way the browser breaks them: whole words, greedily (a word longer than a line is cut), with - // 15% slack on the character width (rega measured bold names wider than the plain sum). Up to 4 lines: a long + // 15% slack on the character width (bold names measured wider than the plain sum). Up to 4 lines: a long // name at the floor size ("Northern Conchucos Ancash Quechua") needs 4, and the photo gives up the height. const lines = wrap ? Math.min(4, wrapLines(words, inner / (EM * pt * 1.15))) : 1 return { diff --git a/plugin-designs/wall-of-people-groups/src/wall.css b/plugin-designs/wall-of-people-groups/src/wall.css index 91cca30ec..300514335 100644 --- a/plugin-designs/wall-of-people-groups/src/wall.css +++ b/plugin-designs/wall-of-people-groups/src/wall.css @@ -122,7 +122,7 @@ body { font-weight: 500; } -/* PHONE (rega, 390×844): the header was one row, so the title wrapped to five lines and the poster-size +/* PHONE (390×844): the header was one row, so the title wrapped to five lines and the poster-size select and the Bless/Social toggle ran off the right edge, unreachable. On a phone the title takes one short line and the controls wrap onto as many rows as they need, every one on screen. */ @media (max-width: 640px) { diff --git a/plugin-designs/wall-of-people-groups/src/wallCanvas.js b/plugin-designs/wall-of-people-groups/src/wallCanvas.js index 3903a1e60..1d84c9b76 100644 --- a/plugin-designs/wall-of-people-groups/src/wallCanvas.js +++ b/plugin-designs/wall-of-people-groups/src/wallCanvas.js @@ -1,7 +1,7 @@ /** * wallCanvas.js — the Wall of People Groups drawn on ONE instead of an SVG of ~36,000 nodes. * - * Measured before (rega, main 16ff7e719, all 2,107 cards): 36,247 DOM nodes (36,214 SVG), a 0.5-1.6 s + * Measured before (main 16ff7e719, all 2,107 cards): 36,247 DOM nodes (36,214 SVG), a 0.5-1.6 s * long task on EVERY wheel tick, 41.7 s to zoom out to the whole wall, p95 350 ms frames when panning * it. The JS heap was only 8.3 MB: the cost was the SVG DOM being re-laid-out and re-painted per tick. * Here each frame draws only the cards in view, in world coordinates, with level of detail: From af5ed4c2754f19a3c27c2d903f493133b3e52ef8 Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Thu, 1 Oct 2026 00:17:53 +0000 Subject: [PATCH 03/25] Gospel Resources and Gospel x Internet: comments state the technical facts in neutral terms, without attributions or agent names --- plugin-designs/gospel-internet/board.vue | 2 +- .../scripts/build-gospel-internet.mjs | 2 +- .../scripts/check-gospel-internet-render.mjs | 2 +- .../scripts/fetch-translation-readiness.mjs | 2 +- .../src/components/GiBoundedSetMap.vue | 2 +- .../src/components/GiCenteredSet.vue | 6 ++-- .../src/components/GiCenteredSetMap.vue | 2 +- .../src/components/GiCharts.vue | 2 +- .../src/components/GiCombos.vue | 2 +- .../gospel-internet/src/components/GiGrid.vue | 2 +- .../gospel-internet/src/components/GiMap.vue | 2 +- plugin-designs/gospel-internet/src/model.js | 2 +- .../gospel-internet/src/useBoard.js | 6 ++-- .../gospel-resources/resources-map.vue | 36 +++++++++---------- .../scripts/build-field-cards.mjs | 2 +- .../scripts/build-people-group-pins.mjs | 4 +-- .../scripts/fetch-ebible-credits.mjs | 2 +- .../scripts/verify-gospel-links.mjs | 2 +- .../scripts/verify-translator-org-links.mjs | 6 ++-- 19 files changed, 42 insertions(+), 44 deletions(-) diff --git a/plugin-designs/gospel-internet/board.vue b/plugin-designs/gospel-internet/board.vue index fa67bff25..dd96633be 100644 --- a/plugin-designs/gospel-internet/board.vue +++ b/plugin-designs/gospel-internet/board.vue @@ -188,7 +188,7 @@ useShadowStyles( 'grid-template-columns:var(--facet-w,264px) minmax(0,1fr) var(--rail-w,360px);' + 'grid-template-rows:auto auto auto minmax(0,1fr) var(--table-h,38%);' + // The charts rail runs the full height beside the map AND the table: sized to the map row alone it was - // ~300px and clipped its four charts (rega: the charts did not look good). + // ~300px and clipped its four charts. 'grid-template-areas:"top top top" "combos combos combos" "note note note" "facets map rail" "facets grid rail";' + 'font:13px/1.35 Roboto,-apple-system,"Segoe UI",sans-serif;overflow:hidden}' + '.gb-top{grid-area:top;display:flex;align-items:center;flex-wrap:wrap;gap:10px 16px;padding:10px 14px;background:#fff;border-bottom:1px solid #d1d4cd}' + diff --git a/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs b/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs index 274fe10e3..24f9a63fb 100644 --- a/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs +++ b/plugin-designs/gospel-internet/scripts/build-gospel-internet.mjs @@ -9,7 +9,7 @@ * data/tables/doxa-people-groups.json (gospel-plugin) slug, lat/lng, pray link * data/tables/country-connectivity.json (World Bank) internet %, year, mobile per 100 * data/tables/internet-by-country.json (cities-internet) top platform - * data/tables/internet-by-adm1.json (exp-shepherd) India by state, NFHS-5 + * data/tables/internet-by-adm1.json (cities-internet) India by state, NFHS-5 * data/tables/messaging-channels.json (cities-internet) top free apps per country (Apple, iOS only) * data/tables/social-platforms.json (cities-internet) social referral share per country (StatCounter) * data/tables/gospel-by-province-groups.json (gospel-plugin) each IND/CHN group's province (approximate) diff --git a/plugin-designs/gospel-internet/scripts/check-gospel-internet-render.mjs b/plugin-designs/gospel-internet/scripts/check-gospel-internet-render.mjs index d2dd3ae40..1dc993f94 100644 --- a/plugin-designs/gospel-internet/scripts/check-gospel-internet-render.mjs +++ b/plugin-designs/gospel-internet/scripts/check-gospel-internet-render.mjs @@ -7,7 +7,7 @@ * This one server-renders them (Vue's renderer, through vite's module loader, no browser, no WebGL): * every panel's setup, computeds and template run against the real table, tree and resources, and the * HTML is asserted on CONTENT the panel must show, never on the panel's own count of itself. - * onMounted does not run under SSR, so map layers are not covered here; that is the browser's (rega's). + * onMounted does not run under SSR, so map layers are not covered here; that is the browser check's job. * * `--poison` blanks the rows handed to the panels and requires the content assertions to FAIL. * Exit 0 pass · 1 fail · 75 not measured (vite could not load the modules). diff --git a/plugin-designs/gospel-internet/scripts/fetch-translation-readiness.mjs b/plugin-designs/gospel-internet/scripts/fetch-translation-readiness.mjs index 77e0f3ed2..d210abd17 100644 --- a/plugin-designs/gospel-internet/scripts/fetch-translation-readiness.mjs +++ b/plugin-designs/gospel-internet/scripts/fetch-translation-readiness.mjs @@ -13,7 +13,7 @@ * (Partial Scripture comes from eBible's index, already in data/tables, and is read by the builder.) * * Codes are mapped EXPLICITLY and every code that cannot be mapped is COUNTED and listed, never dropped - * silently (dx-shepherd's guard 3). + * silently. * Writes data/translation-readiness/snapshot.json. Refuses a much smaller snapshot than the last one. */ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs' diff --git a/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue b/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue index 72af5e0ba..12457113c 100644 --- a/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiBoundedSetMap.vue @@ -44,7 +44,7 @@ const R = 120, SPREAD = 260 // ★ 0.095, NOT THE 0.17 DEFAULT — the same clamp as the centered chart, and the same number so the // two sets read at one scale. renderWorldCopies:false stops the engine zooming out past // log2(canvasWidth/512), so a wide, short figure cannot fit its height at 0.17 and the outermost -// groups fall outside the frame (rega, 1440: 54 outside in ar and 97 in en — the width, not RTL). +// groups fall outside the frame (measured at 1440: 54 outside in ar and 97 in en — the width, not RTL). // Kept EQUAL to GiCenteredSetMap's on purpose: a reader switching tabs compares two drawings, and a // different footprint per tab would make the same population look like a different size. const EXTENT_FRACTION = 0.095 diff --git a/plugin-designs/gospel-internet/src/components/GiCenteredSet.vue b/plugin-designs/gospel-internet/src/components/GiCenteredSet.vue index 6dacd7a40..1516589b7 100644 --- a/plugin-designs/gospel-internet/src/components/GiCenteredSet.vue +++ b/plugin-designs/gospel-internet/src/components/GiCenteredSet.vue @@ -134,7 +134,7 @@ const fmt = (n) => formatCount(Number(n) || 0) useShadowStyles( // ROW, NOT COLUMN, and overflow hidden. Stacked in a centred column the legend — a 360px card // with a 3x3 grid of 44px cells and four footer lines — is taller than the pane on its own, so - // the chart was pushed out of view entirely and the whole panel overflowed its host: rega saw + // the chart was pushed out of view entirely and the whole panel overflowed its host: it showed // the legend covering the row below and the caption drawn over the page tabs. // Every child gets min-height/min-width 0, because a flex item's default minimum is its CONTENT // size — without it an item never shrinks below its natural height and the container overflows @@ -148,10 +148,10 @@ useShadowStyles( // had this; the centered one did not, which is the kind of difference two files drift into // when the same rule is written twice. // min-height in the narrow column: a flex child with auto content can resolve to zero height, - // which is what a 0x0 chart is. rega measured exactly that at 390px. + // which is what a 0x0 chart is, as measured at 390px. // flex-basis 0, not auto. The map inside is position:absolute, so the figure has NO intrinsic // width to be sized from — with basis auto it asks for its content, the content is nothing, and - // the legend beside it takes the column. rega measured the figure at 39px across while the + // the legend beside it takes the column. The figure measured 39px across while the // decoder card had the rest. Growing from zero makes the figure take the remaining space // whatever it happens to contain. // min-width is a FLOOR, not 0. The shipped CSS was right and the figure was still 39px wide: diff --git a/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue b/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue index 813fa0faa..b0618c9ce 100644 --- a/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiCenteredSetMap.vue @@ -52,7 +52,7 @@ const EXTENT = 30 + (RING_MAX - 1) * 22 // 228 — the ring the outermo // ★ 0.095, NOT THE 0.17 DEFAULT. With renderWorldCopies:false the engine will not zoom out past the // point where ONE WORLD fills the canvas WIDTH, so the minimum zoom is log2(canvasWidth/512) and a // wide, short figure is clamped too close to fit its own height. minZoom is irrelevant; the width -// constraint wins. rega measured the symptom at 1440 — 54 groups outside the frame in ar, 97 in en, +// constraint wins. The symptom measured at 1440 — 54 groups outside the frame in ar, 97 in en, // so it was never RTL-specific. At 0.17 the drawing spans 0.34 of the Mercator square and needs // 271px of vertical room where the canvas offers 249; at 0.095 it spans 0.17 and needs 151px // against 169 available at padding 40. Checked by check:gospel-set-fit. diff --git a/plugin-designs/gospel-internet/src/components/GiCharts.vue b/plugin-designs/gospel-internet/src/components/GiCharts.vue index 8930b86fd..878a9e585 100644 --- a/plugin-designs/gospel-internet/src/components/GiCharts.vue +++ b/plugin-designs/gospel-internet/src/components/GiCharts.vue @@ -85,7 +85,7 @@ function onBar(row) { useShadowStyles( '.gic{display:flex;flex-direction:column;gap:4px;height:100%;overflow-y:auto;box-sizing:border-box;padding:10px 12px 16px;background:#fff;color:#3b463d;font:13px/1.35 Roboto,-apple-system,"Segoe UI",sans-serif;scrollbar-width:thin}' + // A flex column's children SHRINK by default: inside this scroll box they were squeezed instead of - // scrolled, so the measure toggle collapsed to a 2px line and a caveat card was cut in half (rega). + // scrolled, so the measure toggle collapsed to a 2px line and a caveat card was cut in half. '.gic > *{flex-shrink:0}' + '.gic-seg{align-self:flex-start;display:inline-flex;border:1px solid #d1d4cd;border-radius:6px;overflow:hidden;margin-bottom:4px}' + '.gic-seg button{font:inherit;font-size:12px;padding:4px 12px;min-height:26px;border:0;background:#fff;color:#3b463d;cursor:pointer}' + diff --git a/plugin-designs/gospel-internet/src/components/GiCombos.vue b/plugin-designs/gospel-internet/src/components/GiCombos.vue index 69b8fc8b0..4abcb6cde 100644 --- a/plugin-designs/gospel-internet/src/components/GiCombos.vue +++ b/plugin-designs/gospel-internet/src/components/GiCombos.vue @@ -116,7 +116,7 @@ useShadowStyles( '.gic2{grid-area:combos;display:flex;align-items:center;gap:10px;padding:6px 14px;background:#fff;border-bottom:1px solid #d1d4cd;' + 'min-width:0;font:12.5px/1.3 Roboto,-apple-system,"Segoe UI",sans-serif;color:#3b463d}' + '.gic2-h{flex:none;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.03em;color:#6d766e}' + - // The selection side is BOUNDED at 40% (rega r82: the placeholder ran under "Try a combination"), but only + // The selection side is BOUNDED at 40% (the placeholder ran under "Try a combination"), but only // its chips and placeholder clip: they scroll or ellipsize. No overflow here and no min-width:0, so the // flex:none label sets the minimum width and is never cut ('YOUR S…' at 390, the zh/ru/hi/ar labels at 1280). '.gic2-sel{display:flex;align-items:center;gap:6px;flex:0 1 auto;max-width:40%}' + diff --git a/plugin-designs/gospel-internet/src/components/GiGrid.vue b/plugin-designs/gospel-internet/src/components/GiGrid.vue index 59bedc834..bbfbea2e3 100644 --- a/plugin-designs/gospel-internet/src/components/GiGrid.vue +++ b/plugin-designs/gospel-internet/src/components/GiGrid.vue @@ -215,7 +215,7 @@ useShadowStyles( @click="board.openGroup(r.slug)" @keydown="onRowKey($event, r)" @mouseenter="bus.setHover(r.slug)" @mouseleave="bus.setHover(null)">
- +
diff --git a/plugin-designs/gospel-internet/src/components/GiMap.vue b/plugin-designs/gospel-internet/src/components/GiMap.vue index 7f3175b33..3948ad1c5 100644 --- a/plugin-designs/gospel-internet/src/components/GiMap.vue +++ b/plugin-designs/gospel-internet/src/components/GiMap.vue @@ -363,7 +363,7 @@ useShadowStyles( 'background:rgba(255,255,255,.96);color:#3b463d;border:1px solid #d1d4cd;border-radius:10px;padding:8px 10px;' + 'font:12px/1.4 Roboto,-apple-system,"Segoe UI",sans-serif;box-shadow:0 2px 10px rgba(0,0,0,.10)}' + '.gim-lh{display:flex;align-items:center;gap:8px;font-weight:600;font-size:12px;margin-bottom:4px}' + - // a real 28px tap target (WCAG 2.5.8 wants 24): the glyph alone measured 16px (rega r75) + // a real 28px tap target (WCAG 2.5.8 wants 24): the glyph alone measured 16px '.gim-lh button{margin-inline-start:auto;border:0;background:none;color:#6d766e;cursor:pointer;font:inherit;display:inline-grid;place-items:center;width:28px;height:28px;padding:0;flex:none}' + '.gim-keys{display:flex;flex-wrap:wrap;gap:3px 10px;margin-bottom:6px}' + '.gim-key{display:inline-flex;align-items:center;gap:5px;font-size:11px}' + diff --git a/plugin-designs/gospel-internet/src/model.js b/plugin-designs/gospel-internet/src/model.js index 2144076ce..4bc27e24a 100644 --- a/plugin-designs/gospel-internet/src/model.js +++ b/plugin-designs/gospel-internet/src/model.js @@ -88,7 +88,7 @@ export const SHARE_FACETS = Object.fromEntries(Object.entries(URL_KEYS)) // MAP STATE: what a link written while the map was the default carries and only the map shows (an open or // selected people group's pin, a country or province, the province view). Such a link with no ?panel= opens -// the MAP, so old map links keep working; a bare link opens the tree (dx-shepherd ENDTOKEN-GOINT-ATTR). +// the MAP, so old map links keep working; a bare link opens the tree. const MAP_STATE = ['group', 'groups', 'country', 'province'] export const hasMapState = (q) => MAP_STATE.some((k) => q.get(k)) || q.get('view') === 'province' diff --git a/plugin-designs/gospel-internet/src/useBoard.js b/plugin-designs/gospel-internet/src/useBoard.js index 4621f5661..aae6816df 100644 --- a/plugin-designs/gospel-internet/src/useBoard.js +++ b/plugin-designs/gospel-internet/src/useBoard.js @@ -73,7 +73,7 @@ export function useBoard() { const x = readiness.value.byIso.get(r.iso639_3) // The column RANKS languages WITHOUT a New Testament (the translation question): a language that has // one scored 5 (its Bible counted as Scripture) and sorted to the top, the opposite of what a - // translation team needs (rega, pre-landing T1). Blank sorts last; the drawer still shows the signals. + // translation team needs. Blank sorts last; the drawer still shows the signals. r.open_data = x && !x.nt ? x.signals : null r.open_grammar = x && x.grammar ? x.grammar : '' r.open_scripture_books = x ? x.scripture_books : null @@ -153,7 +153,7 @@ export function useBoard() { let pendingTree = '' // A ?tree= link applies only once BOTH the tree and the rows are loaded, whichever arrives last: // setTreeNode keeps the node's languages that are on the board, and with no rows yet that is the empty - // set, so the grid restored to 0 (rega F1: "6 of 2,107" before a reload, 0 after). + // set, so the grid restored to 0 ("6 of 2,107" before a reload, 0 after). function applyPendingTree() { if (!pendingTree || !tree.value || !all.value.length) return setTreeNode(pendingTree) @@ -217,7 +217,7 @@ export function useBoard() { if (!writeUrl.value || typeof history === 'undefined') return let keep = location.search // A tree node is shared as ?tree= ALONE: its language list (hundreds of codes for a big - // family) made links that break in chat apps and SMS, the field's channel (dx-shepherd GOFAM). The + // family) made links that break in chat apps and SMS, the field's channel. The // kit writes `language` from the bus; this writer runs after it (flush:'post') and removes it while a // node is active. On load, ?tree= restores the node, which re-applies its languages itself. if (treeNode.value) { const q = new URLSearchParams(keep); q.delete('language'); keep = q.toString() ? '?' + q.toString() : '' } diff --git a/plugin-designs/gospel-resources/resources-map.vue b/plugin-designs/gospel-resources/resources-map.vue index ac05b2e8b..55435fea5 100644 --- a/plugin-designs/gospel-resources/resources-map.vue +++ b/plugin-designs/gospel-resources/resources-map.vue @@ -23,11 +23,10 @@ function setGlobal(name, value) { * on a light country — that case is the point of the map, not a rendering fault. A * country Doxa does not work is left uncoloured, not drawn as covered. * - * > design note 2026-08-01: "mapping out the gospel resources is super important for me - * > … key city posters, cities with internet, gospel resources — all these are - * > missing maps." + * Why this map exists: gospel resources had no map of their own, alongside key city posters + * and cities with internet. * - * MOBILE FIRST, deliberately. The brief asked for mobile-friendliness before this + * MOBILE FIRST, deliberately. Mobile-friendliness was a requirement before this * map existed, so it is built that way rather than retrofitted: * - one WebGL context (the ~16-cap rule; this is the app's only map pane) * - tap-to-select with a bottom SHEET, not a hover popup — hover does not exist @@ -461,12 +460,11 @@ const cardShard = (slug) => { const sxScope = (sx) => { if (!sx) return '' const n = Number(sx.parts) || 0 - /* ⚠ THE WORDS ALREADY EXISTED, TRANSLATED, and I asked for new keys without looking. rega found - gospel.charts.newTestament and gospel.charts.fullBible in all 11 locales — used by the charts - leaf — while my gospel.res.* existed nowhere, so every locale including Arabic fell back to my - English defaults, and even en read "full Bible" against the catalogue's "Full Bible". Two keys - for one word is the drift I keep fixing in other people's code; these are the same words, so - they are the same keys. Only the part count needed a new one. */ + /* ⚠ THE WORDS ALREADY EXISTED, TRANSLATED. gospel.charts.newTestament and gospel.charts.fullBible + are in all 11 locales (used by the charts leaf), while the gospel.res.* keys first added here + existed nowhere, so every locale including Arabic fell back to the English defaults, and even en + read "full Bible" against the catalogue's "Full Bible". Two keys for one word is drift; these are + the same words, so they are the same keys. Only the part count needed a new one. */ const name = n === 66 ? t('gospel.charts.fullBible') : n === 27 ? t('gospel.charts.newTestament') : n ? t('gospel.res.nParts', { n: fmt(n) }) @@ -599,7 +597,7 @@ function openFromUrl() { const country = (sp.get('country') || '').trim() /* ⚠ A COUNTRY IS AN ADDRESS TOO, and it was not one: ?country=IND did nothing at all unless a ?code came with it, because country was only ever a tie-breaker between two pins sharing a - language. The brief's second requirement — click a country, see its resources — had no URL, + language. The second requirement — click a country, see its resources — had no URL, which is precisely what a partner embeds. Accepts the ISO3 or the country's own name. */ if (!code && country) { const key = country.toUpperCase() @@ -1720,7 +1718,7 @@ function openPin(i) { * positioned by no stylesheet at all. * * ONE IMPLEMENTATION, NOT TWO. This was first fixed with a local copy built on the already- - * exported engineStylesheetUrl, because nothing shared needed changing for it. exp-shepherd + * exported engineStylesheetUrl, because nothing shared needed changing for it. The library * then exported ensureEngineCSS for the same defect on connectivity-map, so this converges * onto theirs — and theirs is the better one: it also waits on a link that is PRESENT BUT * NOT YET LOADED, a case the local copy would have skipped straight past. @@ -1820,7 +1818,7 @@ onMounted(async () => { style: resolveNamedStyle(isDark.value ? 'ink' : 'paper', grProvider).style || (isDark.value ? 'mapbox://styles/mapbox/dark-v11' : 'mapbox://styles/mapbox/light-v11'), accessToken: mapboxToken.value, - /* THE ENGINE'S OWN STRINGS, IN THE READER'S LANGUAGE (rega row 34). This leaf calls + /* THE ENGINE'S OWN STRINGS, IN THE READER'S LANGUAGE. This leaf calls `new gl.Map` directly instead of going through useMapInstance, so it did not inherit the locale map the shared composable passes — "Toggle attribution" and the canvas's accessible name "Map" stayed English under ar while every string we own was Arabic. @@ -1845,7 +1843,7 @@ onMounted(async () => { /** * ═══ THE CREDIT MUST NAME THE BASEMAP THAT IS ACTUALLY DRAWN ═════════════════════════ * - * REPORTED by rega: under ?provider=maplibre this line read "geoBoundaries · Mapbox · + * Under ?provider=maplibre this line used to read "geoBoundaries · Mapbox · * © OpenStreetMap" while the tiles came from OpenFreeMap. Crediting the wrong supplier is * worse than crediting none — it is a false statement about whose work is on screen, and * OpenFreeMap's terms ask for their credit. @@ -1869,7 +1867,7 @@ onMounted(async () => { /** * ═══ 'load' MAY HAVE ALREADY FIRED ════════════════════════════════════════════════════ * - * ⚠ THE DEFECT THIS WAS WRITTEN FOR WAS RETRACTED. rega first reported that under + * ⚠ THE DEFECT THIS WAS WRITTEN FOR WAS RETRACTED. A first reading said that under * ?provider=maplibre this map drew the basemap and nothing else; re-measured by polling * until the tiles and features were actually in, it draws 170 fills and 2,107 pins on both * engines. The original reading was taken on a fixed 18 s timer, right as the layers @@ -2085,7 +2083,7 @@ useShadowStyles( '.gm-linklist span{color:#6d766e}' + '.is-dark .gm-linklist{background:rgba(59,70,61,.6);border-color:#6d766e;color:#f3f3f1}' + '.is-dark .gm-linklist span{color:#d1d4cd}' + - /* ⚠ LOGICAL, NOT PHYSICAL (found in use, rega measured on main 52eeb0db). Under ar the five + /* ⚠ LOGICAL, NOT PHYSICAL (found in use, measured on main 52eeb0db). Under ar the five map controls mirror to the LEFT while `left:8px` kept the legend physically left, so the legend sat underneath all five of them. cities-internet shares this class name and passed for one reason: it anchors inline. Every physical left/right in this stylesheet that has a @@ -2510,7 +2508,7 @@ useShadowStyles( language. -->