${imagePreviewsHtml}`;shell.querySelector('.vf-form-card').appendChild(successMsg);successMsg.scrollIntoView({behavior:'smooth',block:'center'});CH.show();MZ.disable()}else throw new Error(data.message||'Upload failed')}catch(e){hideProg();save.textContent='Upload Photos';enable();input.disabled=false;err.textContent='Upload failed β please try again.';err.style.display='block'}};
skip.onclick=()=>{if(label)label.style.display='none';if(description)description.style.display='none';input.style.display='none';head.style.display='none';list.style.display='none';prog.style.display='none';err.style.display='none';row.style.display='none';skippedMsg.style.display='block';skippedMsg.scrollIntoView({behavior:'smooth',block:'center'});V.interact({type:'captured',payload:{photosUploaded:false,photoCount:0,uploadedFiles:[],skipped:true}});CH.show();MZ.disable()};
return()=>{CH.show();MZ.disable()}}};
/* ---- Calendar ---- */
const CE={name:'calendar-picker',type:'response',match:({trace})=>trace?.type==='calendar'||trace?.payload?.name==='calendar-picker',render:({element})=>{CH.hide();MZ.enable();const shell=makeShell({label:'Preferred Date',description:'',submitText:'Confirm Date',errorText:'Please pick a date before confirming.',htmlField:`
Choose a date and then click "Confirm Date" to continue.
`});element.appendChild(shell);stretch(element,shell);AH.hideFor(shell);const calHost=shell.querySelector('.vf-calendar'),err=shell.querySelector('.vf-error'),save=shell.querySelector('.vf-submit');save.disabled=true;const picked=d.createElement('div');picked.className='vf-picked';picked.innerHTML='Date selected: ';save.insertAdjacentElement('afterend',picked);const pickedVal=picked.querySelector('.vf-picked-val'),fmtNice=iso=>{try{const[y,m,dd]=iso.split('-').map(Number),dt=new Date(y,m-1,dd);return dt.toLocaleDateString('en-GB',{weekday:'long',day:'numeric',month:'long',year:'numeric'})}catch{return iso}},toISODateLocal=dt=>{const y=dt.getFullYear(),m=String(dt.getMonth()+1).padStart(2,'0'),d2=String(dt.getDate()).padStart(2,'0');return`${y}-${m}-${d2}`},now=new Date,today=new Date(now.getFullYear(),now.getMonth(),now.getDate()),max=new Date(today.getFullYear()+1,11,31);let selectedISO=null;const fp=flatpickr(calHost,{inline:true,appendTo:calHost,monthSelectorType:'dropdown',minDate:today,maxDate:max,showMonths:1,disableMobile:true,dateFormat:'Y-m-d',defaultDate:null,onReady:(_d,_s,inst)=>{const h=calHost.querySelector('.flatpickr-current-month');if(h&&!h.querySelector('.vf-year-select')){const ys=d.createElement('select');ys.className='vf-year-select';for(let y=today.getFullYear();y<=max.getFullYear();y++){const o=d.createElement('option');o.value=o.textContent=String(y);ys.appendChild(o)}ys.value=String(inst.currentYear||today.getFullYear());ys.addEventListener('change',()=>inst.changeYear(+ys.value));h.appendChild(ys)}},onYearChange:(_d,_s,inst)=>{const ys=calHost.querySelector('.vf-year-select');if(ys)ys.value=String(inst.currentYear)},onMonthChange:(_d,_s,inst)=>{const ys=calHost.querySelector('.vf-year-select');if(ys)ys.value=String(inst.currentYear)},onChange:(_d,s)=>{selectedISO=s;if(s){save.disabled=false;err.style.display='none';picked.style.display='block';pickedVal.textContent=fmtNice(s)}}});shell.querySelectorAll('.vf-chip').forEach(b=>b.addEventListener('click',()=>{const q=b.getAttribute('data-q');let dt=new Date(today);if(q==='tomorrow')dt.setDate(dt.getDate()+1);else if(q==='in2')dt.setDate(dt.getDate()+2);else if(q==='weekend'){const day=dt.getDay(),delta=(6-day+7)%7;dt.setDate(dt.getDate()+delta)}else if(q==='nextweek'){const day=dt.getDay(),delta=(1-day+7)%7||7;dt.setDate(dt.getDate()+delta)}selectedISO=toISODateLocal(dt);try{fp?.setDate(dt,true)}catch(e){}save.disabled=false;err.style.display='none';picked.style.display='block';pickedVal.textContent=fmtNice(selectedISO)}));save.onclick=()=>{if(!selectedISO){err.style.display='block';return}err.style.display='none';SF.celebrate(save);['label','.vf-desc','.vf-cal-wrap'].forEach(s=>{const n=shell.querySelector(s);if(n)n.style.display='none'});picked.style.display='block';setVar('date',selectedISO);MZ.blurActive();CH.show();MZ.disable();V.interact({type:'captured',payload:{date:selectedISO,urgent:false}})};return()=>{CH.show();MZ.disable()}}};
/* ---- Location Picker ---- */
const LP={name:'ext_location_picker',type:'response',match:({trace})=>trace?.type==='ext_location_picker'||trace?.payload?.name==='ext_location_picker',render:({element})=>{
CH.hide();MZ.enable();
const shell=makeShell({label:'Where do you need the work done?',description:'Please enter your location below.',submitText:'Confirm Location',errorText:'Please select a location before continuing.',htmlField:'
Type at least 3 characters to see suggestions
Selected Location:
Getting your location...
'});
element.appendChild(shell);stretch(element,shell);AH.hideFor(shell);
const currentBtn=shell.querySelector('[data-method="current"]'),searchBtn=shell.querySelector('[data-method="search"]'),input=shell.querySelector('.vf-location-input'),dropdownContainer=shell.querySelector('.vf-dropdown-container'),inputHelper=shell.querySelector('.vf-input-helper'),selectedDiv=shell.querySelector('.vf-selected-location'),selectedAddress=shell.querySelector('.vf-selected-address'),loading=shell.querySelector('.vf-location-loading'),error=shell.querySelector('.vf-error'),save=shell.querySelector('.vf-submit'),locationButtons=shell.querySelector('.vf-location-buttons'),autocompleteWrapper=shell.querySelector('.vf-autocomplete-wrapper'),label=shell.querySelector('label'),description=shell.querySelector('.vf-desc');
const fallbackLink=d.createElement('div');fallbackLink.className='vf-location-fallback';fallbackLink.innerHTML='Issues providing your location? Click here...';save.insertAdjacentElement('afterend',fallbackLink);const fallbackAnchor=fallbackLink.querySelector('a');
let selectedLocation=null,geocoder=null,autocompleteService=null,placesService=null,sessionToken=null,currentPredictions=[];
save.disabled=true;save.style.background='#9ca3af';save.style.cursor='not-allowed';
function enableSubmit(){save.disabled=false;save.style.background='#0551fb';save.style.cursor='pointer'}
function showLoading(text){loading.querySelector('span').textContent=text||'Getting your location...';loading.style.display='flex'}
function hideLoading(){loading.style.display='none'}
function showError(message){error.textContent=message;error.style.display='block';hideLoading()}
function hideError(){error.style.display='none'}
function showSelected(location){selectedLocation=location;selectedAddress.textContent=location.formattedAddress;selectedDiv.classList.add('show');inputHelper.classList.remove('show');dropdownContainer.classList.remove('show');enableSubmit();hideError();hideLoading();if(window.innerWidth<=768){input.style.display='none';autocompleteWrapper.style.marginBottom='0'}requestAnimationFrame(()=>{save.scrollIntoView({behavior:'smooth',block:'center'})})}
function extractSuburb(addressComponents){const suburbTypes=['sublocality_level_1','locality','administrative_area_level_3'];for(const type of suburbTypes){const component=addressComponents.find(comp=>comp.types.includes(type));if(component)return component.long_name}return''}
function renderPredictions(predictions){dropdownContainer.innerHTML='';if(!predictions||predictions.length===0){dropdownContainer.classList.remove('show');return}const limitedPredictions=predictions.slice(0,2);currentPredictions=limitedPredictions;limitedPredictions.forEach((prediction,index)=>{const item=d.createElement('div');item.className='vf-prediction-item';item.style.cssText='padding:16px;cursor:pointer;border-bottom:1px solid #e5e7eb;display:flex;align-items:center;gap:12px;-webkit-tap-highlight-color:transparent;transition:background 0.15s ease';const icon=d.createElement('div');icon.style.cssText='width:20px;height:20px;flex-shrink:0;color:#6b7280';icon.innerHTML='π';item.appendChild(icon);const textContainer=d.createElement('div');textContainer.style.cssText='flex:1;min-width:0';const mainText=d.createElement('div');mainText.style.cssText='font-weight:600;color:#111827;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis';mainText.textContent=prediction.structured_formatting.main_text;const secondaryText=d.createElement('div');secondaryText.style.cssText='color:#6b7280;font-size:13px;margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis';secondaryText.textContent=prediction.structured_formatting.secondary_text;textContainer.appendChild(mainText);textContainer.appendChild(secondaryText);item.appendChild(textContainer);if(index===limitedPredictions.length-1)item.style.borderBottom='none';item.addEventListener('mouseenter',()=>{item.style.background='#eff6ff'});item.addEventListener('mouseleave',()=>{item.style.background='white'});item.addEventListener('click',()=>{selectPrediction(prediction)});item.addEventListener('touchstart',()=>{item.style.background='#dbeafe'},{passive:true});item.addEventListener('touchend',()=>{item.style.background='#eff6ff'},{passive:true});dropdownContainer.appendChild(item)});dropdownContainer.classList.add('show')}
function selectPrediction(prediction){if(!placesService)return;showLoading('Getting location details...');dropdownContainer.classList.remove('show');placesService.getDetails({placeId:prediction.place_id,fields:['address_components','formatted_address','geometry','name'],sessionToken:sessionToken},(place,status)=>{hideLoading();if(status===google.maps.places.PlacesServiceStatus.OK&&place){const location={formattedAddress:place.formatted_address,suburb:extractSuburb(place.address_components||[]),lat:place.geometry.location.lat(),lng:place.geometry.location.lng()};input.value=place.formatted_address;showSelected(location);sessionToken=new google.maps.places.AutocompleteSessionToken()}else{showError('Could not get location details. Please try another address.')}})}
function loadGoogleMaps(){return new Promise((resolve,reject)=>{if(window.google&&window.google.maps){resolve();return}const script=d.createElement('script');script.src='https://maps.googleapis.com/maps/api/js?key=AIzaSyAsmsHobVLVfqzS_0JvE7NIPpWZiTLfOic&libraries=places';script.onload=()=>resolve();script.onerror=()=>reject(new Error('Failed to load Google Maps'));d.head.appendChild(script)})}
async function initializeGoogleMaps(){try{await loadGoogleMaps();geocoder=new google.maps.Geocoder();autocompleteService=new google.maps.places.AutocompleteService();const placesDiv=d.createElement('div');placesDiv.style.display='none';d.body.appendChild(placesDiv);placesService=new google.maps.places.PlacesService(placesDiv);sessionToken=new google.maps.places.AutocompleteSessionToken();setupAutocomplete()}catch(err){showError('Failed to load location services. Please refresh and try again.')}}
function setupAutocomplete(){let searchTimeout;input.addEventListener('input',()=>{const value=input.value.trim();clearTimeout(searchTimeout);if(value.length>0&&value.length<3){inputHelper.textContent=`Type ${3-value.length} more character${3-value.length===1?'':'s'}`;inputHelper.classList.add('show');dropdownContainer.classList.remove('show');return}else if(value.length===0){inputHelper.classList.remove('show');dropdownContainer.classList.remove('show');return}inputHelper.textContent='Select an address from the suggestions below';inputHelper.classList.add('show');searchTimeout=setTimeout(()=>{if(value.length>=3)performSearch(value)},300)});input.addEventListener('keydown',(e)=>{if(e.key===' '||e.keyCode===32)e.stopPropagation()});d.addEventListener('click',(e)=>{if(!autocompleteWrapper.contains(e.target))dropdownContainer.classList.remove('show')})}
function performSearch(query){if(!autocompleteService)return;autocompleteService.getPlacePredictions({input:query,componentRestrictions:{country:'za'},types:['address'],sessionToken:sessionToken},(predictions,status)=>{if(status===google.maps.places.PlacesServiceStatus.OK&&predictions){renderPredictions(predictions)}else if(status===google.maps.places.PlacesServiceStatus.ZERO_RESULTS){dropdownContainer.innerHTML='
No addresses found. Try a different search.
';dropdownContainer.classList.add('show')}else{dropdownContainer.classList.remove('show')}})}
async function getCurrentLocation(){if(!navigator.geolocation){showError('Geolocation is not supported by your browser.');return}if(location.protocol!=='https:'&&location.hostname!=='localhost'){showError('Location access requires a secure (HTTPS) connection.');return}if(!geocoder){showError('Location services are still loading. Please try again in a moment.');return}showLoading('Getting your current location...');hideError();try{const position=await new Promise((resolve,reject)=>{navigator.geolocation.getCurrentPosition(resolve,reject,{enableHighAccuracy:true,timeout:15000,maximumAge:300000})});const lat=position.coords.latitude,lng=position.coords.longitude;showLoading('Finding your address...');const response=await new Promise((resolve,reject)=>{geocoder.geocode({location:{lat,lng}},(results,status)=>{if(status==='OK'&&results&&results.length>0)resolve(results[0]);else reject(new Error(`Geocoding failed: ${status}`))})});const location={formattedAddress:response.formatted_address,suburb:extractSuburb(response.address_components||[]),lat:lat,lng:lng};showSelected(location)}catch(error){hideLoading();let errorMessage='Could not get your location: ';if(error.code){switch(error.code){case error.PERMISSION_DENIED:errorMessage+='Location access was denied. Please enable location access in your browser settings.';break;case error.POSITION_UNAVAILABLE:errorMessage+='Your location information is unavailable. Please try entering your address manually.';break;case error.TIMEOUT:errorMessage+='Location request timed out. Please try again or enter your address manually.';break;default:errorMessage+='An unknown error occurred. Please try entering your address manually.'}}else{errorMessage='Could not determine your address. Please enter it manually.'}showError(errorMessage)}}
currentBtn.addEventListener('click',()=>{currentBtn.classList.add('active');searchBtn.classList.remove('active');input.style.display='none';dropdownContainer.classList.remove('show');inputHelper.classList.remove('show');selectedDiv.classList.remove('show');getCurrentLocation()});
searchBtn.addEventListener('click',()=>{searchBtn.classList.add('active');currentBtn.classList.remove('active');input.style.display='block';input.value='';dropdownContainer.classList.remove('show');selectedDiv.classList.remove('show');selectedLocation=null;save.disabled=true;save.style.background='#9ca3af';hideLoading();hideError();setTimeout(()=>{input.focus()},100)});
fallbackAnchor.addEventListener('click',(e)=>{e.preventDefault();V.interact({type:'captured',payload:{locationFallback:true}});MZ.blurActive();CH.show();MZ.disable()});
save.onclick=()=>{if(!selectedLocation){showError('Please select a location before continuing.');return}hideError();SF.celebrate(save);setTimeout(()=>{if(label)label.style.display='none';if(description)description.style.display='none';if(locationButtons)locationButtons.style.display='none';if(autocompleteWrapper)autocompleteWrapper.style.display='none';if(loading)loading.style.display='none';if(error)error.style.display='none';if(save)save.style.display='none';if(fallbackLink)fallbackLink.style.display='none';if(inputHelper)inputHelper.style.display='none'},1000);setVar('formattedAddress',selectedLocation.formattedAddress);setVar('suburb',selectedLocation.suburb);setVar('googleLat',selectedLocation.lat.toString());setVar('googleLong',selectedLocation.lng.toString());MZ.blurActive();CH.show();MZ.disable();V.interact({type:'captured',payload:{formattedAddress:selectedLocation.formattedAddress,suburb:selectedLocation.suburb,latitude:selectedLocation.lat,longitude:selectedLocation.lng,locationConfirmed:true}})};
initializeGoogleMaps();
return()=>{CH.show();MZ.disable()}}};
/* ---- Booking Summary ---- */
const BS={name:'ext_booking_summary',type:'response',match:({trace})=>{const t=trace?.type,n=trace?.payload?.name;return t==='ext_booking_summary'||n==='ext_booking_summary'||t==='booking_summary'||n==='booking_summary'},render:({trace,element})=>{
CH.hide();MZ.enable();
const p=(()=>{try{return typeof trace?.payload==='string'?JSON.parse(trace.payload):(trace?.payload||{})}catch(_){return{}}})();
const jobDiagnosed=p.job_diagnosed??'{{job_diagnosed}}',formattedAddress=p.formattedAddress??'{{formattedAddress}}',dateRequest=p.date_request??'{{date_request}}',timeDisplayed=p.time_displayed??'{{time_displayed}}',userPhone=p.user_phone??'{{user_phone}}';
const shell=d.createElement('div');shell.className='vf-wide-card';
shell.innerHTML=`
By confirming your booking, you agree to Kandua's Terms of Service and consent to us sharing your job details with a trusted and vetted service provider. Booking subject to Pro availability
`;
element.appendChild(shell);stretch(element,shell);AH.hideFor(shell);
const confirmBtn=shell.querySelector('.vf-summary-confirm'),changeBtn=shell.querySelector('.vf-summary-change'),buttonsContainer=shell.querySelector('.vf-summary-buttons');
confirmBtn.onclick=()=>{setVar('confirmation','confirm');SF.celebrate(confirmBtn,changeBtn);setTimeout(()=>{buttonsContainer.style.display='none'},1000);MZ.blurActive();CH.show();MZ.disable();V.interact({type:'captured',payload:{confirmation:'confirm',action:'booking_confirmed'}})};
changeBtn.onclick=()=>{setVar('confirmation','update');setTimeout(()=>{buttonsContainer.style.display='none'},1000);MZ.blurActive();CH.show();MZ.disable();V.interact({type:'captured',payload:{confirmation:'update',action:'change_details'}})};
return()=>{CH.show();MZ.disable()}}};
/* ---- Confetti ---- */
const XE={name:'confetti',type:'response',match:({trace})=>trace?.type==='booking_complete',render:({element})=>{try{confetti({particleCount:100,spread:60,gravity:.6,ticks:300,zIndex:999999999})}catch(e){}setTimeout(()=>{try{confetti({particleCount:80,spread:100,gravity:.6,ticks:300,zIndex:999999999})}catch(e){}},200);setTimeout(()=>{removeIfEmptyWrapper(element)},10);return()=>{}}};
/* ---- Full Name ---- */
const fNm={name:'ext_fullName',type:'response',match:({trace})=>trace?.type==='ext_fullName'||trace?.payload?.name==='ext_fullName',render:({trace,element})=>{CH.hide();MZ.enable();const p=typeof trace.payload==='string'?JSON.parse(trace.payload):(trace.payload||{}),ph=p.placeholder||'John Smith',sh=makeShell({label:'Full Name',description:'Please enter your first and last name.',htmlField:``,errorText:"Please enter both your first and last name (letters, spaces, hyphens and apostrophes only)."});element.appendChild(sh);stretch(element,sh);AH.hideFor(sh);const inp=sh.querySelector('.vf-input'),err=sh.querySelector('.vf-error'),save=sh.querySelector('.vf-submit'),validate=v=>{const trimmed=v.trim();if(!trimmed)return{valid:false,error:'Please enter your full name.'};if(!/^[A-Za-zΓ-ΓΓ-ΓΆΓΈ-ΓΏ' -]+$/.test(trimmed))return{valid:false,error:'Name can only contain letters, spaces, hyphens and apostrophes.'};const words=trimmed.split(/\s+/).filter(w=>w.length>0);if(words.length<2)return{valid:false,error:'Please enter both your first and last name.'};if(trimmed.length>100)return{valid:false,error:'Name is too long. Please enter a shorter name.'};return{valid:true,words}},splitName=words=>{const firstName=words[0],lastName=words[words.length-1],fullName=words.join(' ');return{firstName,lastName,fullName}};save.onclick=()=>{const v=inp.value.trim(),validation=validate(v);if(!validation.valid){err.textContent=validation.error;err.style.display='block';return}err.style.display='none';const{firstName,lastName,fullName}=splitName(validation.words);SF.celebrate(save);setVar('firstName',firstName);setVar('lastName',lastName);setVar('fullName',fullName);MZ.blurActive();CH.show();MZ.disable();V.interact({type:'captured',payload:{firstName,lastName,fullName}})};return()=>{CH.show();MZ.disable()}}};
/* ---- Phone ---- */
const pN={name:'ext_phone',type:'response',match:({trace})=>trace?.type==='ext_phone'||trace?.payload?.name==='ext_phone',render:({element})=>{CH.hide();MZ.enable();const sh=makeShell({label:'Phone Number',description:"Please enter a valid, 10 digit South African phone number like 082 123 4567 - We'll add +27",htmlField:``,errorText:'Please enter a 10 digit number starting with 0.'});element.appendChild(sh);stretch(element,sh);AH.hideFor(sh);const tel=sh.querySelector('.vf-phone'),err=sh.querySelector('.vf-error'),save=sh.querySelector('.vf-submit');tel.addEventListener('keydown',e=>{if(e.key===' '||e.key==='Spacebar')e.preventDefault()});tel.addEventListener('input',()=>{tel.value=tel.value.replace(/\s+/g,'')});tel.addEventListener('paste',e=>{e.preventDefault();const t=(e.clipboardData||window.clipboardData).getData('text')||'';tel.value=t.replace(/\D/g,'')});function validate(raw){const d=(raw||'').replace(/\D/g,'');if(!/^0\d{9}$/.test(d)){const n=d.length;err.textContent=`Please enter a 10 digit number starting with 0 (you entered ${n} digit${n===1?'':'s'}).`;return null}return d}save.onclick=()=>{const dgt=validate(tel.value);if(!dgt){err.style.display='block';return}err.style.display='none';const e164='+27'+dgt.slice(1);SF.celebrate(save);setVar('userPhone',e164);MZ.blurActive();CH.show();MZ.disable();V.interact({type:'captured',payload:{userPhone:e164}})};return()=>{CH.show();MZ.disable()}}};
/* ---- Email ---- */
const eM={name:'ext_email',type:'response',match:({trace})=>trace?.type==='ext_email'||trace?.payload?.name==='ext_email',render:({trace,element})=>{CH.hide();MZ.enable();const p=typeof trace.payload==='string'?JSON.parse(trace.payload):(trace.payload||{}),ph=p.placeholder||'email@me.co.za',w=d.createElement('div');w.className='vf-anti-zoom vf-wide-card';w.innerHTML=`
Please enter a valid email address, or click Skip.
`;element.appendChild(w);stretch(element,w);AH.hideFor(w);const pctEl=w.querySelector('.vf-progress-pct'),fill=w.querySelector('.vf-progress-fill'),dots=[...w.querySelectorAll('.vf-step-dot')];dots.forEach((dot,i)=>{const idx=i+1;dot.classList.remove('complete','current');if(idx1-Math.pow(1-t,3);(function f(n){const t=Math.min(1,(n-st)/dur),v=Math.round(sv+dl*ez(t));fill.style.width=v+'%';pctEl.textContent=v+'%';if(t<1)requestAnimationFrame(f)})(st)}}}
const P1=makeProgressExt({name:'ext_progress_step1',step:1,title:'Problem diagnosis',subtitle:'We are understanding your issue.'}),
P2=makeProgressExt({name:'ext_progress_step2',step:2,title:'Location',subtitle:'Where do you need help?'}),
P3=makeProgressExt({name:'ext_progress_step3',step:3,title:'Personal details',subtitle:'We are almost there.'}),
P4=makeProgressExt({name:'ext_progress_step4',step:4,title:'Done',subtitle:'All set!'});
/* ---- Pros Found ---- */
const PF={name:'ext_pros_found',type:'response',match:({trace})=>trace?.type==='ext_pros_found'||trace?.payload?.name==='ext_pros_found',render:({trace,element})=>{const p=typeof trace?.payload==='string'?(()=>{try{return JSON.parse(trace.payload)}catch(e){return{}}})():(trace?.payload||{}),c=Number(p?.count)||3,a=p?.area||'';setVar('prosFound',true);setVar('prosFoundCount',c);const w=d.createElement('div');w.className='vf-wide-card';w.innerHTML=`
β β β β β β
Great news!
We have found top-rated, vetted Pros ${a?`near ${a}`:'near you'}.
βοΈ βοΈ βοΈ βοΈ βοΈ
All Kandua Pros have been pre-vetted and are rated 4.5β or higher. No need to worry about choosing, we will make sure the right Pro gets your job β .
Answer 9 quick questions about your geyser, and Jess will assess your risk level.
Why this matters
Geysers are South Africa's #1 home insurance claim. The average burst geyser costs R15,000βR50,000+ in damage β and most failures are preventable.
What you'll get
Personalised risk assessment
Maintenance recommendations
Book a vetted geyser pro
Takes about 2 minutes
`;
element.appendChild(container);AH.hideFor(container);CH.hide();
container.querySelector('.jgw-btn').addEventListener('click',()=>{
const card=container.querySelector('.jgw-card');if(card)card.classList.add('jgw-exit');
setTimeout(()=>{container.style.overflow='hidden';container.style.maxHeight=container.scrollHeight+'px';container.offsetHeight;container.style.transition='max-height 0.35s ease,opacity 0.35s ease';container.style.maxHeight='0';container.style.opacity='0'},350);
setTimeout(()=>{try{container.remove()}catch(e){}CH.show();V.interact({type:'start_diagnostic',payload:{}})},700)
});
return()=>{CH.hide()}}};
/* ---- Geyser Results ---- */
const GeyserResultsExtension={name:'GeyserResults',type:'response',match:({trace})=>trace?.type==='ext_geyserResults'||(trace?.payload?.name==='ext_geyserResults'),render:({trace,element})=>{
const p=trace?.payload||{},zone=p.zone||'caution',findings=p.findings||'';
const zones={danger:{accent:'linear-gradient(90deg,#E24B4A,#D85A30)',color:'#E24B4A',chipBg:'#FCEBEB',chipText:'#791F1F',icon:'',headline:'Your geyser needs urgent attention',subtext:'Based on your answers, there are a few things that really concern me. Here\'s what stood out:',nudge:'I\'d strongly suggest getting a plumber out this week β the longer you wait, the bigger the risk.',segments:4},caution:{accent:'linear-gradient(90deg,#EF9F27,#BA7517)',color:'#EF9F27',chipBg:'#FAEEDA',chipText:'#633806',icon:'',headline:'A few things worth keeping an eye on',subtext:'Nothing alarming right now, but there are some signs your geyser could use a check-up:',nudge:'I\'d recommend booking a geyser service in the next month or two β prevention is always cheaper than repair.',segments:3},safe:{accent:'linear-gradient(90deg,#97C459,#639922)',color:'#639922',chipBg:'#EAF3DE',chipText:'#27500A',icon:'',headline:'Looking good β your geyser\'s in great shape',subtext:'Based on your answers, there\'s nothing to worry about right now. Here\'s what\'s working in your favour:',nudge:'Pop back in about 12 months and we\'ll run another check. In the meantime, you\'re in a great spot.',segments:1}};
const z=zones[zone]||zones.caution,findingsList=findings.split(',').map(f=>f.trim()).filter(Boolean);
let segmentsHTML='';for(let i=0;i<5;i++)segmentsHTML+=``;
let chipsHTML='';findingsList.forEach((f,i)=>{chipsHTML+=`${f}`});
const container=d.createElement('div');container.className='vf-wide-card';
container.innerHTML=`