← back to Ventura Corridor
public/search.html
107 lines
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Search · The Corridor</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
@import url('https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,300;0,400;0,500;1,400;1,500&family=Inter:wght@300;400;500;600&family=JetBrains+Mono:wght@200;300&display=swap');
:root{--paper:#faf6ee;--ink:#1a1815;--ink-mute:#6e6356;--metal:#8a6d3b;--metal-glow:#b89968;--accent:#6a3a1a;--rule:#d8cdb8}
*{box-sizing:border-box}
html,body{margin:0;background:var(--paper);color:var(--ink);font-family:'Inter',sans-serif;font-weight:300}
header{text-align:center;padding:48px 24px 24px;border-bottom:1px solid var(--rule)}
header .kicker{font-size:9px;letter-spacing:.4em;text-transform:uppercase;color:var(--metal);font-weight:500}
header h1{font-family:'Cormorant Garamond',serif;font-style:italic;font-weight:500;font-size:54px;margin:8px 0 0;color:var(--ink)}
header h1 em{color:var(--metal);font-style:normal}
.searchbar{padding:24px;border-bottom:1px solid var(--rule);text-align:center}
.searchbar input{font-family:'Cormorant Garamond',serif;font-size:24px;font-style:italic;background:transparent;border:none;border-bottom:1px solid var(--rule);color:var(--ink);padding:12px 8px;min-width:480px;outline:none}
.searchbar input:focus{border-bottom-color:var(--metal)}
.searchbar .meta{font-size:10px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-mute);margin-top:14px}
nav{padding:12px 24px;text-align:center;border-bottom:1px solid var(--rule)}
nav a{font-size:9px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-mute);margin:0 10px;text-decoration:none}
nav a:hover{color:var(--metal)}
main{max-width:780px;margin:0 auto;padding:32px 24px 80px}
.result{padding:18px 0;border-bottom:1px solid var(--rule);text-decoration:none;display:block;color:var(--ink)}
.result h3{font-family:'Cormorant Garamond',serif;font-style:italic;font-weight:500;font-size:24px;margin:0 0 4px;color:var(--ink)}
.result h3 em{font-weight:600;font-style:normal;background:rgba(184,153,104,0.18)}
.result .biz{font-family:'Cormorant Garamond',serif;font-style:italic;font-size:14px;color:var(--ink-mute);margin-bottom:8px}
.result .snippet{font-family:'Cormorant Garamond',serif;font-size:14px;line-height:1.5;color:var(--ink)}
.result .snippet em{font-weight:600;font-style:normal;background:rgba(184,153,104,0.18)}
.result .meta{margin-top:6px;font-size:9px;letter-spacing:.22em;text-transform:uppercase;color:var(--ink-mute);font-family:'Inter',sans-serif}
.result:hover{background:rgba(184,153,104,0.04);padding-left:8px;padding-right:8px}
.empty{padding:80px 24px;text-align:center;color:var(--ink-mute);font-style:italic;font-family:'Cormorant Garamond',serif;font-size:24px}
</style>
</head>
<body>
<header>
<div class="kicker">The Corridor · Volume I</div>
<h1>Search the <em>issue</em></h1>
</header>
<nav>
<a href="/issue">All features</a>
<a href="/issue/restaurant">Restaurants</a>
<a href="/issue/professional">Professional</a>
<a href="/issue/medical">Medical</a>
<a href="/issue/beauty">Beauty</a>
<a href="/issue/salon">Salon</a>
<a href="/issue/shop">Shops</a>
<a href="/issue/real-estate">Real estate</a>
<a href="/issue/hospitality">Hospitality</a>
<a href="/issue/fitness">Fitness</a>
</nav>
<div class="searchbar">
<input id="q" type="search" autofocus placeholder="who's on the corridor?" oninput="onSearch()">
<div class="meta" id="count">type a name, vertical, or street…</div>
</div>
<main id="results"></main>
<script>
function escHtml(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); }
function highlight(text, q) {
if (!q || !text) return escHtml(text);
const safe = escHtml(text);
const safeQ = q.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return safe.replace(new RegExp(safeQ, 'gi'), m => `<em>${m}</em>`);
}
function snippet(text, q, len = 180) {
if (!text) return '';
if (!q) return text.slice(0, len) + (text.length > len ? '…' : '');
const i = text.toLowerCase().indexOf(q.toLowerCase());
if (i < 0) return text.slice(0, len) + '…';
const start = Math.max(0, i - 60);
const end = Math.min(text.length, i + q.length + 120);
return (start > 0 ? '…' : '') + text.slice(start, end) + (end < text.length ? '…' : '');
}
let _t;
async function onSearch() {
clearTimeout(_t);
_t = setTimeout(go, 200);
}
async function go() {
const q = document.getElementById('q').value.trim();
if (!q) {
document.getElementById('results').innerHTML = '';
document.getElementById('count').textContent = 'type a name, vertical, or street…';
return;
}
const data = await fetch('/api/magazine?status=published&limit=200').then(r => r.json());
const ql = q.toLowerCase();
const hits = (data.rows || []).filter(r => {
const hay = ((r.name || '') + ' ' + (r.headline || '') + ' ' + (r.subhead || '') + ' ' + (r.editorial || '') + ' ' + (r.address || '') + ' ' + (r.city || '') + ' ' + (r.naics || '') + ' ' + (r.category_tag || '')).toLowerCase();
return hay.includes(ql);
});
document.getElementById('count').innerHTML = `<b style="color:var(--metal-glow);font-family:'Cormorant Garamond',serif;font-style:italic;font-size:18px">${hits.length}</b> ${hits.length === 1 ? 'feature' : 'features'} match`;
document.getElementById('results').innerHTML = hits.length === 0
? `<div class="empty">No matches in this issue.</div>`
: hits.map(r => `
<a class="result" href="/magazine/${r.id}">
<h3>${highlight(r.headline || r.name, q)}</h3>
<div class="biz">${highlight(r.name, q)}${r.address ? ' · ' + highlight(r.address.split(',')[0], q) : ''}</div>
<div class="snippet">${highlight(snippet(r.editorial || r.subhead, q), q)}</div>
<div class="meta">${escHtml(r.category_tag || '')}${r.paid_ads_count > 0 ? ' · $ sponsor candidate' : ''}</div>
</a>`).join('');
}
</script>
</body>
</html>