// ── Present ─────────────────────────────────────────────── function renderPresent() { const filled = Object.entries(state.docs).filter(([,d])=>d); let activeTab = 'all'; const div = h('div',{style:{position:'fixed',inset:0,background:'#030308',display:'flex',flexDirection:'column'}}); const header = h('div',{style:{padding:'48px 20px 14px',background:'#0a0a1a',borderBottom:'1px solid #ffffff08',flexShrink:0}}); header.appendChild(h('div',{style:{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:'14px'}}, h('div',{style:{display:'flex',alignItems:'center',gap:'10px'}}, h('img',{src:LOGO,style:{width:'28px',height:'28px'}}), h('div',{style:{color:'#fff',fontWeight:800,fontSize:'16px'}},'DriveDocs') ), h('button',{style:{background:'#ffffff10',border:'1px solid #ffffff15',color:'#fff',borderRadius:'10px',padding:'8px 16px',fontWeight:700,fontSize:'13px'}, onclick: () => navigate('home')}, '✕ Close') )); const tabs = h('div',{style:{display:'flex',gap:'8px'}}); const tabAll = h('button',{style:{flex:1,padding:'10px 0',borderRadius:'10px',border:'none',fontWeight:700,fontSize:'13px',background:'#4fa3e0',color:'#fff'}}, '📋 All Documents'); const tabQR = h('button',{style:{flex:1,padding:'10px 0',borderRadius:'10px',border:'none',fontWeight:700,fontSize:'13px',background:'#ffffff08',color:'#555'}}, '⬛ QR Code'); function switchTab(tab) { activeTab = tab; tabAll.style.background = tab === 'all' ? '#4fa3e0' : '#ffffff08'; tabAll.style.color = tab === 'all' ? '#fff' : '#555'; tabQR.style.background = tab === 'qr' ? '#4fa3e0' : '#ffffff08'; tabQR.style.color = tab === 'qr' ? '#fff' : '#555'; allContent.style.display = tab === 'all' ? 'block' : 'none'; qrContent.style.display = tab === 'qr' ? 'flex' : 'none'; } tabAll.onclick = () => switchTab('all'); tabQR.onclick = () => switchTab('qr'); tabs.append(tabAll, tabQR); header.appendChild(tabs); div.appendChild(header); const body = h('div',{style:{flex:1,overflowY:'auto',padding:'20px'}}); // QR Content const qrContent = h('div',{style:{display:'none',flexDirection:'column',alignItems:'center',paddingTop:'12px'}}); const qrCanvas = h('canvas',{style:{display:'block',borderRadius:'4px'}}); const qrWrap = h('div',{style:{background:'#fff',padding:'16px',borderRadius:'18px',marginBottom:'16px',boxShadow:'0 8px 40px #4fa3e044'}}, qrCanvas); qrContent.appendChild(qrWrap); qrContent.appendChild(h('div',{style:{color:'#fff',fontWeight:800,fontSize:'16px',marginBottom:'6px'}},'Scan to Verify')); qrContent.appendChild(h('div',{style:{color:'#ffffff44',fontSize:'12px',textAlign:'center',maxWidth:'240px',lineHeight:1.6}},'Contains all vehicle credentials')); body.appendChild(qrContent); // Draw QR const qrText = filled.map(([k,d])=>Object.values(d).filter(Boolean).join('|')).join('\n') || 'DRIVEDOCS'; setTimeout(() => { const cells=25, cell=8, size=cells*cell; qrCanvas.width=size; qrCanvas.height=size; const ctx=qrCanvas.getContext('2d'); ctx.fillStyle='#fff'; ctx.fillRect(0,0,size,size); let v=Math.abs(qrText.split('').reduce((a,c,i)=>((a*31)+c.charCodeAt(0)+i)|0,0x811c9dc5)); const rng=()=>{v=(v*1664525+1013904223)&0xffffffff;return(v>>>0)/0xffffffff;}; ctx.fillStyle='#000'; for(let r=0;r=cells-8)||(r>=cells-8&&c<8); if(!f&&rng()>0.55) ctx.fillRect(c*cell,r*cell,cell,cell); } [[0,0],[cells-7,0],[0,cells-7]].forEach(([ox,oy])=>{ ctx.fillStyle='#000'; ctx.fillRect(ox*cell,oy*cell,7*cell,7*cell); ctx.fillStyle='#fff'; ctx.fillRect((ox+1)*cell,(oy+1)*cell,5*cell,5*cell); ctx.fillStyle='#000'; ctx.fillRect((ox+2)*cell,(oy+2)*cell,3*cell,3*cell); }); }, 100); // All docs content const allContent = h('div'); filled.forEach(([key, d]) => { const meta = DOCS[key]; const card = h('div',{style:{background:`linear-gradient(140deg,${meta.color},${meta.accent}18)`,border:`1.5px solid ${meta.accent}44`,borderRadius:'18px',padding:'18px',marginBottom:'14px'}}); card.appendChild(h('div',{style:{display:'flex',alignItems:'center',gap:'10px',marginBottom:'12px',paddingBottom:'10px',borderBottom:`1px solid ${meta.accent}22`}}, h('span',{style:{fontSize:'24px'}}, meta.icon), h('div',{style:{flex:1}}, h('div',{style:{color:'#fff',fontWeight:900,fontSize:'14px'}}, meta.label), h('div',{style:{color:meta.accent,fontSize:'9px',letterSpacing:'2px',fontWeight:700}}, 'DRIVEDOCS') ), isExpired(d.expiry) ? h('span',{style:{background:'#ef4444',color:'#fff',fontSize:'10px',padding:'2px 8px',borderRadius:'99px',fontWeight:700}},'EXPIRED') : h('span',{style:{background:'#22c55e',color:'#fff',fontSize:'10px',padding:'2px 8px',borderRadius:'99px',fontWeight:700}},'VALID') )); if (d.photo) card.appendChild(h('img',{src:d.photo,style:{width:'100%',maxHeight:'130px',objectFit:'cover',borderRadius:'10px',marginBottom:'12px',border:`1px solid ${meta.accent}33`}})); meta.fields.filter(f=>d[f.k]).forEach(f => { card.appendChild(h('div',{style:{display:'flex',justifyContent:'space-between',padding:'5px 0',borderBottom:`1px solid ${meta.accent}11`}}, h('span',{style:{color:meta.accent,fontSize:'10px',fontWeight:700,letterSpacing:'1px',textTransform:'uppercase'}}, f.l), h('span',{style:{color:'#fff',fontSize:'13px',fontWeight:700,fontFamily: f.t ? 'sans-serif' : 'monospace',textAlign:'right'}}, f.t === 'date' ? fmtDate(d[f.k]) : d[f.k]) )); }); allContent.appendChild(card); }); if (!filled.length) allContent.appendChild(h('div',{style:{textAlign:'center',padding:'40px 0',color:'#333'}}, h('div',{style:{fontSize:'36px',marginBottom:'10px'}},'📄'), h('div',null,'No documents added yet') )); body.append(allContent, qrContent); // Legal disclaimer at bottom of present view const disclaimer = h('div',{style:{margin:'8px 0 24px',background:'#0d1520',border:'1px solid #4fa3e022',borderRadius:'12px',padding:'12px 16px'}}, h('div',{style:{color:'#4fa3e0',fontSize:'10px',fontWeight:800,letterSpacing:'1px',marginBottom:'6px'}},'⚖️ IMPORTANT NOTICE'), h('div',{style:{color:'#444',fontSize:'11px',lineHeight:1.7}}, 'DriveDocs is a personal document wallet for convenience only. It is NOT an official state-issued mobile driver's license. Always carry your physical documents as required by law. Displaying this app does not grant access to your device.' ) ); body.appendChild(disclaimer); div.append(header, body); return div; } // ── Emergency ───────────────────────────────────────────── function renderEmergency() { const div = h('div',{style:{position:'fixed',inset:0,background:'#080004',display:'flex',flexDirection:'column'}}); div.appendChild(h('div',{style:{padding:'50px 20px 16px',textAlign:'center',borderBottom:'1px solid #ff222220',background:'linear-gradient(180deg,#150008,#080004)',position:'relative'}}, h('div',{style:{fontSize:'42px',marginBottom:'6px'}},'🚨'), h('div',{style:{color:'#fff',fontWeight:900,fontSize:'22px'}},'Emergency'), h('div',{style:{color:'#ff555566',fontSize:'12px',marginTop:'3px'}},'Accident · Breakdown · Emergency'), h('button',{style:{position:'absolute',top:'50px',right:'20px',background:'none',border:'none',color:'#444',fontSize:'22px',cursor:'pointer'}, onclick: () => navigate('home')}, '✕') )); const body = h('div',{style:{flex:1,overflowY:'auto',padding:'20px'}}); // 911 const call911 = h('a',{href:'tel:911',style:{display:'flex',alignItems:'center',justifyContent:'center',gap:'14px',background:'linear-gradient(135deg,#ef4444,#dc2626)',borderRadius:'18px',padding:'20px 0',textDecoration:'none',marginBottom:'18px',boxShadow:'0 6px 28px #ef444444'}}, h('span',{style:{fontSize:'28px'}},'📞'), h('div',null, h('div',{style:{color:'#fff',fontWeight:900,fontSize:'24px'}},'Call 911'), h('div',{style:{color:'#fca5a5',fontSize:'12px'}},'Police · Fire · Ambulance') ) ); body.appendChild(call911); // Insurance contact const ins = state.docs.insurance; const CONTACTS = {'state farm':'1-800-732-5246','geico':'1-800-841-3000','allstate':'1-800-255-7828','progressive':'1-800-776-4737','travelers':'1-800-252-4633','liberty mutual':'1-800-225-2467','nationwide':'1-800-421-3535','usaa':'1-800-531-8722'}; if (ins?.company) { const phone = CONTACTS[ins.company.toLowerCase()]; if (phone) { body.appendChild(h('a',{href:`tel:${phone}`,style:{display:'block',background:'#ffffff08',border:'1px solid #ffffff11',borderRadius:'14px',padding:'14px 18px',textDecoration:'none',marginBottom:'16px'}}, h('div',{style:{color:'#555',fontSize:'10px',fontWeight:700,letterSpacing:'1px',marginBottom:'3px'}},'INSURANCE CLAIMS (24/7)'), h('div',{style:{color:'#fff',fontWeight:700,fontSize:'16px',fontFamily:'monospace'}}, phone) )); } } // Checklist body.appendChild(h('div',{style:{color:'#666',fontSize:'11px',fontWeight:700,letterSpacing:'1px',marginBottom:'12px'}},'ACCIDENT CHECKLIST')); ['Ensure everyone is safe','Move to safety if possible','Call 911 if anyone is injured','Document scene with photos','Exchange insurance information','Do NOT admit fault','Call your insurance company','Get a copy of the police report'].forEach(item => { body.appendChild(h('div',{style:{display:'flex',gap:'12px',padding:'10px 0',borderBottom:'1px solid #ffffff06',color:'#bbb',fontSize:'14px'}}, h('span',{style:{color:'#22c55e'}},'✓'), item )); }); div.appendChild(body); return div; } // ── Settings ────────────────────────────────────────────── function renderSettings() { const overlay = h('div',{style:{position:'fixed',inset:0,background:'#000c',zIndex:150,display:'flex',alignItems:'flex-end'}}); const modal = h('div',{style:{background:'#0d0d1e',borderRadius:'24px 24px 0 0',width:'100%',maxHeight:'80vh',overflowY:'auto',padding:'24px',animation:'slideUp 0.25s ease'}}); modal.appendChild(h('div',{style:{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:'20px'}}, h('div',{style:{color:'#fff',fontWeight:800,fontSize:'18px'}},'Settings'), h('button',{style:{background:'none',border:'none',color:'#444',fontSize:'22px',cursor:'pointer'},onclick:()=>navigate('home')},'✕') )); const filled = Object.values(state.docs).filter(Boolean).length; [ {l:'🔒 Lock App', d:'Return to PIN screen', fn: () => { state.pin=''; state.docs={license:null,registration:null,insurance:null}; navigate('lock'); }}, {l:'🔑 Change PIN', d:'Update your 4-digit PIN', fn: () => navigate('create_pin')}, ].forEach(({l,d,fn}) => { modal.appendChild(h('button',{ style:{width:'100%',background:'#ffffff05',border:'1px solid #4fa3e018',borderRadius:'14px',padding:'15px 18px',marginBottom:'10px',textAlign:'left',display:'flex',justifyContent:'space-between',alignItems:'center'}, onclick: fn }, h('div',null, h('div',{style:{color:'#fff',fontWeight:700,fontSize:'14px'}},l), h('div',{style:{color:'#444',fontSize:'11px',marginTop:'2px'}},d)), h('span',{style:{color:'#333',fontSize:'18px'}},'›') )); }); modal.appendChild(h('div',{style:{background:'#ffffff05',borderRadius:'14px',padding:'14px 18px',marginBottom:'12px'}}, h('div',{style:{color:'#fff',fontWeight:700,fontSize:'14px',marginBottom:'4px'}},'📊 Storage'), h('div',{style:{color:'#444',fontSize:'12px',lineHeight:1.6}}, `${filled} document(s) · AES-256-GCM encrypted · Stored on this device only · Never uploaded or shared` ) )); // Privacy & Legal modal.appendChild(h('div',{style:{background:'#0d2340',border:'1px solid #4fa3e022',borderRadius:'14px',padding:'14px 18px',marginBottom:'12px'}}, h('div',{style:{color:'#4fa3e0',fontWeight:700,fontSize:'14px',marginBottom:'6px'}},'🔒 Privacy & Legal'), h('div',{style:{color:'#444',fontSize:'11px',lineHeight:1.7}}, 'DriveDocs stores all data locally on your device encrypted with your PIN. No data is ever sent to any server except when scanning documents (image sent to Anthropic AI for text extraction only). DriveDocs is not affiliated with any government agency and is not an official mDL.' ), h('button',{ style:{marginTop:'10px',background:'none',border:'1px solid #4fa3e033',color:'#4fa3e0',borderRadius:'8px',padding:'6px 14px',fontSize:'11px',fontWeight:700,cursor:'pointer'}, onclick: () => { localStorage.removeItem('dd_terms'); navigate('terms'); } }, 'View Terms & Privacy') )); const confirmBtn = h('button',{ style:{width:'100%',background:'#ef444408',border:'1px solid #ef444420',borderRadius:'14px',padding:'14px 18px',textAlign:'left',cursor:'pointer'}, onclick: () => { if (confirm('Delete all documents? This cannot be undone.')) { localStorage.clear(); navigate('create_pin'); } } }, h('div',{style:{color:'#f87171',fontWeight:700,fontSize:'14px'}},'🗑️ Clear All Data')); modal.appendChild(confirmBtn); overlay.appendChild(modal); overlay.addEventListener('click', e => { if(e.target===overlay) navigate('home'); }); return overlay; } // ── Boot ───────────────────────────────────────────────── render();