<div id="top"></div>
<div id="directory-app">
<div class="directory-header">
<div class="directory-filter">
<label>Search for a Medical Provider</label>
<input
type="text"
id="searchInput"
placeholder="Or Search by Keyword..."
autocomplete="off"
/>
</div>
<select id="categorySelect">
<option selected disabled>Search By Specialty</option>
</select>
<button type="button" id="printBtn">Print this page</button>
<div id="lastUpdatedInline">Directory last updated: 12/23/2025</div>
</div>
<div class="directory-scroll">
<div id="directoryColumnsTop" class="directory-columns"></div>
<div id="directoryContent"></div>
</div>
<button id="backToTopFloating" aria-label="Back to top">↑</button>
</div>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:wght@600&family=Roboto:wght@500&display=swap"
rel="stylesheet"
/>
<style>
:root{--red:#a33038;--navy:#415465;--light:#bbc7cc;--heading:"Baskerville","Libre Baskerville","Times New Roman",serif;--para:"Roboto",system-ui,-apple-system,"Segoe UI",Arial,sans-serif;}
.content-wrapper{padding-top:0!important;}
#directory-app{font-size:10px;padding:0 5px;margin:0 15px;box-sizing:border-box;font-family:var(--para);letter-spacing: -.08px;font-weight:400; color:var(--navy);}
.directory-header{display:flex;align-items:center;gap:14px;flex-wrap:wrap;margin:10px 0;background:transparent;}
.directory-filter{display:contents;align-items:center;gap:10px;}
.directory-filter label,#lastUpdatedInline,.directory-columns span,.cat-sep span{font-family:var(--heading);font-weight:600;}
.directory-filter label{color:var(--navy);flex:0 0 100%;width:100%;order:1;margin:0 0 6px;font-size:1.7rem;}
#lastUpdatedInline{white-space:nowrap;margin-left:auto;text-align:right;color:var(--navy);font-size:16px;order:5;}
.directory-header select,.directory-header input{font:inherit;}
#searchInput,#printBtn,.directory-header select{box-sizing:border-box;height:30px;padding:0 10px;line-height:normal;vertical-align:middle;}
.directory-header select{background:#fff;color:var(--navy);border:1px solid var(--navy);}
.directory-header select option:checked{background-color:var(--navy);color:#fff;}
#categorySelect{order:2;}
#searchInput{width:220px;flex:0 0 220px;border:1px solid var(--navy);color:var(--navy);background:#fff;order:3;}
#searchInput:focus{outline:none;border-color:var(--navy);}
#searchInput::placeholder{color:var(--navy);opacity:1;}
#printBtn{display:none;cursor:pointer;background:var(--navy);color:#fff;border:1px solid var(--navy);order:4;}
#printBtn:hover{filter:brightness(.92);}
.directory-scroll{overflow-x:auto;padding:10px;}
.directory-columns,.directory-row{display:grid;column-gap:5px;min-width:max-content;}
.directory-columns{margin:6px 0;position:relative;background:var(--navy);color:#fff;padding:10px 15px;left:-20px;width:fit-content;}
.directory-row{margin:14px 0;}
.directory-row span,.directory-columns span{min-width:0;white-space:normal;overflow-wrap:anywhere;word-break:break-word;}
.directory-columns span{text-transform:uppercase;}
.hl{background:yellow;}
.cat-sep{display:block;box-sizing:border-box;padding:14px 20px;margin-top:26px;border-top:2px solid var(--light);border-bottom:2px solid var(--light);}
.cat-sep span{display:block;color:var(--red);text-transform:uppercase;letter-spacing:.04em;}
#floatingCart{display:none;}
#backToTopFloating{position:fixed;right:18px;bottom:18px;width:56px;height:56px;border-radius:999px;border:none;background:var(--red);color:#fff;cursor:pointer;font-size:30px;line-height:1;display:flex;align-items:center;justify-content:center;z-index:9999;box-shadow:0 10px 24px rgba(0,0,0,.22);font-weight:800;-webkit-text-stroke:2px #fff;text-shadow:0 0 0 #fff;}
#backToTopFloating:hover{filter:brightness(.95);}
#backToTopFloating:active{transform:translateY(1px);}
#categorySelect{width:260px;min-width:260px;flex:0 0 260px;}
@media (max-width:1024px){
#directory-app{padding:0 14px;margin:0;}
.directory-header{flex-direction:column;align-items:stretch;gap:10px;}
.directory-filter{flex-direction:column;align-items:stretch;gap:8px;}
.directory-filter label{display:block;white-space:normal;}
.directory-header select,.directory-header input,#searchInput,#printBtn{width:100%!important;max-width:none!important;flex:0 0 auto!important;box-sizing:border-box;}
#lastUpdatedInline{width:100%;text-align:right;}
.directory-scroll{overflow-x:hidden;padding:0;}
.directory-columns{display:none;}
.directory-columns,.directory-row{min-width:0!important;width:100%!important;}
.directory-row{display:block;grid-template-columns:none!important;margin:12px 0;padding:12px;border:1px solid rgba(0,0,0,.12);border-radius:12px;box-sizing:border-box;}
.directory-row span{display:grid!important;grid-template-columns:42% 1fr!important;column-gap:10px!important;align-items:start!important;position:relative!important;width:100%!important;box-sizing:border-box!important;padding:8px 0!important;line-height:1.35!important;font-family:var(--para);font-weight:500;text-transform:none;color:var(--navy);letter-spacing:normal;white-space:normal!important;overflow-wrap:normal!important;word-break:normal!important;hyphens:none!important;}
.directory-row span::before{content:attr(data-label) " ";position:static!important;width:auto!important;max-width:none!important;font-family:var(--heading);font-weight:600;text-transform:uppercase;letter-spacing:.04em;color:var(--navy);line-height:1.2!important;white-space:normal!important;}
.directory-row span .hl{display:inline!important;padding:0!important;}
.directory-row span[data-label="EFF DATE"], .directory-row span[data-label="Eff Date"], .directory-row span[data-label="STREET ADDRESS"], .directory-row span[data-label="Street Address"], .directory-row span[data-label="CITY"], .directory-row span[data-label="City"] { overflow-wrap:normal!important;word-break:normal!important;white-space:normal!important;}
}
</style>
<script>
(() => {
const S1 = "1quVZ_mPmE2sgONv4eOVYTTu2-K-n6j60";
const SHEET1_NAME = "Active providers";
const CAT_COL_INDEX_S1 = 8;
const S2 = "18gR4n19-5N30kNzid7aDWzSunJ0O8tzZ";
const GID2 = 720144501;
const $=(s)=>document.querySelector(s);
const esc=(s)=>String(s??"").replace(/[&<>"']/g,(m)=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[m]));
const reEsc=(s)=>s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");
const norm=(c)=>(c?c.f??c.v??"": "");
const slug=(s)=>String(s||"").toLowerCase().replace(/[^a-z0-9]+/g,"-");
const normKey=(s)=>String(s??"").trim().toLowerCase();
const titleCaseLabel=(s)=>{
const str=String(s??"").replace(/[\u200B-\u200D\uFEFF]/g,"").replace(/\u00A0/g," ").replace(/\s+/g," ").trim();
if(!str)return str;
const capWord=(w)=>{
const raw=String(w||"");
if(!raw)return raw;
if(raw===raw.toUpperCase()&&/[A-Z]/.test(raw))return raw;
if(raw.includes("/"))return raw.split("/").map(capWord).join("/");
if(raw.includes("-"))return raw.split("-").map(capWord).join("-");
const lower=raw.toLowerCase();
return lower.charAt(0).toUpperCase()+lower.slice(1);
};
return str.split(" ").map(capWord).join(" ");
};
const grid=(labels)=>labels.map((t)=>{
t=(t||"").toLowerCase().trim();
if(t.includes("provider")||t.includes("facility"))return"185px";
if(t.includes("specialty")||t.includes("category"))return"175px";
if(t.includes("eff"))return"150px";
if(t.includes("last")&&t.includes("name"))return"120px";
if(t==="n"||t.includes("middle")||t.includes("mi")||t.includes("m.i"))return"46px";
if(t.includes("first")&&t.includes("name"))return"140px";
if(t.includes("degree"))return"70px";
if(t.includes("street")&&t.includes("address"))return"185px";
if(t==="city")return"100px";
if(t==="state")return"55px";
if(t.includes("zip"))return"66px";
if(t.includes("phone"))return"100px";
return"150px";
}).join(" ");
const mergeProviderName=(first,mi,last)=>{
first=String(first||"").trim();mi=String(mi||"").trim();last=String(last||"").trim();
if(!first&&!mi&&!last)return"";
const mid=mi?(mi.length===1?mi+".":mi):"";
return[first,mid,last].filter(Boolean).join(" ").replace(/\s+/g," ").trim();
};
const findIdx=(labels,preds)=>{
const L=labels.map((s)=>String(s||"").toLowerCase().trim());
for(let i=0;i<L.length;i++)if(preds.some((fn)=>fn(L[i])))return i;
return-1;
};
const fetchGviz=async(spreadsheetId,params)=>{
const qs=new URLSearchParams({tqx:"out:json",...params}).toString();
const url=`https://docs.google.com/spreadsheets/d/${spreadsheetId}/gviz/tq?${qs}`;
const t=await fetch(url).then((r)=>r.text());
return JSON.parse(t.match(/setResponse\(([\s\S]+)\);/)[1]);
};
let term="",sections=[],displayCols=[],displayLabels=[],gridTpl="";
const isCardMode=()=>window.matchMedia("(max-width:1024px)").matches;
const wrapVal=(html)=>isCardMode()
? `<i class="tp-val" style="grid-column:2;min-width:0;font-style:normal;display:block">${html}</i>`
: html;
const resetRow=(rowEl)=>rowEl.querySelectorAll("span").forEach((s)=>{
const t=s.textContent||"";
s.innerHTML=wrapVal(esc(t));
});
const hi=(spanEl,rx)=>{
const t=spanEl.textContent||"";
if(!rx.test(t))return false;
rx.lastIndex=0;
const html=esc(t).replace(rx,(m)=>`<mark class="hl">${esc(m)}</mark>`);
spanEl.innerHTML=wrapVal(html);
return true;
};
const rowText=(rowEl)=>[...rowEl.querySelectorAll("span")].map((s)=>(s.textContent||"").toLowerCase()).join(" | ");
const search=()=>{
const q=term.trim();
if(!q){
sections.forEach((sec)=>{
sec.rows.forEach((r)=>{resetRow(r);r.style.display="";});
sec.el.style.display="";
});
return;
}
const qLower=q.toLowerCase();
const phraseExists=sections.some((sec)=>sec.rows.some((r)=>rowText(r).includes(qLower)));
const phraseRx=new RegExp(reEsc(q),"gi");
const tokens=q.split(/\s+/).map((t)=>t.trim()).filter(Boolean);
const tokenRxs=tokens.map((t)=>new RegExp(reEsc(t),"gi"));
sections.forEach((sec)=>{
let showSec=false;
sec.rows.forEach((r)=>{
resetRow(r);
const text=rowText(r);
let ok=false;
if(phraseExists){
ok=text.includes(qLower);
if(ok)[...r.querySelectorAll("span")].forEach((s)=>hi(s,phraseRx));
}else{
ok=tokenRxs.every((rx)=>rx.test(text));
if(ok)[...r.querySelectorAll("span")].forEach((s)=>tokenRxs.forEach((rx)=>hi(s,rx)));
}
r.style.display=ok?"":"none";
if(ok)showSec=true;
});
sec.el.style.display=showSec?"":"none";
});
};
const syncCategoryLines=()=>{
const header=$("#directoryColumnsTop");
if(!header)return;
const w=Math.ceil(header.scrollWidth||header.getBoundingClientRect().width||0);
if(!w)return;
document.querySelectorAll("#directoryContent .cat-sep").forEach((cs)=>{
cs.style.position="relative";cs.style.left="-20px";cs.style.width=w+"px";
});
};
const buildDisplaySchema=(orderedIdx,orderedLabels)=>{
const iFirst=findIdx(orderedLabels,[(t)=>t.includes("first")&&t.includes("name")]);
const iLast=findIdx(orderedLabels,[(t)=>t.includes("last")&&t.includes("name")]);
const iMI=findIdx(orderedLabels,[(t)=>t==="n"||t==="mi"||t.includes("middle")||t.includes("m.i")]);
const hasName=iFirst>-1||iLast>-1||iMI>-1;
const nameSrc={first:iFirst>-1?orderedIdx[iFirst]:null,mi:iMI>-1?orderedIdx[iMI]:null,last:iLast>-1?orderedIdx[iLast]:null};
const cols=[];
for(let i=0;i<orderedIdx.length;i++){
if(hasName&&(i===iFirst||i===iLast||i===iMI))continue;
cols.push({type:"normal",src:orderedIdx[i],label:orderedLabels[i]});
}
if(hasName)cols.splice(1,0,{type:"provider",label:"Provider/Facility",nameSrc});
return cols;
};
const findCol2=(labels2,matchers)=>{
const L=labels2.map((x)=>String(x||"").toLowerCase().trim());
for(let i=0;i<L.length;i++)if(matchers.some((fn)=>fn(L[i])))return i;
return-1;
};
const normalizeLabelKey=(s)=>String(s||"").toLowerCase().replace(/\s+/g," ").trim();
const forceProviderLabelBreak=()=>{
document.querySelectorAll('#directoryContent .directory-row span[data-label="Provider/Facility"]').forEach((s)=>{
if(s.getAttribute("data-label")!=="Provider / Facility")s.setAttribute("data-label","Provider / Facility");
});
};
const alignLastUpdatedToTableRight=()=>{
const el=$("#lastUpdatedInline");
const head=$("#directoryColumnsTop");
const sc=document.querySelector(".directory-scroll");
if(!el||!head||!sc)return;
if(window.matchMedia("(max-width:767px)").matches){
el.style.position="";
el.style.left="";
return;
}
const headRect=head.getBoundingClientRect();
const scRect=sc.getBoundingClientRect();
if(!headRect.width||!scRect.width)return;
const targetRight=Math.min(headRect.right, scRect.right);
el.style.position="relative";
el.style.left="0px";
const elRect=el.getBoundingClientRect();
const dx=Math.round(targetRight - elRect.right);
el.style.left=dx+"px";
};
let _tpAlignRAF=0;
const scheduleAlign=()=>{
if(_tpAlignRAF)return;
_tpAlignRAF=requestAnimationFrame(()=>{
_tpAlignRAF=0;
alignLastUpdatedToTableRight();
});
};
const load=async()=>{
$("#directoryContent").innerHTML=`<p style="padding:20px;font-family:var(--para);color:var(--navy)">Loading...</p>`;
try{
// setLastUpdatedToday();
const d1=await fetchGviz(S1,{sheet:SHEET1_NAME});
const rawCols1=d1.table.cols.map((c,i)=>({label:(c.label||"").trim(),index:i})).filter((c)=>c.label);
let orderedIdx=rawCols1.map((c)=>c.index);
if(orderedIdx.includes(CAT_COL_INDEX_S1))orderedIdx=[CAT_COL_INDEX_S1,...orderedIdx.filter((i)=>i!==CAT_COL_INDEX_S1)];
const orderedLabels=orderedIdx.map((i)=>rawCols1.find((c)=>c.index===i)?.label||"");
displayCols=buildDisplaySchema(orderedIdx,orderedLabels);
displayLabels=displayCols.map((c)=>c.label);
gridTpl=grid(displayLabels);
$("#directoryColumnsTop").style.gridTemplateColumns=gridTpl;
$("#directoryColumnsTop").innerHTML=displayLabels.map((l)=>`<span>${esc(l)}</span>`).join("");
let rows1=d1.table.rows.map((r)=>(r.c||[]).map(norm));
let lastCat="";
rows1.forEach((r)=>{if(r[CAT_COL_INDEX_S1])lastCat=r[CAT_COL_INDEX_S1];else r[CAT_COL_INDEX_S1]=lastCat;});
const unified=[];
rows1.forEach((r)=>{
const data={};
displayCols.forEach((c,idx)=>{
const label=displayLabels[idx]||"";
let val="";
if(c.type==="provider"){
const f=c.nameSrc.first!=null?r[c.nameSrc.first]:"",m=c.nameSrc.mi!=null?r[c.nameSrc.mi]:"",l=c.nameSrc.last!=null?r[c.nameSrc.last]:"";
val=mergeProviderName(f,m,l);
}else val=r[c.src]??"";
data[label]=val;
});
const cat=r[CAT_COL_INDEX_S1]||data["SPECIALTY"]||data["Specialty"]||data["Category"]||"Other";
unified.push({cat,data,src:1});
});
const d2=await fetchGviz(S2,{gid:String(GID2)});
const labels2=d2.table.cols.map((c)=>(c.label||"").trim());
const rows2=d2.table.rows.map((r)=>(r.c||[]).map(norm));
const iFacility=findCol2(labels2,[(t)=>t==="facility name"||(t.includes("facility")&&t.includes("name"))]);
const iAddress=findCol2(labels2,[(t)=>t==="address"||(t.includes("street")&&t.includes("address"))]);
const iCity=findCol2(labels2,[(t)=>t==="city"]);
const iState=findCol2(labels2,[(t)=>t==="state"]);
const iZip=findCol2(labels2,[(t)=>t==="zip code"||t==="zip"||t.includes("zip")]);
const iPhone=findCol2(labels2,[(t)=>t==="phone"||t.includes("phone")]);
const iEff=findCol2(labels2,[(t)=>t.includes("eff")&&(t.includes("date")||t.includes("eff"))&&t.includes("date")]);
const iSpec=findCol2(labels2,[(t)=>t==="specialty"||t.includes("specialty")]);
rows2.forEach((r)=>{
const data={};
displayLabels.forEach((lab)=>(data[lab]=""));
const byKey={};
displayLabels.forEach((lab)=>(byKey[normalizeLabelKey(lab)]=lab));
const setIfExists=(targetLabelGuess,value)=>{
const key=normalizeLabelKey(targetLabelGuess);
const real=byKey[key]||null;
if(real)data[real]=value??"";
};
setIfExists("Provider/Facility",iFacility>-1?r[iFacility]:"");
setIfExists("STREET ADDRESS",iAddress>-1?r[iAddress]:"");
setIfExists("CITY",iCity>-1?r[iCity]:"");
setIfExists("STATE",iState>-1?r[iState]:"");
setIfExists("ZIP",iZip>-1?r[iZip]:"");
setIfExists("PHONE",iPhone>-1?r[iPhone]:"");
setIfExists("EFF DATE",iEff>-1?r[iEff]:"");
setIfExists("SPECIALTY",iSpec>-1?r[iSpec]:"");
const cat=(iSpec>-1?r[iSpec]:"")||data["SPECIALTY"]||"Other";
unified.push({cat,data,src:2});
});
const canon=new Map();
unified.forEach((x)=>{
const k=normKey(x.cat||"Other");
if(!canon.has(k))canon.set(k,{from1:null,from2:null});
const obj=canon.get(k);
if(x.src===1&&!obj.from1)obj.from1=x.cat||"Other";
if(x.src===2&&!obj.from2)obj.from2=x.cat||"Other";
});
const catKeysSorted=[...canon.keys()].sort((a,b)=>{
const A=canon.get(a).from1||canon.get(a).from2||"Other";
const B=canon.get(b).from1||canon.get(b).from2||"Other";
return A.localeCompare(B,undefined,{sensitivity:"base"});
});
$("#categorySelect").innerHTML=`<option value="" selected disabled>Search By Specialty</option>`+catKeysSorted.map((k)=>{
const displayName=titleCaseLabel(canon.get(k).from1||canon.get(k).from2||"Other");
return `<option value="${esc(k)}">${esc(displayName)}</option>`;
}).join("");
const grouped={};
unified.forEach((x)=>{const k=normKey(x.cat||"Other");(grouped[k]??=[]).push(x);});
sections=[];
$("#directoryContent").innerHTML="";
catKeysSorted.forEach((k)=>{
const displayName=canon.get(k).from1||canon.get(k).from2||"Other";
const rs=grouped[k]||[];
const wrap=document.createElement("div");
wrap.id="cat-"+slug(k);
wrap.innerHTML=`<div class="cat-sep"><span>${esc(displayName)}</span></div>`;
const rr=[];
rs.forEach((item)=>{
const el=document.createElement("div");
el.className="directory-row";
el.style.gridTemplateColumns=gridTpl;
el.innerHTML=displayLabels.map((label)=>`<span data-label="${esc(label)}">${esc(item.data[label]??"")}</span>`).join("");
rr.push(el);
wrap.appendChild(el);
});
$("#directoryContent").appendChild(wrap);
sections.push({el:wrap,rows:rr});
});
search();syncCategoryLines();forceProviderLabelBreak();
requestAnimationFrame(()=>alignLastUpdatedToTableRight());
}catch(e){
$("#directoryContent").innerHTML=`<p style="padding:20px;font-family:var(--para);color:var(--navy)">The data could not be loaded. You can try reloading the page or come back after a few minutes.</p>`;
}
};
$("#searchInput").oninput=(e)=>{term=e.target.value.trim();search();};
$("#categorySelect").onchange=(e)=>{const k=e.target.value;if(k)$("#cat-"+slug(k)).scrollIntoView({behavior:"smooth"});};
$("#printBtn").onclick=()=>print();
$("#backToTopFloating").onclick=()=>scrollTo({top:0,behavior:"smooth"});
window.addEventListener("resize",()=>{
syncCategoryLines();
scheduleAlign();
},{passive:true});
document.querySelector(".directory-scroll")?.addEventListener("scroll",scheduleAlign,{passive:true});
load();
})();
</script>