← back to Abrams

public/competitors.html

88 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Abrams — Competitive Landscape</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;1,400&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/static/css/panel.css" />
<style>
  .container { max-width: 980px; margin: 0 auto; padding: 110px 32px 80px; }
  .md h1 { font-family: var(--serif); font-size: 48px; font-weight: 400; margin: 0 0 8px; }
  .md h2 { font-family: var(--serif); font-size: 30px; font-weight: 400; margin: 40px 0 16px; padding-bottom: 8px; border-bottom: 1px solid var(--hairline); }
  .md h3 { font-family: var(--serif); font-size: 22px; font-weight: 500; margin: 28px 0 8px; color: var(--accent); }
  .md p { color: var(--fg); font-size: 15px; line-height: 1.75; margin: 12px 0; }
  .md ul, .md ol { padding-left: 22px; color: var(--fg); }
  .md li { font-size: 14px; line-height: 1.7; padding: 3px 0; }
  .md strong { color: var(--fg); font-weight: 600; }
  .md hr { border: 0; border-top: 1px solid var(--hairline); margin: 32px 0; }
  .md code { background: #000; color: var(--accent); padding: 2px 6px; font-family: var(--mono); font-size: 12px; }
  .md a { color: var(--accent); }
  .md blockquote { border-left: 3px solid var(--accent); padding: 8px 22px; margin: 16px 0; background: var(--bg-2); font-style: italic; color: var(--fg-2); }
  .md table { width: 100%; border-collapse: collapse; margin: 16px 0; }
  .md table th, .md table td { padding: 8px 12px; border-bottom: 1px solid var(--hairline); text-align: left; font-size: 13px; }
  .md table th { color: var(--accent); font-family: var(--mono); font-size: 11px; letter-spacing: .15em; text-transform: uppercase; }
</style>
</head>
<body>

<div class="chrome">
  <a class="logo" href="/">ABRAMS<span class="dot">.</span></a>
  <div class="nav-burger" role="button" aria-label="open menu">
    <span class="bars"><span></span><span></span><span></span></span>
    <span class="label">MENU</span>
  </div>
</div>

<div class="container">
  <div id="md" class="md" style="color:var(--fg-3);">loading competitive analysis…</div>
  <p style="color:var(--fg-3);font-family:var(--mono);font-size:11px;letter-spacing:.15em;margin-top:36px;">
    Generated by product-strategist subagent · download as <a href="/api/competitors" download="competitors.md">competitors.md</a>
  </p>
</div>

<footer>© 2026 Steven Abrams</footer>

<script src="/static/js/corner-nav.js"></script>
<script src="/static/js/gamify.js"></script>
<script>
// Minimal markdown-to-HTML — handles h1/h2/h3, lists, bold, links, paragraphs, hr
function md2html(src) {
  const lines = src.split('\n');
  let html = '', inUl = false, inOl = false, inP = false;
  const closeP = () => { if (inP) { html += '</p>'; inP = false; } };
  const closeLists = () => { if (inUl) { html += '</ul>'; inUl = false; } if (inOl) { html += '</ol>'; inOl = false; } };
  const inline = s => s
    .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    .replace(/`([^`]+)`/g, '<code>$1</code>')
    .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
    .replace(/\*([^*]+)\*/g, '<em>$1</em>')
    .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>');
  for (const raw of lines) {
    const line = raw.trimEnd();
    if (/^---+$/.test(line)) { closeP(); closeLists(); html += '<hr>'; continue; }
    if (/^# (.*)/.test(line)) { closeP(); closeLists(); html += `<h1>${inline(line.replace(/^# /, ''))}</h1>`; continue; }
    if (/^## (.*)/.test(line)) { closeP(); closeLists(); html += `<h2>${inline(line.replace(/^## /, ''))}</h2>`; continue; }
    if (/^### (.*)/.test(line)) { closeP(); closeLists(); html += `<h3>${inline(line.replace(/^### /, ''))}</h3>`; continue; }
    if (/^\s*- (.*)/.test(line)) { closeP(); if (inOl){html+='</ol>';inOl=false;} if (!inUl){html += '<ul>'; inUl = true;} html += `<li>${inline(line.replace(/^\s*- /, ''))}</li>`; continue; }
    if (/^\s*\d+\.\s(.*)/.test(line)) { closeP(); if (inUl){html+='</ul>';inUl=false;} if (!inOl){html += '<ol>'; inOl = true;} html += `<li>${inline(line.replace(/^\s*\d+\.\s/, ''))}</li>`; continue; }
    if (line === '') { closeP(); closeLists(); continue; }
    closeLists();
    if (!inP) { html += '<p>'; inP = true; }
    html += inline(line) + ' ';
  }
  closeP(); closeLists();
  return html;
}

fetch('/api/competitors').then(r => r.text()).then(src => {
  document.getElementById('md').style.color = '';
  document.getElementById('md').innerHTML = md2html(src);
}).catch(e => {
  document.getElementById('md').innerHTML = '<p style="color:var(--err);">Competitor analysis not yet generated — the subagent run may have failed.</p>';
});
</script>
</body>
</html>