diff --git a/plugin-designs/research-dashboard/src/components/dashboards/DashboardDataTable.vue b/plugin-designs/research-dashboard/src/components/dashboards/DashboardDataTable.vue index 096f23e51..04fa0625a 100644 --- a/plugin-designs/research-dashboard/src/components/dashboards/DashboardDataTable.vue +++ b/plugin-designs/research-dashboard/src/components/dashboards/DashboardDataTable.vue @@ -491,19 +491,25 @@ useShadowStyles(` - Filtering or selecting above updates this list + {{ exportToast }} 2.2 at z1), so the existing bbox remains sufficient. // ── ★ THE PIN IS NOW SELF-CONTAINED (card BUILD-MAP-STYLE-MUST-BE-BEAUTIFUL) ────────── // The white ring used to depend on the GROUND for its visibility, and measured only // 1.09–1.24 against the light bins — so on pale countries the ring vanished and the dots @@ -346,12 +347,57 @@ function addLayers() { // // HIT-TESTING unchanged: 'rd-bubble-layer' keeps its id and stays the TOP layer; the halo // is a separate NON-interactive layer beneath it and must not be queried. - m.addLayer({ id: 'rd-bubble-halo', type: 'circle', source: 'rd-bubbles', - paint: { 'circle-radius': ['interpolate', ['linear'], ['zoom'], 1, 3.4, 6, 7.4], 'circle-color': '#fff', 'circle-opacity': 1, - // The outer edge: thin, navy, so the white ring has a hard boundary on ANY ground. - 'circle-stroke-width': ['interpolate', ['linear'], ['zoom'], 1, 0.6, 6, 1], 'circle-stroke-color': '#1a237e', 'circle-stroke-opacity': 0.9 } }) + // ── ★ ONE CIRCLE WITH A WHITE STROKE — NOT A NAVY/WHITE/NAVY SANDWICH ────────────────────── + // (Driver 2026-09-22: "the pins look like eyes, they look kind of creepy") + // + // THE SANDWICH WAS THE BUG, and the comment above described it accurately without noticing what + // it draws. Read outward from the centre at z1 the old construction was: + // navy disc r=1.6 → white field r=3.4 → navy stroke 0.6px + // dark centre, light middle, dark rim. That is the anatomy of an eye — pupil, sclera, iris + // outline — and it is why a bullseye is NAMED after one. Concentric rings sharing a centre are + // exactly the pattern the visual system fuses into a single object; sighting equipment exploits + // the same involuntary alignment. The inner disc sat at 47% of the halo radius at z1, squarely in + // the proportion that reads as a PUPIL rather than as a dot with a border. Worst when zoomed out, + // which is where a world map lives. + // + // THE OUTER NAVY BAND IS THE SPECIFIC CULPRIT: it closes the iris. Without it the same colours + // read as an ordinary pin. So the fix removes a band rather than changing a colour. + // + // MATCHES THE MAPBOX IDIOM, which is ONE layer with a stroke, not two stacked circles. The style + // spec draws `circle-stroke-width` OUTSIDE `circle-radius`, so a single layer already produces + // disc-plus-ring; the old code hand-rolled that and collected a third colour band for the + // trouble. Scaling the stroke with zoom (1.2 → 2) is the documented way to stop the ring + // swallowing the fill when circles are small and dense at low zoom. Cartographic practice agrees: + // Axis Maps and the ICA Commission both call for casings on symbols that OVERLAP rather than a + // global opaque halo, and Esri names halos the single biggest cost in marker drawing performance + // — we were paying that on a second full circle layer across every point. Research + sources: + // _qa-cycle/01-ideation-research/RESEARCH-pin-design-why-they-read-as-eyes.md + // + // ★ THE TWO DRIVER RULINGS THIS PRESERVES — do not undo either: + // 1. ONE FIXED COLOUR AT FULL OPACITY. Pins were once '#e67e22' at circle-opacity 0.6; the + // alpha was the real fault, compositing each pin with whatever bin sat beneath it so it + // rendered a different colour per country at 1.18–1.22 contrast. Never reintroduce pin + // alpha, never tint per bin. + // 2. WHITE RINGS around blue dots so the field does not read as a heat map, and all rings stay + // visible when zoomed out. Still here — it is now the stroke instead of a second layer. + // Navy #1a237e measures 12.24 / 11.09 / 10.20 / 9.26 / 8.20 / 7.29 across the ramp; unchanged. + // + // WHAT IS GIVEN UP, stated honestly: the outer navy band gave the white ring a hard boundary on + // ANY ground, so over the palest bin the ring now blends into the background. The ring's job is + // separating pins from EACH OTHER (it still does that) while the navy disc carries pin-vs-ground + // contrast. MEASURE the pale end before calling this settled. If separation genuinely fails + // there, the fix is a casing on overlapping symbols only — not a restored global dark rim. + // + // HIT-TESTING: 'rd-bubble-layer' keeps its id and is now the ONLY bubble layer, so every + // click/hover handler bound to that id is unaffected. 'rd-bubble-halo' no longer exists. m.addLayer({ id: 'rd-bubble-layer', type: 'circle', source: 'rd-bubbles', - paint: { 'circle-radius': ['interpolate', ['linear'], ['zoom'], 1, 1.6, 6, 5], 'circle-color': '#1a237e', 'circle-opacity': 1 } }) + paint: { + 'circle-radius': ['interpolate', ['linear'], ['zoom'], 1, 2.2, 6, 5.5], + 'circle-color': '#1a237e', 'circle-opacity': 1, + 'circle-stroke-color': '#ffffff', + 'circle-stroke-width': ['interpolate', ['linear'], ['zoom'], 1, 1.2, 6, 2], + 'circle-stroke-opacity': 1, + } }) // selection pen — a prominent neon ring over the selected PG pin(s), drawn ABOVE the normal bubbles. m.addSource('rd-bubble-selected', { type: 'geojson', data: selectedGeo() }) m.addLayer({ id: 'rd-bubble-selected', type: 'circle', source: 'rd-bubble-selected',