feat(npi): healthcare marketing pages, nav dropdown, NPI lookup API + free tool + companion data migration/loader

This commit is contained in:
justin 2026-06-05 01:33:36 -05:00
parent f349d519c6
commit 4b0155542e
9 changed files with 1176 additions and 2 deletions

View file

@ -0,0 +1,94 @@
---
import Base from "../../../layouts/Base.astro";
const title = "Healthcare Provider Compliance — NPI, NPPES & Medicare Filings";
const description = "We handle the federal paperwork that keeps providers billable: Medicare revalidation (PECOS), NPI reactivation, NPPES updates, enrollment, and OIG/SAM exclusion screening. HIPAA not included.";
---
<Base title={title} description={description}>
<main>
<section class="pw-hero">
<h1>Healthcare Provider Compliance</h1>
<p style="font-size: 1.1rem; max-width: 46rem;">
Your NPI is public. Your Medicare enrollment has a clock on it. We
handle the federal filings that keep you billable so a missed
deadline never deactivates your privileges.
</p>
</section>
<section>
<h2>The obligations we cover</h2>
<div class="pw-card-grid">
<a class="pw-card" href="/services/healthcare/npi-revalidation">
<h3>Medicare Revalidation (PECOS)</h3>
<p class="pw-card-price">$399 per filing</p>
<p>CMS requires every enrolled provider to revalidate every 5 years. Miss it and your billing privileges deactivate. We prepare and file it in PECOS.</p>
</a>
<a class="pw-card" href="/services/healthcare/medicare-enrollment">
<h3>Medicare Enrollment (PECOS)</h3>
<p class="pw-card-price">$499 per filing</p>
<p>New CMS-855 enrollment: taxonomy, practice location, authorized official, EFT. We assemble the package and file it.</p>
</a>
<a class="pw-card" href="/order/npi-reactivation">
<h3>NPI Reactivation</h3>
<p class="pw-card-price">$249 per filing</p>
<p>Reactivate a deactivated NPI in NPPES and re-certify the record so you can resume billing.</p>
</a>
<a class="pw-card" href="/order/nppes-update">
<h3>NPPES Data Update</h3>
<p class="pw-card-price">$149 per filing</p>
<p>CMS requires NPPES updates within 30 days of any change. We update and re-attest your record.</p>
</a>
<a class="pw-card" href="/order/oig-sam-screening">
<h3>OIG / SAM Exclusion Screening</h3>
<p class="pw-card-price">$99 / year</p>
<p>Annual OIG LEIE and SAM exclusion screening for you and your staff, with a compliance certificate.</p>
</a>
<a class="pw-card" href="/order/provider-compliance-bundle">
<h3>Provider Compliance Bundle</h3>
<p class="pw-card-price">$699 / year</p>
<p>Revalidation monitoring, OIG/SAM screening, and NPPES upkeep in one annual package.</p>
</a>
</div>
</section>
<section>
<h2>Not sure where you stand?</h2>
<p>
Run our free NPI compliance check. Enter your NPI and we&apos;ll tell you
whether your record is active, whether your Medicare revalidation is due
or past due, and whether anything in the public registries needs attention.
</p>
<p>
<a class="pw-cta-inline" href="/tools/npi-compliance-check">Run the free NPI compliance check →</a>
</p>
</section>
<section class="pw-note">
<p>
<strong>Note:</strong> We handle federal registry and enrollment
compliance. We do <em>not</em> provide HIPAA privacy/security program
services. That&apos;s a separate specialty.
</p>
</section>
<section class="pw-cta-section">
<a class="pw-cta" href="/services/healthcare/npi-revalidation">Check your revalidation status →</a>
</section>
</main>
</Base>
<style>
main { max-width: 900px; margin: 0 auto; padding: 2rem 1.25rem 4rem; }
.pw-hero { padding: 2.5rem 1.5rem; background: linear-gradient(135deg, #0f766e 0%, #14b8a6 100%); color: #fff; border-radius: 14px; margin-bottom: 2rem; }
.pw-hero h1 { color: #fff; margin: 0 0 0.5rem; }
.pw-card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1rem; margin: 1rem 0; }
.pw-card { display: block; padding: 1.25rem; border: 1px solid #e2e8f0; border-radius: 10px; text-decoration: none; color: inherit; transition: box-shadow 0.15s; }
.pw-card:hover { box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); border-color: #14b8a6; }
.pw-card h3 { margin: 0 0 0.3rem; color: #134e4a; font-size: 1.05rem; }
.pw-card-price { font-weight: 700; color: #0f766e; margin: 0 0 0.5rem; font-size: 1rem; }
.pw-note { border-left: 4px solid #14b8a6; background: #f0fdfa; padding: 0.9rem 1.1rem; border-radius: 0 6px 6px 0; margin: 1.5rem 0; }
.pw-cta-inline { color: #0f766e; font-weight: 600; }
.pw-cta-section { margin: 3rem 0; text-align: center; }
.pw-cta { display: inline-block; padding: 1rem 2rem; background: #0f766e; color: #fff; text-decoration: none; font-weight: 600; border-radius: 8px; font-size: 1.05rem; }
.pw-cta:hover { background: #115e59; }
</style>

View file

@ -0,0 +1,69 @@
---
import Base from "../../../layouts/Base.astro";
const title = "Medicare Enrollment (PECOS) — Get Enrolled and Billable";
const description = "New to Medicare or adding a practice location? We assemble and file your CMS-855 enrollment in PECOS: taxonomy, practice location, authorized official, EFT, and reassignments.";
---
<Base title={title} description={description}>
<main>
<section class="pw-hero">
<h1>Medicare Enrollment (PECOS)</h1>
<p style="font-size: 1.1rem; max-width: 44rem;">
Before you can bill Medicare, you have to be enrolled in PECOS with a
clean CMS-855. We build the package, file it, and shepherd it through
your MAC to approval.
</p>
</section>
<section>
<h2>Who needs to enroll</h2>
<ul>
<li><strong>New providers</strong> just out of training or newly licensed and ready to bill Medicare.</li>
<li><strong>Providers joining a group</strong> who need a reassignment (855R) to the group&apos;s billing.</li>
<li><strong>Groups and suppliers</strong> establishing a new billing entity (855B).</li>
<li><strong>Providers adding a practice location</strong> or changing how they bill.</li>
</ul>
</section>
<section>
<h2>What we file</h2>
<ul>
<li><strong>CMS-855I</strong> — individual physician/non-physician practitioner enrollment.</li>
<li><strong>CMS-855B</strong> — group practice / supplier enrollment.</li>
<li><strong>CMS-855R</strong> — reassignment of benefits to a group.</li>
<li><strong>CMS-588 (EFT)</strong> — electronic funds transfer setup so you get paid.</li>
<li><strong>CMS-460</strong> — participation agreement, if you&apos;re enrolling as participating.</li>
</ul>
<div class="pw-callout">
We confirm your <strong>taxonomy code</strong>, <strong>practice
location</strong>, and <strong>authorized official</strong> match
across NPPES and PECOS before we file, because a mismatch is the #1
cause of enrollment rejections.
</div>
</section>
<section>
<h2>How it works</h2>
<ol>
<li>You place the order and give us your NPI and basic practice details.</li>
<li>We pull your NPPES record and identify exactly which 855 forms apply.</li>
<li>We assemble the package and send it to you for signature/attestation.</li>
<li>We submit in PECOS and track it through your MAC, resolving any development requests.</li>
</ol>
</section>
<section class="pw-cta-section">
<a class="pw-cta" href="/order/medicare-enrollment">Start my Medicare enrollment — $499 →</a>
</section>
</main>
</Base>
<style>
main { max-width: 900px; margin: 0 auto; padding: 2rem 1.25rem 4rem; }
.pw-hero { padding: 2.5rem 1.5rem; background: linear-gradient(135deg, #0f766e 0%, #14b8a6 100%); color: #fff; border-radius: 14px; margin-bottom: 2rem; }
.pw-hero h1 { color: #fff; margin: 0 0 0.5rem; }
.pw-callout { border-left: 4px solid #14b8a6; background: #f0fdfa; padding: 0.9rem 1.1rem; border-radius: 0 6px 6px 0; margin: 1rem 0; }
.pw-cta-section { margin: 3rem 0; text-align: center; }
.pw-cta { display: inline-block; padding: 1rem 2rem; background: #0f766e; color: #fff; text-decoration: none; font-weight: 600; border-radius: 8px; font-size: 1.05rem; }
.pw-cta:hover { background: #115e59; }
</style>

View file

@ -0,0 +1,84 @@
---
import Base from "../../../layouts/Base.astro";
const title = "Medicare Revalidation Filing (PECOS) — Don't Let Your Billing Privileges Lapse";
const description = "CMS requires every enrolled provider and supplier to revalidate their Medicare enrollment every 5 years. Miss your deadline and your billing privileges are deactivated. We prepare and file your revalidation in PECOS.";
---
<Base title={title} description={description}>
<main>
<section class="pw-hero">
<h1>Medicare Revalidation Filing</h1>
<p style="font-size: 1.1rem; max-width: 44rem;">
Every Medicare-enrolled provider has to revalidate every 5 years. If
you miss your due date, CMS deactivates your billing privileges and
every claim after that bounces. We file your revalidation for you.
</p>
</section>
<section>
<h2>What revalidation is</h2>
<p>
Under <strong>42 CFR § 424.515</strong>, CMS re-verifies the
information of every enrolled provider and supplier on a recurring
cycle (every 5 years for most providers, every 3 years for DMEPOS
suppliers). You revalidate your entire enrollment record in
<strong>PECOS</strong> (the Provider Enrollment, Chain, and Ownership
System) using the CMS-855 family of forms.
</p>
<div class="pw-callout">
<strong>The deadline is real.</strong> CMS publishes a revalidation due
date for every provider. Submit late or not at all and your Medicare
billing privileges are <em>deactivated</em>. Reactivating means a gap
in payments and a fresh round of paperwork.
</div>
</section>
<section>
<h2>How do I know if I&apos;m due?</h2>
<p>
CMS posts a due date for every enrolled provider. Many providers are
already <strong>past due</strong> and don&apos;t know it because the
notification letter went to an old address. Run our free check and
we&apos;ll look up your status:
</p>
<p>
<a class="pw-cta-inline" href="/tools/npi-compliance-check">Check my revalidation status (free) →</a>
</p>
</section>
<section>
<h2>What we do</h2>
<ul>
<li>Pull your current PECOS enrollment record and confirm what CMS has on file.</li>
<li>Reconcile your practice locations, reassignments, ownership, and authorized officials.</li>
<li>Complete the correct CMS-855 (855I for individuals, 855B for groups/suppliers, 855R for reassignments).</li>
<li>Submit the revalidation in PECOS and track it through to approval.</li>
<li>Flag and resolve any development requests from your MAC before they cause a deactivation.</li>
</ul>
</section>
<section>
<h2>Why not do it yourself</h2>
<ul>
<li><strong>PECOS is unforgiving.</strong> One wrong reassignment or a stale practice location triggers a development request, and the clock keeps ticking.</li>
<li><strong>The forms branch.</strong> Whether you file 855I, 855B, 855R, or a combination depends on how you bill. Pick wrong and you start over.</li>
<li><strong>Deactivation is retroactive.</strong> If you miss the date, the gap isn&apos;t forgiven; claims in the gap stay denied.</li>
</ul>
</section>
<section class="pw-cta-section">
<a class="pw-cta" href="/order/npi-revalidation">File my Medicare revalidation — $399 →</a>
</section>
</main>
</Base>
<style>
main { max-width: 900px; margin: 0 auto; padding: 2rem 1.25rem 4rem; }
.pw-hero { padding: 2.5rem 1.5rem; background: linear-gradient(135deg, #0f766e 0%, #14b8a6 100%); color: #fff; border-radius: 14px; margin-bottom: 2rem; }
.pw-hero h1 { color: #fff; margin: 0 0 0.5rem; }
.pw-callout { border-left: 4px solid #14b8a6; background: #f0fdfa; padding: 0.9rem 1.1rem; border-radius: 0 6px 6px 0; margin: 1rem 0; }
.pw-cta-inline { color: #0f766e; font-weight: 600; }
.pw-cta-section { margin: 3rem 0; text-align: center; }
.pw-cta { display: inline-block; padding: 1rem 2rem; background: #0f766e; color: #fff; text-decoration: none; font-weight: 600; border-radius: 8px; font-size: 1.05rem; }
.pw-cta:hover { background: #115e59; }
</style>

View file

@ -0,0 +1,327 @@
---
import Base from "../../layouts/Base.astro";
---
<Base
title="NPI Compliance Check — Free Medicare Revalidation & NPPES Status"
description="Enter your 10-digit NPI to instantly check your NPPES status, Medicare revalidation due date, OIG exclusion, and record currency. Free tool from Performance West."
>
<main class="max-w-3xl mx-auto px-4 py-10">
<!-- Breadcrumb -->
<nav class="text-sm text-gray-500 mb-6" aria-label="Breadcrumb">
<a href="/" class="hover:text-teal-600">Home</a>
<span class="mx-1">/</span>
<a href="/tools" class="hover:text-teal-600">Free Tools</a>
<span class="mx-1">/</span>
<span class="text-gray-800 font-medium">NPI Compliance Check</span>
</nav>
<h1 class="text-3xl font-bold text-gray-900 mb-2">NPI Compliance Check</h1>
<p class="text-gray-600 mb-6">
Enter your provider name or 10-digit NPI to instantly check your NPPES status,
Medicare revalidation due date, OIG exclusion screening, and record currency.
</p>
<div class="bg-gray-100 border border-gray-200 rounded-lg p-4 text-sm text-gray-600 mb-8">
This tool queries publicly available NPPES, CMS, and OIG data for informational
purposes only. Always confirm critical compliance matters directly with CMS.
This is not legal advice.
</div>
<!-- Search box -->
<div class="bg-white border border-gray-200 rounded-xl p-6 shadow-sm mb-8">
<!-- Name search -->
<label for="name-input" class="block text-sm font-medium text-gray-700 mb-1">Provider or Organization Name</label>
<div class="flex gap-2 mb-2">
<input type="text" id="name-input" placeholder="e.g. Smith, or Acme Medical Group"
class="flex-1 border border-gray-300 rounded-lg px-4 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-teal-400" />
<input type="text" id="state-input" placeholder="State" maxlength="2"
class="w-20 border border-gray-300 rounded-lg px-3 py-2 text-sm uppercase focus:outline-none focus:ring-2 focus:ring-teal-400" />
<button id="name-search-btn"
class="bg-teal-600 hover:bg-teal-700 text-white font-semibold px-5 py-2 rounded-lg text-sm transition">Search</button>
</div>
<div id="name-results" class="hidden mt-3"></div>
<div class="flex items-center gap-3 my-5">
<div class="flex-1 border-t border-gray-200"></div>
<span class="text-sm text-gray-400">or enter NPI directly</span>
<div class="flex-1 border-t border-gray-200"></div>
</div>
<!-- NPI search -->
<label for="npi-input" class="block text-sm font-medium text-gray-700 mb-1">National Provider Identifier (NPI)</label>
<div class="flex gap-2 mb-2">
<input type="text" id="npi-input" placeholder="1234567893" maxlength="10" inputmode="numeric"
class="flex-1 border border-gray-300 rounded-lg px-4 py-2 font-mono text-lg focus:outline-none focus:ring-2 focus:ring-teal-400" />
<button id="npi-check-btn"
class="bg-gray-700 hover:bg-gray-800 text-white font-semibold px-5 py-2 rounded-lg text-sm transition">Check Compliance</button>
</div>
<p class="text-xs text-gray-400">
Don't know your NPI?
<a href="https://npiregistry.cms.hhs.gov/search" target="_blank" rel="noopener" class="text-teal-600 hover:underline">Look it up in NPPES</a>
</p>
</div>
<!-- Loading -->
<div id="loading" class="hidden text-center py-10">
<svg class="animate-spin h-8 w-8 text-teal-600 mx-auto mb-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"></path>
</svg>
<p class="text-gray-700 text-sm font-medium" id="loading-status">Checking NPPES &amp; CMS databases&hellip;</p>
</div>
<!-- Error -->
<div id="error-box" class="hidden bg-red-50 border border-red-200 rounded-lg p-4 text-red-700 text-sm mb-6">
<p id="error-message"></p>
</div>
<!-- Results -->
<div id="results" class="hidden space-y-6">
<div class="bg-white border border-gray-200 rounded-xl p-6 shadow-sm">
<h2 id="provider-name" class="text-xl font-bold text-gray-900"></h2>
<p class="mt-1 text-sm text-gray-600">
NPI: <span id="provider-npi" class="font-mono font-semibold text-gray-800"></span>
</p>
<div id="provider-details" class="mt-2 space-y-1 text-sm text-gray-600"></div>
</div>
<div id="checks-container" class="space-y-4"></div>
<!-- CTA -->
<div id="cta-section" class="bg-white border border-gray-200 rounded-xl p-6 shadow-sm">
<div id="cta-content"></div>
</div>
<p id="checked-at" class="text-xs text-gray-400 text-center"></p>
</div>
</main>
<script type="module" is:inline>
const API = window.__PW_API || "https://api.performancewest.net";
const nameInput = document.getElementById("name-input");
const stateInput = document.getElementById("state-input");
const nameSearchBtn = document.getElementById("name-search-btn");
const nameResults = document.getElementById("name-results");
const npiInput = document.getElementById("npi-input");
const npiCheckBtn = document.getElementById("npi-check-btn");
const loadingEl = document.getElementById("loading");
const errorBox = document.getElementById("error-box");
const errorMessage = document.getElementById("error-message");
const resultsEl = document.getElementById("results");
let currentController = null;
// Service map: each check id -> the order page + price for the CTA.
const SERVICE_MAP = {
"npi-active": { name: "NPI Reactivation", price: 249, url: "/order/npi-reactivation" },
"revalidation": { name: "Medicare Revalidation Filing", price: 399, url: "/order/npi-revalidation" },
"exclusion": { name: "OIG/SAM Exclusion Screening", price: 99, url: "/order/oig-sam-screening" },
"nppes-fresh": { name: "NPPES Data Update", price: 149, url: "/order/nppes-update" },
"optout": { name: "Medicare Enrollment (PECOS)", price: 499, url: "/order/medicare-enrollment" },
};
// --- Name search ---
nameSearchBtn?.addEventListener("click", runNameSearch);
nameInput?.addEventListener("keydown", (e) => { if (e.key === "Enter") runNameSearch(); });
async function runNameSearch() {
const name = nameInput.value.trim();
if (!name) return;
const state = stateInput.value.trim().toUpperCase();
nameResults.classList.remove("hidden");
nameResults.innerHTML = '<p class="text-sm text-gray-500">Searching&hellip;</p>';
try {
const qs = `name=${encodeURIComponent(name)}${state ? "&state=" + encodeURIComponent(state) : ""}`;
const res = await fetch(`${API}/api/v1/npi/search?${qs}`);
if (!res.ok) throw new Error("Search failed");
const data = await res.json();
const items = data.results || [];
if (items.length === 0) {
nameResults.innerHTML = '<p class="text-sm text-gray-500">No results found.</p>';
return;
}
let html = `<p class="text-xs text-gray-400 mb-2">${items.length} result${items.length !== 1 ? "s" : ""} — click to check compliance</p>`;
html += '<div class="space-y-2 max-h-72 overflow-y-auto border border-gray-200 rounded-lg divide-y divide-gray-100">';
for (const item of items) {
html += `<button data-npi="${item.npi}" class="name-result-btn w-full text-left px-4 py-3 flex items-center justify-between gap-3 hover:bg-teal-50 transition cursor-pointer" style="display:flex;">
<div style="min-width:0;flex:1;">
<p style="font-weight:600;font-size:14px;color:#111827;margin:0;">${item.name || "Unknown"}</p>
<p style="font-size:12px;color:#6b7280;margin:2px 0 0;">${item.taxonomy || ""}${item.city ? " — " + item.city + ", " + (item.state || "") : ""}</p>
</div>
<div style="text-align:right;flex-shrink:0;">
<p style="font-family:monospace;font-size:12px;color:#0f766e;font-weight:600;margin:0;">${item.npi}</p>
<p style="font-size:11px;color:${item.status === "A" ? "#059669" : "#dc2626"};margin:2px 0 0;">${item.status === "A" ? "Active" : "Inactive"}</p>
</div>
</button>`;
}
html += "</div>";
nameResults.innerHTML = html;
nameResults.querySelectorAll(".name-result-btn").forEach((btn) => {
btn.addEventListener("click", () => {
npiInput.value = btn.getAttribute("data-npi");
runCheck();
});
});
} catch (err) {
nameResults.innerHTML = `<p class="text-sm text-red-600">Search error: ${err.message || "Unknown error"}</p>`;
}
}
// --- NPI compliance check ---
npiCheckBtn.addEventListener("click", runCheck);
npiInput.addEventListener("keydown", (e) => { if (e.key === "Enter") runCheck(); });
async function runCheck() {
const raw = npiInput.value.trim().replace(/\D/g, "");
if (raw.length !== 10) {
showError("NPI must be exactly 10 digits (e.g. 1234567893). You entered " + raw.length + " digits.");
return;
}
npiInput.value = raw;
const url = new URL(window.location);
url.searchParams.set("npi", raw);
history.replaceState(null, "", url);
resultsEl.classList.add("hidden");
errorBox.classList.add("hidden");
loadingEl.classList.remove("hidden");
if (currentController) currentController.abort();
const controller = new AbortController();
currentController = controller;
try {
const timeout = setTimeout(() => controller.abort(), 30000);
const res = await fetch(`${API}/api/v1/npi/lookup?npi=${raw}`, { signal: controller.signal });
clearTimeout(timeout);
if (currentController !== controller) return;
if (!res.ok) {
const body = await res.json().catch(() => ({}));
if (res.status === 404) {
showError(`NPI ${raw} was not found in NPPES. Verify your number at <a href="https://npiregistry.cms.hhs.gov/search" target="_blank" style="color:#0f766e;text-decoration:underline;">NPPES Registry</a>.`, true);
return;
}
throw new Error(body.error || "NPI lookup failed. Please try again.");
}
renderResults(await res.json());
} catch (err) {
if (err.name === "AbortError") {
showError("The CMS databases are taking too long to respond. Please try again in a moment.");
} else {
showError(err.message || "Could not reach the CMS databases right now. Please try again.");
}
} finally {
loadingEl.classList.add("hidden");
}
}
function showError(msg, allowHtml) {
if (allowHtml) errorMessage.innerHTML = msg;
else errorMessage.textContent = msg;
errorBox.classList.remove("hidden");
}
const colorMap = {
green: { bg: "bg-green-50", border: "border-green-200", iconColor: "text-green-600", textColor: "text-green-800" },
yellow: { bg: "bg-amber-50", border: "border-amber-200", iconColor: "text-amber-600", textColor: "text-amber-800" },
red: { bg: "bg-red-50", border: "border-red-200", iconColor: "text-red-600", textColor: "text-red-800" },
unknown: { bg: "bg-gray-50", border: "border-gray-200", iconColor: "text-gray-500", textColor: "text-gray-700" },
};
const icons = {
green: `<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>`,
yellow: `<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01M10.29 3.86l-8.8 15.32A1 1 0 002.36 21h19.28a1 1 0 00.87-1.5l-9.64-16.64a1 1 0 00-1.74 0z"/></svg>`,
red: `<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>`,
unknown: `<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01"/></svg>`,
};
function renderResults(data) {
errorBox.classList.add("hidden");
document.getElementById("provider-name").textContent = data.name || "Unknown Provider";
document.getElementById("provider-npi").textContent = data.npi;
const details = document.getElementById("provider-details");
const bits = [];
if (data.enumeration_type) bits.push(`<span class="font-medium text-gray-700">Type:</span> ${data.enumeration_type === "NPI-2" ? "Organization" : "Individual"}`);
if (data.taxonomy?.desc) bits.push(`<span class="font-medium text-gray-700">Taxonomy:</span> ${data.taxonomy.desc}`);
if (data.practice_state) bits.push(`<span class="font-medium text-gray-700">Practice State:</span> ${data.practice_state}`);
if (data.enumeration_date) bits.push(`<span class="font-medium text-gray-700">Enumerated:</span> ${data.enumeration_date}`);
details.innerHTML = bits.map((b) => `<p>${b}</p>`).join("");
const container = document.getElementById("checks-container");
container.innerHTML = "";
for (const check of (data.checks || [])) {
const status = check.status || "unknown";
const c = colorMap[status] || colorMap.unknown;
const card = document.createElement("div");
card.className = `${c.bg} ${c.border} border rounded-xl p-4 flex items-start gap-3`;
card.innerHTML = `<div class="${c.iconColor} mt-0.5 flex-shrink-0">${icons[status] || icons.unknown}</div>
<div class="flex-1">
<p class="font-semibold ${c.textColor}">${check.label || check.id}</p>
${check.detail ? `<p class="text-sm ${c.textColor} mt-1">${check.detail}</p>` : ""}
</div>`;
container.appendChild(card);
}
renderCta(data);
document.getElementById("checked-at").textContent = "Checked at " + new Date().toLocaleString();
resultsEl.classList.remove("hidden");
document.getElementById("results").scrollIntoView({ behavior: "smooth", block: "start" });
}
function renderCta(data) {
const checks = data.checks || [];
const npi = data.npi;
const services = [];
const seen = new Set();
for (const check of checks) {
if (check.status === "green") continue;
const svc = SERVICE_MAP[check.id];
if (svc && !seen.has(check.id)) { services.push({ ...svc, id: check.id }); seen.add(check.id); }
}
const ctaContent = document.getElementById("cta-content");
if (services.length > 0) {
let html = `<div style="background:#fef2f2;border:2px solid #fca5a5;border-radius:10px;padding:16px;margin-bottom:16px;">
<p style="font-size:14px;color:#991b1b;font-weight:700;margin:0 0 6px;">&#9888; ${services.length} item${services.length > 1 ? "s need" : " needs"} attention</p>
<p style="font-size:13px;color:#7f1d1d;margin:0;">We can prepare and file these with CMS on your behalf so your billing privileges stay intact.</p>
</div>`;
html += `<h3 class="text-lg font-bold text-gray-900 mb-1">Recommended filings</h3>`;
html += `<p class="text-sm text-gray-600 mb-4">Based on your NPPES, CMS, and OIG records.</p>`;
html += `<div class="space-y-2">`;
for (const svc of services) {
html += `<a href="${svc.url}?npi=${npi}" class="flex items-center justify-between gap-3 p-3 rounded-lg border border-gray-200 hover:bg-teal-50 hover:border-teal-300 transition cursor-pointer" style="text-decoration:none;color:inherit;">
<span class="font-medium text-gray-900">${svc.name}</span>
<span class="font-semibold text-teal-700 whitespace-nowrap">$${svc.price} &rarr;</span>
</a>`;
}
html += `</div>`;
html += `<div class="mt-5 text-center">
<a href="/order/provider-compliance-bundle?npi=${npi}" style="background:#0f766e;color:#fff;font-weight:700;padding:12px 28px;border-radius:8px;display:inline-block;font-size:15px;text-decoration:none;">
Get the Provider Compliance Bundle ($699/yr) &rarr;
</a>
<p style="font-size:11px;color:#6b7280;margin-top:8px;">Revalidation monitoring, screening, and NPPES upkeep in one package.</p>
</div>`;
ctaContent.innerHTML = html;
} else {
ctaContent.innerHTML = `<h3 class="text-lg font-bold text-green-800 mb-2">Looking good &mdash; all checks passed</h3>
<p class="text-sm text-gray-600 mb-4">Your NPI and Medicare records appear current. No action is needed right now.</p>
<a href="/order/oig-sam-screening?npi=${data.npi}" class="inline-block bg-teal-600 hover:bg-teal-700 text-white font-semibold px-5 py-2 rounded-lg text-sm transition" style="text-decoration:none;">
Add annual OIG/SAM screening &mdash; $99/yr
</a>`;
}
}
// --- Auto-fill from URL ---
(function init() {
const params = new URLSearchParams(window.location.search);
const npi = params.get("npi");
const name = params.get("name");
if (npi) { npiInput.value = npi; runCheck(); }
else if (name) { nameInput.value = name; runNameSearch(); }
})();
</script>
</Base>

File diff suppressed because one or more lines are too long