diff --git a/plugin-designs/gospel-resources/resources-map.vue b/plugin-designs/gospel-resources/resources-map.vue
index 0650c28a4..dae1fb796 100644
--- a/plugin-designs/gospel-resources/resources-map.vue
+++ b/plugin-designs/gospel-resources/resources-map.vue
@@ -542,12 +542,34 @@ function openFromUrl() {
// "a red dot can sit on a green country" is the thing that stops a correct map reading as
// a bug, so it stays one tap away rather than being dropped on small screens.
const narrow = ref(false)
+// ONE state for both widths. It used to govern the mobile sheet only — every section read
+// `!narrow || legendOpen`, so on desktop the legend was always open and there was no way to
+// shut it. Now `legendOpen` alone decides, and desktop gets the same collapse control mobile
+// has always had.
const legendOpen = ref(true)
+// Remembered for the SESSION, not forever: a reader who collapses the legend to see the map
+// wants it to stay collapsed while they work, and wants a fresh visit to start from the
+// designed state rather than from a decision they made once and forgot. sessionStorage is
+// exactly that lifetime. Wrapped because a sandboxed or storage-blocked embed throws on
+// access, and a legend that cannot remember is far better than a map that will not load.
+const LEGEND_KEY = 'gm.legendOpen'
+function rememberLegend(v) {
+ try { window.sessionStorage?.setItem(LEGEND_KEY, v ? '1' : '0') } catch (_) {}
+}
+function recallLegend() {
+ try { const v = window.sessionStorage?.getItem(LEGEND_KEY); return v == null ? null : v === '1' }
+ catch (_) { return null }
+}
+function toggleLegend() { legendOpen.value = !legendOpen.value; rememberLegend(legendOpen.value) }
function syncNarrow() {
const n = typeof window !== 'undefined' && window.innerWidth < 700
if (n === narrow.value) return
narrow.value = n
- legendOpen.value = !n
+ // A remembered choice wins over the width default. Without this, dragging a window across
+ // the breakpoint would silently reopen a legend the reader had deliberately closed — the
+ // resize would be undoing their decision rather than responding to it.
+ const remembered = recallLegend()
+ legendOpen.value = remembered == null ? !n : remembered
}
if (typeof window !== 'undefined') {
syncNarrow()
@@ -1950,6 +1972,21 @@ useShadowStyles(
// Narrow screens: the rail is still on the right, so the legend must stop short
// of it rather than run under it. 46px = 32px button + 3px inset + clearance.
'@media(max-width:480px){.gm-legend{width:auto;inset-inline-end:46px}}' +
+ // The caret's box matches .stl-collapse-btn exactly (22x20, 5px radius, 1px border) so the
+ // two legends read as the same control. The colours are this map's, because the palette is
+ // the map's and the geometry is the fleet's.
+ // Collapsed, the panel must SHRINK TO THE CARET. Hiding the contents alone would leave a
+ // 380px box of empty background sitting over the map — the reader would have pressed
+ // collapse and got a blank panel instead of their map back, which is worse than no control.
+ '.gm-legend.closed{width:auto;padding:4px;background:rgba(243,243,241,.92)}' +
+ '.gm-legend.closed .gm-collapse{position:static;inset:auto}' +
+ '.gm-collapse{position:absolute;inset-block-start:8px;inset-inline-end:8px;width:22px;height:20px;' +
+ 'display:flex;align-items:center;justify-content:center;background:rgba(209,212,205,.4);' +
+ 'border:1px solid #d1d4cd;border-radius:5px;cursor:pointer;color:#6d766e;padding:0;' +
+ 'transition:transform .3s ease-out,color .1s,background .1s;transform-origin:center}' +
+ '.gm-collapse:hover{color:#3b463d;background:rgba(146,177,149,.25);border-color:#92b195}' +
+ '.gm-collapse svg{transition:transform .18s;transform:rotate(180deg)}' +
+ '.gm-collapse.closed svg{transform:rotate(0deg)}' +
'.is-dark .gm-legend{background:rgba(19,32,25,.95);color:#e5e7eb}' +
'.gm-title{font-weight:700;font-size:15.5px;margin-bottom:5px}' +
'.gm-keys{display:flex;flex-wrap:wrap;gap:5px 12px}' +
@@ -2172,7 +2209,7 @@ useShadowStyles(