improve keyboard navigation

Hunter
Monday, July 6th, 2026 at 1:14 PM

resources/map.js+8-1
resources/panel.js+10-4
resources/styles.css+2 

resources/map.js
 27  		return true;
 28  	}
 29  
    -	// Rewrite the address bar to the event's deep link (no reload / history entry).
 30 +	// Rewrite the address bar to the event's deep link (no reload / history entry), so the
 31 +	// app keeps its single-page feel: Back leaves the app rather than showing the last
 32 +	// event you viewed.
 33  	function syncUrl(m) {
 34  		if (window.history && history.replaceState) {
 35  			history.replaceState(null, "", fmt.eventUrl(m));

120  			marker.setZIndexOffset((events.length - i) * Z_STEP);
121  			m._marker = marker; // so selectEvent can reach this pin's DOM
122  			marker.on("click", function () { selectMarker(m, true); });
123 +			// Leaflet gives focused markers tabIndex + role=button but only wires Enter for
124 +			// popups, so activate the pin ourselves (13 = Enter, matching Leaflet's own idiom)
125 +			marker.on("keypress", function (e) {
126 +				if (e.originalEvent.keyCode === 13) { selectMarker(m, true); }
127 +			});
128  		});
129  
130  		window.panel.setIntroData(events, selectMarker, pastMode);
resources/panel.js
181  	}
182  
183  	// Build the intro's upcoming-events table and return the segments to type (one per
    -	// cell). Rows are clickable. No events -> a single "No upcoming events." line. A flex
    -	// list of <div>s (not a <table>) so columns collapse cleanly.
184 +	// cell). Rows are <a> deep links (clickable + keyboard/new-tab). No events -> a single
185 +	// "No upcoming events." line.
186  	function buildIntroList() {
187  		introListEl = null;
188  		if (!introEvents.length) {

198  
199  		var segments = [];
200  		introEvents.forEach(function (m) {
    -			var row = document.createElement("div");
201 +			// a real link: shareable, focusable, works with keyboard / new-tab / middle-click
202 +			var row = document.createElement("a");
203  			row.className = "intro-row";
    -			row.addEventListener("click", function () {
204 +			row.href = fmt.eventUrl(m);
205 +			// native anchor activation: mouse click and keyboard Enter both fire click here.
206 +			// modified clicks (new tab/window, middle button) fall through to the browser.
207 +			row.addEventListener("click", function (e) {
208 +				if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) { return; }
209 +				e.preventDefault();
210  				if (selectFromIntro) { selectFromIntro(m, true); }
211  			});
212  
resources/styles.css
102  	margin: 0 calc(-1 * var(--link-pad));
103  	cursor: pointer;
104  	-webkit-tap-highlight-color: transparent;
105 +	color: inherit;
106 +	text-decoration: none;
107  }
108  .intro-name {
109  	grid-column: 1;