<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<script src="store.js"></script>
<style>
  @keyframes sheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
  @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
  .cn-scroll::-webkit-scrollbar { width: 0; height: 0; }
  .cn-root button, .cn-root input, .cn-root textarea { font-family: inherit; }
  .cn-root {
    --sans:'IBM Plex Sans','Segoe UI',sans-serif; --mono:'IBM Plex Mono',monospace;
    --grad1:#1a3c5e44; --grad2:#27496b33;
    --base:#0b1118; --panel:#111a27; --elev:#152233; --input:#0d1622; --hover:#1b2a3a;
    --border:#263548; --bstrong:#35506a; --divider:#1c2735;
    --t1:#e5edf5; --t2:#c7d3e0; --t3:#8ea2b7; --t4:#70859b;
    --accent:#3b82c4; --accentlight:#93c5fd; --danger:#e06c6c; --money:#6ee7b7;
    --shadow-card:0 14px 26px rgba(2,12,24,0.28), inset 0 1px 0 #ffffff07;
    --nav-bg:#0d1622cc;
  }
  .cn-root[data-theme="light"] {
    --grad1:#3b82c41c; --grad2:#27496b10;
    --base:#eaeef3; --panel:#ffffff; --elev:#f4f7fb; --input:#eef2f7; --hover:#e6ecf4;
    --border:#d6dde7; --bstrong:#b6c3d4; --divider:#e8edf3;
    --t1:#16202c; --t2:#33414f; --t3:#5a6b7d; --t4:#84909e;
    --accent:#3b82c4; --accentlight:#1f6fb8; --danger:#c0322f; --money:#057a55;
    --shadow-card:0 8px 20px rgba(40,70,110,0.10), inset 0 1px 0 #ffffff;
    --nav-bg:#ffffffd9;
  }
