All projects
Agent-built productSeptember 2026Reported by the owner
The OM, evolved
Built for
Philadelphia retail brokers and sponsors
Project
Slate CRE
A static PDF became a responsive interactive OM with a live engagement dashboard that shows which sections each prospect read.
02
Demo



Slate refuses embedding, so open the live OM in a new tab.Open live ↗
03
How it works
01
Each OM is a self-contained page with its own design system, so a broker can send one link.
02
Engagement dashboard reads section-level view events from the OM.
03
Admin page gated by a Vercel middleware login; no framework runtime.
04
Static hosting on Vercel keeps cost near zero per deal.
05
Stack and code
- HTML
- CSS
- JavaScript
- Vercel Middleware
- Vercel
- Claude Code
middleware.js— Login gate for the admin surface.
// Vercel Edge Middleware - gates the demo OMs and dashboard behind an admin cookie.
// Verifies an HMAC-SHA256 signed token. The matching login endpoint lives at /api/login.
export const config = {
matcher: [
'/fort-williams-square-om',
'/fort-williams-square-om.html',
],
};
const COOKIE_NAME = 'slate_admin';
export default async function middleware(request) {
const cookieHeader = request.headers.get('cookie') || '';
const token = readCookie(cookieHeader, COOKIE_NAME);
const secret = process.env.AUTH_SECRET;
if (token && secret && (await verifyToken(token, secret))) {
return; // authenticated, continue to static file
}
const url = new URL(request.url);
const target = new URL('/admin', url.origin);
target.searchParams.set('next', url.pathname + url.search);
return Response.redirect(target.toString(), 302);
}
function readCookie(header, name) {
const parts = header.split(/;\s*/);
for (const p of parts) {
const eq = p.indexOf('=');
if (eq < 0) continue;
if (p.slice(0, eq).trim() === name) return p.slice(eq + 1);
}
return null;
}
// Token format: base64url(JSON({exp})).base64url(HMAC-SHA256(payload, secret))
async function verifyToken(token, secret) {
const dot = token.indexOf('.');
if (dot < 1) return false;
const payload = token.slice(0, dot);
const sigGiven = token.slice(dot + 1);
let expectedSig;
try {
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign'],
);
const sigBuf = await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(payload));
expectedSig = base64url(sigBuf);
} catch {
return false;
}
if (!timingSafeEqual(sigGiven, expectedSig)) return false;
let parsed;
try {
parsed = JSON.parse(b64urlDecode(payload));
} catch {
return false;
}
if (typeof parsed.exp !== 'number') return false;
if (Date.now() / 1000 > parsed.exp) return false;
return true;
}
function base64url(buf) {
const bytes = new Uint8Array(buf);
let str = '';
for (let i = 0; i < bytes.length; i++) str += String.fromCharCode(bytes[i]);
return btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}
function b64urlDecode(s) {
const pad = s.length % 4 === 0 ? 0 : 4 - (s.length % 4);
const b64 = (s + '='.repeat(pad)).replace(/-/g, '+').replace(/_/g, '/');
return atob(b64);
}
function timingSafeEqual(a, b) {
if (a.length !== b.length) return false;
let diff = 0;
for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
return diff === 0;
}
~/Documents/Slate/middleware.js
mayfair-commons-om.html— Head and first section of the interactive OM.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mayfair Commons · Offering Memorandum · Slate CRE</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #faf9f5; display: flex; align-items: center; justify-content: center; min-height: 100vh; font-family: -apple-system, BlinkMacSystemFont, sans-serif; }
#__bundler_loading { position: fixed; bottom: 20px; right: 20px; font: 13px/1.4 -apple-system, BlinkMacSystemFont, sans-serif; color: #666; background: #fff; padding: 8px 14px; border-radius: 8px; box-shadow: 0 1px 4px rgba(0,0,0,0.12); z-index: 10000; }
#__bundler_thumbnail { position: fixed; inset: 0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: #faf9f5; z-index: 9999; }
#__bundler_thumbnail svg { width: 100%; height: 100%; object-fit: contain; }
#__bundler_placeholder { color: #999; font-size: 14px; }
</style>
<noscript>
<style>#__bundler_loading { display: none; }</style>
<div style="position:fixed;bottom:12px;left:12px;font:13px/1.4 -apple-system,BlinkMacSystemFont,sans-serif;color:#999;background:rgba(255,255,255,0.9);padding:6px 12px;border-radius:6px;box-shadow:0 1px 4px rgba(0,0,0,0.08);z-index:10000;">
This page requires JavaScript to display.
</div>
</noscript>
</head>
<body>
<div id="__bundler_thumbnail">
<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
<rect width="400" height="300" fill="#1B2942"></rect>
<rect x="120" y="90" width="160" height="120" fill="#E6CFA8"></rect>
<rect x="120" y="90" width="160" height="16" fill="#B5523A"></rect>
<rect x="138" y="128" width="52" height="64" fill="#4A6B7D"></rect>
<rect x="200" y="128" width="30" height="64" fill="#8FA9B8"></rect>
<rect x="238" y="128" width="24" height="64" fill="#8FA9B8"></rect>
<text x="200" y="245" fill="#E6CFA8" font-family="Georgia,serif" font-size="26" font-style="italic" text-anchor="middle">Mayfair Commons</text>
</svg>
</div>
<div id="__bundler_loading">Unpacking...</div>
<script>
document.addEventListener('DOMContentLoaded', async function() {
const loading = document.getElementById('__bundler_loading');
function setStatus(msg) { if (loading) loading.textContent = msg; }
const FONT_MIME = /^(font[/]|application[/](x-)?font-|application[/]vnd\.ms-fontobject)/i;
const MIME_TOKEN = /^[\w.+-]+[/][\w.+-]+$/;
function toBase64(bytes) {
let bin = '';
for (let i = 0; i < bytes.length; i += 0x8000) {
bin += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
}
return btoa(bin);
}
// Error sink persists across replaceWith since it's on window, not the DOM.
window.addEventListener('error', function(e) {
var p = document.body || document.documentElement;
var d = document.getElementById('__bundler_err') || p.appendChild(document.createElement('div'));
d.id = '__bundler_err';
d.style.cssText = 'position:fixed;bottom:12px;left:12px;right:12px;font:12px/1.4 ui-monospace,monospace;background:#2a1215;color:#ff8a80;padding:10px 14px;border-radius:8px;border:1px solid #5c2b2e;z-index:99999;white-space:pre-wrap;max-height:40vh;overflow:auto';
d.textContent = (d.textContent ? d.textContent + String.fromCharCode(10) : '') +
'[bundle] ' + (e.message || e.type) +
(e.filename ? ' (' + e.filename.slice(0, 60) + ':' + e.lineno + ')' : '');
}, true);
try {
const manifestEl = document.querySelector('script[type="__bundler/manifest"]');
const templateEl = document.querySelector('script[type="__bundler/template"]');
if (!manifestEl || !templateEl) {
setStatus('Error: missing bundle data');
console.error('[bundler] Missing script tags — manifestEl:', !!manifestEl, 'templateEl:', !!templateEl);
return;
}
const manifest = JSON.parse(manifestEl.textContent);
let template = JSON.parse(templateEl.textContent);
const uuids = Object.keys(manifest);
setStatus('Unpacking ' + uuids.length + ' assets...');
const blobUrls = {};
await Promise.all(uuids.map(async (uuid) => {
const entry = manifest[uuid];
try {
~/Documents/Slate/mayfair-commons-om.html