Merge remote-tracking branch 'origin/development' into prayer-progress-plugin/pin-opacity
# Conflicts: # public/dx-maps/doxa-maps-spa/manifest.json # public/dx-maps/doxa-simple-map/doxa-simple-map.js # public/dx-maps/doxa-simple-map/doxa-simple-map/adoption/index.html # public/dx-maps/doxa-simple-map/doxa-simple-map/engagement/index.html # public/dx-maps/doxa-simple-map/doxa-simple-map/index.html # public/dx-maps/doxa-simple-map/doxa-simple-map/prayer/index.html # public/dx-maps/manifest.json
138
README.md
|
|
@ -6,7 +6,9 @@ Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His
|
|||
|
||||
## Bottom line up front
|
||||
|
||||
- **Why:** dx-maps exists to give disciples, churches and leaders what they need to bless the
|
||||
- **Why:** To Bless the Nations
|
||||
|
||||
- **Great Commission:** dx-maps exists to give disciples, churches and leaders what they need to bless the
|
||||
nations. We believe the deeper answer to the Great Commission is indigenous disciples, churches
|
||||
and leaders multiplying among their own peoples and blessing them, so that the Commission is
|
||||
fulfilled through love. The maps serve that in three ways: praying for the unreached peoples of
|
||||
|
|
@ -32,6 +34,12 @@ Made by Vibe Coders for Vibe Coders to prepare the way of the Lord and make His
|
|||
|
||||
Built with love, with the prayer that these maps bless the nations.
|
||||
|
||||
**A blessing for you.** These maps and posters are given to bless the people who bless the nations.
|
||||
We love you, and we are thankful for your work. We bless you in Jesus' name.
|
||||
|
||||
Every poster is in Posterverse (`plugin-designs/posterverse/`): the UPG 100 List, Country Posters,
|
||||
Poster Design and Wall of People Groups. They are all in the Plugin Studio (`bun run dev`) too.
|
||||
|
||||
# dx-maps — one build, maps that embed everywhere
|
||||
|
||||
**Write a map once in `plugin-designs/<your-map>/`, run one build, and drop it on any site** —
|
||||
|
|
@ -62,7 +70,7 @@ capabilities, three outputs, one shared library, one build.
|
|||
| **One app nests every map** — and each stays embeddable on its own | [capability 5](#capability-5--app-shell-the-research-gallery) · `bun run check:composite-reg` |
|
||||
| **5 capabilities, every one shippable in every output** | `bun run verify:5-caps` |
|
||||
| **Shared library, private plugins** — reuse without breaking each other | `bun run check:library-index` |
|
||||
| **One agent per plugin, one shepherd, one working `development`** | [agent swarm](#build-it-with-an-agent-swarm) |
|
||||
| **One owner per plugin, a second reader for every change, one landing at a time** | [agent swarm](#build-it-with-an-agent-swarm) |
|
||||
|
||||
## For vibe coders: build, open the Studio, embed a map
|
||||
|
||||
|
|
@ -169,11 +177,11 @@ the Research Gallery, which DX Life publishes as **Deep Research**. An eighth pr
|
|||
|
||||
| Capability | Example | iframe | Mobile app | JavaScript embed | WordPress / Disciple Tools | Desktop |
|
||||
|---|---|:-:|:-:|:-:|:-:|:-:|
|
||||
| **5 — app shell** | Research Gallery | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **4 — composite** | UPG-100 List | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **3 — multi-view** | research map | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **2 — presets** | Prayer Progress | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **1 — single element** | all people groups | ✅ | ✅ | ✅ | 🧪 | 🧪 |
|
||||
| **5 — app shell** | Research Gallery | ✅ | ✅ | ✅ | 🧪 | ✅ |
|
||||
| **4 — composite** | UPG-100 List | ✅ | ✅ | ✅ | 🧪 | ✅ |
|
||||
| **3 — multi-view** | research map | ✅ | ✅ | ✅ | 🧪 | ✅ |
|
||||
| **2 — presets** | Prayer Progress | ✅ | ✅ | ✅ | 🧪 | ✅ |
|
||||
| **1 — single element** | all people groups | ✅ | ✅ | ✅ | 🧪 | ✅ |
|
||||
|
||||
<sub>✅ works today · 🧪 proven, not yet in production. Every plugin builds into the same folder, and that folder is what all five methods ship — so a capability never limits where a plugin can go.</sub>
|
||||
|
||||
|
|
@ -425,9 +433,9 @@ put it* and *how a page shows it*.
|
|||
|
||||
| # | Output | What it is | Status |
|
||||
|---|---|---|---|
|
||||
| **1** | **The web folder** `public/dx-maps/` | every map, each embeddable on its own, all working together — 1205 files, 154 MiB (about 160 MB) | ✅ production |
|
||||
| **1** | **The web folder** `public/dx-maps/` | every map, each embeddable on its own, all working together — 1367 files, 163 MiB (about 170 MB) | ✅ production |
|
||||
| **2** | **Mobile app** | the same folder, wrapped as an Android app by Capacitor | ✅ built |
|
||||
| **3** | **Desktop app** | the same folder, wrapped as a desktop app by Tauri | 🧪 proven, not yet in production |
|
||||
| **3** | **Desktop app** | the same folder, wrapped as a desktop app by Tauri | ✅ works today |
|
||||
|
||||
### Output 1 — the web folder
|
||||
|
||||
|
|
@ -581,16 +589,16 @@ bun run phone # builds the app and installs it on a phone or emulator
|
|||
|
||||
</details>
|
||||
|
||||
### Output 3 — the desktop app 🧪 proven, not yet in production
|
||||
### Output 3 — the desktop app ✅ works today
|
||||
|
||||
**We got the JAMstack map app running as a desktop app.**
|
||||
|
||||

|
||||

|
||||
|
||||
**Rudder, the voice-to-text plugin, runs as its own desktop app too**, built with
|
||||
`bun run desktop:build rudder-voice` ([HOWTO-build-the-desktop-app](./documentation/HOWTO-build-the-desktop-app.md)).
|
||||
|
||||

|
||||

|
||||
|
||||
<details>
|
||||
<summary><b>More about the desktop app</b></summary>
|
||||
|
|
@ -687,11 +695,62 @@ your own page, set `"locale"` in its `profile-config`. Arabic reads right to lef
|
|||
## Build it with an agent swarm
|
||||
|
||||
**Many AI agents can build many plugins at once without breaking each other's work.** Every plugin
|
||||
has one agent building it, and one shepherd agent makes sure every agent does its job well. All the
|
||||
plugins live in one project with one shared library, and changes are added to `development`, the shared
|
||||
working version, one at a time. `main` is the production version, and it moves only when a release is published.
|
||||
has one agent that owns it, and changes are added to `development`, the shared working version, one
|
||||
at a time. `main` is the production version: it moves only when you publish a release.
|
||||
|
||||

|
||||
**Bring your own harness.** dx-maps works with whatever AI coding agent or agent harness you already
|
||||
use. The repo gives you the branch strategy below, the checks (`bun run gates:pre`) and the Plugin
|
||||
Studio; it does not ship a harness. Roles such as a coordinator that puts the landings in order, or a
|
||||
gate-keeper that reads each change before it lands, are jobs your harness gives to its agents.
|
||||
|
||||

|
||||
|
||||
<sub>[Open the diagram full size](documentation/readme-images/delegation-model.png): on a phone its small text needs it.</sub>
|
||||
|
||||
### Set up the branch strategy
|
||||
|
||||
1. **`main` is production.** It moves only when you publish a release (step 7).
|
||||
2. **`development` is the shared working branch.** Create it once, from `main`:
|
||||
```bash
|
||||
git fetch origin && git switch -c development origin/main && git push -u origin development
|
||||
```
|
||||
3. **One branch and worktree per plugin agent**, from `origin/development`, then install once in each:
|
||||
```bash
|
||||
git worktree add -b regions/work ../dx-maps-regions origin/development
|
||||
git worktree add -b upg100/work ../dx-maps-upg100 origin/development
|
||||
cd ../dx-maps-regions && bun install --frozen-lockfile
|
||||
```
|
||||
4. **One preview server per agent, each on its own port:**
|
||||
```bash
|
||||
bun x vite --port 5301 --strictPort # the next agent uses 5302, and so on
|
||||
```
|
||||
5. **Land one at a time, through a lock.** Catch up with `development`, run the checks, and push only
|
||||
if nothing fails (`gates:pre` exits non-zero when a check fails, so the push never runs then):
|
||||
```bash
|
||||
flock /tmp/dx-maps-development.lock sh -c '
|
||||
git fetch origin && git rebase origin/development &&
|
||||
bun run gates:pre && git push origin HEAD:development'
|
||||
```
|
||||
`flock` guards one machine. Agents on several machines need a shared lock, such as one your
|
||||
harness provides.
|
||||
6. **One agent owns the full build**; the others build only their own plugin:
|
||||
```bash
|
||||
BUILD_ALLOW=1 bun run build # the build owner: every plugin
|
||||
BUNDLE=<name> bun x vite build && bun run tree && bun run fix:sri # everyone else: one plugin
|
||||
```
|
||||
7. **Release:** fast-forward `main` to a `development` commit that passed the checks, tag it, and
|
||||
deploy from the tag:
|
||||
```bash
|
||||
git fetch origin && git switch main && git merge --ff-only origin/main
|
||||
git merge --ff-only <development-commit> # a commit that passed the checks
|
||||
bun run gates:pre # the checks must pass on exactly this commit
|
||||
git tag -a v1.0.0 -m "Release v1.0.0" && git push origin main v1.0.0
|
||||
```
|
||||
The tag's `public/dx-maps/` folder is the built site: deploy that
|
||||
([how to deploy](./documentation/DEPLOY.md)).
|
||||
|
||||
How to contribute, and the bar for shared code: [documentation/CONTRIBUTING.md](./documentation/CONTRIBUTING.md).
|
||||
Diagram source: [`delegation-model.svg`](./documentation/readme-images/src/delegation-model.svg).
|
||||
|
||||
<details>
|
||||
<summary><b>What problem does this solve? (in plain words)</b></summary>
|
||||
|
|
@ -702,15 +761,17 @@ map breaks at once**. And when several agents work in the same project at the sa
|
|||
each other — two agents edit the same file, one change quietly erases another, or half-finished work
|
||||
lands in the version everyone else is using.
|
||||
|
||||
So the project works like a busy kitchen with one head chef:
|
||||
So the project works like a busy kitchen:
|
||||
|
||||
1. **Each agent works in its own copy of the project** — its own counter, so nobody bumps anybody.
|
||||
2. **Before adding its work, each agent catches up**: it pulls in everyone else's latest work and
|
||||
fixes any clashes with its own. The agent fixes the clash, not the next person.
|
||||
3. **Only one agent adds work at a time.** They wait in a line (the *merge queue*), the checks run,
|
||||
and only then does the change go into `development`. `development` always works.
|
||||
4. **A shepherd agent watches everyone**: plugins built in the studio, shared parts really shared,
|
||||
and every change going through the line.
|
||||
1. **Each agent works in its own branch and copy of the project** — its own counter, so nobody bumps
|
||||
anybody. One owner per plugin, and one per shared file.
|
||||
2. **Only one change lands at a time.** Whoever holds the *landing lock* first catches up with
|
||||
everyone else's latest work, fixes any clash itself and runs every check; the change goes into
|
||||
`development` only if nothing fails. `development` always works.
|
||||
3. **Someone keeps order.** A coordinator can put the landings in order, and a gate-keeper can read
|
||||
each change by its content before it lands. Your harness gives these jobs to its agents.
|
||||
4. **`main` moves only when you publish a release**, after the checks pass on that exact version.
|
||||
A preview can follow `development`; the published site follows `main`.
|
||||
|
||||
That's how many plugins get built at the same time, in one project, without the maps breaking.
|
||||
|
||||
|
|
@ -727,31 +788,8 @@ That's how many plugins get built at the same time, in one project, without the
|
|||
| **worktree** | a private copy of the project for one agent to work in |
|
||||
| **merge conflict** | two changes touched the same lines; someone has to decide how to combine them |
|
||||
| **rebase** | catching your work up with everyone else's latest before adding it |
|
||||
| **merge queue** | the line agents wait in, so only one change goes into `main` at a time |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary><b>The commands (for the curious)</b></summary>
|
||||
|
||||
```bash
|
||||
# one private copy (worktree) + one agent per plugin
|
||||
git worktree add -b regions/work ../dx-maps-regions origin/development
|
||||
git worktree add -b upg100/work ../dx-maps-upg100 origin/development
|
||||
|
||||
# one preview server per agent, each on its own port
|
||||
bun x vite --port 5301 --strictPort
|
||||
|
||||
# add work through the line: one at a time, caught up first, checks must pass
|
||||
flock /tmp/dx-maps-development.lock sh -c '
|
||||
git fetch origin && git rebase origin/development &&
|
||||
bun run gates:pre && git push origin HEAD:development'
|
||||
```
|
||||
|
||||
One agent owns the full build (it rebuilds everyone's output); the others check just their own
|
||||
plugin with `BUNDLE=<name> bun x vite build`. How to contribute, and the bar for shared code:
|
||||
[documentation/CONTRIBUTING.md](./documentation/CONTRIBUTING.md). Diagram source:
|
||||
[`delegation-model.svg`](./documentation/readme-images/src/delegation-model.svg).
|
||||
| **landing lock** | only one change lands on `development` at a time; its holder catches up and runs every check first |
|
||||
| **tag** | a fixed name for one release commit, such as `v1.0.0`, so you can always deploy exactly it |
|
||||
|
||||
</details>
|
||||
|
||||
|
|
@ -773,7 +811,7 @@ and choose `"provider": "mapbox"` (see *No Mapbox token?* below). For Rudder's v
|
|||
`bash backend/rudder-engine/setup-runtime.sh` once; without Go, everything except voice works.
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://git.nuxtbase.app/doxa-open/doxa-maps.git # latest code only: seconds, not hundreds of MB of history
|
||||
git clone --depth 1 <your-repo-url> # latest code only: seconds, not hundreds of MB of history
|
||||
cd doxa-maps
|
||||
bun install --frozen-lockfile
|
||||
cp .env.example .env.local # optional: paste your pk. token after VITE_MAPBOX_TOKEN= (.env works too; .env.local wins)
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ func (e *engine) startWith(lang, device string, src *recorderProc, sink func(eve
|
|||
recMu.Unlock()
|
||||
writeMu.Lock()
|
||||
// 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.
|
||||
silence := make([]byte, 3200)
|
||||
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))
|
||||
emit(event{"type": "error", "message": string(msg)})
|
||||
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).
|
||||
committed = true
|
||||
e.logs.add("speech server committed the stream; reading its last words")
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
BIN
data/posters/india-an.webp
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
data/posters/india-ap.webp
Normal file
|
After Width: | Height: | Size: 221 KiB |
BIN
data/posters/india-ar.webp
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
data/posters/india-as.webp
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
data/posters/india-br.webp
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
data/posters/india-ch.webp
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
data/posters/india-ct.webp
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
data/posters/india-dh.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
data/posters/india-dl.webp
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
data/posters/india-ga.webp
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
data/posters/india-gj.webp
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
data/posters/india-hp.webp
Normal file
|
After Width: | Height: | Size: 112 KiB |
BIN
data/posters/india-hr.webp
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
data/posters/india-jh.webp
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
data/posters/india-jk.webp
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
data/posters/india-ka.webp
Normal file
|
After Width: | Height: | Size: 222 KiB |
BIN
data/posters/india-kl.webp
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
data/posters/india-la.webp
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
data/posters/india-ld.webp
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
data/posters/india-mh.webp
Normal file
|
After Width: | Height: | Size: 179 KiB |
BIN
data/posters/india-ml.webp
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
data/posters/india-mn.webp
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
data/posters/india-mp.webp
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
data/posters/india-or.webp
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
data/posters/india-pb.webp
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
data/posters/india-py.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
data/posters/india-rj.webp
Normal file
|
After Width: | Height: | Size: 169 KiB |
BIN
data/posters/india-sk.webp
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
data/posters/india-tg.webp
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
data/posters/india-tn.webp
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
data/posters/india-tr.webp
Normal file
|
After Width: | Height: | Size: 96 KiB |
BIN
data/posters/india-up.webp
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
data/posters/india-ut.webp
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
data/posters/india-wb.webp
Normal file
|
After Width: | Height: | Size: 146 KiB |
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -214,8 +214,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -342,8 +340,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1925" x2="1474" y2="1925" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@
|
|||
.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-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. */
|
||||
.hseries-dim2{font-family:Lato,'Segoe UI',system-ui,sans-serif;font-size:12px;
|
||||
fill:#A79E8A;letter-spacing:.02em}
|
||||
|
|
@ -204,8 +204,8 @@
|
|||
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
|
||||
estimator's error rather than sitting one glyph from overflow. */
|
||||
/* 13.5px bold. Brief: "this is too large, it should not span full width —
|
||||
there needs to be gaps." At 15px bold it measured 1396px of the 1448px band,
|
||||
/* 13.5px bold. At full width it was too large: the line needs gaps at both
|
||||
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
|
||||
each end, so the line reads as SET INSIDE the band rather than filling it.
|
||||
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"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<!-- THE MAP FLOATS. Brief: "a border around the map itself like a border line
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating."
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look like it is floating.
|
||||
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 —
|
||||
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
|
||||
and a shadow around the outside of that line that makes the map itself look
|
||||
like it is floating." The white mat was tried and rejected — the shadow-only
|
||||
<!-- THE MAP FLOATS: a border line around the map itself, with a shadow outside
|
||||
that line, makes the map look 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
|
||||
white where the lines crossed it. -->
|
||||
<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.
|
||||
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. -->
|
||||
<!-- 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
|
||||
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
|
||||
|
|
@ -332,8 +330,8 @@
|
|||
shadow now define that edge far more clearly, so the rule just floated as a
|
||||
stray horizontal line above the map.) -->
|
||||
<line x1="26" y1="1936" x2="1474" y2="1936" class="rule"/>
|
||||
<!-- FOOTER SCRIPTURE — TIER 1. Brief: "should be larger text bold and not two
|
||||
lines but one line with scripture reference on the right." It is one of the
|
||||
<!-- FOOTER SCRIPTURE — TIER 1: larger, bold, on ONE line, with the scripture
|
||||
reference on the right. It is one of the
|
||||
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
|
||||
right-aligned to the map's own edge, which is what keeps it on ONE line. -->
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ const refuse = (msg) => (PUBLIC
|
|||
|
||||
// fetch() hands back the body DECOMPRESSED, so the upstream Content-Encoding and Content-Length no longer describe
|
||||
// what is sent on: forwarding them made browsers try to gunzip plain HTML, and PocketBase's dashboard never rendered
|
||||
// (rega, 2026-09-29). And PocketBase redirects to its own root (/_ -> /_/), which is put back under the mount (/pb/_/).
|
||||
// (measured 2026-09-29). And PocketBase redirects to its own root (/_ -> /_/), which is put back under the mount (/pb/_/).
|
||||
// A backend path as the backend will read it: percent-decoded, runs of slashes collapsed. The admin gates run on
|
||||
// THIS form, so /pb/%5F/ (PocketBase decodes it to /_/) meets the same check as /pb/_/ (studio, 2026-09-29).
|
||||
// Encoded slashes or backslashes and undecodable input are refused: nothing legitimate sends them.
|
||||
|
|
@ -179,7 +179,7 @@ Bun.serve({
|
|||
const bp = backendPath(p)
|
||||
if (bp === null) return text('bad path', 400)
|
||||
if (!p.startsWith('/api-prod/api/') || !bp.startsWith('/api-prod/api/')) return text('only /api/ is public', 403)
|
||||
// As on /pb: the superuser and every _system collection stay closed. No public client uses them (dx-base, V2 review).
|
||||
// As on /pb: the superuser and every _system collection stay closed. No public client uses them (a platform security review).
|
||||
if (bp.startsWith('/api-prod/api/collections/_')) return text('for admins only', 403)
|
||||
return proxy(req, url, '/api-prod/', 8091)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
AGENT-BRIEF.md
|
||||
_archive
|
||||
data-private
|
||||
library/scripts/posters/snapshots/_granular-experiment.mjs.shelved
|
||||
plugin-designs/key-cities
|
||||
name:*field-access*
|
||||
name:*visa*.json
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ building ones run the live source with hot reload; the showing ones serve the **
|
|||
## 1. Your laptop — `bun run dev`
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://git.nuxtbase.app/doxa-open/doxa-maps.git
|
||||
git clone --depth 1 <your-repo-url>
|
||||
cd doxa-maps
|
||||
bun install --frozen-lockfile
|
||||
bun run dev # → http://localhost:5173/ is the Plugin Studio
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ The scripts these commands are grounded in (from this branch's `package.json`):
|
|||
"build": "bash internal/build-lock.sh",
|
||||
"build:unlocked":"bun run gates:pre && bun run library-index && bun run check:library-index && bun run clean && bun run token && bun run build:bundles && bun run tree && bun run gates:post",
|
||||
"gates:pre":"node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:doc-scripts",
|
||||
"gates:post":"node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed",
|
||||
"gates:post":"node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:script-flags check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:pin-dim-slider check:legend-source check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed",
|
||||
"build:bundles": "node internal/run-parallel.mjs --bundles", // the 22 vite builds, 4 at a time
|
||||
"preview": "bun x vite preview",
|
||||
"serve": "node internal/serve-strict.mjs", // 404s a directory, like production
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ served folder". The licence travels with the asset; the explanation travels with
|
|||
| `boundaries/gb-CHN-ADM1.geojson` | geoBoundaries gbOpen, boundaryID `CHN-ADM1-43563684` | **Public Domain**, licenceSource `commons.wikimedia.org`, year 2019 | recorded in `boundaries/ATTRIBUTION` | none — upstream records no attribution duty |
|
||||
| `boundaries/world-adm0*.geojson` (2 files, 162 countries) | geoBoundaries gbOpen, assembled per country (its features carry geoBoundaries' own `iso3`/`shapeGroup`/`shapeName`; corrected — this register first guessed Natural Earth) | **MIXED, and ESTABLISHED per country as of 2026-09-28**: 162 upstream licences. **32** duty-free (28 Public Domain + 4 CC0) · **65** attribution-only · **65 SHARE-ALIKE CLASS** (58 ODbL 1.0 + 7 explicit ShareAlike: AGO GAB GEO GIN MMR NER TUR) | **Yes** — the per-country table (licence + licenseSource + boundaryID + boundarySource, fetched verbatim from the geoBoundaries API, 162 of 162, 0 failures) ships at `plugin-designs/my-upg-100-list/data/adm0/PROVENANCE.json`. `boundaries/ATTRIBUTION` references it by path rather than copying it | **OPEN, legal — not a measurement**: whether a redistributed boundary file is a "Derivative Database" under ODbL is unanswered, and half the duty-bearing countries turn on it. It needs an owner's decision on data licensing |
|
||||
| `people-groups/*` (3 files, 8 MB) | peoplegroups.org | **not established** | no terms ship | **OPEN, judgment** — redistribution terms unknown |
|
||||
| `pplr/*` (6 files, 16 MB) | **PPLR ArcGIS Online org** `services2.arcgis.com/S4ydGgujXcif36k3`, aggregating IMB (peoplegroups.org), Joshua Project, the ROP registry, and carrying WCD (World Christian Database) IDs. Built from a local 78 MB extract not present in this repo — evidence from exp-shepherd | **UNRESOLVED.** `aCountry` and `aIMBPeople` publish no `copyrightText`, description or licence (queried `f=json`) | nothing ships | **OPEN, judgment** — redistribution not established. WCD is a proprietary Brill database, so even the IDs need a check |
|
||||
| `pplr/*` (6 files, 16 MB) | **PPLR ArcGIS Online org** `services2.arcgis.com/S4ydGgujXcif36k3`, aggregating IMB (peoplegroups.org), Joshua Project, the ROP registry, and carrying WCD (World Christian Database) IDs. Built from a local 78 MB extract not present in this repo | **UNRESOLVED.** `aCountry` and `aIMBPeople` publish no `copyrightText`, description or licence (queried `f=json`) | nothing ships | **OPEN, judgment** — redistribution not established. WCD is a proprietary Brill database, so even the IDs need a check |
|
||||
| `tables/*` (304 files, 36 MB) | derived by `library/scripts/research/fetch-*.mjs` from several upstream APIs | **varies, not established per table** | nothing ships; there is no source or licence doc in `tables/` | **OPEN, judgment** — the largest gap by volume |
|
||||
| `images/worldmap.svg` | recorded in `images/PROVENANCE` | see that file | the PROVENANCE file ships and explains why no licence file sits beside it | none claimed; reasoning recorded rather than assumed |
|
||||
| `previews/*` (378 files) | our own poster previews | ours | n/a | none |
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ the app is rebuilt.**
|
|||
| `build-gallery.mjs` | `out/index.html` — browsable preview grid, sectioned by region. |
|
||||
| `build-hd-gallery.mjs` | `out/hd.html` — full-resolution review + copy-to-clipboard. |
|
||||
| `export-public.mjs` | Copies PNGs into region folders OUTSIDE the repo (see below). |
|
||||
| `snapshots/` | Frozen template versions. v5 is the approved current; v1–v4 are the lineage. |
|
||||
|
||||
## Environment
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
# needs-eyes — partner-embed fixtures
|
||||
|
||||
Under the team's Playwright policy (2026-09-25) only **rega** runs a browser on the VPS, and a
|
||||
maintainer's laptop is the other pair of eyes. So a check that can only be seen has to be *openable by
|
||||
someone else* — not a construct that lives inside one agent's gate process.
|
||||
|
||||
These pages are generated, not hand-written:
|
||||
A check that can only be seen has to be *openable by someone else*, not a construct that lives inside
|
||||
one gate's process. These pages are generated on demand and are not committed; write them with:
|
||||
|
||||
```
|
||||
node internal/check-embedded-rtl.mjs --emit
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
|
||||
<style>html,body{margin:0}#w{height:900px}</style></head>
|
||||
<body><h1 id="partner-heading">Partner page</h1><div id="w"></div>
|
||||
<!-- A REAL PARTNER EMBED SETS THIS. The generated leaf pages bake window.MAP_APP_API_URL, and
|
||||
without it every data fetch resolves same-origin against the partner and fails, so the wall
|
||||
renders zero cards and the locale has nothing to translate. The first run of this gate
|
||||
omitted it and reported "never mounted" — a harness gap, not the RTL defect. -->
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<script src="http://127.0.0.1:47395/dx-maps/my-upg-100-list/my-upg-100-list.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
var el = document.createElement("my-upg-100-list-map");
|
||||
el.setAttribute('profile-config', JSON.stringify({ profile: "poster-wall",
|
||||
assetsBase: "http://127.0.0.1:47395/dx-maps/assets" }));
|
||||
el.style.cssText = 'display:block;width:100%;height:900px';
|
||||
document.getElementById('w').appendChild(el);
|
||||
</script></body></html>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
|
||||
<style>html,body{margin:0}#w{height:900px}</style></head>
|
||||
<body><h1 id="partner-heading">Partner page</h1><div id="w"></div>
|
||||
<!-- A REAL PARTNER EMBED SETS THIS. The generated leaf pages bake window.MAP_APP_API_URL, and
|
||||
without it every data fetch resolves same-origin against the partner and fails, so the wall
|
||||
renders zero cards and the locale has nothing to translate. The first run of this gate
|
||||
omitted it and reported "never mounted" — a harness gap, not the RTL defect. -->
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<script src="http://127.0.0.1:47395/dx-maps/my-upg-100-list/my-upg-100-list.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
var el = document.createElement("my-upg-100-list-map");
|
||||
el.setAttribute('profile-config', JSON.stringify({ profile: "poster-wall", locale: 'ar',
|
||||
assetsBase: "http://127.0.0.1:47395/dx-maps/assets" }));
|
||||
el.style.cssText = 'display:block;width:100%;height:900px';
|
||||
document.getElementById('w').appendChild(el);
|
||||
</script></body></html>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en"><head><meta charset="utf-8"><title>partner</title>
|
||||
<style>html,body{margin:0}#w{height:900px}</style></head>
|
||||
<body><h1 id="partner-heading">Partner page</h1><div id="w"></div>
|
||||
<!-- A REAL PARTNER EMBED SETS THIS. The generated leaf pages bake window.MAP_APP_API_URL, and
|
||||
without it every data fetch resolves same-origin against the partner and fails, so the wall
|
||||
renders zero cards and the locale has nothing to translate. The first run of this gate
|
||||
omitted it and reported "never mounted" — a harness gap, not the RTL defect. -->
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<script src="http://127.0.0.1:47395/dx-maps/my-upg-100-list/my-upg-100-list.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
var el = document.createElement("my-upg-100-list-map");
|
||||
el.setAttribute('profile-config', JSON.stringify({ profile: "upg-100-mfe", locale: 'ar',
|
||||
assetsBase: "http://127.0.0.1:47395/dx-maps/assets" }));
|
||||
el.style.cssText = 'display:block;width:100%;height:900px';
|
||||
document.getElementById('w').appendChild(el);
|
||||
</script></body></html>
|
||||
|
Before Width: | Height: | Size: 816 KiB After Width: | Height: | Size: 858 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 38 KiB |
|
|
@ -1,4 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="1500" height="2030" viewBox="0 0 1500 2030" font-family="Inter, 'Helvetica Neue', Arial, sans-serif">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1500" height="2086" viewBox="0 0 1500 2086" font-family="Inter, 'Helvetica Neue', Arial, sans-serif">
|
||||
<defs>
|
||||
<marker id="aA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#2563eb"/></marker>
|
||||
<marker id="aS" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#7c3aed"/></marker>
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
<path id="agL" d="M 194,850 A 556,556 0 0 1 1306,850"/>
|
||||
<path id="mqL" d="M 679,884 A 71,71 0 0 0 821,884"/>
|
||||
</defs>
|
||||
<rect width="1500" height="2030" fill="#ffffff"/>
|
||||
<text x="750" y="54" text-anchor="middle" font-size="34" font-weight="800" fill="#0f172a">How dx-maps is built: one studio, one agent per plugin, one shepherd</text>
|
||||
<rect width="1500" height="2086" fill="#ffffff"/>
|
||||
<text x="750" y="54" text-anchor="middle" font-size="34" font-weight="800" fill="#0f172a">How dx-maps is built: one owner per plugin, one landing at a time</text>
|
||||
<circle cx="750" cy="850" r="540" fill="none" stroke="#2563eb" stroke-width="1.6" stroke-dasharray="3 7" opacity=".75"/>
|
||||
<circle cx="750" cy="850" r="355" fill="none" stroke="#15803d" stroke-width="2.2" opacity=".85"/>
|
||||
<circle cx="750" cy="850" r="606" fill="none" stroke="#7c3aed" stroke-width="2" stroke-dasharray="8 7" opacity=".6"/>
|
||||
|
|
@ -29,8 +29,8 @@
|
|||
<circle cx="750" cy="850" r="120" fill="#fef2f2" stroke="#dc2626" stroke-width="3" stroke-dasharray="10 6"/>
|
||||
<path id="mqT" d="M 646,850 A 104,104 0 0 1 854,850" fill="none"/>
|
||||
<path id="mqB" d="M 644,850 A 106,106 0 0 0 856,850" fill="none"/>
|
||||
<text font-size="13" font-weight="800" fill="#b91c1c" letter-spacing="1.6"><textPath href="#mqT" startOffset="50%" text-anchor="middle">MERGE QUEUE · GUARD</textPath></text>
|
||||
<text font-size="12" font-weight="700" fill="#b91c1c" letter-spacing="1.2"><textPath href="#mqB" startOffset="50%" text-anchor="middle">ONE AGENT AT A TIME</textPath></text>
|
||||
<text font-size="13" font-weight="800" fill="#b91c1c" letter-spacing="1.6"><textPath href="#mqT" startOffset="50%" text-anchor="middle">LANDING LOCK</textPath></text>
|
||||
<text font-size="12" font-weight="700" fill="#b91c1c" letter-spacing="1.2"><textPath href="#mqB" startOffset="50%" text-anchor="middle">ONE LANDING AT A TIME</textPath></text>
|
||||
<rect x="617" y="846" width="26" height="20" rx="4" fill="#dc2626"/>
|
||||
<path d="M 622,846 v -6 a 8,8 0 0 1 16,0 v 6" fill="none" stroke="#dc2626" stroke-width="3.5"/>
|
||||
<circle cx="630" cy="856" r="3" fill="#fff"/>
|
||||
|
|
@ -38,9 +38,9 @@
|
|||
<path d="M 862,846 v -6 a 8,8 0 0 1 16,0 v 6" fill="none" stroke="#dc2626" stroke-width="3.5"/>
|
||||
<circle cx="870" cy="856" r="3" fill="#fff"/>
|
||||
<circle cx="750" cy="850" r="72" fill="#166534" stroke="#14532d" stroke-width="3"/>
|
||||
<text x="750" y="852" text-anchor="middle" font-size="34" font-weight="800" fill="#fff">main</text>
|
||||
<text x="750" y="876" text-anchor="middle" font-size="12.5" fill="#dcfce7">the one branch</text>
|
||||
<text x="750" y="892" text-anchor="middle" font-size="12.5" fill="#dcfce7">always green</text>
|
||||
<text x="750" y="850" text-anchor="middle" font-size="18" font-weight="800" fill="#fff">development</text>
|
||||
<text x="750" y="872" text-anchor="middle" font-size="12.5" fill="#dcfce7">every change</text>
|
||||
<text x="750" y="888" text-anchor="middle" font-size="12.5" fill="#dcfce7">lands here</text>
|
||||
<text font-size="16" font-weight="800" fill="#15803d" letter-spacing="3"><textPath href="#plL" startOffset="50%" text-anchor="middle">PLUGINS</textPath></text>
|
||||
<text font-size="16" font-weight="800" fill="#2563eb" letter-spacing="3"><textPath href="#agL" startOffset="18%" text-anchor="middle">AGENTS</textPath></text>
|
||||
<line x1="862.1" y1="579.3" x2="822.7" y2="674.5" stroke="#15803d" stroke-width="1.4" opacity=".45"/>
|
||||
|
|
@ -117,67 +117,83 @@
|
|||
<circle cx="543.4" cy="351.1" r="40" fill="#ecfeff" stroke="#0891b2" stroke-width="2.5"/>
|
||||
<text x="543.4" y="349.1" text-anchor="middle" font-size="12" font-weight="700" fill="#155e75">roaming</text>
|
||||
<text x="543.4" y="364.1" text-anchor="middle" font-size="12" font-weight="700" fill="#155e75">agent</text>
|
||||
<circle cx="750" cy="244" r="60" fill="#f5f3ff" stroke="#7c3aed" stroke-width="3.5"/>
|
||||
<text x="750" y="251" text-anchor="middle" font-size="19" font-weight="800" fill="#4c1d95">shepherd</text>
|
||||
<text x="834" y="192" font-size="17" fill="#4c1d95">one shepherd makes sure all plugin agents</text>
|
||||
<text x="834" y="216" font-size="17" fill="#4c1d95">are doing their job building the plugins well</text>
|
||||
<text x="750" y="1502" text-anchor="middle" font-size="20" font-weight="800" fill="#1e3a8a">each plugin has one agent responsible for building it</text>
|
||||
<rect x="36" y="110" width="300" height="280" rx="14" fill="#f8fafc" stroke="#e2e8f0" stroke-width="2"/>
|
||||
<circle cx="64" cy="144" r="12" fill="#166534" stroke="#14532d" stroke-width="2.5"/>
|
||||
<text x="88" y="149" font-size="15" fill="#334155">main — the one branch</text>
|
||||
<circle cx="64" cy="180" r="12" fill="#fef2f2" stroke="#dc2626" stroke-width="2.5"/>
|
||||
<text x="88" y="185" font-size="15" fill="#334155">merge queue — one at a time</text>
|
||||
<circle cx="64" cy="216" r="12" fill="#fffbeb" stroke="#d97706" stroke-width="2.5"/>
|
||||
<text x="88" y="221" font-size="15" fill="#334155">Plugin Studio — the one repo</text>
|
||||
<circle cx="64" cy="252" r="12" fill="#f0fdf4" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="88" y="257" font-size="15" fill="#334155">plugin (badge = capability)</text>
|
||||
<circle cx="64" cy="288" r="12" fill="#ecfeff" stroke="#0891b2" stroke-width="2.5"/>
|
||||
<text x="88" y="293" font-size="15" fill="#334155">Experimental shelf</text>
|
||||
<circle cx="64" cy="324" r="12" fill="#eff6ff" stroke="#2563eb" stroke-width="2.5"/>
|
||||
<text x="88" y="329" font-size="15" fill="#334155">agent — builds one plugin</text>
|
||||
<circle cx="64" cy="360" r="12" fill="#f5f3ff" stroke="#7c3aed" stroke-width="2.5"/>
|
||||
<text x="88" y="365" font-size="15" fill="#334155">shepherd — oversees all</text>
|
||||
<rect x="60" y="1536" width="1380" height="218" rx="20" fill="#fef2f2" stroke="#fca5a5" stroke-width="2"/>
|
||||
<text x="750" y="1578" text-anchor="middle" font-size="22" font-weight="800" fill="#7f1d1d">How main is protected — so many plugins can be built at once</text>
|
||||
<rect x="80.0" y="1602" width="410" height="130" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="116.0" cy="1638" r="20" fill="#dc2626"/>
|
||||
<text x="116.0" y="1645" text-anchor="middle" font-size="20" font-weight="800" fill="#fff">1</text>
|
||||
<text x="148.0" y="1645" font-size="20" font-weight="800" fill="#0f172a">Work in your own copy</text>
|
||||
<text x="102.0" y="1686" font-size="16" fill="#475569">each agent builds its own plugin,</text>
|
||||
<text x="102.0" y="1710" font-size="16" fill="#475569">in its own private copy of the project</text>
|
||||
<text x="517.5" y="1677" text-anchor="middle" font-size="30" font-weight="800" fill="#dc2626">→</text>
|
||||
<rect x="545.0" y="1602" width="410" height="130" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="581.0" cy="1638" r="20" fill="#dc2626"/>
|
||||
<text x="581.0" y="1645" text-anchor="middle" font-size="20" font-weight="800" fill="#fff">2</text>
|
||||
<text x="613.0" y="1645" font-size="20" font-weight="800" fill="#0f172a">Catch up, fix the clashes</text>
|
||||
<text x="567.0" y="1686" font-size="16" fill="#475569">pull in everyone else's latest work and</text>
|
||||
<text x="567.0" y="1710" font-size="16" fill="#475569">fix any clashes before adding yours</text>
|
||||
<text x="982.5" y="1677" text-anchor="middle" font-size="30" font-weight="800" fill="#dc2626">→</text>
|
||||
<rect x="1010.0" y="1602" width="410" height="130" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="1046.0" cy="1638" r="20" fill="#dc2626"/>
|
||||
<text x="1046.0" y="1645" text-anchor="middle" font-size="20" font-weight="800" fill="#fff">3</text>
|
||||
<text x="1078.0" y="1645" font-size="20" font-weight="800" fill="#0f172a">Add changes one at a time</text>
|
||||
<text x="1032.0" y="1686" font-size="16" fill="#475569">the queue lets ONE agent in at a time;</text>
|
||||
<text x="1032.0" y="1710" font-size="16" fill="#475569">the checks must pass, main stays working</text>
|
||||
<circle cx="690" cy="248" r="56" fill="#f5f3ff" stroke="#7c3aed" stroke-width="3.5"/>
|
||||
<text x="690" y="254" text-anchor="middle" font-size="14" font-weight="800" fill="#4c1d95">coordinator</text>
|
||||
<circle cx="812" cy="248" r="56" fill="#f1f5f9" stroke="#334155" stroke-width="3.5"/>
|
||||
<text x="812" y="254" text-anchor="middle" font-size="14" font-weight="800" fill="#1e293b">gate-keeper</text>
|
||||
<text x="884" y="192" font-size="17" fill="#4c1d95">a coordinator puts the landings in order;</text>
|
||||
<text x="884" y="216" font-size="17" fill="#1e293b">a gate-keeper checks each one by its content</text>
|
||||
<text x="750" y="1502" text-anchor="middle" font-size="20" font-weight="800" fill="#1e3a8a">one owner per plugin, in its own branch · a second reader for every change</text>
|
||||
<rect x="36" y="110" width="304" height="280" rx="14" fill="#f8fafc" stroke="#e2e8f0" stroke-width="2"/>
|
||||
<circle cx="64" cy="142" r="11" fill="#166534" stroke="#14532d" stroke-width="2.5"/>
|
||||
<text x="88" y="147" font-size="15" fill="#334155">development — shared branch</text>
|
||||
<circle cx="64" cy="174" r="11" fill="#fef2f2" stroke="#dc2626" stroke-width="2.5"/>
|
||||
<text x="88" y="179" font-size="15" fill="#334155">landing lock — one at a time</text>
|
||||
<circle cx="64" cy="206" r="11" fill="#fffbeb" stroke="#d97706" stroke-width="2.5"/>
|
||||
<text x="88" y="211" font-size="15" fill="#334155">Plugin Studio — the one repo</text>
|
||||
<circle cx="64" cy="238" r="11" fill="#f0fdf4" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="88" y="243" font-size="15" fill="#334155">plugin (badge = capability)</text>
|
||||
<circle cx="64" cy="270" r="11" fill="#ecfeff" stroke="#0891b2" stroke-width="2.5"/>
|
||||
<text x="88" y="275" font-size="15" fill="#334155">Experimental shelf</text>
|
||||
<circle cx="64" cy="302" r="11" fill="#eff6ff" stroke="#2563eb" stroke-width="2.5"/>
|
||||
<text x="88" y="307" font-size="15" fill="#334155">agent — owns one plugin</text>
|
||||
<circle cx="64" cy="334" r="11" fill="#f5f3ff" stroke="#7c3aed" stroke-width="2.5"/>
|
||||
<text x="88" y="339" font-size="15" fill="#334155">coordinator — orders landings</text>
|
||||
<circle cx="64" cy="366" r="11" fill="#f1f5f9" stroke="#334155" stroke-width="2.5"/>
|
||||
<text x="88" y="371" font-size="15" fill="#334155">gate-keeper — checks landings</text>
|
||||
<rect x="60" y="1536" width="1380" height="232" rx="20" fill="#fef2f2" stroke="#fca5a5" stroke-width="2"/>
|
||||
<text x="750" y="1578" text-anchor="middle" font-size="22" font-weight="800" fill="#7f1d1d">How a change lands — one at a time, so many plugins can be built at once</text>
|
||||
<rect x="80.0" y="1600" width="305" height="148" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="114.0" cy="1634" r="18" fill="#dc2626"/>
|
||||
<text x="114.0" y="1641" text-anchor="middle" font-size="19" font-weight="800" fill="#fff">1</text>
|
||||
<text x="142.0" y="1641" font-size="19" font-weight="800" fill="#0f172a">Own branch</text>
|
||||
<text x="100.0" y="1680" font-size="15" fill="#475569">each owner builds its plugin</text>
|
||||
<text x="100.0" y="1702" font-size="15" fill="#475569">in its own branch and copy;</text>
|
||||
<text x="100.0" y="1724" font-size="15" fill="#475569">one owner per shared file</text>
|
||||
<text x="405.0" y="1682" text-anchor="middle" font-size="26" font-weight="800" fill="#dc2626">→</text>
|
||||
<rect x="425.0" y="1600" width="305" height="148" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="459.0" cy="1634" r="18" fill="#dc2626"/>
|
||||
<text x="459.0" y="1641" text-anchor="middle" font-size="19" font-weight="800" fill="#fff">2</text>
|
||||
<text x="487.0" y="1641" font-size="19" font-weight="800" fill="#0f172a">Second reader</text>
|
||||
<text x="445.0" y="1680" font-size="15" fill="#475569">another agent reads every</text>
|
||||
<text x="445.0" y="1702" font-size="15" fill="#475569">change by its content</text>
|
||||
<text x="445.0" y="1724" font-size="15" fill="#475569">before it may land</text>
|
||||
<text x="750.0" y="1682" text-anchor="middle" font-size="26" font-weight="800" fill="#dc2626">→</text>
|
||||
<rect x="770.0" y="1600" width="305" height="148" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="804.0" cy="1634" r="18" fill="#dc2626"/>
|
||||
<text x="804.0" y="1641" text-anchor="middle" font-size="19" font-weight="800" fill="#fff">3</text>
|
||||
<text x="832.0" y="1641" font-size="19" font-weight="800" fill="#0f172a">One at a time</text>
|
||||
<text x="790.0" y="1680" font-size="15" fill="#475569">in the lock: merge development,</text>
|
||||
<text x="790.0" y="1702" font-size="15" fill="#475569">rebuild, run every check;</text>
|
||||
<text x="790.0" y="1724" font-size="15" fill="#475569">push only if nothing fails</text>
|
||||
<text x="1095.0" y="1682" text-anchor="middle" font-size="26" font-weight="800" fill="#dc2626">→</text>
|
||||
<rect x="1115.0" y="1600" width="305" height="148" rx="14" fill="#fff" stroke="#dc2626" stroke-width="2"/>
|
||||
<circle cx="1149.0" cy="1634" r="18" fill="#dc2626"/>
|
||||
<text x="1149.0" y="1641" text-anchor="middle" font-size="19" font-weight="800" fill="#fff">4</text>
|
||||
<text x="1177.0" y="1641" font-size="19" font-weight="800" fill="#0f172a">Promote to main</text>
|
||||
<text x="1135.0" y="1680" font-size="15" fill="#475569">main is production: it moves</text>
|
||||
<text x="1135.0" y="1702" font-size="15" fill="#475569">only when the owner approves,</text>
|
||||
<text x="1135.0" y="1724" font-size="15" fill="#475569">after full checks on that version</text>
|
||||
<line x1="750" y1="956" x2="750" y2="1786" stroke="#334155" stroke-width="2.5" stroke-dasharray="2 5" opacity=".0"/>
|
||||
<rect x="60" y="1780" width="1380" height="230" rx="20" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2"/>
|
||||
<text x="750" y="1824" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">main → ONE build → one web folder → three outputs</text>
|
||||
<rect x="80.0" y="1852" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="290.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 1</text>
|
||||
<text x="290.0" y="1906" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">The web folder</text>
|
||||
<text x="290.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">iframe · JS embed · WordPress (for fun)</text>
|
||||
<rect x="190.0" y="1946" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||
<text x="290.0" y="1964" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">PRODUCTION</text>
|
||||
<rect x="540.0" y="1852" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="750.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 2</text>
|
||||
<text x="750.0" y="1906" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">Mobile app</text>
|
||||
<text x="750.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">the same folder, wrapped by Capacitor</text>
|
||||
<rect x="650.0" y="1946" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||
<text x="750.0" y="1964" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">BUILT</text>
|
||||
<rect x="1000.0" y="1852" width="420" height="130" rx="14" fill="#fff" stroke="#d97706" stroke-width="2.5"/>
|
||||
<text x="1210.0" y="1874" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 3</text>
|
||||
<text x="1210.0" y="1906" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">Desktop app</text>
|
||||
<text x="1210.0" y="1932" text-anchor="middle" font-size="15" fill="#475569">the same folder, wrapped by Tauri</text>
|
||||
<rect x="1110.0" y="1946" width="200" height="26" rx="13" fill="#d97706" opacity=".13"/>
|
||||
<text x="1210.0" y="1964" text-anchor="middle" font-size="12" font-weight="800" fill="#d97706">PROVEN · NOT YET LIVE</text>
|
||||
<rect x="60" y="1812" width="1380" height="254" rx="20" fill="#f8fafc" stroke="#cbd5e1" stroke-width="2"/>
|
||||
<text x="750" y="1856" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">main → ONE build → one web folder → three outputs</text>
|
||||
<text x="750" y="1882" text-anchor="middle" font-size="16" fill="#475569">the dev preview follows development · the published site follows main</text>
|
||||
<rect x="80.0" y="1908" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="290.0" y="1930" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 1</text>
|
||||
<text x="290.0" y="1962" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">The web folder</text>
|
||||
<text x="290.0" y="1988" text-anchor="middle" font-size="15" fill="#475569">iframe · JS embed · WordPress (for fun)</text>
|
||||
<rect x="190.0" y="2002" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||
<text x="290.0" y="2020" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">PRODUCTION</text>
|
||||
<rect x="540.0" y="1908" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="750.0" y="1930" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 2</text>
|
||||
<text x="750.0" y="1962" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">Mobile app</text>
|
||||
<text x="750.0" y="1988" text-anchor="middle" font-size="15" fill="#475569">the same folder, wrapped by Capacitor</text>
|
||||
<rect x="650.0" y="2002" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||
<text x="750.0" y="2020" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">BUILT</text>
|
||||
<rect x="1000.0" y="1908" width="420" height="130" rx="14" fill="#fff" stroke="#15803d" stroke-width="2.5"/>
|
||||
<text x="1210.0" y="1930" text-anchor="middle" font-size="13" font-weight="800" fill="#64748b">OUTPUT 3</text>
|
||||
<text x="1210.0" y="1962" text-anchor="middle" font-size="22" font-weight="800" fill="#0f172a">Desktop app</text>
|
||||
<text x="1210.0" y="1988" text-anchor="middle" font-size="15" fill="#475569">the same folder, wrapped by Tauri</text>
|
||||
<rect x="1110.0" y="2002" width="200" height="26" rx="13" fill="#15803d" opacity=".13"/>
|
||||
<text x="1210.0" y="2020" text-anchor="middle" font-size="12" font-weight="800" fill="#15803d">WORKING</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
|
@ -158,7 +158,7 @@ the IMB / peoplegroups.org dataset (never a hand-kept roster). Aliases name them
|
|||
|
||||
## Open points found while writing this down
|
||||
|
||||
Each is a defect or a decision, routed through dx-shepherd.
|
||||
Each is a defect or a decision, still open.
|
||||
|
||||
1. **`profile-config.tokenUrl` has no reader.** It is written by the studio and the gallery and
|
||||
read by nothing. The reader's own URL is also never forwarded as `tokenUrl`, only as the token
|
||||
|
|
|
|||
|
|
@ -356,8 +356,6 @@ The measurements behind the rules above, kept for whoever needs the reasoning.
|
|||
- **Undocumented gates (2026-09-22).** 26 gates lived outside the build and only nine were listed; two
|
||||
unlisted ones had been failing for two sessions. A standalone gate nobody knows about does not run —
|
||||
which is why the table above is the list to run before publishing.
|
||||
- **`check:dxlife`.** It exits 2 ("clone not found") on any machine without a sibling DX-life clone — a
|
||||
deliberate third value that `bun run` reports as a failure. It is not in the build, so a clone still builds.
|
||||
- **Screenshot distinctness.** Two README slots once showed the same view under two captions: the shot
|
||||
chased a "Next" control that did not exist inside a `try/catch`, and the PNGs differed by 0.16% render
|
||||
noise, so no checksum caught them.
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ export function backendDevPlugin(root, env = process.env) {
|
|||
name: 'dx-backend-dev',
|
||||
apply: 'serve',
|
||||
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
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ done
|
|||
|
||||
# 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
|
||||
# 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
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -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 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
|
||||
// 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] } }
|
||||
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.
|
||||
|
|
@ -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))
|
||||
// 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
|
||||
// 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 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))
|
||||
|
|
|
|||
|
|
@ -1,101 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-backlog-refs.mjs — a card may not point at a card that does not exist.
|
||||
*
|
||||
* THE DEFECT THIS CATCHES. `depends_on` is the board's readiness mechanism: a card is ready when
|
||||
* its dependencies are done. A dependency naming a card that is NOT IN THE BACKLOG can never be
|
||||
* done, so the card can never become ready — and nothing says so. It reads as ordinary blocked
|
||||
* work forever.
|
||||
*
|
||||
* MEASURED 2026-09-24, three dangling references across the builder backlog:
|
||||
* IDEA-SLIM-MAP-SEARCHBAR-HOW-DO-FILTERS-FIT <- BUILD-PEOPLE-GROUP-SEARCH-…depends_on
|
||||
* IDEA-REGIONS-DASHBOARD-REVERSE-ENGINEER-DXLIFE <- two regions cards' depends_on
|
||||
* PRINCIPLE-MAPS-ARE-SECONDARY-LISTS-AND-GRIDS-ARE-PRIMARY <- two cards' parent
|
||||
* The first one matters most: that card is status:todo and waiting on something unfindable.
|
||||
* The last is cited by the loop prompt itself as an open card ("PRINCIPLE-MA"), so a reader is
|
||||
* being pointed at work that has no record.
|
||||
*
|
||||
* ★ SEVERITY IS NOT UNIFORM, so this gate grades it. A dangling `depends_on` on an OPEN card is a
|
||||
* FAILURE — that card is unreachable. A dangling `parent`, or a dangling anything on a card that is
|
||||
* already done, is REPORTED but does not fail: losing a parent is a filing problem, not a blocked
|
||||
* one, and failing on it would only teach people to delete the reference rather than restore the
|
||||
* card. Naming the difference is the point.
|
||||
*
|
||||
* ⚠ IT ONLY CHECKS REFERENCES THAT LOOK LIKE CARD IDS. Fields like `parent` also legitimately hold
|
||||
* prose in some cards ("the regions epic"), and treating those as broken links would drown the
|
||||
* real ones. The shape is SCREAMING-KEBAB, 9+ chars — the convention every id in this board uses.
|
||||
*
|
||||
* Usage: node internal/check-backlog-refs.mjs
|
||||
* SKIPs when _qa-cycle is absent (it lives outside every repo, by design).
|
||||
*/
|
||||
import { readFileSync, existsSync } from 'node:fs'
|
||||
import { resolve, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url))
|
||||
const QA = process.env.QA_CYCLE || resolve(HERE, '..', '..', '_qa-cycle')
|
||||
if (!existsSync(QA)) {
|
||||
console.log('BACKLOG-REFS: SKIP — _qa-cycle not present (expected in CI; it is in no repo).')
|
||||
console.log(' This gate reads the AGENTIC BACKLOGS, which live outside the repo and are not')
|
||||
console.log(' cloned with it. Absence is expected; it is NOT a pass.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const PHASES = ['01-ideation-research', '02-builder-cycle', '03-deployment-cycle']
|
||||
const REF_FIELDS = ['parent', 'depends_on', 'blocks', 'supersedes', 'supersedes_mechanism_of']
|
||||
// The id convention every card in this board follows. Prose in a `parent` field must NOT match.
|
||||
const ID_SHAPE = /^[A-Z][A-Z0-9-]{8,}$/
|
||||
|
||||
const cards = []
|
||||
for (const phase of PHASES) {
|
||||
const p = join(QA, phase, 'backlog.json')
|
||||
if (!existsSync(p)) continue
|
||||
const j = JSON.parse(readFileSync(p, 'utf8'))
|
||||
for (const c of j.cards || []) cards.push({ ...c, _phase: phase })
|
||||
}
|
||||
if (!cards.length) {
|
||||
console.log('BACKLOG-REFS: SKIP — no cards found in any phase backlog.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const ids = new Set(cards.map((c) => c.id))
|
||||
const OPEN = new Set(['todo', 'doing', 'in-progress', 'blocked'])
|
||||
|
||||
const blocking = [] // dangling depends_on on an OPEN card — the card can never become ready
|
||||
const noted = [] // everything else dangling — filing debt, reported not failed
|
||||
|
||||
for (const c of cards) {
|
||||
const f = c.fields || {}
|
||||
for (const key of REF_FIELDS) {
|
||||
const raw = f[key] !== undefined ? f[key] : c[key]
|
||||
if (!raw) continue
|
||||
for (const ref of (Array.isArray(raw) ? raw : [raw])) {
|
||||
if (typeof ref !== 'string' || !ID_SHAPE.test(ref)) continue
|
||||
if (ids.has(ref)) continue
|
||||
const entry = { from: c.id, key, ref, status: c.status || '(none)' }
|
||||
if (key === 'depends_on' && OPEN.has(String(c.status || '').toLowerCase())) blocking.push(entry)
|
||||
else noted.push(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`BACKLOG-REFS: ${cards.length} card(s) across ${PHASES.length} backlog(s), `
|
||||
+ `${REF_FIELDS.length} reference field(s) checked.`)
|
||||
if (noted.length) {
|
||||
console.log(` ${noted.length} dangling reference(s) that do NOT block anything:`)
|
||||
for (const n of noted) console.log(` ${n.from} [${n.status}] .${n.key} -> ${n.ref}`)
|
||||
console.log(' Filing debt: a lost parent or a stale link on a finished card. Restore the card')
|
||||
console.log(' or drop the reference — but do not delete it just to quiet this line.')
|
||||
}
|
||||
|
||||
if (blocking.length) {
|
||||
console.error(`\nBACKLOG-REFS: FAIL — ${blocking.length} OPEN card(s) depend on a card that does not exist.`)
|
||||
for (const b of blocking) {
|
||||
console.error(` ✗ ${b.from} [${b.status}] depends_on ${b.ref}`)
|
||||
}
|
||||
console.error('\n A dependency that is not in the backlog can never be done, so these cards can')
|
||||
console.error(' never become ready — and nothing says so. They read as ordinary blocked work.')
|
||||
console.error(' Fix: restore the missing card, or remove the dependency if it is obsolete.')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('BACKLOG-REFS: PASS — every open card\'s dependencies name a card that exists.')
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-backlog-status-enum.mjs — a card's `status` must be a machine-readable enum, not prose.
|
||||
*
|
||||
* THE DEFECT THIS CATCHES, and it was MY OWN reporting error for an entire flight. Cards were being
|
||||
* written with explanatory statuses:
|
||||
*
|
||||
* "done — reconciled: big-picture + dashboard-plus-grid are current, unified-research-dashboard…"
|
||||
* "blocked — Steps 1+3 DONE and re-verified 2026-09-22 at 19/19 bundles…"
|
||||
* "ready"
|
||||
*
|
||||
* The prose is genuinely valuable — several of those strings carry measurements — but it made
|
||||
* `status` unqueryable. Every count I reported used `status !== 'done'`, which is FALSE for
|
||||
* `"done — reconciled: …"`, so finished cards kept being counted as open.
|
||||
*
|
||||
* MEASURED 2026-09-22 across the three phase backlogs: 130 of 395 cards carried a prose status, and
|
||||
* 71 of those were ALREADY DONE. Reported open was 176; the real number was 105. The P0 figure I had
|
||||
* been quoting every tick was 21; it was actually 14.
|
||||
*
|
||||
* ★ WHY THIS IS WORSE THAN AN ORDINARY OFF-BY-N. A backlog is a decision instrument — it answers
|
||||
* "what is left" and "is the remaining work human-gated". Inflating the open count makes a drained
|
||||
* board look stalled, which pushes toward manufacturing work rather than holding honestly. And the
|
||||
* error is self-concealing: every individual card reads correctly to a human, because the prose says
|
||||
* exactly what is true. Only the AGGREGATE is wrong.
|
||||
*
|
||||
* THE FIX PRESERVES THE PROSE. Migration moved it to `fields.status_detail` and normalised `status`
|
||||
* to the enum; 130/130 preserved, 0 lost. The explanation was never the problem — putting it in the
|
||||
* field that tooling reads was.
|
||||
*
|
||||
* WHAT IT ASSERTS: every card's `status` is ONE WORD from a closed set — the five workflow
|
||||
* states plus four terminal ones (superseded / deferred / decided / verified-already-true).
|
||||
* The point is not the particular words: it is that `status` stays machine-readable so the
|
||||
* open-count is right. Prose belongs in fields.status_detail.
|
||||
*
|
||||
* It deliberately does NOT require `status_detail` to exist. Most cards need no explanation, and
|
||||
* demanding one would turn a real check into paperwork that gets satisfied with filler.
|
||||
*
|
||||
* NEGATIVE-TESTED: setting one card's status back to "done — reconciled: …" fails by name.
|
||||
*/
|
||||
import { readFileSync, existsSync } from 'node:fs'
|
||||
import { resolve, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const QA = join(ROOT, '../_qa-cycle')
|
||||
const PHASES = ['01-ideation-research', '02-builder-cycle', '03-deployment-cycle']
|
||||
// ★ THE ENUM WAS TOO NARROW, not the cards wrong. MEASURED 2026-09-24 across 633 cards:
|
||||
// EIGHT distinct status values are in use and EVERY ONE IS A SINGLE WORD — there is no
|
||||
// prose left anywhere in the field. The nine this gate was failing carried legitimate
|
||||
// TERMINAL states the original enum simply predated: superseded (5), deferred (2),
|
||||
// decided, verified-already-true.
|
||||
//
|
||||
// This gate exists to keep the AGGREGATE honest — "a prose status makes every open-count
|
||||
// wrong while each card still READS correctly". A one-word terminal state serves that
|
||||
// purpose perfectly; it is exactly what the 130-card migration was FOR. Rewriting nine
|
||||
// accurate statuses to 'done' would have destroyed real distinctions (a SUPERSEDED card
|
||||
// was replaced by another; a DEFERRED one was consciously postponed; neither was
|
||||
// finished) to satisfy a list — losing information to make a checker green.
|
||||
//
|
||||
// OPEN vs CLOSED is the property that matters, and all four additions are CLOSED:
|
||||
// open = todo + doing + in-progress (66), closed = done + these four (507), blocked (60).
|
||||
const TERMINAL_EXTRA = ['superseded', 'deferred', 'decided', 'verified-already-true']
|
||||
const ALLOWED = new Set(['todo', 'doing', 'in-progress', 'blocked', 'done', ...TERMINAL_EXTRA])
|
||||
|
||||
if (!existsSync(QA)) {
|
||||
// _qa-cycle lives outside every repo the CI clones, so absence is EXPECTED there. Say so loudly
|
||||
// rather than passing silently — a gate that reports success when it never ran is the failure
|
||||
// mode this repo has already been bitten by.
|
||||
console.log('BACKLOG-STATUS: SKIP — _qa-cycle not present (expected in CI; it is in no repo).')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const offenders = []
|
||||
let total = 0
|
||||
const counts = {}
|
||||
|
||||
for (const ph of PHASES) {
|
||||
const p = join(QA, ph, 'backlog.json')
|
||||
if (!existsSync(p)) continue
|
||||
let cards
|
||||
try {
|
||||
cards = JSON.parse(readFileSync(p, 'utf8')).cards || []
|
||||
} catch (e) {
|
||||
console.error(`BACKLOG-STATUS: FAIL — ${ph}/backlog.json does not parse: ${e.message}`)
|
||||
process.exit(1)
|
||||
}
|
||||
for (const c of cards) {
|
||||
total++
|
||||
const s = String(c.status)
|
||||
counts[s] = (counts[s] || 0) + 1
|
||||
if (!ALLOWED.has(s)) offenders.push({ ph, id: c.id, s })
|
||||
}
|
||||
}
|
||||
|
||||
if (!total) {
|
||||
console.error('BACKLOG-STATUS: FAIL — found 0 cards across three phase backlogs.')
|
||||
console.error(' A gate with nothing to assert is not passing; it is not running.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (offenders.length) {
|
||||
console.error(`BACKLOG-STATUS: FAIL — ${offenders.length} of ${total} card(s) carry a non-enum status.\n`)
|
||||
for (const o of offenders.slice(0, 12)) {
|
||||
console.error(` ${o.ph} ${o.id}`)
|
||||
console.error(` status: "${o.s.slice(0, 84)}${o.s.length > 84 ? '…' : ''}"`)
|
||||
}
|
||||
if (offenders.length > 12) console.error(` …and ${offenders.length - 12} more`)
|
||||
console.error('\n Allowed: ' + [...ALLOWED].join(' / '))
|
||||
console.error(' Keep the explanation — move it to fields.status_detail. The prose is valuable and')
|
||||
console.error(' often carries measurements; it just must not live in the field tooling filters on.')
|
||||
console.error(' A prose status makes every open-count wrong while each card still READS correctly,')
|
||||
console.error(' so the error hides in the aggregate: 130 of 395 cards once carried one, 71 of them')
|
||||
console.error(' already done, and the reported open count was 176 against a real 105.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(`BACKLOG-STATUS: PASS — ${total} card(s), every status is a queryable enum.`)
|
||||
const order = ['todo', 'doing', 'in-progress', 'blocked', 'done']
|
||||
console.log(' ' + order.filter((k) => counts[k]).map((k) => `${k} ${counts[k]}`).join(' · '))
|
||||
console.log(' Explanations live in fields.status_detail, which is not required — most cards need')
|
||||
console.log(' none, and demanding one would turn this into paperwork satisfied with filler.')
|
||||
|
|
@ -86,6 +86,64 @@ ok(dry(formatClipboard(arabic, { color: '#0072b2' })) !== 'ok', 'NEGATIVE CONTRO
|
|||
const indiaText = X.selectionClipboard([{ key: 'adm1:Mahārāshtra', name: 'Mahārāshtra', count: 54, kind: 'adm1' }, { key: 'adm1:Goa', name: 'Goa', count: 5, kind: 'adm1' }], {})
|
||||
ok(dry(indiaText) === 'ok', `apply-grouping's dry run refuses an India state selection: ${dry(indiaText)}`, 'an India state selection resolves too (shape names are canonical)')
|
||||
|
||||
// ── A3. the client's regroupable() agrees with apply-grouping's own verdict ─────────────────────────────
|
||||
// Copy warns when its first line will not regroup (a mix of kinds, or a legend zone). The warning is only honest if
|
||||
// regroupable() says exactly what the tool's dry run says, so each case is run through BOTH.
|
||||
const T = (key, name, count, kind, country) => ({ key, name, count, kind, ...(country ? { country } : {}) })
|
||||
const REGROUP_CASES = {
|
||||
'India states': [T('adm1:Mahārāshtra', 'Mahārāshtra', 54, 'adm1', 'IND'), T('adm1:Goa', 'Goa', 5, 'adm1', 'IND')],
|
||||
'a China province': [T('adm1:Gansu Province', 'Gansu Province', 3, 'adm1', 'CHN')],
|
||||
'countries': [T('adm0:NPL', 'Nepal', 34, 'adm0'), T('adm0:BTN', 'Bhutan', 13, 'adm0')],
|
||||
'a state + a country': [T('adm1:Goa', 'Goa', 5, 'adm1', 'IND'), T('adm0:NPL', 'Nepal', 34, 'adm0')],
|
||||
'India + China': [T('adm1:Goa', 'Goa', 5, 'adm1', 'IND'), T('adm1:Gansu Province', 'Gansu Province', 3, 'adm1', 'CHN')],
|
||||
'a legend zone': [T('zone:test', 'Test Zone', 12, 'zone')],
|
||||
}
|
||||
const disagree = []
|
||||
for (const [name, sel] of Object.entries(REGROUP_CASES)) {
|
||||
const tool = dry(X.selectionClipboard(sel, {})) === 'ok'
|
||||
if (X.regroupable(sel) !== tool) disagree.push(`${name}: regroupable ${X.regroupable(sel)}, apply-grouping ${tool ? 'accepts' : 'refuses'}`)
|
||||
}
|
||||
// …and the COUNTRY an ADM1 territory needs for that verdict is always known: from the feature's own props on a
|
||||
// map tap, else from the baked table (a props-only toggle once left it null, which read as a false warning).
|
||||
const { resolveTerritory, ADM1_COUNTS, ADM1_COUNTRY } = await import(pathToFileURL(P('plugin-designs/my-upg-100-list/data/territory-counts.js')).href)
|
||||
const noCountry = Object.keys(ADM1_COUNTS).filter((n) => !['IND', 'CHN'].includes(ADM1_COUNTRY[n]))
|
||||
ok(!noCountry.length && resolveTerritory({ shapeName: 'Mahārāshtra' }).country === 'IND' && resolveTerritory({ shapeName: 'Guizhou Province' }).country === 'CHN',
|
||||
`an ADM1 territory without a known country: ${noCountry.slice(0, 5).join(', ') || 'a props-only toggle'}`, 'every ADM1 name resolves its country, even from a props-only toggle')
|
||||
ok(!disagree.length, `Copy's regroup warning disagrees with apply-grouping: ${disagree.join('; ')}`,
|
||||
`the regroup warning matches apply-grouping's own verdict in all ${Object.keys(REGROUP_CASES).length} cases (India, China, countries, and the three it refuses)`)
|
||||
|
||||
// ── A6. A STATE-ONLY selection titles its Prayer list / Card deck ───────────────────────────────────────
|
||||
// Development threw "Reduce of empty array" for a state-only selection: the title named zones and countries only, so
|
||||
// a selection of Gujarāt alone had NO names. On the REAL served table: Gujarāt alone is titled "Gujarāt"; a state +
|
||||
// a country names both; a province with no table beside it is not named; Mizoram (no groups listed) alone gives the
|
||||
// keyed "no people groups" text. NEGATIVE CONTROL: the old naming (zones + countries) gives [] and reduce() throws.
|
||||
{
|
||||
const { keyedError, errorText } = await import(pathToFileURL(P('library/i18n/keyedError.js')).href)
|
||||
const wallEn = JSON.parse(readFileSync(P('library/i18n/locales/en/wall.json'), 'utf8'))
|
||||
const tr = (k) => k.split('.').slice(1).reduce((o, x) => (o == null ? o : o[x]), wallEn) ?? k
|
||||
const states = JSON.parse(readFileSync(P('data/people-groups/india-states.json'), 'utf8')).states
|
||||
const st = (shape) => states.find((x) => x.shapeName === shape)
|
||||
const guj = st('Gujarāt'), miz = st('Mizoram')
|
||||
const fromOf = (pairs) => { const m = new Map(); for (const [slugs, name] of pairs) for (const s of slugs) m.set(s, [...(m.get(s) || []), name]); return m }
|
||||
const noRecords = () => keyedError('wall.hud.noRecords', 'that list has no people groups to print')
|
||||
const join = (a, b) => `${a} + ${b}`
|
||||
const T = (kind, name) => ({ kind, name })
|
||||
const alone = X.selectionTitleNames([], [T('adm1', 'Gujarāt')], fromOf([[guj.slugs, 'Gujarāt']]), noRecords)
|
||||
const both = X.selectionTitleNames([], [T('adm0', 'Nepal'), T('adm1', 'Gujarāt')], fromOf([[['npl-a', 'npl-b'], 'Nepal'], [guj.slugs, 'Gujarāt']]), noRecords)
|
||||
const withProvince = X.selectionTitleNames([], [T('adm1', 'Guizhou Province'), T('adm1', 'Gujarāt')], fromOf([[guj.slugs, 'Gujarāt']]), noRecords)
|
||||
let empty = ''
|
||||
try { X.selectionTitleNames([], [T('adm1', 'Mizoram')], fromOf([[miz.slugs, 'Mizoram']]), noRecords); empty = 'no throw' } catch (e) { empty = errorText(e, tr) }
|
||||
let oldThrows = false
|
||||
try { [].reduce(join) } catch (_) { oldThrows = true }
|
||||
const oldNames = [...[], ...[T('adm1', 'Gujarāt')].filter((t) => t.kind === 'adm0').map((t) => t.name)]
|
||||
ok(guj.count === 90 && miz.count === 0 && oldNames.length === 0 && oldThrows,
|
||||
`NEGATIVE CONTROL: Gujarāt ${guj.count} / Mizoram ${miz.count} groups, the old naming gave ${JSON.stringify(oldNames)}`,
|
||||
'negative control: on the old naming a Gujarāt-only selection has no names, and reduce() throws')
|
||||
ok(alone.length === 1 && alone.reduce(join) === 'Gujarāt' && both.join('|') === 'Nepal|Gujarāt' && withProvince.join('|') === 'Gujarāt' && empty === tr('wall.hud.noRecords'),
|
||||
`titles: alone ${JSON.stringify(alone)}, with a country ${JSON.stringify(both)}, with a province ${JSON.stringify(withProvince)}, Mizoram alone "${empty}"`,
|
||||
`a state-only selection is titled "Gujarāt"; a state + a country names both; a province with no table is not named; Mizoram alone reads "${empty}"`)
|
||||
}
|
||||
|
||||
// ── B. the CSV ───────────────────────────────────────────────────────────────────────────────────
|
||||
function parseCsv(s) { // RFC 4180: quoted fields, doubled quotes, CRLF records
|
||||
const rows = []; let row = [], cell = '', q = false
|
||||
|
|
@ -124,6 +182,20 @@ const wired = {
|
|||
'the prefetch on batch mode': /if \(next\) selectionRecordsLoad\(\)/.test(vue),
|
||||
'one detection for every action': (vue.match(/await selectionPeople\(\)/g) || []).length >= 2 && /selectionPeople\(\)\.then/.test(vue),
|
||||
// India's states resolve through the served table (library/data/indiaStates.js), fetched only once a state is selected
|
||||
// In batch mode a tap on a pin opens NO detail sheet (it covered the whole HUD on a phone); the tapestry
|
||||
// listener under it toggles the territory. The real-tap proof is a browser check; this pins the guard in the source.
|
||||
'a pin tap in batch mode opens no sheet': /map\.on\('click', 'upg-pins-hit', \(e\) => \{[\s\S]{0,700}?if \(batchMode\.value\) return\s*\n\s*const f = e && e\.features/.test(vue),
|
||||
// …and that tap toggles the territory EXACTLY ONCE: the tapestry listener already does it, so the pin handler
|
||||
// must not toggle a second time (select-then-deselect in one tap).
|
||||
'the pin handler adds no second toggle': (() => {
|
||||
const at = vue.indexOf("map.on('click', 'upg-pins-hit', (e) => {"); if (at < 0) return false
|
||||
const body = vue.slice(at, vue.indexOf('\n })', at))
|
||||
return !/toggleTerritory|toggleByProps|toggleZoneSelection/.test(body)
|
||||
})(),
|
||||
'the regroup warning on Copy': /const warn = regroupable\(sel\) \? '' : t\('upgMap\.copyNotRegroupable'\)/.test(vue),
|
||||
// The packet / card-deck title goes through selectionTitleNames (arm A6 proves it), with the keyed empty case.
|
||||
'the title names come from selectionTitleNames': /const names = selectionTitleNames\(zoneNames, selectedTerritories\.value, from,\s*\n\s*\(\) => keyedError\('wall\.hud\.noRecords'/.test(vue)
|
||||
&& !/const names = \[\.\.\.zoneNames, \.\.\.countryNames\]/.test(vue),
|
||||
'India states detected from the served table': /import \{ loadIndiaStates \} from '@map\/data\/indiaStates\.js'/.test(vue)
|
||||
&& /india\.byShapeName\.get\(t\.name\)/.test(vue) && /t\.kind === 'adm1'\) indiaStates\(\)\.catch/.test(vue),
|
||||
}
|
||||
|
|
@ -133,7 +205,7 @@ ok(!missingWires.length, `the HUD is missing: ${missingWires.join(', ')}`, 'the
|
|||
// ── D. the strings, ×11 ──────────────────────────────────────────────────────────────────────────
|
||||
const LANGS = ['en', 'es', 'fr', 'pt', 'de', 'it', 'ro', 'ru', 'ar', 'hi', 'zh']
|
||||
const KEYS = {
|
||||
upgMap: ['copySelection', 'exportCsv', 'selectionColourGroup', 'selectionColour', 'customColour', 'copiedSelection', 'copiedTerritoriesOnly', 'csvSaved', 'csvFailed'] }
|
||||
upgMap: ['copySelection', 'exportCsv', 'copyNotRegroupable', 'selectionColourGroup', 'selectionColour', 'customColour', 'copiedSelection', 'copiedTerritoriesOnly', 'csvSaved', 'csvFailed'] }
|
||||
const cat = (l, f) => JSON.parse(readFileSync(P('library/i18n/locales', l, `${f}.json`), 'utf8'))
|
||||
const bad = []
|
||||
for (const [f, keys] of Object.entries(KEYS)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// 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
|
||||
// provider case went UNMEASURED: a hollow PASS (rega, 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.
|
||||
// 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 the browser checks do); otherwise a baked one is still used if a tree has it.
|
||||
const bakedToken = process.env.TEST_MAPBOX_TOKEN || (() => {
|
||||
for (const b of manifest.bundles || []) {
|
||||
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`)
|
||||
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
|
||||
// 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.
|
||||
const VIEW = { 'country-posters-mfe': { view: 'map' } }
|
||||
const cfg = JSON.stringify({ profile: t.profile, ...(VIEW[t.element] || {}), ...c.config(bakedToken) }).replace(/'/g, ''')
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
*
|
||||
* ★ 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:
|
||||
* 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 /
|
||||
* 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
|
||||
|
|
@ -61,7 +61,7 @@ const TREE = join(ROOT, 'public', TREE_NAME)
|
|||
const argv = process.argv.slice(2)
|
||||
const oi = argv.indexOf('--only')
|
||||
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
|
||||
// checks those (the landing precondition passes the ones a landing can have changed).
|
||||
const ALL = argv.includes('--all')
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
*
|
||||
* 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
|
||||
* 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:
|
||||
* a receiver renamed in source ('map-filtered' -> 'map-filter' in research-map-plus-grid.vue);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
* `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
|
||||
* 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 { resolve, dirname, join } from 'node:path'
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* 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.
|
||||
*
|
||||
* 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
|
||||
* @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
|
||||
|
|
|
|||
|
|
@ -1,149 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-done-evidence.mjs — a card may not reach status:done with NO evidence.
|
||||
*
|
||||
* WHY (2026-09-21)
|
||||
* Two quality cards were status:done with an EMPTY bluf. Both turned out genuinely done — but
|
||||
* only because I opened a browser and read the source. A board-wide count then found 18 done
|
||||
* cards carrying NEITHER a bluf NOR any fields: a status word and nothing else.
|
||||
*
|
||||
* ★ THE HAND-OFF IS THE POINT. The Doxa team will read this board to learn what exists. A card
|
||||
* reading "done" with no evidence cannot be reconciled against the code at all — they cannot
|
||||
* tell a shipped deliverable from an abandoned one someone closed. This flight found SIX cards
|
||||
* whose stated status disagreed with reality; an evidence-less card is the unfalsifiable case.
|
||||
*
|
||||
* WHAT COUNTS AS EVIDENCE (deliberately generous — this is a floor, not a style guide)
|
||||
* EITHER a non-empty `bluf` OR a non-empty `fields` object. Both are free to write at the moment
|
||||
* the work is fresh, which is the only moment the evidence is cheap and accurate.
|
||||
*
|
||||
* BASELINE, NOT A CLIFF
|
||||
* The 10 pre-existing offenders are allow-listed by id. A gate that fails on day one for history
|
||||
* nobody can fix gets disabled, and then it protects nothing. This fails ONLY on a NEW one — the
|
||||
* same shape as check-label-joins, which allow-lists two known occurrences.
|
||||
* ★ TO CLEAR AN ENTRY: add real evidence to the card, then delete its id here. The list should
|
||||
* only ever shrink; a PR that grows it is doing the thing this gate exists to stop.
|
||||
*
|
||||
* NEGATIVE-TESTED: adding a fresh done card with no bluf/fields makes this FAIL naming it,
|
||||
* EXIT=1; removing it returns PASS.
|
||||
*/
|
||||
import { readFileSync, existsSync } from 'node:fs'
|
||||
import { resolve, dirname, join } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
// ★ DEPTH 2, NOT 1 — and that is deliberate but load-bearing. Every sibling gate in internal/
|
||||
// resolves ROOT one level up (the repo). This one goes TWO levels up, because the backlogs it
|
||||
// checks live in `_qa-cycle/`, which is AGENTIC SCRATCH OUTSIDE THE REPO — tracked by no git
|
||||
// repository, and never cloned with the studio.
|
||||
//
|
||||
// THE HAND-OFF DEFECT THIS GUARD FIXES (measured 2026-09-22). On this machine _qa-cycle happens
|
||||
// to sit beside the repo, so the gate passes. An inheritor who clones ONLY the studio has no
|
||||
// sibling _qa-cycle — and the gate did NOT fail there. It printed
|
||||
// "0 card(s) across 3 backlog(s), 0 done" ... "PASS"
|
||||
// and exited 0. A VACUOUSLY GREEN gate is worse than no gate: it certifies nothing while looking
|
||||
// healthy, which is exactly the failure shape this repo keeps re-learning.
|
||||
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
|
||||
const QA = join(ROOT, '_qa-cycle')
|
||||
|
||||
if (!existsSync(QA)) {
|
||||
console.log('DONE-EVIDENCE: SKIP — no _qa-cycle/ beside this repo, so there are no backlogs to check.')
|
||||
console.log(' This gate reads the AGENTIC BACKLOGS, which live outside the repo and are not cloned')
|
||||
console.log(' with it. Absence is expected for anyone who checked out the studio on its own; it is')
|
||||
console.log(' NOT a pass. Run it from a workspace that has _qa-cycle/ as a sibling of this repo.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const BACKLOGS = [
|
||||
'01-ideation-research/backlog.json',
|
||||
'02-builder-cycle/backlog.json',
|
||||
'03-deployment-cycle/backlog.json',
|
||||
]
|
||||
|
||||
// Pre-existing, from before the rule. Verified 2026-09-21. SHRINK ONLY.
|
||||
// ★ THE BASELINE IS NOW EMPTY, and that is the strongest state this gate can be in.
|
||||
// All ten exemptions had since gained evidence — the gate reported every one of them
|
||||
// as STALE on 2026-09-24 — so the list was pure dead weight: ten ids that exempted
|
||||
// nothing while implying ten cards still owed a record.
|
||||
// An EMPTY set is not an unset one: every done card must now carry evidence, with no
|
||||
// exemptions, and the gate fails outright for a single card that does not. Keeping
|
||||
// `{ ... }` full of satisfied ids would mean the same today and quietly permit a
|
||||
// regression the moment someone "reused" an entry.
|
||||
// If a genuinely un-reconstructable card ever needs an exemption, add its id here WITH
|
||||
// the reason — and the gate will tell you the day it becomes stale.
|
||||
const BASELINE = new Set([
|
||||
])
|
||||
let checked = 0, done = 0
|
||||
const offenders = []
|
||||
const staleBaseline = []
|
||||
|
||||
// What counts as evidence on a done card, by schema family:
|
||||
// 1. `bluf` or a non-empty `fields` object (the original schema);
|
||||
// 2. `one_line` or `verify` (the second schema; see the note in the loop below);
|
||||
// 3. a non-empty `evidence` LIST with at least one non-blank entry. 44 cards record their proof
|
||||
// this way, and 12 of them carry none of the other fields, so each false-failed on closing
|
||||
// (DESIGN-TEAM-ROLES-MEMBER-DESIGNER-ADMIN did, with its proof sitting in the list).
|
||||
function carriesEvidence(c) {
|
||||
const str = (v) => String(v || '').trim().length > 0
|
||||
if (str(c.bluf) || str(c.one_line) || str(c.verify)) return true
|
||||
if (c.fields && Object.keys(c.fields).length > 0) return true
|
||||
if (Array.isArray(c.evidence) && c.evidence.some((e) => str(typeof e === 'string' ? e : JSON.stringify(e ?? '')))) return true
|
||||
return false
|
||||
}
|
||||
// Self-test, both sides: a non-empty list passes; an empty list, or one of blanks, still fails.
|
||||
{
|
||||
const ok = carriesEvidence({ status: 'done', evidence: ['check:x PASS 12/12'] })
|
||||
const empty = carriesEvidence({ status: 'done', evidence: [] })
|
||||
const blanks = carriesEvidence({ status: 'done', evidence: ['', ' '] })
|
||||
if (!ok || empty || blanks) {
|
||||
console.error('DONE-EVIDENCE: SELF-TEST FAILED — an evidence list is not judged by its content')
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
for (const rel of BACKLOGS) {
|
||||
const p = join(QA, rel)
|
||||
if (!existsSync(p)) continue
|
||||
let cards
|
||||
try { cards = JSON.parse(readFileSync(p, 'utf8')).cards || [] } catch { continue }
|
||||
for (const c of cards) {
|
||||
checked++
|
||||
if (c.status !== 'done') continue
|
||||
done++
|
||||
// ★ THE BOARD USES TWO CARD SCHEMAS, and this gate only knew one. Older cards carry
|
||||
// `bluf` + `fields`; a second family carries `one_line` (what was done), `why`, and
|
||||
// `verify` (the artifact and how it was checked). MEASURED 2026-09-24 across 443 done
|
||||
// cards: 40 carry `verify` and 41 carry `one_line`, and ALL THREE cards this gate was
|
||||
// failing had one or both — with named probes, assertion counts and negative tests.
|
||||
// They were among the best-evidenced cards on the board.
|
||||
// `verify` is if anything the STRONGER signal: it names how the claim was checked,
|
||||
// which is exactly what "evidence" means here. Demanding a redundant `bluf` alongside
|
||||
// it would have taught the next agent to pad the field the checker reads rather than
|
||||
// write the record — the gate training the behaviour it exists to prevent.
|
||||
if (carriesEvidence(c)) {
|
||||
// A baselined id that now HAS evidence is stale — report it so the list shrinks.
|
||||
if (BASELINE.has(c.id)) staleBaseline.push(`${c.id} (${rel})`)
|
||||
continue
|
||||
}
|
||||
if (BASELINE.has(c.id)) continue
|
||||
offenders.push(`${c.id} (${rel})`)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`DONE-EVIDENCE: ${checked} card(s) across ${BACKLOGS.length} backlog(s), ${done} done, ` +
|
||||
`${BASELINE.size} baselined.`)
|
||||
for (const s of staleBaseline) {
|
||||
console.log(` i BASELINE NOW STALE — ${s} has evidence; remove its id from check-done-evidence.mjs`)
|
||||
}
|
||||
|
||||
if (offenders.length) {
|
||||
console.error(`\nDONE-EVIDENCE: FAIL — ${offenders.length} card(s) are done with NO evidence in any field:`)
|
||||
for (const o of offenders) console.error(` ✗ ${o}`)
|
||||
console.error(
|
||||
'\n Add ONE of: `bluf` or `one_line` saying what was done, `verify` naming how it was checked,\n' +
|
||||
' a `fields` entry naming the artifact, or an `evidence` list. Any ONE is enough — the point is a\n' +
|
||||
' record a reader can reconcile against the code, not a particular field name.\n' +
|
||||
' it was verified. Write it now, while the work is fresh — reconstructing it later produces a\n' +
|
||||
' plausible story indistinguishable from a real record.'
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('DONE-EVIDENCE: PASS — every done card carries evidence (bluf, one_line, verify, fields or an evidence list).')
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-dxlife-untouched.mjs — evidence for the ZERO DX-LIFE EDITS standing law.
|
||||
*
|
||||
* ★ WHY THIS IS NOT `git status` (a real false-pass, 2026-09-20)
|
||||
* I verified the invariant for several ticks with:
|
||||
* cd root-dx-life && git status --porcelain | wc -l # -> 0
|
||||
* and reported that 0 as proof. root-dx-life IS NOT A GIT REPOSITORY — it is a
|
||||
* plain directory clone. `git status` prints "fatal: not a git repository" to STDERR and nothing
|
||||
* to stdout, so `wc -l` counts 0. THE CHECK RETURNED THE PASS VALUE *BECAUSE IT FAILED*.
|
||||
* Identical in shape to the settled-tree churn bug (bfs rejected the argument, stderr discarded,
|
||||
* wc -l counted an empty stream): A COMMAND'S ERROR PATH PRODUCING THE PASS VALUE THROUGH AN
|
||||
* UNCHECKED PIPE. A safety invariant is the worst possible place for that.
|
||||
*
|
||||
* ★ WHAT THIS DOES INSTEAD — a VCS-INDEPENDENT WITNESS.
|
||||
* Walks the clone and records (path, size, mtime) for every file into a baseline JSON. On later
|
||||
* runs it DIFFS against that baseline and reports added / removed / modified. No VCS required,
|
||||
* so it cannot be defeated by the tree not being a repo.
|
||||
*
|
||||
* FIRST RUN writes the baseline and says so plainly — it does NOT claim the tree is unmodified,
|
||||
* because a baseline taken now can only prove "unchanged SINCE NOW". Honest framing matters more
|
||||
* than a green line: this gate earns its value over time, not on its first invocation.
|
||||
*
|
||||
* USAGE
|
||||
* node internal/check-dxlife-untouched.mjs # create or verify the baseline
|
||||
* DXLIFE=/path/to/clone node internal/check-dxlife-untouched.mjs
|
||||
* ★ WHAT IT WALKS: the DX Life CLONES, not every directory under the umbrella (2026-09-29). root-dx-life
|
||||
* also holds git WORKTREES of doxa-life (wt-rg-*, today's research-gallery branches) and a DANGLING
|
||||
* one (research-map-clean, whose .git points at a path that no longer exists). Walking them counted
|
||||
* sanctioned work and generated output as "added" and held this gate red, while the clones themselves
|
||||
* were untouched. A directory whose .git is a FILE beginning `gitdir:` is a worktree and is skipped,
|
||||
* keyed on that file rather than on `git worktree list`, which does not show a dangling one. Skipped,
|
||||
* never deleted: research-map-clean's parent repo is gone, so it may hold the only copy of its work.
|
||||
* Generated dirs are skipped too, and the baseline is compared only WITHIN this scope, so narrowing
|
||||
* the scope reads as no change rather than as thousands of removals.
|
||||
*
|
||||
* EXIT 0 unchanged (or baseline created) · 1 drift detected · 75 not measured (no clone on this machine)
|
||||
*/
|
||||
import { readdirSync, statSync, existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
||||
import { join, resolve, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url))
|
||||
const CLONE = process.env.DXLIFE || resolve(HERE, '..', '..', '..', 'root-dx-life')
|
||||
const STORE = resolve(HERE, '..', '..', '_qa-cycle', '_scratch-coordinator', 'dxlife-baseline.json')
|
||||
|
||||
if (!existsSync(CLONE)) {
|
||||
// Not measured, not failed: a machine without the DX Life umbrella has nothing this law covers.
|
||||
console.log(`DXLIFE-UNTOUCHED: NOT MEASURED — no DX Life clone at ${CLONE} on this machine.`)
|
||||
process.exit(75)
|
||||
}
|
||||
|
||||
const SKIP = new Set(['node_modules', '.git', '.output', '.nuxt', '.cache', 'dist'])
|
||||
const isWorktree = (dir) => {
|
||||
try { const g = join(dir, '.git'); return statSync(g).isFile() && readFileSync(g, 'utf8').startsWith('gitdir:') } catch { return false }
|
||||
}
|
||||
const worktrees = [] // relative paths skipped as worktrees, reported below
|
||||
const snap = {}
|
||||
;(function walk(dir) {
|
||||
if (dir !== CLONE && isWorktree(dir)) { worktrees.push(dir.slice(CLONE.length + 1)); return }
|
||||
let entries
|
||||
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return }
|
||||
for (const e of entries) {
|
||||
if (SKIP.has(e.name)) continue
|
||||
const p = join(dir, e.name)
|
||||
if (e.isDirectory()) { walk(p); continue }
|
||||
try {
|
||||
const st = statSync(p)
|
||||
snap[p.slice(CLONE.length + 1)] = `${st.size}:${Math.round(st.mtimeMs)}`
|
||||
} catch { /* unreadable file — skip rather than fail the invariant check */ }
|
||||
}
|
||||
})(CLONE)
|
||||
|
||||
const n = Object.keys(snap).length
|
||||
if (!existsSync(STORE)) {
|
||||
mkdirSync(dirname(STORE), { recursive: true })
|
||||
writeFileSync(STORE, JSON.stringify({ created: new Date().toISOString(), clone: CLONE, files: snap }, null, 0))
|
||||
console.log(`DXLIFE-UNTOUCHED: BASELINE CREATED — ${n} files recorded at ${CLONE}.`)
|
||||
console.log(' This run proves NOTHING about the past; it establishes the reference point.')
|
||||
console.log(' From here on, any add/remove/modify under that tree will be reported.')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Compare within the walked scope: a baseline entry under a skipped worktree or generated dir is out of scope.
|
||||
const inScope = (rel) => !worktrees.some((w) => rel === w || rel.startsWith(w + '/')) && !rel.split('/').some((seg) => SKIP.has(seg))
|
||||
const base = Object.fromEntries(Object.entries(JSON.parse(readFileSync(STORE, 'utf8')).files).filter(([k]) => inScope(k)))
|
||||
console.log(`DXLIFE-UNTOUCHED: skipped ${worktrees.length} worktree(s) (a .git FILE): ${worktrees.join(', ') || 'none'}`)
|
||||
const added = Object.keys(snap).filter((k) => !(k in base))
|
||||
const removed = Object.keys(base).filter((k) => !(k in snap))
|
||||
const changed = Object.keys(snap).filter((k) => k in base && base[k] !== snap[k])
|
||||
|
||||
console.log(`DXLIFE-UNTOUCHED: ${n} files walked · +${added.length} -${removed.length} ~${changed.length}`)
|
||||
if (added.length || removed.length || changed.length) {
|
||||
console.error('DXLIFE-UNTOUCHED: FAIL — the read-only clone CHANGED:')
|
||||
for (const f of added.slice(0, 5)) console.error(` ADDED ${f}`)
|
||||
for (const f of removed.slice(0, 5)) console.error(` REMOVED ${f}`)
|
||||
for (const f of changed.slice(0, 5)) console.error(` MODIFIED ${f}`)
|
||||
console.error(' ZERO DX-LIFE EDITS is a standing law. Investigate before doing anything else.')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('DXLIFE-UNTOUCHED: PASS — no file added, removed or modified since the baseline.')
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
*
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* ★ AND A CENSUS THAT COUNTED FUNCTION BODIES SAW FOUR OF SIXTEEN. That is why this gate greps for the
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* 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
|
||||
* `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
|
||||
* WORKS WHEN CLICKED. The browser resolves it against the page you copied it from, so the person testing
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ const run = async () => {
|
|||
const f = __srcFeatures(m, 'rd-bubbles'); return !!(f && f.length > 0);
|
||||
})()`, { timeout: 90000 })
|
||||
} 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
|
||||
// on one dashboard, one build, only the engine differing, by calling the app's own setData() to
|
||||
// shrink 2107 features to 5:
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import { readFileSync, readdirSync, statSync, existsSync } from 'node:fs'
|
|||
import { join, relative, resolve, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
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
|
||||
// the pair-form / transitive / cycle details below, and the drift would be silent.
|
||||
import { effectiveStages, stageReason } from './lib/effective-stage.mjs'
|
||||
|
|
@ -73,7 +73,7 @@ function findFrozen(ast, aliases) {
|
|||
if (name && (TRANSLATORS.has(name) || aliases.has(name))) {
|
||||
// tStatic(path, EXPLICIT_LOCALE) — the caller pinned it deliberately.
|
||||
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()
|
||||
// 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.
|
||||
|
|
@ -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?
|
||||
*
|
||||
* ⚠ 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
|
||||
* — 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
|
||||
|
|
@ -206,7 +206,7 @@ function findStoredTreeFreeze(code) {
|
|||
// 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
|
||||
// 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
|
||||
// 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
|
||||
|
|
@ -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('…') } } })
|
||||
*
|
||||
|
|
@ -388,7 +388,7 @@ const CASES = [
|
|||
['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 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],
|
||||
// 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.
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ try {
|
|||
the data was right and the feature was dead. */
|
||||
/* ⚠ 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
|
||||
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. */
|
||||
const anyRow = (JSON.parse(readFileSync(resolve(PUBLIC, TREE_NAME, 'assets/tables/doxa-people-groups.json'), 'utf8')).rows || [])
|
||||
.find((r) => r.slug && r.mapped)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* check-gospel-engine-contract.mjs — the map must use the engine it was ASKED for, or say nothing
|
||||
* 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
|
||||
* 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
|
||||
|
|
|
|||