<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Welcome to sfpermits.ai</title>
{% include "fragments/head_obsidian.html" %}
<style nonce="{{ csp_nonce }}">
/* ── Welcome / Onboarding Page — obsidian design system ─────────────── */
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
/* ── Minimal header ──────────────────────────────────────────────────── */
.welcome-header {
border-bottom: 1px solid var(--glass-border);
padding: var(--space-4) 0;
}
.welcome-header .obs-container {
display: flex;
align-items: center;
justify-content: space-between;
}
.welcome-wordmark {
font-family: var(--mono);
font-size: var(--text-xs);
font-weight: 300;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(--text-tertiary);
text-decoration: none;
transition: color 0.3s;
}
.welcome-wordmark:hover { color: var(--accent); }
.welcome-greeting {
font-family: var(--sans);
font-size: var(--text-sm);
font-weight: 300;
color: var(--text-secondary);
}
/* ── Main content ────────────────────────────────────────────────────── */
.welcome-main {
flex: 1;
padding: var(--space-16) 0 var(--space-20);
}
/* ── Hero ────────────────────────────────────────────────────────────── */
.welcome-hero {
text-align: center;
margin-bottom: var(--space-12);
opacity: 0;
animation: fadeUp 1.0s 0.1s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.welcome-badge {
display: inline-flex;
align-items: center;
gap: var(--space-2);
background: var(--accent-glow);
border: 1px solid var(--accent-ring);
border-radius: var(--radius-full);
padding: 5px var(--space-4);
font-family: var(--mono);
font-size: var(--text-xs);
font-weight: 400;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--accent);
margin-bottom: var(--space-6);
}
.welcome-hero h1 {
font-family: var(--sans);
font-size: var(--text-2xl);
font-weight: 300;
color: var(--text-primary);
margin: 0 0 var(--space-4) 0;
line-height: 1.2;
}
.welcome-hero h1 .accent { color: var(--accent); }
.welcome-hero p {
font-family: var(--sans);
font-size: var(--text-base);
font-weight: 300;
color: var(--text-secondary);
max-width: 480px;
margin: 0 auto var(--space-10) auto;
line-height: 1.6;
}
/* ── Progress dots ───────────────────────────────────────────────────── */
.progress-dots {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-3);
margin-bottom: 0;
}
.progress-dot {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
background: var(--glass);
border: 1px solid var(--glass-border);
transition: background 0.3s, border-color 0.3s;
}
.progress-dot.active {
background: var(--accent);
border-color: var(--accent);
box-shadow: 0 0 8px var(--accent-glow);
}
.progress-dot.done {
background: var(--signal-green);
border-color: var(--signal-green);
}
.dot-connector {
width: 32px;
height: 1px;
background: var(--glass-border);
}
/* ── Step cards ──────────────────────────────────────────────────────── */
.steps-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
margin-bottom: var(--space-10);
opacity: 0;
animation: fadeUp 1.0s 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
@media (max-width: 768px) {
.steps-grid { grid-template-columns: 1fr; }
}
.step-card .glass-card {
height: 100%;
display: flex;
flex-direction: column;
}
.step-card.is-active .glass-card {
border-color: var(--accent-ring);
box-shadow: 0 0 0 1px rgba(94, 234, 212, 0.08);
}
.step-number {
font-family: var(--mono);
font-size: var(--text-xs);
font-weight: 400;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.08em;
margin-bottom: var(--space-4);
display: flex;
align-items: baseline;
gap: var(--space-2);
}
.step-number .num {
color: var(--accent);
font-size: var(--text-lg);
font-weight: 300;
}
.step-icon {
font-size: 1.75rem;
margin-bottom: var(--space-3);
display: block;
}
.step-title {
font-family: var(--sans);
font-size: var(--text-lg);
font-weight: 300;
color: var(--text-primary);
margin: 0 0 var(--space-3) 0;
}
.step-desc {
font-family: var(--sans);
font-size: var(--text-sm);
font-weight: 300;
color: var(--text-secondary);
line-height: 1.6;
margin: 0 0 var(--space-6) 0;
flex: 1;
}
.step-cta { margin-top: auto; }
/* ── Footer CTA card ─────────────────────────────────────────────────── */
.welcome-footer-card {
text-align: center;
margin-bottom: var(--space-8);
opacity: 0;
animation: fadeUp 1.0s 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards;
}
.welcome-footer-card h2 {
font-family: var(--sans);
font-size: var(--text-xl);
font-weight: 300;
color: var(--text-primary);
margin: 0 0 var(--space-3) 0;
}
.welcome-footer-card p {
font-family: var(--sans);
font-size: var(--text-sm);
font-weight: 300;
color: var(--text-secondary);
margin: 0 0 var(--space-6) 0;
line-height: 1.6;
}
/* ── Skip link ───────────────────────────────────────────────────────── */
.skip-link {
display: block;
text-align: center;
font-family: var(--mono);
font-size: var(--text-xs);
font-weight: 300;
color: var(--text-tertiary);
text-decoration: none;
padding: var(--space-3) 0 var(--space-8) 0;
transition: color 0.2s;
}
.skip-link:hover { color: var(--text-secondary); }
/* ── Ghost CTA button-like variant for step cards ────────────────────── */
.step-ghost-btn {
display: block;
width: 100%;
padding: 10px var(--space-4);
font-family: var(--mono);
font-size: var(--text-sm);
font-weight: 300;
color: var(--text-secondary);
background: var(--glass);
border: 1px solid var(--glass-border);
border-radius: var(--radius-sm);
text-align: center;
text-decoration: none;
cursor: pointer;
transition: border-color 0.3s, color 0.3s, background 0.2s;
}
.step-ghost-btn:hover {
border-color: var(--accent-ring);
color: var(--accent);
background: var(--accent-glow);
}
.step-ghost-btn.primary {
border-color: var(--accent-ring);
color: var(--accent);
background: var(--accent-glow);
}
.step-ghost-btn.primary:hover {
background: rgba(94, 234, 212, 0.12);
}
</style>
</head>
<body class="obsidian">
<!-- Minimal header — no full nav on onboarding -->
<header class="welcome-header">
<div class="obs-container">
<a href="/" class="welcome-wordmark">sfpermits.ai</a>
<span class="welcome-greeting">
Welcome, {{ user.get('display_name') or user.get('email', '').split('@')[0] }}
</span>
</div>
</header>
<div class="welcome-main">
<div class="obs-container">
<!-- Hero -->
<section class="welcome-hero">
<div class="welcome-badge">Beta Access Granted</div>
<h1>Welcome to <span class="accent">sfpermits.ai</span></h1>
<p>Your account is ready. Here’s what you can do — it only takes a few minutes to get value from your first search.</p>
<!-- Progress dots: 3 steps -->
<div class="progress-dots">
<div class="progress-dot active" id="dot-1"></div>
<div class="dot-connector"></div>
<div class="progress-dot" id="dot-2"></div>
<div class="dot-connector"></div>
<div class="progress-dot" id="dot-3"></div>
</div>
</section>
<!-- 3-step cards -->
<section class="steps-grid">
<!-- Step 1: Search -->
<div class="step-card is-active" id="step-1">
<div class="glass-card">
<div class="step-number"><span class="num">1</span> Start here</div>
<span class="step-icon" aria-hidden="true">🔍</span>
<h3 class="step-title">Search any SF address</h3>
<p class="step-desc">
Enter a street address to see full permit history, inspection records, routing progress, and AI-powered timeline estimates. You’re already in — no setup needed.
</p>
<div class="step-cta">
<a href="/search" class="step-ghost-btn primary">
Go to Search →
</a>
</div>
</div>
</div>
<!-- Step 2: Property Report -->
<div class="step-card" id="step-2">
<div class="glass-card">
<div class="step-number"><span class="num">2</span> Go deeper</div>
<span class="step-icon" aria-hidden="true">📋</span>
<h3 class="step-title">Pull a property report</h3>
<p class="step-desc">
After your search, open a full property report to see permit health scores, entity network maps, complaints, and violations. Ideal for due diligence or permit strategy.
</p>
<div class="step-cta">
<a href="/search" class="step-ghost-btn">
Search an address →
</a>
</div>
</div>
</div>
<!-- Step 3: Watchlist -->
<div class="step-card" id="step-3">
<div class="glass-card">
<div class="step-number"><span class="num">3</span> Stay informed</div>
<span class="step-icon" aria-hidden="true">🔔</span>
<h3 class="step-title">Watch properties for changes</h3>
<p class="step-desc">
Add any property, permit, or entity to your watchlist. We check nightly and send a morning brief when something moves — status changes, new inspections, new filings.
</p>
<div class="step-cta">
<a href="/account" class="step-ghost-btn">
View watchlist settings →
</a>
</div>
</div>
</div>
</section>
<!-- Footer CTA -->
<section class="welcome-footer-card">
<div class="glass-card">
<h2>Ready to get started?</h2>
<p>Search any SF address to see permit intelligence in action. Most users find something useful in their first 60 seconds.</p>
<a href="/search"
class="ghost-cta"
style="font-size: var(--text-base);"
onclick="dismissOnboarding()">
Start searching now →
</a>
</div>
</section>
<!-- Skip link -->
<a href="/search" class="skip-link" onclick="dismissOnboarding(); return false;">
Skip onboarding — take me to search
</a>
</div>
</div>
<script nonce="{{ csp_nonce }}">
function dismissOnboarding() {
// Fire-and-forget POST to mark onboarding complete
fetch('/onboarding/dismiss', {
method: 'POST',
credentials: 'same-origin',
headers: { 'X-CSRFToken': document.querySelector('meta[name="csrf-token"]')?.content || '' }
})
.catch(function() {}); // Non-fatal if it fails
}
</script>
</body>
</html>