</style>
</helmet>
<div class="cn-root" data-theme="{{ themeAttr }}" style="position:absolute; inset:0; background:radial-gradient(900px 460px at 12% -8%, var(--grad1), transparent 60%), radial-gradient(760px 380px at 92% -2%, var(--grad2), transparent 58%), var(--base); display:flex; flex-direction:column; font-family:var(--sans); color:var(--t1); letter-spacing:0.01em; overflow:hidden;">

  <!-- status bar -->
  <div style="height:46px; flex:none; display:flex; align-items:flex-end; justify-content:space-between; padding:0 24px 6px; font-family:var(--mono); font-size:13px; color:var(--t2);">
    <span>9:41</span>
    <span style="display:flex; gap:6px; align-items:center; font-size:11px; letter-spacing:0.02em;">5G ▮▮▮▯ 84%</span>
  </div>

  <!-- top bar -->
  <div style="flex:none; height:52px; display:flex; align-items:center; justify-content:space-between; padding:0 16px; border-bottom:1px solid var(--border);">
    <span style="font-family:var(--mono); font-weight:600; font-size:15px; letter-spacing:0.04em; color:var(--t1);">·Ten31·</span>
    <div style="display:flex; align-items:center; gap:10px;">
      <button onClick="{{ openQuickLog }}" aria-label="Log communication" style="width:36px; height:36px; border-radius:999px; border:1px solid var(--border); background:var(--elev); color:var(--t3); cursor:pointer; display:flex; align-items:center; justify-content:center;">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"></path><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"></path></svg>
      </button>
      <button onClick="{{ toggleTheme }}" aria-label="Toggle theme" style="width:36px; height:36px; border-radius:999px; border:1px solid var(--border); background:var(--elev); color:var(--t3); font-size:15px; cursor:pointer; display:flex; align-items:center; justify-content:center; line-height:1;">{{ themeIcon }}</button>
      <button onClick="{{ toggleAccount }}" aria-label="Account" style="width:36px; height:36px; border-radius:999px; border:1px solid var(--bstrong); background:var(--elev); color:var(--accentlight); font-family:var(--mono); font-weight:600; font-size:13px; cursor:pointer; display:flex; align-items:center; justify-content:center;">GG</button>
    </div>
  </div>

  <!-- title + search -->
  <div style="flex:none; padding:14px 16px 12px;">
    <div style="display:flex; align-items:baseline; justify-content:space-between;">
      <span style="font-size:21px; font-weight:600; letter-spacing:-0.01em;">Contacts</span>
      <span style="font-family:var(--mono); font-size:12px; color:var(--t4);">{{ countLabel }}</span>
    </div>
    <input value="{{ search }}" onInput="{{ onSearch }}" placeholder="Search name, email, or firm…" style="width:100%; height:44px; margin-top:13px; background:var(--input); border:1px solid var(--border); border-radius:8px; color:var(--t1); font-family:var(--sans); font-size:15px; padding:0 14px; outline:none; box-sizing:border-box;" />
  </div>

  <!-- directory list -->
  <div class="cn-scroll" style="flex:1; min-height:0; overflow-y:auto; padding:2px 16px 20px; display:flex; flex-direction:column;">
    <sc-for list="{{ groups }}" as="g" hint-placeholder-count="6">
      <div style="position:sticky; top:0; z-index:2; background:var(--base); padding:10px 4px 6px; font-family:var(--mono); font-size:11px; font-weight:600; letter-spacing:0.1em; color:var(--t4);">{{ g.letter }}</div>
      <div style="display:flex; flex-direction:column; gap:8px; padding-bottom:6px;">
        <sc-for list="{{ g.people }}" as="c" hint-placeholder-count="2">
          <button onClick="{{ c.open }}" style="text-align:left; cursor:pointer; width:100%; background:var(--panel); border:1px solid var(--border); border-radius:10px; padding:11px 13px; box-shadow:var(--shadow-card); display:flex; align-items:center; gap:12px; color:var(--t1);">
            <span style="flex:none; width:40px; height:40px; border-radius:999px; background:var(--elev); border:{{ c.ring }}; display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:13px; font-weight:600; color:var(--accentlight);">{{ c.initials }}</span>
            <span style="flex:1; min-width:0; display:flex; flex-direction:column; gap:3px;">
              <span style="font-size:15px; font-weight:600; line-height:1.2; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ c.name }}</span>
              <span style="display:flex; align-items:center; gap:8px; min-width:0;">
                <span style="font-size:13px; color:var(--t3); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ c.org }}</span>
                <span style="flex:none; font-family:var(--mono); font-size:9px; font-weight:600; letter-spacing:0.04em; text-transform:uppercase; padding:2px 7px; border-radius:999px; background:{{ c.stageBg }}; color:{{ c.stageText }}; border:1px solid {{ c.stageBorder }};">{{ c.stage }}</span>
              </span>
            </span>
            <span style="flex:none; font-family:var(--mono); font-size:11px; color:{{ c.lastColor }};">{{ c.last }}</span>
          </button>
        </sc-for>
      </div>
    </sc-for>
    <sc-if value="{{ listEmpty }}" hint-placeholder-val="{{ false }}">
      <div style="padding:48px 20px; text-align:center; color:var(--t4); font-size:14px;">No contacts match.</div>
    </sc-if>
  </div>

  <!-- bottom tab bar -->
  <div style="flex:none; display:flex; border-top:1px solid var(--border); background:var(--nav-bg); backdrop-filter:blur(8px); padding-bottom:18px;">
    <sc-for list="{{ tabs }}" as="t" hint-placeholder-count="4">
      <button onClick="{{ t.go }}" style="flex:1; background:none; border:none; cursor:pointer; height:56px; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:5px; color:{{ t.color }};">
        <span style="width:20px; height:20px; display:flex; align-items:center; justify-content:center;">{{ t.icon }}</span>
        <span style="font-family:var(--mono); font-size:10px; letter-spacing:0.04em;">{{ t.label }}</span>
      </button>
    </sc-for>
  </div>

  <!-- account menu -->
  <sc-if value="{{ accountMenu }}" hint-placeholder-val="{{ false }}">
    <div onClick="{{ closeAccount }}" style="position:absolute; inset:0; z-index:40; animation:fadeIn 120ms ease;">
      <div style="position:absolute; top:96px; right:16px; width:208px; background:var(--elev); border:1px solid var(--bstrong); border-radius:10px; box-shadow:0 24px 56px rgba(1,8,17,0.5); overflow:hidden;">
        <div style="padding:14px 16px; border-bottom:1px solid var(--border);">
          <div style="font-size:14px; font-weight:600; color:var(--t1);">Grant Gilliam</div>
          <div style="font-size:12px; color:var(--t3); margin-top:2px;">grant@ten31.xyz</div>
        </div>
        <div style="padding:6px;">
          <div style="padding:11px 12px; border-radius:7px; font-size:14px; color:var(--t2);">Profile</div>
          <div style="padding:11px 12px; border-radius:7px; font-size:14px; color:var(--danger);">Log out</div>
        </div>
      </div>
    </div>
  </sc-if>

  <!-- contact detail sheet -->
  <sc-if value="{{ detailOpen }}" hint-placeholder-val="{{ false }}">
    <div onClick="{{ closeDetail }}" style="position:absolute; inset:0; z-index:50; background:rgba(4,9,16,0.55); animation:fadeIn 150ms ease; display:flex; flex-direction:column; justify-content:flex-end;">
      <div onClick="{{ stop }}" style="background:var(--panel); border-top:1px solid var(--bstrong); border-radius:20px 20px 0 0; box-shadow:0 -24px 56px rgba(1,8,17,0.4); animation:sheetUp 280ms cubic-bezier(.2,.8,.2,1); padding:0 20px 26px; max-height:90%; display:flex; flex-direction:column;">
        <div style="padding:10px 0 4px; display:flex; justify-content:center; flex:none;"><div style="width:38px; height:4px; border-radius:999px; background:var(--bstrong);"></div></div>
        <div class="cn-scroll" style="overflow-y:auto;">
          <!-- header -->
          <div style="display:flex; align-items:center; gap:13px; padding:6px 0 4px;">
            <span style="flex:none; width:52px; height:52px; border-radius:999px; background:var(--elev); border:{{ d.ring }}; display:flex; align-items:center; justify-content:center; font-family:var(--mono); font-size:16px; font-weight:600; color:var(--accentlight);">{{ d.initials }}</span>
            <div style="min-width:0;">
              <div style="font-size:19px; font-weight:600; line-height:1.2;">{{ d.name }}</div>
              <div style="font-size:13px; color:var(--t3); margin-top:2px;">{{ d.org }}</div>
            </div>
          </div>

          <!-- email -->
          <button onClick="{{ copyEmail }}" style="width:100%; text-align:left; cursor:pointer; margin-top:14px; background:var(--input); border:1px solid var(--border); border-radius:10px; padding:12px 14px; display:flex; align-items:center; justify-content:space-between; gap:10px; color:var(--t1);">
            <span style="display:flex; flex-direction:column; gap:3px; min-width:0;">
              <span style="font-family:var(--mono); font-size:10px; letter-spacing:0.06em; text-transform:uppercase; color:var(--t4);">Email</span>
              <span style="font-family:var(--mono); font-size:14px; color:var(--accentlight); overflow:hidden; text-overflow:ellipsis;">{{ d.email }}</span>
            </span>
            <span style="flex:none; font-family:var(--mono); font-size:11px; color:var(--t4);">copy</span>
          </button>

          <!-- actions -->
          <div style="display:flex; gap:10px; margin-top:12px;">
            <button onClick="{{ logForContact }}" style="flex:2; height:46px; border-radius:8px; border:none; background:linear-gradient(#3b82c4,#2f6ea9); color:#fff; font-size:14px; font-weight:600; cursor:pointer; box-shadow:0 6px 14px rgba(12,40,68,0.35);">Log communication</button>
            <button onClick="{{ draftEmail }}" style="flex:1; height:46px; border-radius:8px; border:1px solid var(--bstrong); background:var(--elev); color:var(--t2); font-size:14px; font-weight:500; cursor:pointer;">Email</button>
          </div>

          <!-- organization -->
          <div style="font-family:var(--mono); font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--t3); margin:20px 0 9px;">Organization</div>
          <div style="background:var(--input); border:1px solid var(--border); border-radius:10px; padding:13px 14px; display:flex; flex-direction:column; gap:11px;">
            <div style="display:flex; align-items:center; justify-content:space-between; gap:10px;">
              <span style="display:flex; align-items:center; gap:8px; min-width:0;">
                <sc-if value="{{ d.existing }}" hint-placeholder-val="{{ false }}"><span style="flex:none; width:0; height:0; border-top:13px solid var(--accent); border-right:13px solid transparent;"></span></sc-if>
                <span style="font-size:15px; font-weight:600; overflow:hidden; text-overflow:ellipsis;">{{ d.org }}</span>
              </span>
              <span style="flex:none; font-family:var(--mono); font-size:10px; font-weight:600; letter-spacing:0.04em; text-transform:uppercase; padding:3px 9px; border-radius:999px; background:{{ d.stageBg }}; color:{{ d.stageText }}; border:1px solid {{ d.stageBorder }};">{{ d.stage }}</span>
            </div>
            <div style="display:flex; align-items:center; justify-content:space-between;">
              <span style="display:flex; flex-direction:column; gap:3px;">
                <span style="font-family:var(--mono); font-size:10px; letter-spacing:0.06em; text-transform:uppercase; color:var(--t4);">Committed</span>
                <span style="font-family:var(--mono); font-size:15px; font-weight:600; color:{{ d.amtColor }};">{{ d.amount }}</span>
              </span>
              <span style="display:flex; flex-direction:column; gap:3px; text-align:right;">
                <span style="font-family:var(--mono); font-size:10px; letter-spacing:0.06em; text-transform:uppercase; color:var(--t4);">Last contact</span>
                <span style="font-family:var(--mono); font-size:14px; color:{{ d.lastColor }};">{{ d.last }}</span>
              </span>
            </div>
            <sc-if value="{{ d.hasNote }}" hint-placeholder-val="{{ false }}">
              <div style="border-top:1px solid var(--divider); padding-top:10px; display:flex; align-items:center; gap:8px;">
                <span style="font-family:var(--mono); font-size:10px; font-weight:600; letter-spacing:0.05em; text-transform:uppercase; padding:2px 6px; border-radius:4px; background:{{ d.noteBg }}; color:{{ d.noteText }};">{{ d.noteType }}</span>
                <span style="font-size:13px; color:var(--t2); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">{{ d.noteSummary }}</span>
              </div>
            </sc-if>
            <button onClick="{{ openInGrid }}" style="align-self:flex-start; background:none; border:none; padding:0; cursor:pointer; color:var(--accentlight); font-size:13px;">Open investor in Grid ›</button>
          </div>
        </div>
      </div>
    </div>
  </sc-if>

  <!-- log communication sheet (also pencil quick-log) -->
  <sc-if value="{{ logOpen }}" hint-placeholder-val="{{ false }}">
    <div onClick="{{ closeLog }}" style="position:absolute; inset:0; z-index:65; background:rgba(4,9,16,0.6); animation:fadeIn 150ms ease; display:flex; flex-direction:column; justify-content:flex-end;">
      <div onClick="{{ stop }}" style="background:var(--panel); border-top:1px solid var(--bstrong); border-radius:20px 20px 0 0; box-shadow:0 -24px 56px rgba(1,8,17,0.4); animation:sheetUp 280ms cubic-bezier(.2,.8,.2,1); padding:0 20px 26px; max-height:90%; display:flex; flex-direction:column;">
        <div style="padding:10px 0 4px; display:flex; justify-content:center; flex:none;"><div style="width:38px; height:4px; border-radius:999px; background:var(--bstrong);"></div></div>
        <div style="display:flex; align-items:center; justify-content:space-between; padding:8px 0 4px; flex:none;">
          <span style="font-size:18px; font-weight:600; color:var(--t1);">Log communication</span>
          <button onClick="{{ closeLog }}" style="background:none; border:none; color:var(--t3); font-size:22px; cursor:pointer; line-height:1; padding:0 4px;">×</button>
        </div>
        <div class="cn-scroll" style="overflow-y:auto; margin-top:10px;">
          {{ logBody }}
        </div>
      </div>
    </div>
  </sc-if>

  <!-- toast -->
  <sc-if value="{{ toast }}" hint-placeholder-val="{{ false }}">
    <div style="position:absolute; left:16px; right:16px; bottom:92px; z-index:70; background:var(--elev); border:1px solid var(--bstrong); border-radius:10px; box-shadow:0 10px 24px rgba(4,12,22,0.35); padding:13px 16px; font-size:14px; color:var(--t1); display:flex; align-items:center; gap:10px; animation:fadeIn 150ms ease;">
      <span style="color:var(--money);">✓</span>{{ toast }}
    </div>
  </sc-if>

