new-site/site/public/order/neca-ocn/index.html
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

310 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Obtain a NECA Operating Company Number (OCN) for your telecom carrier. Required for STIR/SHAKEN, LRN, and numbering authority. $2,650 all-in.">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<title>NECA OCN Registration — Performance West Inc.</title>
<script>
window.__PW_API = (function() {
var h = window.location.hostname;
if (h === "localhost" || h === "127.0.0.1") return "http://" + h + ":3001";
if (h === "dev.performancewest.net") return "https://api.dev.performancewest.net";
return "https://api.performancewest.net";
})();
</script>
<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=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Inter',system-ui,sans-serif;color:#1f2937;background:#f9fafb;line-height:1.6}
a{color:#1e3a5f;text-decoration:none}
.wrap{max-width:680px;margin:0 auto;padding:2rem 1.25rem 4rem}
h1{font-size:1.65rem;font-weight:700;color:#111827;margin-bottom:.25rem}
.subtitle{font-size:.9rem;color:#6b7280;margin-bottom:1.5rem}
.card{background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:1.25rem;margin-bottom:1rem}
.card h2{font-size:1.05rem;font-weight:700;color:#1e3a5f;margin-bottom:.75rem}
.label{display:block;font-size:.82rem;font-weight:600;color:#374151;margin-bottom:.3rem;margin-top:.6rem}
select,input[type=text],input[type=email],input[type=tel]{width:100%;padding:.5rem .7rem;border:1px solid #d1d5db;border-radius:8px;font-size:.88rem;background:#fff}
select:focus,input:focus{outline:none;border-color:#1e3a5f;box-shadow:0 0 0 2px rgba(30,58,95,.15)}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:.4rem;padding:.65rem 1.5rem;border-radius:8px;font-weight:600;font-size:.92rem;cursor:pointer;border:none;transition:all .15s}
.btn-primary{background:#1e3a5f;color:#fff}.btn-primary:hover{background:#162e4d}
.btn-primary:disabled{opacity:.5;cursor:not-allowed}
.info-banner{padding:.6rem .9rem;background:#ecfdf5;border-left:3px solid #059669;border-radius:0 6px 6px 0;font-size:.85rem;color:#065f46;margin-bottom:1.25rem}
.price-box{background:#f0f4f8;border:2px solid #1e3a5f;border-radius:10px;padding:1rem;text-align:center;margin-bottom:1.25rem}
.price-box .price{font-size:1.8rem;font-weight:800;color:#1e3a5f}
.price-box .price-note{font-size:.78rem;color:#6b7280}
.feature-grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem;margin:.75rem 0}
.feature-item{display:flex;align-items:flex-start;gap:.4rem;font-size:.82rem;color:#374151}
.feature-item svg{width:16px;height:16px;color:#059669;flex-shrink:0;margin-top:2px}
.state-picker{display:grid;grid-template-columns:repeat(auto-fill, minmax(110px, 1fr));gap:.35rem;max-height:260px;overflow-y:auto;border:1px solid #e5e7eb;border-radius:8px;padding:.5rem}
.state-chip{display:flex;align-items:center;gap:.3rem;padding:.25rem .5rem;border-radius:6px;font-size:.78rem;cursor:pointer;border:1px solid transparent;transition:all .1s}
.state-chip:hover{background:#f0f4f8}
.state-chip.selected{background:#eff6ff;border-color:#1e3a5f;font-weight:600}
.state-chip input{accent-color:#1e3a5f;width:14px;height:14px}
.tandem-box{background:#fffbeb;border:1px solid #fbbf24;border-radius:8px;padding:.75rem;margin-top:.75rem}
.tandem-box h3{font-size:.88rem;font-weight:700;color:#92400e;margin-bottom:.3rem}
.hidden{display:none}
.err{color:#dc2626;font-size:.82rem;margin-top:.5rem;display:none}
</style>
</head>
<body>
<div class="wrap">
<h1>NECA OCN Registration</h1>
<p class="subtitle">Obtain a unique Operating Company Number from NECA. Required for STIR/SHAKEN signing, LRN assignments, and numbering authority.</p>
<div class="price-box">
<div class="price">$2,650</div>
<div class="price-note">All-in: $650 OCN application + $2,000 sponsoring CLEC agreement</div>
</div>
<div class="info-banner">
<strong>Tax deductible</strong> &mdash; OCN registration fees are deductible as ordinary business expenses under IRC &sect; 162.
</div>
<!-- What's included -->
<div class="card">
<h2>What's Included</h2>
<div class="feature-grid">
<div class="feature-item">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
<span>NECA Company Code application</span>
</div>
<div class="feature-item">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
<span>Sponsoring CLEC agreement</span>
</div>
<div class="feature-item">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
<span>STIR/SHAKEN readiness</span>
</div>
<div class="feature-item">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
<span>LRN / number portability</span>
</div>
<div class="feature-item">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
<span>10 business day processing</span>
</div>
<div class="feature-item">
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
<span>NECA filing fee included</span>
</div>
</div>
</div>
<!-- Intake Form -->
<div class="card">
<h2>Carrier Information</h2>
<label class="label">Legal Entity Name *</label>
<input type="text" id="entity-name" placeholder="Acme Telecom LLC">
<label class="label">Contact Name *</label>
<input type="text" id="contact-name" placeholder="John Smith">
<label class="label">Contact Email *</label>
<input type="email" id="contact-email" placeholder="john@acmetelecom.com">
<label class="label">Contact Phone *</label>
<input type="tel" id="contact-phone" placeholder="(555) 123-4567">
<label class="label">FRN (FCC Registration Number)</label>
<input type="text" id="frn" placeholder="0012345678" maxlength="10">
<label class="label">Service Category *</label>
<select id="service-category">
<option value="IPES">IPES (Interconnected VoIP)</option>
<option value="CLEC">CLEC (Competitive Local Exchange Carrier)</option>
<option value="Non-Incumbent LEC">Non-Incumbent LEC</option>
<option value="ISP">ISP (Internet Service Provider)</option>
<option value="Reseller">Reseller</option>
<option value="Other">Other</option>
</select>
<label class="label">Operating States * <span style="font-weight:400;color:#9ca3af">(select all that apply)</span></label>
<div style="margin-bottom:.3rem">
<label style="font-size:.78rem;cursor:pointer">
<input type="checkbox" id="select-all-states" style="accent-color:#1e3a5f"> Select all states
</label>
</div>
<div class="state-picker" id="state-picker"></div>
<div class="tandem-box" id="tandem-box">
<h3>Sponsoring CLEC / Tandem Agreement</h3>
<p style="font-size:.82rem;color:#78350f;margin-bottom:.5rem">
An OCN requires a sponsoring CLEC agreement for numbering authority and interconnection. If you do not have an existing tandem/interconnection agreement, we will arrange one as part of this service.
</p>
<label style="font-size:.82rem;cursor:pointer">
<input type="checkbox" id="has-tandem"> I already have an existing tandem/interconnection agreement
</label>
</div>
<div style="margin-top:1rem;padding:.6rem;background:#f8fafc;border-radius:8px;border:1px solid #e2e8f0">
<div style="display:flex;justify-content:space-between;font-size:.88rem;padding:.15rem 0">
<span>OCN Registration</span><span style="font-weight:600">$650</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:.88rem;padding:.15rem 0" id="tandem-line">
<span>Sponsoring CLEC Agreement</span><span style="font-weight:600">$2,000</span>
</div>
<div style="display:flex;justify-content:space-between;font-size:1rem;font-weight:700;padding:.4rem 0;border-top:1px solid #e2e8f0;margin-top:.3rem">
<span>Total</span><span id="total-price">$2,650</span>
</div>
</div>
<div style="margin-top:1rem;text-align:right">
<button class="btn btn-primary" id="btn-pay">Continue to Payment</button>
</div>
<p class="err" id="form-error"></p>
</div>
</div>
<script>
(function() {
var STATES = [
['AL','Alabama'],['AK','Alaska'],['AZ','Arizona'],['AR','Arkansas'],['CA','California'],
['CO','Colorado'],['CT','Connecticut'],['DE','Delaware'],['DC','District of Columbia'],['FL','Florida'],
['GA','Georgia'],['HI','Hawaii'],['ID','Idaho'],['IL','Illinois'],['IN','Indiana'],
['IA','Iowa'],['KS','Kansas'],['KY','Kentucky'],['LA','Louisiana'],['ME','Maine'],
['MD','Maryland'],['MA','Massachusetts'],['MI','Michigan'],['MN','Minnesota'],['MS','Mississippi'],
['MO','Missouri'],['MT','Montana'],['NE','Nebraska'],['NV','Nevada'],['NH','New Hampshire'],
['NJ','New Jersey'],['NM','New Mexico'],['NY','New York'],['NC','North Carolina'],['ND','North Dakota'],
['OH','Ohio'],['OK','Oklahoma'],['OR','Oregon'],['PA','Pennsylvania'],['RI','Rhode Island'],
['SC','South Carolina'],['SD','South Dakota'],['TN','Tennessee'],['TX','Texas'],['UT','Utah'],
['VT','Vermont'],['VA','Virginia'],['WA','Washington'],['WV','West Virginia'],['WI','Wisconsin'],['WY','Wyoming']
];
var API = window.__PW_API;
var selectedStates = new Set();
var picker = document.getElementById('state-picker');
// Render state chips
STATES.forEach(function(s) {
var chip = document.createElement('label');
chip.className = 'state-chip';
chip.innerHTML = '<input type="checkbox" value="'+s[0]+'"> '+s[0]+' '+s[1];
chip.querySelector('input').addEventListener('change', function(e) {
if (e.target.checked) { selectedStates.add(s[0]); chip.classList.add('selected'); }
else { selectedStates.delete(s[0]); chip.classList.remove('selected'); }
});
picker.appendChild(chip);
});
// Select all
document.getElementById('select-all-states').addEventListener('change', function() {
var checked = this.checked;
picker.querySelectorAll('input').forEach(function(cb) {
cb.checked = checked;
var code = cb.value;
if (checked) { selectedStates.add(code); cb.closest('.state-chip').classList.add('selected'); }
else { selectedStates.delete(code); cb.closest('.state-chip').classList.remove('selected'); }
});
});
// Tandem toggle — adjust price
document.getElementById('has-tandem').addEventListener('change', function() {
var line = document.getElementById('tandem-line');
var total = document.getElementById('total-price');
if (this.checked) {
line.style.textDecoration = 'line-through';
line.style.opacity = '0.5';
total.textContent = '$650';
} else {
line.style.textDecoration = '';
line.style.opacity = '';
total.textContent = '$2,650';
}
});
// Checkout
document.getElementById('btn-pay').addEventListener('click', async function() {
var btn = this;
var errEl = document.getElementById('form-error');
errEl.style.display = 'none';
var entityName = document.getElementById('entity-name').value.trim();
var contactName = document.getElementById('contact-name').value.trim();
var contactEmail = document.getElementById('contact-email').value.trim();
var contactPhone = document.getElementById('contact-phone').value.trim();
if (!entityName || !contactName || !contactEmail || !contactPhone) {
errEl.textContent = 'Please fill in all required fields (entity name, contact name, email, phone).';
errEl.style.display = 'block';
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(contactEmail)) {
errEl.textContent = 'Please enter a valid email address.';
errEl.style.display = 'block';
return;
}
if (selectedStates.size === 0) {
errEl.textContent = 'Please select at least one operating state.';
errEl.style.display = 'block';
return;
}
btn.disabled = true;
btn.textContent = 'Creating order...';
var hasTandem = document.getElementById('has-tandem').checked;
var orderBody = {
service_slug: 'ocn-registration',
customer_name: contactName,
customer_email: contactEmail,
customer_phone: contactPhone,
intake_data: {
entity_legal_name: entityName,
contact_name: contactName,
contact_email: contactEmail,
contact_phone: contactPhone,
frn: document.getElementById('frn').value.trim() || undefined,
service_category: document.getElementById('service-category').value,
operating_states: Array.from(selectedStates),
has_existing_tandem: hasTandem,
},
};
try {
// Step 1: Create compliance order
var orderResp = await fetch(API + '/api/v1/compliance-orders', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(orderBody),
});
var orderData = await orderResp.json();
if (!orderResp.ok) throw new Error(orderData.error || 'Order creation failed');
btn.textContent = 'Redirecting to payment...';
// Step 2: Create Stripe session
var sessionResp = await fetch(API + '/api/v1/checkout/create-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
order_id: orderData.id,
order_type: 'compliance',
payment_method: 'card',
}),
});
var sessionData = await sessionResp.json();
if (!sessionResp.ok) throw new Error(sessionData.error || 'Checkout failed');
if (sessionData.checkout_url) {
window.location.href = sessionData.checkout_url;
} else {
throw new Error('No checkout URL returned');
}
} catch (err) {
errEl.textContent = err.message || 'Something went wrong. Please try again.';
errEl.style.display = 'block';
btn.disabled = false;
btn.textContent = 'Continue to Payment';
}
});
})();
</script>
</body>
</html>