diff --git a/ghostkey-web/src/App.tsx b/ghostkey-web/src/App.tsx
index bab01bc..541e03c 100644
--- a/ghostkey-web/src/App.tsx
+++ b/ghostkey-web/src/App.tsx
@@ -359,13 +359,13 @@ export default function App() {
Skip to content
- {/* Wait for the first /health probe before showing the network
- banner. `network` defaults to "testnet", so rendering early
- flashed an "Alpha: testnet, don't use real money" warning on
- every reload of a mainnet vault before flipping to the live
- banner. A wrong-network safety warning, even for a moment,
- is worse than a brief absence. */}
- {health !== "unknown" && }
+ {/* Only show the network banner once /health has SUCCEEDED.
+ `network` defaults to "testnet", so rendering before the
+ probe (or after a failed one) told a mainnet owner
+ "Alpha: testnet, don't use real-money keys" — false and
+ alarming at the exact moment the server was already down.
+ When the probe fails, ServerOfflineBanner below covers it. */}
+ {health === "ok" && }
{demoMode && }
{health === "offline" && }
{/*
diff --git a/ghostkey-web/src/SignInPortal.tsx b/ghostkey-web/src/SignInPortal.tsx
index 4866ad2..2679221 100644
--- a/ghostkey-web/src/SignInPortal.tsx
+++ b/ghostkey-web/src/SignInPortal.tsx
@@ -81,6 +81,10 @@ type Phase =
export function SignInPortal({ onNavigate }: Props) {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
+ // Mistyped passwords are the #1 sign-in failure, and the field hides
+ // every character. A show toggle costs nothing and rescues most of
+ // them (Bitcoin UX principle: password UX is money UX).
+ const [showPassword, setShowPassword] = useState(false);
const [phase, setPhase] = useState({ kind: "idle" });
const [error, setError] = useState(null);
@@ -351,17 +355,27 @@ export function SignInPortal({ onNavigate }: Props) {
- {
- setPassword(e.target.value);
- reset();
- }}
- autoComplete="current-password"
- className="input"
- disabled={phase.kind === "looking" || phase.kind === "unsealing"}
- />
+