</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props="{&quot;$preview&quot;:{&quot;width&quot;:393,&quot;height&quot;:812},&quot;theme&quot;:{&quot;editor&quot;:&quot;enum&quot;,&quot;options&quot;:[&quot;dark&quot;,&quot;light&quot;],&quot;default&quot;:&quot;dark&quot;,&quot;tsType&quot;:&quot;'dark'|'light'&quot;}}">
class Component extends DCLogic {
  constructor(props) {
    super(props);
    this.state = {
      theme: props.theme === 'light' ? 'light' : 'dark',
      search: '',
      accountMenu: false,
      detailId: null,        // contactId
      log: null,             // { contactId|null, type, summary, details, q }
      toast: null,
    };
  }

  componentDidMount() { if (window.T31Store) this._unsub = window.T31Store.subscribe(() => this.forceUpdate()); }
  componentWillUnmount() { if (this._unsub) this._unsub(); }

  seed() {
    const C = (name, email) => ({ name, email });
    return [
      { id: 1, name: 'Northwall Capital', stage: 'commitment', daysAgo: 2, amt: 2500000,
        contacts: [C('Dana Reyes', 'dana@northwall.com'), C('Per Holt', 'per@northwall.com')],
        notes: [['Email', 'Confirmed $2.5M allocation across funds', '2026-06-17']] },
      { id: 2, name: 'Brightseed Partners', stage: 'engaged', daysAgo: 5, amt: 0,
        contacts: [C('Omar Said', 'omar@brightseed.vc')], notes: [['Note', 'Intro from Polaris — warm', '2026-06-14']] },
      { id: 3, name: 'Cedarline Family Office', stage: 'commitment', daysAgo: 7, amt: 1200000,
        contacts: [C('Lena Cho', 'lena@cedarline.com')], notes: [['Call', 'Wire received, fully funded', '2026-06-12']] },
      { id: 4, name: 'Vance & Co', stage: 'engaged', daysAgo: 3, amt: 0,
        contacts: [C('Marcus Vance', 'mv@vanceco.com')], notes: [] },
      { id: 5, name: 'Polaris Endowment', stage: 'diligence', daysAgo: 1, amt: 5000000,
        contacts: [C('Ruth Almeida', 'ralmeida@polaris.org')], notes: [['Meeting', 'IC presentation went well', '2026-06-18']] },
      { id: 7, name: 'Meridian Trust', stage: 'commitment', daysAgo: 4, amt: 800000,
        contacts: [C('Sofia Marin', 'sofia@meridiantrust.com')], notes: [['Note', 'Signed side letter', '2026-06-14']] },
      { id: 8, name: 'Atlas Ventures Fund', stage: 'engaged', daysAgo: 6, amt: 0,
        contacts: [C('Will Tanaka', 'will@atlasvf.com')], notes: [] },
      { id: 9, name: 'K. Whitfield', stage: null, daysAgo: 21, amt: 0,
        contacts: [C('Kira Whitfield', 'kira@whitfield.io')], notes: [['Note', 'No allocation — parked', '2026-05-28']] },
      { id: 10, name: 'Granite Bay LP', stage: 'commitment', daysAgo: 30, amt: 3300000,
        contacts: [C('Tom Becker', 'tom@granitebay.com')], notes: [] },
    ];
  }

