Files
astraea/templates/landing.html
drjones 9f9d812320 Astraea v1.0 — multi-agent WA family-law assistant
Eight specialist agents over a 16-book verified WA law corpus (RAG with citations),
per-user document vault, WA court-form PDF auto-fill, comms missions with DV
safety guard, no-KYC auth, TTS. Self-hosted: Flask + SQLite + Ollama, stdlib-only RAG.

Includes README, LICENSE (MIT + not-legal-advice notice), DEPLOY runbook, .gitignore.
2026-09-07 18:49:32 -07:00

125 lines
8.0 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Astraea — Your Washington Divorce Attorney</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #05070f; color: #e8edfb; overflow-x: hidden; -webkit-font-smoothing: antialiased;
}
.hero {
position: relative; min-height: 100vh; display: flex; align-items: center;
background: radial-gradient(1200px 700px at 50% -10%, #16224a 0%, #070b1a 60%, #05070f 100%);
}
.hero-bg { position: absolute; inset: 0; overflow: hidden; }
.hero-bg img {
width: 100%; height: 100%; object-fit: cover; opacity: .55;
-webkit-mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 75%);
mask-image: radial-gradient(ellipse at 50% 40%, #000 30%, transparent 75%);
}
.hero-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(5,7,15,.35), rgba(5,7,15,.9) 80%, #05070f); }
.wrap { position: relative; z-index: 2; width: 100%; max-width: 1180px; margin: 0 auto; padding: 40px 26px; }
header { display: flex; align-items: center; justify-content: space-between; }
.brand { display: flex; align-items: center; gap: 13px; }
.logo { width: 46px; height: 46px; border-radius: 13px; background: linear-gradient(135deg,#6ea8ff,#a78bfa); display: grid; place-items: center; font-size: 24px; box-shadow: 0 0 26px rgba(124,155,255,.55); }
.brand h1 { font-size: 24px; font-weight: 800; letter-spacing: .4px; }
.brand h1 span { color: #f5d06f; }
.pill { font-size: 12px; color: #aeb8d9; border: 1px solid #233055; padding: 6px 13px; border-radius: 999px; display: flex; align-items: center; gap: 7px; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: #4ade80; box-shadow: 0 0 10px #4ade80; }
.hero-content { display: grid; grid-template-columns: 1.25fr .75fr; gap: 50px; align-items: center; padding: 60px 0 40px; }
.left h2 { font-size: clamp(34px, 5vw, 60px); line-height: 1.05; font-weight: 800; letter-spacing: -1px; }
.left h2 .grad { background: linear-gradient(90deg,#7dd3fc,#a78bfa,#f0abfc); -webkit-background-clip: text; background-clip: text; color: transparent; }
.left .sub { color: #aeb8d9; font-size: 18px; line-height: 1.6; margin-top: 20px; max-width: 560px; }
.left .sub b { color: #e8edfb; }
.agents-row { display: flex; flex-wrap: wrap; gap: 9px; margin-top: 26px; }
.agent-chip { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; padding: 8px 13px; border-radius: 999px; border: 1px solid #233055; background: rgba(16,24,50,.6); }
.cta-row { margin-top: 34px; display: flex; gap: 14px; flex-wrap: wrap; }
.btn { border: none; border-radius: 13px; padding: 15px 28px; font-size: 16px; font-weight: 700; cursor: pointer; transition: transform .12s ease, box-shadow .2s ease; }
.btn-primary { background: linear-gradient(135deg,#3b82f6,#6366f1); color: #fff; box-shadow: 0 10px 30px rgba(59,130,246,.4); }
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 16px 40px rgba(59,130,246,.55); }
.btn-ghost { background: rgba(16,24,50,.6); color: #e8edfb; border: 1px solid #233055; }
.card {
background: linear-gradient(180deg, rgba(20,29,61,.9), rgba(16,24,50,.85));
border: 1px solid #233055; border-radius: 20px; padding: 28px;
box-shadow: 0 30px 70px rgba(0,0,0,.5); backdrop-filter: blur(8px);
}
.card h3 { font-size: 20px; font-weight: 700; margin-bottom: 4px; }
.card .hint { color: #8b98c4; font-size: 13px; margin-bottom: 20px; }
.field { margin-bottom: 15px; }
.field label { display: block; font-size: 12.5px; color: #aeb8d9; margin-bottom: 6px; }
.field input { width: 100%; background: #0c142e; border: 1px solid #233055; color: #e8edfb; border-radius: 11px; padding: 12px 14px; font-size: 15px; outline: none; }
.field input:focus { border-color: #6366f1; }
.err { color: #fca5a5; font-size: 13px; margin-bottom: 12px; min-height: 18px; }
.tabs { display: flex; gap: 4px; margin-bottom: 20px; background: #0c142e; border-radius: 11px; padding: 4px; }
.tabs button { flex: 1; border: none; background: transparent; color: #8b98c4; padding: 10px; border-radius: 8px; cursor: pointer; font-size: 14px; font-weight: 600; }
.tabs button.on { background: #1c2748; color: #e8edfb; }
footer { text-align: center; padding: 20px 0 40px; color: #5a658a; font-size: 12.5px; }
footer a { color: #8b98c4; }
@media (max-width: 860px) { .hero-content { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<div class="hero">
<div class="hero-bg"><img src="/static/hero.png" alt="Astraea"></div>
<div class="hero-overlay"></div>
<div class="wrap">
<header>
<div class="brand"><div class="logo">⚖️</div><div><h1>ASTRA<span>EA</span></h1></div></div>
<div class="pill"><span class="dot"></span> Washington family law, on your side</div>
</header>
<div class="hero-content">
<div class="left">
<h2>Your personal<br><span class="grad">divorce attorney</span>,<br>reinvented.</h2>
<p class="sub">Astraea is a constellation of <b>eight Washington State legal specialists</b> — grounded in the real Revised Code of Washington and <b>verified case law</b> — that give you sound legal advice on the facts, prep your court paperwork on demand, and even handle professional correspondence for you.</p>
<div class="agents-row">
{% for a in agents %}<div class="agent-chip">{{ a.emoji }} {{ a.name.split(' & ')[0].split(' ')[0] }}</div>{% endfor %}
</div>
<div class="cta-row">
<button class="btn btn-primary" onclick="document.getElementById('auth').scrollIntoView({behavior:'smooth'})">Begin your case →</button>
<button class="btn btn-ghost" onclick="window.location.href='/app'">I have an account</button>
</div>
</div>
<div class="card" id="auth">
<h3>Welcome back</h3>
<div class="hint">No email, no verification. Just you and your counsel.</div>
<div class="tabs">
<button id="tabLogin" class="on" onclick="setTab('login')">Sign in</button>
<button id="tabRegister" onclick="setTab('register')">Create account</button>
</div>
<div class="err" id="err"></div>
<div class="field"><label>Username</label><input id="u" autocomplete="username"></div>
<div class="field"><label>Password</label><input id="p" type="password" autocomplete="current-password"></div>
<button class="btn btn-primary" style="width:100%" id="go">Sign in</button>
</div>
</div>
<footer>
Astraea provides practical legal guidance grounded in Washington statute and case law — not a substitute for a licensed attorney.
<br><a href="https://buymeacoffee.com/r26xrthzttg" target="_blank" rel="noopener">☕ Support Astraea</a>
</footer>
</div>
</div>
<script>
let mode = 'login';
function setTab(m){ mode = m; document.getElementById('tabLogin').classList.toggle('on', m==='login'); document.getElementById('tabRegister').classList.toggle('on', m==='register'); document.getElementById('go').textContent = m==='login' ? 'Sign in' : 'Create account'; }
async function go(){
const u = document.getElementById('u').value.trim(), p = document.getElementById('p').value;
const err = document.getElementById('err');
if(!u || !p){ err.textContent = 'Enter a username and password.'; return; }
const r = await fetch('/api/'+mode, {method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({username:u, password:p})});
const d = await r.json();
if(!r.ok){ err.textContent = d.error || 'Something went wrong'; return; }
localStorage.setItem('astraea_token', d.token);
localStorage.setItem('astraea_user', d.username);
window.location.href = '/app';
}
document.getElementById('go').onclick = go;
document.getElementById('p').addEventListener('keydown', e => { if(e.key==='Enter') go(); });
</script>
</body>
</html>