diff --git a/scripts/span-codeman.sh b/scripts/span-codeman.sh
new file mode 100755
index 00000000..06c98275
--- /dev/null
+++ b/scripts/span-codeman.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+#
+# span-codeman.sh — open a Codeman window stretched across ALL displays, so that
+# in-page floating session panels can be dragged from one physical monitor to
+# the other. Spawned by the header "multi-monitor" button (POST
+# /api/system/span-displays), or run by hand at the desk.
+#
+# ── PREREQUISITE (one-time, manual) ──────────────────────────────────────────
+# System Settings → Desktop & Dock → turn OFF "Displays have separate Spaces",
+# then LOG OUT and back in. Until you do, macOS keeps every window on a single
+# display and this script's window will clamp to one monitor instead of spanning.
+# (Equivalent CLI: `defaults write com.apple.spaces spans-displays -bool true`,
+# still needs a re-login. Revert with `-bool false`.)
+#
+# Why a maximized --app window and not fullscreen: browser fullscreen is
+# per-display and will NOT span. We size a windowed app to the union of all
+# displays instead. macOS only.
+#
+set -euo pipefail
+
+URL="${1:-http://localhost:5000}"
+
+# Union rect of all displays in top-left-origin points — exactly what Chromium's
+# --window-position/--window-size expect. Finder's desktop window bounds already
+# encloses every monitor (and handles a monitor placed left/above via a negative
+# origin), so no per-display math or coordinate flipping is needed.
+bounds=$(osascript -e 'tell application "Finder" to get bounds of window of desktop')
+X=$(echo "$bounds" | awk -F', *' '{print $1}')
+Y=$(echo "$bounds" | awk -F', *' '{print $2}')
+R=$(echo "$bounds" | awk -F', *' '{print $3}')
+B=$(echo "$bounds" | awk -F', *' '{print $4}')
+W=$((R - X))
+H=$((B - Y))
+echo "Display union: position ${X},${Y} size ${W}x${H}"
+
+# Pick a Chromium-family browser. Brave leads the list — plain Google Chrome
+# bounced when launched this way on the desk machine (created its profile then
+# exited without a window). Force a specific one with, e.g.,
+# BROWSER="Google Chrome" ./span-codeman.sh
+app="${BROWSER:-}"
+if [ -z "$app" ]; then
+ for c in "Brave Browser" "Google Chrome" "Google Chrome Beta" "Chromium" "Microsoft Edge"; do
+ [ -x "/Applications/$c.app/Contents/MacOS/$c" ] && app="$c" && break
+ done
+fi
+bin="/Applications/$app.app/Contents/MacOS/$app"
+[ -n "$app" ] && [ -x "$bin" ] || { echo "No Chrome-family browser found (BROWSER='$app')" >&2; exit 1; }
+
+# A dedicated, PER-BROWSER profile forces a FRESH instance — an already-running
+# browser would hand the URL to itself and silently ignore the geometry flags.
+# Per-browser so a Chrome-made profile can't confuse Brave (or vice-versa).
+slug=$(echo "$app" | tr '[:upper:] ' '[:lower:]-')
+profile="$HOME/.codeman-gesture-$slug"
+
+echo "Browser: $bin"
+echo "URL: $URL"
+
+# Detach so the caller (terminal / web server) isn't blocked for the window's life.
+nohup "$bin" \
+ --app="$URL" \
+ --user-data-dir="$profile" \
+ --window-position="${X},${Y}" \
+ --window-size="${W},${H}" \
+ --no-first-run \
+ --no-default-browser-check \
+ >/dev/null 2>&1 &
+
+echo "Launched spanning window (pid $!)."
+echo "If it filled only one monitor, the 'separate Spaces' prerequisite above"
+echo "isn't active yet — toggle it off, log out/in, and re-run."
diff --git a/src/web/public/index.html b/src/web/public/index.html
index d78490eb..b5473817 100644
--- a/src/web/public/index.html
+++ b/src/web/public/index.html
@@ -105,7 +105,8 @@
-