  stageColors(s, theme) {
    const light = theme === 'light';
    const dark = {
      'lead': { bg: '#70859b22', text: '#8ea2b7', border: '#2635488a' },
      'engaged': { bg: '#3b82c422', text: '#93c5fd', border: '#3b82c44d' },
      'diligence': { bg: '#e0b3411f', text: '#e0b341', border: '#e0b3413d' },
      'commitment': { bg: '#10b9811f', text: '#6ee7b7', border: '#10b9813d' },
    };
    const lite = {
      'lead': { bg: '#5a6b7d14', text: '#5a6b7d', border: '#d6dde7' },
      'engaged': { bg: '#3b82c416', text: '#2266a0', border: '#bcd2ea' },
      'diligence': { bg: '#e0b34122', text: '#8a6c12', border: '#e4d29a' },
      'commitment': { bg: '#10b98118', text: '#057a55', border: '#a9ddca' },
    };
    const map = light ? lite : dark;
    return map[s] || (light ? { bg: '#5a6b7d12', text: '#84909e', border: '#d6dde7' } : { bg: '#1b2a3a', text: '#70859b', border: '#263548' });
  }
  noteTag(t, theme) {
    const light = theme === 'light';
    const dark = { 'Email': { bg: '#3b82c422', text: '#93c5fd' }, 'Call': { bg: '#10b98122', text: '#6ee7b7' }, 'Meeting': { bg: '#f59e0b1f', text: '#fcd34d' }, 'Note': { bg: '#1b2a3a', text: '#8ea2b7' } };
    const lite = { 'Email': { bg: '#3b82c41a', text: '#2266a0' }, 'Call': { bg: '#10b9811a', text: '#057a55' }, 'Meeting': { bg: '#f59e0b1a', text: '#a76a07' }, 'Note': { bg: '#5a6b7d14', text: '#5a6b7d' } };
    const map = light ? lite : dark;
    return map[t] || map['Note'];
  }
  recency(days, theme) {
    const AMBER = 10, STALE = 30;
    const light = theme === 'light';
    if (days >= STALE) return { text: days + 'd stale', color: light ? '#c0322f' : '#f87171' };
    if (days >= AMBER) return { text: days + 'd ago', color: light ? '#a76a07' : '#e0b341' };
    return { text: days + 'd ago', color: light ? '#84909e' : '#70859b' };
  }
  money(n) {
    if (!n) return '$0';
    if (n >= 1e6) return '$' + (n / 1e6).toFixed(n % 1e6 === 0 ? 0 : 1) + 'M';
    if (n >= 1e3) return '$' + Math.round(n / 1e3) + 'K';
    return '$' + n;
  }
  initials(name) {
    const parts = name.replace(/[^A-Za-z .]/g, '').trim().split(/\s+/);
    if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
    return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
  }
  stageLabel(s) { return s || 'no stage'; }

