Full detail pages: the centered set reads upward to the cross with a legend that says what a ring is; the life in weeks and the reached grid are one light SVG each, the same shape, x11
Centered set: the ring numbers ride the axis BELOW the cross, 9 at the bottom, so the eye climbs 9 → 1 to it; the dots turn half a circle to keep the number lane clear. The ring legend opens with what a ring means in plain words (sets_rings_lede x11) before listing the rings that hold someone. Life in weeks + the reached grid: each grid is ONE inline SVG, two paths of square subpaths (empty, shaded), hairlines between decades, small week and year numbers; measured on the two pages together: 10,553 page nodes → 86, load 474 → 214 ms, print 641 → 320 ms. The reached grid now has the life grid's exact shape (same geometry, no week or year numbers, centred), shaded from the top by this list's share. Gates: variants and render decode each grid's subpaths back to box indexes in reading order (5,200 distinct, Jesus' 33 years = the first 1,716, the share from the top, the same viewBox for both); two-sided: shading the LAST 33 years fails both. Gospel's three optional nits folded in (it un’altra, example_note x5, ru чёрточка).
This commit is contained in:
parent
7e18948698
commit
0a407907d5
4 changed files with 133 additions and 82 deletions
|
|
@ -184,6 +184,23 @@ const scriptureIsEnglish = lang !== 'en'
|
|||
&& !(catalog[lang] || {}).point4_passage
|
||||
await page.evaluate((words) => { window.__EXPECTED_RHYTHMS = words }, expected)
|
||||
|
||||
// The life grid's decoder: every square subpath (M x y …) of an .lc-svg back to its box index, from the geometry
|
||||
// the SVG declares. Used by the life-calendar checks below.
|
||||
await page.evaluate(() => {
|
||||
window.__lcBoxes = (svg) => {
|
||||
const [x0, y0, p, dec] = String(svg.dataset.geom || '').split(',').map(Number)
|
||||
const weeks = Number(svg.dataset.weeks)
|
||||
const idx = (d) => [...String(d || '').matchAll(/M([\d.]+) ([\d.]+)/g)].map((m) => {
|
||||
const x = Number(m[1]) - 0.5 - x0, y = Number(m[2]) - 0.5 - y0
|
||||
const b = Math.floor(y / (10 * p + dec))
|
||||
return (b * 10 + Math.round((y - b * (10 * p + dec)) / p)) * weeks + Math.round(x / p)
|
||||
})
|
||||
const off = idx((svg.querySelector('path.lc-off') || {}).getAttribute?.('d'))
|
||||
const sh = svg.querySelector('path.lc-on, path.lc-reached')
|
||||
const shaded = sh ? idx(sh.getAttribute('d')).sort((a, b) => a - b) : []
|
||||
return { total: off.length + shaded.length, shaded, distinct: new Set([...off, ...shaded]).size }
|
||||
}
|
||||
})
|
||||
const r = await page.evaluate(() => {
|
||||
const body = document.body.innerText
|
||||
const imgs = [...document.querySelectorAll('.card img, img.ws-ph')] // profile cards, or lightweight rows (2026-09-30)
|
||||
|
|
@ -253,29 +270,30 @@ const r = await page.evaluate(() => {
|
|||
// stays exact whether or not the caller supplied set data.
|
||||
setsSheets: document.querySelectorAll('.sheet-sets').length,
|
||||
christianSheets: document.querySelectorAll('.sheet-christian').length,
|
||||
lifeCells: document.querySelectorAll('.lc-frame .lc-c').length,
|
||||
// ★ THE LIFE GRIDS ARE ONE SVG EACH (2026-10-02): two paths of square subpaths (empty and shaded) placed by
|
||||
// the geometry the SVG states (data-geom x0,y0,pitch,decadeGap). Each subpath is decoded back to its box index
|
||||
// in reading order, so the checks below still see box by box what is drawn, not a count the renderer asserts.
|
||||
lifeCells: [...document.querySelectorAll('.lc-frame .lc-svg')].reduce((a, g) => a + window.__lcBoxes(g).total, 0),
|
||||
prayBoxes: document.querySelectorAll('.pray-box').length,
|
||||
fdTopOver: (() => { const b = document.querySelector('.fd-top'); if (!b) return 0; const top = b.getBoundingClientRect().top
|
||||
return Math.round(Math.max(0, ...[...b.children].map((k) => k.getBoundingClientRect().bottom + parseFloat(getComputedStyle(k).marginBottom))) - top - b.clientHeight) })(),
|
||||
funnelTip: document.querySelectorAll('.funnel-tip').length,
|
||||
lifeShaded: document.querySelectorAll('.lc-frame .lc-reached').length,
|
||||
lifeShaded: (() => { const g = document.querySelector('.sheet-christian .lc-svg'); return g ? window.__lcBoxes(g).shaded.length : null })(),
|
||||
// ★ FILLED FROM THE TOP, in reading order (the packet's design), measured on the DOM order of the cells:
|
||||
// the shaded boxes must be EXACTLY the first N. This used to require a SCATTER, the opposite design;
|
||||
// a count alone still cannot tell a top fill from a scatter or a fill from the bottom.
|
||||
lifeTopFill: (() => {
|
||||
const grid = document.querySelector('.sheet-christian .lc-frame')
|
||||
if (!grid) return null
|
||||
const cells = [...grid.querySelectorAll('.lc-c')]
|
||||
const n = cells.filter((c) => c.classList.contains('lc-reached')).length
|
||||
return cells.slice(0, n).every((c) => c.classList.contains('lc-reached'))
|
||||
const g = document.querySelector('.sheet-christian .lc-svg')
|
||||
if (!g) return null
|
||||
const s = window.__lcBoxes(g).shaded
|
||||
return s.every((v, i) => v === i)
|
||||
})(),
|
||||
// The life page: Jesus' 33 years, the first 33 x 52 = 1,716 boxes and no others.
|
||||
jesusTopFill: (() => {
|
||||
const grid = document.querySelector('.sheet-life .lc-frame')
|
||||
if (!grid) return null
|
||||
const cells = [...grid.querySelectorAll('.lc-c')]
|
||||
const on = cells.filter((c) => c.classList.contains('lc-on')).length
|
||||
return { on, top: cells.slice(0, on).every((c) => c.classList.contains('lc-on')) }
|
||||
const g = document.querySelector('.sheet-life .lc-svg')
|
||||
if (!g) return null
|
||||
const s = window.__lcBoxes(g).shaded
|
||||
return { on: s.length, top: s.every((v, i) => v === i) }
|
||||
})(),
|
||||
funnelEq: document.querySelectorAll('.funnel-eq').length,
|
||||
funnelSteps: document.querySelectorAll('.funnel-mine').length,
|
||||
|
|
|
|||
|
|
@ -155,22 +155,34 @@ ok(!/<em>[^<]*<\/em><\/span>/.test(full.slice(full.indexOf('class="card"'))), 't
|
|||
// because none looked inside the grid. Read the cells of each frame, in order (the legend's sample boxes
|
||||
// sit outside the frame and are excluded):
|
||||
{
|
||||
const frame = (html, cls) => {
|
||||
// The grid is ONE SVG per page (packet-render lifeGrid): decode each square subpath back to its box index
|
||||
// from the geometry the SVG declares, so the check reads the boxes as drawn, in reading order.
|
||||
const grid = (html, cls) => {
|
||||
const i = html.indexOf(`<div class="sheet ${cls}`)
|
||||
// By class PREFIX: the knows-Christ grid is `lc-frame lc-plain` (drawn without week/year axes).
|
||||
const a = html.indexOf('<div class="lc-frame', i)
|
||||
return a < 0 ? '' : html.slice(a, html.indexOf('<div class="lc-legend">', a))
|
||||
const a = html.indexOf('<svg class="lc-svg"', i)
|
||||
return a < 0 || i < 0 ? '' : html.slice(a, html.indexOf('</svg>', a))
|
||||
}
|
||||
const cells = (h) => [...h.matchAll(/<i class="lc-c( lc-on| lc-reached)?"><\/i>/g)].map((m) => (m[1] || '').trim())
|
||||
const life = cells(frame(full, 'sheet-life'))
|
||||
ok(life.length === 5200, `the life calendar should hold 100 x 52 = 5,200 boxes, got ${life.length}`)
|
||||
const gold = life.filter((c) => c === 'lc-on').length
|
||||
ok(gold === 33 * 52 && life.slice(0, gold).every((c) => c === 'lc-on'),
|
||||
`the life calendar should shade Jesus' 33 years as its FIRST 1,716 boxes — got ${gold}, from the top: ${life.slice(0, gold).every((c) => c === 'lc-on')}`)
|
||||
const ev = cells(frame(full, 'sheet-christian'))
|
||||
const shaded = ev.filter((c) => c === 'lc-reached').length
|
||||
ok(ev.length === 5200 && shaded === Math.round(0.032 * 5200) && ev.slice(0, shaded).every((c) => c === 'lc-reached'),
|
||||
`the %-evangelical grid should shade 3.2% = ${Math.round(0.032 * 5200)} boxes FROM THE TOP, got ${shaded} of ${ev.length}`)
|
||||
const boxes = (svg) => {
|
||||
const [x0, y0, p, dec] = ((svg.match(/data-geom="([^"]+)"/) || [])[1] || '').split(',').map(Number)
|
||||
const weeks = Number((svg.match(/data-weeks="(\d+)"/) || [])[1])
|
||||
const idx = (d) => [...String(d || '').matchAll(/M([\d.]+) ([\d.]+)/g)].map((m) => {
|
||||
const x = Number(m[1]) - 0.5 - x0, y = Number(m[2]) - 0.5 - y0, b = Math.floor(y / (10 * p + dec))
|
||||
return (b * 10 + Math.round((y - b * (10 * p + dec)) / p)) * weeks + Math.round(x / p)
|
||||
})
|
||||
const off = idx((svg.match(/<path class="lc-off" d="([^"]*)"/) || [])[1])
|
||||
const on = idx((svg.match(/<path class="lc-(?:on|reached)" d="([^"]*)"/) || [])[1]).sort((a, b) => a - b)
|
||||
return { total: off.length + on.length, distinct: new Set([...off, ...on]).size, on }
|
||||
}
|
||||
const life = boxes(grid(full, 'sheet-life'))
|
||||
ok(life.total === 5200 && life.distinct === 5200, `the life calendar should hold 100 x 52 = 5,200 distinct boxes, got ${life.total} (${life.distinct} distinct)`)
|
||||
ok(life.on.length === 33 * 52 && life.on.every((v, i) => v === i),
|
||||
`the life calendar should shade Jesus' 33 years as its FIRST 1,716 boxes — got ${life.on.length}, from the top: ${life.on.every((v, i) => v === i)}`)
|
||||
const ev = boxes(grid(full, 'sheet-christian'))
|
||||
ok(ev.total === 5200 && ev.on.length === Math.round(0.032 * 5200) && ev.on.every((v, i) => v === i),
|
||||
`the %-evangelical grid should shade 3.2% = ${Math.round(0.032 * 5200)} boxes FROM THE TOP, got ${ev.on.length} of ${ev.total}`)
|
||||
// THE SAME TABLE: the people grid has the life grid's exact shape (the Driver: the same aspect ratio).
|
||||
const vb = (svg) => (svg.match(/viewBox="([^"]+)"/) || [])[1]
|
||||
ok(vb(grid(full, 'sheet-life')) && vb(grid(full, 'sheet-life')) === vb(grid(full, 'sheet-christian')), 'the people grid should have the life grid\'s exact shape')
|
||||
}
|
||||
|
||||
// ★ UNKNOWN POINTS ARE STATED EVEN THOUGH THEY CANNOT BE PLOTTED. layoutBoundedSet gives them no
|
||||
|
|
|
|||
|
|
@ -209,16 +209,14 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
the legend and foot ~80px, which lands above the ${H - M}px margin with room to spare. */
|
||||
.pg-head-c,.pg-lede-c{text-align:center;margin-inline:auto}
|
||||
.lc-frame{width:max-content;margin:0 auto;padding:14px 22px 18px;border:1.5px solid #3b463d;background:#fff}
|
||||
.lc-wk,.lc-dec{display:grid;grid-template-columns:30px repeat(52,11px);column-gap:3px}
|
||||
.lc-wk{height:16px;margin-bottom:4px;font:400 10px Georgia,serif;color:#6f6a5c}
|
||||
.lc-wk span{text-align:center;line-height:16px}
|
||||
.lc-dec{grid-auto-rows:11px;row-gap:3px}
|
||||
.lc-plain .lc-all{display:grid;grid-template-columns:repeat(65,14.5px);gap:3px}
|
||||
.lc-plain .lc-c{width:14.5px;height:14.5px}
|
||||
.lc-plain + .lc-legend{font-size:22px;gap:48px}
|
||||
.lc-plain ~ .lc-per{font-size:20px}
|
||||
.lc-dec+.lc-dec{margin-top:6px;padding-top:6px;border-top:1px solid #d8d3c4}
|
||||
.lc-y{font:400 10px/11px Georgia,serif;color:#8a8577;text-align:end;padding-inline-end:6px}
|
||||
/* The life grid is one SVG (lifeGrid): two paths of boxes, hairlines between decades, small numbers. */
|
||||
.lc-svg{display:block}
|
||||
.lc-svg .lc-off{fill:#fff;stroke:#8f8a78;stroke-width:1}
|
||||
.lc-svg .lc-on{fill:#c9a13b;stroke:#a8832a;stroke-width:1}
|
||||
.lc-svg .lc-reached{fill:#2f6d4f;stroke:#2f6d4f;stroke-width:1}
|
||||
.lc-svg .lc-rule{stroke:#d8d3c4;stroke-width:1}
|
||||
.lc-svg .lc-t{font:400 10px Georgia,serif;fill:#6f6a5c}
|
||||
.lc-svg .lc-ty{fill:#8a8577}
|
||||
.lc-c{display:block;box-sizing:border-box;width:11px;height:11px;border:1px solid #8f8a78;background:#fff}
|
||||
/* Jesus' 33 years: warm gold, the one shading on the life page. */
|
||||
.lc-on{background:#c9a13b;border-color:#a8832a}
|
||||
|
|
@ -350,6 +348,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
.set-solo-centered + .set-legend, .set-solo-centered ~ .set-legend{max-width:${SET_SOLO_W.centered}px}
|
||||
.set-legend{margin:26px auto 0;max-width:${SET_SOLO}px}
|
||||
.set-legend-h{font:700 17px Georgia,serif;color:#1b1a17;margin:0 0 8px}
|
||||
.set-legend-p{margin:0 0 10px;font:400 16px/1.45 Georgia,serif;color:#413d35}
|
||||
.set-rings{margin:0;padding:0;list-style:none;display:grid;grid-template-columns:1fr 1fr;column-gap:40px;
|
||||
row-gap:6px;font:400 16px Georgia,serif;color:#413d35}
|
||||
.set-rings li{display:flex;align-items:center;gap:10px}
|
||||
|
|
@ -973,36 +972,43 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
// ★ axes:false DRAWS THE SAME BOXES WITH NO WEEK OR YEAR LABELS, for a page where a box is a share
|
||||
// of PEOPLE rather than a week of a life (the knows-Christ page): an axis numbered in weeks and years
|
||||
// there would describe a unit the page is not using.
|
||||
// ★ ONE PICTURE, NOT 5,200 BOXES (2026-10-02). The grid was 5,200 bordered HTML boxes in a CSS grid, twice
|
||||
// in the full packet: heavy to lay out and to hold in a phone's memory. It is now ONE inline SVG per page whose
|
||||
// boxes are two <path>s (the empty weeks and the shaded ones, a square subpath each): the same 100 x 52 boxes,
|
||||
// vector on paper, a handful of nodes in the page. Both pages use the SAME geometry (LC), so the people grid
|
||||
// has exactly the life grid's shape; it simply prints no week or year numbers, since its box is a share of
|
||||
// people. data-* attributes state what is drawn, and the gates read the subpaths back, box by box.
|
||||
const LC = { cell: 11, pitch: 14, x0: 36, y0: 22, dec: 12 };
|
||||
function lifeGrid({ shaded = 0, shadeClass = 'lc-on', axes = true } = {}) {
|
||||
const YEARS = 100, WEEKS = 52, TOTAL = YEARS * WEEKS;
|
||||
const n = Math.max(0, Math.min(TOTAL, Math.round(Number(shaded) || 0)));
|
||||
// Week labels sit in the column of the week they name (column 1 is the year labels).
|
||||
const WEEK_MARKS = [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50];
|
||||
const head = axes ? `<div class="lc-wk">${WEEK_MARKS.map((w) => `<span style="grid-column:${w + 1}">${w}</span>`).join('')}</div>` : '';
|
||||
// ★ WITHOUT AXES THE GRID REFLOWS TO FILL THE PAGE. 52 columns exist only because a year has 52
|
||||
// weeks; a page whose box is a share of PEOPLE has no such constraint, and at 52 x 11px the grid
|
||||
// used about half the sheet's width. The same 5,200 boxes are laid out 65 x 80 (both divide 5,200,
|
||||
// so the rectangle is whole) in 14.5px squares. MEASURED in a browser: 80 x 65 across the full width
|
||||
// left 478px of blank sheet below the legend (72% of the height used), because a wide grid is short;
|
||||
// 65 x 80 spends that height on bigger boxes and still leaves room for a longer lede in other languages.
|
||||
if (!axes) {
|
||||
const cells = []
|
||||
for (let i = 0; i < TOTAL; i++) cells.push(`<i class="lc-c${i < n ? ' ' + shadeClass : ''}"></i>`)
|
||||
return { TOTAL, YEARS, WEEKS, shadedCount: n, html: `<div class="lc-frame lc-plain"><div class="lc-all">${cells.join('')}</div></div>` };
|
||||
}
|
||||
const decades = [];
|
||||
for (let d = 0; d < YEARS / 10; d++) {
|
||||
const rows = [];
|
||||
for (let y = d * 10; y < d * 10 + 10; y++) {
|
||||
const year = y + 1;
|
||||
if (axes) rows.push(`<b class="lc-y">${year === 1 || year % 5 === 0 ? num(year) : ''}</b>`);
|
||||
for (let w = 0; w < WEEKS; w++) {
|
||||
rows.push(`<i class="lc-c${y * WEEKS + w < n ? ' ' + shadeClass : ''}"></i>`);
|
||||
}
|
||||
// Without axes the label margins are split evenly round the boxes, so the grid sits centred in the same frame.
|
||||
const x0 = axes ? LC.x0 : LC.x0 / 2, y0 = axes ? LC.y0 : LC.y0 / 2;
|
||||
const at = (i) => { const r = Math.floor(i / WEEKS), c = i % WEEKS; return [x0 + c * LC.pitch, y0 + r * LC.pitch + Math.floor(r / 10) * LC.dec]; };
|
||||
const sq = (i) => { const [x, y] = at(i); return `M${x + 0.5} ${y + 0.5}h${LC.cell - 1}v${LC.cell - 1}h${1 - LC.cell}z`; };
|
||||
let on = '', off = '';
|
||||
for (let i = 0; i < TOTAL; i++) { if (i < n) on += sq(i); else off += sq(i); }
|
||||
const gap = LC.pitch - LC.cell;
|
||||
const W = LC.x0 + WEEKS * LC.pitch - gap, H = LC.y0 + YEARS * LC.pitch + (YEARS / 10 - 1) * LC.dec - gap;
|
||||
let marks = '';
|
||||
if (axes) {
|
||||
// Week numbers over their own column; year numbers (1, 5, 10, …) beside their row.
|
||||
for (const w of [1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50]) marks += `<text class="lc-t" x="${LC.x0 + (w - 1) * LC.pitch + LC.cell / 2}" y="12" text-anchor="middle">${w}</text>`;
|
||||
for (let y = 1; y <= YEARS; y++) {
|
||||
if (y !== 1 && y % 5) continue;
|
||||
const [, top] = at((y - 1) * WEEKS);
|
||||
marks += `<text class="lc-t lc-ty" x="${LC.x0 - 6}" y="${top + LC.cell - 1.5}" text-anchor="end">${num(y)}</text>`;
|
||||
}
|
||||
decades.push(`<div class="lc-dec">${rows.join('')}</div>`);
|
||||
}
|
||||
return { TOTAL, YEARS, WEEKS, shadedCount: n, html: `<div class="lc-frame${axes ? '' : ' lc-plain'}">${head}${decades.join('')}</div>` };
|
||||
// A hairline between decades, in the extra gap, so ten years read as one block.
|
||||
let rules = '';
|
||||
for (let d = 1; d < YEARS / 10; d++) {
|
||||
const [, top] = at(d * 10 * WEEKS);
|
||||
rules += `<line class="lc-rule" x1="${x0}" x2="${x0 + WEEKS * LC.pitch - gap}" y1="${top - LC.dec / 2 - gap / 2}" y2="${top - LC.dec / 2 - gap / 2}"/>`;
|
||||
}
|
||||
const svg = `<svg class="lc-svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}" data-total="${TOTAL}" data-shaded="${n}" data-weeks="${WEEKS}" data-geom="${x0},${y0},${LC.pitch},${LC.dec}">`
|
||||
+ `${marks}${rules}<path class="lc-off" d="${off}"/>${on ? `<path class="${shadeClass}" d="${on}"/>` : ''}</svg>`;
|
||||
return { TOTAL, YEARS, WEEKS, shadedCount: n, html: `<div class="lc-frame${axes ? '' : ' lc-plain'}">${svg}</div>` };
|
||||
}
|
||||
|
||||
// Page 2 — a human life, unshaded except for the weeks already lived if the caller supplies an age.
|
||||
|
|
@ -1545,10 +1551,12 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
bands.push(`<circle class="${n % 2 ? 'set-band-a' : 'set-band-b'}" cx="0" cy="0" r="${R0 + n * BW}"/>`);
|
||||
}
|
||||
bands.push(`<circle class="set-core" cx="0" cy="0" r="${R0}"/>`);
|
||||
// Numbers ride the vertical axis above the centre, one per band; dots keep clear of that lane.
|
||||
// ★ READ UPWARD TO THE CROSS (2026-10-02): the numbers ride the vertical axis BELOW the centre, 9 at the
|
||||
// bottom, so the eye climbs 9 → 1 to the cross. The dots keep clear of that lane: bandFill leaves the
|
||||
// lane straight up clear, and every dot is turned half a circle (x, y → -x, -y) to put it below.
|
||||
const nums = [];
|
||||
for (let n = 1; n <= RINGS; n++) {
|
||||
const y = -(R0 + (n - 0.5) * BW);
|
||||
const y = R0 + (n - 0.5) * BW;
|
||||
nums.push(`<circle class="set-num" cx="0" cy="${y}" r="9"/><text class="set-num-t" x="0" y="${y}">${n}</text>`);
|
||||
}
|
||||
// THE SHARED BAND GEOMETRY (charts/ringBands.js bandFill, the same module the screen set charts
|
||||
|
|
@ -1567,7 +1575,7 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
labels: radii.map((x) => ({ ring: x.ring, keepOut: laneFor(x.r) })),
|
||||
});
|
||||
const dots = bf.placed.map((p) =>
|
||||
`<circle class="set-in" cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" r="${(p.pin || DOT).toFixed(1)}"/>`).join('');
|
||||
`<circle class="set-in" cx="${(-p.x).toFixed(1)}" cy="${(-p.y).toFixed(1)}" r="${(p.pin || DOT).toFixed(1)}"/>`).join('');
|
||||
panels.push({ kind: 'centered', html: `<div class="set-panel">
|
||||
<h2 class="set-h">${esc(t('sets_head_centered'))}</h2>
|
||||
<div class="set-sub">${esc(t('sets_centered_title'))}</div>
|
||||
|
|
@ -1578,7 +1586,9 @@ export function createPacketRenderer({ strings, lang = 'en', fontData = null, de
|
|||
{ placed: num(bf.placed.length), centre: num(T.centre), unplaced: num(T.unplaced + bf.unplaced.length) }))}</p>
|
||||
</div>` });
|
||||
const rows = ringLegend(T);
|
||||
if (rows) legend = `<div class="set-legend"><div class="set-legend-h">${esc(t('sets_rings_head'))}</div>${rows}</div>`;
|
||||
// The legend first says what a ring IS, in plain words, then lists the rings that hold someone.
|
||||
if (rows) legend = `<div class="set-legend"><div class="set-legend-h">${esc(t('sets_rings_head'))}</div>`
|
||||
+ `<p class="set-legend-p">${esc(t('sets_rings_lede'))}</p>${rows}</div>`;
|
||||
}
|
||||
|
||||
// ★ ONE CHART PER PAGE (2026-09-29, second look: the bounded set on one page and the centered set on
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@
|
|||
"blessing_credit": "Numbers 6:24–26 · World English Bible, British Edition (public domain) · ebible.org",
|
||||
"howto_bless_col": "Bless them by name, with the blessing above or another from Scripture.",
|
||||
"example_title": "Example",
|
||||
"example_note": "One mark each time you pray it; the fifth mark crosses the four before it.",
|
||||
"example_note": "One mark each time you pray; the fifth mark crosses the four before it.",
|
||||
"themes_title": "Prayer themes",
|
||||
"themes_intro": "Weave these themes into your five-minute blocks on the prayer wheel. They add range and fresh ideas.",
|
||||
"themes": [
|
||||
|
|
@ -358,7 +358,8 @@
|
|||
"Habakkuk 2:14",
|
||||
"Matthew 8:26"
|
||||
]
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Each dot is a people group in this list. Its ring shows how close it lives to believers who could reach it: the nearer the cross, the more they share. Ring 1 speaks the same dialect and is the same people; ring 9 shares only a language family; past the last ring, believers live nearby but share no language. Read from 9 at the bottom up to 1 at the cross."
|
||||
},
|
||||
"es": {
|
||||
"map_page_kicker": "GRUPOS ÉTNICOS NO EVANGELIZADOS",
|
||||
|
|
@ -671,7 +672,7 @@
|
|||
"blessing_credit": "Números 6:24–26 · Reina Valera 1909 (dominio público) · ebible.org",
|
||||
"howto_bless_col": "Bendícelos por su nombre, con la bendición de arriba u otra de las Escrituras.",
|
||||
"example_title": "Ejemplo",
|
||||
"example_note": "Una marca cada vez que lo ores; la quinta cruza las cuatro anteriores.",
|
||||
"example_note": "Una marca cada vez que ores; la quinta cruza las cuatro anteriores.",
|
||||
"themes_title": "Temas de oración",
|
||||
"themes_intro": "Entreteje estos temas en tus bloques de cinco minutos de la rueda de oración. Dan variedad e ideas nuevas.",
|
||||
"themes": [
|
||||
|
|
@ -688,7 +689,8 @@
|
|||
"Tesoro",
|
||||
"Monte",
|
||||
"Mar"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Cada punto es un pueblo de esta lista. Su anillo muestra qué tan cerca vive de creyentes que podrían alcanzarlo: cuanto más cerca de la cruz, más comparten. El anillo 1 habla el mismo dialecto y es el mismo pueblo; el anillo 9 solo comparte una familia de lenguas; fuera del último anillo, los creyentes viven cerca pero no comparten idioma. Lee desde el 9, abajo, hasta el 1, en la cruz."
|
||||
},
|
||||
"fr": {
|
||||
"map_page_kicker": "GROUPES ETHNIQUES NON ATTEINTS",
|
||||
|
|
@ -1001,7 +1003,7 @@
|
|||
"blessing_credit": "Nombres 6:24–26 · French Ostervald Bible (domaine public) · ebible.org",
|
||||
"howto_bless_col": "Bénissez-les par leur nom, avec la bénédiction ci-dessus ou une autre tirée des Écritures.",
|
||||
"example_title": "Exemple",
|
||||
"example_note": "Un trait chaque fois que vous le priez ; le cinquième barre les quatre précédents.",
|
||||
"example_note": "Un trait chaque fois que vous priez ; le cinquième barre les quatre précédents.",
|
||||
"themes_title": "Thèmes de prière",
|
||||
"themes_intro": "Intégrez ces thèmes à vos blocs de cinq minutes de la roue de prière. Ils apportent de la variété et des idées nouvelles.",
|
||||
"themes": [
|
||||
|
|
@ -1018,7 +1020,8 @@
|
|||
"Trésor",
|
||||
"Montagne",
|
||||
"Mer"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Chaque point est un peuple de cette liste. Son anneau montre à quelle distance il vit de croyants qui pourraient l’atteindre : plus on est près de la croix, plus ils ont en commun. L’anneau 1 parle le même dialecte et forme le même peuple ; l’anneau 9 ne partage qu’une famille de langues ; au-delà du dernier anneau, des croyants vivent tout près mais sans langue commune. Lisez de 9, en bas, jusqu’à 1, à la croix."
|
||||
},
|
||||
"pt": {
|
||||
"map_page_kicker": "GRUPOS ÉTNICOS NÃO ALCANÇADOS",
|
||||
|
|
@ -1345,7 +1348,8 @@
|
|||
"Tesouro",
|
||||
"Monte",
|
||||
"Mar"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Cada ponto é um povo desta lista. Seu anel mostra quão perto ele vive de crentes que poderiam alcançá-lo: quanto mais perto da cruz, mais eles têm em comum. O anel 1 fala o mesmo dialeto e é o mesmo povo; o anel 9 compartilha só uma família de línguas; além do último anel, há crentes por perto, mas sem língua em comum. Leia do 9, embaixo, até o 1, na cruz."
|
||||
},
|
||||
"de": {
|
||||
"map_page_kicker": "UNERREICHTE VOLKSGRUPPEN",
|
||||
|
|
@ -1675,7 +1679,8 @@
|
|||
"Schatz",
|
||||
"Berg",
|
||||
"Meer"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Jeder Punkt ist eine Volksgruppe dieser Liste. Ihr Ring zeigt, wie nah sie bei Gläubigen lebt, die sie erreichen könnten: Je näher am Kreuz, desto mehr haben sie gemeinsam. Ring 1 spricht denselben Dialekt und ist dasselbe Volk; Ring 9 teilt nur eine Sprachfamilie; jenseits des letzten Rings leben Gläubige in der Nähe, aber ohne gemeinsame Sprache. Lesen Sie von 9 unten hinauf bis 1 am Kreuz."
|
||||
},
|
||||
"it": {
|
||||
"map_page_kicker": "GRUPPI ETNICI NON RAGGIUNTI",
|
||||
|
|
@ -1983,9 +1988,9 @@
|
|||
"pace_note": "Pregare ogni giorno per tutti i popoli è per gli intercessori che ne hanno il dono e dedicano circa tre ore al giorno alla preghiera. Per la maggior parte di noi, ogni settimana è un ritmo sano e costante.",
|
||||
"blessing_text": "«Il Signore ti benedica e ti guardi. Il Signore faccia risplendere le sua faccia verso te, e ti sia propizio. Alzi il Signore la sua faccia verso te, e ti stabilisca la pace.»",
|
||||
"blessing_credit": "Numeri 6:24–26 · Italian Diodati Bible (pubblico dominio) · ebible.org",
|
||||
"howto_bless_col": "Benedicili chiamandoli per nome, con la benedizione qui sopra o un'altra dalle Scritture.",
|
||||
"howto_bless_col": "Benedicili chiamandoli per nome, con la benedizione qui sopra o un’altra dalle Scritture.",
|
||||
"example_title": "Esempio",
|
||||
"example_note": "Un trattino ogni volta che lo preghi; il quinto attraversa i quattro precedenti.",
|
||||
"example_note": "Un trattino ogni volta che preghi; il quinto attraversa i quattro precedenti.",
|
||||
"themes_title": "Temi di preghiera",
|
||||
"themes_intro": "Intreccia questi temi nei tuoi blocchi di cinque minuti della ruota di preghiera. Danno varietà e idee nuove.",
|
||||
"themes": [
|
||||
|
|
@ -2002,7 +2007,8 @@
|
|||
"Tesoro",
|
||||
"Monte",
|
||||
"Mare"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Ogni punto è un popolo di questa lista. Il suo anello mostra quanto vive vicino a credenti che potrebbero raggiungerlo: più è vicino alla croce, più hanno in comune. L’anello 1 parla lo stesso dialetto ed è lo stesso popolo; l’anello 9 condivide solo una famiglia linguistica; oltre l’ultimo anello, i credenti vivono vicino ma senza una lingua comune. Leggi dal 9, in basso, fino all’1, alla croce."
|
||||
},
|
||||
"ro": {
|
||||
"map_page_kicker": "GRUPURI ETNICE NEATINSE",
|
||||
|
|
@ -2327,7 +2333,8 @@
|
|||
"Comoara",
|
||||
"Muntele",
|
||||
"Marea"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Fiecare punct este un popor din această listă. Inelul lui arată cât de aproape trăiește de credincioși care l-ar putea atinge: cu cât e mai aproape de cruce, cu atât au mai multe în comun. Inelul 1 vorbește același dialect și este același popor; inelul 9 are în comun doar o familie de limbi; dincolo de ultimul inel, credincioșii trăiesc aproape, dar fără o limbă comună. Citește de la 9, jos, până la 1, la cruce."
|
||||
},
|
||||
"ru": {
|
||||
"map_page_kicker": "НЕОХВАЧЕННЫЕ НАРОДЫ",
|
||||
|
|
@ -2647,7 +2654,7 @@
|
|||
],
|
||||
"howto_bless_col": "Благословляйте их по имени — словами выше или другими словами из Писания.",
|
||||
"example_title": "Пример",
|
||||
"example_note": "Одна черточка за каждую молитву; пятая перечёркивает четыре предыдущие.",
|
||||
"example_note": "Одна чёрточка за каждую молитву; пятая перечёркивает четыре предыдущие.",
|
||||
"themes_title": "Темы для молитвы",
|
||||
"themes_intro": "Вплетайте эти темы в пятиминутные отрезки молитвенного колеса. Они добавят разнообразия и новых идей.",
|
||||
"themes": [
|
||||
|
|
@ -2664,7 +2671,8 @@
|
|||
"Сокровище",
|
||||
"Гора",
|
||||
"Море"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "Каждая точка — народ из этого списка. Её кольцо показывает, насколько близко он живёт к верующим, которые могли бы его достичь: чем ближе к кресту, тем больше у них общего. Кольцо 1 говорит на том же диалекте и составляет тот же народ; кольцо 9 разделяет лишь языковую семью; за последним кольцом верующие живут рядом, но без общего языка. Читайте от 9 внизу вверх до 1 у креста."
|
||||
},
|
||||
"ar": {
|
||||
"map_page_kicker": "شعوب غير مبلَّغة",
|
||||
|
|
@ -2994,7 +3002,8 @@
|
|||
"الكنز",
|
||||
"الجبل",
|
||||
"البحر"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "كل نقطة شعب من هذه القائمة. تُظهر حلقته مدى قربه من مؤمنين يمكنهم الوصول إليه: كلما اقترب من الصليب زاد ما يشتركون فيه. الحلقة 1 تتكلم اللهجة نفسها وهي الشعب نفسه؛ الحلقة 9 لا تشترك إلا في عائلة لغوية؛ وبعد الحلقة الأخيرة يعيش مؤمنون قريبًا لكن بلا لغة مشتركة. اقرأ من 9 في الأسفل صعودًا إلى 1 عند الصليب."
|
||||
},
|
||||
"hi": {
|
||||
"map_page_kicker": "अनछुए जनसमूह",
|
||||
|
|
@ -3319,7 +3328,8 @@
|
|||
"ख़ज़ाना",
|
||||
"पहाड़",
|
||||
"समुद्र"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "हर बिंदु इस सूची का एक जनसमूह है। उसका वलय दिखाता है कि वह उन विश्वासियों के कितने पास रहता है जो उस तक पहुँच सकते हैं: क्रूस के जितना पास, उतना अधिक साझा। वलय 1 वही बोली बोलता है और वही लोग है; वलय 9 केवल एक भाषा-परिवार साझा करता है; अंतिम वलय के बाहर विश्वासी पास रहते हैं, पर कोई साझा भाषा नहीं। नीचे 9 से ऊपर क्रूस पर 1 तक पढ़ें।"
|
||||
},
|
||||
"zh": {
|
||||
"map_page_kicker": "未得之民",
|
||||
|
|
@ -3646,6 +3656,7 @@
|
|||
"宝藏",
|
||||
"山",
|
||||
"海"
|
||||
]
|
||||
],
|
||||
"sets_rings_lede": "每个点是这份名单中的一个族群。它所在的环表示它离能够接触到它的信徒有多近:越靠近十字架,彼此相同之处越多。第 1 环讲同一种方言,是同一个民族;第 9 环只同属一个语系;最后一环之外,信徒住在附近,却没有共同语言。从下方的 9 向上读到十字架旁的 1。"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue