diff --git a/library/i18n/locales/ar/upgMap.json b/library/i18n/locales/ar/upgMap.json
index bd77e9a42..53d106ba5 100644
--- a/library/i18n/locales/ar/upgMap.json
+++ b/library/i18n/locales/ar/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "ملصقات UPG100",
"wallIntroA": "كيف تُطعم جمعًا من خمسة آلاف؟ أجلسهم يسوع جماعاتٍ من خمسين ومن مئة، فصار المستحيل قابلًا للمشاركة (لوقا ٩:١٤). فعلنا المثل مع {n} شعوب غير مبلَّغة: ٢٧ قائمة من نحو خمسين إلى مئة. تُطبع كل قائمة حزمةَ صلاة وملصقًا، ليحمل ٢٧ تلميذًا أو كنيسة أو قائدًا محليًا كلٌّ واحدة — مصلّين لأجلهم ",
"peopleStillLoading": "لا تزال الشعوب قيد التحميل، حاول مرة أخرى بعد لحظة",
- "posterDesignTerritory": "ملصق منطقة UPG-100"
+ "posterDesignTerritory": "ملصق منطقة UPG100"
}
diff --git a/library/i18n/locales/de/upgMap.json b/library/i18n/locales/de/upgMap.json
index c247491fe..4240cd3d1 100644
--- a/library/i18n/locales/de/upgMap.json
+++ b/library/i18n/locales/de/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "UPG100-Poster",
"wallIntroA": "Wie würden Sie eine Menge von fünftausend speisen? Jesus ließ sie in Gruppen zu fünfzig und hundert lagern, und das Unmögliche wurde teilbar (Lukas 9,14). Dasselbe haben wir mit den {n} unerreichten Volksgruppen getan: 27 Listen von etwa fünfzig bis hundert. Jede Liste druckt als Gebetspaket und als Poster, damit 27 Jünger, Gemeinden oder einheimische Leiter je eine tragen können — und für sie beten ",
"peopleStillLoading": "die Volksgruppen werden noch geladen; versuchen Sie es gleich noch einmal",
- "posterDesignTerritory": "UPG-100-Gebietsplakat"
+ "posterDesignTerritory": "UPG100-Gebietsplakat"
}
diff --git a/library/i18n/locales/en/upgMap.json b/library/i18n/locales/en/upgMap.json
index 916e5e21e..7364a6190 100644
--- a/library/i18n/locales/en/upgMap.json
+++ b/library/i18n/locales/en/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "UPG100 Posters",
"wallIntroA": "How would you feed a crowd of five thousand? Jesus sat them down in groups of fifty and a hundred, and the impossible became shareable (Luke 9:14). We did the same with the {n} unreached people groups: 27 lists of about fifty to a hundred. Each list prints as a prayer packet and a poster, so 27 disciples, churches or local leaders can each carry one — praying for them ",
"peopleStillLoading": "the people groups are still loading, try again in a moment",
- "posterDesignTerritory": "UPG-100 territory poster"
+ "posterDesignTerritory": "UPG100 zone poster"
}
diff --git a/library/i18n/locales/es/upgMap.json b/library/i18n/locales/es/upgMap.json
index da700b62f..794d3f36f 100644
--- a/library/i18n/locales/es/upgMap.json
+++ b/library/i18n/locales/es/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "Carteles UPG100",
"wallIntroA": "¿Cómo alimentarías a una multitud de cinco mil? Jesús los sentó en grupos de cincuenta y de cien, y lo imposible se volvió compartible (Lucas 9:14). Hicimos lo mismo con los {n} pueblos no alcanzados: 27 listas de unos cincuenta a cien. Cada lista se imprime como paquete de oración y cartel, para que 27 discípulos, iglesias o líderes locales lleven una cada uno — orando por ellos ",
"peopleStillLoading": "los pueblos aún se están cargando; inténtalo de nuevo en un momento",
- "posterDesignTerritory": "Cartel de territorio UPG-100"
+ "posterDesignTerritory": "Cartel de zona UPG100"
}
diff --git a/library/i18n/locales/fr/upgMap.json b/library/i18n/locales/fr/upgMap.json
index 06bd814ca..937900fa5 100644
--- a/library/i18n/locales/fr/upgMap.json
+++ b/library/i18n/locales/fr/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "Affiches UPG100",
"wallIntroA": "Comment nourririez-vous une foule de cinq mille personnes ? Jésus les a fait asseoir par groupes de cinquante et de cent, et l’impossible est devenu partageable (Luc 9.14). Nous avons fait de même avec les {n} peuples non atteints : 27 listes d’environ cinquante à cent. Chaque liste s’imprime en dossier de prière et en affiche, pour que 27 disciples, Églises ou responsables locaux en portent chacun une — en priant pour eux ",
"peopleStillLoading": "les peuples sont encore en cours de chargement ; réessayez dans un instant",
- "posterDesignTerritory": "Affiche de territoire UPG-100"
+ "posterDesignTerritory": "Affiche de zone UPG100"
}
diff --git a/library/i18n/locales/hi/upgMap.json b/library/i18n/locales/hi/upgMap.json
index d96462b7c..4eb1cfafd 100644
--- a/library/i18n/locales/hi/upgMap.json
+++ b/library/i18n/locales/hi/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "UPG100 पोस्टर",
"wallIntroA": "पाँच हज़ार की भीड़ को आप कैसे खिलाते? यीशु ने उन्हें पचास-पचास और सौ-सौ के समूहों में बिठाया, और असंभव बाँटने योग्य हो गया (लूका 9:14). हमने {n} अनपहुँचे जनसमूहों के साथ वही किया: लगभग पचास से सौ तक की 27 सूचियाँ. हर सूची प्रार्थना पैकेट और पोस्टर के रूप में छपती है, ताकि 27 शिष्य, कलीसियाएँ या स्थानीय अगुवे एक-एक ले सकें — उनके लिए प्रार्थना करते हुए ",
"peopleStillLoading": "जनसमूह अभी लोड हो रहे हैं, थोड़ी देर में फिर कोशिश करें",
- "posterDesignTerritory": "UPG-100 क्षेत्र पोस्टर"
+ "posterDesignTerritory": "UPG100 क्षेत्र का पोस्टर"
}
diff --git a/library/i18n/locales/it/upgMap.json b/library/i18n/locales/it/upgMap.json
index c9f8d025e..6c9a47d53 100644
--- a/library/i18n/locales/it/upgMap.json
+++ b/library/i18n/locales/it/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "Poster UPG100",
"wallIntroA": "Come sfameresti una folla di cinquemila? Gesù li fece sedere a gruppi di cinquanta e di cento, e l’impossibile divenne condivisibile (Luca 9:14). Abbiamo fatto lo stesso con i {n} popoli non raggiunti: 27 liste di circa cinquanta-cento. Ogni lista si stampa come pacchetto di preghiera e manifesto, così 27 discepoli, chiese o responsabili locali ne portano una ciascuno — pregando per loro ",
"peopleStillLoading": "i popoli si stanno ancora caricando; riprova tra un momento",
- "posterDesignTerritory": "Manifesto di territorio UPG-100"
+ "posterDesignTerritory": "Poster di zona UPG100"
}
diff --git a/library/i18n/locales/pt/upgMap.json b/library/i18n/locales/pt/upgMap.json
index 9cffd9585..7a1a7b2cf 100644
--- a/library/i18n/locales/pt/upgMap.json
+++ b/library/i18n/locales/pt/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "Cartazes UPG100",
"wallIntroA": "Como você alimentaria uma multidão de cinco mil? Jesus os sentou em grupos de cinquenta e de cem, e o impossível tornou-se compartilhável (Lucas 9.14). Fizemos o mesmo com os {n} povos não alcançados: 27 listas de cerca de cinquenta a cem. Cada lista imprime como pacote de oração e cartaz, para que 27 discípulos, igrejas ou líderes locais levem uma cada um — orando por eles ",
"peopleStillLoading": "os povos ainda estão carregando; tente de novo em um instante",
- "posterDesignTerritory": "Cartaz de território UPG-100"
+ "posterDesignTerritory": "Cartaz de zona UPG100"
}
diff --git a/library/i18n/locales/ro/upgMap.json b/library/i18n/locales/ro/upgMap.json
index 331a40a1e..056cdb3ad 100644
--- a/library/i18n/locales/ro/upgMap.json
+++ b/library/i18n/locales/ro/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "Afișe UPG100",
"wallIntroA": "Cum ai hrăni o mulțime de cinci mii? Isus i-a așezat în grupuri de cincizeci și de o sută, iar imposibilul a devenit împărțibil (Luca 9:14). Am făcut la fel cu cele {n} popoare neatinse: 27 de liste de aproximativ cincizeci până la o sută. Fiecare listă se tipărește ca pachet de rugăciune și afiș, ca 27 de ucenici, biserici sau lideri locali să poarte fiecare câte una — rugându-se pentru ei ",
"peopleStillLoading": "popoarele încă se încarcă; încearcă din nou peste o clipă",
- "posterDesignTerritory": "Afiș de teritoriu UPG-100"
+ "posterDesignTerritory": "Poster de zonă UPG100"
}
diff --git a/library/i18n/locales/ru/upgMap.json b/library/i18n/locales/ru/upgMap.json
index 3e91bde32..ffaa2788c 100644
--- a/library/i18n/locales/ru/upgMap.json
+++ b/library/i18n/locales/ru/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "Плакаты UPG100",
"wallIntroA": "Как накормить толпу в пять тысяч человек? Иисус рассадил их группами по пятьдесят и по сто, и невозможное стало посильным (Лк. 9:14). Мы поступили так же с {n} неохваченными народами: 27 списков примерно по пятьдесят-сто. Каждый список печатается как молитвенный пакет и плакат, чтобы 27 учеников, церквей или местных служителей взяли по одному — молясь о них ",
"peopleStillLoading": "народы ещё загружаются; попробуйте ещё раз через мгновение",
- "posterDesignTerritory": "Плакат территории UPG-100"
+ "posterDesignTerritory": "Плакат зоны UPG100"
}
diff --git a/library/i18n/locales/zh/upgMap.json b/library/i18n/locales/zh/upgMap.json
index 326f50cdb..ce9f431d5 100644
--- a/library/i18n/locales/zh/upgMap.json
+++ b/library/i18n/locales/zh/upgMap.json
@@ -20,5 +20,5 @@
"wallHeading": "UPG100 海报",
"wallIntroA": "你会怎样喂饱五千人的群众?耶稣让他们一排五十、一排一百地坐下,不可能的事就变得可以分担了(路加福音 9:14)。我们对 {n} 个未得之民做了同样的事:27 份名单,每份约五十到一百。每份名单都能印成祷告手册和海报,让 27 位门徒、教会或当地领袖各领一份 — 为他们祷告 ",
"peopleStillLoading": "族群仍在加载中,请稍后再试",
- "posterDesignTerritory": "UPG-100 区域海报"
+ "posterDesignTerritory": "UPG100 区域海报(单张)"
}
diff --git a/library/packet/poster-render.js b/library/packet/poster-render.js
index 7b0871815..25b7a95a7 100644
--- a/library/packet/poster-render.js
+++ b/library/packet/poster-render.js
@@ -290,6 +290,7 @@ const HEAD_COLUMNS_CSS = `
const HEAD_COLUMNS_FULL_CSS = `
.hcolk{font-size:19px;fill:#2f2a22;font-weight:800;letter-spacing:.1em}
.hcolt{font-size:22px;fill:#3a362c}
+ .hcolb{font-weight:700;fill:#2f2a22}
.hcolh{font-family:Georgia,'Times New Roman',serif;font-size:23px;font-weight:700;fill:#2f2a22;letter-spacing:.04em;font-variant:small-caps}
.hvref{font-size:19px}
.hverse{font-size:19.5px}
@@ -851,6 +852,27 @@ const strategyParas = () => (POSTER_TEXT.strategyBody ? [].concat(POSTER_TEXT.st
// series.json (poster.sideBoxMore: one paragraph, or a list; [] prints none), as UPG100 does to keep its band even.
const whatIsMore = () => (POSTER_TEXT.sideBoxMore !== undefined ? [].concat(POSTER_TEXT.sideBoxMore) : [WHAT_IS_MORE]);
const WHAT_IS_MORE = 'Each map comes with a prayer list of its fifty to a hundred people groups — their names, a representative photo, and more detail — to mobilise informed intercession.';
+// A paragraph's lines as SVG text content, its **...** runs set bold (.hcolb); the markers are not printed.
+// The wrap measures the plain text: a short bold run is a few px wider, inside the measure's room.
+function boldLines(text, width) {
+ let plain = '', bold = [], on = false;
+ for (const part of String(text).replace(/\s+/g, ' ').trim().split('**')) {
+ for (let i = 0; i < part.length; i++) { plain += part[i]; bold.push(on); } // per UTF-16 unit, as plain[p] reads
+ on = !on;
+ }
+ let p = 0;
+ return wrapPx(plain, HC.fs, width).map((ln) => {
+ while (plain[p] === ' ') p++;
+ let out = '', run = '', runBold = null;
+ const flush = () => { if (run) out += runBold ? `${esc(run)}` : esc(run); run = ''; };
+ for (let k = 0; k < ln.length; k++, p++) {
+ if (bold[p] !== runBold) { flush(); runBold = bold[p]; }
+ run += plain[p];
+ }
+ flush();
+ return out;
+ });
+}
function headColumnsBand(top, facts, approxLists) {
const GRID_L = BAND_MARGIN_G, GRID_W = PW - 2 * BAND_MARGIN_G;
const CW = (GRID_W - 2 * HC.gut) / 3;
@@ -885,14 +907,16 @@ function headColumnsBand(top, facts, approxLists) {
bottom = Math.max(bottom, y);
} else {
const { head, paras } = col[0];
- const wrapped = paras.map((t) => wrapPx(t, HC.fs, TW));
- const blockW = Math.min(TW, Math.max(textW(head, HC.headFs), ...wrapped.flat().map((ln) => textW(ln, HC.fs))));
+ // ★ MIRRORED (2026-10-03): both prose columns take the full measure, so each sits the same distance from
+ // its rule and the two outer columns mirror each other round the centred facts column. (Each used to be
+ // centred on its own widest line, so the two could sit at different distances from their rules.)
+ const blockW = TW;
const x = (centre - blockW * HC_FALLBACK / 2).toFixed(1);
frags += `${esc(head)}`;
let y = top + HC.headLh;
- wrapped.forEach((lines, k) => {
+ paras.forEach((text, k) => {
if (k) y += HC.gap;
- for (const ln of lines) { frags += `${esc(ln)}`; y += HC.lh; }
+ for (const ln of boldLines(text, TW)) { frags += `${ln}`; y += HC.lh; }
});
bottom = Math.max(bottom, y);
}
diff --git a/library/scripts/posters/series.json b/library/scripts/posters/series.json
index 21c598936..cbdb05c85 100644
--- a/library/scripts/posters/series.json
+++ b/library/scripts/posters/series.json
@@ -29,7 +29,7 @@
"titleQualifier": "the coloured words before the region name, e.g. 'Unengaged of' (:2427)",
"seriesName": "the name in quotes on the Series line and in the side box (:1877, :1907, :2115)",
"sideBoxHeading": "the right-hand explainer's heading (:2097)",
- "sideBoxBody": "that explainer's first paragraph (:2115)",
+ "sideBoxBody": "that explainer's first paragraph (:2115); in the head-columns band **...** prints a bold run",
"strategyBody": "the Jesus column's text: one paragraph, or a list of them; absent = poster-render's STRATEGY_DEFAULT",
"sideBoxMore": "the explainer's further paragraphs: one, or a list; [] prints none; absent = poster-render's WHAT_IS_MORE"
},
@@ -68,8 +68,8 @@
"titleQualifier": "Unengaged of",
"seriesName": "UPG100 List",
"sideBoxHeading": "What is a UPG100 List?",
- "sideBoxBody": "This map is one of a {approxLists}-map set called the {seriesName} — a large, overwhelming target in the thousands, organised into clear groupings and displayed on maps and prayer lists.",
- "strategyBody": "Jesus fed the 5,000 by multiplying a small offering. He had the crowd sit in groups of hundreds and fifties (Mark 6:40). So we pray by name, a few groups at a time.",
+ "sideBoxBody": "This map is one of a {approxLists}-map set called the {seriesName} — a large, overwhelming target in the thousands, organised into clear groupings and displayed on maps and prayer lists. Each map comes with a prayer list of its fifty to a hundred people groups — their names, a representative photo, and more detail — to mobilise **informed intercession.**",
+ "strategyBody": "2000 People Groups? Jesus’ disciples were faced with a similar problem, “How can we feed so many with only five loaves and two fish?” Through radical faith and miracle-expectant prayer, He focused on the few in front of Him — dividing the crowd and entrusting faithful men and women to serve the most strategic groups of fifties and hundreds (Mark 6:40).",
"sideBoxMore": [],
"headColumns": true
},