Your Living Family Story

Connect your family,
one branch at a time

Add relatives and invite them via WhatsApp. Each confirmation grows your tree and brings your family closer — wherever they are in the world.

7
Family Members
4
Confirmed
2
Pending
3
Generations
AN
Antonio Greco
Your Grandfather
Confirmed
♥
LG
Lucia Greco
Your Grandmother
Confirmed
JR
James Rivera
Your Grandfather
Pending
♥
MR
Marie Rivera
Your Grandmother
Invited
MG
Marco Greco
Your Father
Confirmed
♥
SR
Sofia Rivera
Your Mother
Confirmed
You
Marc Greco
You
Owner
AG
Anna Greco
Your Sister
Pending
gp1: { name: 'Antonio Greco', relation: 'Your Grandfather', initials: 'AN', status: 'confirmed', phone: '+39 02 1234 5678', dob: '1938-03-15', bg: 'rgba(122,158,126,0.15)', color: '#4A7A4E' }, gp2: { name: 'Lucia Greco', relation: 'Your Grandmother', initials: 'LG', status: 'confirmed', phone: '+39 02 1234 5679', dob: '1942-07-22', bg: 'rgba(212,165,165,0.2)', color: '#A05050' }, gp3: { name: 'James Rivera', relation: 'Your Grandfather', initials: 'JR', status: 'pending', phone: '+1 305 555 0101', dob: '1940-11-08', bg: 'rgba(200,146,42,0.12)', color: '#9A6B0F' }, gp4: { name: 'Marie Rivera', relation: 'Your Grandmother', initials: 'MR', status: 'invited', phone: '+1 305 555 0102', dob: '1944-05-30', bg: 'rgba(212,165,165,0.15)', color: '#A05050' }, dad: { name: 'Marco Greco', relation: 'Your Father', initials: 'MG', status: 'confirmed', phone: '+1 212 555 0201', dob: '1965-08-12', bg: 'rgba(61,43,31,0.1)', color: 'var(--bark)' }, mom: { name: 'Sofia Rivera', relation: 'Your Mother', initials: 'SR', status: 'confirmed', phone: '+1 212 555 0202', dob: '1968-02-28', bg: 'rgba(212,165,165,0.2)', color: '#A05050' }, self: { name: 'Marc Greco', relation: 'You', initials: 'MG', status: 'self', phone: '+1 917 555 0001', dob: '1990-06-15', bg: 'var(--bark)', color: 'var(--cream)' }, sib1: { name: 'Anna Greco', relation: 'Your Sister', initials: 'AG', status: 'pending', phone: '+1 917 555 0002', dob: '1993-09-04', bg: 'rgba(200,146,42,0.12)', color: '#9A6B0F' }, }; const statusConfig = { confirmed: { label: 'Confirmed', color: 'var(--sage)', badgeClass: 'badge-confirmed' }, pending: { label: 'Pending Confirmation', color: 'var(--gold)', badgeClass: 'badge-pending' }, invited: { label: 'Invited', color: '#A05050', badgeClass: 'badge-invited' }, self: { label: 'Tree Owner', color: 'var(--bark)', badgeClass: '' }, }; let nextId = 10; function renderMemberList() { const list = document.getElementById('memberList'); list.innerHTML = ''; Object.entries(members).forEach(([id, m]) => { const s = statusConfig[m.status]; list.innerHTML += `
${m.initials}
${m.name}
${m.relation}
${s.label.split(' ')[0]}
`; }); } function selectNode(id) { const m = members[id]; if (!m) return; const s = statusConfig[m.status]; // Update selected state document.querySelectorAll('.tree-node').forEach(n => n.classList.remove('selected')); document.querySelectorAll('.member-card').forEach(n => n.classList.remove('active')); const node = document.getElementById('node-' + id); if (node) node.classList.add('selected'); const mc = document.getElementById('mc-' + id); if (mc) mc.classList.add('active'); // Fill panel document.getElementById('panelAvatar').style.cssText = `background:${m.bg}; color:${m.color}; width:64px; height:64px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-family:'Cormorant Garamond',serif; font-size:1.6rem; font-weight:600; flex-shrink:0;`; document.getElementById('panelAvatar').textContent = m.initials; document.getElementById('panelName').textContent = m.name; document.getElementById('panelRelation').textContent = m.relation; document.getElementById('panelBadge').innerHTML = `${s.label}`; let bodyHtml = `

Details

Phone ${m.phone}
Born ${m.dob ? new Date(m.dob).toLocaleDateString('en-US', {year:'numeric',month:'long',day:'numeric'}) : 'Unknown'}
Status ${s.label}
`; if (m.status === 'pending' || m.status === 'invited') { const msg = generateWhatsAppMsg(m.name, m.relation); bodyHtml += `

WhatsApp Invitation

${m.status === 'pending' ? 'Waiting for confirmation. You can resend the invite below.' : 'Invitation sent. Still waiting for a response.'}

${msg}
`; } if (m.status === 'confirmed' && id !== 'self') { bodyHtml += `
✓ Confirmed Member
${m.name} has confirmed their place in your family tree. They can now add their own relatives to grow the tree further.
`; } document.getElementById('panelBody').innerHTML = bodyHtml; document.getElementById('detailPanel').classList.add('open'); } function closePanel() { document.getElementById('detailPanel').classList.remove('open'); document.querySelectorAll('.tree-node').forEach(n => n.classList.remove('selected')); document.querySelectorAll('.member-card').forEach(n => n.classList.remove('active')); } function generateWhatsAppMsg(name, relation) { const fname = name.split(' ')[0]; return `Hi ${fname}! 👋 Marc Greco is building a KinTree family tree and has added you as his ${relation.toLowerCase().replace('your ', '')}. Tap the link below to confirm your connection and see your family tree grow! 🌳\n\nhttps://kintree.ai/join/abc123`; } function openAddModal() { document.getElementById('addModal').classList.add('open'); } function closeAddModal() { document.getElementById('addModal').classList.remove('open'); document.getElementById('modalName').value = ''; document.getElementById('modalRelation').value = ''; document.getElementById('modalPhone').value = ''; document.getElementById('modalDob').value = ''; } function addMember() { const name = document.getElementById('modalName').value.trim(); const relation = document.getElementById('modalRelation').value; const phone = document.getElementById('modalPhone').value.trim(); if (!name || !relation || !phone) { showToast('⚠️ Please fill in all required fields'); return; } const initials = name.split(' ').map(w => w[0]).join('').slice(0,2).toUpperCase(); const colors = [ { bg: 'rgba(122,158,126,0.15)', color: '#4A7A4E' }, { bg: 'rgba(200,146,42,0.12)', color: '#9A6B0F' }, { bg: 'rgba(212,165,165,0.2)', color: '#A05050' }, { bg: 'rgba(61,43,31,0.1)', color: 'var(--bark)' }, ]; const c = colors[nextId % colors.length]; const id = 'member' + nextId++; members[id] = { name, relation: 'Your ' + relation, initials, status: 'pending', phone, dob: document.getElementById('modalDob').value, bg: c.bg, color: c.color }; // Add node to tree const gen3 = document.getElementById('gen3'); const addBtn = gen3.querySelector('.add-node'); const newNode = document.createElement('div'); newNode.className = 'tree-node'; newNode.id = 'node-' + id; newNode.style.animationDelay = '0.1s'; newNode.onclick = () => selectNode(id); newNode.innerHTML = `
${initials}
${name}
Your ${relation}
Pending
`; const spacer = document.createElement('div'); spacer.style.width = '24px'; gen3.insertBefore(spacer, addBtn); gen3.insertBefore(newNode, addBtn); // Update stats updateStats(); renderMemberList(); closeAddModal(); // Show success const msg = generateWhatsAppMsg(name, 'Your ' + relation); document.getElementById('successMsg').textContent = `A WhatsApp invitation has been sent to ${name} at ${phone}. Once they confirm, they'll appear on your tree!`; document.getElementById('successPreview').textContent = msg; document.getElementById('successModal').classList.add('open'); } function closeSuccessModal() { document.getElementById('successModal').classList.remove('open'); } function quickAdd() { const name = document.getElementById('quickName').value.trim(); const relation = document.getElementById('quickRelation').value; const phone = document.getElementById('quickPhone').value.trim(); if (!name || !relation || !phone) { showToast('⚠️ Please fill in name, relationship, and phone'); return; } document.getElementById('modalName').value = name; document.getElementById('modalRelation').value = relation; document.getElementById('modalPhone').value = phone; document.getElementById('quickName').value = ''; document.getElementById('quickRelation').value = ''; document.getElementById('quickPhone').value = ''; addMember(); } function resendInvite(id) { const m = members[id]; showToast(`📱 Invite resent to ${m.name}`); } function shareTree() { navigator.clipboard?.writeText('https://kintree.ai/tree/marc-greco-abc123').catch(() => {}); showToast('🔗 Tree link copied to clipboard!'); } function updateStats() { const all = Object.values(members); document.getElementById('statTotal').textContent = all.length; document.getElementById('statConfirmed').textContent = all.filter(m => m.status === 'confirmed' || m.status === 'self').length; document.getElementById('statPending').textContent = all.filter(m => m.status === 'pending' || m.status === 'invited').length; } function showToast(msg) { const t = document.getElementById('toast'); t.textContent = msg; t.classList.add('show'); setTimeout(() => t.classList.remove('show'), 3000); } // Close modal on overlay click document.getElementById('addModal').addEventListener('click', function(e) { if (e.target === this) closeAddModal(); }); document.getElementById('successModal').addEventListener('click', function(e) { if (e.target === this) closeSuccessModal(); }); // Close panel on escape document.addEventListener('keydown', e => { if (e.key === 'Escape') { closePanel(); closeAddModal(); closeSuccessModal(); } }); // Init renderMemberList(); updateStats();