Source comments state design history and measurements as facts, without quoting or naming anyone

Comments in library/ and internal/ that quoted a person, attributed a decision to one, used first person,
named fleet agents or carried private host names now state the same technical fact in neutral words:
poster-render (including the comments it writes into generated posters), packet-render, the poster and
research scripts, PosterWall, DataTable, the legends, the layouts and the gates. The network-policy note
states the policy itself. Behaviour is unchanged: a few printed gate messages and reason labels drop an
agent name, the poster-render integrity pin follows its comment-only edit, and series.json changes only in
its _meta and _why notes. The historical poster design snapshots (library/scripts/posters/snapshots/),
which nothing reads, are removed.
This commit is contained in:
Doxa Maps 2026-10-01 00:13:20 +00:00
parent 3f7d489409
commit 2c397d25ef
179 changed files with 753 additions and 17481 deletions

View file

@ -423,7 +423,7 @@ func (e *engine) startWith(lang, device string, src *recorderProc, sink func(eve
recMu.Unlock() recMu.Unlock()
writeMu.Lock() writeMu.Lock()
// The streaming model holds back the last fraction of a second (its right context) until more audio // The streaming model holds back the last fraction of a second (its right context) until more audio
// or a pause arrives, and a commit alone does not flush it (rega: a Stop mid-sentence lost "what // or a pause arrives, and a commit alone does not flush it (measured: a Stop mid-sentence lost "what
// your country…"). 1.2 s of silence first lets it finish the words already spoken. // your country…"). 1.2 s of silence first lets it finish the words already spoken.
silence := make([]byte, 3200) silence := make([]byte, 3200)
for i := 0; i < 12; i++ { for i := 0; i < 12; i++ {
@ -750,7 +750,7 @@ func (e *engine) startWith(lang, device string, src *recorderProc, sink func(eve
e.logs.add("speech server error: %s", string(msg)) e.logs.add("speech server error: %s", string(msg))
emit(event{"type": "error", "message": string(msg)}) emit(event{"type": "error", "message": string(msg)})
case t == "input_audio_buffer.committed": case t == "input_audio_buffer.committed":
// The commit acknowledges Stop; the words for the audio before it can still follow (rega: a // The commit acknowledges Stop; the words for the audio before it can still follow (measured: a
// Stop mid-word lost "what your country…"). Keep reading until the final text or 6 s of quiet (a loaded server can take seconds per backlog chunk). // Stop mid-word lost "what your country…"). Keep reading until the final text or 6 s of quiet (a loaded server can take seconds per backlog chunk).
committed = true committed = true
e.logs.add("speech server committed the stream; reading its last words") e.logs.add("speech server committed the stream; reading its last words")

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="512" width="840" height="1208" fill="var(--sea)" <rect x="330" y="512" width="840" height="1208" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="489" width="840" height="1231" fill="var(--sea)" <rect x="330" y="489" width="840" height="1231" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="627" width="840" height="1093" fill="var(--sea)" <rect x="330" y="627" width="840" height="1093" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -194,7 +194,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -214,8 +214,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -286,15 +286,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="351" width="840" height="1333" fill="var(--sea)" <rect x="330" y="351" width="840" height="1333" fill="var(--sea)"
@ -318,7 +316,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -342,8 +340,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1925" x2="1474" y2="1925" class="rule"/> <line x1="26" y1="1925" x2="1474" y2="1925" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="512" width="840" height="1208" fill="var(--sea)" <rect x="330" y="512" width="840" height="1208" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="443" width="840" height="1277" fill="var(--sea)" <rect x="330" y="443" width="840" height="1277" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -184,7 +184,7 @@
.hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em} .hseries{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:14px;fill:#7E7562;letter-spacing:.01em}
.hseries-em{font-weight:700;fill:#6B4A78} .hseries-em{font-weight:700;fill:#6B4A78}
.hseries-dim{fill:#A79E8A} .hseries-dim{fill:#A79E8A}
/* The site reference on its OWN line — Brief: "so that it is less wide". A /* The site reference on its OWN line, so the series block is less wide. A
single long line was forcing the series block wider than the map. */ single long line was forcing the series block wider than the map. */
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px; .hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
fill:#A79E8A;letter-spacing:.02em} fill:#A79E8A;letter-spacing:.02em}
@ -204,8 +204,8 @@
the map's width (~1100px); the band is now 1388px, so the type follows. the map's width (~1100px); the band is now 1388px, so the type follows.
15px measures ~1263px — fills the band while keeping headroom over the width 15px measures ~1263px — fills the band while keeping headroom over the width
estimator's error rather than sitting one glyph from overflow. */ estimator's error rather than sitting one glyph from overflow. */
/* 13.5px bold. Brief: "this is too large, it should not span full width — /* 13.5px bold. At full width it was too large: the line needs gaps at both
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band, ends. At 15px bold it measured 1396px of the 1448px band,
i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at i.e. touching both rules; 13.5 measures ~1256px and leaves ~96px of paper at
each end, so the line reads as SET INSIDE the band rather than filling it. each end, so the line reads as SET INSIDE the band rather than filling it.
Bold + dark ink still carry the weight. */ Bold + dark ink still carry the weight. */
@ -276,15 +276,13 @@
<feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/> <feDropShadow dx="0" dy="3" stdDeviation="5" flood-color="#2A2721" flood-opacity="0.34"/>
</filter> </filter>
</defs> </defs>
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating.
like it is floating."
A soft drop shadow under a crisp 1.5px border lifts the map off the paper, A soft drop shadow under a crisp 1.5px border lifts the map off the paper,
so it reads as an object ON the sheet rather than a window cut INTO it — so it reads as an object ON the sheet rather than a window cut INTO it —
which is also what makes the surrounding text feel like a mount around it. --> which is also what makes the surrounding text feel like a mount around it. -->
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line <!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
and a shadow around the outside of that line that makes the map itself look that line, makes the map look like it is floating. The white mat was tried and rejected — the shadow-only
like it is floating." The white mat was tried and rejected — the shadow-only
border reads better, and the mat also cut every leader line with a band of border reads better, and the mat also cut every leader line with a band of
white where the lines crossed it. --> white where the lines crossed it. -->
<rect x="330" y="466" width="840" height="1254" fill="var(--sea)" <rect x="330" y="466" width="840" height="1254" fill="var(--sea)"
@ -308,7 +306,7 @@
down to where the region name stops being the loudest thing on the poster. down to where the region name stops being the loudest thing on the poster.
Splitting it lets the CATEGORY sit quietly above and the REGION stay big: Splitting it lets the CATEGORY sit quietly above and the REGION stay big:
the region is what a passer-by uses to find their sheet on a wall of 30. --> the region is what a passer-by uses to find their sheet on a wall of 30. -->
<!-- TITLE, two-tone. Brief: the title reads "Unengaged of <Region>", with <!-- TITLE, two-tone. The title reads "Unengaged of <Region>", with
"Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by "Unengaged of" in a DIFFERENT COLOUR from the region name — so a passer-by
reads at a glance that it is these PEOPLE who are unengaged, while the reads at a glance that it is these PEOPLE who are unengaged, while the
region stays the loudest thing on the sheet. The overline above it is now region stays the loudest thing on the sheet. The overline above it is now
@ -332,8 +330,8 @@
shadow now define that edge far more clearly, so the rule just floated as a shadow now define that edge far more clearly, so the rule just floated as a
stray horizontal line above the map.) --> stray horizontal line above the map.) -->
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/> <line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two <!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
lines but one line with scripture reference on the right." It is one of the reference on the right. It is one of the
three things that must read when a reader steps closer, alongside the title three things that must read when a reader steps closer, alongside the title
and the people-group names, so it carries display weight. The reference is and the people-group names, so it carries display weight. The reference is
right-aligned to the map's own edge, which is what keeps it on ONE line. --> right-aligned to the map's own edge, which is what keeps it on ONE line. -->

View file

@ -194,7 +194,7 @@ export function backendDevPlugin(root, env = process.env) {
name: 'dx-backend-dev', name: 'dx-backend-dev',
apply: 'serve', apply: 'serve',
configureServer(server) { configureServer(server) {
// ENGINE NOT RUNNING, QUIETLY (dx-shepherd, R500): with no backend process the proxy answered // ENGINE NOT RUNNING, QUIETLY (R500): with no backend process the proxy answered
// 500 ECONNREFUSED, which every page showed as console errors. While a backend is down its routes get // 500 ECONNREFUSED, which every page showed as console errors. While a backend is down its routes get
// well-formed answers instead: /events streams ONE status event saying the engine is offline (the // well-formed answers instead: /events streams ONE status event saying the engine is offline (the
// plugin renders its own "engine not running" state from it), anything else returns an empty JSON // plugin renders its own "engine not running" state from it), anything else returns an empty JSON

View file

@ -168,7 +168,7 @@ done
# Everything busy: keep POLLING EVERY SLOT until the deadline, and report a timeout as UNMEASURED # Everything busy: keep POLLING EVERY SLOT until the deadline, and report a timeout as UNMEASURED
# rather than as a verdict. This used to queue on slot 1 alone (flock -w on slot 1), so a slot that # rather than as a verdict. This used to queue on slot 1 alone (flock -w on slot 1), so a slot that
# freed up was never taken: measured 2026-09-25 (dx-shepherd), ten waiters were piled on slot 1 while # freed up was never taken: measured 2026-09-25, ten waiters were piled on slot 1 while
# slot 2 sat free, and with slot 1 held 12 s and slot 2 freed at ~3 s a caller waited 11.5 s — the # slot 2 sat free, and with slot 1 held 12 s and slot 2 freed at ~3 s a caller waited 11.5 s — the
# two-slot semaphore had collapsed into ONE queue. Polling takes the first slot that frees (~4 s in # two-slot semaphore had collapsed into ONE queue. Polling takes the first slot that frees (~4 s in
# the same test). Trade-off, accepted: polling is not first-come-first-served, so under heavy # the same test). Trade-off, accepted: polling is not first-come-first-served, so under heavy

View file

@ -30,7 +30,7 @@ check('a GeoJSON feature is matched to rows on the same point', colocatedWith(id
check('a lone point shares with nobody', colocatedWith(idx, rows[3]).length === 0, colocatedWith(idx, rows[3])) check('a lone point shares with nobody', colocatedWith(idx, rows[3]).length === 0, colocatedWith(idx, rows[3]))
check('a row without a position is ignored', colocatedWith(idx, rows[4]).length === 0, colocatedWith(idx, rows[4])) check('a row without a position is ignored', colocatedWith(idx, rows[4]).length === 0, colocatedWith(idx, rows[4]))
// A click hands over the RENDERED feature: the engine rebuilt its geometry from tile coordinates, so it is // A click hands over the RENDERED feature: the engine rebuilt its geometry from tile coordinates, so it is
// near the roster point but not equal to it: ~0.001 degree at low zoom (rega, row 46, a shared point that never matched). // near the roster point but not equal to it: ~0.001 degree at low zoom (a browser check, row 46: a shared point that never matched).
const rendered = { type: 'Feature', properties: { name: 'Koraga' }, geometry: { type: 'Point', coordinates: [75.2486, 12.8431] } } const rendered = { type: 'Feature', properties: { name: 'Koraga' }, geometry: { type: 'Point', coordinates: [75.2486, 12.8431] } }
check('a rendered (tile-rounded) feature still finds its co-located groups', colocatedWith(idx, rendered).sort().join() === 'Godda,Maila', colocatedWith(idx, rendered)) check('a rendered (tile-rounded) feature still finds its co-located groups', colocatedWith(idx, rendered).sort().join() === 'Godda,Maila', colocatedWith(idx, rendered))
// The same people in two countries: the nearest roster row wins, not the first. // The same people in two countries: the nearest roster row wins, not the first.
@ -39,7 +39,7 @@ const renderedFar = { type: 'Feature', properties: { name: 'Koraga' }, geometry:
check('a repeated name resolves to the nearest roster point', colocatedWith(twin, renderedFar).join() === 'Other', colocatedWith(twin, renderedFar)) check('a repeated name resolves to the nearest roster point', colocatedWith(twin, renderedFar).join() === 'Other', colocatedWith(twin, renderedFar))
// The rows a map DRAWS are normalized people groups (name + latitude/longitude, no geometry), and every // The rows a map DRAWS are normalized people groups (name + latitude/longitude, no geometry), and every
// map built on useMapData must offer them to its legends: the prayer map did not, so its legends read the // map built on useMapData must offer them to its legends: the prayer map did not, so its legends read the
// raw API cache and neither the shared-point sentence nor the standing note appeared (rega, row 46 at 835ec1ac). // raw API cache and neither the shared-point sentence nor the standing note appeared (a browser check, row 46 at 835ec1ac).
const drawn = [{ name: 'A', latitude: 12.85, longitude: 75.25 }, { name: 'B', latitude: '12.85', longitude: '75.25' }] const drawn = [{ name: 'A', latitude: 12.85, longitude: 75.25 }, { name: 'B', latitude: '12.85', longitude: '75.25' }]
const clickedA = { type: 'Feature', properties: { name: 'A' }, geometry: { type: 'Point', coordinates: [75.2504, 12.8497] } } const clickedA = { type: 'Feature', properties: { name: 'A' }, geometry: { type: 'Point', coordinates: [75.2504, 12.8497] } }
check('normalized rows (no geometry) index and match a clicked pin', colocatedWith(buildColocationIndex(drawn), clickedA).join() === 'B', colocatedWith(buildColocationIndex(drawn), clickedA)) check('normalized rows (no geometry) index and match a clicked pin', colocatedWith(buildColocationIndex(drawn), clickedA).join() === 'B', colocatedWith(buildColocationIndex(drawn), clickedA))

View file

@ -111,8 +111,8 @@ if (!targets.length) {
// The token to hand the partner page is the one the tree already bakes into its own leaves: this // The token to hand the partner page is the one the tree already bakes into its own leaves: this
// gate must not invent a key, and a token that is not this tree's would 401 and look like our bug. // gate must not invent a key, and a token that is not this tree's would 401 and look like our bug.
// The tree bakes no token since the auto engine (the repo ships none), so every tk case SKIPPED and every // The tree bakes no token since the auto engine (the repo ships none), so every tk case SKIPPED and every
// provider case went UNMEASURED: a hollow PASS (rega, ded4b3c1). A seat that has a real public token passes // provider case went UNMEASURED: a hollow PASS (measured at ded4b3c1). A seat that has a real public token passes
// it as TEST_MAPBOX_TOKEN (as rega's checks do); otherwise a baked one is still used if a tree has it. // it as TEST_MAPBOX_TOKEN (as the browser checks do); otherwise a baked one is still used if a tree has it.
const bakedToken = process.env.TEST_MAPBOX_TOKEN || (() => { const bakedToken = process.env.TEST_MAPBOX_TOKEN || (() => {
for (const b of manifest.bundles || []) { for (const b of manifest.bundles || []) {
for (const p of b.profiles || []) { for (const p of b.profiles || []) {
@ -170,10 +170,10 @@ try {
lines.push(` · ${t.element} [${c.knob}] SKIPPED — no baked token in this tree to hand the host`) lines.push(` · ${t.element} [${c.knob}] SKIPPED — no baked token in this tree to hand the host`)
continue continue
} }
// The composite's OWN profile rides with the knob under test (rega, 1a761741: gospel-resources, // The composite's OWN profile rides with the knob under test (measured at 1a761741: gospel-resources,
// my-upg-100-list, regions-dashboard and two more drew no map under a knob-only config, because a // my-upg-100-list, regions-dashboard and two more drew no map under a knob-only config, because a
// multi-profile element needs to be told which profile is the composite). A knob still wins. // multi-profile element needs to be told which profile is the composite). A knob still wins.
// A composite that opens on a non-map view must be told to show its map (exp-shepherd: country-posters-mfe // A composite that opens on a non-map view must be told to show its map (country-posters-mfe
// reads profile-config `view`), or there is no canvas to measure. // reads profile-config `view`), or there is no canvas to measure.
const VIEW = { 'country-posters-mfe': { view: 'map' } } const VIEW = { 'country-posters-mfe': { view: 'map' } }
const cfg = JSON.stringify({ profile: t.profile, ...(VIEW[t.element] || {}), ...c.config(bakedToken) }).replace(/'/g, '&#39;') const cfg = JSON.stringify({ profile: t.profile, ...(VIEW[t.element] || {}), ...c.config(bakedToken) }).replace(/'/g, '&#39;')

View file

@ -11,7 +11,7 @@
* *
* ★ WHY IT NOW REBUILDS AND COMPARES BYTES, NOT MTIMES (2026-09-25). The first version compared * ★ WHY IT NOW REBUILDS AND COMPARES BYTES, NOT MTIMES (2026-09-25). The first version compared
* the newest child SOURCE mtime with the parent BUNDLE mtime. File times are not build facts: * the newest child SOURCE mtime with the parent BUNDLE mtime. File times are not build facts:
* a git merge or checkout rewrites sources with checkout-time mtimes, so rega's publish of * a git merge or checkout rewrites sources with checkout-time mtimes, so a publish of
* beb13da3 stopped on "doxa-maps-spa / plugin-studio / stack-demo STALE vs gospel-resources / * beb13da3 stopped on "doxa-maps-spa / plugin-studio / stack-demo STALE vs gospel-resources /
* research-dashboard" although a rebuild reproduced the committed bytes exactly — and two of * research-dashboard" although a rebuild reproduced the committed bytes exactly — and two of
* those parents mount their children BY TAG at runtime, so they compile none of the child in * those parents mount their children BY TAG at runtime, so they compile none of the child in
@ -61,7 +61,7 @@ const TREE = join(ROOT, 'public', TREE_NAME)
const argv = process.argv.slice(2) const argv = process.argv.slice(2)
const oi = argv.indexOf('--only') const oi = argv.indexOf('--only')
const ONLY = oi >= 0 && argv[oi + 1] ? new Set(argv[oi + 1].split(',')) : null const ONLY = oi >= 0 && argv[oi + 1] ? new Set(argv[oi + 1].split(',')) : null
// NOT ONLY COMPOSITES (rega, 2026-09-25): a library change merged into main reached prayer-walk-planner, // NOT ONLY COMPOSITES (measured 2026-09-25): a library change merged into main reached prayer-walk-planner,
// which composes nothing, so this gate could not see it was stale. --all checks EVERY bundle; --bundles a,b // which composes nothing, so this gate could not see it was stale. --all checks EVERY bundle; --bundles a,b
// checks those (the landing precondition passes the ones a landing can have changed). // checks those (the landing precondition passes the ones a landing can have changed).
const ALL = argv.includes('--all') const ALL = argv.includes('--all')

View file

@ -26,7 +26,7 @@
* *
* NOT A SUBSTITUTE FOR A BROWSER. Both ends existing is necessary, not sufficient: UPG-100's * NOT A SUBSTITUTE FOR A BROWSER. Both ends existing is necessary, not sufficient: UPG-100's
* select-zone listener exists and still loses the first click, because the lazy Map pane attaches it * select-zone listener exists and still loses the first click, because the lazy Map pane attaches it
* too late (rega, 2026-09-26). The runtime half is check:gallery-embed's `linked` phase. * too late (measured 2026-09-26). The runtime half is check:gallery-embed's `linked` phase.
* *
* NEGATIVE-TESTED (2026-09-26, main 521f8b18, PASS with 17 links and the 3 LATE_KNOWN sites), each FAILS: * NEGATIVE-TESTED (2026-09-26, main 521f8b18, PASS with 17 links and the 3 LATE_KNOWN sites), each FAILS:
* a receiver renamed in source ('map-filtered' -> 'map-filter' in research-map-plus-grid.vue); * a receiver renamed in source ('map-filtered' -> 'map-filter' in research-map-plus-grid.vue);

View file

@ -19,7 +19,7 @@
* `tk` to a baked-in child, at production stage. See the comment at the scan below. * `tk` to a baked-in child, at production stage. See the comment at the scan below.
* *
* Source-level and headless BY DESIGN: the behavioural twin (check:composite-embed-knobs) needs a * Source-level and headless BY DESIGN: the behavioural twin (check:composite-embed-knobs) needs a
* browser and only rega may run one. Exit 0 pass · 1 fail. * browser, and this gate runs without one. Exit 0 pass · 1 fail.
*/ */
import { readFileSync, existsSync } from 'node:fs' import { readFileSync, existsSync } from 'node:fs'
import { resolve, dirname, join } from 'node:path' import { resolve, dirname, join } from 'node:path'

View file

@ -10,7 +10,7 @@
* and Simple Research, both in the Research Gallery). Every structural gate passed. This gate runs * and Simple Research, both in the Research Gallery). Every structural gate passed. This gate runs
* the thing that failed: the composite's own setup, and its mounted hooks. * the thing that failed: the composite's own setup, and its mounted hooks.
* *
* HOW. No browser (fleet policy: only rega launches one). Vite loads each composite .vue through * HOW. No browser (by design: this gate runs headless). Vite loads each composite .vue through
* ssrLoadModule with the repo's alias and the same vue({ customElement: true }) plugin, and * ssrLoadModule with the repo's alias and the same vue({ customElement: true }) plugin, and
* @vue/runtime-core mounts it on a no-op renderer. The SSR compile gives the component an * @vue/runtime-core mounts it on a no-op renderer. The SSR compile gives the component an
* ssrRender rather than a render, so the renderer draws nothing and no CHILD is set up: this * ssrRender rather than a render, so the renderer draws nothing and no CHILD is set up: this

View file

@ -12,7 +12,7 @@
* *
* Nothing throws, because the t() shim falls back to its key. So it reads as "a few strings not * Nothing throws, because the t() shim falls back to its key. So it reads as "a few strings not
* translated yet" and it survived in country-posters, key-cities, gospel-resources, cities-internet and * translated yet" and it survived in country-posters, key-cities, gospel-resources, cities-internet and
* my-upg-100-list simultaneously — confirmed independently by regions-plugin, who censused all 19 entry * my-upg-100-list simultaneously — confirmed independently by a census of all 19 entry
* files and found the same five and no sixth. * files and found the same five and no sixth.
* *
* ★ AND A CENSUS THAT COUNTED FUNCTION BODIES SAW FOUR OF SIXTEEN. That is why this gate greps for the * ★ AND A CENSUS THAT COUNTED FUNCTION BODIES SAW FOUR OF SIXTEEN. That is why this gate greps for the

View file

@ -4,7 +4,7 @@
* *
* THE DEFECT (2026-09-28, found testing live): the link copied from Simple Research was * THE DEFECT (2026-09-28, found testing live): the link copied from Simple Research was
* `research-map-plus-grid/research-map-plus-grid/index.html` while the page was * `research-map-plus-grid/research-map-plus-grid/index.html` while the page was
* `http://127.0.0.1:5192/#f=…` — "the perfect example of how embeddability is not working". * `http://127.0.0.1:5192/#f=…`, a clear case of an embed that could not work.
* *
* WHY IT SURVIVED SO LONG, and it is the reason this is a gate rather than a fix: a relative share link * WHY IT SURVIVED SO LONG, and it is the reason this is a gate rather than a fix: a relative share link
* WORKS WHEN CLICKED. The browser resolves it against the page you copied it from, so the person testing * WORKS WHEN CLICKED. The browser resolves it against the page you copied it from, so the person testing

View file

@ -196,7 +196,7 @@ const run = async () => {
const f = __srcFeatures(m, 'rd-bubbles'); return !!(f && f.length > 0); const f = __srcFeatures(m, 'rd-bubbles'); return !!(f && f.length > 0);
})()`, { timeout: 90000 }) })()`, { timeout: 90000 })
} catch (e) { } catch (e) {
// ★ DISTINGUISH "I CANNOT MEASURE THIS ENGINE" FROM "THE PRODUCT IS BROKEN". // ★ DISTINGUISH "THIS ENGINE CANNOT BE MEASURED HERE" FROM "THE PRODUCT IS BROKEN".
// This gate reads the source's CURRENT GeoJSON off `_data`, a private Mapbox internal. MEASURED // This gate reads the source's CURRENT GeoJSON off `_data`, a private Mapbox internal. MEASURED
// on one dashboard, one build, only the engine differing, by calling the app's own setData() to // on one dashboard, one build, only the engine differing, by calling the app's own setData() to
// shrink 2107 features to 5: // shrink 2107 features to 5:

View file

@ -38,7 +38,7 @@ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'
import { join, relative, resolve, dirname } from 'node:path' import { join, relative, resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url' import { fileURLToPath } from 'node:url'
import { TREE_NAME } from './_tree-name.mjs' import { TREE_NAME } from './_tree-name.mjs'
// ★ SHARED WITH check:hardcoded-english (regions-plugin). Two gates must agree about WHICH files are // ★ SHARED WITH check:hardcoded-english (the same file list). Two gates must agree about WHICH files are
// in scope even when they disagree about what they find in them; a second copy would drift on exactly // in scope even when they disagree about what they find in them; a second copy would drift on exactly
// the pair-form / transitive / cycle details below, and the drift would be silent. // the pair-form / transitive / cycle details below, and the drift would be silent.
import { effectiveStages, stageReason } from './lib/effective-stage.mjs' import { effectiveStages, stageReason } from './lib/effective-stage.mjs'
@ -73,7 +73,7 @@ function findFrozen(ast, aliases) {
if (name && (TRANSLATORS.has(name) || aliases.has(name))) { if (name && (TRANSLATORS.has(name) || aliases.has(name))) {
// tStatic(path, EXPLICIT_LOCALE) — the caller pinned it deliberately. // tStatic(path, EXPLICIT_LOCALE) — the caller pinned it deliberately.
const pinned = name === 'tStatic' && node.arguments.length > 1 const pinned = name === 'tStatic' && node.arguments.length > 1
// ★ tStatic IS NEVER LIVE, WHEREVER IT SITS (exp, 2026-09-28). It resolves the locale via // ★ tStatic IS NEVER LIVE, WHEREVER IT SITS (2026-09-28). It resolves the locale via
// getRuntimeLocale(), which reads a PLAIN window property — not a Vue ref — so a computed() // getRuntimeLocale(), which reads a PLAIN window property — not a Vue ref — so a computed()
// wrapping it has NO reactive dependency and never invalidates. computed(() => tStatic(x)) // wrapping it has NO reactive dependency and never invalidates. computed(() => tStatic(x))
// is strictly WORSE than the naked call: it looks like the fix, so nobody looks again. // is strictly WORSE than the naked call: it looks like the fix, so nobody looks again.
@ -153,7 +153,7 @@ const DEAD_LOCALE_GETTER = /\b(getRuntimeLocale|detectLocale|localeFromUrl)\s*\(
/** /**
* Is offset `at` inside the parentheses of a computed()/watch()/watchEffect() that tracks the locale? * Is offset `at` inside the parentheses of a computed()/watch()/watchEffect() that tracks the locale?
* *
* ⚠ BRACE DEPTH, NOT A LOOKBACK WINDOW (gospel, GOS-FROZEN-SR). The first version of this asked * ⚠ BRACE DEPTH, NOT A LOOKBACK WINDOW (GOS-FROZEN-SR). The first version of this asked
* whether a wrapper appeared in the preceding 260 characters. gospel hit exactly this in GiDrawer.vue * whether a wrapper appeared in the preceding 260 characters. gospel hit exactly this in GiDrawer.vue
* — the enclosing `computed(` opens at line 35 and the call sites are at 95-100 — and reported SIX * — the enclosing `computed(` opens at line 35 and the call sites are at 95-100 — and reported SIX
* false positives on working code, nearly "fixing" it. Measured on the same shape here: 2,600 * false positives on working code, nearly "fixing" it. Measured on the same shape here: 2,600
@ -206,7 +206,7 @@ function findStoredTreeFreeze(code) {
// function RETURNS A STRUCTURE whose values come from the translator — an options object or a // function RETURNS A STRUCTURE whose values come from the translator — an options object or a
// node list — which a caller then stores. So require the translator call to sit inside a // node list — which a caller then stores. So require the translator call to sit inside a
// returned object/array literal, not merely somewhere in the body. // returned object/array literal, not merely somewhere in the body.
// ★ THE MECHANISM IS "BUILT CORRECTLY, THEN PARKED", not "built once at setup" (gospel, on // ★ THE MECHANISM IS "BUILT CORRECTLY, THEN PARKED", not "built once at setup" (found on
// upg100's legendOpts). That function re-reads t() correctly on EVERY call — its own comment // upg100's legendOpts). That function re-reads t() correctly on EVERY call — its own comment
// says the function shape is deliberate — and the freeze is one level out: the RESULT is // says the function shape is deliberate — and the freeze is one level out: the RESULT is
// assigned into a ref and nothing re-fills it. A rule that only looked for a translator outside // assigned into a ref and nothing re-fills it. A rule that only looked for a translator outside
@ -282,7 +282,7 @@ function findDeadGetterComputed(code) {
} }
/** /**
* SHAPE 3 — labels evaluated inside a provide() literal (gospel, GOS-FROZEN-SR; real in GiCombos.vue). * SHAPE 3 — labels evaluated inside a provide() literal (GOS-FROZEN-SR; real in GiCombos.vue).
* *
* provide('dashboardConfig', { facets: { access_level: { label: T('…') } } }) * provide('dashboardConfig', { facets: { access_level: { label: T('…') } } })
* *
@ -388,7 +388,7 @@ const CASES = [
['GOOD inside an arrow', () => scanSource("const { t } = useI18n()\nconst f = () => t('a.b')").length, 0], ['GOOD inside an arrow', () => scanSource("const { t } = useI18n()\nconst f = () => t('a.b')").length, 0],
['GOOD tStatic with explicit locale', () => scanSource("const x = tStatic('a.b', 'en')").length, 0], ['GOOD tStatic with explicit locale', () => scanSource("const x = tStatic('a.b', 'en')").length, 0],
['GOOD watch(() => t())', () => scanSource("const { t } = useI18n()\nwatch(src, () => t('a.b'))").length, 0], ['GOOD watch(() => t())', () => scanSource("const { t } = useI18n()\nwatch(src, () => t('a.b'))").length, 0],
// ★ exp's false negative: a computed cannot make tStatic live. // ★ A known false negative: a computed cannot make tStatic live.
['BAD computed(() => tStatic()) is STILL frozen', () => scanSource("const X = computed(() => tStatic('a.b'))").length, 1], ['BAD computed(() => tStatic()) is STILL frozen', () => scanSource("const X = computed(() => tStatic('a.b'))").length, 1],
// A function BODY re-reads the global on every call (useOptionalT:74 returns such an arrow), // A function BODY re-reads the global on every call (useOptionalT:74 returns such an arrow),
// so it is NOT frozen. Only a reactive wrapper caches it. I had this case backwards at first. // so it is NOT frozen. Only a reactive wrapper caches it. I had this case backwards at first.

View file

@ -171,7 +171,7 @@ try {
the data was right and the feature was dead. */ the data was right and the feature was dead. */
/* ⚠ THE NAME, CASE-FOLDED, ANYWHERE IN THE CARD. This asserted `text.includes(slug.split('-')[0])` /* ⚠ THE NAME, CASE-FOLDED, ANYWHERE IN THE CARD. This asserted `text.includes(slug.split('-')[0])`
— "abai" against a rendered "Abai Sungai" — and reported a false negative on a card that was — "abai" against a rendered "Abai Sungai" — and reported a false negative on a card that was
rendering correctly, which cost rega a round. A slug fragment is not the name; the roster has rendering correctly, which cost a review round. A slug fragment is not the name; the roster has
the name, so the roster is what it compares. */ the name, so the roster is what it compares. */
const anyRow = (JSON.parse(readFileSync(resolve(PUBLIC, TREE_NAME, 'assets/tables/doxa-people-groups.json'), 'utf8')).rows || []) const anyRow = (JSON.parse(readFileSync(resolve(PUBLIC, TREE_NAME, 'assets/tables/doxa-people-groups.json'), 'utf8')).rows || [])
.find((r) => r.slug && r.mapped) .find((r) => r.slug && r.mapped)

View file

@ -2,7 +2,7 @@
* check-gospel-engine-contract.mjs — the map must use the engine it was ASKED for, or say nothing * check-gospel-engine-contract.mjs — the map must use the engine it was ASKED for, or say nothing
* at all. Never a different one. * at all. Never a different one.
* *
* THE DEFECT THIS IS WRITTEN AGAINST (regions' C3 read + upg100's addendum, 2026-09-26). * THE DEFECT THIS IS WRITTEN AGAINST (the C3 review and its addendum, 2026-09-26).
* resources-map resolved its engine as `engineGlobal(provider) || window.mapboxgl`. engineGlobal * resources-map resolved its engine as `engineGlobal(provider) || window.mapboxgl`. engineGlobal
* already defaults to mapbox, so that fallback could only fire for provider 'maplibre' — and then * already defaults to mapbox, so that fallback could only fire for provider 'maplibre' — and then
* it returned MAPBOX. A page that chose the keyless engine silently got the one that needs a * it returned MAPBOX. A page that chose the keyless engine silently got the one that needs a

View file

@ -1,7 +1,7 @@
/** /**
* check-gospel-english-leaks.mjs — a translated key must not render in English. * check-gospel-english-leaks.mjs — a translated key must not render in English.
* *
* THE METHOD, from rega by way of dx-shepherd. A string is a LEAK when the catalogue has a * THE METHOD, taken from the browser review. A string is a LEAK when the catalogue has a
* DIFFERENT value for the locale and the page still shows the English one. That is a render * DIFFERENT value for the locale and the page still shows the English one. That is a render
* or wiring fault, and it is a different thing from an untranslated string, which is a * or wiring fault, and it is a different thing from an untranslated string, which is a
* catalogue gap. Only the first is this check's business. * catalogue gap. Only the first is this check's business.
@ -75,7 +75,7 @@ try {
/* ═══ WHOLE LABELS, NOT SUBSTRINGS — the third correction, and the last ═══════════ /* ═══ WHOLE LABELS, NOT SUBSTRINGS — the third correction, and the last ═══════════
* Substring matching over the rendered blob reported keys that were never rendered at * Substring matching over the rendered blob reported keys that were never rendered at
* all: "Language" matched inside the DATA — the people-group language "Sign Language" — * all: "Language" matched inside the DATA — the people-group language "Sign Language" —
* and "Region" inside a region name. Those are exactly the data strings rega's method * and "Region" inside a region name. Those are exactly the data strings the method
* excludes, and no amount of length filtering separates a label from a word that * excludes, and no amount of length filtering separates a label from a word that
* happens to sit inside one. * happens to sit inside one.
* So the unit is an ELEMENT'S OWN LABEL: the trimmed text a leaf element renders. A * So the unit is an ELEMENT'S OWN LABEL: the trimmed text a leaf element renders. A

View file

@ -39,7 +39,7 @@ import { fileURLToPath } from 'node:url'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const PORT = Number(process.env.RTL_SHAPE_PORT || 5393) const PORT = Number(process.env.RTL_SHAPE_PORT || 5393)
const POISON = process.argv.includes('--poison') const POISON = process.argv.includes('--poison')
// ⚠ PARAMETERISED, not forked. dx-shepherd routed this gate to the other RAW-map owners // ⚠ PARAMETERISED, not forked. This gate was routed to the other RAW-map owners
// (cities-internet, key-cities, upg-100-list, RegionCountryMap) — copying it would give each of // (cities-internet, key-cities, upg-100-list, RegionCountryMap) — copying it would give each of
// them their own drifting copy of the pan-first logic and the poison arm, which are the two parts // them their own drifting copy of the pan-first logic and the poison arm, which are the two parts
// that took corrections to get right. The leaf is the only thing that differs. // that took corrections to get right. The leaf is the only thing that differs.

View file

@ -6,7 +6,7 @@
* to zoom out past the point where ONE WORLD fills the canvas WIDTH. That puts a hard floor under the * to zoom out past the point where ONE WORLD fills the canvas WIDTH. That puts a hard floor under the
* zoom at log2(canvasWidth/512) — and on a WIDE, SHORT figure that floor is too close for the * zoom at log2(canvasWidth/512) — and on a WIDE, SHORT figure that floor is too close for the
* drawing's own height, so the outermost groups fall outside the frame. minZoom does not help; the * drawing's own height, so the outermost groups fall outside the frame. minZoom does not help; the
* width constraint wins. rega measured it at 1440: 54 groups outside in ar and 97 in en, which is * width constraint wins. A browser check measured it at 1440: 54 groups outside in ar and 97 in en, which is
* what proved it was the WIDTH and never RTL. * what proved it was the WIDTH and never RTL.
* *
* WHAT IT ASSERTS. For every layout the charts actually get, the drawing's vertical extent at the * WHAT IT ASSERTS. For every layout the charts actually get, the drawing's vertical extent at the
@ -45,11 +45,11 @@ export function neededHeightPx(extent, fraction, canvasWidth) {
return worldFraction * 512 * Math.pow(2, floorZoom(canvasWidth)) return worldFraction * 512 * Math.pow(2, floorZoom(canvasWidth))
} }
// The canvases the charts are actually given: rega's measured figures at 1440 and 1280, the embed // The canvases the charts are actually given: the measured figures at 1440 and 1280, the embed
// card, and two narrow ones so a phone cannot regress unnoticed. // card, and two narrow ones so a phone cannot regress unnoticed.
const LAYOUTS = [ const LAYOUTS = [
{ name: '1440 board (rega measured)', w: 796, h: 249 }, { name: '1440 board (measured)', w: 796, h: 249 },
{ name: '1280 board (rega measured)', w: 636, h: 220 }, { name: '1280 board (measured)', w: 636, h: 220 },
{ name: 'embed card', w: 1198, h: 560 }, { name: 'embed card', w: 1198, h: 560 },
{ name: 'tablet', w: 380, h: 300 }, { name: 'tablet', w: 380, h: 300 },
{ name: 'phone', w: 340, h: 240 }, { name: 'phone', w: 340, h: 240 },
@ -77,13 +77,13 @@ for (const chart of CHARTS) {
} }
// ★ NEGATIVE CONTROL. The 0.17 default is the value that was measured FAILING on the page, so if // ★ NEGATIVE CONTROL. The 0.17 default is the value that was measured FAILING on the page, so if
// this arm does not fail the calculation is not measuring what rega measured and a pass means // this arm does not fail the calculation is not measuring what the browser measured and a pass means
// nothing. Asserted on the tightest real layout. // nothing. Asserted on the tightest real layout.
const poison = neededHeightPx(CENTERED_EXTENT, 0.17, 796) const poison = neededHeightPx(CENTERED_EXTENT, 0.17, 796)
ok( ok(
'NEG CONTROL: the old 0.17 fraction still does NOT fit 796x249', 'NEG CONTROL: the old 0.17 fraction still does NOT fit 796x249',
poison > 249 - 2 * FIT_PADDING, poison > 249 - 2 * FIT_PADDING,
`0.17 needs ${poison.toFixed(0)}px of ${249 - 2 * FIT_PADDING}px — the defect rega measured`, `0.17 needs ${poison.toFixed(0)}px of ${249 - 2 * FIT_PADDING}px — the measured defect`,
) )
// The two charts must share one footprint, or the same population reads as a different size when a // The two charts must share one footprint, or the same population reads as a different size when a

View file

@ -2,13 +2,13 @@
/** /**
* check-grid-image-loading — the people-group cards must not fetch 24 photos during boot. * check-grid-image-loading — the people-group cards must not fetch 24 photos during boot.
* *
* THE MEASUREMENT (rega, SR-LOAD on 21ebb4f26): a cold load of Simple Research downloaded 24 * THE MEASUREMENT (SR-LOAD on 21ebb4f26): a cold load of Simple Research downloaded 24
* people-group photos, 5.3 MB, DURING BOOT — competing with the map engine and the API data on the * people-group photos, 5.3 MB, DURING BOOT — competing with the map engine and the API data on the
* same connection. Nothing was wrong with any single image; there was simply no instruction about * same connection. Nothing was wrong with any single image; there was simply no instruction about
* WHEN to fetch them, so the browser fetched them all at once. * WHEN to fetch them, so the browser fetched them all at once.
* *
* ★ WHY A GATE AND NOT JUST THE FIX. This component is being MERGED with the regions list right now * ★ WHY A GATE AND NOT JUST THE FIX. This component is being MERGED with the regions list right now
* (a settled decision, regions-plugin building it), and a merge rewrites markup. Four attributes on * (a settled decision, built by the regions plugin), and a merge rewrites markup. Four attributes on
* one <img> are exactly the kind of thing that survives a review and not a rewrite — and losing them * one <img> are exactly the kind of thing that survives a review and not a rewrite — and losing them
* is invisible: the page still works, it just costs 5.3 MB again on every cold load. * is invisible: the page still works, it just costs 5.3 MB again on every cold load.
* *
@ -36,7 +36,7 @@ if (!m) {
const tag = m[0] const tag = m[0]
if (!/:loading=/.test(tag)) { if (!/:loading=/.test(tag)) {
fails.push('the card image has no loading attribute, so every photo is fetched during boot — ' fails.push('the card image has no loading attribute, so every photo is fetched during boot — '
+ 'rega measured 24 of them, 5.3 MB, competing with the map and the API data.') + 'a cold load measured 24 of them, 5.3 MB, competing with the map and the API data.')
} }
if (!/decoding="async"/.test(tag)) fails.push('the card image does not decode off the main thread (decoding="async").') if (!/decoding="async"/.test(tag)) fails.push('the card image does not decode off the main thread (decoding="async").')
if (!/\bwidth=/.test(tag) || !/\bheight=/.test(tag)) { if (!/\bwidth=/.test(tag) || !/\bheight=/.test(tag)) {

View file

@ -24,8 +24,8 @@
* by whether the lookup it guards actually works. `msg !== 'messages.engineMissing' ? msg : 'English'` * by whether the lookup it guards actually works. `msg !== 'messages.engineMissing' ? msg : 'English'`
* is treated as a fallback — but if the key is looked up with the wrong prefix, the lookup ALWAYS * is treated as a fallback — but if the key is looked up with the wrong prefix, the lookup ALWAYS
* misses and the English arm becomes the NORMAL path, in every locale, while this gate stays quiet. * misses and the English arm becomes the NORMAL path, in every locale, while this gate stays quiet.
* That is exactly what exp-shepherd found in cities-internet after I triaged the same line as "not a * That is exactly what a later review found in cities-internet after the same line had been triaged as
* leak". A hardcoded-English scan cannot see a misconfigured lookup; check:i18n-keys-exist and a * "not a leak". A hardcoded-English scan cannot see a misconfigured lookup; check:i18n-keys-exist and a
* locale render are what catch that. Treat a guarded fallback as "not a leak HERE", never as "this * locale render are what catch that. Treat a guarded fallback as "not a leak HERE", never as "this
* string is not reaching users". * string is not reaching users".
* *
@ -59,7 +59,7 @@ const ALLOW = [
{ why: 'HTML fragments: these plugins build popup markup in template literals, so a quasi is often ' { why: 'HTML fragments: these plugins build popup markup in template literals, so a quasi is often '
+ 'a tag, or the seam between two attributes ("\" alt=\"" / "\" loading=\"lazy\">")', + 'a tag, or the seam between two attributes ("\" alt=\"" / "\" loading=\"lazy\">")',
test: (s) => /<\/?[a-zA-Z][^>]*>?/.test(s) test: (s) => /<\/?[a-zA-Z][^>]*>?/.test(s)
// dir and lang join the list (exp, END-EXP-SR-DIRLANG): `'" dir="'` is the seam between two // dir and lang join the list (SR-DIRLANG): `'" dir="'` is the seam between two
// attributes in a print page assembled by concatenation, and every RTL-aware page writes both. // attributes in a print page assembled by concatenation, and every RTL-aware page writes both.
// They are the same class as class= and href= — attribute NAMES, not text a reader sees. // They are the same class as class= and href= — attribute NAMES, not text a reader sees.
|| /\b(class|href|src|alt|loading|width|height|style|id|target|rel|dir|lang)\s*=/.test(s) || /\b(class|href|src|alt|loading|width|height|style|id|target|rel|dir|lang)\s*=/.test(s)
@ -91,7 +91,7 @@ const ALLOW = [
test: (s) => /^[a-z-]+\($/.test(s) test: (s) => /^[a-z-]+\($/.test(s)
|| /^[,)\s]*[a-z-]+\([^)]*\)[,)\s]*$/.test(s) || /^[,)\s]*[a-z-]+\([^)]*\)[,)\s]*$/.test(s)
|| /^[,)\s]+$/.test(s) }, || /^[,)\s]+$/.test(s) },
// ★ DECLARATION SEAMS, the other half of the same split (exp, I18N-EXP-PLAN). Splitting // ★ DECLARATION SEAMS, the other half of the same split (I18N-EXP-PLAN). Splitting
// `width:${w}px;height:${h}px` leaves the quasi "px;height:" — a seam BETWEEN two declarations, // `width:${w}px;height:${h}px` leaves the quasi "px;height:" — a seam BETWEEN two declarations,
// carrying the tail of one value and the head of the next property. It has a colon but no value // carrying the tail of one value and the head of the next property. It has a colon but no value
// and no brace, so every CSS shape above misses it. exp measured these as false leaks in // and no brace, so every CSS shape above misses it. exp measured these as false leaks in
@ -351,7 +351,7 @@ function isDataComparison(node, parent) {
// resource not found means NOT FOUND in the pools checked" is a sentence that happens to be used as // resource not found means NOT FOUND in the pools checked" is a sentence that happens to be used as
// a key, and that is a real leak — gospel found twelve defects hiding in exactly that kind of // a key, and that is a real leak — gospel found twelve defects hiding in exactly that kind of
// already-classified bucket. Anything with sentence punctuation stays loud whatever its value is. // already-classified bucket. Anything with sentence punctuation stays loud whatever its value is.
// ★ REGION IDENTITIES, EXEMPT ONLY WHILE THEY ARE TRANSLATABLE (studio's judgement, R26). // ★ REGION IDENTITIES, EXEMPT ONLY WHILE THEY ARE TRANSLATABLE (R26).
// legend-tree's REGION_OF maps a zone slug to a region NAME, and that name is an identity: node ids // legend-tree's REGION_OF maps a zone slug to a region NAME, and that name is an identity: node ids
// derive from it, and every surface that shows one translates it at render through REGION_KEY — // derive from it, and every surface that shows one translates it at render through REGION_KEY —
// upg-100-list.vue:440 `REGION_KEY[english] ? t(\`poster.${…}\`) : english`, poster-wall.vue:129 the // upg-100-list.vue:440 `REGION_KEY[english] ? t(\`poster.${…}\`) : english`, poster-wall.vue:129 the
@ -1009,7 +1009,7 @@ const RENDERED = { africa: 'A sentence the reader actually sees here', asia: 'An
// names, values that are prose a reader sees: the keys must STILL count, because this is a // names, values that are prose a reader sees: the keys must STILL count, because this is a
// label table, not a join table. Without this case the allData guard can be removed and every // label table, not a join table. Without this case the allData guard can be removed and every
// test still passes — measured by poisoning it. // test still passes — measured by poisoning it.
// ★ REGION IDENTITIES (studio, R26): exempt ONLY while REGION_KEY names them. A region added // ★ REGION IDENTITIES (R26): exempt ONLY while REGION_KEY names them. A region added
// without a key falls through `|| english` and renders English in eleven locales — the defect // without a key falls through `|| english` and renders English in eleven locales — the defect
// upg100 already shipped once with Americas and Europe. The conditional IS the rule. // upg100 already shipped once with Americas and Europe. The conditional IS the rule.
{ {
@ -1068,7 +1068,7 @@ const a = greeting + 'Welcome to the prayer map'
</script>` </script>`
ok(scanSfc(CAT, 'concat.vue').some((f) => f.text === 'Welcome to the prayer map'), ok(scanSfc(CAT, 'concat.vue').some((f) => f.text === 'Welcome to the prayer map'),
'COUNTS a sentence built by CONCATENATION — only an equality test means "compared against data"') 'COUNTS a sentence built by CONCATENATION — only an equality test means "compared against data"')
// ★ exp's CSS DECLARATION SEAMS (I18N-EXP-PLAN), tested on the SHAPE rather than through a fixture. // ★ CSS DECLARATION SEAMS (I18N-EXP-PLAN), tested on the SHAPE rather than through a fixture.
// A seam like "px;height:" only exists as a separate string if a quasi is judged alone, and this // A seam like "px;height:" only exists as a separate string if a quasi is judged alone, and this
// gate deliberately judges a split template literal as ONE string — so a .vue fixture can never // gate deliberately judges a split template literal as ONE string — so a .vue fixture can never
// produce one, and an assertion written that way passes whether or not the rule exists. I wrote // produce one, and an assertion written that way passes whether or not the rule exists. I wrote
@ -1081,7 +1081,7 @@ const a = greeting + 'Welcome to the prayer map'
'prose ending in a colon is NOT — the seam shape needs a semicolon-separated declaration') 'prose ending in a colon is NOT — the seam shape needs a semicolon-separated declaration')
ok(!allowedBy('Pools checked; nothing found:'), ok(!allowedBy('Pools checked; nothing found:'),
'prose with a semicolon AND a colon is NOT — the segments must be property:value pairs') 'prose with a semicolon AND a colon is NOT — the segments must be property:value pairs')
// ★ exp's HTML ATTRIBUTE SEAM (END-EXP-SR-DIRLANG). `'<html lang="' + l + '" dir="' + d + '">'` // ★ HTML ATTRIBUTE SEAM (SR-DIRLANG). `'<html lang="' + l + '" dir="' + d + '">'`
// leaves the quasi `" dir="`, an attribute seam rather than a declaration seam. dir and lang were // leaves the quasi `" dir="`, an attribute seam rather than a declaration seam. dir and lang were
// the only standard attributes missing from the HTML shape. Asserted on the shape for the same // the only standard attributes missing from the HTML shape. Asserted on the shape for the same
// reason as the CSS seam: the gate judges a split literal as ONE string, so no fixture reaches it. // reason as the CSS seam: the gate judges a split literal as ONE string, so no fixture reaches it.

View file

@ -1,7 +1,7 @@
/** /**
* check-instance-config-keys.mjs — a preset's profile-config keys must be keys the profile READS. * check-instance-config-keys.mjs — a preset's profile-config keys must be keys the profile READS.
* *
* ★ THE DEFECT (rega RQ20): people-groups-grid's "driven" preset declares "show-search":"false" and * ★ THE DEFECT (RQ20): people-groups-grid's "driven" preset declares "show-search":"false" and
* "show-filters":"false", and the page renders the search box and the filters bar anyway. Nothing * "show-filters":"false", and the page renders the search box and the filters bar anyway. Nothing
* errored — 0 console errors — because nothing was wrong at runtime. The key simply never reached * errored — 0 console errors — because nothing was wrong at runtime. The key simply never reached
* the component: * the component:

View file

@ -2,7 +2,7 @@
/** /**
* check-leaf-engine-per-provider — a leaf loads the engine its PROVIDER asks for, and no other. * check-leaf-engine-per-provider — a leaf loads the engine its PROVIDER asks for, and no other.
* *
* THE BUG (card BUG-LEAF-PAGES-PRELOAD-MAPBOX-EVEN-UNDER-MAPLIBRE; measured by exp-shepherd at * THE BUG (card BUG-LEAF-PAGES-PRELOAD-MAPBOX-EVEN-UNDER-MAPLIBRE; measured at
* 577149af). generate-tree chose a leaf's engine peer at BUILD time, from the instance config, and * 577149af). generate-tree chose a leaf's engine peer at BUILD time, from the instance config, and
* baked it into <head> as static tags: mapbox-gl.css, the geocoder css, mapbox-gl.js and the geocoder * baked it into <head> as static tags: mapbox-gl.css, the geocoder css, mapbox-gl.js and the geocoder
* js, all from api.mapbox.com. The provider is chosen at RUNTIME (resolveMapProvider: * js, all from api.mapbox.com. The provider is chosen at RUNTIME (resolveMapProvider:

View file

@ -2,7 +2,7 @@
/** /**
* check-leaf-english-leaks — a translated key must not render in English, on ANY leaf. * check-leaf-english-leaks — a translated key must not render in English, on ANY leaf.
* *
* THE METHOD is rega's, by way of dx-shepherd, and is already proven in * THE METHOD comes from the browser review and is already proven in
* check-gospel-english-leaks.mjs. A string is a LEAK when the catalogue has a DIFFERENT value * check-gospel-english-leaks.mjs. A string is a LEAK when the catalogue has a DIFFERENT value
* for the locale and the page still shows the English one. That is a render or wiring fault, * for the locale and the page still shows the English one. That is a render or wiring fault,
* and it is a different thing from an untranslated string, which is a catalogue gap. * and it is a different thing from an untranslated string, which is a catalogue gap.
@ -145,7 +145,7 @@ for (const t of TARGETS) {
// {value:'unengaged', label:'Unengaged'}, 2 distinct values, 100% populated, and // {value:'unengaged', label:'Unengaged'}, 2 distinct values, 100% populated, and
// dashboard.config.js:132 counts 2,081 Unengaged / 27 Engaged. They are API VALUES // dashboard.config.js:132 counts 2,081 Unengaged / 27 Engaged. They are API VALUES
// that happen to equal a catalogue string. // that happen to equal a catalogue string.
// That is the population rega's method excludes by name ("DATA, the people-group and // That is the population the method excludes by name ("DATA, the people-group and
// place names that come from the publishers"). Whole-label matching stops a substring // place names that come from the publishers"). Whole-label matching stops a substring
// impersonating a label; it cannot stop a DATA CELL rendering the same text. So the // impersonating a label; it cannot stop a DATA CELL rendering the same text. So the
// table body is excluded by ROLE, the way prose already is. // table body is excluded by ROLE, the way prose already is.

View file

@ -7,7 +7,7 @@
* fmtCount }. There is no `rows`. Two plugins independently read `legend.rows` anyway, and because * fmtCount }. There is no `rows`. Two plugins independently read `legend.rows` anyway, and because
* JavaScript answers `undefined` rather than complaining, each failure wore a different costume: * JavaScript answers `undefined` rather than complaining, each failure wore a different costume:
* *
* - countries-map: Export poster THREW (exp-shepherd's NE-17, fixed in d6916e05e) * - countries-map: Export poster THREW (NE-17, fixed in d6916e05e)
* - research-map: provide('legendRows', legend.rows) provided undefined; the poster fallback * - research-map: provide('legendRows', legend.rows) provided undefined; the poster fallback
* `legend?.rows?.value || []` could only ever be []; and the packet's legend slot was * `legend?.rows?.value || []` could only ever be []; and the packet's legend slot was
* `rows: legend?.rows?.value || []` with no second source, so that poster printed NO legend at * `rows: legend?.rows?.value || []` with no second source, so that poster printed NO legend at

View file

@ -5,7 +5,7 @@
* WHY: the shared legend (.stl-panel, its reopen button, the detail buttons, the tree-guide hairlines) used * WHY: the shared legend (.stl-panel, its reopen button, the detail buttons, the tree-guide hairlines) used
* `left: 8px`. MapToolbar uses `inset-inline-end`, which mirrors under dir="rtl"; a physical `left` does not. * `left: 8px`. MapToolbar uses `inset-inline-end`, which mirrors under dir="rtl"; a physical `left` does not.
* So in Arabic the controls moved left onto a legend that stayed left, on three production maps at once * So in Arabic the controls moved left onto a legend that stayed left, on three production maps at once
* (Simple Research, Prayer Progress, UPG-100; rega's survey at 52eeb0db). The fix is inset-inline-start / * (Simple Research, Prayer Progress, UPG-100; a browser survey at 52eeb0db). The fix is inset-inline-start /
* inset-inline-end; this gate keeps a physical left/right from coming back. * inset-inline-end; this gate keeps a physical left/right from coming back.
* *
* WHAT IT READS: every CSS declaration in library/components/legends/*.vue (comments stripped). A * WHAT IT READS: every CSS declaration in library/components/legends/*.vue (comments stripped). A

View file

@ -12,7 +12,7 @@
* *
* ★ BUT showsFlyTools() DESCRIBES THE LEGEND TYPE, NOT WHETHER THE TOOLBAR EXISTS. Fly + Clusters are * ★ BUT showsFlyTools() DESCRIBES THE LEGEND TYPE, NOT WHETHER THE TOOLBAR EXISTS. Fly + Clusters are
* leaf-only: the composite — which is what a reader calls "Simple Research" — passes legendTools:false, * leaf-only: the composite — which is what a reader calls "Simple Research" — passes legendTools:false,
* so the toolbar is never drawn there and the 56px reserved a slot for nothing. rega measured it at * so the toolbar is never drawn there and the 56px reserved a slot for nothing. A browser check measured it at
* 1440x900 on main e1d0bc20: the band from the map's top edge (y 44) to the legend card (y 100) is 56px * 1440x900 on main e1d0bc20: the band from the map's top edge (y 44) to the legend card (y 100) is 56px
* of empty map on both tabs, no toolbar anywhere in the strip. * of empty map on both tabs, no toolbar anywhere in the strip.
* *

View file

@ -44,11 +44,11 @@ const MISSING = [
'asia-china-xinjiang-uyghur-autonomous-region', 'asia-china-xinjiang-uyghur-autonomous-region',
'asia-india-bihar', 'asia-india-bihar',
] ]
// asia-india-madhya-pradesh WAS here and is now FILED. upg100 hit this arm while filing it, and // asia-india-madhya-pradesh WAS here and is now FILED. The filing change hit this arm, and the
// did the right thing with it: they reported the red instead of editing my expectation to make // red was reported instead of the pinned expectation being edited to turn the change green: editing
// their change green — "editing someone else's pinned expectation to make my own change green is // another owner's pinned expectation to make one's own change green is precisely the move that hollows
// precisely the move that hollows out a guard". dx-shepherd then ruled that their data and this // out a guard. The data and this fixture landed TOGETHER, the only ordering where neither side is red
// fixture land TOGETHER, which is the only ordering where neither of us is red in between. // in between.
ok(leavesOf(TREE[0]).map((l) => l.id).join(',') === 'z1,z2,z3', 'leavesOf walks NESTED children, not one level') ok(leavesOf(TREE[0]).map((l) => l.id).join(',') === 'z1,z2,z3', 'leavesOf walks NESTED children, not one level')
@ -85,7 +85,7 @@ const naive = TREE // what a region-grouped tree gives you, unaugmented
ok(!zoneIdsIn(naive).has('asia-china-xinjiang-uyghur-autonomous-region'), ok(!zoneIdsIn(naive).has('asia-china-xinjiang-uyghur-autonomous-region'),
'NEG CONTROL: the unaugmented tree really does omit xinjiang, so the bucket is not decoration') 'NEG CONTROL: the unaugmented tree really does omit xinjiang, so the bucket is not decoration')
// ── a region sweep is NOT a zone sweep (gospel, END-GOS-VLG2) ─────────────────────────────────── // ── a region sweep is NOT a zone sweep (gospel-resources) ───────────────────────────────────
// ★ The consequence of the bucket that neither of us should leave implicit: ticking every REGION // ★ The consequence of the bucket that neither of us should leave implicit: ticking every REGION
// still does not select every ZONE, because the unfiled zones have no region BY DEFINITION. // still does not select every ZONE, because the unfiled zones have no region BY DEFINITION.
// Nothing on screen would say so — the reader just gets a smaller total, which is the exact // Nothing on screen would say so — the reader just gets a smaller total, which is the exact
@ -113,7 +113,7 @@ ok(selectedId(leavesOf(EMPTYZONE[0])[0]) === 'z9', 'and it still emits its own i
ok(withUnassigned(EMPTYZONE, ['z9']).length === EMPTYZONE.length, ok(withUnassigned(EMPTYZONE, ['z9']).length === EMPTYZONE.length,
'presence is by ID, not by member count, so an unresolvable zone is not ALSO dumped in the bucket') 'presence is by ID, not by member count, so an unresolvable zone is not ALSO dumped in the bucket')
// ── the gap is REAL TODAY, not just real in my fixture (gospel, END-GOS-VLG3) ─────────────────── // ── the gap is REAL TODAY, not just real in a fixture (gospel-resources) ───────────────────
// ★ EVERY ASSERTION ABOVE RUNS AGAINST A FIXTURE. That proves the MODULE behaves, and it proves the // ★ EVERY ASSERTION ABOVE RUNS AGAINST A FIXTURE. That proves the MODULE behaves, and it proves the
// fixture models a gap — it does not prove the gap still exists in the data we ship. So the negative // fixture models a gap — it does not prove the gap still exists in the data we ship. So the negative
// control above could stay green forever while the bucket quietly became dead code, or while a FIFTH // control above could stay green forever while the bucket quietly became dead code, or while a FIFTH
@ -148,7 +148,7 @@ ok(realGap.join(',') === MISSING.join(','),
realGap.join(',') === MISSING.join(',') realGap.join(',') === MISSING.join(',')
? 'and it is exactly the four this check names, so the fixture above models the shipped data rather than a story about it' ? 'and it is exactly the four this check names, so the fixture above models the shipped data rather than a story about it'
: `THE GAP MOVED — re-derive. shipped now: [${realGap.join(', ')}] but this check asserts: [${MISSING.join(', ')}]. A zone ADDED here is groups that just became unreachable; a zone REMOVED got filed and the fixture is stale. Either way the numbers in the commit body are no longer true.`) : `THE GAP MOVED — re-derive. shipped now: [${realGap.join(', ')}] but this check asserts: [${MISSING.join(', ')}]. A zone ADDED here is groups that just became unreachable; a zone REMOVED got filed and the fixture is stale. Either way the numbers in the commit body are no longer true.`)
// ── the zones that got FILED stay filed (dx-shepherd RG12) ───────────────────────────────────── // ── the zones that got FILED stay filed (RG12) ─────────────────────────────────────
// The gap arms watch the BAD direction. Filing is the good one, and it is exactly as revertible: // The gap arms watch the BAD direction. Filing is the good one, and it is exactly as revertible:
// drop a key from zone-regions and those groups leave the region tree again, silently, with the // drop a key from zone-regions and those groups leave the region tree again, silently, with the
// bucket the only thing still carrying them. Two of these three were never visible to the gap arms // bucket the only thing still carrying them. Two of these three were never visible to the gap arms
@ -163,7 +163,7 @@ for (const z of FILED) {
ok(joinable.has('asia-china-xinjiang-uyghur-autonomous-region') && !filed.has('asia-china-xinjiang-uyghur-autonomous-region'), ok(joinable.has('asia-china-xinjiang-uyghur-autonomous-region') && !filed.has('asia-china-xinjiang-uyghur-autonomous-region'),
'xinjiang specifically: joinable in admin-to-list, absent from zone-regions — its 100 groups are reachable ONLY through the bucket') 'xinjiang specifically: joinable in admin-to-list, absent from zone-regions — its 100 groups are reachable ONLY through the bucket')
// ── the id ambiguity that is masked by a data coincidence (gospel, END-GOS-VLGC) ──────────────── // ── the id ambiguity that is masked by a data coincidence (gospel-resources) ────────────────
// ★ REGION IDS AND ZONE IDS SHARE ONE NAMESPACE. selectedId emits one bare string with no kind, and // ★ REGION IDS AND ZONE IDS SHARE ONE NAMESPACE. selectedId emits one bare string with no kind, and
// the two sets OVERLAP in the shipped data. gospel found it while wiring and I re-measured it here // the two sets OVERLAP in the shipped data. gospel found it while wiring and I re-measured it here
// rather than taking the report: 33 zone ids, 7 region ids, intersection exactly {europe}, whose // rather than taking the report: 33 zone ids, 7 region ids, intersection exactly {europe}, whose

View file

@ -2,7 +2,7 @@
/** /**
* check-map-attribution — the credit line names the basemap of the engine that actually drew the map. * check-map-attribution — the credit line names the basemap of the engine that actually drew the map.
* *
* WHY (licence credit, rega's row 28 at ccc2b1bb): addMapAttribution always credited "Mapbox · * WHY (licence credit, row 28 at ccc2b1bb): addMapAttribution always credited "Mapbox ·
* © OpenStreetMap". Under MapLibre the tiles are OpenFreeMap's (OpenMapTiles on OpenStreetMap), so * © OpenStreetMap". Under MapLibre the tiles are OpenFreeMap's (OpenMapTiles on OpenStreetMap), so
* gospel-resources, key-cities and cities-internet credited Mapbox, which served nothing, and omitted * gospel-resources, key-cities and cities-internet credited Mapbox, which served nothing, and omitted
* the OpenMapTiles credit. Headless: the real module against stand-in map classes and a minimal DOM. * the OpenMapTiles credit. Headless: the real module against stand-in map classes and a minimal DOM.

View file

@ -1,4 +1,4 @@
/* check-map-unmount-guard — the fast, browser-free half of the unmount-race proof (by gospel-plugin, /* check-map-unmount-guard — the fast, browser-free half of the unmount-race proof (by the gospel-resources owner,
adopted into internal/ by the studio lane; the end-to-end half is check-map-unmount-mid-init.mjs). adopted into internal/ by the studio lane; the end-to-end half is check-map-unmount-mid-init.mjs).
Proves the guard by REPRODUCING the race against the real file: take the shipped source, Proves the guard by REPRODUCING the race against the real file: take the shipped source,

View file

@ -17,7 +17,7 @@
* (f) ProfileLoader stops mounting the notice, or the rule stops staying quiet inside the Gallery; * (f) ProfileLoader stops mounting the notice, or the rule stops staying quiet inside the Gallery;
* (g) a flagged profile's element is mounted by neither ProfileLoader nor its own useScreenGate — * (g) a flagged profile's element is mounted by neither ProfileLoader nor its own useScreenGate —
* dashboard-plus-grid defines its element without ProfileLoader, so its leaf carried * dashboard-plus-grid defines its element without ProfileLoader, so its leaf carried
* "desktop-only" and mounted the map on a phone anyway (rega, 390px). * "desktop-only" and mounted the map on a phone anyway (measured at 390px).
* `--self-test` plants one bad case per rule and proves each can fail before a pass is trusted. * `--self-test` plants one bad case per rule and proves each can fail before a pass is trusted.
*/ */
import { readFileSync, existsSync, readdirSync } from 'node:fs' import { readFileSync, existsSync, readdirSync } from 'node:fs'

View file

@ -2,7 +2,7 @@
/** /**
* check-no-credentials-omit.mjs — no data fetch in library/ or plugin-designs/ may use credentials:'omit'. * check-no-credentials-omit.mjs — no data fetch in library/ or plugin-designs/ may use credentials:'omit'.
* *
* WHY (dx-shepherd, 2026-09-27): behind any login gate (basic auth, a session cookie, OIDC) the Research * WHY (2026-09-27): behind any login gate (basic auth, a session cookie, OIDC) the Research
* Gallery said "Could not load Big Picture", because doxa-maps-spa fetched its SAME-ORIGIN manifest with * Gallery said "Could not load Big Picture", because doxa-maps-spa fetched its SAME-ORIGIN manifest with
* credentials:'omit', so the gate's cookie never went and the request was refused. 'same-origin' (the fetch * credentials:'omit', so the gate's cookie never went and the request was refused. 'same-origin' (the fetch
* default) sends credentials only to the page's own origin: a cross-origin CDN or token service still * default) sends credentials only to the page's own origin: a cross-origin CDN or token service still

View file

@ -3,7 +3,7 @@
* check-no-silent-engine-substitution — a profile must never fall back to a DIFFERENT map engine than * check-no-silent-engine-substitution — a profile must never fall back to a DIFFERENT map engine than
* the one its provider asked for. * the one its provider asked for.
* *
* THE DEFECT, found by regions' C3 read and routed by dx-shepherd (END-SHEP-C3C, 2026-09-26): * THE DEFECT, found by the C3 review (2026-09-26):
* *
* const gl = engineGlobal(provider) || window.mapboxgl * const gl = engineGlobal(provider) || window.mapboxgl
* *
@ -72,7 +72,7 @@ function scan(text) {
// documented what they removed in a /* */ block whose second line begins with a BACKTICK: // documented what they removed in a /* */ block whose second line begins with a BACKTICK:
// `engineGlobal(grProvider) || window.mapboxgl`. engineGlobal already defaults to mapbox, so // `engineGlobal(grProvider) || window.mapboxgl`. engineGlobal already defaults to mapbox, so
// The prefix test saw no comment marker, matched the quoted pattern, and reported gospel as the last // The prefix test saw no comment marker, matched the quoted pattern, and reported gospel as the last
// remaining offender at main 9df086e5. I relayed that to dx-shepherd as fact and it was wrong: the // remaining offender at main 9df086e5. That was relayed as fact and it was wrong: the
// line number had also moved three times (1490 -> 1505 -> 1509) purely because their file grew, which // line number had also moved three times (1490 -> 1505 -> 1509) purely because their file grew, which
// is what made a stale card look like a live defect. // is what made a stale card look like a live defect.
// ★ A GATE THAT FAILS ON THE EXPLANATION OF A FIX IS WORSE THAN NO GATE. It tells the one person who // ★ A GATE THAT FAILS ON THE EXPLANATION OF A FIX IS WORSE THAN NO GATE. It tells the one person who

View file

@ -21,7 +21,7 @@
* (2) is the half that matters over time: a fixed shell plus a plugin that adds .foo{min-height:700px} * (2) is the half that matters over time: a fixed shell plus a plugin that adds .foo{min-height:700px}
* next month is the same bug again, and nobody would connect it to this. * next month is the same bug again, and nobody would connect it to this.
* *
* Source-level and headless BY DESIGN. Only rega runs a browser here, and the browser proof is rega's * Source-level and headless BY DESIGN. This gate runs no browser, and the browser proof is a separate
* scroll-container count. This is the cheap guard that keeps the class from coming back between runs. * scroll-container count. This is the cheap guard that keeps the class from coming back between runs.
* ★ THIS GATE IS RED ON PURPOSE AT LANDING, and is NOT wired into gates:pre yet. gospel-internet's * ★ THIS GATE IS RED ON PURPOSE AT LANDING, and is NOT wired into gates:pre yet. gospel-internet's
* .gb still carries min-height:640px; that fix belongs to its owner, not to me, and wiring a red gate * .gb still carries min-height:640px; that fix belongs to its owner, not to me, and wiring a red gate
@ -103,7 +103,7 @@ for (const bundle of readdirSync(DESIGNS)) {
// //
// ★ THIS IS GATED BECAUSE IT WAS ALREADY PORTED ONCE AND ARRIVED SHORT. doxa-maps-spa's .dms-pane was // ★ THIS IS GATED BECAUSE IT WAS ALREADY PORTED ONCE AND ARRIVED SHORT. doxa-maps-spa's .dms-pane was
// copied from VerticalTabsLayout's .vtl-host -- its own comment says "porting the same proven shape" -- // copied from VerticalTabsLayout's .vtl-host -- its own comment says "porting the same proven shape" --
// and took the first two lines. rega measured the result: 12 of 16 gallery tabs overflowing by exactly // and took the first two lines. A browser check measured the result: 12 of 16 gallery tabs overflowing by exactly
// the DX-life header height, on plugins with no floor of their own. Two thirds of a fix reads as a // the DX-life header height, on plugins with no floor of their own. Two thirds of a fix reads as a
// whole one in review, which is precisely why a reviewer cannot be the check. // whole one in review, which is precisely why a reviewer cannot be the check.
for (const [file, label] of [ for (const [file, label] of [

View file

@ -1,6 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
/* check-optional-t-equivalence.mjs — `bun run check:optional-t-equivalence`. Source-only, headless. /* check-optional-t-equivalence.mjs — `bun run check:optional-t-equivalence`. Source-only, headless.
EVERY LATER C4 WAVE EXTENDS THIS FILE (dx-shepherd, REGC4G): a wave that migrates a site without EVERY LATER C4 WAVE EXTENDS THIS FILE (REGC4G): a wave that migrates a site without
adding its row here is a wave whose behaviour nobody re-checks. Sections 1-6 are the helper's own adding its row here is a wave whose behaviour nobody re-checks. Sections 1-6 are the helper's own
contract; section 7 is the per-site register. */ contract; section 7 is the per-site register. */
// C4: does useOptionalT() behave like the twenty hand-written guarded translators it replaces? // C4: does useOptionalT() behave like the twenty hand-written guarded translators it replaces?
@ -122,11 +122,11 @@ const SITES = [
['library/components/hints/PinHint.vue', 'useOptionalI18n', 'PINHINT_FALLBACK[key]'], ['library/components/hints/PinHint.vue', 'useOptionalI18n', 'PINHINT_FALLBACK[key]'],
['plugin-designs/pplr-data-maps/src/composables/usePplrT.js', 'useOptionalT', null], ['plugin-designs/pplr-data-maps/src/composables/usePplrT.js', 'useOptionalT', null],
['plugin-designs/upg-neighbor/src/composables/useNeighborT.js', 'useOptionalT', null], ['plugin-designs/upg-neighbor/src/composables/useNeighborT.js', 'useOptionalT', null],
// wave 3a — gospel-resources (owner: gospel-plugin). All four fell back to enLookup, i.e. the // wave 3a — gospel-resources. All four fell back to enLookup, i.e. the
// SHIPPED ENGLISH, not the locale-aware catalogue; each passes that enLookup in, so a bare host // SHIPPED ENGLISH, not the locale-aware catalogue; each passes that enLookup in, so a bare host
// still renders English. Switching them to the catalogue default would make a bare host honour // still renders English. Switching them to the catalogue default would make a bare host honour
// ?lang=ar — a real improvement, and gospel-plugin's to make, not this migration's. // ?lang=ar — a real improvement, and its owner's to make, not this migration's.
// ★ gospel-plugin CHOSE the locale-honouring catalogue fallback (2026-09-26) rather than keeping // ★ gospel-resources' owner CHOSE the locale-honouring catalogue fallback (2026-09-26) rather than keeping
// enLookup's English: "most partners will drop a custom element or an iframe and never install // enLookup's English: "most partners will drop a custom element or an iframe and never install
// vue-i18n ... an Arabic ministry site embedding this map serves its readers English while the // vue-i18n ... an Arabic ministry site embedding this map serves its readers English while the
// bundle already carries all 11 catalogues. That is a bug, not a decision." So these four take NO // bundle already carries all 11 catalogues. That is a bug, not a decision." So these four take NO
@ -143,13 +143,13 @@ const SITES = [
// wave 3b — cities-internet. All three were byte-identical in the guard block; each keeps its own // wave 3b — cities-internet. All three were byte-identical in the guard block; each keeps its own
// enLookup fallback, so a bare host renders the same English. Their local fill() used `k in params`, // enLookup fallback, so a bare host renders the same English. Their local fill() used `k in params`,
// so the shared fillParams also removes a way for them to print the literal "null". // so the shared fillParams also removes a way for them to print the literal "null".
// ★ exp-shepherd, as cities-internet's owner, took the locale-honouring default (2026-09-26): "the // ★ cities-internet's owner took the locale-honouring default (2026-09-26): the bundle already
// bundle already carries all 11 catalogues, so a bare host getting English would be a bug". So these // carries all 11 catalogues, so a bare host getting English would be a bug. So these
// three pass NO fallback, and their English-only enLookup + per-area catalogue import are deleted. // three pass NO fallback, and their English-only enLookup + per-area catalogue import are deleted.
['plugin-designs/cities-internet/connectivity-list.vue', 'useOptionalT', null], ['plugin-designs/cities-internet/connectivity-list.vue', 'useOptionalT', null],
['plugin-designs/cities-internet/connectivity-map.vue', 'useOptionalT', null], ['plugin-designs/cities-internet/connectivity-map.vue', 'useOptionalT', null],
['plugin-designs/cities-internet/data-tables.vue', 'useOptionalT', null], ['plugin-designs/cities-internet/data-tables.vue', 'useOptionalT', null],
// wave 3d — all-people-groups. Treated as UNOWNED (studio agreed), so the conservative reading wins: // wave 3d — all-people-groups. Treated as UNOWNED, so the conservative reading wins:
// its inline EN map stays the fallback rather than switching to the catalogue, because nobody is // its inline EN map stays the fallback rather than switching to the catalogue, because nobody is
// available to decide that this bundle should start honouring the locale on a bare host -- THEN // available to decide that this bundle should start honouring the locale on a bare host -- THEN
// the locale-honouring fallback was RULED for everywhere (2026-09-27; the goal: a bare-host // the locale-honouring fallback was RULED for everywhere (2026-09-27; the goal: a bare-host
@ -162,11 +162,11 @@ const SITES = [
// the inline EN map. Taking a SITES array wholesale from another branch re-asserts ITS view of every // the inline EN map. Taking a SITES array wholesale from another branch re-asserts ITS view of every
// file in it, including the ones you changed; the gate caught it, which is the only reason it is right. // file in it, including the ones you changed; the gate caught it, which is the only reason it is right.
['plugin-designs/all-people-groups/all-people-groups.vue', 'useOptionalI18n', null], ['plugin-designs/all-people-groups/all-people-groups.vue', 'useOptionalI18n', null],
// wave 3c — my-upg-100-list (owner: upg100-plugin). No namespace, since callers pass full keys. // wave 3c — my-upg-100-list. No namespace, since callers pass full keys.
// ⚠ THE THIRD COLUMN IS null, NOT 'humanizeKey(k)', and that is a DEPARTURE from 4c05e589, which // ⚠ THE THIRD COLUMN IS null, NOT 'humanizeKey(k)', and that is a DEPARTURE from 4c05e589, which
// kept the fallback. Correct as regions-plugin wrote it; wrong the moment dx-shepherd took the // kept the fallback. Correct as first written; wrong the moment the owner took the
// product call (UPGHK, 2026-09-27) to delete it. A row asserting a fallback the code no longer // product call (UPGHK, 2026-09-27) to delete it. A row asserting a fallback the code no longer
// passes fails by design — the same hole regions flagged to me in the opposite direction. // passes fails by design — the same hole flagged earlier in the opposite direction.
// ★ WHY IT WAS DELETED. humanizeKey rendered a key's last segment title-cased, failing two ways: on // ★ WHY IT WAS DELETED. humanizeKey rendered a key's last segment title-cased, failing two ways: on
// the messages keys it printed "Upg100 Wall Intro" where a whole sentence exists (wrong even in // the messages keys it printed "Upg100 Wall Intro" where a whole sentence exists (wrong even in
// English), and on the tabs keys it printed the RIGHT English by coincidence — those keys are named // English), and on the tabs keys it printed the RIGHT English by coincidence — those keys are named
@ -175,7 +175,7 @@ const SITES = [
// someone changes locale. // someone changes locale.
// The premise for keeping it ("these keys are not fully catalogued") was MINE and was FALSE: seven // The premise for keeping it ("these keys are not fully catalogued") was MINE and was FALSE: seven
// keys across both files, all string literals, every one present in all eleven locales, 0 gaps. // keys across both files, all string literals, every one present in all eleven locales, 0 gaps.
// Same class of decision gospel-plugin took for its four sites above, and the OPPOSITE of the 3d // Same class of decision gospel-resources' owner took for its four sites above, and the OPPOSITE of the 3d
// reading directly above — which is right, because that file is unowned and these two are not. // reading directly above — which is right, because that file is unowned and these two are not.
['plugin-designs/my-upg-100-list/poster-wall.vue', 'useOptionalT', null], ['plugin-designs/my-upg-100-list/poster-wall.vue', 'useOptionalT', null],
['plugin-designs/my-upg-100-list/upg-100-mfe.vue', 'useOptionalT', null], ['plugin-designs/my-upg-100-list/upg-100-mfe.vue', 'useOptionalT', null],
@ -188,11 +188,11 @@ const SITES = [
// third bare call — a new unguarded translator — still fails this gate. // third bare call — a new unguarded translator — still fails this gate.
// ★ DELIBERATELY NOT IN THIS REGISTER, so nobody "completes" C4 by converting it: // ★ DELIBERATELY NOT IN THIS REGISTER, so nobody "completes" C4 by converting it:
// plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue // plugin-designs/pplr-data-maps/src/components/SemanticTreeLegend.vue
// It still hand-rolls the guard, and exp-shepherd asked for it to stay that way: the file is carded for // It still hand-rolls the guard, and its owner asked for it to stay that way: the file is carded for
// retirement (RETIRE-PPLR-SEMANTICTREELEGEND) in favour of the library component, so converting it is // retirement (RETIRE-PPLR-SEMANTICTREELEGEND) in favour of the library component, so converting it is
// work that gets thrown away. A migration whose value expires before it lands is not worth the diff. // work that gets thrown away. A migration whose value expires before it lands is not worth the diff.
// Two more sites are their owners' to cut, not absent by oversight: country-posters-mfe.vue // Two more sites are their owners' to cut, not absent by oversight: country-posters-mfe.vue
// (exp-shepherd, into its single posters landing) and key-cities/cities-map.vue (exp-shepherd, gated). // (into its single posters landing) and key-cities/cities-map.vue (gated).
const ALLOWED_BARE = { 'plugin-designs/gospel-resources/resources-map.vue': 2 } const ALLOWED_BARE = { 'plugin-designs/gospel-resources/resources-map.vue': 2 }
for (const [file, helper, fallbackFrag] of SITES) { for (const [file, helper, fallbackFrag] of SITES) {
@ -221,13 +221,13 @@ for (const [file, helper, fallbackFrag] of SITES) {
// Which sites were MOVED to the catalogue by a decision, vs which always resolved there. Saying "as it // Which sites were MOVED to the catalogue by a decision, vs which always resolved there. Saying "as it
// always did" about a moved file is a false statement inside a gate message, the one place a reader has // always did" about a moved file is a false statement inside a gate message, the one place a reader has
// no reason to doubt. gospel-resources and cities-internet moved by their owners' decisions, // no reason to doubt. gospel-resources and cities-internet moved by their owners' decisions,
// my-upg-100-list by dx-shepherd's product call (UPGHK) deleting humanizeKey, and all-people-groups by // my-upg-100-list by a product call (UPGHK) deleting humanizeKey, and all-people-groups by
// the embed-contract ruling (locale-honouring everywhere, 2026-09-27). // the embed-contract ruling (locale-honouring everywhere, 2026-09-27).
const MOVED_BY = { const MOVED_BY = {
'gospel-resources': "gospel-plugin moved it here from enLookup's English", 'gospel-resources': "its owner moved it here from enLookup's English",
'cities-internet': "exp-shepherd moved it here from enLookup's English", 'cities-internet': "its owner moved it here from enLookup's English",
'my-upg-100-list': 'upg100-plugin moved it here from humanizeKey, a title-cased key segment', 'my-upg-100-list': 'its owner moved it here from humanizeKey, a title-cased key segment',
'all-people-groups': "dx-shepherd's embed-contract ruling moved it here from its inline English map", 'all-people-groups': "the embed contract moved it here from its inline English map",
} }
const mover = Object.keys(MOVED_BY).find((d) => file.includes(d)) const mover = Object.keys(MOVED_BY).find((d) => file.includes(d))
ok(!/fallback:/.test(code), mover ok(!/fallback:/.test(code), mover
@ -236,7 +236,7 @@ for (const [file, helper, fallbackFrag] of SITES) {
} }
} }
// ── 7b. gospel-plugin's ASKED-FOR ROW, against the REAL catalogue ─────────────────────────────── // ── 7b. gospel-resources' ASKED-FOR ROW, against the REAL catalogue ───────────────────────────────
// Their words: "a BARE host (no vue-i18n) with ?lang=ar must render pins.approximate in Arabic — not // Their words: "a BARE host (no vue-i18n) with ?lang=ar must render pins.approximate in Arabic — not
// the key, not gospel.pins.approximate, not English." It covers their conditions 1 and 2 together: a // the key, not gospel.pins.approximate, not English." It covers their conditions 1 and 2 together: a
// namespace would corrupt the key, and an EN-only fallback would serve English to an Arabic reader. // namespace would corrupt the key, and an EN-only fallback would serve English to an Arabic reader.

View file

@ -5,7 +5,7 @@
* WHY: JSON.parse keeps the LAST of two equal keys and says nothing, so a duplicate silently kills the * WHY: JSON.parse keeps the LAST of two equal keys and says nothing, so a duplicate silently kills the
* first entry. "check:religion-label-not-value" sat twice in //scripts, one description dead, while * first entry. "check:religion-label-not-value" sat twice in //scripts, one description dead, while
* check:doc-scripts passed (it reads the parsed object, where only one survives). It was a merge artifact: * check:doc-scripts passed (it reads the parsed object, where only one survives). It was a merge artifact:
* the landing tooling unions both sides' package.json additions. Found by regions-plugin; only Bun's parser * the landing tooling unions both sides' package.json additions. Found during a landing; only Bun's parser
* warned, while running something unrelated. * warned, while running something unrelated.
* *
* HOW: a small JSON scanner that tracks every object's keys by position, so it sees what JSON.parse * HOW: a small JSON scanner that tracks every object's keys by position, so it sees what JSON.parse

View file

@ -129,7 +129,7 @@ ok(Math.round(per / 100) * 100 === 40200,
// it: 32 contiguous cells fill most of row 1 of 52, so the page printed as one solid bar across the // it: 32 contiguous cells fill most of row 1 of 52, so the page printed as one solid bar across the
// header — the ordinary chart the life grid exists to avoid — and the existing gate asserts only // header — the ordinary chart the life grid exists to avoid — and the existing gate asserts only
// that the shaded COUNT is between 0 and 5,200, which is equally true of a bar and of a scatter. // that the shaded COUNT is between 0 and 5,200, which is equally true of a bar and of a scatter.
// A count cannot distinguish a distribution. rega's eyes caught it; nothing else could have. // A count cannot distinguish a distribution. A visual review caught it; nothing else could have.
const picked = scatterCells(5200, 32, 'upg-100') const picked = scatterCells(5200, 32, 'upg-100')
const spread = scatterSpread(picked, 52) const spread = scatterSpread(picked, 52)
ok(spread.cells === 32, '32 shaded cells are picked, distinct') ok(spread.cells === 32, '32 shaded cells are picked, distinct')

View file

@ -33,7 +33,7 @@ import { pathToFileURL, fileURLToPath } from 'node:url'
import { requirePathInThisCheckout } from './_serves-this-tree.mjs' import { requirePathInThisCheckout } from './_serves-this-tree.mjs'
// ★ --expect-funnel TURNS AN OPTIONAL PAGE INTO A DECLARED REQUIREMENT, and it exists because of a // ★ --expect-funnel TURNS AN OPTIONAL PAGE INTO A DECLARED REQUIREMENT, and it exists because of a
// real near-miss (regions-plugin, 2026-09-26, reported unprompted). A merge resolved with // real near-miss (2026-09-26, reported unprompted). A merge resolved with
// `git checkout --ours` silently deleted the printPacket call site that supplies the funnel data — // `git checkout --ours` silently deleted the printPacket call site that supplies the funnel data —
// 60 lines — AND EVERY GATE STAYED GREEN. It had to: the funnel page is optional by design // 60 lines — AND EVERY GATE STAYED GREEN. It had to: the funnel page is optional by design
// (buildPacket omits it when the arg is null), so a packet with no page 2 is not malformed, just // (buildPacket omits it when the arg is null), so a packet with no page 2 is not malformed, just
@ -274,8 +274,8 @@ const r = await page.evaluate(() => {
})(), })(),
funnelEq: document.querySelectorAll('.funnel-eq').length, funnelEq: document.querySelectorAll('.funnel-eq').length,
funnelSteps: document.querySelectorAll('.funnel-mine').length, funnelSteps: document.querySelectorAll('.funnel-mine').length,
// ★ MEASURED SO THE GATE CAN REPORT WHAT IT ASSERTED. rega: "the gate prints no funnel line, even // ★ MEASURED SO THE GATE CAN REPORT WHAT IT ASSERTED. A review found the gate printed no funnel line, even
// with --expect-funnel ... a reader cannot see WHAT it asserted about page 2." A silent pass is // with --expect-funnel, so a reader could not see WHAT it asserted about page 2. A silent pass is
// the mirror of a printed-but-unasserted number: the check is real and the evidence is invisible. // the mirror of a printed-but-unasserted number: the check is real and the evidence is invisible.
// ⚠ ZERO POPULATION IS COUNTED ON PURPOSE. The reader's row printed "0 people" in every packet // ⚠ ZERO POPULATION IS COUNTED ON PURPOSE. The reader's row printed "0 people" in every packet
// (Number(null) is 0, so the finite-number guard admitted a deliberate no-data marker). Fixed in // (Number(null) is 0, so the finite-number guard admitted a deliberate no-data marker). Fixed in
@ -302,10 +302,10 @@ const r = await page.evaluate(() => {
.filter((d) => /^0(\.0+)?\s+\D/.test(d.textContent.trim())).length .filter((d) => /^0(\.0+)?\s+\D/.test(d.textContent.trim())).length
})(), })(),
funnelShare: (document.querySelector('.funnel-share')?.textContent || '').trim().slice(0, 80), funnelShare: (document.querySelector('.funnel-share')?.textContent || '').trim().slice(0, 80),
// ★ LATIN RUNS ON A NON-LATIN SHEET, which is the one gap rega named and I left open: "the gate // ★ LATIN RUNS ON A NON-LATIN SHEET, the one gap a review named and that was left open: the gate
// passed: it counts rows, not the row's language." It asserted the funnel page's STRUCTURE and // passed because it counted rows, not the rows' language. It asserted the funnel page's STRUCTURE
// nothing about whether its prose reached the reader's language — my own standing rule turned on // and nothing about whether its prose reached the reader's language, the standing rule turned on
// my own gate. // this gate itself.
// //
// ⚠ IT ONLY WORKS IN A NON-LATIN-SCRIPT LOCALE, and that is why it is worth having rather than // ⚠ IT ONLY WORKS IN A NON-LATIN-SCRIPT LOCALE, and that is why it is worth having rather than
// aspiring to a general "is this translated?" test, which is not decidable. On a hi/ar/ru/zh sheet // aspiring to a general "is this translated?" test, which is not decidable. On a hi/ar/ru/zh sheet
@ -749,7 +749,7 @@ if (r.lifeSheets) {
} }
} }
// ⚠ AND THE SHADE MUST BE SCATTERED, NOT STACKED — the assertion that would have caught what three gates // ⚠ AND THE SHADE MUST BE SCATTERED, NOT STACKED — the assertion that would have caught what three gates
// and a passing suite did not. rega's contact sheet (2026-09-28) showed page 3 reading as ONE SOLID BAR: // and a passing suite did not. A contact sheet (2026-09-28) showed page 3 reading as ONE SOLID BAR:
// 32 contiguous cells fill most of row 1 of 52, so the page said "a big green block" where the // 32 contiguous cells fill most of row 1 of 52, so the page said "a big green block" where the
// intent is 32 boxes lost in 5,200. Every assertion passed, because they all measured the COUNT. // intent is 32 boxes lost in 5,200. Every assertion passed, because they all measured the COUNT.
// Half the grid height is a deliberately loose floor: a genuine scatter of even a few cells spans most of // Half the grid height is a deliberately loose floor: a genuine scatter of even a few cells spans most of
@ -793,7 +793,7 @@ if (r.funnel && !r.funnelSteps) {
// ★ AND IF THE CALLER DECLARED IT, ITS ABSENCE IS A FAILURE. See the --expect-funnel note at the top: // ★ AND IF THE CALLER DECLARED IT, ITS ABSENCE IS A FAILURE. See the --expect-funnel note at the top:
// a lost call site produces a packet that is CORRECT but missing page 2, which is why nothing caught // a lost call site produces a packet that is CORRECT but missing page 2, which is why nothing caught
// a merge deleting one. // a merge deleting one.
// ★ EVERY FUNNEL ROW MUST HAVE A BAR. rega measured the first version: the reader's step had no bar, // ★ EVERY FUNNEL ROW MUST HAVE A BAR. A browser check measured the first version: the reader's step had no bar,
// so the funnel never visibly narrowed to "yours" — a funnel whose final term is not drawn as part of // so the funnel never visibly narrowed to "yours" — a funnel whose final term is not drawn as part of
// the funnel fails at the one thing it is for. Rows and bars are counted separately so the next // the funnel fails at the one thing it is for. Rows and bars are counted separately so the next
// regression is a mismatch rather than an invisible omission. // regression is a mismatch rather than an invisible omission.
@ -805,8 +805,8 @@ if (r.funnel && r.funnelRows !== r.funnelBars) {
// because Number(null) is 0 and the renderer's guard tested finiteness rather than PRESENCE. A people // because Number(null) is 0 and the renderer's guard tested finiteness rather than PRESENCE. A people
// group with no people is not a fact; it is missing data wearing a number's clothes. // group with no people is not a fact; it is missing data wearing a number's clothes.
// ★ AND ON A NON-LATIN SHEET, THE FUNNEL'S PROSE MUST BE IN THE READER'S SCRIPT. See funnelLatinRuns // ★ AND ON A NON-LATIN SHEET, THE FUNNEL'S PROSE MUST BE IN THE READER'S SCRIPT. See funnelLatinRuns
// above for why this is only meaningful in hi/ar/ru/zh. This is the gap rega named after the ?list= // above for why this is only meaningful in hi/ar/ru/zh. This is the gap a review named after the ?list=
// render: "the gate passed: it counts rows, not the row's language", and it was right — the English // render (the gate passed because it counted rows, not the rows' language), and it was right — the English
// list label sat among Devanagari and every assertion here was green. // list label sat among Devanagari and every assertion here was green.
// //
// ⚠ THE ALLOWLIST IS TWO ENTRIES AND BOTH ARE DECISIONS, NOT BOOKMARKS: // ⚠ THE ALLOWLIST IS TWO ENTRIES AND BOTH ARE DECISIONS, NOT BOOKMARKS:

View file

@ -189,7 +189,7 @@ ok(count(light, /sheet-sets-(bounded|centered)/g) === 0, 'lightweight still carr
// "rows close on the bottom margin" one already deleted from check-profile-table. A printed sheet // "rows close on the bottom margin" one already deleted from check-profile-table. A printed sheet
// is simply much taller than gospel's constrained pane, which is why their bug is not ours. // is simply much taller than gospel's constrained pane, which is why their bug is not ours.
// The real protection is the empty-ring filter asserted above (ten rows is the ceiling) plus // The real protection is the empty-ring filter asserted above (ten rows is the ceiling) plus
// rega's rendered check; a headless budget with 790px of slack would be decoration. // the rendered browser check; a headless budget with 790px of slack would be decoration.
} }
// An unknown variant must print the FULL packet. A typo that printed a SHORTER packet would look // An unknown variant must print the FULL packet. A typo that printed a SHORTER packet would look
@ -204,7 +204,7 @@ ok(/printPacketAs[\s\S]{0,600}prints\.printPacket\(/.test(WALL),
'printPacketAs does not call prints.printPacket — the Prayer List caret would print the columnar LIST again, not the packet') 'printPacketAs does not call prints.printPacket — the Prayer List caret would print the columnar LIST again, not the packet')
ok(/variant/.test(PRINTS.match(/async function printPacket\([^)]*\)/)?.[0] || ''), ok(/variant/.test(PRINTS.match(/async function printPacket\([^)]*\)/)?.[0] || ''),
'usePosterPrints.printPacket does not accept a variant — the caret\'s choice would be silently dropped') 'usePosterPrints.printPacket does not accept a variant — the caret\'s choice would be silently dropped')
// printList itself STAYS (dx-shepherd: don't delete it, just stop the button calling it). // printList itself STAYS (by decision: keep it, just stop the button calling it).
ok(/function printList\(/.test(PRINTS) && /printList,/.test(PRINTS), ok(/function printList\(/.test(PRINTS) && /printList,/.test(PRINTS),
'printList was removed — it must remain exported for any surface that still wants the columnar list') 'printList was removed — it must remain exported for any surface that still wants the columnar list')

View file

@ -132,7 +132,7 @@ const gate = join(HERE, 'check-packet-render.mjs')
// ★ THE FUNNEL PAGE IS REQUIRED HERE, and this line is the half of the fix that was missing. // ★ THE FUNNEL PAGE IS REQUIRED HERE, and this line is the half of the fix that was missing.
// check-packet-render gained --expect-funnel after a merge deleted the call site supplying the funnel // check-packet-render gained --expect-funnel after a merge deleted the call site supplying the funnel
// data and every gate stayed green (regions-plugin, 2026-09-26). But a DECLARABLE expectation that // data and every gate stayed green (2026-09-26). But a DECLARABLE expectation that
// nothing declares is not a closed hole — it is an invisible defect traded for a visible obligation. // nothing declares is not a closed hole — it is an invisible defect traded for a visible obligation.
// I said as much to regions and then owed the second half. // I said as much to regions and then owed the second half.
// //

View file

@ -3,7 +3,7 @@
* check-pane-config-reforward — a composite's panes keep following the outer config for the whole * check-pane-config-reforward — a composite's panes keep following the outer config for the whole
* session, and a locale change never rewrites a filter into a display name. * session, and a locale change never rewrites a filter into a display name.
* *
* ★ THE BUG THIS GUARDS, measured by rega on development 92ca8b6e6 across FOUR composites * ★ THE BUG THIS GUARDS, measured on development 92ca8b6e6 across FOUR composites
* (regions-mfe, upg-100-mfe, country-posters-mfe, research-map-plus-grid). The outer element's * (regions-mfe, upg-100-mfe, country-posters-mfe, research-map-plus-grid). The outer element's
* profile-config reached locale 'ar' by +1.5 s — attribute AND property, same node — while every * profile-config reached locale 'ar' by +1.5 s — attribute AND property, same node — while every
* NESTED host carried NO locale key at boot and still none at +15 s. Text per composite at +15 s: * NESTED host carried NO locale key at boot and still none at +15 s. Text per composite at +15 s:
@ -15,7 +15,7 @@
* ★ IT RUNS THE SHIPPED HELPER WITH REAL VUE. usePaneConfig is imported, not re-typed, and driven * ★ IT RUNS THE SHIPPED HELPER WITH REAL VUE. usePaneConfig is imported, not re-typed, and driven
* by real refs through a real watchEffect — the dependency-tracking hole this gate's case 3 covers * by real refs through a real watchEffect — the dependency-tracking hole this gate's case 3 covers
* (a plain array leaves a LATER entry's getter untracked) is invisible to any test that fakes * (a plain array leaves a LATER entry's getter untracked) is invisible to any test that fakes
* reactivity, and it was found by studio only because their test used real Vue. * reactivity, and it was found only because a test used real Vue.
* *
* ★ AND IT ASSERTS THE DOM, NOT THE RETURN VALUE. Every case reads the attribute back off the * ★ AND IT ASSERTS THE DOM, NOT THE RETURN VALUE. Every case reads the attribute back off the
* element, because "the helper decided to write" and "the pane was told" are different claims and * element, because "the helper decided to write" and "the pane was told" are different claims and
@ -76,7 +76,7 @@ const cfgOf = (el) => {
paneConfig.dispose() paneConfig.dispose()
} }
// ── 1b. NO LOCALE KEY AT BOOT IS THE REAL BEFORE-STATE, not a stale one. rega measured the nested // ── 1b. NO LOCALE KEY AT BOOT IS THE REAL BEFORE-STATE, not a stale one. A browser check measured the nested
// hosts carrying no locale key at all, so the first re-forward is absent -> 'ar'. // hosts carrying no locale key at all, so the first re-forward is absent -> 'ar'.
{ {
const outer = ref({ apiBaseUrl: '/api' }) const outer = ref({ apiBaseUrl: '/api' })
@ -225,7 +225,7 @@ const cfgOf = (el) => {
// gridLanguage() falls back to `linked.language.value || linked.language.label`, and `label` is a // gridLanguage() falls back to `linked.language.value || linked.language.label`, and `label` is a
// DISPLAY NAME. The grid's valueMatches() compares whole normalised values, so a name fetched in // DISPLAY NAME. The grid's valueMatches() compares whole normalised values, so a name fetched in
// one language matches no row in another — the failure that took the grid 2,107 -> 0 and read as // one language matches no row in another — the failure that took the grid 2,107 -> 0 and read as
// "no results" (rega, 2026-09-26). This asserts the CLASS, not today's instance. // "no results" (measured 2026-09-26). This asserts the CLASS, not today's instance.
{ {
const locale = ref('en') const locale = ref('en')
// A language whose display label follows the locale, as the API returns it, but whose code does not. // A language whose display label follows the locale, as the API returns it, but whose code does not.

View file

@ -11,7 +11,7 @@
* this._data = typeof options.data === 'string' ? { url: options.data } : { geojson: options.data } * this._data = typeof options.data === 'string' ? { url: options.data } : { geojson: options.data }
* *
* so `data.features` was undefined and the reader returned an EMPTY LIST on MapLibre — the keyless * so `data.features` was undefined and the reader returned an EMPTY LIST on MapLibre — the keyless
* engine the public builds use. Measured by rega on 2026-09-28: a language-family selection and a * engine the public builds use. Measured on 2026-09-28: a language-family selection and a
* prayer selection both logged "0 pin(s) selected" with the pins plainly on screen. * prayer selection both logged "0 pin(s) selected" with the pins plainly on screen.
* *
* ★ THE FAILURE MODE IS WHY A TEST IS THE ONLY GUARD. Every caller is written to NO-OP when it finds no * ★ THE FAILURE MODE IS WHY A TEST IS THE ONLY GUARD. Every caller is written to NO-OP when it finds no
@ -79,7 +79,7 @@ for (const [label, src] of [['mapbox', { _data: COLLECTION }], ['maplibre', { _d
eq('expressions are ANDed', matchesExpression(['all', ['==', ['get', 'uniqueId'], 'a'], ['==', ['get', 'languageFamily'], 'Indo-European']], FEATURES[0].properties), false) eq('expressions are ANDed', matchesExpression(['all', ['==', ['get', 'uniqueId'], 'a'], ['==', ['get', 'languageFamily'], 'Indo-European']], FEATURES[0].properties), false)
// ── PACKETSEL: the prayer count is LIVE, so the packet must read feature-state ────────────────── // ── PACKETSEL: the prayer count is LIVE, so the packet must read feature-state ──────────────────
// rega, RQ3(b): the packet's "Has Prayer" selection showed 50 of 997 where the legend row said 699. // RQ3(b): the packet's "Has Prayer" selection showed 50 of 997 where the legend row said 699.
// The legend is right. Polling writes each pin's current count with setFeatureState() and // The legend is right. Polling writes each pin's current count with setFeatureState() and
// prayer-progress.js:94 colours from ['feature-state','peoplePraying']; the GeoJSON property is // prayer-progress.js:94 colours from ['feature-state','peoplePraying']; the GeoJSON property is
// whatever was true at page load. Reading the property selects the wrong people to pray for, which is // whatever was true at page load. Reading the property selects the wrong people to pray for, which is

View file

@ -108,9 +108,9 @@ async function measure(width, lang) {
await page.waitForTimeout(10000) await page.waitForTimeout(10000)
// ★ HOVER IS INVISIBLE TO A STATIC PROBE, which is exactly how the blue outline stayed // ★ HOVER IS INVISIBLE TO A STATIC PROBE, which is exactly how the blue outline stayed
// unbuilt for so long. Repeated reports asked to "restore the blue outline around the poster"; // unbuilt for so long. A blue outline around the hovered poster was reported missing more than once;
// measuring the loaded page found `outline:none` on all 136 framed elements and concluded it had // measuring the loaded page found `outline:none` on all 136 framed elements and concluded it had
// never existed — true, and useless, because the affordance being asked for only exists WHILE // never existed — true, and useless, because the affordance in question only exists WHILE
// THE POINTER IS OVER THE CARD. The pointer has to actually move. // THE POINTER IS OVER THE CARD. The pointer has to actually move.
// //
// ⚠ AND IT MUST NOT MOVE ANYTHING. The whole rest of this gate asserts where the print row sits. // ⚠ AND IT MUST NOT MOVE ANYTHING. The whole rest of this gate asserts where the print row sits.

View file

@ -3,7 +3,7 @@
* check-poster-layout-optin — the composed poster layout is OPT-IN, and every caller that does not * check-poster-layout-optin — the composed poster layout is OPT-IN, and every caller that does not
* ask for it gets byte-for-byte what it got before. * ask for it gets byte-for-byte what it got before.
* *
* WHY THE OPT-IN EXISTS (dx-shepherd's ruling, from upg100's check). The renderer used to raster the * WHY THE OPT-IN EXISTS (a decision that followed upg-100-list's check). The renderer used to raster the
* map at FULL PAGE size and composite the title, legend and footer on top of it, so the legend lay * map at FULL PAGE size and composite the title, legend and footer on top of it, so the legend lay
* over the map it describes. usePosterLayout had always computed the correct geometry and nothing * over the map it describes. usePosterLayout had always computed the correct geometry and nothing
* consumed it. Fixing that unconditionally would have broken a different product: country-posters * consumed it. Fixing that unconditionally would have broken a different product: country-posters

View file

@ -83,7 +83,7 @@ ok(typeof html === 'string' && html.length > 10000 && /<svg\b/.test(html), `the
// THE CLASSIC-SCRIPT COPY (strict-CSP pages load it with <script integrity>): pinned, served as the tree // THE CLASSIC-SCRIPT COPY (strict-CSP pages load it with <script integrity>): pinned, served as the tree
// writes it, and — run as a real classic script — the SAME generator: its sheet is byte-identical to the // writes it, and — run as a real classic script — the SAME generator: its sheet is byte-identical to the
// module's for the same inputs. (The <script> path itself needs a browser; that half is rega's.) // module's for the same inputs. (The <script> path itself needs a browser; that half is a browser check.)
{ {
const classic = globalVariant(source) const classic = globalVariant(source)
const servedClassic = readFileSync(join(REPO, 'public/dx-maps/assets/lib/poster-render.global.js'), 'utf8') const servedClassic = readFileSync(join(REPO, 'public/dx-maps/assets/lib/poster-render.global.js'), 'utf8')

View file

@ -3,7 +3,7 @@
* check-preset-config-merge — a preset page keeps its profile's defaults. Headless. * check-preset-config-merge — a preset page keeps its profile's defaults. Headless.
* *
* A preset (<bundle>/<profile>/<instance>/index.html) bakes a config made of the profile's * A preset (<bundle>/<profile>/<instance>/index.html) bakes a config made of the profile's
* <profile>.meta.json `config` with the instance's `props` MERGED over it. It used to REPLACE it: go-int's * <profile>.meta.json `config` with the instance's `props` MERGED over it. It used to REPLACE it: the
* upg-neighbor presets lost the profile's pinHint:false. For every built preset page this asserts each * upg-neighbor presets lost the profile's pinHint:false. For every built preset page this asserts each
* meta `config` key is present, with the instance's own value where the instance sets one. * meta `config` key is present, with the instance's own value where the instance sets one.
* Rule stated in documentation/PRESET-PLUGIN-STANDARD.md. * Rule stated in documentation/PRESET-PLUGIN-STANDARD.md.

View file

@ -7,7 +7,7 @@
* It runs in Node with no layout engine, so it asserts the things that are TRUE OF THE MODEL: the * It runs in Node with no layout engine, so it asserts the things that are TRUE OF THE MODEL: the
* geometry closes, every group gets a row, the fit ladder picks the size it claims, the fallback * geometry closes, every group gets a row, the fit ladder picks the size it claims, the fallback
* fires in all three states. It CANNOT assert that the rendered row is 73px tall in a browser — * fires in all three states. It CANNOT assert that the rendered row is 73px tall in a browser —
* that is rega's seat (internal/check-packet-render.mjs, which launches a browser and is exit-75 * that is the browser gate (internal/check-packet-render.mjs, which launches a browser and is exit-75
* NOT MEASURED anywhere else). A character-count budget is a MODEL of wrapping, not a measurement * NOT MEASURED anywhere else). A character-count budget is a MODEL of wrapping, not a measurement
* of it, and the honest place to say so is here rather than in a commit message nobody re-reads. * of it, and the honest place to say so is here rather than in a commit message nobody re-reads.
* *
@ -61,7 +61,7 @@ const CH = (H - 2 * M - HEAD - GUT * (PER_PAGE - 1)) / PER_PAGE
// to fire: 20 rows (73px) and 21 rows (69px) pass because the content genuinely fits; 24 rows // to fire: 20 rows (73px) and 21 rows (69px) pass because the content genuinely fits; 24 rows
// (60px) and 28 fail. That is the boundary this is meant to guard. // (60px) and 28 fail. That is the boundary this is meant to guard.
// ⚠ THE ROW IS A BORDER-BOX, so its padding and border come OUT of the declared height rather than // ⚠ THE ROW IS A BORDER-BOX, so its padding and border come OUT of the declared height rather than
// being added to it. rega measured 81px against a 73.25px model before `box-sizing:border-box` was // being added to it. A browser check measured 81px against a 73.25px model before `box-sizing:border-box` was
// set — the rows were rendering 7.75px taller than the geometry that positions them, overlapping // set — the rows were rendering 7.75px taller than the geometry that positions them, overlapping
// their successors, while the stack still fitted the sheet (ending 8px INSIDE the bottom margin // their successors, while the stack still fitted the sheet (ending 8px INSIDE the bottom margin
// rather than on it). Nothing clipped and nothing ran off the page, which is exactly why only a // rather than on it). Nothing clipped and nothing ran off the page, which is exactly why only a
@ -156,7 +156,7 @@ ok(long.text.endsWith('…'), 'a clamped description is not marked with an ellip
} }
// ── 5. EVERY SHIPPED LOCALE FITS, MEASURED AGAINST THE REAL CATALOGUE ────────────────────────── // ── 5. EVERY SHIPPED LOCALE FITS, MEASURED AGAINST THE REAL CATALOGUE ──────────────────────────
// ★ This is the arm that would have caught a locale I cannot eyeball. The ladder is INSURANCE, not // ★ This is the arm that would have caught a locale no reviewer can read by eye. The ladder is INSURANCE, not
// routine: if a real locale is routinely stepping down, the budget is wrong and the fix is the // routine: if a real locale is routinely stepping down, the budget is wrong and the fix is the
// budget, not the ladder. Longest today is German at 178 against a 234-char budget. // budget, not the ladder. Longest today is German at 178 against a 234-char budget.
{ {
@ -177,4 +177,4 @@ if (fails.length) {
for (const f of fails) console.error(` ✗ ${f}`) for (const f of fails) console.error(` ✗ ${f}`)
process.exit(1) process.exit(1)
} }
console.log('PROFILE-TABLE: PASS — the row is tall enough for a name plus two description lines at the smallest ladder size, every group gets a row across page boundaries, the fit ladder steps 17→15→14 and cuts at a whole word only as a last resort, a missing or empty describer degrades to the metadata line, and every shipped locale fits at full size. NOT covered here: the RENDERED row height and whether the description actually wraps to two lines — those need a browser and are rega\'s seat.') console.log('PROFILE-TABLE: PASS — the row is tall enough for a name plus two description lines at the smallest ladder size, every group gets a row across page boundaries, the fit ladder steps 17→15→14 and cuts at a whole word only as a last resort, a missing or empty describer degrades to the metadata line, and every shipped locale fits at full size. NOT covered here: the RENDERED row height and whether the description actually wraps to two lines — those need a browser check.')

View file

@ -1,6 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
/* check-provider-for.mjs — run as `bun run check:provider-for`. Source-only, headless, ~0.1s. /* check-provider-for.mjs — run as `bun run check:provider-for`. Source-only, headless, ~0.1s.
Placed in internal/ with studio's explicit authorisation for this one gate (END-STU-C3AB): it spans Placed in internal/ by explicit decision for this one gate: it spans
library/ plus three separate plugins, so no single plugin's scripts/ folder owns it. */ library/ plus three separate plugins, so no single plugin's scripts/ folder owns it. */
// C3: do the library helpers behave like the raw-global code they replace, on BOTH engine shapes AND // C3: do the library helpers behave like the raw-global code they replace, on BOTH engine shapes AND
// on the ambiguous case that is neither? // on the ambiguous case that is neither?

View file

@ -10,7 +10,7 @@
* ★ NARROWED AFTER LANDING, and this is what the next reader needs: the ordering is not merely * ★ NARROWED AFTER LANDING, and this is what the next reader needs: the ordering is not merely
* "not the cause" — it is UNCONSTRUCTIBLE today. region-dashboards.vue:1091 gates the entire map * "not the cause" — it is UNCONSTRUCTIBLE today. region-dashboards.vue:1091 gates the entire map
* subtree behind v-if="loading", and loadRegions assigns points inside the try while loading=false * subtree behind v-if="loading", and loadRegions assigns points inside the try while loading=false
* is in the finally, so the map CANNOT mount with empty points. rega measured 0 grey in 36 mounts, * is in the finally, so the map CANNOT mount with empty points. A browser check measured 0 grey in 36 mounts,
* including a run with the prayer API held back 20s: the map was constructed AFTER the data, every * including a run with the prayer API held back 20s: the map was constructed AFTER the data, every
* time. So this gate guards a FUTURE refactor that drops that v-if data gate — not a live path. * time. So this gate guards a FUTURE refactor that drops that v-if data gate — not a live path.
* *
@ -27,7 +27,7 @@
* *
* ★ WHAT THIS ASSERTS, and it is a MODEL not a mock: the four arrival orders, against the real * ★ WHAT THIS ASSERTS, and it is a MODEL not a mock: the four arrival orders, against the real
* repaint rule. It fails if any order can leave the fill flat-grey while points exist. A browser * repaint rule. It fails if any order can leave the fill flat-grey while points exist. A browser
* cannot be run here (that is rega's seat), so the ordering is modelled and the SOURCE is asserted * cannot be run here (that is a browser check), so the ordering is modelled and the SOURCE is asserted
* to carry the repaint that makes the model's good cases reachable. * to carry the repaint that makes the model's good cases reachable.
*/ */
import { readFileSync } from 'node:fs' import { readFileSync } from 'node:fs'

View file

@ -85,14 +85,14 @@ const LINKED_KNOWN = {
// - both fixes are ancestors of main: 12a64702 (release the retained request on success — the FIRST // - both fixes are ancestors of main: 12a64702 (release the retained request on success — the FIRST
// press was losing the zone) and 67e9ede5 (the wall clears its memory when its pane goes inert). // press was losing the zone) and 67e9ede5 (the wall clears its memory when its pane goes inert).
// - the receiving side still registers its listener. // - the receiving side still registers its listener.
// - rega proved it end to end on BOTH engines at 810ffa26. // - a browser check proved it end to end on BOTH engines at 810ffa26.
} }
const MAPBOX_KNOWN = { const MAPBOX_KNOWN = {
// pplr-data-maps (new Experimental bundle, exp-shepherd) loads the Mapbox GEOCODER script even when // pplr-data-maps (new Experimental bundle) loads the Mapbox GEOCODER script even when
// the host asked for maplibre: the only api.mapbox.com request in the whole keyless gallery, both // the host asked for maplibre: the only api.mapbox.com request in the whole keyless gallery, both
// modes, measured 2026-09-25 while that tab was open. Routed via dx-shepherd; delete this entry in // modes, measured 2026-09-25 while that tab was open. Routed to its owner; delete this entry in
// the owner's fix commit so the gate enforces zero again. // the owner's fix commit so the gate enforces zero again.
'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js': 'exp-shepherd (pplr-data-maps loads the Mapbox geocoder under maplibre)', 'https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.0/mapbox-gl-geocoder.min.js': 'pplr-data-maps loads the Mapbox geocoder under maplibre',
// EMPTY ON PURPOSE. The one entry here — my-upg-100-list/upg-100-list.vue:1464 pinning // EMPTY ON PURPOSE. The one entry here — my-upg-100-list/upg-100-list.vue:1464 pinning
// mapbox-gl.css v3.3.0 regardless of engine — was fixed and the exemption deleted in the SAME // mapbox-gl.css v3.3.0 regardless of engine — was fixed and the exemption deleted in the SAME
// COMMIT as the fix, so main never had a window where the gate red-flagged its own known bug and // COMMIT as the fix, so main never had a window where the gate red-flagged its own known bug and

View file

@ -2,7 +2,7 @@
/** /**
* check-select-zone-race — "Show on map" works on the FIRST click, not the second. * check-select-zone-race — "Show on map" works on the FIRST click, not the second.
* *
* THE DEFECT (PRODUCTION-MATRIX:422, measured by rega): the composite's list pane dispatches * THE DEFECT (PRODUCTION-MATRIX:422, measured in a browser): the composite's list pane dispatches
* 'doxa:select-zone' at the map pane, but the map pane attaches its listener in onMounted and the * 'doxa:select-zone' at the map pane, but the map pane attaches its listener in onMounted and the
* pane mounts lazily. So the first click landed before any listener existed, did nothing, and only * pane mounts lazily. So the first click landed before any listener existed, did nothing, and only
* a SECOND click selected the zone. A control you must press twice reads as broken. * a SECOND click selected the zone. A control you must press twice reads as broken.
@ -19,7 +19,7 @@
* Delivery and arrival both succeeding still lost the selection without this. * Delivery and arrival both succeeding still lost the selection without this.
* *
* Source-level and headless: the race is a lifecycle ordering between two custom elements, which a * Source-level and headless: the race is a lifecycle ordering between two custom elements, which a
* DOM-less runner cannot stage and which the browser seat measures end to end (rega owns that). * DOM-less runner cannot stage and which the browser seat measures end to end.
* What this pins is that none of the three mechanisms quietly disappears. * What this pins is that none of the three mechanisms quietly disappears.
* *
* Exit 0 pass · 1 fail. * Exit 0 pass · 1 fail.
@ -58,7 +58,7 @@ ok(/addEventListener\('doxa:select-zone'/.test(LIST),
// ── 3. EFFECT: retained while unready, drained at the readiness signal ───────────────────────── // ── 3. EFFECT: retained while unready, drained at the readiness signal ─────────────────────────
// ⚠ THE SUBTLEST LAYER. Delivery and arrival can both succeed and the selection still not hold, // ⚠ THE SUBTLEST LAYER. Delivery and arrival can both succeed and the selection still not hold,
// because onLegendSelect returns early while the tapestry layer is down. rega read that state // because onLegendSelect returns early while the tapestry layer is down. A browser check read that state
// twice, 13 ms apart, with the correct id. // twice, 13 ms apart, with the correct id.
// ⚠ TWO DISTINCT RETAIN SITES, and a single /_pendingZoneId = zoneId/ match passes while ONE of // ⚠ TWO DISTINCT RETAIN SITES, and a single /_pendingZoneId = zoneId/ match passes while ONE of
// them is deleted — measured: removing the not-found retain left the gate green on the other. // them is deleted — measured: removing the not-found retain left the gate green on the other.

View file

@ -75,7 +75,7 @@ const HAND_VOCAB = {
* THE FAILURE: the hand list above held the phrases I had already FOUND. It could not see framing * THE FAILURE: the hand list above held the phrases I had already FOUND. It could not see framing
* I never enumerated, so when a SERIES=country poster still printed "Unengaged of West Africa", * I never enumerated, so when a SERIES=country poster still printed "Unengaged of West Africa",
* "Series: List of 100 UPG's · map 11 of 28" and a side box calling it "one of a 30-map set", this * "Series: List of 100 UPG's · map 11 of 28" and a side box calling it "one of a 30-map set", this
* gate said PASS — and I reported that PASS to three agents AS PROOF the render was clean. rega * gate said PASS — and that PASS was reported AS PROOF the render was clean. A review
* found it by reading the rendered page. A hand list makes a gate a LOWER BOUND on correctness * found it by reading the rendered page. A hand list makes a gate a LOWER BOUND on correctness
* while it looks like a guarantee. * while it looks like a guarantee.
* *

View file

@ -84,7 +84,7 @@ function shadowCssOf(script, filename) {
fold(node.arguments[0], acc) fold(node.arguments[0], acc)
// The style ID (second argument). A plain identifier is the only correct shape: a CSS rule moved // The style ID (second argument). A plain identifier is the only correct shape: a CSS rule moved
// past the first argument concatenates onto it ('@media(...){...}doxa-gospel-map'), which is valid // past the first argument concatenates onto it ('@media(...){...}doxa-gospel-map'), which is valid
// JS, keeps the build green and silently loses the rule (gospel-plugin's trap, COMMON.md). // JS, keeps the build green and silently loses the rule (a known trap, COMMON.md).
const lab = { text: '', literal: true, present: !!node.arguments[1] } const lab = { text: '', literal: true, present: !!node.arguments[1] }
if (node.arguments[1]) { const la = { css: '', complete: true, endsLiteral: false }; fold(node.arguments[1], la); lab.text = la.css; lab.literal = la.complete } if (node.arguments[1]) { const la = { css: '', complete: true, endsLiteral: false }; fold(node.arguments[1], la); lab.text = la.css; lab.literal = la.complete }
acc.label = lab acc.label = lab
@ -133,7 +133,7 @@ function scanCss(cssRaw) {
if (depth === 0 && ch === ';' && /[A-Za-z-]+\s*:/.test(chunk)) { orphans.push(chunk.trim().slice(0, 90)); chunk = '' } if (depth === 0 && ch === ';' && /[A-Za-z-]+\s*:/.test(chunk)) { orphans.push(chunk.trim().slice(0, 90)); chunk = '' }
i++ i++
} }
// ★ A SELECTOR THAT NEVER GETS A BLOCK (regions-plugin, 2026-09-29). Whatever is left in `chunk` // ★ A SELECTOR THAT NEVER GETS A BLOCK (2026-09-29). Whatever is left in `chunk`
// once the scan ends sits at depth 0 after the last '}' — i.e. text that looks like a selector and // once the scan ends sits at depth 0 after the last '}' — i.e. text that looks like a selector and
// has no declarations. That is how regions-list.vue shipped // has no declarations. That is how regions-list.vue shipped
// [lang="ru"] .rl-title, [lang="ru-RU"] .rl-title,regions-list // [lang="ru"] .rl-title, [lang="ru-RU"] .rl-title,regions-list
@ -198,7 +198,7 @@ ok(!r.dangling.length, 'trailing whitespace is not a dangling selector')
'an argument built partly from an imported constant is reported PARTIAL, not scanned as a whole sheet') 'an argument built partly from an imported constant is reported PARTIAL, not scanned as a whole sheet')
} }
{ {
// gospel-plugin's trap: a rule moved past the CSS argument concatenates onto the style id // The known trap: a rule moved past the CSS argument concatenates onto the style id
const trap = "useShadowStyles('.a{ color:red; }', '@media (max-width:1px){ .b{ color:red; } }' + 'doxa-gospel-map')" const trap = "useShadowStyles('.a{ color:red; }', '@media (max-width:1px){ .b{ color:red; } }' + 'doxa-gospel-map')"
const lab = shadowCssOf(trap, 'x.js')[0].label const lab = shadowCssOf(trap, 'x.js')[0].label
ok(lab.literal && !/^[a-z0-9-]+$/.test(lab.text), 'the style-id arm reads a rule concatenated onto the id as NOT a plain identifier') ok(lab.literal && !/^[a-z0-9-]+$/.test(lab.text), 'the style-id arm reads a rule concatenated onto the id as NOT a plain identifier')

View file

@ -4,7 +4,7 @@
* *
* WHY: a page's own reduced-motion reset never crosses a shadow boundary, so a component rendered inside a * WHY: a page's own reduced-motion reset never crosses a shadow boundary, so a component rendered inside a
* custom element's shadow root animates for a reader who asked for less motion unless the root carries its * custom element's shadow root animates for a reader who asked for less motion unless the root carries its
* own reset (regions-plugin, ProgressBar; ten legend and map-control components had none). * own reset (ProgressBar; ten legend and map-control components had none).
* *
* WHAT: (1) library/composables/useShadowStyles.js injects REDUCED_MOTION_CSS, a prefers-reduced-motion: * WHAT: (1) library/composables/useShadowStyles.js injects REDUCED_MOTION_CSS, a prefers-reduced-motion:
* reduce block, once per shadow root; (2) every library/components/**.vue that declares a transition or an * reduce block, once per shadow root; (2) every library/components/**.vue that declares a transition or an

View file

@ -174,7 +174,7 @@ if (debt.length) {
// Links that already exist must keep opening. // Links that already exist must keep opening.
eq('a legacy JSON hash still restores', parseView('#f=' + encodeURIComponent(JSON.stringify({ affinity_bloc: 'A008' }))).filters, { affinity_bloc: 'A008' }) eq('a legacy JSON hash still restores', parseView('#f=' + encodeURIComponent(JSON.stringify({ affinity_bloc: 'A008' }))).filters, { affinity_bloc: 'A008' })
eq('an old link with no q restores no search', parseView('#f=religion_family:Hinduism').search, '') eq('an old link with no q restores no search', parseView('#f=religion_family:Hinduism').search, '')
// QUERY MODE, requested by go-int: its board already shares as ?access=…&band=…, restored before // QUERY MODE, requested by the gospel-internet board: it already shares as ?access=…&band=…, restored before
// first render, and writes only on its own leaves so a partner's address bar is never touched. A // first render, and writes only on its own leaves so a partner's address bar is never touched. A
// hash-only composable would have given that board TWO URL states instead of replacing its glue. // hash-only composable would have given that board TWO URL states instead of replacing its glue.
// The host-param case is the one that matters: writing must not eat a query the page already owns. // The host-param case is the one that matters: writing must not eat a query the page already owns.
@ -184,7 +184,7 @@ if (debt.length) {
eq('query round-trips filters and search', parseQuery(serializeQuery({ access: 'low', band: ['2g', '3g'] }, 'bhutan'), ['access', 'band']), { filters: { access: 'low', band: ['2g', '3g'] }, search: 'bhutan' }) eq('query round-trips filters and search', parseQuery(serializeQuery({ access: 'low', band: ['2g', '3g'] }, 'bhutan'), ['access', 'band']), { filters: { access: 'low', band: ['2g', '3g'] }, search: 'bhutan' })
eq('query reads only declared facet keys', parseQuery('?access=low&panel=map', ['access']).filters, { access: 'low' }) eq('query reads only declared facet keys', parseQuery('?access=low&panel=map', ['access']).filters, { access: 'low' })
eq('query keeps a comma inside a value', parseQuery(serializeQuery({ language: ['Arabic, Sudanese'] }, ''), ['language']).filters, { language: 'Arabic, Sudanese' }) eq('query keeps a comma inside a value', parseQuery(serializeQuery({ language: ['Arabic, Sudanese'] }, ''), ['language']).filters, { language: 'Arabic, Sudanese' })
// ★ AN ALIAS MAP, because a param name already published cannot be renamed. go-int has shared // ★ AN ALIAS MAP, because a param name already published cannot be renamed. gospel-internet has shared
// ?access= against a bus key of access_level; insisting the two match would have renamed every // ?access= against a bus key of access_level; insisting the two match would have renamed every
// param on adoption and broken every link already sitting on a partner page — the exact harm this // param on adoption and broken every link already sitting on a partner page — the exact harm this
// card exists to prevent, delivered by the thing meant to prevent it. Driven with its real names. // card exists to prevent, delivered by the thing meant to prevent it. Driven with its real names.
@ -193,7 +193,7 @@ if (debt.length) {
eq('alias reads an already-shared link', parseQuery('?access=low&app=sms', ALIAS), { filters: { access_level: 'low', messaging: 'sms' }, search: '' }) eq('alias reads an already-shared link', parseQuery('?access=low&app=sms', ALIAS), { filters: { access_level: 'low', messaging: 'sms' }, search: '' })
eq('an undeclared bus key keeps its own name', serializeQuery({ country: 'BT' }, '', '', { facetKeys: ALIAS }), '?country=BT') eq('an undeclared bus key keeps its own name', serializeQuery({ country: 'BT' }, '', '', { facetKeys: ALIAS }), '?country=BT')
eq('a host param survives under an alias map', serializeQuery({ access_level: 'low' }, '', '?utm=x', { facetKeys: ALIAS }), '?utm=x&access=low') eq('a host param survives under an alias map', serializeQuery({ access_level: 'low' }, '', '?utm=x', { facetKeys: ALIAS }), '?utm=x&access=low')
// ★ BOTH SEPARATORS ON READ, ONE ON WRITE. go-int's board has always written band=yes|partial and // ★ BOTH SEPARATORS ON READ, ONE ON WRITE. The gospel-internet board has always written band=yes|partial and
// those links are on partner pages; splitting only on ',' read that back as the single value // those links are on partner pages; splitting only on ',' read that back as the single value
// "yes|partial" and matched no row, so adopting this composable would have broken every multi-value // "yes|partial" and matched no row, so adopting this composable would have broken every multi-value
// link they had already shared. Reserving `|` on write is what makes accepting it on read free. // link they had already shared. Reserving `|` on write is what makes accepting it on read free.

View file

@ -56,7 +56,7 @@ const MARKERS = [
id: 'legend-a11y-keys', id: 'legend-a11y-keys',
component: 'library/components/legends/SemanticTreeLegend.vue', component: 'library/components/legends/SemanticTreeLegend.vue',
marker: 'legend.a11y', marker: 'legend.a11y',
landed: 'regions, c4c60a4 and earlier', landed: 'c4c60a4 and earlier',
why: 'accessible names + aria-expanded on the legend row caret and clear chip', why: 'accessible names + aria-expanded on the legend row caret and clear chip',
}, },
{ {
@ -81,7 +81,7 @@ const MARKERS = [
// if it still compiles ensureEngineCSS without the guard; with neither shape it is NOT AFFECTED. // if it still compiles ensureEngineCSS without the guard; with neither shape it is NOT AFFECTED.
staleRe: /const ([\w$]+)=([\w$]+)\.getRootNode\(\);if\(!\(\1 instanceof ShadowRoot\)\)return/g, staleRe: /const ([\w$]+)=([\w$]+)\.getRootNode\(\);if\(!\(\1 instanceof ShadowRoot\)\)return/g,
marker: 'if (!element) return (ensureEngineCSS)', marker: 'if (!element) return (ensureEngineCSS)',
landed: 'studio, 2026-09-25', landed: '2026-09-25',
why: 'a map whose pane unmounts mid-initialise no longer throws "reading getRootNode" (check-map-unmount-mid-init)', why: 'a map whose pane unmounts mid-initialise no longer throws "reading getRootNode" (check-map-unmount-mid-init)',
}, },
{ {
@ -96,7 +96,7 @@ const MARKERS = [
builtRe: /cdn\.jsdelivr\.net\/npm\/@mapbox\/mapbox-gl-rtl-text/, builtRe: /cdn\.jsdelivr\.net\/npm\/@mapbox\/mapbox-gl-rtl-text/,
staleRe: /GL JS script to your page/g, staleRe: /GL JS script to your page/g,
marker: 'installRtlTextPlugin(gl) (MapLibre RTL shaping)', marker: 'installRtlTextPlugin(gl) (MapLibre RTL shaping)',
landed: 'rega, 2026-09-25', landed: '2026-09-25',
why: 'Arabic, Hebrew and Persian map labels are shaped on MapLibre (keyless) maps, not drawn as disconnected glyphs', why: 'Arabic, Hebrew and Persian map labels are shaped on MapLibre (keyless) maps, not drawn as disconnected glyphs',
}, },
{ {
@ -110,7 +110,7 @@ const MARKERS = [
builtRe: /\["coalesce",\["get","name:"/, builtRe: /\["coalesce",\["get","name:"/,
staleRe: /GL JS script to your page/g, staleRe: /GL JS script to your page/g,
marker: 'localizeBasemapLabels(map.value, readerLang()) (basemap labels localized)', marker: 'localizeBasemapLabels(map.value, readerLang()) (basemap labels localized)',
landed: 'rega, 2026-09-25', landed: '2026-09-25',
why: 'country, city and street names on the basemap render in the reader\'s language (Mapbox setLanguage, MapLibre name:<lang>)', why: 'country, city and street names on the basemap render in the reader\'s language (Mapbox setLanguage, MapLibre name:<lang>)',
}, },
] ]

View file

@ -1,7 +1,7 @@
#!/usr/bin/env node #!/usr/bin/env node
/** /**
* check-snippets-draw — every PUBLISHED embed snippet draws, pasted verbatim on a partner page on another origin. * check-snippets-draw — every PUBLISHED embed snippet draws, pasted verbatim on a partner page on another origin.
* (card GATE-EVERY-PUBLISHED-SNIPPET-DRAWS-A-MAP; ported from rega's probes snipgate.mjs and mbxcheck.mjs) * (card GATE-EVERY-PUBLISHED-SNIPPET-DRAWS-A-MAP; ported from the browser probes snipgate.mjs and mbxcheck.mjs)
* *
* WHY. The manifest's embed snippets are the one artefact a partner copies. check:snippet-engine proves what a * WHY. The manifest's embed snippets are the one artefact a partner copies. check:snippet-engine proves what a
* snippet SAYS (the engine it preloads, the provider it names); only a browser proves it DRAWS. The probe this is * snippet SAYS (the engine it preloads, the provider it names); only a browser proves it DRAWS. The probe this is

View file

@ -118,7 +118,7 @@ ok(/:only-child[^{]*\{[^}]*border-radius:\s*4px/.test(TOOLBAR), 'and a lone cont
// ── 2c. NO CHILD BRINGS ITS OWN OUTLINE ────────────────────────────────────────── // ── 2c. NO CHILD BRINGS ITS OWN OUTLINE ──────────────────────────────────────────
// ShareButton is not a .mcb -- it wraps its button in .share-wrap with border-radius:50% and a // ShareButton is not a .mcb -- it wraps its button in .share-wrap with border-radius:50% and a
// conic-gradient ring on ::before -- so the button rules missed it and it kept a round rainbow halo // conic-gradient ring on ::before -- so the button rules missed it and it kept a round rainbow halo
// inside the square panel. Found by rega at 390, not by me. // inside the square panel. Found by a browser check at 390, not by this gate.
ok(/\.map-toolbar--square \.share-wrap \{[^}]*border-radius:\s*0/.test(TOOLBAR), ok(/\.map-toolbar--square \.share-wrap \{[^}]*border-radius:\s*0/.test(TOOLBAR),
'the square panel flattens .share-wrap, which is a div and not a .mcb') 'the square panel flattens .share-wrap, which is a div and not a .mcb')
ok(/\.map-toolbar--square \.share-wrap::before \{[^}]*display:\s*none/.test(TOOLBAR), ok(/\.map-toolbar--square \.share-wrap::before \{[^}]*display:\s*none/.test(TOOLBAR),
@ -157,7 +157,7 @@ for (const [name, path] of CONSUMERS) {
} }
ok(optedIn === 0, `none of the ${CONSUMERS.length} MapToolbar consumers passes controls=, so every one renders exactly as before (${optedIn} opted in)`) ok(optedIn === 0, `none of the ${CONSUMERS.length} MapToolbar consumers passes controls=, so every one renders exactly as before (${optedIn} opted in)`)
// ── 6. THE GEOMETRY, for rega to confirm with eyes ────────────────────────────────────────────── // ── 6. THE GEOMETRY, for a browser check to confirm ──────────────────────────────────────────────
// A square toolbar is SHORTER, because the gaps become dividers. That is the one measurable knock-on: // A square toolbar is SHORTER, because the gaps become dividers. That is the one measurable knock-on:
// MapControlMenu's matchToolbar limits a menu to the toolbar column's height. // MapControlMenu's matchToolbar limits a menu to the toolbar column's height.
const BTN = 32, GAP_DESKTOP = 8, GAP_MOBILE = 6 const BTN = 32, GAP_DESKTOP = 8, GAP_MOBILE = 6
@ -169,7 +169,7 @@ for (const [name, , n] of CONSUMERS) {
} }
const five = { round: 5 * BTN + 4 * GAP_DESKTOP, square: 5 * BTN + 4 } const five = { round: 5 * BTN + 4 * GAP_DESKTOP, square: 5 * BTN + 4 }
ok(five.square < five.round, ok(five.square < five.round,
`a 5-control toolbar goes ${five.round}px -> ${five.square}px, so a matchToolbar menu gets ${five.round - five.square}px shorter -- rega should confirm the menu still fits its content`) `a 5-control toolbar goes ${five.round}px -> ${five.square}px, so a matchToolbar menu gets ${five.round - five.square}px shorter -- a browser check should confirm the menu still fits its content`)
console.log(` at mobile gap ${GAP_MOBILE}px a 5-control toolbar is ${5 * BTN + 4 * GAP_MOBILE}px round vs ${five.square}px square`) console.log(` at mobile gap ${GAP_MOBILE}px a 5-control toolbar is ${5 * BTN + 4 * GAP_MOBILE}px round vs ${five.square}px square`)
// ── 7. NEG CONTROLS ───────────────────────────────────────────────────────────────────────────── // ── 7. NEG CONTROLS ─────────────────────────────────────────────────────────────────────────────

View file

@ -31,7 +31,7 @@ function undeclaredLabels(manifest) {
// b.stage drags every experimental sibling in with it. Measured: cities-internet is a // b.stage drags every experimental sibling in with it. Measured: cities-internet is a
// production bundle whose connectivity-list and data-tables are EXPERIMENTAL, and // production bundle whose connectivity-list and data-tables are EXPERIMENTAL, and
// gospel-resources likewise for charts / data-tables / resources-list. That was 5 false // gospel-resources likewise for charts / data-tables / resources-list. That was 5 false
// violations out of 7 (regions-plugin caught it, 2026-09-28). // violations out of 7 (caught 2026-09-28).
// I had already moved off stageOf (bundle-keyed) once; moving to b.stage was the SAME // I had already moved off stageOf (bundle-keyed) once; moving to b.stage was the SAME
// mistake wearing a different field name. The profile's own stage is the only one that // mistake wearing a different field name. The profile's own stage is the only one that
// answers "is this row production". // answers "is this row production".

View file

@ -2,7 +2,7 @@
/** /**
* check-window-globals-released — a component that puts something on `window` must take it back. * check-window-globals-released — a component that puts something on `window` must take it back.
* *
* rega's heap snapshots (_qa-cycle/rega/leak-722ac7789) measured Simple Research and All People * Heap snapshots at 722ac7789 measured Simple Research and All People
* Groups mounted twice: 54.2 MB -> 65.1 MB, live Vue apps 6 -> 8. EVERY unmounted app stayed in * Groups mounted twice: 54.2 MB -> 65.1 MB, live Vue apps 6 -> 8. EVERY unmounted app stayed in
* memory, one copy per mount, and the retainers were all the same shape — a window key set during * memory, one copy per mount, and the retainers were all the same shape — a window key set during
* setup and never cleared: * setup and never cleared:
@ -82,7 +82,7 @@ for (const file of files) {
// - a PAGE-WIDE CACHE keyed off itself: `window.__x = window.__x || {}` is shared on purpose // - a PAGE-WIDE CACHE keyed off itself: `window.__x = window.__x || {}` is shared on purpose
// across every component on the page, so no single component may clear it. // across every component on the page, so no single component may clear it.
// What does retain is a closure (it captures the component) or a bare reference to one of its // What does retain is a closure (it captures the component) or a bare reference to one of its
// objects — which is every case rega's snapshots actually found. // objects — which is every case the snapshots actually found.
if (/^(true|false|-?\d|['"`])/.test(after)) continue if (/^(true|false|-?\d|['"`])/.test(after)) continue
if (new RegExp(`window\\s*\\.\\s*${name}\\b`).test(after)) continue if (new RegExp(`window\\s*\\.\\s*${name}\\b`).test(after)) continue
if (!set.has(name)) set.set(name, []) if (!set.has(name)) set.set(name, [])

View file

@ -176,7 +176,7 @@ function fixTree(root) {
} }
// The Research Gallery reads a BYTE-IDENTICAL copy of the manifest first when it runs on a foreign // The Research Gallery reads a BYTE-IDENTICAL copy of the manifest first when it runs on a foreign
// origin (generate-tree writes it; see doxa-maps-spa.vue). Repairing the root manifest alone left the // origin (generate-tree writes it; see doxa-maps-spa.vue). Repairing the root manifest alone left the
// copy with the OLD integrity, so an embedded gallery refused the rebuilt bundle (rega, 2026-09-25). // copy with the OLD integrity, so an embedded gallery refused the rebuilt bundle (measured 2026-09-25).
for (const rel of ['doxa-maps-spa/manifest.json']) { for (const rel of ['doxa-maps-spa/manifest.json']) {
const copy = join(root, rel) const copy = join(root, rel)
if (existsSync(copy) && readFileSync(copy, 'utf8') !== after) { if (existsSync(copy) && readFileSync(copy, 'utf8') !== after) {

View file

@ -8,8 +8,8 @@
"check:hardcoded-english": "report-only while its findings are triaged (97 user-visible English strings on 2026-09-29); chaining it now would hold every gate red. Its :self-test IS chained, so the matcher stays proven", "check:hardcoded-english": "report-only while its findings are triaged (97 user-visible English strings on 2026-09-29); chaining it now would hold every gate red. Its :self-test IS chained, so the matcher stays proven",
"check:shot-fonts": "checks that the MACHINE taking README screenshots can render emoji (fc-list): a property of that box, not of the product, so it runs before a screenshot session, not in the gates", "check:shot-fonts": "checks that the MACHINE taking README screenshots can render emoji (fc-list): a property of that box, not of the product, so it runs before a screenshot session, not in the gates",
"check:coverage-consistent": "the full run includes clause L, the age of the oldest link verification (45 days), which depends on the CALENDAR: chained, it would turn gates red on a date (first 2026-11-08) with no code change. Run it with the link re-verification; every other clause runs in gates:pre as check:coverage-consistent:chained", "check:coverage-consistent": "the full run includes clause L, the age of the oldest link verification (45 days), which depends on the CALENDAR: chained, it would turn gates red on a date (first 2026-11-08) with no code change. Run it with the link re-verification; every other clause runs in gates:pre as check:coverage-consistent:chained",
"check:snippets-draw": "AWAITING ITS FIRST BROWSER RUN (rega): the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded.", "check:snippets-draw": "AWAITING ITS FIRST BROWSER RUN: the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded.",
"check:grid-merge-defaults": "WAITS FOR ITS CODE: it asserts the defaults of three grid props (filtersToggle, backdrop, backdropFloor) that are not on development, because the grid merge (b1e839a33) has not landed. Chain it in gates:pre in the same landing as that merge.", "check:grid-merge-defaults": "WAITS FOR ITS CODE: it asserts the defaults of three grid props (filtersToggle, backdrop, backdropFloor) that are not on development, because the grid merge (b1e839a33) has not landed. Chain it in gates:pre in the same landing as that merge.",
"check:snippets-draw:mapbox": "AWAITING ITS FIRST BROWSER RUN (rega): the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded. Needs MAP_TOKEN." "check:snippets-draw:mapbox": "AWAITING ITS FIRST BROWSER RUN: the browser suite lists a gate only after it has run. Moves into verify-browser-all's GATES, and out of this list, once that run is recorded. Needs MAP_TOKEN."
} }
} }

View file

@ -2,13 +2,13 @@
/** /**
* generate-wagf-region-names.mjs — the WAGF region names, in the reader's language, from vendored CLDR. * generate-wagf-region-names.mjs — the WAGF region names, in the reader's language, from vendored CLDR.
* *
* ★ THE DEFECT (rega KA3, 3d64bcad, ?lang=ar). Big Picture's region chart labels its bars with the * ★ THE DEFECT (KA3, 3d64bcad, ?lang=ar). Big Picture's region chart labels its bars with the
* prayer API's ENGLISH region names — "Africa", "Asia", "Latin America & Caribbean" — on an Arabic page. * prayer API's ENGLISH region names — "Africa", "Asia", "Latin America & Caribbean" — on an Arabic page.
* They are not chrome and no catalogue held them: they are DATA labels, so every i18n gate was green * They are not chrome and no catalogue held them: they are DATA labels, so every i18n gate was green
* while the chart read English to every reader. The regions are a closed set of seven * while the chart read English to every reader. The regions are a closed set of seven
* (library/colors/wagf-region.js WAGF_REGIONS), which is what makes cataloguing them possible at all. * (library/colors/wagf-region.js WAGF_REGIONS), which is what makes cataloguing them possible at all.
* *
* ★ WHY GENERATED AND NOT HAND-AUTHORED. Ten locales x seven names is seventy strings I cannot write. * ★ WHY GENERATED AND NOT HAND-AUTHORED. Ten locales x seven names is seventy strings that cannot be hand-written reliably.
* Five of the seven ARE standard UN M49 macro-regions, so CLDR already names them in every locale we * Five of the seven ARE standard UN M49 macro-regions, so CLDR already names them in every locale we
* ship, and Node's own ICU is the same data library/utils/countryName.js already trusts for countries. * ship, and Node's own ICU is the same data library/utils/countryName.js already trusts for countries.
* Generating is what keeps a translation out of my hands: the strings are copied from CLDR, committed, * Generating is what keeps a translation out of my hands: the strings are copied from CLDR, committed,

View file

@ -167,8 +167,8 @@ async function shot(name, path, {
// ★ WAIT ON THE MAP ITSELF, NOT A CLOCK (2026-09-26). Every map a page (or any frame in it) creates is // ★ WAIT ON THE MAP ITSELF, NOT A CLOCK (2026-09-26). Every map a page (or any frame in it) creates is
// recorded, so the capture can wait until each visible one has its style and tiles loaded AND has // recorded, so the capture can wait until each visible one has its style and tiles loaded AND has
// drawn features on OUR layers (sources that are not the basemap's). A fixed 7 s wait shot the // drawn features on OUR layers (sources that are not the basemap's). A fixed 7 s wait shot the
// upg-100-list counter over a bare basemap: that leaf paints 9.5-16.5 s in (rega, 3 cold loads at // upg-100-list counter over a bare basemap: that leaf paints 9.5-16.5 s in (3 cold loads at
// fc2fa2e4). Same predicate rega measures with (_qa-cycle/rega/upglist-fc2fa2e4/upglist.mjs). // fc2fa2e4). The same predicate the browser check measures with.
await ctx.addInitScript(() => { await ctx.addInitScript(() => {
window.__SHOT_MAPS = [] window.__SHOT_MAPS = []
const patch = (x) => { const patch = (x) => {
@ -300,7 +300,7 @@ await shot('cap3-monolithic-research', `/${TREE_NAME}/doxa-research-map/research
// upg-100-mfe (the cap-4 composite) · upg-100-list (the list) · poster-wall. // upg-100-mfe (the cap-4 composite) · upg-100-list (the list) · poster-wall.
// There are no "list pages" — see the note below the second shot. ---- // There are no "list pages" — see the note below the second shot. ----
await shot('cap4-upg100-mfe', `/${TREE_NAME}/my-upg-100-list/upg-100-mfe/index.html`, { wait: 8000 }) await shot('cap4-upg100-mfe', `/${TREE_NAME}/my-upg-100-list/upg-100-mfe/index.html`, { wait: 8000 })
// wait 20000: this leaf PAINTS 9.5-16.5 s after load under swiftshader (rega, 3 cold loads at fc2fa2e4, // wait 20000: this leaf PAINTS 9.5-16.5 s after load under swiftshader (3 cold loads at fc2fa2e4,
// judged by rendered features on its own layers), and its "195 polys · 27/27 zones · 2107 pins" counter // judged by rendered features on its own layers), and its "195 polys · 27/27 zones · 2107 pins" counter
// appears BEFORE the paint, so a 7 s wait shot the counter over a bare basemap. The shot now waits on the // appears BEFORE the paint, so a 7 s wait shot the counter over a bare basemap. The shot now waits on the
// map itself (style + tiles + features drawn on our layers, see shot()); `wait` is only a fallback. // map itself (style + tiles + features drawn on our layers, see shot()); `wait` is only a fallback.

View file

@ -90,7 +90,7 @@ if (!profileModules) {
throw new Error('[ProfileLoader] No profileModules provided. Bundle entry must `app.provide("profileModules", import.meta.glob("./*.vue"))`') throw new Error('[ProfileLoader] No profileModules provided. Bundle entry must `app.provide("profileModules", import.meta.glob("./*.vue"))`')
} }
// ★ A ONE-PROFILE BUNDLE ANSWERS A BARE TAG (rega, DX Life drop-in, 2026-09-29). Every country page // ★ A ONE-PROFILE BUNDLE ANSWERS A BARE TAG (DX Life drop-in, 2026-09-29). Every country page
// rendered `⚠️ Profile "" not found.` where its map should be: the host wrote <doxa-country-map> // rendered `⚠️ Profile "" not found.` where its map should be: the host wrote <doxa-country-map>
// with no profile-config, profileName resolved to null, and the loader had nothing to load — while // with no profile-config, profileName resolved to null, and the loader had nothing to load — while
// the element sat in the DOM at full size with 0 console errors and 0 failed requests, which is why // the element sat in the DOM at full size with 0 console errors and 0 failed requests, which is why

View file

@ -65,7 +65,7 @@ useShadowStyles(`
.rm-side-backdrop.open { opacity:1;pointer-events:auto; } .rm-side-backdrop.open { opacity:1;pointer-events:auto; }
/* box-shadow lives on .open ONLY: when closed the drawer is translateX(100%) off /* box-shadow lives on .open ONLY: when closed the drawer is translateX(100%) off
the right edge, but a left-pointing shadow (-4px) bled back onto the map's right the right edge, but a left-pointing shadow (-4px) bled back onto the map's right
edge — the mystery "shadow on the right side" (coder staging QA 2026-06-25). */ edge — the mystery "shadow on the right side". */
.rm-side-drawer { position:absolute;top:0;right:0;bottom:0;width:340px;max-width:100vw;background:#171c23;color:#e7ebf0;border-left:1px solid #232a33;z-index:1600;transform:translateX(100%);transition:transform .22s ease;display:flex;flex-direction:column;font:13px -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; } .rm-side-drawer { position:absolute;top:0;right:0;bottom:0;width:340px;max-width:100vw;background:#171c23;color:#e7ebf0;border-left:1px solid #232a33;z-index:1600;transform:translateX(100%);transition:transform .22s ease;display:flex;flex-direction:column;font:13px -apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; }
.rm-side-drawer.open { transform:translateX(0); box-shadow:-4px 0 16px rgba(0,0,0,0.4); } .rm-side-drawer.open { transform:translateX(0); box-shadow:-4px 0 16px rgba(0,0,0,0.4); }
.rm-side-header { display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #232a33;flex:0 0 auto; } .rm-side-header { display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid #232a33;flex:0 0 auto; }

View file

@ -95,7 +95,7 @@ const labelW = computed(() => {
return Math.max(96, Math.min(260, Math.round((width.value || 640) * 0.34))) return Math.max(96, Math.min(260, Math.round((width.value || 640) * 0.34)))
}) })
// Room on the right for the printed value when a bar runs long. // Room on the right for the printed value when a bar runs long.
// The value column fits the LONGEST value label (rega, row 82: a fixed 74px clipped "10,160,000" at the edge // The value column fits the LONGEST value label (row 82: a fixed 74px clipped "10,160,000" at the edge
// of a 335px card). ~7px per character at the value font, plus the 8px gap and a margin; never under 74. // of a 335px card). ~7px per character at the value font, plus the 8px gap and a margin; never under 74.
const VALUE_GUTTER = computed(() => { const VALUE_GUTTER = computed(() => {
const longest = Math.max(0, ...rows.value.filter((r) => !r.unknown).map((r) => String(props.format(total(r))).length)) const longest = Math.max(0, ...rows.value.filter((r) => !r.unknown).map((r) => String(props.format(total(r))).length))
@ -199,7 +199,7 @@ useShadowStyles(
<!-- direction:ltr on the SVG: its geometry is left-to-right by construction (labels at x=0, values after <!-- direction:ltr on the SVG: its geometry is left-to-right by construction (labels at x=0, values after
the bar end). Under an inherited rtl, text-anchor start flips, so labels ran off the left edge and the bar end). Under an inherited rtl, text-anchor start flips, so labels ran off the left edge and
values back over their bars (rega, row 82, ar). Each label's own text still shapes as Arabic. --> values back over their bars (row 82, ar). Each label's own text still shapes as Arabic. -->
<svg v-else :width="width || 640" :height="svgH" role="img" :aria-label="ariaLabel" direction="ltr" style="direction:ltr"> <svg v-else :width="width || 640" :height="svgH" role="img" :aria-label="ariaLabel" direction="ltr" style="direction:ltr">
<defs> <defs>
<!-- Hatching means NOT MEASURED. It must not look like a small bar. --> <!-- Hatching means NOT MEASURED. It must not look like a small bar. -->

View file

@ -358,7 +358,7 @@ function addTrunks(rows) {
// the research palette remains free to change its own neutral without reaching in here. // the research palette remains free to change its own neutral without reaching in here.
const KIT_NEUTRAL_SWATCH = '#c4c8c0' const KIT_NEUTRAL_SWATCH = '#c4c8c0'
// A dashboard that supplies no `legend` gets an honest degraded rail — uncoloured swatches and raw // A dashboard that supplies no `legend` gets an honest degraded rail — uncoloured swatches and raw
// values — rather than a crash. That is the state go-int sees before it wires its own palette, and // values — rather than a crash. That is the state a new dashboard sees before it wires its own palette, and
// a half-configured dashboard should look plain, not broken. // a half-configured dashboard should look plain, not broken.
const legend = computed(() => (cfg && cfg.legend) || null) const legend = computed(() => (cfg && cfg.legend) || null)
const colorFor = (...a) => (legend.value?.colorFor ? legend.value.colorFor(...a) : KIT_NEUTRAL_SWATCH) const colorFor = (...a) => (legend.value?.colorFor ? legend.value.colorFor(...a) : KIT_NEUTRAL_SWATCH)

View file

@ -451,7 +451,7 @@ onBeforeUnmount(() => {
}) })
useShadowStyles(` useShadowStyles(`
/* ★ NOT ON PAPER (rega, RQ-RGPRINT on fc93c97e4). "Tap a coloured dot — someone still needs /* ★ NOT ON PAPER (measured on a print of fc93c97e4). "Tap a coloured dot — someone still needs
prayer" printed across the bottom of the map. It is an instruction for a finger, addressed to prayer" printed across the bottom of the map. It is an instruction for a finger, addressed to
a reader who is now holding a sheet of paper; on the page it reads as part of the map's own a reader who is now holding a sheet of paper; on the page it reads as part of the map's own
caption and is simply untrue there. */ caption and is simply untrue there. */

View file

@ -38,7 +38,7 @@ useShadowStyles(`
left:8px, width:380px) so the LegendDesktop card and the SemanticTreeLegend left:8px, width:380px) so the LegendDesktop card and the SemanticTreeLegend
sit at exactly the same position+size — no visible jump when the user sit at exactly the same position+size — no visible jump when the user
clicks a pin and the detail panel swaps in over the language-family legend clicks a pin and the detail panel swaps in over the language-family legend
(qa: 2026-05-02). */ */
.legend-container{position:absolute;top:60px;inset-inline-start:8px;width:var(--map-legend-width,380px);max-height:calc(100vh - 80px);background:#fff;border-radius:var(--map-radius,16px);box-shadow:0 2px 8px rgba(0,0,0,0.12),0 1px 3px rgba(0,0,0,0.08);z-index:1000;overflow:hidden;display:flex;flex-direction:column;transition:width 200ms cubic-bezier(0.2,0,0,1),max-height 200ms cubic-bezier(0.2,0,0,1),min-width 200ms cubic-bezier(0.2,0,0,1),border-radius 200ms linear,box-shadow 200ms linear;will-change:width,max-height;} .legend-container{position:absolute;top:60px;inset-inline-start:8px;width:var(--map-legend-width,380px);max-height:calc(100vh - 80px);background:#fff;border-radius:var(--map-radius,16px);box-shadow:0 2px 8px rgba(0,0,0,0.12),0 1px 3px rgba(0,0,0,0.08);z-index:1000;overflow:hidden;display:flex;flex-direction:column;transition:width 200ms cubic-bezier(0.2,0,0,1),max-height 200ms cubic-bezier(0.2,0,0,1),min-width 200ms cubic-bezier(0.2,0,0,1),border-radius 200ms linear,box-shadow 200ms linear;will-change:width,max-height;}
/* ── DESKTOP TYPE SCALE for the LegendRows-based card (2026-09-16: larger, more /* ── DESKTOP TYPE SCALE for the LegendRows-based card (2026-09-16: larger, more
readable desktop legends). The semantic-tree panel's scale lives in SemanticTreeLegend.vue readable desktop legends). The semantic-tree panel's scale lives in SemanticTreeLegend.vue
@ -124,7 +124,7 @@ useShadowStyles(`
/* Desktop slim rows — scoped to .legend-container so mobile (.legend-mobile-sheet) /* Desktop slim rows — scoped to .legend-container so mobile (.legend-mobile-sheet)
keeps its 44px tap targets. Original desktop rows were 36px tall with 6px keeps its 44px tap targets. Original desktop rows were 36px tall with 6px
pill padding — too clunky per user feedback 2026-04-27. */ pill padding — too clunky in QA review (2026-04-27). */
.legend-container .lrg-row, .legend-container .lrg-row,
.legend-container .lft-row{min-height:24px;} .legend-container .lft-row{min-height:24px;}
.legend-container .lrg-item-inner, .legend-container .lrg-item-inner,

View file

@ -55,7 +55,7 @@ useShadowStyles(`
single tight row, slightly reduced and translucent. The corners sit just above the sheet's single tight row, slightly reduced and translucent. The corners sit just above the sheet's
collapsed strip (48px), so the watermark is always visible when the legend is closed; an collapsed strip (48px), so the watermark is always visible when the legend is closed; an
opened sheet covers it. */ opened sheet covers it. */
/* ★ THE FAB SITS ON THE ATTRIBUTION TOO, not just the sheet (rega at 390x844, dx-life /* ★ THE FAB SITS ON THE ATTRIBUTION TOO, not just the sheet (measured at 390x844 on DX Life's
/research: the gallery's 44px vtl-fab at (12,788) is topmost over Cities' bottom-left /research: the gallery's 44px vtl-fab at (12,788) is topmost over Cities' bottom-left
attribution line "…oundaries · OpenFreeMap"). This rule already lifted the engine's bottom attribution line "…oundaries · OpenFreeMap"). This rule already lifted the engine's bottom
corners clear of the legend SHEET; it never knew about the circle, which lives in the LAYOUT corners clear of the legend SHEET; it never knew about the circle, which lives in the LAYOUT
@ -146,7 +146,7 @@ useShadowStyles(`
row's column-1 caret slot (LegendRows .lrg-title-caret-slot or row's column-1 caret slot (LegendRows .lrg-title-caret-slot or
SemanticTreeLegend .stl-tb-caret-slot). Vertical alignment with the SemanticTreeLegend .stl-tb-caret-slot). Vertical alignment with the
title text comes from the slot's align-items:center, so no absolute title text comes from the slot's align-items:center, so no absolute
positioning or padding offsets are needed (qa: 2026-05-02 iter-9). */ positioning or padding offsets are needed. */
/* Collapse caret — matches STL's .stl-collapse-btn exactly (22x20px, /* Collapse caret — matches STL's .stl-collapse-btn exactly (22x20px,
same bg/border/radius/hover as the desktop SemanticTreeLegend caret). */ same bg/border/radius/hover as the desktop SemanticTreeLegend caret). */
.mobile-collapse-caret{width:22px;height:20px;display:flex;align-items:center;justify-content:center;background:rgba(208,215,222,0.4);border:1px solid #d0d7de;border-radius:5px;cursor:pointer;color:#57606a;padding:0;transition:transform 0.3s ease-out,color 0.1s,background 0.1s,border-color 0.1s;transform-origin:center;outline:none;-webkit-tap-highlight-color:transparent;flex-shrink:0;} .mobile-collapse-caret{width:22px;height:20px;display:flex;align-items:center;justify-content:center;background:rgba(208,215,222,0.4);border:1px solid #d0d7de;border-radius:5px;cursor:pointer;color:#57606a;padding:0;transition:transform 0.3s ease-out,color 0.1s,background 0.1s,border-color 0.1s;transform-origin:center;outline:none;-webkit-tap-highlight-color:transparent;flex-shrink:0;}
@ -193,7 +193,7 @@ useShadowStyles(`
a DEFINITE height via flex:1 — height:100% on a non-flex parent with only a DEFINITE height via flex:1 — height:100% on a non-flex parent with only
flex:1 (no explicit height) falls back to auto, which made the inner flex:1 (no explicit height) falls back to auto, which made the inner
scrollable shorter than the sheet on tier-3 fullyOpen and left a white scrollable shorter than the sheet on tier-3 fullyOpen and left a white
gap at the bottom (qa: 2026-05-02 iter-10). gap at the bottom.
IMPORTANT: NO backticks anywhere in this comment block — it lives IMPORTANT: NO backticks anywhere in this comment block — it lives
inside a JS template literal (useShadowStyles) and a stray backtick inside a JS template literal (useShadowStyles) and a stray backtick
silently closes the string and blanks the entire mobile stylesheet. */ silently closes the string and blanks the entire mobile stylesheet. */
@ -217,7 +217,7 @@ useShadowStyles(`
breathing room on each side — caret doesn't hug the sheet edge. The breathing room on each side — caret doesn't hug the sheet edge. The
title row's subgrid offsets the title text so it begins right after title row's subgrid offsets the title text so it begins right after
the caret without overlap. Same widening on column 5 (right gutter) the caret without overlap. Same widening on column 5 (right gutter)
keeps the table visually balanced. qa: 2026-05-02 iter-9. */ keeps the table visually balanced. */
.legend-mobile-sheet .legend-row-group{--lrg-caret-col:24px;} .legend-mobile-sheet .legend-row-group{--lrg-caret-col:24px;}
.legend-content::-webkit-scrollbar{width:4px;} .legend-content::-webkit-scrollbar{width:4px;}
.legend-content::-webkit-scrollbar-track{background:transparent;} .legend-content::-webkit-scrollbar-track{background:transparent;}
@ -238,7 +238,7 @@ useShadowStyles(`
/* Fully-collapsed sheet (showing only "Legend" + caret) drops the /* Fully-collapsed sheet (showing only "Legend" + caret) drops the
16px top-corner radius — square edges look cleaner when the sheet 16px top-corner radius — square edges look cleaner when the sheet
is acting as a thin bottom footer. Rounded corners return when is acting as a thin bottom footer. Rounded corners return when
expanded (user feedback 2026-04-27). */ expanded. */
.legend-mobile-sheet.collapsed{box-shadow:0 -2px 8px rgba(0,0,0,0.15);height:48px !important;min-height:48px;border-top-left-radius:0;border-top-right-radius:0;padding-inline-start:var(--map-fab-beside,0px);} .legend-mobile-sheet.collapsed{box-shadow:0 -2px 8px rgba(0,0,0,0.15);height:48px !important;min-height:48px;border-top-left-radius:0;border-top-right-radius:0;padding-inline-start:var(--map-fab-beside,0px);}
/* The detail footer is placed absolutely, so the sheet's padding does not reach it: the same inset, directly. */ /* The detail footer is placed absolutely, so the sheet's padding does not reach it: the same inset, directly. */
.legend-mobile-sheet.collapsed .collapsed-detail-footer{inset-inline-start:var(--map-fab-beside,0px);} .legend-mobile-sheet.collapsed .collapsed-detail-footer{inset-inline-start:var(--map-fab-beside,0px);}
@ -263,7 +263,7 @@ useShadowStyles(`
.legend-mobile-sheet.collapsed .lft-title-row, .legend-mobile-sheet.collapsed .lft-title-row,
.legend-mobile-sheet.collapsed .stl-titlebar{padding:0 16px!important;display:flex;align-items:center;justify-content:center;gap:8px;height:48px;min-height:0!important;border-bottom:none!important;} .legend-mobile-sheet.collapsed .stl-titlebar{padding:0 16px!important;display:flex;align-items:center;justify-content:center;gap:8px;height:48px;min-height:0!important;border-bottom:none!important;}
/* line-height:1.4 (not 1) so the descender of 'g' in "Legend" / "Engagement /* line-height:1.4 (not 1) so the descender of 'g' in "Legend" / "Engagement
Progress" isn't clipped at the bottom — qa: 2026-05-02. */ Progress" isn't clipped at the bottom. */
.legend-mobile-sheet.collapsed .stl-tb-title, .legend-mobile-sheet.collapsed .stl-tb-title,
.legend-mobile-sheet.collapsed .lrg-title, .legend-mobile-sheet.collapsed .lrg-title,
.legend-mobile-sheet.collapsed .lrg-title-row .lrg-title, .legend-mobile-sheet.collapsed .lrg-title-row .lrg-title,
@ -294,8 +294,8 @@ useShadowStyles(`
.legend-mobile-sheet .stl-panel.closed{transform:none!important;opacity:1!important;pointer-events:auto!important;} .legend-mobile-sheet .stl-panel.closed{transform:none!important;opacity:1!important;pointer-events:auto!important;}
.legend-mobile-sheet .stl-inner{border:none!important;border-radius:0!important;box-shadow:none!important;background:transparent!important;} .legend-mobile-sheet .stl-inner{border:none!important;border-radius:0!important;box-shadow:none!important;background:transparent!important;}
/* Slim row height on mobile prayer/engagement/adoption tabs so they read /* Slim row height on mobile prayer/engagement/adoption tabs so they read
as thin/dense like desktop, not chunky touch-targets (qa: 2026-05-03 user as thin/dense like desktop, not chunky touch-targets (the mobile
feedback — "desktop legend rows are thinner, can you make mobile match"). rows match the thinner desktop rows).
Drops .lrg-item-inner min-height 36→28, vertical padding 6→2, and tightens Drops .lrg-item-inner min-height 36→28, vertical padding 6→2, and tightens
the .lrg-items row-gap 6→3 for the same compact-table feel. */ the .lrg-items row-gap 6→3 for the same compact-table feel. */
.legend-mobile-sheet .lrg-item-inner{min-height:28px!important;padding:2px 0!important;} .legend-mobile-sheet .lrg-item-inner{min-height:28px!important;padding:2px 0!important;}
@ -306,7 +306,7 @@ useShadowStyles(`
only sets flex:1 — without min-height:0, the flex item's default only sets flex:1 — without min-height:0, the flex item's default
min-height:auto = content height, so a long list grows past the parent min-height:auto = content height, so a long list grows past the parent
instead of scrolling, leaving white space below the panel on tier-3 instead of scrolling, leaving white space below the panel on tier-3
fullyOpen (qa: 2026-05-02 iter-11). LegendRows' .lrg-items already has fullyOpen. LegendRows' .lrg-items already has
min-height:0 in its base CSS, which is why iter-10 fixed Prayer/Engagement/ min-height:0 in its base CSS, which is why iter-10 fixed Prayer/Engagement/
Adoption but missed Language Families. */ Adoption but missed Language Families. */
.legend-mobile-sheet .stl-rows{min-height:0;} .legend-mobile-sheet .stl-rows{min-height:0;}
@ -315,7 +315,7 @@ useShadowStyles(`
/* (Removed iter-1 rule that was setting padding-left:0 here — it was /* (Removed iter-1 rule that was setting padding-left:0 here — it was
overriding the iter-5 padding-left:36px rule above, causing the caret overriding the iter-5 padding-left:36px rule above, causing the caret
to overlap the title text "Lan-guage Families" → "guage Families" to overlap the title text "Lan-guage Families" → "guage Families"
visible. qa: 2026-05-02.) Border-bottom color preserved. */ visible.) Border-bottom color preserved. */
.legend-mobile-sheet .stl-titlebar{border-bottom-color:rgba(0,0,0,0.08)!important;} .legend-mobile-sheet .stl-titlebar{border-bottom-color:rgba(0,0,0,0.08)!important;}
/* ── Mobile language-family tree overrides ── /* ── Mobile language-family tree overrides ──
@ -323,8 +323,7 @@ useShadowStyles(`
The desktop CSS makes the title-row a SUBGRID of .lft-items so the The desktop CSS makes the title-row a SUBGRID of .lft-items so the
UPGS / POPULATION column headers line up with the data rows' badges. UPGS / POPULATION column headers line up with the data rows' badges.
An explicit grid-template-columns on the title-row breaks subgrid An explicit grid-template-columns on the title-row breaks subgrid
inheritance and causes the misaligned headers we saw in user feedback inheritance and causes misaligned headers.
(2026-04-27).
Tap-area: rows are 44px tall (Apple HIG) so the row is comfortably Tap-area: rows are 44px tall (Apple HIG) so the row is comfortably
tappable; the in-row caret stays slim like desktop. The colored pill tappable; the in-row caret stays slim like desktop. The colored pill
keeps its desktop look. keeps its desktop look.
@ -448,7 +447,7 @@ const dataStore = inject('dataStore')
// Provided by research-map app profile so the mobile SemanticTreeLegend // Provided by research-map app profile so the mobile SemanticTreeLegend
// row-clicks fire the same dim/clear pipeline as the desktop instance // row-clicks fire the same dim/clear pipeline as the desktop instance
// (qa: 2026-05-03 iter-18 — without this, deselecting a language-family // (without this, deselecting a language-family
// row didn't restore the dimmed pins because mapStore.selectFamily was // row didn't restore the dimmed pins because mapStore.selectFamily was
// never updated by the mobile flow). // never updated by the mobile flow).
const onSemanticTreeSelect = inject('onSemanticTreeSelect', () => {}) const onSemanticTreeSelect = inject('onSemanticTreeSelect', () => {})
@ -984,7 +983,7 @@ onBeforeUnmount(() => { if (_creditTimer) { clearTimeout(_creditTimer); _creditT
detail mode. PeopleGroupDetail isn't designed to render at 48px, so detail mode. PeopleGroupDetail isn't designed to render at 48px, so
we render a footer-style title bar inline (caret + people-group we render a footer-style title bar inline (caret + people-group
name) that mimics the LegendRows / SemanticTreeLegend collapsed name) that mimics the LegendRows / SemanticTreeLegend collapsed
footer pattern. (qa: 2026-05-03 user feedback iter-16) --> footer pattern. -->
<div <div
v-if="legendState === 'collapsed' && legendMode === 'detail' && selectedPeopleGroup" v-if="legendState === 'collapsed' && legendMode === 'detail' && selectedPeopleGroup"
class="collapsed-detail-footer" class="collapsed-detail-footer"

View file

@ -67,7 +67,7 @@ useShadowStyles(`
part of the legend chrome. Dark is the STL default; .lpb-light mirrors the part of the legend chrome. Dark is the STL default; .lpb-light mirrors the
.stl-panel[data-theme="light"] palette. ── */ .stl-panel[data-theme="light"] palette. ── */
.lpb{position:relative;display:flex;align-items:center;gap:8px;min-width:0;width:100%;box-sizing:border-box;} .lpb{position:relative;display:flex;align-items:center;gap:8px;min-width:0;width:100%;box-sizing:border-box;}
/* Hit area vs paint (WCAG 2.5.8, rega at 390: every segment was a 10px-tall button). The track is 24px /* Hit area vs paint (WCAG 2.5.8, measured at 390: every segment was a 10px-tall button). The track is 24px
tall so each segment button is a 24px target, but it paints only a 10px band: the track's background is tall so each segment button is a 24px target, but it paints only a 10px band: the track's background is
a centred 10px strip and each segment clips its colour to its content box inside 7px of padding. The a centred 10px strip and each segment clips its colour to its content box inside 7px of padding. The
-7px margins keep the legend's layout exactly 10px tall, as before. */ -7px margins keep the legend's layout exactly 10px tall, as before. */
@ -199,7 +199,7 @@ function hide() { activeKey.value = null }
fills the span from the left and the has-prayer colour completes it. --> fills the span from the left and the has-prayer colour completes it. -->
<span class="lpb-span" :style="{ width: coveredPct + '%' }" :data-span-pct="coveredPct.toFixed(2)"> <span class="lpb-span" :style="{ width: coveredPct + '%' }" :data-span-pct="coveredPct.toFixed(2)">
<!-- An empty segment is not drawn: a zero-width button is an invisible tab stop, and it took the <!-- An empty segment is not drawn: a zero-width button is an invisible tab stop, and it took the
bar's rounded left end (rega). --> bar's rounded left end. -->
<button <button
v-for="seg in spanSegs.filter(s => segPctOfSpan(s) > 0)" :key="seg.key" v-for="seg in spanSegs.filter(s => segPctOfSpan(s) > 0)" :key="seg.key"
type="button" type="button"

View file

@ -179,7 +179,7 @@ useShadowStyles(`
Background darkens the row color by 28% (mix with #000) so white badge text Background darkens the row color by 28% (mix with #000) so white badge text
stays readable on bright rows like the engagement-tab "Engaged" green. The stays readable on bright rows like the engagement-tab "Engaged" green. The
previous mix used currentColor (which is white in light mode), so on bright previous mix used currentColor (which is white in light mode), so on bright
rows it LIGHTENED the badge — wrong direction. qa: 2026-05-02. */ rows it LIGHTENED the badge — wrong direction. */
.lrg-badge{display:flex;align-items:center;justify-content:center;} .lrg-badge{display:flex;align-items:center;justify-content:center;}
.lrg-badge-inner{display:inline-block;background:color-mix(in srgb, var(--lrg-item-color, #888), #000 28%);border-radius:8px;padding:2px 10px;font-size:11px;font-weight:600;font-family:'Courier New',monospace;white-space:nowrap;min-width:32px;text-align:center;} .lrg-badge-inner{display:inline-block;background:color-mix(in srgb, var(--lrg-item-color, #888), #000 28%);border-radius:8px;padding:2px 10px;font-size:11px;font-weight:600;font-family:'Courier New',monospace;white-space:nowrap;min-width:32px;text-align:center;}

Some files were not shown because too many files have changed in this diff Show more