  // flatten investors → contacts (from the shared store)
  allContacts() {
    const out = [];
    const S = window.T31Store;
    const list = S ? S.investors : [];
    list.forEach(inv => (inv.contacts || []).forEach((c, idx) => {
      out.push({ cid: inv.id * 100 + idx, name: c.name, email: c.email, orgId: inv.id, org: inv.name, stage: inv.stage, amt: S ? S.committed(inv) : 0, daysAgo: inv.daysAgo, notes: inv.notes });
    }));
    return out;
  }
  contactById(cid) { return this.allContacts().find(c => c.cid === cid); }

  toast(msg) { this.setState({ toast: msg }); clearTimeout(this._tt); this._tt = setTimeout(() => this.setState({ toast: null }), 2000); }
  setLog(patch) { this.setState(s => ({ log: Object.assign({}, s.log, patch) })); }
  saveLog() {
    const lg = this.state.log; if (!lg || !lg.contactId || !lg.summary.trim()) return;
    const c = this.contactById(lg.contactId);
    const entry = [lg.type, lg.summary.trim(), '2026-06-19'];
    if (window.T31Store) window.T31Store.logNote(c.orgId, entry);
    this.setState({ log: null });
    this.toast('Logged for ' + c.name);
  }

  renderVals() {
    const s = this.state;
    const theme = s.theme;
    const q = s.search.trim().toLowerCase();

    let people = this.allContacts();
    if (q) people = people.filter(c => c.name.toLowerCase().includes(q) || c.email.toLowerCase().includes(q) || c.org.toLowerCase().includes(q));
    people.sort((a, b) => a.name.localeCompare(b.name));

    const mk = (c) => {
      const sc = this.stageColors(c.stage, theme);
      const rec = this.recency(c.daysAgo, theme);
      return {
        cid: c.cid, name: c.name, org: c.org, initials: this.initials(c.name),
        ring: c.amt > 0 ? '1.5px solid var(--accent)' : '1px solid var(--border)',
        stage: this.stageLabel(c.stage), stageBg: sc.bg, stageText: sc.text, stageBorder: sc.border,
        last: rec.text, lastColor: rec.color,
        open: () => this.setState({ detailId: c.cid }),
      };
    };

    // group by first letter of name
    const groups = [];
    people.forEach(c => {
      const letter = c.name[0].toUpperCase();
      let g = groups[groups.length - 1];
      if (!g || g.letter !== letter) { g = { letter, people: [] }; groups.push(g); }
      g.people.push(mk(c));
    });

    // detail
    const sel = s.detailId ? this.contactById(s.detailId) : null;
    let d = null;
    if (sel) {
      const sc = this.stageColors(sel.stage, theme);
      const rec = this.recency(sel.daysAgo, theme);
      const note = sel.notes && sel.notes[0];
      const nt = note ? this.noteTag(note[0], theme) : null;
      d = {
        name: sel.name, org: sel.org, email: sel.email, initials: this.initials(sel.name),
        ring: sel.amt > 0 ? '1.5px solid var(--accent)' : '1px solid var(--border)',
        existing: sel.amt > 0,
        stage: this.stageLabel(sel.stage), stageBg: sc.bg, stageText: sc.text, stageBorder: sc.border,
        amount: this.money(sel.amt), amtColor: sel.amt > 0 ? (theme === 'light' ? '#057a55' : '#6ee7b7') : 'var(--t4)',
        last: rec.text, lastColor: rec.color,
        hasNote: !!note, noteType: note ? note[0].toUpperCase() : '', noteSummary: note ? note[1] : '',
        noteBg: nt ? nt.bg : '', noteText: nt ? nt.text : '',
      };
    }

    // tabs
    const tabs = [
      { key: 'grid', label: 'Grid' }, { key: 'pipeline', label: 'Pipeline' },
      { key: 'reminders', label: 'Reminders' }, { key: 'contacts', label: 'Contacts' },
    ].map(t => ({
      label: t.label, color: t.key === 'contacts' ? 'var(--accent)' : 'var(--t4)',
      icon: this.tabIcon(t.key, t.key === 'contacts'),
      go: () => { if (window.T31Store) window.T31Store.setTab(t.key); },
    }));

    const logBody = s.log ? this.buildLog(s.log) : null;

    return {
      themeAttr: theme, themeIcon: theme === 'light' ? '☾' : '☀',
      toggleTheme: () => { const t = theme === 'light' ? 'dark' : 'light'; if (window.T31Store) window.T31Store.setTheme(t); this.setState({ theme: t }); },
      toggleAccount: () => this.setState(st => ({ accountMenu: !st.accountMenu })),
      closeAccount: () => this.setState({ accountMenu: false }),
      accountMenu: s.accountMenu,
      countLabel: people.length + (people.length === 1 ? ' person' : ' people'),
      search: s.search, onSearch: e => this.setState({ search: e.target.value }),
      groups, listEmpty: people.length === 0, tabs,
      // detail
      detailOpen: !!sel, d,
      closeDetail: () => this.setState({ detailId: null }),
      stop: e => e.stopPropagation(),
      copyEmail: () => this.toast('Email copied'),
      draftEmail: () => this.toast('Drafting email to ' + (sel ? sel.email : '')),
      openInGrid: () => { if (window.T31Store && sel) window.T31Store.openInvestor(sel.orgId); },
      logForContact: () => this.setState({ log: { contactId: sel.cid, type: 'Note', summary: '', details: '', q: '' } }),
      openInGridLabel: 'Open investor in Grid',
      // log
      openQuickLog: () => this.setState({ log: { contactId: null, type: 'Note', summary: '', details: '', q: '' } }),
      closeLog: () => this.setState({ log: null }),
      logOpen: !!s.log, logBody,
      toast: s.toast,
    };
  }

