Terminal sign-in

Terminal / Sign in
POST /auth/login · from = /analogues · n = 9 gated routes
Sample
Mock states Toggles Motion
VEILLE·Multi-asset research terminal
VEILLE
GET /site-config · 200 · 2 fields

Welcome back

Sign in to your research workspace.

No account? Create one

Data may be delayed or synthetic. Research tool — not investment advice.

The gate

One form, and the whole terminal behind it.

Nothing on this screen is tier-gated or admin-only. The screen is the gate: every protected route bounces here, and returns the reader to where they were.

Protected routes

n = 9 gated routes
RouteGuardReturns to
/marketsProtectedRoute/markets
/chartsProtectedRoute/charts
/newsProtectedRoute/news
/analoguesfromProtectedRoute/analogues
/backtestProtectedRoute/backtest
/datasetsProtectedRoute/datasets
/economistProtectedRoute/economist
/logbookProtectedRoute/logbook
/accountProtectedRoute/account

The guard hands this page a router state from. Nothing about it is shown in the form — it only decides where the reader lands. With no from, the default is /markets. If a session token already exists, this page never renders at all.

Behind the button

What one submit actually does.

Two requests, one cache drop, one redirect — and a failure message this page never writes itself.

Submit — request trace

n = 2 requests Sample
  1. 01POST /auth/login  { email, password }Errors cleared at the start of every submit— ms
  2. 02200 · { access_token, token_type }Or a 4xx whose detail string becomes the error row214 ms
  3. 03queryClient.clear()Whole React Query cache dropped before the new session lands
  4. 04authStore ← token + emailEmail stored trimmed and lowercased
  5. 05GET /auth/me → is_adminFire-and-forget; does not block the redirect, shows nothing here96 ms
  6. 06navigate('/analogues', { replace: true })Replace, so the back button never returns to the gate

Timings are sample figures for a single mocked round trip (n = 1 submit), not a measured product statistic.

Error copy is server-owned

n = 4 sources
Incorrect email or password
Server · detail, verbatim
Login failed — is the API running?
Client fallback · thrown error is not an ApiError
Network error — is the API reachable?
API layer · request never landed
HTTP 502
Degraded · non-JSON body → status text or HTTP code

The card never writes its own message for a failed sign-in. It prints what the API returned, and shakes once. The row is not dismissible — it clears when the next submit starts.

States

n = 6 states
Idle
Fields empty, no error row, button enabled.
Busy
Button disabled, spinner and “Signing in…”. Inputs stay editable; no page overlay, no skeleton.
Error
Alert row between password and button, card shakes once.
Success
No success state — the route changes immediately.
Site-config pending or failed
Page renders anyway on the default brand strings. No spinner for it.
Empty / loading
Neither exists here. Nothing is listed and nothing is fetched before render.

Entrance cascade — reading order

n = 8 blocks Sample
T-ZERO Brand block Auth card Card header Email field Password field Submit button “No account?” row Disclaimer
Base 0.04 s Step 0.04 s Disclaimer 0.32 s

The order is structural — it is the reading order of the column. The three figures are one sum: 0.04 s to the first block, one 0.04 s step per block after it, so the eighth block — the disclaimer — lands at 0.04 + 7 × 0.04 = 0.32 s. They are sample design values for the full eight-block cascade, not measured offsets; this mock animates three of the eight — blocks 1, 2 and 8, at 0.04, 0.08 and 0.32 s. With reduced motion, or effects off, every step is dropped and the shake with it: identical static layout, same content.