When `gsd --web` is opened without the #token= hash fragment (manual URL
entry, bookmark, new tab), `authenticatedFetch` previously fell through to
a naked `fetch()` that always returned 401, flooding the console with
cascading errors and leaving the UI in a broken state with no recovery path.
Three changes:
1. `web/lib/auth.ts` — `authFetch()` now returns a synthetic 401 Response
when `getAuthToken()` returns null instead of delegating to bare fetch.
This makes missing-token failures consistent and immediately catchable
by all callers without a network round-trip.
2. `web/lib/gsd-workspace-store.tsx` — Added `"unauthenticated"` to
`WorkspaceStatus`. `refreshBoot()` now detects a 401 response from
/api/boot and patches `bootStatus` to `"unauthenticated"` instead of
throwing a generic error. This is a distinct state — not an error worth
retrying, but a configuration problem the user must resolve.
3. `web/components/gsd/app-shell.tsx` — Added an early-return guard that
renders a minimal "Authentication Required" screen when
`bootStatus === "unauthenticated"`. The screen explains the problem and
tells users to copy the full terminal URL (including `#token=…`) or
restart with `gsd --web`.
Fixes#2731