  buildLog(lg) {
    const h = React.createElement;
    const theme = this.state.theme;
    const dark = theme !== 'light';
    const T = { input: dark ? '#0d1622' : '#eef2f7', border: dark ? '#263548' : '#d6dde7', bstrong: dark ? '#35506a' : '#b6c3d4',
      t1: dark ? '#e5edf5' : '#16202c', t2: dark ? '#c7d3e0' : '#33414f', t3: dark ? '#8ea2b7' : '#5a6b7d', t4: dark ? '#70859b' : '#84909e',
      elev: dark ? '#152233' : '#f4f7fb', accentlight: dark ? '#93c5fd' : '#1f6fb8', panel: dark ? '#111a27' : '#fff' };
    const inputStyle = { width: '100%', height: 46, background: T.input, border: '1px solid ' + T.border, borderRadius: 8, color: T.t1, fontFamily: 'var(--sans)', fontSize: 15, padding: '0 14px', outline: 'none', boxSizing: 'border-box' };
    const label = (t) => h('div', { style: { fontFamily: 'var(--mono)', fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase', color: T.t3, margin: '16px 0 8px' } }, t);

    // phase 1: pick contact
    if (!lg.contactId) {
      const qn = (lg.q || '').trim().toLowerCase();
      let pool = this.allContacts();
      if (qn) pool = pool.filter(c => c.name.toLowerCase().includes(qn) || c.email.toLowerCase().includes(qn) || c.org.toLowerCase().includes(qn));
      else pool = pool.sort((a, b) => a.daysAgo - b.daysAgo);
      pool = pool.slice(0, 8);
      return h('div', null,
        h('div', { style: { fontSize: 13, color: T.t3, lineHeight: 1.5, marginBottom: 12 } }, 'Pick a contact, then log the communication.'),
        h('input', { value: lg.q, onChange: e => this.setLog({ q: e.target.value }), style: inputStyle, placeholder: 'Search contact or firm…', autoFocus: true }),
        h('div', { style: { display: 'flex', flexDirection: 'column', gap: 8, marginTop: 12 } }, pool.length ? pool.map(c => {
          const sc = this.stageColors(c.stage, theme);
          return h('button', { key: c.cid, onClick: () => this.setLog({ contactId: c.cid }), style: { width: '100%', textAlign: 'left', cursor: 'pointer', background: T.input, border: '1px solid ' + T.border, borderRadius: 10, padding: '11px 13px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, color: T.t1 } },
            h('span', { style: { display: 'flex', flexDirection: 'column', gap: 3, minWidth: 0 } },
              h('span', { style: { fontSize: 15, fontWeight: 500 } }, c.name),
              h('span', { style: { fontSize: 12, color: T.t3 } }, c.org)),
            h('span', { style: { flex: 'none', fontFamily: 'var(--mono)', fontSize: 10, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', padding: '3px 8px', borderRadius: 999, background: sc.bg, color: sc.text, border: '1px solid ' + sc.border } }, this.stageLabel(c.stage)));
        }) : h('div', { style: { fontSize: 13, color: T.t4, padding: '16px 4px' } }, 'No matches.'))
      );
    }

    // phase 2: form
    const c = this.contactById(lg.contactId);
    const types = ['Note', 'Email', 'Call', 'Meeting'];
    const disabled = !lg.summary.trim();
    return h('div', null,
      h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, background: T.input, border: '1px solid ' + T.border, borderRadius: 10, padding: '11px 13px' } },
        h('span', { style: { display: 'flex', flexDirection: 'column', gap: 2, minWidth: 0 } },
          h('span', { style: { fontFamily: 'var(--mono)', fontSize: 10, letterSpacing: '0.06em', textTransform: 'uppercase', color: T.t4 } }, 'Logging for'),
          h('span', { style: { fontSize: 15, fontWeight: 600, color: T.t1 } }, c.name + ' · ' + c.org)),
        h('button', { onClick: () => this.setLog({ contactId: null }), style: { flex: 'none', background: 'none', border: 'none', color: T.accentlight, fontSize: 13, cursor: 'pointer' } }, 'Change')),
      label('Type'),
      h('div', { style: { display: 'flex', gap: 8 } }, types.map(tp => {
        const on = lg.type === tp; const tc = this.noteTag(tp, theme);
        return h('button', { key: tp, onClick: () => this.setLog({ type: tp }), style: { flex: 1, height: 40, borderRadius: 7, cursor: 'pointer', fontFamily: 'var(--mono)', fontSize: 12, fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', border: '1px solid ' + (on ? T.bstrong : T.border), background: on ? tc.bg : T.input, color: on ? tc.text : T.t3 } }, tp);
      })),
      label('Summary'),
      h('input', { value: lg.summary, onChange: e => this.setLog({ summary: e.target.value }), style: inputStyle, placeholder: 'Short headline', autoFocus: true }),
      label('Details'),
      h('textarea', { value: lg.details, onChange: e => this.setLog({ details: e.target.value }), style: Object.assign({}, inputStyle, { height: 92, padding: '12px 14px', resize: 'none', lineHeight: 1.45 }), placeholder: 'Full context kept in communications history' }),
      h('div', { style: { fontSize: 12, color: T.t4, marginTop: 8, lineHeight: 1.45 } }, 'Posts to ' + c.org + '\u2019s timeline via the one-row log path and bumps last contact to today.'),
      h('button', { onClick: () => this.saveLog(), disabled: disabled, style: { width: '100%', height: 48, marginTop: 18, borderRadius: 8, border: 'none', background: disabled ? T.elev : 'linear-gradient(#3b82c4,#2f6ea9)', color: disabled ? T.t4 : '#fff', fontSize: 15, fontWeight: 600, cursor: disabled ? 'default' : 'pointer' } }, 'Log communication')
    );
  }

  tabIcon(key, active) {
    const c = active ? '#3b82c4' : (this.state.theme === 'light' ? '#84909e' : '#70859b');
    const mk = (children) => React.createElement('svg', { width: 20, height: 20, viewBox: '0 0 20 20', fill: 'none' }, children);
    const r = (pp) => React.createElement('rect', pp);
    const ln = (pp) => React.createElement('line', Object.assign({}, pp, { stroke: c, strokeWidth: 1.6, strokeLinecap: 'round' }));
    if (key === 'grid') return mk([
      r({ key: 1, x: 3, y: 3, width: 6, height: 6, rx: 1, stroke: c, strokeWidth: 1.6 }),
      r({ key: 2, x: 11, y: 3, width: 6, height: 6, rx: 1, stroke: c, strokeWidth: 1.6 }),
      r({ key: 3, x: 3, y: 11, width: 6, height: 6, rx: 1, stroke: c, strokeWidth: 1.6 }),
      r({ key: 4, x: 11, y: 11, width: 6, height: 6, rx: 1, stroke: c, strokeWidth: 1.6 }),
    ]);
    if (key === 'pipeline') return mk([
      r({ key: 1, x: 3, y: 3, width: 4.5, height: 14, rx: 1, stroke: c, strokeWidth: 1.6 }),
      r({ key: 2, x: 9.25, y: 3, width: 4.5, height: 10, rx: 1, stroke: c, strokeWidth: 1.6 }),
      r({ key: 3, x: 15.5, y: 3, width: 1.5, height: 6, rx: 0.7, fill: c }),
    ]);
    if (key === 'reminders') return mk([
      React.createElement('circle', { key: 1, cx: 10, cy: 11, r: 6.2, stroke: c, strokeWidth: 1.6 }),
      ln({ key: 2, x1: 10, y1: 11, x2: 10, y2: 7.5 }),
      ln({ key: 3, x1: 10, y1: 11, x2: 12.4, y2: 12 }),
      ln({ key: 4, x1: 7, y1: 3.4, x2: 4.4, y2: 5.4 }),
      ln({ key: 5, x1: 13, y1: 3.4, x2: 15.6, y2: 5.4 }),
    ]);
    return mk([
      React.createElement('circle', { key: 1, cx: 10, cy: 7, r: 3.2, stroke: c, strokeWidth: 1.6 }),
      React.createElement('path', { key: 2, d: 'M4 16.5c0-3 2.7-4.8 6-4.8s6 1.8 6 4.8', stroke: c, strokeWidth: 1.6, strokeLinecap: 'round' }),
    ]);
  }
}
</script>
</body>
</html>
