body {background-color: #F9FAFB!important;} a {color: #007195!important;} banner{display:none!important} .row.justify-content-center.m-0.p-0{display:none!important} *{box-sizing:border-box} body{background:transparent;font-family:var(--font-sans,sans-serif);color:#212529} /* shared */ .filter-label{display:flex;align-items:center;gap:8px;font-size:14px;color:#212529;cursor:pointer;padding:4px 0;user-select:none} .filter-label input[type=checkbox]{width:15px;height:15px;cursor:pointer;accent-color:#378ADD;flex-shrink:0} .divider{border:none;border-top:0.5px solid #dee2e6;margin:1rem 0} .sort-btn{width:100%;text-align:left;background:transparent;border:0.5px solid #dee2e6;padding:8px 12px;font-size:13px;color:#6c757d;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;transition:background .15s} .sort-btn:hover{background:#f8f9fa} .sort-btn.active{border-color:#0dcaf0;color:#fff;background:#017195} /* collapsible filter sections */ .filter-section{margin-bottom:.25rem} .filter-section-header{width:100%;display:flex;align-items:center;justify-content:space-between;background:transparent;border:none;padding:10px 0;font-size:13px;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:#212529;cursor:pointer} .filter-section-header:hover{color:#007195} .filter-section-header .chev{transition:transform .15s;font-size:12px;color:#6c757d} .filter-section-header[aria-expanded="false"] .chev{transform:rotate(-90deg)} .filter-section-count{display:inline-flex;align-items:center;justify-content:center;background:#378ADD;color:#fff;border-radius:20px;font-size:11px;font-weight:600;min-width:16px;height:16px;padding:0 5px;margin-left:6px} .filter-section-body{overflow:hidden;padding-bottom:.5rem} .filter-section-body.collapsed{display:none} /* cards */ .provider-card{background:#ffffff;border:1px solid #767879!important;border-radius:8px;overflow:hidden;display:flex;flex-direction:column;height:100%} .provider-img-wrap{position:relative;width:100%;height:200px;flex-shrink:0} .provider-img{width:100%;height:100%;object-fit:cover;display:block; border-radius: 0px!important; object-position: top;} .provider-img-placeholder{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#eef1f3;color:#b0b7bd;font-size:48px} .provider-body{padding:1rem;display:flex;flex:1} .provider-name{font-size:15px;font-weight:700;margin-bottom:.15rem;line-height:1.4} .provider-name a{color:#212529!important;text-decoration:none} .provider-name a:hover{color:#007195!important;text-decoration:underline} .provider-creds{color:#6c757d;} .provider-div-badge{display:inline-block;font-size:14px;font-weight:600;color:#212529;margin-bottom:.6rem; } .service-badge{display:inline-block;font-size:12px;padding:2px 8px;border-radius:20px;background:#f8f9fa;color:#6c757d;border:0.5px solid #dee2e6;margin:2px 2px 2px 0} .specialty-badge{display:inline-block;font-size:12px;padding:2px 8px;border-radius:20px;background:#EAF7EF;color:#1B6B3A;border:0.5px solid #cdeedb;margin:2px 2px 2px 0} .badge-group-label{ font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; color: #005377; margin: .5rem 0 .2rem; margin-top: 0rem !important; font-weight: 600;} .badge-row{margin-bottom:.25rem} .location-row{font-size:14px;color:#6c757d;margin-bottom:.5rem; max-width: 300px;} .location-row .loc-name{font-weight:600;color:#212529;} .location-row a{display:inline-flex;align-items:baseline;gap:5px;font-size:14px!important} .dist-badge{font-size:13px!important;padding:2px 8px;border-radius:20px;font-weight:700;display:inline-flex;background:#9ac9db;color:#005377!important;margin-bottom:.4rem} .result-count{font-size:16px;flex-shrink:0} .no-results{text-align:center;padding:3rem 1rem;color:#6c757d;font-size:14px} /* search */ .search-wrap{position:relative;margin-bottom:.75rem;flex-shrink:0} .search-wrap .search-icon{position:absolute;left:10px;top:50%;transform:translateY(-50%);font-size:14px;color:#6c757d;pointer-events:none} #searchInput{width:100%;padding:8px 32px 8px 32px;font-size:14px;border:0.5px solid #767879;border-radius:10px;background:#ffffff;color:#212529;outline:none;transition:border-color .15s} #searchInput:focus{border-color:#378ADD} #searchInput::placeholder{color:#6c757d} #clearSearch{position:absolute;right:8px;top:50%;transform:translateY(-50%);background:none;border:none;cursor:pointer;font-size:14px;color:#6c757d;padding:2px 4px;display:none;line-height:1} #clearSearch:hover{color:#212529} mark{background:#B5D4F4;color:#0C447C;border-radius:2px;padding:0 1px} #allFiltersBtn{ display:flex;align-items:center;justify-content:center;gap:8px; width:100%;background:#007195;border:0.5px solid #007195;border-radius:var(--border-radius-md); padding:10px 14px;font-size:14px;font-weight:600;color:#ffffff;cursor:pointer; margin-bottom:1rem;transition:background .15s;flex-shrink:0; } #allFiltersBtn:hover{background:#0084ae} #allFiltersActiveCount{display:inline-flex;align-items:center;justify-content:center;background:#378ADD;color:#fff;border-radius:20px;font-size:11px;font-weight:600;min-width:18px;height:18px;padding:0 5px} #allFiltersBtn[aria-expanded="true"]{background:#ffffff;color:#005478} #resultsCol{width:100%;display:flex;flex-direction:column} #cardGrid{margin:0 -6px} /* filter drawer */ #filterBackdrop{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:1050;opacity:0;pointer-events:none;transition:opacity .25s ease} #filterBackdrop.open{opacity:1;pointer-events:auto} #filterDrawer{ position:fixed;top:0;left:0;height:100vh;width:min(85vw,360px); background:#ffffff;z-index:1060;box-shadow:2px 0 16px rgba(0,0,0,.2); transform:translateX(-100%);transition:transform .28s ease; display:flex;flex-direction:column; } #filterDrawer.open{transform:translateX(0)} #filterDrawerHeader{display:flex;align-items:center;justify-content:space-between;padding:1rem 1.25rem;border-bottom:0.5px solid #dee2e6;flex-shrink:0;padding-top:185px} #filterDrawerHeader h3{font-size:16px;font-weight:700;color:#212529;margin:0} #closeDrawerBtn{background:none;border:none;font-size:18px;color:#6c757d;cursor:pointer;padding:4px 6px;line-height:1} #closeDrawerBtn:hover{color:#212529} #filterDrawerBody{flex:1;overflow-y:auto;padding:1.25rem;scrollbar-width:thin;scrollbar-color:#dee2e6 transparent} #filterDrawerBody::-webkit-scrollbar{width:4px} #filterDrawerBody::-webkit-scrollbar-track{background:transparent} #filterDrawerBody::-webkit-scrollbar-thumb{background:#dee2e6;border-radius:4px} #filterDrawerFooter{flex-shrink:0;padding:1rem 1.25rem;border-top:0.5px solid #dee2e6} #showResultsBtn{width:100%;background:#378ADD;border:0.5px solid #378ADD;border-radius:var(--border-radius-md);color:#fff;font-size:14px;font-weight:600;padding:10px 0;cursor:pointer;transition:background .15s} #showResultsBtn:hover{background:#185FA5;border-color:#185FA5} @media(min-width:992px){ #cardGrid .card-col{flex:0 0 100%;max-width:100%} .provider-img-wrap{width:200px;height:200px;flex-shrink:0} .provider-body{padding:1.25rem; padding-top:2.25rem; } } @media(max-width:991px){ #filterDrawerHeader{padding-top:120px} #filterDrawerFooter{padding-bottom:120px} .provider-img-wrap{height:300px} .provider-img.rounded-circle {object-position: 0px -15px; border-radius: 0px!important; border: none!important;} } .lds-spinner, .lds-spinner div, .lds-spinner div:after { box-sizing: border-box; } .lds-spinner { color: currentColor; display: inline-block; position: relative; width: 80px; height: 80px; } .lds-spinner div { transform-origin: 40px 40px; animation: lds-spinner 1.2s linear infinite; } .lds-spinner div:after { content: " "; display: block; position: absolute; top: 3.2px; left: 36.8px; width: 6.4px; height: 17.6px; border-radius: 20%; background: currentColor; } .lds-spinner div:nth-child(1) { transform: rotate(0deg); animation-delay: -1.1s; } .lds-spinner div:nth-child(2) { transform: rotate(30deg); animation-delay: -1s; } .lds-spinner div:nth-child(3) { transform: rotate(60deg); animation-delay: -0.9s; } .lds-spinner div:nth-child(4) { transform: rotate(90deg); animation-delay: -0.8s; } .lds-spinner div:nth-child(5) { transform: rotate(120deg); animation-delay: -0.7s; } .lds-spinner div:nth-child(6) { transform: rotate(150deg); animation-delay: -0.6s; } .lds-spinner div:nth-child(7) { transform: rotate(180deg); animation-delay: -0.5s; } .lds-spinner div:nth-child(8) { transform: rotate(210deg); animation-delay: -0.4s; } .lds-spinner div:nth-child(9) { transform: rotate(240deg); animation-delay: -0.3s; } .lds-spinner div:nth-child(10) { transform: rotate(270deg); animation-delay: -0.2s; } .lds-spinner div:nth-child(11) { transform: rotate(300deg); animation-delay: -0.1s; } .lds-spinner div:nth-child(12) { transform: rotate(330deg); animation-delay: 0s; } @keyframes lds-spinner { 0% { opacity: 1; } 100% { opacity: 0; } } Find a provider. Filter by credentials, division, office, services, or specialties. ✕ All Filters Clear all filters Filter & Sort Sort by distance Division Office Specialties Services Credentials Show results Clear all filters /* ────────── data loading ────────── Two JSON script tags (type="application/json") are injected elsewhere on the page: one with id "providers-array", one with id "offices-array". Both may land in the DOM after this script runs, so we poll for both before booting the widget. */ let providers = []; let offices = []; let officeIndex = {}; // locId -> office (with numeric coords) let divLabels = {}; // divId -> divLabel function parseJSONScript(id){ const el = document.getElementById(id); if(!el) return null; try{ return JSON.parse(el.textContent); } catch(e){ console.error(`Failed to parse #${id} JSON:`, e); return []; } } function waitForData(ids, callback, attemptsLeft=100, intervalMs=50){ const results = {}; let allFound = true; for(const id of ids){ const parsed = parseJSONScript(id); if(parsed === null){ allFound = false; break; } results[id] = parsed; } if(allFound){ callback(results); return; } if(attemptsLeft <= 0){ console.error('Required data script tag(s) never appeared on the page:', ids.filter(id=>!(id in results))); ids.forEach(id=>{ if(!(id in results)) results[id] = []; }); callback(results); return; } setTimeout(()=>waitForData(ids, callback, attemptsLeft-1, intervalMs), intervalMs); } function buildOfficeIndex(offs){ const idx = {}; offs.forEach(o=>{ idx[o.locId] = { ...o, coordLat: parseFloat(o.coordLat), coordLong: parseFloat(o.coordLong) }; if(o.divId && o.divLabel) divLabels[o.divId] = o.divLabel; }); return idx; } function resolveOffices(provider){ return (provider.locIds || []).map(id=>officeIndex[id]).filter(Boolean); } /* ────────── state ────────── */ let userLat=null,userLng=null,sortByDist=false; let divSet=new Set(), officeSet=new Set(), specSet=new Set(), svcSet=new Set(), credSet=new Set(); let searchQuery=''; let suppressURLSync=false; function haversine(lat1,lon1,lat2,lon2){ const R=3958.8,dLat=(lat2-lat1)*Math.PI/180,dLon=(lon2-lon1)*Math.PI/180; const a=Math.sin(dLat/2)**2+Math.cos(lat1*Math.PI/180)*Math.cos(lat2*Math.PI/180)*Math.sin(dLon/2)**2; return R*2*Math.atan2(Math.sqrt(a),Math.sqrt(1-a)); } function escapeRegex(s){return s.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')} function highlight(text,query){ if(!query || !text) return text; return text.replace(new RegExp('('+escapeRegex(query)+')','gi'),'$1'); } function nearestDistance(provOffices){ if(userLat===null || provOffices.length===0) return null; let min=Infinity; provOffices.forEach(o=>{ if(Number.isFinite(o.coordLat) && Number.isFinite(o.coordLong)){ const d=haversine(userLat,userLng,o.coordLat,o.coordLong); if(d state sync ────────── Params: q, div, office, spec, svc, cred, sort=dist */ function applyURLParams(){ const params = new URLSearchParams(window.location.search); const q = params.get('q'); if(q) searchQuery = q; const map = { div: divSet, office: officeSet, spec: specSet, svc: svcSet, cred: credSet }; Object.entries(map).forEach(([key,set])=>{ const v = params.get(key); if(v) v.split(',').map(s=>decodeURIComponent(s.trim())).filter(Boolean).forEach(x=>set.add(x)); }); if(params.get('sort') === 'dist') sortByDist = true; } function updateURL(){ if(suppressURLSync) return; const params = new URLSearchParams(); if(searchQuery) params.set('q', searchQuery); const map = { div: divSet, office: officeSet, spec: specSet, svc: svcSet, cred: credSet }; Object.entries(map).forEach(([key,set])=>{ if(set.size) params.set(key, [...set].map(s=>encodeURIComponent(s)).join(',')); }); if(sortByDist) params.set('sort','dist'); const qs = params.toString(); const newURL = qs ? `${window.location.pathname}?${qs}${window.location.hash}` : `${window.location.pathname}${window.location.hash}`; window.history.replaceState(null, '', newURL); } /* ────────── filter option building ────────── */ function buildFilters(){ const divMap={}, officeMap={}, specSetAll=new Set(), svcSetAll=new Set(), credSetAll=new Set(); providers.forEach(p=>{ if(p.divId) divMap[p.divId] = divLabels[p.divId] || p.divId; (p.specialties||[]).forEach(s=>specSetAll.add(s)); (p.services||[]).forEach(s=>svcSetAll.add(s)); (p.creds||[]).forEach(c=>credSetAll.add(c)); }); Object.values(officeIndex).forEach(o=>{ officeMap[o.locId] = o.name; }); buildCheckboxGroup('divFilters', Object.entries(divMap).sort((a,b)=>a[1].localeCompare(b[1])), divSet, 'div', true); buildCheckboxGroup('officeFilters', Object.entries(officeMap).sort((a,b)=>a[1].localeCompare(b[1])), officeSet, 'office', true); buildCheckboxGroup('specFilters', [...specSetAll].sort().map(s=>[s,s]), specSet, 'spec', false); buildCheckboxGroup('svcFilters', [...svcSetAll].sort().map(s=>[s,s]), svcSet, 'svc', false); buildCheckboxGroup('credFilters', [...credSetAll].sort().map(s=>[s,s]), credSet, 'cred', false); } // entries: array of [value, label] function buildCheckboxGroup(containerId, entries, activeSet, type){ const c = document.getElementById(containerId); if(entries.length === 0){ c.innerHTML = `None available`; return; } c.innerHTML = entries.map(([value,label])=>{ const checked = activeSet.has(value) ? 'checked' : ''; return ` ${label}`; }).join(''); } const filterSets = { div: divSet, office: officeSet, spec: specSet, svc: svcSet, cred: credSet }; function toggleFilter(cb){ const set = filterSets[cb.dataset.type]; cb.checked ? set.add(cb.value) : set.delete(cb.value); updateActiveCount();updateClearAllVisibility();render();updateURL(); } function toggleSection(btn, bodyId){ const body = document.getElementById(bodyId); const expanded = btn.getAttribute('aria-expanded') === 'true'; btn.setAttribute('aria-expanded', expanded ? 'false' : 'true'); body.classList.toggle('collapsed', expanded); } function updateActiveCount(){ const counts = { div: divSet.size, office: officeSet.size, spec: specSet.size, svc: svcSet.size, cred: credSet.size }; Object.entries(counts).forEach(([key,n])=>{ const el = document.getElementById(key+'Count'); if(el){ el.textContent = n; el.style.display = n>0 ? 'inline-flex' : 'none'; } }); const total = divSet.size+officeSet.size+specSet.size+svcSet.size+credSet.size+(sortByDist?1:0); const el = document.getElementById('allFiltersActiveCount'); el.textContent = total; el.style.display = total>0 ? 'inline-flex' : 'none'; } function updateClearAllVisibility(){ const hasActive = divSet.size>0||officeSet.size>0||specSet.size>0||svcSet.size>0||credSet.size>0||sortByDist||searchQuery.length>0; const display = hasActive ? '' : 'none'; document.getElementById('clearAllBtn1').style.display = display; document.getElementById('clearAllBtn2').style.display = display; } /* drawer open/close */ function openDrawer(){ document.getElementById('filterDrawer').classList.add('open'); document.getElementById('filterBackdrop').classList.add('open'); document.getElementById('allFiltersBtn').setAttribute('aria-expanded','true'); document.body.style.overflow='hidden'; } function closeDrawer(){ document.getElementById('filterDrawer').classList.remove('open'); document.getElementById('filterBackdrop').classList.remove('open'); document.getElementById('allFiltersBtn').setAttribute('aria-expanded','false'); document.body.style.overflow=''; } document.addEventListener('keydown', e=>{ if(e.key==='Escape' && document.getElementById('filterDrawer').classList.contains('open')) closeDrawer(); }); function onSearch(val){ searchQuery=val.trim(); document.getElementById('clearSearch').style.display = searchQuery ? 'block' : 'none'; updateClearAllVisibility();render();updateURL(); } function clearSearch(){ searchQuery='';document.getElementById('searchInput').value=''; document.getElementById('clearSearch').style.display='none'; updateClearAllVisibility();render();updateURL(); } function toggleSort(){ const btn=document.getElementById('sortBtn'); if(sortByDist){ sortByDist=false; btn.classList.remove('active');btn.querySelector('.sortBtnLabel').textContent='Sort by distance'; updateActiveCount();updateClearAllVisibility();render();updateURL();return; } if(userLat!==null){ sortByDist=true; btn.classList.add('active');btn.querySelector('.sortBtnLabel').textContent='Sorted by distance'; updateActiveCount();updateClearAllVisibility();render();updateURL();return; } const g=document.getElementById('geoStatus'); g.style.display='block';g.textContent='Detecting your location…'; navigator.geolocation.getCurrentPosition(pos=>{ userLat=pos.coords.latitude;userLng=pos.coords.longitude;sortByDist=true; btn.classList.add('active');btn.querySelector('.sortBtnLabel').textContent='Sorted by distance'; g.textContent='Location found.';setTimeout(()=>{g.style.display='none'},2000); updateActiveCount();updateClearAllVisibility();render();updateURL(); },()=>{ g.textContent='Location unavailable. Please allow location access.'; sortByDist=false; updateURL(); }); } function clearAll(){ divSet.clear();officeSet.clear();specSet.clear();svcSet.clear();credSet.clear(); sortByDist=false;searchQuery=''; document.querySelectorAll('#divFilters input,#officeFilters input,#specFilters input,#svcFilters input,#credFilters input').forEach(cb=>cb.checked=false); const btn=document.getElementById('sortBtn'); btn.classList.remove('active');btn.querySelector('.sortBtnLabel').textContent='Sort by distance'; document.getElementById('searchInput').value=''; document.getElementById('clearSearch').style.display='none'; updateActiveCount();updateClearAllVisibility();render();updateURL(); } function telify(phone){return (phone||'').replace(/[^0-9+]/g,'');} function providerMatches(p, q){ const credOk = credSet.size===0 || (p.creds||[]).some(c=>credSet.has(c)); const divOk = divSet.size===0 || divSet.has(p.divId); const officeOk = officeSet.size===0 || (p.locIds||[]).some(id=>officeSet.has(id)); const svcOk = svcSet.size===0 || [...svcSet].every(s=>(p.services||[]).includes(s)); const specOk = specSet.size===0 || [...specSet].every(s=>(p.specialties||[]).includes(s)); if(!(credOk&&divOk;&&officeOk;&&svcOk;&&specOk;)) return false; if(!q) return true; const hay = [ p.name, ...(p.creds||[]), ...(p.specialties||[]), ...(p.services||[]), divLabels[p.divId] || '' ].join(' ').toLowerCase(); return hay.includes(q); } function render(){ const q = searchQuery.toLowerCase(); let list = providers .map(p=>({ p, provOffices: resolveOffices(p) })) .filter(({p})=>providerMatches(p,q)); if(sortByDist && userLat!==null){ list = list.map(entry=>({ ...entry, dist: nearestDistance(entry.provOffices) })); list.sort((a,b)=>{ if(a.dist===null && b.dist===null) return 0; if(a.dist===null) return 1; if(b.dist===null) return -1; return a.dist-b.dist; }); } document.getElementById('resultCount').textContent = list.length===providers.length ? `Showing all ${list.length} providers` : `${list.length} provider${list.length!==1?'s':''} found`; document.getElementById('showResultsBtn').textContent = `Show ${list.length} result${list.length!==1?'s':''}`; const grid = document.getElementById('cardGrid'); if(list.length===0){ grid.innerHTML = `No providers match your search.`; return; } grid.innerHTML = list.map(({p,provOffices,dist})=>{ const hlName = highlight(p.name, searchQuery); const credsLine = (p.creds||[]).length ? (p.creds||[]).join(', ') : ''; const divBadge = divLabels[p.divId] ? `${divLabels[p.divId]}` : ''; const distBadge = (dist!==undefined && dist!==null) ? `${dist.toFixed(1)} miles` : ''; const specBadges = (p.specialties||[]).length ? `Specialties${(p.specialties||[]).map(s=>`${highlight(s,searchQuery)}`).join('')}` : ''; const svcBadges = (p.services||[]).length ? `Services${(p.services||[]).map(s=>`${highlight(s,searchQuery)}`).join('')}` : ''; const locationRows = provOffices.length ? `${provOffices.map(o=>` ${o.name} ${o.phone||''} ${o.address} `).join('')}` : ''; return ` ${p.provImg ? `` : ""} ${hlName}${credsLine ? `, ${credsLine}` : ""} ${divBadge} ${distBadge} ${p.services.includes('New Patient Appointments') ? ` Appointments` : ""} ${specBadges}${svcBadges} Office Locations ${locationRows} `; }).join(''); } /* ────────── boot ────────── */ function initFromURL(){ suppressURLSync = true; applyURLParams(); buildFilters(); const searchInput = document.getElementById('searchInput'); if(searchQuery){ searchInput.value = searchQuery; document.getElementById('clearSearch').style.display = 'block'; } updateActiveCount(); updateClearAllVisibility(); render(); if(sortByDist){ sortByDist = false; // toggleSort() re-enables once geolocation succeeds toggleSort(); } suppressURLSync = false; updateURL(); } waitForData(['providers-array','offices-array'], (data)=>{ providers = data['providers-array'] || []; offices = data['offices-array'] || []; officeIndex = buildOfficeIndex(offices); initFromURL(); });