<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Permit Preview — sfpermits.ai</title>
<meta name="description" content="Find out what permits you need for your San Francisco project. Free instant preview — no signup required.">
<style nonce="{{ csp_nonce }}">
:root {
--bg: #0f1117;
--surface: #1a1d27;
--surface-2: #252834;
--border: #333749;
--text: #e4e6eb;
--text-muted: #8b8fa3;
--accent: #4f8ff7;
--accent-hover: #3a7ae0;
--success: #34d399;
--warning: #fbbf24;
--error: #f87171;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.6;
min-height: 100vh;
}
.container { max-width: 900px; margin: 0 auto; padding: 0 24px; }
header {
border-bottom: 1px solid var(--border);
padding: 20px 0;
}
header .container {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo { font-size: 1.4rem; font-weight: 700; color: var(--accent); text-decoration: none; }
.logo span { color: var(--text-muted); font-weight: 400; }
.header-right { display: flex; align-items: center; gap: 10px; }
.header-btn {
font-size: 0.85rem; padding: 8px 18px; border-radius: 8px;
text-decoration: none; font-weight: 600; font-family: inherit; cursor: pointer;
}
.header-btn-outline {
background: none; border: 1px solid var(--border); color: var(--text-muted);
}
.header-btn-outline:hover { color: var(--text); border-color: var(--text-muted); }
.header-btn-primary {
background: var(--accent); border: 1px solid var(--accent); color: #fff;
}
.header-btn-primary:hover { background: var(--accent-hover); }
main { padding: 40px 0 80px; }
.preview-header { margin-bottom: 32px; }
.preview-header h1 { font-size: 1.5rem; margin-bottom: 6px; }
.preview-header .desc {
font-size: 0.9rem; color: var(--text-muted);
background: var(--surface); border: 1px solid var(--border);
border-radius: 8px; padding: 10px 14px; margin-top: 12px;
font-style: italic;
}
/* Results grid */
.results-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 24px;
}
@media (max-width: 640px) { .results-grid { grid-template-columns: 1fr; } }
.result-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px;
padding: 24px;
}
.result-card h2 {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-muted);
margin-bottom: 12px;
}
.result-value {
font-size: 1.3rem;
font-weight: 700;
margin-bottom: 8px;
}
.result-value.otc { color: var(--success); }
.result-value.in-house { color: var(--warning); }
.result-value.timeline { color: var(--accent); }
.result-detail { font-size: 0.85rem; color: var(--text-muted); line-height: 1.5; }
/* Fork comparison (kitchen/bath) */
.fork-section {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px;
padding: 24px;
margin-bottom: 24px;
}
.fork-section h2 {
font-size: 0.8rem; text-transform: uppercase;
letter-spacing: 0.06em; color: var(--text-muted); margin-bottom: 16px;
}
.fork-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
@media (max-width: 480px) { .fork-grid { grid-template-columns: 1fr; } }
.fork-option {
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px;
}
.fork-option.otc-path { border-color: var(--success); background: rgba(52,211,153,0.04); }
.fork-option.inhouse-path { border-color: var(--warning); background: rgba(251,191,36,0.04); }
.fork-label {
font-size: 0.72rem; font-weight: 700; text-transform: uppercase;
letter-spacing: 0.07em; margin-bottom: 8px;
}
.fork-label.otc-path { color: var(--success); }
.fork-label.inhouse-path { color: var(--warning); }
.fork-title { font-size: 1rem; font-weight: 600; margin-bottom: 4px; }
.fork-timeline { font-size: 1.5rem; font-weight: 800; margin-bottom: 6px; }
.fork-timeline.otc-path { color: var(--success); }
.fork-timeline.inhouse-path { color: var(--warning); }
.fork-note { font-size: 0.8rem; color: var(--text-muted); line-height: 1.4; }
/* Locked cards */
.locked-section {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
margin-bottom: 32px;
}
@media (max-width: 700px) { .locked-section { grid-template-columns: 1fr; } }
.locked-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 14px;
padding: 20px;
position: relative;
overflow: hidden;
min-height: 140px;
}
.locked-card::after {
content: '';
position: absolute;
top: 40%; left: 0; right: 0; bottom: 0;
background: linear-gradient(to bottom, transparent, var(--surface) 80%);
pointer-events: none;
}
.locked-card h3 { font-size: 0.9rem; font-weight: 700; margin-bottom: 6px; }
.locked-preview { font-size: 0.8rem; color: var(--text-muted); opacity: 0.6; }
.locked-cta {
position: absolute;
bottom: 14px; left: 0; right: 0;
text-align: center; z-index: 1;
}
.locked-cta a {
display: inline-block; background: var(--accent); color: #fff;
padding: 8px 20px; border-radius: 8px; text-decoration: none;
font-weight: 600; font-size: 0.85rem;
}
.locked-cta a:hover { background: var(--accent-hover); }
/* === Sprint 58C: Lightweight methodology footer === */
.methodology-micro {
margin-top: 12px;
padding-top: 8px;
border-top: 1px solid var(--border);
font-size: 0.75rem;
color: var(--text-muted);
opacity: 0.8;
}
/* CTA bar */
.signup-cta {
background: linear-gradient(135deg, rgba(79,143,247,0.12), rgba(79,143,247,0.04));
border: 1px solid rgba(79,143,247,0.3);
border-radius: 14px;
padding: 28px;
text-align: center;
}
.signup-cta h2 { font-size: 1.2rem; margin-bottom: 8px; }
.signup-cta p { font-size: 0.9rem; color: var(--text-muted); margin-bottom: 20px; }
.signup-cta-btns { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
.btn-primary {
background: var(--accent); color: #fff; padding: 12px 28px;
border-radius: 10px; text-decoration: none; font-weight: 600;
font-size: 0.95rem;
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-secondary {
background: none; border: 1px solid var(--border); color: var(--text-muted);
padding: 12px 28px; border-radius: 10px; text-decoration: none; font-size: 0.9rem;
}
.btn-secondary:hover { color: var(--text); border-color: var(--text-muted); }
/* Error state */
.error-box {
background: rgba(248,113,113,0.08);
border: 1px solid rgba(248,113,113,0.3);
border-radius: 12px;
padding: 20px;
color: var(--error);
margin-bottom: 24px;
}
footer {
border-top: 1px solid var(--border); padding: 24px 0; text-align: center;
}
footer p { font-size: 0.8rem; color: var(--text-muted); }
footer a { color: var(--accent); text-decoration: none; }
</style>
</head>
<body>
{% if is_staging %}
<div style="background: #fbbf24; color: #1a1d27; text-align: center; padding: 8px 16px; font-size: 14px; font-weight: 600; letter-spacing: 0.02em; position: relative; z-index: 9999;">
⚠ STAGING ENVIRONMENT
</div>
{% endif %}
<header>
<div class="container">
<a href="/" class="logo">sfpermits<span>.ai</span></a>
<div class="header-right">
<a href="/auth/login" class="header-btn header-btn-outline">Sign in</a>
<a href="/auth/login" class="header-btn header-btn-primary">Get started free</a>
</div>
</div>
</header>
<main>
<div class="container">
<div class="preview-header">
<h1>Permit Preview</h1>
<p style="color: var(--text-muted); font-size: 0.9rem;">
Free instant preview — no account required
</p>
<div class="desc">"{{ description }}"{{ ' · ' ~ neighborhood if neighborhood else '' | safe }}</div>
</div>
{% if error %}
<div class="error-box">
<strong>Preview unavailable:</strong> {{ error }}
</div>
{% endif %}
{% if predict or timeline %}
<!-- Core results -->
<div class="results-grid">
{% if predict %}
<div class="result-card">
<h2>Review Path</h2>
<div class="result-value {{ 'otc' if predict.is_otc else 'in-house' }}">
{{ 'Over-the-Counter (OTC)' if predict.is_otc else 'In-House Review' }}
</div>
<div class="result-detail">{{ predict.review_reason }}</div>
{% if predict.form %}
<div style="margin-top: 10px; font-size: 0.8rem; color: var(--text-muted);">
Form: <strong style="color: var(--text);">{{ predict.form }}</strong>
</div>
{% endif %}
{# Sprint 58C: lightweight methodology micro-footer #}
<div class="methodology-micro">
Decision-tree permit classification · SF DBI permit decision tree + 86-concept knowledge index
</div>
</div>
{% endif %}
{% if timeline %}
<div class="result-card">
<h2>Estimated Timeline</h2>
<div class="result-value timeline">{{ timeline.p50_display }}</div>
<div class="result-detail">{{ timeline.detail }}</div>
{% if timeline.range %}
<div style="margin-top: 8px; font-size: 0.78rem; color: var(--text-muted);">
Range: {{ timeline.range }}
</div>
{% endif %}
{# Sprint 58C: lightweight methodology micro-footer #}
<div class="methodology-micro">
Historical permit statistics · 1.1M+ historical permits (timeline_stats)
</div>
</div>
{% endif %}
</div>
<!-- Kitchen/bath fork comparison -->
{% if fork %}
<div class="fork-section">
<h2>Layout Decision Matters</h2>
<p style="font-size: 0.85rem; color: var(--text-muted); margin-bottom: 16px;">
Whether you move plumbing or gas lines determines your review path — and your timeline.
</p>
<div class="fork-grid">
<div class="fork-option otc-path">
<div class="fork-label otc-path">OTC Path</div>
<div class="fork-title">Keep existing layout</div>
<div class="fork-timeline otc-path">~3–4 weeks</div>
<div class="fork-note">No fixture relocation → OTC review, Form 8. Walk in, permit same day.</div>
</div>
<div class="fork-option inhouse-path">
<div class="fork-label inhouse-path">In-House Path</div>
<div class="fork-title">Change layout</div>
<div class="fork-timeline inhouse-path">~3–6 months</div>
<div class="fork-note">Moving sink, toilet, or range → in-house review, licensed professional required.</div>
</div>
</div>
</div>
{% endif %}
<!-- Locked cards: fees, documents, risk -->
<div class="locked-section">
<div class="locked-card">
<h3>Fee Estimate</h3>
<div class="locked-preview">DBI filing fee • Plan check • SFPUC inspection • Green Building surcharge...</div>
<div class="locked-cta">
<a href="/auth/login">Sign up free to unlock →</a>
</div>
</div>
<div class="locked-card">
<h3>Required Documents</h3>
<div class="locked-preview">Permit application • Site plan • Floor plan • Structural drawings • EPR checklist...</div>
<div class="locked-cta">
<a href="/auth/login">Sign up free to unlock →</a>
</div>
</div>
<div class="locked-card">
<h3>Revision Risk</h3>
<div class="locked-preview">Based on {{ predict.project_type if predict else 'your project' }} permits in SF • 38% revision rate • Common triggers...</div>
<div class="locked-cta">
<a href="/auth/login">Sign up free to unlock →</a>
</div>
</div>
</div>
{% endif %}
<!-- Signup CTA -->
<div class="signup-cta">
<h2>Get the full analysis — free</h2>
<p>Unlock fee estimates, required documents, revision risk, and AI plan review. Takes 30 seconds.</p>
<div class="signup-cta-btns">
<a href="/auth/login" class="btn-primary">Sign up free</a>
<a href="/" class="btn-secondary">Back to search</a>
</div>
</div>
</div>
</main>
<footer>
<div class="container">
<p>Built on San Francisco open data. <a href="/health">System status</a></p>
</div>
</footer>
</body>
</html>