← back to Consulting Intake

public/portal.html

105 lines

<!doctype html>
<html lang="en">
<head>
<script>
// Credential-safe fetch guard (fleet drop-in) — if this page is opened with
// credentials in the URL (a saved bookmark / Chrome-remembered basic-auth:
// https://user:pass@host/…), the browser poisons document.baseURI, so any
// relative or root-relative fetch('/api/…') throws "Request cannot be constructed
// from a URL that includes credentials" and callers silently fall to an empty
// state. Resolve every non-absolute request URL against the credential-free
// location instead of baseURI. Placed first so it wraps window.fetch before any
// app script runs. Ref: creds-in-url-fetch-guard-fleet-pattern.
(function () {
  var _fetch = window.fetch.bind(window);
  var cleanBase = function () { return location.origin + location.pathname; };
  window.fetch = function (input, init) {
    try {
      if (typeof input === 'string' && !/^[a-z]+:\/\//i.test(input) && input.indexOf('//') !== 0) {
        input = new URL(input, cleanBase()).href;
      } else if (input instanceof Request && !/^[a-z]+:\/\//i.test(input.url)) {
        input = new Request(new URL(input.url, cleanBase()).href, input);
      }
    } catch (_) { /* fall through to native */ }
    return _fetch(input, init);
  };
})();
</script>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Consulting Intake · Deliverable</title>
<style>
  :root{--ink:#14110e;--paper:#f6f2ec;--gold:#a9884f;--line:#dcd3c5}
  *{box-sizing:border-box}
  body{margin:0;font-family:Georgia,serif;background:var(--paper);color:var(--ink)}
  nav{display:flex;justify-content:space-between;align-items:center;padding:16px 28px;background:#fff;border-bottom:1px solid var(--line);font-family:system-ui,sans-serif}
  nav .brand{letter-spacing:.06em}
  nav a{color:var(--gold);text-decoration:none;font-size:13px;letter-spacing:.1em;text-transform:uppercase;margin-left:18px}
  .hero{padding:56px 28px 34px;text-align:center}
  .hero .eyebrow{letter-spacing:.32em;text-transform:uppercase;font-size:11px;color:var(--gold);font-family:system-ui,sans-serif}
  .hero h1{font-weight:400;font-size:44px;margin:.2em 0}
  .hero p{font-style:italic;color:#6b6357;max-width:640px;margin:0 auto}
  section{max-width:1080px;margin:0 auto;padding:28px}
  h2{font-weight:400;font-size:26px;border-bottom:1px solid var(--line);padding-bottom:8px}
  .cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:18px;margin-top:18px}
  .vcard{background:#fff;border:1px solid var(--line);text-decoration:none;color:inherit;transition:.2s}
  .vcard:hover{box-shadow:0 16px 40px rgba(20,17,14,.1);transform:translateY(-2px)}
  .vcard .swatch{height:120px;background:var(--gold)}
  .vcard .body{padding:14px 16px}
  .vcard b{font-family:system-ui,sans-serif;font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:#9a9082}
  .list{font-family:system-ui,sans-serif;font-size:15px}
  .list li{margin:8px 0}
  .badge{display:inline-block;font-family:system-ui,sans-serif;font-size:11px;background:#faf7f1;border:1px solid var(--gold);color:var(--gold);padding:2px 9px;border-radius:20px;margin-left:8px}
</style>
</head>
<body>
  <nav>
    <div class="brand" id="brand">Consulting Intake</div>
    <div><a href="/portal">Deliverable</a><a href="/admin">Growth &amp; Social</a><a href="/">Sign-in</a></div>
  </nav>

  <div class="hero">
    <div class="eyebrow">Consulting Deliverable</div>
    <h1 id="name">Consulting Intake</h1>
    <p id="tag">Tell us about your business — we'll build your new website and social presence.</p>
  </div>

  <section>
    <h2>Website concept directions <span class="badge" id="vcount">—</span></h2>
    <p class="list" style="color:#6b6357">Several high-end redesign concepts of your new site — pick the direction that feels right. (Generate/refresh with <code>node build.mjs</code>.)</p>
    <div class="cards" id="versions"></div>
  </section>

  <section>
    <h2>Priority growth moves</h2>
    <ul class="list" id="sugs"></ul>
  </section>

  <section>
    <h2>Integrated social snapshot</h2>
    <ul class="list" id="social"></ul>
  </section>

<script>
async function j(u){ const r = await fetch(u); return r.ok ? r.json() : null; }
(async () => {
  const c = await j('/api/client'); if (c && c.business_name){ document.getElementById('name').textContent = c.business_name; document.getElementById('brand').textContent = c.business_name; if(c.tagline) document.getElementById('tag').textContent = c.tagline; }
  // version concepts (generated into /versions by build.mjs)
  const known = ['editorial','modern','bold','warm','classic'];
  const vc = document.getElementById('versions'); let found = 0;
  for (const v of known){ const ok = await fetch('/versions/'+v+'.html',{method:'HEAD'}).then(r=>r.ok).catch(()=>false);
    if(ok){ found++; const a=document.createElement('a'); a.className='vcard'; a.href='/versions/'+v+'.html';
      a.innerHTML='<div class="swatch"></div><div class="body"><b>'+v+'</b></div>'; vc.appendChild(a); } }
  document.getElementById('vcount').textContent = found + ' concepts';
  if(!found) vc.innerHTML = '<p style="font-family:system-ui;color:#9a9082">No concepts generated yet — run <code>node build.mjs</code>.</p>';
  // suggestions
  const s = await j('/api/admin/suggestions')||[]; const su=document.getElementById('sugs');
  su.innerHTML = s.length ? s.map(x=>'<li><b>'+(x.title||'')+'</b> <span class="badge">impact '+(x.impact||'?')+' / effort '+(x.effort||'?')+'</span><br><span style="color:#6b6357">'+(x.rationale||'')+'</span></li>').join('') : '<li style="color:#9a9082">No growth moves seeded yet.</li>';
  // socials
  const so = await j('/api/admin/socials')||{}; const el=document.getElementById('social');
  const rows = Object.entries(so).filter(([k])=>!['connected','notes'].includes(k)).map(([k,v])=>'<li><b>'+k+'</b> — '+(v?('<a href="'+(String(v).startsWith('http')?v:'#')+'">'+v+'</a>'):'<span style="color:#c66">not set up yet</span>')+'</li>');
  el.innerHTML = rows.join('') + (so.notes?'<li style="color:#9a9082;font-size:13px">'+so.notes+'</li>':'');
})();
</script>
</body>
</html>