← 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 => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[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>