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-group-title{font-size:13px;font-weight:500;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.75rem} .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} .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} /* cards */ .office-card{background:#ffffff;border:1px solid #767879!important;border-radius:8px;overflow:hidden;display:flex;flex-direction:column;height:100%; } .office-img{width:100%;height:235px;object-fit:cover;background:#f8f9fa} .office-body{padding:1rem;flex:1;display:flex;flex-direction:column} .office-name{font-size:14px;font-weight:700;color:#212529;margin-bottom:.5rem;line-height:1.4} .office-detail{font-size:13px;color:#6c757d;margin-bottom:.3rem;display:flex;align-items:flex-start;gap:6px} .office-detail i{font-size:13px;margin-top:2px;flex-shrink:0} .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} .badge-row{margin-top:.5rem;margin-bottom:.75rem;} .card-actions{display:flex;gap:6px;margin-top:auto} .dirs-btn{display:block;text-align:center!important;border:0.5px solid #378ADD;border-radius:var(--border-radius-md);color:#005377;font-size:13px;padding:7px 0;text-decoration:none;transition:background .15s;flex:1} .dirs-btn:hover{background:#E6F1FB;color:#185FA5;text-decoration:none} .appt-btn{display:block; text-align:center;background:#378ADD;border:0.5px solid #378ADD;border-radius:var(--border-radius-md);color:#fff;font-size:13px;padding:7px 0;text-decoration:none;transition:background .15s;} .appt-btn:hover{background:#185FA5;border-color:#185FA5;color:#fff;text-decoration:none} .call-btn{display:flex;align-items:center;justify-content:center;background-color:#99c8db!important;border:0.5px solid #dee2e6;border-radius:var(--border-radius-md);color:#6c757d;font-size:13px;padding:7px 10px;text-decoration:none;transition:background .15s;white-space:nowrap} .call-btn:hover{background:#f8f9fa;color:#212529;text-decoration:none} .result-count{font-size:16px;flex-shrink:0} .no-results{text-align:center;padding:3rem 1rem;color:#6c757d;font-size:14px} .dist-badge{font-size:15px!important;padding:2px 8px;border-radius:20px;font-weight:700; display: inline-flex} /* 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} /* All Filters trigger button (under search bar) */ #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} #allFiltersBtn i.fa-sliders{font-size:14px} #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; } /* results (full width) */ #resultsCol{width:100%;display:flex;flex-direction:column} #cardGrid{margin:0 -6px} /* ── filter drawer (modal, slides from left) ── */ #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,340px); 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 25%;max-width:25%} } @media(max-width:991px){ #filterDrawerHeader{ padding-top: 120px;} #filterDrawerFooter{ padding-bottom: 120px;} } .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 medical office near you. Filter or search by location name or service type. ✕ All Filters Clear all filters Filter & Sort Sort by distance Practice Services Show results Clear all filters let locations = []; function parseOfficesArray(){ const dataEl = document.getElementById('offices-array'); if(!dataEl) return null; try{ return JSON.parse(dataEl.textContent); }catch(e){ console.error('Failed to parse #offices-array JSON:', e); return []; } } function waitForOfficesArray(callback, attemptsLeft=100, intervalMs=50){ const parsed = parseOfficesArray(); if(parsed !== null){ locations = parsed; callback(); return; } if(attemptsLeft<=0){ console.error('#offices-array script tag never appeared on the page.'); locations = []; callback(); return; } setTimeout(()=>waitForOfficesArray(callback, attemptsLeft-1, intervalMs), intervalMs); } /* ── hidden "area" filter: not exposed in the filter UI, only reachable via ?area= URL param. Still fully clearable via clearAll(). Maps area codes to exact location names. ── */ const areaMap = { butler: ["Primary Care Associates"], cranberry: ["Schogel, Fardo, Atrasz and Associates"], gibsonia: ["Heyl Family Practice - Gibsonia"], mccandless: ["Heyl Family Practice - McCandless", "Northern Area Family Medicine"], newcastle: ["Family Practice Associates"], ross: ["Dayalan and Associates Family Medicine", "Grob, Scheri, Woodburn and Griffin - Perrysville", "Koman, Kimmell and Gabriel Family Medicine - Pittsburgh", "Genesis Blood Draws & Lab"], sevenfields: ["Koman, Kimmell and Gabriel Family Medicine - Seven Fields"], westview: ["Heyl Family Practice - West View"], wexford: ["Genesis Women's Health and Gynecology", "Grob, Scheri, Woodburn and Griffin - Wexford"] }; let userLat=null,userLng=null,sortByDist=false; let activeDivs=new Set(),activeSvcs=new Set(),searchQuery=''; let activeArea=null; let suppressURLSync=false; // prevents re-writing the URL while we're still applying it on load 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)return text; return text.replace(new RegExp('('+escapeRegex(query)+')','gi'),'$1'); } /* ────────── URL <-> state sync ────────── */ // Reads current filter/search/sort state from the URL query string. // Params used: // q = free-text search // div = comma-separated division ids (e.g. div=heyl,kkgm) // svc = comma-separated service names, URL-encoded (e.g. svc=Bloodwork,Women%27s%20Health) // sort = "dist" to enable sort-by-distance // area = a single hidden area code (e.g. area=wexford) — not shown in the filter UI, // restricts results to a pre-defined set of locations by name. function applyURLParams(){ const params = new URLSearchParams(window.location.search); const q = params.get('q'); if(q){ searchQuery = q; } const divParam = params.get('div'); if(divParam){ divParam.split(',').map(s=>s.trim()).filter(Boolean).forEach(id=>activeDivs.add(id)); } const svcParam = params.get('svc'); if(svcParam){ svcParam.split(',').map(s=>decodeURIComponent(s.trim())).filter(Boolean).forEach(s=>activeSvcs.add(s)); } if(params.get('sort') === 'dist'){ sortByDist = true; // actual geolocation prompt/fetch happens in initFromURL() after render } const areaParam = params.get('area'); if(areaParam && areaMap[areaParam]){ activeArea = areaParam; } } // Writes current state back to the URL without reloading the page. function updateURL(){ if(suppressURLSync) return; const params = new URLSearchParams(); if(searchQuery) params.set('q', searchQuery); if(activeDivs.size) params.set('div', [...activeDivs].join(',')); if(activeSvcs.size) params.set('svc', [...activeSvcs].map(s=>encodeURIComponent(s)).join(',')); if(sortByDist) params.set('sort', 'dist'); if(activeArea) params.set('area', activeArea); 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); } function buildFilters(){ const divMap={},svcSet=new Set(); locations.forEach(l=>{divMap[l.divId]=l.divLabel;l.services.forEach(s=>svcSet.add(s))}); const divEntries=Object.entries(divMap).sort((a,b)=>a[1].localeCompare(b[1])); const svcEntries=[...svcSet].sort(); const df=document.getElementById('divFilters'); divEntries.forEach(([id,label])=>{ const checked = activeDivs.has(id) ? 'checked' : ''; df.innerHTML+=` ${label}`; }); const sf=document.getElementById('svcFilters'); svcEntries.forEach(s=>{ const checked = activeSvcs.has(s) ? 'checked' : ''; sf.innerHTML+=` ${s}`; }); } function toggleDiv(cb){cb.checked?activeDivs.add(cb.value):activeDivs.delete(cb.value);updateActiveCount();updateClearAllVisibility();render();updateURL();} function toggleSvc(cb){cb.checked?activeSvcs.add(cb.value):activeSvcs.delete(cb.value);updateActiveCount();updateClearAllVisibility();render();updateURL();} function updateActiveCount(){ const n=activeDivs.size+activeSvcs.size+(sortByDist?1:0); const el=document.getElementById('allFiltersActiveCount'); el.textContent=n;el.style.display=n>0?'inline-flex':'none'; } function updateClearAllVisibility(){ const hasActive = activeDivs.size>0 || activeSvcs.size>0 || sortByDist || searchQuery.length>0 || !!activeArea; 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; // couldn't actually sort, so don't persist a broken state updateURL(); }); } function clearAll(){ activeDivs.clear();activeSvcs.clear();sortByDist=false;searchQuery='';activeArea=null; document.querySelectorAll('#divFilters input,#svcFilters 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 render(){ const q=searchQuery.toLowerCase(); const areaNames = activeArea ? areaMap[activeArea] : null; let list=locations.filter(l=>{ const divOk=activeDivs.size===0||activeDivs.has(l.divId); const svcOk=activeSvcs.size===0||[...activeSvcs].every(s=>l.services.includes(s)); const searchOk=!q||l.name.toLowerCase().includes(q)||l.services.some(s=>s.toLowerCase().includes(q)); const areaOk=!areaNames||areaNames.includes(l.name); return divOk&&svcOk;&&searchOk;&&areaOk; }); if(sortByDist&&userLat;!==null){ list=list.map(l=>({...l,dist:haversine(userLat,userLng,l.coordLat,l.coordLong)})); list.sort((a,b)=>a.dist-b.dist); } const totalCount = areaNames ? areaNames.length : locations.length; document.getElementById('resultCount').textContent=list.length===totalCount?`Showing all ${list.length} locations`:`${list.length} location${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 locations match your search.`;return;} grid.innerHTML=list.map(l=>{ const distBadge=l.dist!==undefined?`${l.dist.toFixed(1)} miles`:''; const hlName=highlight(l.name,searchQuery); const badges=l.services.map(s=>`${highlight(s,searchQuery)}`).join(''); const apptBtn=l.locNum?`Appointments`: 'CALL TO SCHEDULE APPOINTMENT'; return ` ${hlName} ${distBadge} ${l.phone} ${l.address} ${badges} ${apptBtn} `; }).join(''); } // Initial boot sequence: read URL -> build UI (checkboxes reflect URL) -> reflect search box -> // render -> if URL asked for distance sort, request geolocation -> only then start writing URL updates. 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 was tentatively set true by applyURLParams(); actually acquire location. sortByDist = false; // reset, toggleSort() will re-enable it once geolocation succeeds toggleSort(); } suppressURLSync = false; updateURL(); // normalize the URL to exactly match current state } // Wait for #offices-array to be present/parseable before booting the widget. waitForOfficesArray(initFromURL);