Mobile parity: one missing property was making every phone width wrong

Mobile was the last dimension with no measurement behind it. Diffed ours against
LIVE doxa.life at 390 and 768, the same way desktop was diffed. Result after the
fix: region page 44 IDENTICAL, 0 differ; country page 26 IDENTICAL, 0 differ; no
horizontal overflow on either side at either width.

★ THE FIND. Their base/_globals.scss html rule carries `scrollbar-gutter: stable`
and we had nothing equivalent, so EVERY mobile width was 13.5px wider than
theirs — their container 353.5px against our 367px at 390.

It was invisible at 1440, because both sides cap at 1200 there. That is exactly
why a desktop-only diff could report 68 of 68 identical while every phone width
was wrong, and it is the clearest case yet for the rule about not trusting a
number measured at one size.

And it CASCADED into a different LAYOUT, not just different numbers: at 768 our
container came out 707.2px against their 693.7px, and their .switcher flips at
700px of container — so their three goal cards STACKED and ours stayed
three-across. One missing property, a visibly different page.

Ported onto .rd-scroll and .rl-scroll, this element's own scrolling box. A
shadow root cannot set the host document's html rule and should not try; our
scroller is the box every child measures against, so reserving the gutter there
reproduces their effect exactly (the width probe now reads pane 375 -> container
353.5 at 390, matching them).

Their 576px card reflow is verified rather than assumed: at 390 our .uupg__card
resolves to "image adopted" / "header header" / "content content" / "footer
footer" and at 768 to the three-column form, both byte-identical to theirs.

ACCEPTED, with its magnitude rather than buried: four vw-scaled clamps land
2-4% low (h1 52.71 vs 53.76 at 768, the prayer bar 22.5 vs 23.39 at 390). Their
clamps scale with vw and ours with cqw, deliberately, so the element sizes
correctly as one pane of the regions-mfe composite — and vw includes the
scrollbar while cqw does not, so the gap is now a systematic ~15px. The probe
prints these as a residual instead of counting them identical; switching to vw
would buy ~1px of heading and break a shipping surface, which is the wrong
trade, but it is the Driver's to overrule.

Desktop unaffected: region 68/68, country 28/28, every width still matching
their container formula, no gate newly red.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 03:11:00 +00:00
parent 64339a2250
commit 50ce92ef8a
7 changed files with 30 additions and 10 deletions

View file

@ -653,7 +653,17 @@ useShadowStyles(
// .container to the DETAIL routes too -- RegionDetail.vue:46 and CountryDetail.vue:35 are both
// class="container page-content ..." -- so the dashboard half is capped by the same rule.
// gap = their .stack--3xl (55.349px), the gap between every block on that page.
'.rd-scroll{ flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;' +
// ★ scrollbar-gutter: stable — ported from their base/_globals.scss html rule, and it is
// LOAD-BEARING for layout, not cosmetic. MEASURED at 390px: their container is 353.5px
// and ours was 367px, because their html reserves the scrollbar gutter always and our
// scroller did not. 13.5px, invisible at 1440 where both cap at 1200 — which is exactly
// why a desktop-only diff read 68/68 while every mobile width was wrong.
// Worse, it CASCADED: at 768 our container came out 707.2px and theirs 693.7px, and
// their .switcher flips at 700px of container — so their three goal cards stacked and
// ours stayed three-across. One missing property, a different layout.
// We cannot set this on the host document from inside a shadow root, and should not;
// this element's own scroller is the box every child measures against.
'.rd-scroll{ flex:1 1 auto; min-height:0; overflow-y:auto; scrollbar-gutter:stable; -webkit-overflow-scrolling:touch;' +
' padding:55.349px 0 73.6142px; display:flex; flex-direction:column; gap:55.349px;' +
' container-type:inline-size; }' +
'.rd-scroll > *{ box-sizing:border-box; width:calc(90% + 16px); max-width:1200px;' +

View file

@ -277,7 +277,17 @@ useShadowStyles(
// in research-dashboard.vue:284. Safe here because nothing in this element is position:fixed --
// inline-size containment is what made a fixed scrim pane-relative in check:scrim-viewport.
// Side padding is deliberately 0: the .container formula below supplies the gutter, as theirs does.
'.rl-scroll{ flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;' +
// ★ scrollbar-gutter: stable — ported from their base/_globals.scss html rule, and it is
// LOAD-BEARING for layout, not cosmetic. MEASURED at 390px: their container is 353.5px
// and ours was 367px, because their html reserves the scrollbar gutter always and our
// scroller did not. 13.5px, invisible at 1440 where both cap at 1200 — which is exactly
// why a desktop-only diff read 68/68 while every mobile width was wrong.
// Worse, it CASCADED: at 768 our container came out 707.2px and theirs 693.7px, and
// their .switcher flips at 700px of container — so their three goal cards stacked and
// ours stayed three-across. One missing property, a different layout.
// We cannot set this on the host document from inside a shadow root, and should not;
// this element's own scroller is the box every child measures against.
'.rl-scroll{ flex:1 1 auto; min-height:0; overflow-y:auto; scrollbar-gutter:stable; -webkit-overflow-scrolling:touch;' +
' padding:20px 0; display:flex; flex-direction:column; gap:23.5264px;' +
' container-type:inline-size; }' +
// ★ THE DESKTOP MAX-WIDTH, ported verbatim from DX-life's .container

View file

@ -838,7 +838,7 @@
"bundle": "regions-dashboard",
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
"integrity": "sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+",
"integrity": "sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz",
"elements": [
"regions-dashboard"
],
@ -858,7 +858,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -872,7 +872,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
},
{
@ -886,7 +886,7 @@
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
"embed": {
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
}
}
]

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

File diff suppressed because one or more lines are too long

View file

@ -29,7 +29,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

View file

@ -40,7 +40,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
<script src="../regions-dashboard.js" integrity="sha384-WGTEO4UGtTqTkbrv4VjKXBejcEHt6EYnem9sSu+xcDmxTAWDpBQ6DwjLfgubA7k+" crossorigin="anonymous"></script>
<script src="../regions-dashboard.js" integrity="sha384-PkjCojT7LBeF8fuNq3Zm+b+UZZM3lqeypf6/5pVKRQLcwoaBge32MRu2pShn6DSz" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order: