diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md
index 9329e4122..aceebe477 100644
--- a/docs/CURRENT-STATE.md
+++ b/docs/CURRENT-STATE.md
@@ -2935,8 +2935,10 @@ its criteria cannot be silently ignored.
Fire Perimeters uses capped, timed server reads with stale-on-error caching and a per-client limit. Unchanged snapshots retain geometry; link checks cancel on disable or selection change, and the row legend shows reported containment.
Street Level lives in the right context rail next to CCTV as an ordinary
-collapsible panel (`#street-level-panel`, share token `0`, option owner
-`street-level`) and starts collapsed. It is modelled on the iD editor's photo
+collapsible panel (`#street-level-panel`, layer share token `0`, option owner
+`street-level`, panel `ui` token `t`) and starts collapsed. It opens itself
+only when the user (or voice, or a tool) switches the layer on or a photo
+opens, never on a saved-state restore, and never stores those automatic opens. It is modelled on the iD editor's photo
overlay: one panel, a PROVIDERS chip per registered imagery provider, shared
360°/flat and captured-since filters (stored as relative days so a link keeps
its meaning), one viewer host, and one on-globe credit per active provider.
diff --git a/src/sharelink.celestial.test.mjs b/src/sharelink.celestial.test.mjs
index 567bb09dd..d9e3395d2 100644
--- a/src/sharelink.celestial.test.mjs
+++ b/src/sharelink.celestial.test.mjs
@@ -184,6 +184,24 @@ test('a collapsed Recent Imagery panel survives the share-link round trip beside
] });
});
+test('a collapsed Street Level panel survives the share-link round trip (review P3)', () => {
+ const manager = makeManager();
+ manager.setPanelStateProvider(() => ({ specs: [
+ { id: 'cctv-panel', collapsed: false },
+ { id: 'street-level-panel', collapsed: true },
+ ] }));
+ clearTimeout(manager._debounceTimer);
+ manager._updateHash();
+ const params = new URLSearchParams(window.location.hash.slice(1));
+ assert.equal(params.get('ui'), 'v.c.0_t.c.1');
+ const restored = makeManager(`#v=2&lat=10&lon=20&ui=${params.get('ui')}`)
+ .parseInitialHash();
+ assert.deepEqual(restored.panelState, { specs: [
+ { id: 'cctv-panel', collapsed: false, pinned: null },
+ { id: 'street-level-panel', collapsed: true, pinned: null },
+ ] });
+});
+
test('camera-only, partial, and malformed panel shares remain valid incoming state', () => {
const cameraOnly = makeManager('#lat=10&lon=20').parseInitialHash();
assert.ok(cameraOnly);
diff --git a/src/sharelink.js b/src/sharelink.js
index 36862dcbf..e2a9e41b2 100644
--- a/src/sharelink.js
+++ b/src/sharelink.js
@@ -34,6 +34,7 @@ const SHARE_PANEL_STATE_REGISTRY = Object.freeze([
{ id: 'data-panel', token: 'd', pinnable: false },
{ id: 'cctv-panel', token: 'v', pinnable: false },
{ id: 'recent-imagery-panel', token: 'i', pinnable: false },
+ { id: 'street-level-panel', token: 't', pinnable: false },
{ id: 'radio-panel', token: 'r', pinnable: false },
{ id: 'scene-panel', token: 's', pinnable: false },
{ id: 'global-context-panel', token: 'g', pinnable: false },
diff --git a/src/streetLevelMarkup.test.mjs b/src/streetLevelMarkup.test.mjs
index 9e327bec7..3ace37ca2 100644
--- a/src/streetLevelMarkup.test.mjs
+++ b/src/streetLevelMarkup.test.mjs
@@ -83,6 +83,15 @@ test('the keyless state gates the controls rather than leaving dead buttons', ()
);
assert.match(html, /