The login page is the first surface the user sees on a gated dashboard
and shipped with off-the-shelf system fonts and a generic orange
accent that didn't match the React dashboard waiting on the other
side of the OAuth round trip. Apply the same visual language the SPA
uses (the @nous-research/ui package) so the auth flow feels like one
product, not two.
What changes (visual only — no functional changes):
Typography
- Body: Collapse (regular + bold), served from /fonts/ — the same
woff2 files the dashboard SPA loads via the design-system's
fonts.css.
- Display: Rules Compressed (regular + medium) for the brand
wordmark and the page heading.
- Brand chrome (heading, buttons, footer) uses the DS idiom:
uppercase + letter-spacing 0.2em (matching the DS Button class).
Colour
- Background: #170d02 (deep brown-black; --background-base in DS).
- Accent: #ffac02 (amber; --midground in DS).
- Foreground: #ffffff.
- Hairlines: color-mix() of the midground at 18% / 35%, mirroring
the DS "@theme inline" derived tokens.
Button surface
- Solid amber surface with dark text, no rounded corners (DS Button
is squared). Inset bevel — — directly mirrors the DS
Button SHADOW_DEFAULT (). :active uses filter:invert(1) which matches the DS
Button's .
Atmosphere
- Subtle 3px dither (repeating-conic-gradient at 4% midground) +
a midground radial glow at top — same idioms as the DS .dither
utility and the SPA's panel chrome.
- slide-up fade-in entrance animation matching DS @keyframes
slide-up (0.6s ease-out). Honours prefers-reduced-motion.
Brand wordmark
- 'NOUS · RESEARCH' above the card in Rules Compressed, amber,
0.32em tracking. Establishes ownership before the user squints
at the buttons.
Empty-state page
- The 'Sign-in unavailable' fallback (no providers registered)
got the same colour-token and typography treatment so the
misconfigured-deploy experience is also coherent.
Fonts are served from /fonts/*.woff2 — a path the dashboard-auth gate
already allowlists pre-auth (see _GATE_PUBLIC_PREFIXES in
middleware.py:42), so the login page renders with the brand typeface
without needing the React bundle loaded. The page is still entirely
static HTML+CSS with no JS — the original constraint (no SPA
dependency, no session token) is preserved.
The class="provider-btn" selector is unchanged — the existing test
suite extracts the anchor href via that class, and a regression that
renamed it would silently break tests/hermes_cli/test_dashboard_auth_401_reauth.py.
A docstring note on the module flags this so future visual tweaks
don't break the contract by accident.
Visual smoke-test: rendered both the happy path (multiple providers
listed) and the empty-state page in a browser and verified all five
DS criteria — brown-black bg, amber accent, uppercase wide-tracking
type, inset-bevel buttons, Nous · Research wordmark — render
correctly with no unstyled fallbacks. 208/208 dashboard-auth tests
remain green.