Spaces:
Running
Running
| <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#9d1515"><title>918 Bail Bonds | Emergency Help</title> | |
| <style> | |
| :root{font-family:Inter,system-ui,sans-serif;color:#172033;background:#f7f8fb;--red:#a71919;--dark:#172033;--line:#dfe5ef}*{box-sizing:border-box}body{margin:0}.wrap{max-width:980px;margin:auto;padding:22px 16px}.top{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.brand{font-weight:850;color:var(--dark);font-size:20px}.small{font-size:13px;color:#667085}.card{background:#fff;border:1px solid var(--line);border-radius:16px;padding:20px;box-shadow:0 8px 26px #1720330d}.hero{background:linear-gradient(135deg,#691010,#b71c1c);color:#fff;border-radius:18px;padding:28px 20px;text-align:center}.hero h1{font-size:clamp(28px,6vw,52px);margin:0 0 8px}.hero p{margin:0;color:#ffe1e1}.grid{display:grid;grid-template-columns:1fr .8fr;gap:18px;margin-top:18px}@media(max-width:720px){.grid{grid-template-columns:1fr}}label{display:block;font-weight:700;font-size:13px;margin:12px 0 5px}input,select,button{font:inherit}input,select{width:100%;padding:11px;border:1px solid #cbd5e1;border-radius:9px}.check{display:flex;gap:9px;align-items:flex-start;font-weight:500;margin-top:14px}.check input{width:auto;margin-top:3px}.save,.secondary,.link{border:0;border-radius:9px;padding:11px 15px;font-weight:800;cursor:pointer}.save{background:var(--dark);color:#fff;width:100%;margin-top:16px}.secondary{background:#fff;color:var(--dark);border:1px solid #b8c2d2}.emergency{width:min(300px,78vw);height:min(300px,78vw);border-radius:50%;border:12px solid #fff;background:#a71919;color:#fff;font-weight:900;font-size:clamp(22px,5vw,38px);line-height:1.05;box-shadow:0 10px 30px #69101055,0 0 0 5px #f2b8b8;cursor:pointer;margin:18px auto 10px;display:block}.emergency:active{transform:scale(.97)}.emergency:disabled{opacity:.55;cursor:not-allowed}.center{text-align:center}.status{margin-top:16px;padding:13px;border-radius:10px;background:#eef4ff;white-space:pre-wrap}.status.good{background:#eaf8ef;color:#176b3c}.status.warn{background:#fff4e5;color:#884b00}.hidden{display:none}.step{display:flex;gap:10px;margin:11px 0;align-items:flex-start}.dot{width:23px;height:23px;border-radius:50%;background:#e7ebf3;text-align:center;line-height:23px;font-size:12px;font-weight:800;flex:none}.ready .dot{background:#eaf8ef;color:#176b3c}.notice{background:#fff4e5;color:#744600;padding:12px;border-radius:10px;margin-top:12px}.calls{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}.calls a{text-decoration:none}.privacy{font-size:12px;color:#667085;line-height:1.45;margin-top:16px} | |
| </style></head><body><main class="wrap"> | |
| <header class="top"><div><div class="brand">918 Bail Bonds</div><div class="small">Pre-arrest emergency support</div></div><nav><a class="secondary" href="./bondsman-portal.html">Bondsman center</a></nav></header> | |
| <section class="hero"><h1>I'M GOING TO JAIL</h1><p>Press once before arrest and your prepared plan is activated, routed, and monitored. Verified 918 and BBT utility can unlock configured automation tiers.</p><button id="emergency" class="emergency" disabled>START<br>EMERGENCY<br>HELP</button><div id="ready" class="small" style="color:#ffe1e1">Complete setup below to enable the button.</div></section> | |
| <div class="grid"><section class="card"><h2>One-time assisted setup</h2><p class="small">The setup assistant saves only the information needed to route your request. It cannot grant permissions without your approval.</p><form id="setup"><label for="name">Full legal name</label><input id="name" autocomplete="name" required><label for="dob">Date of birth</label><input id="dob" type="date" required><label for="phone">Your mobile phone</label><input id="phone" type="tel" autocomplete="tel" required><label for="county">County</label><select id="county"><option>Tulsa</option><option>Oklahoma</option><option>Rogers</option><option>Cleveland</option></select><label for="booking">Booking number, if already known</label><input id="booking" autocomplete="off"><label for="bondphone">Preferred bondsman phone</label><input id="bondphone" type="tel" placeholder="Optional, for one-tap calling"><label for="token918">918 token balance (1 token = $1,000 utility value)</label><input id="token918" type="number" min="0" placeholder="Preview until wallet verification is connected"><label for="bbt">BBT balance (1 BBT = $0.10 utility value)</label><input id="bbt" type="number" min="0" placeholder="Preview until wallet verification is connected"><label class="check"><input id="consent" type="checkbox" required><span>I consent to this information being sent to my selected bondsman and reviewed by a licensed bondsman.</span></label><button class="save" type="submit">Save emergency plan and prepare permissions</button></form><div id="setupstatus" class="status hidden"></div></section> | |
| <aside class="card"><h2>Prepared plan</h2><div id="plan"><div class="step"><span class="dot">1</span><span>Save your identity and contact details.</span></div><div class="step"><span class="dot">2</span><span>Allow location if you want it attached to the request.</span></div><div class="step"><span class="dot">3</span><span>Allow notifications for status updates.</span></div><div class="step"><span class="dot">4</span><span>Press the emergency button before arrest.</span></div></div><button id="edit" class="secondary hidden" style="width:100%;margin-top:12px">Edit saved plan</button><p class="privacy">This tool prepares and routes information. It does not determine eligibility, identify a person automatically, contact a jail as an agent, or replace a licensed bondsman, attorney, emergency services, or legal advice.</p></aside></div> | |
| <section class="card" style="margin-top:18px"><h2>Public Tulsa booking feed</h2><p class="small">Shows only fields the configured source permits for public display. Refreshes every 60 seconds; the source may cache reports for up to one hour.</p><div id="booking-feed" class="status">Checking booking feed…</div></section> | |
| <div id="result" class="card hidden" style="margin-top:18px"><h2>Emergency request</h2><div id="resulttext" class="status"></div><div id="calls" class="calls"></div></div> | |
| </main><script> | |
| const key='918_bail_emergency_profile_v1';const qs=s=>document.querySelector(s);const profile=()=>JSON.parse(localStorage.getItem(key)||'null');const configured=(window.BAILBONDS_API||((window.BAILBONDS_CONFIG||{}).apiBase)||new URLSearchParams(location.search).get('api')||'').replace(/\/$/,'');let timer; | |
| function setStatus(text,kind=''){const e=qs('#setupstatus');e.className='status '+kind;e.textContent=text;e.classList.remove('hidden')} | |
| function ready(){const p=profile();qs('#emergency').disabled=!p;qs('#ready').textContent=p?'Emergency plan ready. Press only when you need it.':'Complete setup below to enable the button.';qs('#edit').classList.toggle('hidden',!p);if(p){['name','dob','phone','county','booking','bondphone','token918','bbt'].forEach(k=>{if(qs('#'+k))qs('#'+k).value=p[k]||''});qs('#consent').checked=!!p.consent;qs('#plan').innerHTML='<div class="step ready"><span class="dot">✓</span><span>Identity and contact details saved on this device.</span></div><div class="step ready"><span class="dot">✓</span><span>Emergency request is ready to send.</span></div><div class="step"><span class="dot">3</span><span>Location and notifications are requested only after you approve them.</span></div><div class="step"><span class="dot">4</span><span>Press the red button before arrest to activate the plan.</span></div>'}} | |
| async function permissionSetup(){let locationState='not requested',notifications='not requested';try{if(navigator.geolocation){await new Promise(resolve=>navigator.geolocation.getCurrentPosition(()=>{locationState='allowed'},()=>{locationState='declined'},{timeout:5000}))}}catch(_){locationState='unavailable'}try{if('Notification'in window){const r=await Notification.requestPermission();notifications=r}}catch(_){notifications='unavailable'}localStorage.setItem(key,JSON.stringify({...profile(),locationState,notifications}));setStatus('Plan saved. Token automation preview: '+(profile().token918||profile().bbt?'eligible for configured workflow tier':'capture and notify only')+'. Location: '+locationState+'. Notifications: '+notifications+'. The emergency button is ready.','good');ready()} | |
| qs('#setup').addEventListener('submit',async e=>{e.preventDefault();const data={name:qs('#name').value.trim(),dob:qs('#dob').value,phone:qs('#phone').value.trim(),county:qs('#county').value,booking:qs('#booking').value.trim(),bondphone:qs('#bondphone').value.trim(),token918:Number(qs('#token918').value||0),bbt:Number(qs('#bbt').value||0),consent:qs('#consent').checked,createdAt:new Date().toISOString()};localStorage.setItem(key,JSON.stringify(data));await permissionSetup()});qs('#edit').onclick=()=>{qs('#setup').scrollIntoView({behavior:'smooth'});qs('#name').focus()}; | |
| function showResult(text,kind=''){qs('#result').classList.remove('hidden');qs('#resulttext').className='status '+kind;qs('#resulttext').textContent=text;const p=profile();qs('#calls').innerHTML=p?.bondphone?'<a class="save" href="tel:'+encodeURIComponent(p.bondphone)+'">Call preferred bondsman</a>':''} | |
| async function watch(id){clearInterval(timer);timer=setInterval(async()=>{try{const r=await fetch(configured+'/api/public/requests/'+encodeURIComponent(id),{cache:'no-store'});if(!r.ok)return;const d=await r.json();showResult('Reference: '+id+'\nLive status: '+d.status+'\nA licensed bondsman must review and respond.','good');if(['reviewed','closed'].includes(d.status))clearInterval(timer)}catch(_){}} ,5000)} | |
| qs('#emergency').addEventListener('click',async()=>{const p=profile();if(!p||!p.consent)return;qs('#emergency').disabled=true;showResult('Preparing your emergency packet…');let coords={latitude:null,longitude:null};try{if(navigator.geolocation)coords=await new Promise(resolve=>navigator.geolocation.getCurrentPosition(x=>resolve({latitude:x.coords.latitude,longitude:x.coords.longitude}),()=>resolve(coords),{timeout:5000}))}catch(_){}const payload={full_name:p.name,date_of_birth:p.dob,phone:p.phone,county:p.county,booking_number:p.booking||'',consent:true,emergency:true,location:coords,token_918_balance:p.token918||0,bbt_balance:p.bbt||0,token_balances_verified:false,source:'918-pre-arrest-emergency-plan'};if(!configured){const id='offline_'+Date.now();localStorage.setItem('918_last_emergency',JSON.stringify({id,payload,createdAt:new Date().toISOString()}));showResult('Emergency packet prepared on this device. No live API is configured, so it could not be transmitted.\n\nCall your preferred bondsman now or connect the app to its API.','warn');qs('#emergency').disabled=false;return}try{const r=await fetch(configured+'/api/intake',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)});const d=await r.json();if(!r.ok)throw Error(d.error||'The live service rejected the request');showResult('Reference: '+d.request_id+'\nLive status: '+d.status+'\nYour prepared emergency plan is active.\nAutomation tier: '+(d.automation?.tier||'standard')+' — '+(d.automation?.label||'workflow request received')+'\nA licensed bondsman must review and respond.','good');watch(d.request_id)}catch(err){localStorage.setItem('918_last_emergency',JSON.stringify({payload,createdAt:new Date().toISOString()}));showResult('The live service could not be reached. Your emergency packet is saved locally for retry.\n\n'+err.message,'warn');qs('#emergency').disabled=false}}); | |
| async function refreshBookings(){const box=qs('#booking-feed');if(!configured){box.textContent='Booking feed is not connected on this static demo. Connect the live API to display authorized public records.';return}try{const r=await fetch(configured+'/api/public/bookings',{cache:'no-store'});const d=await r.json();if(d.status==='not_configured'){box.textContent='Booking source is not configured.';return}if(!d.records?.length){box.textContent=d.status==='source_unavailable'?'Booking source unavailable.':'No public booking records returned.';return}box.innerHTML='<strong>'+d.count+' records</strong> · fetched '+new Date(d.fetched_at).toLocaleString()+'<br><br>'+d.records.map(x=>'<div style="padding:8px 0;border-bottom:1px solid #dfe5ef"><strong>'+String(x.name||x.booking_id||'Booking record').replace(/[<>]/g,'')+'</strong> · '+String(x.status||'booked').replace(/[<>]/g,'')+'<br><span class="small">'+String(x.booked_at||'time unavailable').replace(/[<>]/g,'')+' · '+String(x.charges||'charges unavailable').replace(/[<>]/g,'')+'</span></div>').join('')}catch(_){box.textContent='Booking feed temporarily unavailable.'}} | |
| refresh();refreshBookings();setInterval(refresh,5000);setInterval(refreshBookings,60000); | |
| </script></body></html> |