feat(otm): sign-in page onto the shared OTM standard (0.4.1)
Build / Build and release (push) Failing after 15s

Restyle login_page to the platform standard: brand (name + tagline)
above one centered card, "Sign in with OTM" as the full-width primary
button, and a muted Face ID/Touch ID/passkey helper line under it.
13ft has no local accounts, so there's no "Other ways to sign in"
step. The fail-closed "isn't wired to OTM yet" 503 copy and the tides
beacon are unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014LCQmFvKshyAJsE8C3B5ET
This commit is contained in:
2026-10-18 23:32:37 -05:00
co-authored by Claude Fable 5.1
parent f76c562a29
commit 9b37dd5f2b
2 changed files with 26 additions and 8 deletions
+4
View File
@@ -1,5 +1,9 @@
# Changelog — 13ft (OTM fork)
## 0.4.1 — 2026-10-18
- Sign-in page restyled onto the platform's shared OTM sign-in standard: brand (name + tagline) above one centered card, the "Sign in with OTM" button full-width and primary, a muted Face ID/Touch ID/passkey line underneath. 13ft has no local accounts, so there's no "Other ways to sign in" step — the fail-closed "isn't wired to OTM yet" copy still shows when unconfigured.
## 0.4.0 — 2026-10-18
First OTM release of the fork (upstream wasi-master/13ft 0.3.4 underneath).
+22 -8
View File
@@ -32,7 +32,7 @@ from typing import Any, Optional
import requests
APP_VERSION = "0.4.0"
APP_VERSION = "0.4.1"
OTM_AUDIENCE = "app:13ft"
OTM_BASE = os.environ.get("OTM_SSO_AUTHORIZE_BASE", "https://admin.poweredbyotm.com")
SESSION_COOKIE = "thirteen_session"
@@ -246,18 +246,32 @@ SSO_REASONS = {
def login_page(next_path: str, reason: Optional[str], configured: bool) -> str:
msg = ""
if reason:
msg = f'<p style="color:#b3556b">{html_mod.escape(SSO_REASONS.get(reason, "Sign-in failed."))}</p>'
msg = f'<p style="color:#c0566e;margin:0 0 1rem;font-size:.9rem">{html_mod.escape(SSO_REASONS.get(reason, "Sign-in failed."))}</p>'
if not configured:
msg = '<p style="color:#b3556b">This 13ft isn\'t wired to OTM yet, so nobody can sign in. (Fails closed on purpose — it\'s a fetch proxy.)</p>'
button = (
f'<p><a href="{html_mod.escape(otm_sign_in_url(next_path))}" style="display:inline-block;padding:.7em 1.2em;border:1px solid currentColor;border-radius:8px;text-decoration:none;color:inherit;font-weight:600">Sign in with OTM</a></p>'
msg = '<p style="color:#c0566e;margin:0;font-size:.9rem">This 13ft isn\'t wired to OTM yet, so nobody can sign in. (Fails closed on purpose — it\'s a fetch proxy.)</p>'
# OTM-only app (no local accounts) — step 2's primary button is the whole
# card, with the passkey helper line under it. There is no step 3 ("Other
# ways to sign in"): there is no other way.
card = msg + (
f'<a href="{html_mod.escape(otm_sign_in_url(next_path))}" '
'style="display:block;box-sizing:border-box;width:100%;text-align:center;padding:.85em 1.2em;'
'margin-top:.75rem;border-radius:9px;border:1px solid var(--accent);background:var(--accent);'
'color:var(--accent-ink);text-decoration:none;font-weight:600">Sign in with OTM</a>'
'<p style="text-align:center;font-size:.8rem;opacity:.7;margin:.9rem 0 0">'
'Your OTM account — Face ID, Touch ID or a passkey works there.</p>'
if configured else ""
)
return (
'<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">'
'<title>13ft — sign in</title><style>:root{color-scheme:light dark}body{font-family:system-ui,sans-serif;max-width:28rem;margin:12vh auto;padding:0 1rem;line-height:1.5}</style></head>'
'<body><h1>13ft</h1><p>Read the article without the cruft. Sign-in is by OTM account — one login for every OTM tool.</p>'
f'{msg}{button}{tides_beacon()}</body></html>'
'<title>13ft — sign in</title><style>:root{color-scheme:light dark;--accent:#3a6df0;--accent-ink:#fff}'
'body{font-family:system-ui,sans-serif;max-width:24rem;margin:10vh auto;padding:0 1rem;line-height:1.5}'
'.brand{text-align:center;margin:0 0 1.75rem}.brand h1{margin:0 0 .3rem;font-size:1.9rem}'
'.brand p{margin:0;opacity:.7;font-size:.95rem}'
'.card{border:1px solid #8884;border-radius:14px;padding:1.5rem}</style></head>'
'<body>'
'<div class="brand"><h1>13ft</h1><p>Read the article without the cruft.</p></div>'
f'<div class="card">{card}</div>'
f'{tides_beacon()}</body></html>'
)