Merge remote-tracking branch 'origin/development' into bundler-2/bp-grid-locale

This commit is contained in:
Doxa Maps 2026-09-29 23:15:51 +00:00
commit 67fed8b79f
232 changed files with 280 additions and 218 deletions

View file

@ -279,13 +279,17 @@ function syncAttribClearance(root) {
if (!el || !root?.querySelector) return
let h = 0
try {
// TWO attribution flavours ship in this repo and the pill must clear EITHER:
// .mapboxgl-ctrl-attrib — Mapbox's own control (useMapInstance, attributionControl:true)
// .doxa-attribution — our own credit line (useMapAttribution.js) on the profiles
// that construct with attributionControl:false
// Both are licence-required credit, so both are obstacles, never things to cover.
// THREE attribution flavours ship in this repo and the pill must clear ANY of them:
// .mapboxgl-ctrl-attrib — Mapbox's own control (useMapInstance, attributionControl:true)
// .maplibregl-ctrl-attrib — MapLibre's, the DEFAULT engine. Missing from this list, the pill
// kept the 20px fallback and, on a 390px phone where the credit wraps
// to two lines (37px), covered it by 35px on every map. Mapbox measured 0.
// .doxa-attribution — our own credit line (useMapAttribution.js) on the profiles
// that construct with attributionControl:false
// All are licence-required credit, so all are obstacles, never things to cover.
const a = root.querySelector(
'.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl-attrib, .mapboxgl-ctrl-bottom-right .doxa-attribution, .doxa-attribution')
'.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl-attrib, .maplibregl-ctrl-bottom-right .maplibregl-ctrl-attrib, '
+ '.mapboxgl-ctrl-bottom-right .doxa-attribution, .maplibregl-ctrl-bottom-right .doxa-attribution, .doxa-attribution')
if (a) {
// Measure the strip the control OCCUPIES from the bottom of the box we are anchored
// in, not the control's own height: Mapbox adds a margin inside

View file

@ -252,6 +252,41 @@ useShadowStyles(`
.mi-foot input{ width:20px; height:20px; }
/* A phone gets the whole screen: a floating card with margins leaves too little room for a page. */
/* ★ THE ONE-TIME NUDGE IS DESKTOP-ONLY, AND THE THRESHOLD IS MEASURED, NOT CHOSEN. The pointer
is anchored beside the button and extends ~213px INWARD, straight across whatever the tab puts
in its top strip. Measured on Deep Research, on the shared browser, worst overlap per width:
390 Regions "Region Dashboards" 1416px^2 UPG-100 1428px^2
768 Cities legend 6560 Gospel legend 6560 + input 6240 Regions selector 4200
900 Cities legend 2432 Gospel legend 2496 + input 2176 Regions selector 3696
1024 Cities CLEAN Gospel CLEAN Regions selector 2100
1200 CLEAN CLEAN CLEAN
1280 CLEAN CLEAN CLEAN
UPG-100 measures the same shape independently: 768 over "Prayer Resources" AND "Map", 1024 over
"Map", clear at 1200/1280/1440 -- in en AND ar.
So it is not a phone problem, which is how it was first reported and first fixed: it persists
to 1024 and only clears near 1200. A max-width:600px rule would have left 768, 900 and 1024
broken on four tabs -- found because gospel counted PANELS (.gm-legend/.gm-sheet) as well as
controls, which a control-only probe misses entirely.
ARABIC IS MEASURED, not argued: via DX Life's /ar/ route (dir=rtl confirmed), the pointer is
hidden and clean at 390, 768 and 1024, and at 1440 it mirrors to the start side (x=104) and is
clean there too.
THE RULE IS ITS OWN BLOCK, deliberately: the 600px block below also full-screens the dialog,
and this has nothing to do with the phone sheet. The .98 covers fractional viewport widths
under browser zoom.
Hiding it below the measured clear width is the only rule that holds everywhere: the button it
points at is untouched, so nothing becomes unreachable, and a hint that covers a map's own
legend, search box or view switcher is worse than no hint.
Two other shapes were measured and both were worse: stacking it below the button traded one
collision for five (Gospel's "Resources found" 2010px^2, plus the zoom +/- of three tabs), and
hiding only its text still left 675px^2 on "Region Dashboards" and 900px^2 on Gospel's input.
⚠ NOT A PLACEMENT CHANGE. The per-tab pos.narrow values in the shell's tabInfo are measured per
tab (the buttons sit at x=8, 12, 294 and 346 across the seven tabs at 390); an inset-inline-*
pinned here would win by specificity and silently flatten all of them. The button is
placement's business; this rule is only about the hint. */
@media (max-width:1199.98px){
.mi-pointer{ display:none; }
}
@media (max-width:600px){
.mi-scrim{ padding:0; align-items:stretch; }
.mi-dialog{ width:100%; max-height:none; height:100%; border-radius:0; }

View file

@ -138,7 +138,7 @@
<div class="dms-slot" :ref="el => setSlot(i, el)"></div>
<!-- The tab's "what can this do for you?" button (library MapInfo), from src/tabInfo.js. It sits
over the pane, top-right; profile-config info:false removes it. -->
<MapInfo v-if="infoFor(t)" :key="'info-' + t.id" v-bind="infoProps(t)" :style="infoStyle(t)" :placement="infoPos(t).placement || 'top-right'"
<MapInfo v-if="infoFor(t)" :key="'info-' + t.id" v-bind="infoProps(t)" :style="infoStyle(t)" :placement="infoPos(t).placement || 'top-right'" :class="{ 'dms-info-raise': infoPos(t).raise }"
:assets-base="assetsBase" :locale="effectiveLocale" />
<div v-if="t.state === 'loading'" class="dms-hint">{{ tr('loading', { label: tabLabel(t) }) }}</div>
<div v-else-if="t.state === 'error'" class="dms-hint err">
@ -1659,6 +1659,11 @@ export default defineComponent({
documents this opt-out for exactly this case, and .vtl-host and plugin-studio's .ps-pane both set
it. This pane was ported from .vtl-host and took two of its three lines. */
.dms-pane { position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden; overscroll-behavior: contain; --profile-min-h: 0; }
/* A tab whose free corner lies over a positioned bar of the map's own (Big Picture's filter bar, z-index 55)
raises the info BUTTON above that bar (src/tabInfo.js pos.raise). Only the button: the one-time pointer
stays under the bar, so it covers no filter even visually. Measured: nothing interactive under the button,
the button topmost at its centre, and a click opens the popup, at 1440, 1024 and 800, en and ar. */
.mi.dms-info-raise .mi-btn { z-index: 60; }
.dms-slot { position: relative; min-height: 100%; }
.dms-hint {
position: absolute; inset: 0; display: grid; place-items: center; text-align: center;

View file

@ -22,23 +22,32 @@
* pos.narrow overrides on a phone (the shell's `narrow`, max-width:760px), where the chrome differs:
* Regions' and UPG-100's strip is full at its END (selector + Share) and empty at its START, so the
* button goes to the start (placement 'top-left', which MapInfo resolves to the logical start, so
* Arabic mirrors); Gospel's panel spans to 345px of 390, so its button takes the corner above the
* zoom column. Measured with the same probe at 390 in en and ar.
* ★ ADD AN EXAMPLE ONLY WHEN ITS FILE IS IN THE TREE. A missing file renders the popup's "not
* available" box in front of the audience; an example that is not there yet is simply left out.
* Arabic mirrors); Gospel's and Cities' legend panels span to 344px of 390, so their button takes the corner
* above the zoom column. Measured with the same probe at 390 in en and ar.
* ★ AN EXAMPLE'S FILES SHIP WITH IT. MapInfo drops an example whose lead image fails to load (quietly,
* title and all), so a link can land before its files; but a popup is only worth opening once they are
* in the tree, so land the two together.
* ★ KEYS MUST NOT CONTAIN A DOT beyond the area separator: the gallery has no vue-i18n, so MapInfo
* resolves through tStatic, which splits the path on "." and would never find a sentence-shaped key.
*/
// The UPG-100 zone's pre-generated packet ('full' variant) and card deck, shot once and shared: Simple
// Research's packet is the same renderer, method and variant (usePosterPrints.printPacket, 'full'), so it
// shows these pages too, CAPTIONED as an example zone, because their data is a UPG-100 zone and not a
// Simple Research selection.
const MEC = 'previews/examples/middle-east-core'
const MEC_PAGES = Array.from({ length: 12 }, (_, i) => `${MEC}/packet-p${String(i + 1).padStart(2, '0')}.webp`)
const MEC_CARDS = Array.from({ length: 70 }, (_, i) => `${MEC}/card-${String(i + 1).padStart(2, '0')}.webp`)
export const TAB_INFO = {
'upg-100': {
storageKey: 'upg-100',
pos: { side: '56px', narrow: { placement: 'top-left', side: '12px' } },
leadKey: 'mapInfo.tab.upg-100.lead',
takeawayKey: 'mapInfo.tab.upg-100.takeaway',
// The packet pages (12) and the card deck (70) join this list when their pre-generated shots land
// in assets/previews/examples/middle-east-core/.
examples: [
{ kind: 'image', src: 'previews/middle-east-core.webp', full: 'posters/middle-east-core.html' },
{ kind: 'pages', pages: MEC_PAGES },
{ kind: 'deck', cards: MEC_CARDS },
],
},
'regions-dashboard': {
@ -58,7 +67,10 @@ export const TAB_INFO = {
},
'dashboard-plus-grid': {
storageKey: 'dashboard-plus-grid',
pos: { top: '46px' },
// top 46px is the empty END of Big Picture's filter area at every width (1 row at 1440, 2 at 1024,
// 3 at 800), but that bar is a positioned layer (z-index 55) painted above the button, which left it
// unclickable (rega, 1440). raise lifts the BUTTON over the bar; the pointer stays under it.
pos: { top: '46px', raise: true },
leadKey: 'mapInfo.tab.dashboard-plus-grid.lead',
takeawayKey: 'mapInfo.tab.dashboard-plus-grid.takeaway',
// Big Picture's export is a spreadsheet, so its example is a real excerpt of one (sr): the six
@ -87,7 +99,10 @@ export const TAB_INFO = {
pos: { top: '56px', side: '56px' },
leadKey: 'mapInfo.tab.research-map-plus-grid.lead',
takeawayKey: 'mapInfo.tab.research-map-plus-grid.takeaway',
examples: [],
// Its own poster joins when rega's shot is in the tree.
examples: [
{ kind: 'pages', pages: MEC_PAGES, titleKey: 'mapInfo.tab.research-map-plus-grid.examplePacket' },
],
},
'prayer-map': {
storageKey: 'prayer-map',
@ -98,7 +113,7 @@ export const TAB_INFO = {
},
'cities-internet': {
storageKey: 'cities-internet',
pos: { side: '56px' },
pos: { side: '56px', narrow: { side: '4px' } },
leadKey: 'mapInfo.tab.cities-internet.lead',
takeawayKey: 'mapInfo.tab.cities-internet.takeaway',
examples: [],

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View file

@ -46,7 +46,7 @@
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}`;function Lm(e){let t=0;for(let o=0;o<e.length;o++)t=(t<<5)-t+e.charCodeAt(o),t|=0;return"s"+Math.abs(t).toString(36)}const Cm={class:"pin-hint-pill"},Mm=["aria-label"],Em={__name:"PinHint",setup(e){const t=(H,j)=>qt(H)||j,o={"messages.pinHint.tapDot":"Tap a dot to see who still needs prayer"},{t:n,te:a}=iu("",{fallback:H=>o[H]}),s=De("uiStore",null),i=De("profileConfig",null),r=ce(()=>(s==null?void 0:s.theme)==="dark"),l=ce(()=>{var Q;const H=(Q=i==null?void 0:i.value)==null?void 0:Q.pinHintLabel;if(H)return H;const j="Tap a coloured dot — someone still needs prayer";try{return a("messages.pinHint.tapDot")?n("messages.pinHint.tapDot"):j}catch{return j}}),c=oe(!1),u=oe(!0);let f=null;const b=oe(!0);let v=[];const k=["inert","hidden","aria-hidden"];function A(H){const j=[];let Q=H;for(;Q;)Q.nodeType===1&&j.push(Q),Q=Q.parentNode||Q.host||null;return j}function N(H){return A(H).some(j=>j.hasAttribute("inert")||j.hasAttribute("hidden")||j.getAttribute("aria-hidden")==="true")}function x(H){const j=()=>{const Q=!N(H);Q!==b.value&&(b.value=Q,Q&&mo(z))};if(j(),!(typeof MutationObserver>"u"))for(const Q of A(H))try{const re=new MutationObserver(j);re.observe(Q,{attributes:!0,attributeFilter:k}),v.push(re)}catch{}}const w=oe(null);let $=null,_=null,S=null,M=null,T=null,P=null,y=[],K=null,q=null;function B(){c.value=!1}function R(H,j){var re;let Q=H==null?void 0:H.parentElement;for(let de=0;Q&&de<12;de++,Q=Q.parentElement){if(Q===j||(re=Q.classList)!=null&&re.contains("profile-loader"))return null;let me="";try{me=getComputedStyle(Q).position}catch{}if(me&&me!=="static"){const Ne=Q.getBoundingClientRect();if(Ne.height>=120&&Ne.width>=160)return Q}}return null}function J(H){var Q;const j=(Q=M==null?void 0:M.querySelector)==null?void 0:Q.call(M,".legend-mobile-sheet");if(!j)return 1/0;if(T&&P!==j){try{T.disconnect()}catch{}T=null}if(!T&&typeof ResizeObserver<"u")try{T=new ResizeObserver(()=>z()),T.observe(j),P=j}catch{T=null}try{const re=j.getBoundingClientRect();return!(re.height>16)||!(re.width>16)||H.right<=re.left||re.right<=H.left?1/0:re.top}catch{return 1/0}}function z(){const H=w.value;if(!(!H||!u.value||!b.value))try{H.style.removeProperty("--pin-hint-lift");const j=H.getBoundingClientRect();if(!j.height)return;const Q=Math.min(window.innerHeight,J(j)),re=Math.round(j.bottom-Q);re>0&&H.style.setProperty("--pin-hint-lift",re+8+"px")}catch{}}function X(H){const j=w.value;if(!j||!(H!=null&&H.querySelector))return;let Q=0;try{const de=H.querySelector(".mapboxgl-ctrl-bottom-right .mapboxgl-ctrl-attrib, .mapboxgl-ctrl-bottom-right .doxa-attribution, .doxa-attribution");if(de){const me=de.getBoundingClientRect(),Ne=j.parentElement,ke=Ne?Ne.getBoundingClientRect():null;if(Q=ke?Math.max(me.height||0,ke.bottom-me.top):me.height||0,q!==de&&typeof ResizeObserver<"u"){if(S)try{S.disconnect()}catch{}q=de,S=new ResizeObserver(()=>X(H)),S.observe(de)}}}catch{}const re=Math.min(Math.max(Math.ceil(Q)||20,20),72);j.style.setProperty("--pin-hint-attrib-clear",re+"px"),z()}return $o(()=>{var re,de,me;const H=ft(),j=(me=(de=(re=H==null?void 0:H.proxy)==null?void 0:re.$el)==null?void 0:de.getRootNode)==null?void 0:me.call(de);if(!j||typeof j.querySelector!="function")return;M=j;let Q=0;$=setInterval(()=>{Q++;const Ne=j.querySelector("canvas.maplibregl-canvas, canvas.mapboxgl-canvas");Ne?(clearInterval($),$=null,c.value=!0,mo(()=>{const ke=R(Ne,j),Ve=w.value;if(ke&&Ve&&Ve.parentElement!==ke)try{ke.appendChild(Ve),_=ke}catch{}if(ke&&x(ke),ke&&typeof IntersectionObserver<"u")try{f=new IntersectionObserver(xe=>{const $e=xe[xe.length-1],Ue=!!$e&&$e.isIntersecting&&$e.intersectionRatio>=.2;Ue!==u.value&&(u.value=Ue,Ue&&mo(z))},{threshold:[0,.2,.5]}),f.observe(ke)}catch{}X(j),z(),y.push(setTimeout(z,1200),setTimeout(z,3e3),setTimeout(z,6e3));try{const xe=w.value&&w.value.offsetParent||null;if(xe&&typeof ResizeObserver<"u"){if(K)try{K.disconnect()}catch{}K=new ResizeObserver(()=>z()),K.observe(xe)}}catch{}})):Q>40&&(clearInterval($),$=null)},300)}),Fo(()=>{var H;if($&&clearInterval($),f){try{f.disconnect()}catch{}f=null}for(const j of v)try{j.disconnect()}catch{}if(v=[],S){try{S.disconnect()}catch{}S=null}if(q=null,K){try{K.disconnect()}catch{}K=null}if(T){try{T.disconnect()}catch{}T=null}P=null;for(const j of y)clearTimeout(j);if(y=[],M=null,_&&((H=w.value)==null?void 0:H.parentElement)===_)try{_.removeChild(w.value)}catch{}_=null}),Uo(`
}`;function Lm(e){let t=0;for(let o=0;o<e.length;o++)t=(t<<5)-t+e.charCodeAt(o),t|=0;return"s"+Math.abs(t).toString(36)}const Cm={class:"pin-hint-pill"},Mm=["aria-label"],Em={__name:"PinHint",setup(e){const t=(H,j)=>qt(H)||j,o={"messages.pinHint.tapDot":"Tap a dot to see who still needs prayer"},{t:n,te:a}=iu("",{fallback:H=>o[H]}),s=De("uiStore",null),i=De("profileConfig",null),r=ce(()=>(s==null?void 0:s.theme)==="dark"),l=ce(()=>{var Q;const H=(Q=i==null?void 0:i.value)==null?void 0:Q.pinHintLabel;if(H)return H;const j="Tap a coloured dot — someone still needs prayer";try{return a("messages.pinHint.tapDot")?n("messages.pinHint.tapDot"):j}catch{return j}}),c=oe(!1),u=oe(!0);let f=null;const b=oe(!0);let v=[];const k=["inert","hidden","aria-hidden"];function A(H){const j=[];let Q=H;for(;Q;)Q.nodeType===1&&j.push(Q),Q=Q.parentNode||Q.host||null;return j}function N(H){return A(H).some(j=>j.hasAttribute("inert")||j.hasAttribute("hidden")||j.getAttribute("aria-hidden")==="true")}function x(H){const j=()=>{const Q=!N(H);Q!==b.value&&(b.value=Q,Q&&mo(z))};if(j(),!(typeof MutationObserver>"u"))for(const Q of A(H))try{const re=new MutationObserver(j);re.observe(Q,{attributes:!0,attributeFilter:k}),v.push(re)}catch{}}const w=oe(null);let $=null,_=null,S=null,M=null,T=null,P=null,y=[],K=null,q=null;function B(){c.value=!1}function R(H,j){var re;let Q=H==null?void 0:H.parentElement;for(let de=0;Q&&de<12;de++,Q=Q.parentElement){if(Q===j||(re=Q.classList)!=null&&re.contains("profile-loader"))return null;let me="";try{me=getComputedStyle(Q).position}catch{}if(me&&me!=="static"){const Ne=Q.getBoundingClientRect();if(Ne.height>=120&&Ne.width>=160)return Q}}return null}function J(H){var Q;const j=(Q=M==null?void 0:M.querySelector)==null?void 0:Q.call(M,".legend-mobile-sheet");if(!j)return 1/0;if(T&&P!==j){try{T.disconnect()}catch{}T=null}if(!T&&typeof ResizeObserver<"u")try{T=new ResizeObserver(()=>z()),T.observe(j),P=j}catch{T=null}try{const re=j.getBoundingClientRect();return!(re.height>16)||!(re.width>16)||H.right<=re.left||re.right<=H.left?1/0:re.top}catch{return 1/0}}function z(){const H=w.value;if(!(!H||!u.value||!b.value))try{H.style.removeProperty("--pin-hint-lift");const j=H.getBoundingClientRect();if(!j.height)return;const Q=Math.min(window.innerHeight,J(j)),re=Math.round(j.bottom-Q);re>0&&H.style.setProperty("--pin-hint-lift",re+8+"px")}catch{}}function X(H){const j=w.value;if(!j||!(H!=null&&H.querySelector))return;let Q=0;try{const de=H.querySelector(".mapboxgl-ctrl-bottom-right .mapboxgl-ctrl-attrib, .maplibregl-ctrl-bottom-right .maplibregl-ctrl-attrib, .mapboxgl-ctrl-bottom-right .doxa-attribution, .maplibregl-ctrl-bottom-right .doxa-attribution, .doxa-attribution");if(de){const me=de.getBoundingClientRect(),Ne=j.parentElement,ke=Ne?Ne.getBoundingClientRect():null;if(Q=ke?Math.max(me.height||0,ke.bottom-me.top):me.height||0,q!==de&&typeof ResizeObserver<"u"){if(S)try{S.disconnect()}catch{}q=de,S=new ResizeObserver(()=>X(H)),S.observe(de)}}}catch{}const re=Math.min(Math.max(Math.ceil(Q)||20,20),72);j.style.setProperty("--pin-hint-attrib-clear",re+"px"),z()}return $o(()=>{var re,de,me;const H=ft(),j=(me=(de=(re=H==null?void 0:H.proxy)==null?void 0:re.$el)==null?void 0:de.getRootNode)==null?void 0:me.call(de);if(!j||typeof j.querySelector!="function")return;M=j;let Q=0;$=setInterval(()=>{Q++;const Ne=j.querySelector("canvas.maplibregl-canvas, canvas.mapboxgl-canvas");Ne?(clearInterval($),$=null,c.value=!0,mo(()=>{const ke=R(Ne,j),Ve=w.value;if(ke&&Ve&&Ve.parentElement!==ke)try{ke.appendChild(Ve),_=ke}catch{}if(ke&&x(ke),ke&&typeof IntersectionObserver<"u")try{f=new IntersectionObserver(xe=>{const $e=xe[xe.length-1],Ue=!!$e&&$e.isIntersecting&&$e.intersectionRatio>=.2;Ue!==u.value&&(u.value=Ue,Ue&&mo(z))},{threshold:[0,.2,.5]}),f.observe(ke)}catch{}X(j),z(),y.push(setTimeout(z,1200),setTimeout(z,3e3),setTimeout(z,6e3));try{const xe=w.value&&w.value.offsetParent||null;if(xe&&typeof ResizeObserver<"u"){if(K)try{K.disconnect()}catch{}K=new ResizeObserver(()=>z()),K.observe(xe)}}catch{}})):Q>40&&(clearInterval($),$=null)},300)}),Fo(()=>{var H;if($&&clearInterval($),f){try{f.disconnect()}catch{}f=null}for(const j of v)try{j.disconnect()}catch{}if(v=[],S){try{S.disconnect()}catch{}S=null}if(q=null,K){try{K.disconnect()}catch{}K=null}if(T){try{T.disconnect()}catch{}T=null}P=null;for(const j of y)clearTimeout(j);if(y=[],M=null,_&&((H=w.value)==null?void 0:H.parentElement)===_)try{_.removeChild(w.value)}catch{}_=null}),Uo(`
@media print { .pin-hint { display: none !important; } }
.pin-hint { position: absolute; left: 50%;
bottom: calc(var(--pin-hint-attrib-clear, 20px) + 12px + var(--pin-hint-lift, 0px)); transform: translateX(-50%);

View file

@ -70,7 +70,7 @@
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
<!-- Bundle IIFE — registers the <all-people-groups> custom element. -->
<script src="../all-people-groups.js" integrity="sha384-iuTg/9NOhnbwRr9AmR7DEZXqNBIfrw8AHrgHD6U+ZcJGDHXFH5uSQTDzbfIAPA9e" crossorigin="anonymous"></script>
<script src="../all-people-groups.js" integrity="sha384-1tlJDUPXPgioha7BM6CPslfjD+zvorBn1KsXs2G3qBhe28oxuckN/0pAG6hfo3sz" crossorigin="anonymous"></script>
<script>
// Mapbox token resolution — the A3 ladder, in precedence order:

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

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