Shortcuts now register on mount (base keys work from first paint) and the
map rebuilds when /api/configs lands, so config-gated keys (i/D/P/p) no
longer vanish when the fetch loses the old 2-second race.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Earth Online entry and panel now show on any deployment whose backend
has CLOUDFLARE_API_KEY (runtime cloudFlare configs flag); the social
sections (composer / feed / visitor map / beacon) stay gated on the pulse
beacon backend. VITE_PULSE_URL is renamed to VITE_PULSE_BEACON_URL, no
legacy fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
vaul-vue swallows open-auto-focus, so the trigger stayed focused inside
#mainpart while it was aria-hidden — Chrome blocked that, and Tab then
walked the hidden page. Also drop the leftover Bootstrap scrollspy
tabindex wrapping the homepage.
Co-Authored-By: Cursor Grok 4.6 <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
The dispatcher checked the overlay state on keydown, but actions run
from the 10ms key-pool timer — an overlay opening inside that window
still had the queued action fire behind it. The callback now rechecks
before running and clears the pool either way.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The focal-point math measured from the scroll container's edge, so the
px-4 padding and the m-auto centering offset made the graph drift away
from the pinch midpoint. Anchoring on the SVG's own rect and correcting
against a post-render re-measure keeps the point under the fingers.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The panel is opt-in and easily closed, so the global info mask only
hurt readability there — masking targets the main page a screenshot
would capture, not a panel the visitor chose to open.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Every shortcut in the map is a home-page action, so firing one from
behind a Dialog, Sheet or Drawer acted out of sight — and the
network-heavy ones (speed test, connectivity, DNS leak) spent the
visitor's bandwidth on a result they could not see. Pressing "?" inside
the Advanced Tools drawer also stacked the Help dialog on top of it.
The dispatcher now ignores keystrokes while any overlay is open. The
rule keys off the component's form rather than its purpose: the ui/
roots register themselves through use-overlay-shortcuts.js, so anything
built on them inherits it and overlays nest. Esc and the native
scrolling keys are untouched.
Drops the "f" fullscreen shortcut, which only ever acted on an open
drawer; the drawer header keeps its button.
Also fixes two long-standing dispatcher bugs the same code touches:
registration appended to a module-level map, so re-mounting Home (route
away and back) stacked a second copy of every action and left the first
pointing at unmounted refs; and the typing guard covered INPUT but not
TEXTAREA or contenteditable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DNS hijacking, captive portals and LAN echoes hand back reserved
addresses. The card sent them straight to /api/*, where requirePublicIP
rejected them with a 400 the visitor saw as a failed source.
fetchIP now gates on isUsablePublicIP — the shared definition the guard
itself uses — so the address is still shown and the card settles, only
the detail request is skipped. selectIPGeoSource's refetch filter moves
to the same predicate; isValidIP accepted reserved space and re-sent the
400 on every database switch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Upstream answers 200 with `loc` / `org` / `country` absent for anycast,
bogon and degraded responses. Splitting them unguarded threw, and
geo-handler turned that into a 500 the visitor's card surfaced as a
failed source.
Missing coordinates now degrade to null instead of Number('') === 0,
which would have placed those IPs at the origin of the coordinate
system; a missing org yields empty asn / org; an unknown ISO code falls
back to the label that was already written but unreachable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tool now emits persona:finished, and the report carries the grade
against the declared country plus each check's verdict. Verdict-only by
design: the live results' detail objects hold what the visitor supplied
(issuing bank, the country a shared position resolved to), and a report
link is readable by anyone who has it, so the builder drops them.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The one tool whose measurements leave the browser gets its own section:
what travels with a run, that nothing is stored, and the two opt-in
inputs — a coarsened position and a card issuer prefix checked against a
third-party database.
New sign-in-gated advanced tool: declare a target country, run the
browser-side probes, and get a graded report of how closely the persona
sites can see matches the one you expect — with four-locale copy and
Node-runner coverage for every non-visual piece.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POST /api/persona/evaluate — thin proxy to the private evaluator, with
smoke tests and the AGENTS note on its header handling.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Searchable, virtualized country selector over the full ISO 3166-1 set,
localized via Intl.DisplayNames.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>