new-site/site/public/order/fcc-carrier-registration/index.html
justin 618fafe1d5 order: payment-first express checkout + fix dead Tawk chat widget
Conversion fix for the checkout drop-off (54 sessions reached an /order/ page
over 3 days, 0 advanced to payment). Root cause was friction, not a bug: every
order page dropped a cold email-click straight into a 28-field intake Wizard
before showing any payment option.

- New ExpressCheckout.astro: payment-first entry. Shows price + the minimal
  fields the API needs (prefilled from public records: ?dot= FMCSA census for
  trucking, ?npi= NPPES lookup for healthcare) + Continue to payment. Creates a
  single-service batch-of-one (POST /compliance-orders/batch, which does NOT
  gate Stripe on intake_data_validated) then create-session -> Stripe. Full
  intake is collected AFTER payment via the per-service 'Complete Your Intake
  Form' email the webhook already sends (links to /order/<slug>?order=CO-xxx,
  which re-enters the Wizard in paid-intake mode).

- New OrderFlow.astro: single source of truth replacing ~50 near-identical thin
  Wizard wrappers. Trucking + healthcare default to payment-first (express on
  top, marketing hero moved BELOW the CTA). Telecom + corporate keep Wizard-first
  (rich pre-payment FCC/499 intake, no public-records prefill). Paid-intake
  re-entry (?order=/?token=) always renders the full Wizard.

- Rewrote all 50 /order/*.astro pages to use OrderFlow (foreign-qualification
  keeps its multi-state toggle via slotted content).

- Fixed the dead Tawk.to live-chat widget site-wide: the snippet set an invalid
  crossorigin='*' attribute, forcing the browser into anonymous CORS mode and
  blocking the script (0 chat requests fired anywhere). Removed it to match
  Tawk's official snippet (footer partial + 73 static public/*.html files).

Verified: build clean; express on top with hero below; ?dot=/?npi= prefill;
paid-intake re-entry swaps to Wizard; telecom stays wizard-first; batch-of-one
-> live Stripe URL; both POST endpoints allow the prod origin via CORS.
2026-06-25 11:32:48 -05:00

1086 lines
84 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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="Register your telecom carrier or ISP with the FCC — CORES/FRN, Form 499, state PUC, RMD, CPNI, CALEA. Optional business formation included.">
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<title>FCC Carrier / ISP 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:740px;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],input[type=number]{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;font-family:inherit}
.btn-primary{background:#1e3a5f;color:#fff}.btn-primary:hover{background:#162e4d}
.btn-primary:disabled{opacity:.5;cursor:not-allowed}
.btn-back{background:#e5e7eb;color:#374151}
.hidden{display:none}
.step-dots{display:flex;gap:.75rem;margin-bottom:1.5rem}
.step-dot{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.72rem;font-weight:700;background:#e5e7eb;color:#6b7280}
.step-dot.active{background:#1e3a5f;color:#fff}
.step-dot.done{background:#22c55e;color:#fff}
.q-card{padding:.65rem .85rem;border:1.5px solid #d1d5db;border-radius:8px;background:#fff;cursor:pointer;transition:all .15s;text-align:left;font-family:inherit;font-size:.88rem;color:#374151;width:100%;display:block;margin-bottom:.4rem}
.q-card:hover{border-color:#1e3a5f;background:#f0f4f8}
.q-card.selected{border-color:#1e3a5f;background:#eff6ff;color:#1e3a5f;font-weight:600}
.q-card.selected::before{content:"\2713 "}
.q-label{font-weight:600;color:#1f2937;font-size:.95rem;margin-bottom:.3rem}
.q-hint{font-size:.82rem;color:#6b7280;margin-bottom:.6rem}
.info-box{padding:.6rem .85rem;background:#eff6ff;border:1px solid #bfdbfe;border-radius:8px;font-size:.82rem;color:#1e40af;margin:.75rem 0}
.warn-box{padding:.6rem .85rem;background:#fef3c7;border:1px solid #fbbf24;border-radius:8px;font-size:.82rem;color:#92400e;margin:.75rem 0}
.svc-row{display:flex;align-items:center;gap:.6rem;padding:.5rem .7rem;border:1px solid #e5e7eb;border-radius:8px;margin-bottom:.35rem;font-size:.85rem}
.svc-row input{accent-color:#1e3a5f;width:16px;height:16px;flex-shrink:0}
.svc-row .svc-name{flex:1;font-weight:600}
.svc-row .svc-price{color:#059669;font-weight:600;white-space:nowrap}
.svc-row .svc-inc{color:#9ca3af;font-size:.78rem}
.svc-row.disabled{opacity:.5}
.price-box{background:#f0f4f8;border:2px solid #1e3a5f;border-radius:10px;padding:1rem;text-align:center;margin:1rem 0}
.price-box .price{font-size:1.6rem;font-weight:800;color:#1e3a5f}
.price-box .price-detail{font-size:.78rem;color:#6b7280}
.entity-toggle{display:flex;gap:.5rem;margin-bottom:.75rem}
.entity-toggle button{flex:1;padding:.6rem;border:2px solid #d1d5db;border-radius:8px;background:#fff;cursor:pointer;font-weight:600;font-size:.88rem;font-family:inherit;color:#374151;transition:all .15s}
.entity-toggle button.active{border-color:#1e3a5f;background:#eff6ff;color:#1e3a5f}
.recommend-box{padding:.75rem;background:#f0fdf4;border:2px solid #22c55e;border-radius:8px;margin:.75rem 0;font-size:.85rem}
.recommend-box strong{color:#166534}
.err{color:#dc2626;font-size:.82rem;margin-top:.5rem;display:none}
</style>
<script defer src="/js/pw-bot-filter.js"></script><script defer src="https://analytics.performancewest.net/script.js" data-website-id="55250014-ee15-44ac-a1f6-81dabad3fe0f" data-before-send="umamiBeforeSend"></script><script defer src="/js/pw-analytics.js"></script></head>
<body>
<div class="wrap">
<h1>FCC Carrier / ISP Registration</h1>
<p class="subtitle">Start your telecom carrier or ISP from scratch. We handle all federal and state registrations based on the services you plan to offer.</p>
<div class="step-dots">
<div class="step-dot active" id="dot-1">1</div>
<div class="step-dot" id="dot-2">2</div>
<div class="step-dot" id="dot-3">3</div>
<div class="step-dot" id="dot-4">4</div>
<div class="step-dot" id="dot-5">5</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- STEP 1: Service Wizard -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<div id="step-1" class="card">
<h2>What services will you offer?</h2>
<p class="q-hint">Tell us what you plan to do and we'll determine exactly which registrations you need.</p>
<!-- Q1: Service types -->
<div id="q1">
<p class="q-label">What type of service will you offer?</p>
<p class="q-hint">Select all that apply.</p>
<label class="svc-row"><input type="checkbox" data-svc="voice"> <span class="svc-name">Voice / phone service</span> <span class="svc-inc">Interconnected VoIP, CLEC, IXC, or traditional telephony</span></label>
<label class="svc-row"><input type="checkbox" data-svc="broadband"> <span class="svc-name">Broadband internet service</span> <span class="svc-inc">ISP, fiber, fixed wireless, cable</span></label>
<div style="margin-top:.5rem;padding:.5rem .75rem;background:#fefce8;border:1px solid #fde68a;border-radius:8px;font-size:.82rem;color:#92400e;line-height:1.5">
<strong>Other service types?</strong> If you plan to offer non-interconnected VoIP (voice apps without phone numbers, like Microsoft Teams or Discord), satellite, paging, private radio, or other specialized services, <a href="/contact?subject=FCC+Registration+Inquiry" style="color:#1e3a5f;font-weight:600;text-decoration:underline">contact us for a custom assessment</a> — registration requirements vary significantly for these services.
</div>
</div>
<!-- Q1b: Customer type — separate section, only shown after checking voice/broadband -->
<div id="q1b" class="hidden" style="margin-top:1rem">
<p class="q-label">Who are your customers?</p>
<p class="q-hint">This affects which FCC filings are required. If your voice and broadband customers are different, select the option that covers both.</p>
<button type="button" class="q-card" data-custtype="retail">Retail — I sell directly to businesses or consumers</button>
<button type="button" class="q-card" data-custtype="wholesale">Wholesale — I sell to other carriers, resellers, or CLECs</button>
<button type="button" class="q-card" data-custtype="both">Both — I have retail end users and wholesale/carrier customers</button>
</div>
<!-- Q2: Voice delivery (shown if voice selected) -->
<div id="q2" class="hidden" style="margin-top:1rem">
<p class="q-label">How will you deliver voice service?</p>
<button type="button" class="q-card" data-voice="reseller">I'll resell service from a provider like Bandwidth, Telnyx, Twilio, or a UCaaS platform</button>
<button type="button" class="q-card" data-voice="own_switch">I'll run my own switching equipment (softswitch, SBC, PBX) and buy wholesale trunks</button>
<button type="button" class="q-card" data-voice="ucaas">I'll offer a hosted/UCaaS platform and interconnect directly with carriers</button>
<button type="button" class="q-card" data-voice="unsure">Not sure yet — I'm still planning</button>
</div>
<!-- Q3: Infrastructure needs (shown if own_switch or ucaas) -->
<div id="q3" class="hidden" style="margin-top:1rem">
<p class="q-label">Will you need any of these?</p>
<p class="q-hint">Check all that apply. These determine whether you need your own OCN and STIR/SHAKEN certificate.</p>
<label class="svc-row"><input type="checkbox" data-infra="lcr"> <span class="svc-name">Least-cost routing (LCR)</span> <span class="svc-inc">Route across multiple wholesale providers</span></label>
<label class="svc-row"><input type="checkbox" data-infra="own_dids"> <span class="svc-name">Your own phone numbers (DID blocks)</span> <span class="svc-inc">From number providers like Bandwidth, Inteliquent</span></label>
<label class="svc-row"><input type="checkbox" data-infra="interconnect"> <span class="svc-name">Direct carrier interconnection</span> <span class="svc-inc">SIP trunking you sell to other carriers</span></label>
<label class="svc-row"><input type="checkbox" data-infra="stir_sign"> <span class="svc-name">Sign outbound calls with your own identity</span> <span class="svc-inc">STIR/SHAKEN certificate — required if you originate calls</span></label>
<div id="stir-explain" class="info-box hidden">
<strong>Do you need a STIR/SHAKEN certificate?</strong><br>
Only if you <strong>originate</strong> calls (place them onto the network). As of June 2025, originating providers must sign all calls with their own certificate — they can no longer rely on upstream providers to sign on their behalf.<br><br>
If you only <strong>receive and terminate</strong> pre-signed calls from other carriers, you don't need a signing certificate. You'll verify incoming signatures (a software configuration, not a certificate purchase) and file your RMD as "partial implementation."
</div>
<div id="ocn-explain" class="info-box hidden">
<strong>Why do wholesale voice vendors require an OCN?</strong><br>
An Operating Company Number identifies your company in the telecom numbering system. Major voice providers like Bandwidth, Telnyx, Lumen, and Peerless require it to open a wholesale account, port numbers, and assign DID blocks. Without an OCN, you're limited to reseller-tier access.
</div>
</div>
<!-- Q4: Broadband type (shown if broadband selected) -->
<div id="q4" class="hidden" style="margin-top:1rem">
<p class="q-label">How will you provide internet service?</p>
<button type="button" class="q-card" data-bb="reseller">I resell another provider's internet (white-label fiber, cable resale)</button>
<button type="button" class="q-card" data-bb="facilities">I own or lease network infrastructure (fiber, fixed wireless, towers)</button>
<button type="button" class="q-card" data-bb="cloud">I provide cloud services only (SaaS, hosting) — no last-mile internet</button>
<div id="cloud-warn" class="warn-box hidden">
Based on your description, you may not need FCC carrier registration. Cloud and SaaS services are generally not regulated as telecommunications. <a href="/contact" style="font-weight:600;text-decoration:underline">Contact us for a free assessment.</a>
</div>
</div>
<!-- Q5: Customer geography (retail) / Operating scope (wholesale) -->
<div id="q5" class="hidden" style="margin-top:1rem">
<div id="q5-retail">
<p class="q-label">Where will your end-user customers be located?</p>
<p class="q-hint">Retail telecom providers must register with the state PUC in each state where they serve customers. State PUC registration is triggered by customer location — your service terminates in their state using local infrastructure, creating regulatory nexus regardless of where you're incorporated.</p>
<button type="button" class="q-card" data-states="1">One state only — all my customers will be in a single state</button>
<button type="button" class="q-card" data-states="few">A few states (25) — I know which states I'll serve initially</button>
<button type="button" class="q-card" data-states="nationwide">Nationwide — I'll accept customers from any US state</button>
<div id="state-puc-explain" class="hidden" style="margin-top:.5rem">
<div class="info-box">
<strong>State PUC Registration</strong><br>
About 27 states require full certification and tariff filing (CA, NY, PA, FL, IL, etc.), ~9 require registration only, and a handful (TX, UT, VA, WY) have minimal requirements. You don't need to register in all 50 states on day one — start with the states where you'll have customers, then expand as you grow. We handle state PUC filings at $399/state.
</div>
</div>
</div>
<div id="q5-wholesale" class="hidden">
<p class="q-label">Where will your carrier customers be located?</p>
<p class="q-hint">Wholesale carriers generally don't need state PUC registration — the retail carrier serving end users holds the state obligation, not you. Your FCC registrations (CORES, Form 499, RMD) cover you at the federal level.</p>
<button type="button" class="q-card" data-states="1">Primarily one region</button>
<button type="button" class="q-card" data-states="few">A few regions</button>
<button type="button" class="q-card" data-states="nationwide">Nationwide</button>
</div>
</div>
<!-- Q6: International services -->
<div id="q6" class="hidden" style="margin-top:1rem">
<p class="q-label">Will you offer international services?</p>
<p class="q-hint">Calls to/from other countries, international data transit, or serving customers outside the US.</p>
<button type="button" class="q-card" data-intl="yes">Yes — international calling, transit, or foreign customers</button>
<button type="button" class="q-card" data-intl="no">No — domestic US only</button>
<div id="intl-explain" class="hidden">
<div class="info-box">
<strong>International Section 214 Authorization</strong><br>
The FCC requires a separate International Section 214 license to provide telecommunications services between the US and foreign points. This applies to international voice termination, international toll-free, global SIP trunking, and international data transit. Domestic-only carriers do not need this.
</div>
<!-- CRTC block, default (general international) framing -->
<div id="crtc-general" style="margin-top:.6rem;padding:.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:10px">
<div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem">
<img src="/images/flags/canada.png" alt="Canada" style="width:24px;height:auto;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.15)">
<strong style="font-size:.9rem;color:#1e3a5f">Serving or terminating to Canada?</strong>
</div>
<p style="font-size:.82rem;color:#374151;margin-bottom:.5rem;line-height:1.5">
To terminate voice traffic to Canada, sell to Canadian customers, or operate Canadian infrastructure, you need a <strong>CRTC registration</strong> (Canada's FCC equivalent) — not a US International 214. Registered Canadian carriers get direct access to Canadian DIDs, local number portability, and CRTC interconnection, usually at lower termination rates than routing through a US-based international gateway.
</p>
<p style="font-size:.8rem;color:#6b7280;margin-bottom:.5rem">
Our Canada CRTC package includes BC incorporation, CRTC telecom registration, and a Canadian registered office — everything you need to operate as a licensed Canadian carrier.
</p>
<a href="/order/canada-crtc" target="_blank" style="display:inline-block;padding:.4rem 1rem;background:#1e3a5f;color:#fff;font-weight:600;font-size:.82rem;border-radius:6px;text-decoration:none">Canada CRTC Registration ($3,899) &rarr;</a>
</div>
<!-- CRTC block, A-Z wholesale framing (shown when wholesale-only + international voice) -->
<div id="crtc-az" class="hidden" style="margin-top:.6rem;padding:.85rem;background:linear-gradient(135deg,#eff6ff,#f0f9ff);border:2px solid #1e3a5f;border-radius:10px">
<div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.4rem">
<img src="/images/flags/canada.png" alt="Canada" style="width:24px;height:auto;border-radius:3px;box-shadow:0 1px 2px rgba(0,0,0,.15)">
<strong style="font-size:.92rem;color:#1e3a5f">Doing A-Z wholesale? Register as a Canadian carrier instead.</strong>
</div>
<p style="font-size:.82rem;color:#1e3a5f;margin-bottom:.5rem;line-height:1.5">
For an A-Z wholesale operation, a US-based International 214 just makes you another international gateway buying Canadian minutes at markup. A <strong>CRTC registration</strong> makes you a Canadian carrier in your own right — direct access to Canadian DID inventory, local number portability, and CRTC interconnection, with termination rates wholesalers actually compete on.
</p>
<p style="font-size:.8rem;color:#1e40af;margin-bottom:.5rem;line-height:1.5">
Most A-Z wholesalers expanding north do this as a registered domestic reseller — no Canadian citizenship required. Our package is fully turnkey: BC incorporation, CRTC telecom registration, and a Canadian registered office.
</p>
<a href="/order/canada-crtc" target="_blank" style="display:inline-block;padding:.45rem 1.1rem;background:#1e3a5f;color:#fff;font-weight:600;font-size:.82rem;border-radius:6px;text-decoration:none">Canada CRTC Registration ($3,899) &rarr;</a>
</div>
</div>
</div>
<div style="margin-top:1rem;text-align:right">
<button class="btn btn-primary" id="btn-next-1">See What You Need &rarr;</button>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- STEP 2: Registration Checklist -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<div id="step-2" class="card hidden">
<h2>Your Registration Package</h2>
<p class="q-hint">Based on your answers, here's what you need. You can add or remove services.</p>
<p style="font-size:.78rem;font-weight:600;color:#059669;text-transform:uppercase;margin-bottom:.5rem">Included in your $1,299 package</p>
<div id="base-services"></div>
<p style="font-size:.78rem;font-weight:600;color:#d97706;text-transform:uppercase;margin:.75rem 0 .5rem">Optional add-ons (additional cost)</p>
<div id="addon-services"></div>
<div class="price-box">
<div class="price" id="total-price">$1,299</div>
<div class="price-detail" id="price-detail">Base registration package</div>
</div>
<div style="display:flex;gap:.5rem;justify-content:flex-end">
<button class="btn btn-back" id="btn-back-2">&larr; Back</button>
<button class="btn btn-primary" id="btn-next-2">Choose Entity &rarr;</button>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- STEP 3: Entity Choice -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<div id="step-3" class="card hidden">
<h2>Your Business Entity</h2>
<p class="q-hint">We'll file registrations under this entity. You can use an existing one or we'll form a new one.</p>
<div class="entity-toggle">
<button type="button" id="entity-existing" class="active">Use Existing Entity</button>
<button type="button" id="entity-new">Form a New Entity</button>
</div>
<!-- Existing entity search -->
<div id="entity-existing-form">
<label class="label">Search by FRN or company name</label>
<input type="text" id="entity-search" placeholder="e.g. 0012345678 or Acme Telecom LLC">
<div id="entity-search-results" style="margin-top:.5rem"></div>
<div id="entity-details" class="hidden" style="margin-top:.75rem;padding:.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px">
<label class="label">Legal Name</label>
<input type="text" id="ex-legal-name">
<label class="label">EIN / TIN</label>
<input type="text" id="ex-ein" placeholder="12-3456789 or 123-45-6789">
<label class="label">State of Formation</label>
<input type="text" id="ex-formation-state" maxlength="2" placeholder="WY">
</div>
</div>
<!-- New formation -->
<div id="entity-new-form" class="hidden">
<!-- Nexus guidance questions -->
<div id="formation-guide">
<p class="q-label">Where do you live / primary office?</p>
<select id="home-state" style="margin-bottom:.5rem">
<option value="">Select state...</option>
</select>
<p class="q-label">Do you have employees or a physical office there?</p>
<div style="display:flex;gap:.5rem;margin-bottom:.5rem">
<button type="button" class="q-card" data-nexus="yes" style="flex:1">Yes</button>
<button type="button" class="q-card" data-nexus="no" style="flex:1">No — remote/virtual</button>
</div>
<div id="size-q" class="hidden">
<p class="q-label">Expected first-year size?</p>
<button type="button" class="q-card" data-size="small">Small (&lt; $50K, 1-2 people)</button>
<button type="button" class="q-card" data-size="medium">Medium ($50K-$500K, small team)</button>
<button type="button" class="q-card" data-size="large">Large (&gt; $500K, multiple states)</button>
</div>
<div id="state-recommend" class="recommend-box hidden"></div>
</div>
<label class="label">Formation State</label>
<select id="new-formation-state"></select>
<label class="label">Entity Type</label>
<select id="new-entity-type">
<option value="llc">LLC (recommended for most carriers)</option>
<option value="corporation">Corporation</option>
</select>
<label class="label">Entity Name (leave blank for numbered)</label>
<input type="text" id="new-entity-name" placeholder="e.g. Acme Telecom LLC">
<div id="formation-fee-display" style="margin-top:.5rem;font-size:.85rem;color:#6b7280"></div>
</div>
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
<button class="btn btn-back" id="btn-back-3">&larr; Back</button>
<button class="btn btn-primary" id="btn-next-3">Contact Info &rarr;</button>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- STEP 4: Contact & Officer -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<div id="step-4" class="card hidden">
<h2>Contact & Officer Information</h2>
<p class="q-hint">This information is used on all FCC filings and state registrations.</p>
<label class="label">Contact Name *</label>
<input type="text" id="contact-name">
<label class="label">Contact Email *</label>
<input type="email" id="contact-email">
<label class="label">Contact Phone *</label>
<input type="tel" id="contact-phone">
<label class="label">Title</label>
<input type="text" id="contact-title" value="Chief Executive Officer">
<label class="label" style="margin-top:1rem">Business Address *</label>
<input type="text" id="addr-street" placeholder="Street address">
<div style="display:flex;gap:.5rem;margin-top:.3rem">
<input type="text" id="addr-city" placeholder="City" style="flex:2">
<input type="text" id="addr-state" placeholder="ST" maxlength="2" style="flex:.5">
<input type="text" id="addr-zip" placeholder="ZIP" style="flex:1">
</div>
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
<button class="btn btn-back" id="btn-back-4">&larr; Back</button>
<button class="btn btn-primary" id="btn-next-4">Review &rarr;</button>
</div>
</div>
<!-- ═══════════════════════════════════════════════════════════════ -->
<!-- STEP 5: Review & Payment -->
<!-- ═══════════════════════════════════════════════════════════════ -->
<div id="step-5" class="card hidden">
<h2>Review & Checkout</h2>
<div id="review-content"></div>
<!-- Referral / discount code -->
<div style="margin-top:.75rem;padding:.75rem;border:1px dashed #d1d5db;border-radius:8px">
<label class="label" for="discount-code" style="margin-top:0">Referral or discount code</label>
<div style="display:flex;gap:.5rem">
<input type="text" id="discount-code" placeholder="e.g. REF-JAYK05" maxlength="40" style="flex:1;text-transform:uppercase">
<button type="button" class="btn btn-back" id="btn-apply-discount" style="white-space:nowrap">Apply</button>
</div>
<p id="discount-status" style="font-size:.8rem;margin-top:.4rem;display:none"></p>
</div>
<label style="display:flex;align-items:flex-start;gap:.5rem;padding:.65rem;margin-top:.75rem;border:1px solid #e5e7eb;border-radius:8px;cursor:pointer;font-size:.75rem;color:#6b7280;line-height:1.5">
<input type="checkbox" id="engage-check" required style="margin-top:2px;accent-color:#1e3a5f">
<span>I authorize Performance West Inc. to prepare and submit regulatory filings on my behalf as described above. I understand Performance West provides compliance consulting services, not legal advice or legal representation. I confirm the information I provide is accurate to the best of my knowledge. <a href="/terms" target="_blank" style="color:#1e3a5f;text-decoration:underline">Terms of Service</a></span>
</label>
<div style="margin-top:.75rem">
<label class="label">Payment Method</label>
<div style="display:flex;flex-direction:column;gap:.3rem">
<label class="svc-row"><input type="radio" name="pay" value="ach" checked> <span class="svc-name">ACH Bank Transfer</span> <span style="color:#059669;font-size:.78rem">No fee</span></label>
<label class="svc-row"><input type="radio" name="pay" value="card"> <span class="svc-name">Credit / Debit Card</span> <span style="color:#9ca3af;font-size:.78rem">+3%</span></label>
<label class="svc-row"><input type="radio" name="pay" value="paypal"> <span class="svc-name">PayPal</span> <span style="color:#9ca3af;font-size:.78rem">+3%</span></label>
<label class="svc-row"><input type="radio" name="pay" value="crypto"> <span class="svc-name">Cryptocurrency</span> <span style="color:#059669;font-size:.78rem">No fee</span></label>
</div>
</div>
<div style="margin-top:1rem;display:flex;gap:.5rem;justify-content:flex-end">
<button class="btn btn-back" id="btn-back-5">&larr; Back</button>
<button class="btn btn-primary" id="btn-pay">Continue to Payment</button>
</div>
<p class="err" id="checkout-err"></p>
</div>
</div>
<script>
(function() {
var API = window.__PW_API;
// ── State data ──
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']
];
// Populate state dropdowns
['home-state','new-formation-state'].forEach(function(id) {
var sel = document.getElementById(id);
STATES.forEach(function(s) {
var opt = document.createElement('option');
opt.value = s[0]; opt.textContent = s[0] + ' — ' + s[1];
sel.appendChild(opt);
});
});
// ── Wizard state ──
var wizard = {
serviceTypes: [], customerType: 'retail', international: false,
voiceDelivery: '', infraNeeds: [],
broadbandType: '', operatingStates: '',
// Derived
needsRmd: false, needsCpni: false, needsCalea: false, needsBdc: false,
needsOcn: false, needsStirShaken: false, needs214: false,
// Entity
entitySource: 'existing', formationState: '', entityType: 'llc',
entityName: '', ein: '', frn: '', legalName: '',
// Contact
contactName: '', contactEmail: '', contactPhone: '', contactTitle: 'Chief Executive Officer',
addrStreet: '', addrCity: '', addrState: '', addrZip: '',
// State PUC
pucStates: [],
// Pricing
baseFee: 129900, formationFee: 0, stateFee: 0, pucFee: 0, addonFee: 0,
// Discount / referral
discountCode: '', discountCents: 0,
};
// ── Step navigation ──
function showStep(n) {
[1,2,3,4,5].forEach(function(i) {
document.getElementById('step-' + i).classList.toggle('hidden', i !== n);
var dot = document.getElementById('dot-' + i);
dot.className = 'step-dot' + (i < n ? ' done' : i === n ? ' active' : '');
});
document.querySelector('.wrap').scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// ── Q1: Service types ──
function updateVoiceQuestions() {
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
var isWholesaleOnly = wizard.customerType === 'wholesale';
// Wholesale voice carriers run their own switching by definition — skip Q2, show Q3 directly
if (hasVoice && isWholesaleOnly) {
document.getElementById('q2').classList.add('hidden');
document.getElementById('q3').classList.remove('hidden');
wizard.voiceDelivery = 'own_switch'; // implied for wholesale
} else if (hasVoice) {
document.getElementById('q2').classList.remove('hidden');
// Q3 visibility still controlled by Q2 answer
} else {
document.getElementById('q2').classList.add('hidden');
document.getElementById('q3').classList.add('hidden');
}
}
document.querySelectorAll('[data-svc]').forEach(function(cb) {
cb.addEventListener('change', function() {
wizard.serviceTypes = Array.from(document.querySelectorAll('[data-svc]:checked')).map(function(c) { return c.dataset.svc; });
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
var hasBroadband = wizard.serviceTypes.indexOf('broadband') >= 0;
document.getElementById('q1b').classList.toggle('hidden', !hasVoice && !hasBroadband);
updateVoiceQuestions();
document.getElementById('q4').classList.toggle('hidden', !hasBroadband);
// Q5 shown only after Q1b is answered (customer type determines which variant)
});
});
// ── Q1b: Customer type ──
document.querySelectorAll('[data-custtype]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-custtype]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
wizard.customerType = btn.dataset.custtype;
// Re-evaluate whether to show Q2 or skip to Q3
updateVoiceQuestions();
// Show Q5 with the correct variant for customer type
var isWholesaleOnly = wizard.customerType === 'wholesale';
document.getElementById('q5').classList.remove('hidden');
document.getElementById('q5-retail').classList.toggle('hidden', isWholesaleOnly);
document.getElementById('q5-wholesale').classList.toggle('hidden', !isWholesaleOnly);
});
});
// ── Q2: Voice delivery ──
document.querySelectorAll('[data-voice]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-voice]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
wizard.voiceDelivery = btn.dataset.voice;
var showQ3 = wizard.voiceDelivery === 'own_switch' || wizard.voiceDelivery === 'ucaas';
document.getElementById('q3').classList.toggle('hidden', !showQ3);
});
});
// ── Q3: Infrastructure needs ──
document.querySelectorAll('[data-infra]').forEach(function(cb) {
cb.addEventListener('change', function() {
wizard.infraNeeds = Array.from(document.querySelectorAll('[data-infra]:checked')).map(function(c) { return c.dataset.infra; });
var needsOcn = wizard.infraNeeds.indexOf('lcr') >= 0 || wizard.infraNeeds.indexOf('own_dids') >= 0 ||
wizard.infraNeeds.indexOf('interconnect') >= 0 || wizard.infraNeeds.indexOf('stir_sign') >= 0;
document.getElementById('ocn-explain').classList.toggle('hidden', !needsOcn);
// Show STIR/SHAKEN info when any infra option is checked (helps decide if they need signing)
document.getElementById('stir-explain').classList.toggle('hidden', wizard.infraNeeds.length === 0);
});
});
// ── Q4: Broadband type ──
document.querySelectorAll('[data-bb]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-bb]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
wizard.broadbandType = btn.dataset.bb;
document.getElementById('cloud-warn').classList.toggle('hidden', wizard.broadbandType !== 'cloud');
});
});
// ── Q5: Customer geography ──
document.querySelectorAll('[data-states]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-states]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
wizard.operatingStates = btn.dataset.states;
// Show PUC info for multi-state or nationwide
document.getElementById('state-puc-explain').classList.toggle('hidden', btn.dataset.states === '1');
document.getElementById('q6').classList.remove('hidden');
});
});
// ── Q6: International services ──
function updateCrtcFraming() {
// A-Z wholesale profile: wholesale-only carrier offering international voice.
// For these carriers CRTC registration is the natural offering, not an upsell —
// they'd otherwise just be a US gateway reselling Canadian minutes at markup.
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
var wholesaleOnly = wizard.customerType === 'wholesale';
var isAzWholesale = wizard.international && hasVoice && wholesaleOnly;
var gen = document.getElementById('crtc-general');
var az = document.getElementById('crtc-az');
if (gen && az) {
gen.classList.toggle('hidden', isAzWholesale);
az.classList.toggle('hidden', !isAzWholesale);
}
}
document.querySelectorAll('[data-intl]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-intl]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
wizard.international = btn.dataset.intl === 'yes';
document.getElementById('intl-explain').classList.toggle('hidden', !wizard.international);
updateCrtcFraming();
});
});
// ── Step 1 → 2: Derive registrations ──
document.getElementById('btn-next-1').addEventListener('click', function() {
if (wizard.serviceTypes.length === 0) { alert('Please select at least one service type.'); return; }
if (!document.querySelector('[data-custtype].selected')) { alert('Please select who your customers are (retail, wholesale, or both).'); return; }
if (wizard.serviceTypes.indexOf('voice') >= 0 && wizard.customerType !== 'wholesale' && !wizard.voiceDelivery) { alert('Please select how you will deliver voice service.'); return; }
var hasVoice = wizard.serviceTypes.indexOf('voice') >= 0;
var hasBroadband = wizard.serviceTypes.indexOf('broadband') >= 0;
var hasRetail = wizard.customerType === 'retail' || wizard.customerType === 'both';
var wholesaleOnly = wizard.customerType === 'wholesale';
// Voice carriers always need RMD + CPNI regardless of retail/wholesale
wizard.needsRmd = hasVoice;
wizard.needsCpni = hasVoice;
// CALEA: carriers serving end users (retail/both). Wholesale-only "carrier's carrier"
// providers are exempt as "interconnecting carriers" under CALEA.
// Also required for facilities-based broadband serving end users.
wizard.needsCalea = !wholesaleOnly && (hasVoice || (hasBroadband && wizard.broadbandType === 'facilities'));
// BDC only for broadband providers with retail end users
wizard.needsBdc = hasBroadband && hasRetail;
// International Section 214 authorization
wizard.needs214 = wizard.international;
wizard.needsOcn = wizard.infraNeeds.indexOf('lcr') >= 0 || wizard.infraNeeds.indexOf('own_dids') >= 0 ||
wizard.infraNeeds.indexOf('interconnect') >= 0 || wizard.infraNeeds.indexOf('stir_sign') >= 0;
wizard.needsStirShaken = wizard.infraNeeds.indexOf('stir_sign') >= 0 || wizard.needsOcn;
buildChecklist();
showStep(2);
});
// ── Build registration checklist (Step 2) ──
function buildChecklist() {
var base = document.getElementById('base-services');
var addons = document.getElementById('addon-services');
base.innerHTML = ''; addons.innerHTML = '';
function baseSvc(name, desc, relevant) {
var row = document.createElement('div');
row.className = 'svc-row' + (relevant ? '' : ' disabled');
row.innerHTML = '<span style="color:#059669;font-size:1rem;flex-shrink:0">\u2713</span> ' +
'<span class="svc-name">' + name + '</span> ' +
'<span class="svc-inc">' + (relevant ? 'Included' : 'Included (not needed for your service type)') + '</span>';
base.appendChild(row);
}
function addonRow(name, price, key, preChecked) {
var row = document.createElement('label');
row.className = 'svc-row';
row.innerHTML = '<input type="checkbox" data-reg="' + key + '" ' + (preChecked ? 'checked' : '') + '> ' +
'<span class="svc-name">' + name + '</span> ' +
'<span class="svc-price">+$' + (price / 100).toLocaleString() + '</span>';
row.querySelector('input').addEventListener('change', updatePrice);
addons.appendChild(row);
}
// All included in base $1,299
baseSvc('CORES / FRN Registration', 'FCC Registration Number', true);
baseSvc('Form 499 Initial Registration', 'USAC Universal Service Fund enrollment', true);
baseSvc('D.C. Registered Agent (Annual)', 'FCC process-of-service address', true);
baseSvc('RMD Registration', 'Robocall Mitigation Database', wizard.needsRmd);
baseSvc('CPNI Annual Certification', 'Customer data protection compliance', wizard.needsCpni);
baseSvc('CALEA SSI Plan', 'Lawful intercept compliance plan', wizard.needsCalea);
baseSvc('BDC Filing', 'Broadband Data Collection', wizard.needsBdc);
// Add-ons (extra cost)
addonRow('STIR/SHAKEN Implementation', 49900, 'stir_shaken', wizard.needsStirShaken);
addonRow('NECA OCN Registration', 265000, 'ocn', wizard.needsOcn);
addonRow('International Section 214 Authorization', 149900, 'intl_214', wizard.needs214);
addonRow('State PUC Registration (per state)', 39900, 'state_puc', false);
// State PUC picker — appears when state_puc checkbox is checked
var pucPicker = document.createElement('div');
pucPicker.id = 'puc-state-picker';
pucPicker.className = 'hidden';
pucPicker.style.cssText = 'margin:0.5rem 0 0.5rem 1.5rem;padding:0.5rem 0.75rem;background:#f8fafc;border:1px solid #e2e8f0;border-radius:8px';
pucPicker.innerHTML = '<p style="font-size:.8rem;font-weight:600;color:#374151;margin-bottom:.35rem">Select states where you will have customers:</p>' +
'<div id="puc-state-grid" style="display:grid;grid-template-columns:repeat(4,1fr);gap:2px;max-height:200px;overflow-y:auto"></div>' +
'<p id="puc-count" style="font-size:.78rem;color:#6b7280;margin-top:.3rem">0 states selected ($0)</p>';
addons.appendChild(pucPicker);
var stateGrid = document.getElementById('puc-state-grid');
STATES.forEach(function(s) {
var lbl = document.createElement('label');
lbl.style.cssText = 'display:flex;align-items:center;gap:3px;font-size:.78rem;cursor:pointer;padding:2px 4px;border-radius:4px';
lbl.innerHTML = '<input type="checkbox" data-puc-state="' + s[0] + '" style="accent-color:#1e3a5f"> ' + s[0];
lbl.title = s[1];
stateGrid.appendChild(lbl);
});
// Toggle picker visibility when state_puc checkbox changes
var pucCheckbox = addons.querySelector('[data-reg="state_puc"]');
if (pucCheckbox) {
pucCheckbox.addEventListener('change', function() {
pucPicker.classList.toggle('hidden', !this.checked);
if (!this.checked) {
// Uncheck all states
document.querySelectorAll('[data-puc-state]').forEach(function(cb) { cb.checked = false; });
wizard.pucStates = [];
}
updatePrice();
});
}
// Update count + price when states are checked
stateGrid.addEventListener('change', function() {
wizard.pucStates = Array.from(document.querySelectorAll('[data-puc-state]:checked')).map(function(cb) { return cb.dataset.pucState; });
document.getElementById('puc-count').textContent = wizard.pucStates.length + ' state' + (wizard.pucStates.length !== 1 ? 's' : '') + ' selected ($' + (wizard.pucStates.length * 399).toLocaleString() + ')';
updatePrice();
});
updatePrice();
}
function updatePrice() {
var total = wizard.baseFee;
var details = ['Base: $1,299'];
// Add-on fees from checked checkboxes
document.querySelectorAll('[data-reg]').forEach(function(cb) {
if (!cb.checked) return;
var key = cb.dataset.reg;
if (key === 'stir_shaken') { total += 49900; details.push('STIR/SHAKEN: +$499'); }
if (key === 'ocn') { total += 265000; details.push('OCN: +$2,650'); }
if (key === 'intl_214') { total += 149900; details.push('Intl 214: +$1,499'); }
if (key === 'state_puc') { var n = wizard.pucStates.length || 1; total += 39900 * n; details.push('State PUC: +$' + (399 * n).toLocaleString() + ' (' + n + ' state' + (n > 1 ? 's' : '') + ')'); }
});
// Formation fees (if new entity)
if (wizard.formationFee || wizard.stateFee) {
total += wizard.formationFee + wizard.stateFee;
details.push('Formation: +$' + ((wizard.formationFee + wizard.stateFee) / 100).toLocaleString());
}
document.getElementById('total-price').textContent = '$' + (total / 100).toLocaleString();
document.getElementById('price-detail').textContent = details.join(' · ');
}
// ── Step 2 → 3 ──
document.getElementById('btn-next-2').addEventListener('click', function() { showStep(3); });
document.getElementById('btn-back-2').addEventListener('click', function() { showStep(1); });
// ── Step 3: Entity toggle ──
document.getElementById('entity-existing').addEventListener('click', function() {
wizard.entitySource = 'existing';
this.classList.add('active');
document.getElementById('entity-new').classList.remove('active');
document.getElementById('entity-existing-form').classList.remove('hidden');
document.getElementById('entity-new-form').classList.add('hidden');
wizard.formationFee = 0; wizard.stateFee = 0;
updatePrice();
});
document.getElementById('entity-new').addEventListener('click', function() {
wizard.entitySource = 'new_formation';
this.classList.add('active');
document.getElementById('entity-existing').classList.remove('active');
document.getElementById('entity-new-form').classList.remove('hidden');
document.getElementById('entity-existing-form').classList.add('hidden');
});
// ── Formation state guidance ──
document.querySelectorAll('[data-nexus]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-nexus]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
if (btn.dataset.nexus === 'yes') {
var st = document.getElementById('home-state').value;
showRecommendation(st, 'Your home state (' + st + ') is recommended since you already have business presence there. This avoids foreign qualification fees.');
document.getElementById('size-q').classList.add('hidden');
} else {
document.getElementById('size-q').classList.remove('hidden');
}
});
});
document.querySelectorAll('[data-size]').forEach(function(btn) {
btn.addEventListener('click', function() {
document.querySelectorAll('[data-size]').forEach(function(b) { b.classList.remove('selected'); });
btn.classList.add('selected');
var home = document.getElementById('home-state').value;
if (btn.dataset.size === 'small') {
showRecommendation('WY', 'Wyoming is recommended for small operations — $100 formation fee, no state income tax, $60/yr annual report, strong privacy. You\'ll need foreign qualification if you expand to other states.');
} else if (btn.dataset.size === 'medium') {
showRecommendation(home || 'DE', (home ? home : 'Delaware') + ' is recommended. ' + (home ? 'Forming in your home state avoids foreign qualification fees.' : 'Delaware is investor-friendly with a specialized business court.'));
} else {
showRecommendation(home || 'WY', 'Forming in your home state is recommended for larger operations — you\'ll need foreign qualifications in other states regardless.');
}
});
});
function showRecommendation(state, reason) {
var box = document.getElementById('state-recommend');
box.innerHTML = '<strong>Recommended: ' + state + '</strong><br>' + reason +
'<br><button type="button" onclick="document.getElementById(\'new-formation-state\').value=\'' + state + '\';updateFormationFee()" style="margin-top:.5rem;font-size:.82rem;font-weight:600;color:#1e3a5f;background:none;border:none;cursor:pointer;text-decoration:underline">Use ' + state + '</button>';
box.classList.remove('hidden');
document.getElementById('new-formation-state').value = state;
updateFormationFee();
}
// ── Formation fee lookup ──
var stateFeesCache = null;
window.updateFormationFee = function() {
var state = document.getElementById('new-formation-state').value;
var type = document.getElementById('new-entity-type').value;
if (!state) return;
if (stateFeesCache) {
applyFormationFee(state, type);
} else {
fetch(API + '/api/v1/states').then(function(r) { return r.json(); }).then(function(d) {
stateFeesCache = d;
applyFormationFee(state, type);
}).catch(function() {});
}
};
function applyFormationFee(state, type) {
if (!stateFeesCache) return;
var fees = (stateFeesCache.states || stateFeesCache).find(function(s) { return s.state_code === state; });
if (!fees) return;
var fee = type === 'corporation' ? (fees.corp_formation_fee || fees.llc_formation_fee) : fees.llc_formation_fee;
wizard.stateFee = fee || 0;
wizard.formationFee = 2500; // $25 filing markup
document.getElementById('formation-fee-display').textContent =
'State filing fee: $' + ((wizard.stateFee) / 100).toLocaleString() + ' + $25 filing service = $' + ((wizard.stateFee + 2500) / 100).toLocaleString();
updatePrice();
}
document.getElementById('new-formation-state').addEventListener('change', function() { updateFormationFee(); });
document.getElementById('new-entity-type').addEventListener('change', function() { updateFormationFee(); });
// ── Step 3 → 4 ──
document.getElementById('btn-next-3').addEventListener('click', function() { showStep(4); });
document.getElementById('btn-back-3').addEventListener('click', function() { showStep(2); });
// ── Step 4 → 5 ──
document.getElementById('btn-next-4').addEventListener('click', function() {
var name = document.getElementById('contact-name').value.trim();
var email = document.getElementById('contact-email').value.trim();
var phone = document.getElementById('contact-phone').value.trim();
if (!name || !email || !phone) { alert('Please fill in contact name, email, and phone.'); return; }
wizard.contactName = name; wizard.contactEmail = email; wizard.contactPhone = phone;
wizard.contactTitle = document.getElementById('contact-title').value.trim();
wizard.addrStreet = document.getElementById('addr-street').value.trim();
wizard.addrCity = document.getElementById('addr-city').value.trim();
wizard.addrState = document.getElementById('addr-state').value.trim().toUpperCase();
wizard.addrZip = document.getElementById('addr-zip').value.trim();
buildReview();
showStep(5);
// Auto-validate a prefilled referral code (e.g. from ?code= link) on first arrival
var dcInput = document.getElementById('discount-code');
if (dcInput && dcInput.value.trim() && !wizard.appliedDiscount) {
document.getElementById('btn-apply-discount').click();
}
});
document.getElementById('btn-back-4').addEventListener('click', function() { showStep(3); });
document.getElementById('btn-back-5').addEventListener('click', function() { showStep(4); });
// ── Apply referral / discount code ──
document.getElementById('btn-apply-discount').addEventListener('click', async function() {
var btn = this;
var input = document.getElementById('discount-code');
var statusEl = document.getElementById('discount-status');
var code = input.value.trim().toUpperCase();
statusEl.style.display = 'block';
if (!code) {
// Clearing the code
wizard.appliedDiscount = null; wizard.discountCode = ''; wizard.discountCents = 0;
statusEl.style.color = '#6b7280'; statusEl.textContent = 'No code applied.';
buildReview();
return;
}
btn.disabled = true; var prev = btn.textContent; btn.textContent = 'Checking...';
statusEl.style.color = '#6b7280'; statusEl.textContent = 'Validating code...';
try {
var email = wizard.contactEmail || document.getElementById('contact-email').value.trim();
var url = API + '/api/v1/discount/' + encodeURIComponent(code) +
'?service=fcc_carrier_registration' + (email ? '&email=' + encodeURIComponent(email) : '');
var resp = await fetch(url);
var data = await resp.json();
if (!resp.ok || !data.valid) {
wizard.appliedDiscount = null; wizard.discountCode = ''; wizard.discountCents = 0;
statusEl.style.color = '#dc2626';
statusEl.textContent = data.error || 'Code is not valid.';
buildReview();
return;
}
wizard.appliedDiscount = { discount_type: data.discount_type, discount_value: data.discount_value };
wizard.discountCode = data.code || code;
buildReview(); // recomputes wizard.discountCents against discountable base
statusEl.style.color = '#059669';
var label = data.discount_type === 'percent' ? (data.discount_value + '% off service fees') : ('$' + (data.discount_value / 100).toLocaleString() + ' off service fees');
statusEl.textContent = 'Applied ' + wizard.discountCode + ' \u2014 ' + label + (wizard.discountCents > 0 ? ' (\u2212$' + (wizard.discountCents / 100).toLocaleString() + ')' : '') + '.';
} catch (err) {
statusEl.style.color = '#dc2626';
statusEl.textContent = 'Could not validate code. Please try again.';
} finally {
btn.disabled = false; btn.textContent = prev;
}
});
// ── Build review ──
function buildReview() {
var services = [];
document.querySelectorAll('[data-reg]:checked').forEach(function(cb) { services.push(cb.dataset.reg); });
var html = '<table style="width:100%;border-collapse:collapse;font-size:.85rem;margin-bottom:1rem">';
html += '<tr style="border-bottom:2px solid #e5e7eb"><th style="text-align:left;padding:.4rem">Item</th><th style="text-align:right;padding:.4rem">Price</th></tr>';
var included = ['CORES/FRN', 'Form 499 Initial', 'D.C. Registered Agent'];
if (wizard.needsRmd) included.push('RMD');
if (wizard.needsCpni) included.push('CPNI');
if (wizard.needsCalea) included.push('CALEA');
if (wizard.needsBdc) included.push('BDC');
var items = [
{ name: 'FCC Carrier Registration Package (' + included.join(', ') + ')', price: wizard.baseFee },
];
if (services.indexOf('stir_shaken') >= 0) items.push({ name: 'STIR/SHAKEN Implementation', price: 49900 });
if (services.indexOf('ocn') >= 0) items.push({ name: 'NECA OCN Registration', price: 265000 });
if (services.indexOf('intl_214') >= 0) items.push({ name: 'International Section 214 Authorization', price: 149900 });
if (services.indexOf('state_puc') >= 0) items.push({ name: 'State PUC Registration', price: 39900 });
if (wizard.entitySource === 'new_formation') {
items.push({ name: 'Business Formation (' + (document.getElementById('new-formation-state').value || '?') + ' ' + document.getElementById('new-entity-type').value.toUpperCase() + ')', price: wizard.formationFee + wizard.stateFee });
}
var total = 0;
var discountableBase = 0; // service fee + add-ons; excludes formation/state filing fees
items.forEach(function(item) {
total += item.price;
var isFormation = item.name.indexOf('Business Formation') === 0;
if (!isFormation) discountableBase += item.price;
html += '<tr style="border-bottom:1px solid #f3f4f6"><td style="padding:.35rem">' + item.name + '</td>';
html += '<td style="text-align:right;padding:.35rem">' + (item.price ? '$' + (item.price / 100).toLocaleString() : 'Included') + '</td></tr>';
});
// Recompute discount against the current discountable base (in case services changed)
if (wizard.appliedDiscount) {
var d = wizard.appliedDiscount;
wizard.discountCents = d.discount_type === 'percent'
? Math.round((discountableBase * d.discount_value) / 100)
: Math.min(d.discount_value, discountableBase);
} else {
wizard.discountCents = 0;
}
if (wizard.discountCents > 0) {
html += '<tr style="color:#059669"><td style="padding:.35rem">Discount (' + wizard.discountCode + ')</td>';
html += '<td style="text-align:right;padding:.35rem">-$' + (wizard.discountCents / 100).toLocaleString() + '</td></tr>';
total -= wizard.discountCents;
}
html += '<tr style="border-top:2px solid #e5e7eb;font-weight:700"><td style="padding:.5rem">Total</td><td style="text-align:right;padding:.5rem">$' + (total / 100).toLocaleString() + '</td></tr>';
html += '</table>';
html += '<div style="font-size:.85rem;color:#6b7280;margin-bottom:.5rem">';
html += '<strong>Entity:</strong> ' + (wizard.entitySource === 'existing' ? (document.getElementById('ex-legal-name').value || 'Existing entity') : 'New ' + document.getElementById('new-entity-type').value.toUpperCase() + ' in ' + (document.getElementById('new-formation-state').value || '?'));
html += '<br><strong>Contact:</strong> ' + wizard.contactName + ' (' + wizard.contactEmail + ')';
html += '</div>';
document.getElementById('review-content').innerHTML = html;
}
// ── Checkout ──
document.getElementById('btn-pay').addEventListener('click', async function() {
var btn = this;
var errEl = document.getElementById('checkout-err');
errEl.style.display = 'none';
if (!document.getElementById('engage-check').checked) { errEl.textContent = 'Please accept the authorization terms.'; errEl.style.display = 'block'; return; }
btn.disabled = true; btn.textContent = 'Creating order...';
var services = [];
document.querySelectorAll('[data-reg]:checked').forEach(function(cb) { services.push(cb.dataset.reg); });
var payMethod = document.querySelector('input[name=pay]:checked').value;
try {
var orderResp = await fetch(API + '/api/v1/fcc-carrier-registration', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
customer_name: wizard.contactName,
customer_email: wizard.contactEmail,
customer_phone: wizard.contactPhone,
entity_source: wizard.entitySource,
entity_legal_name: wizard.entitySource === 'existing' ? document.getElementById('ex-legal-name').value.trim() : document.getElementById('new-entity-name').value.trim(),
ein: wizard.entitySource === 'existing' ? document.getElementById('ex-ein').value.trim() : '',
formation_state: wizard.entitySource === 'existing' ? document.getElementById('ex-formation-state').value.trim() : document.getElementById('new-formation-state').value,
entity_type: wizard.entitySource === 'new_formation' ? document.getElementById('new-entity-type').value : '',
frn: wizard.frn || '',
contact_name: wizard.contactName,
contact_email: wizard.contactEmail,
contact_phone: wizard.contactPhone,
contact_title: wizard.contactTitle,
address_street: wizard.addrStreet,
address_city: wizard.addrCity,
address_state: wizard.addrState,
address_zip: wizard.addrZip,
service_wizard: {
service_types: wizard.serviceTypes,
customer_type: wizard.customerType,
international: wizard.international,
voice_delivery: wizard.voiceDelivery,
infra_needs: wizard.infraNeeds,
broadband_type: wizard.broadbandType,
operating_states: wizard.operatingStates,
puc_states: wizard.pucStates,
},
services: services,
discount_code: wizard.discountCode || '',
engagement_accepted: true,
}),
});
var orderData = await orderResp.json();
if (!orderResp.ok) throw new Error(orderData.error || 'Order creation failed');
btn.textContent = 'Redirecting to payment...';
var sessionResp = await fetch(API + '/api/v1/checkout/create-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ order_id: orderData.order_number, order_type: 'fcc_carrier_registration', payment_method: payMethod }),
});
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');
} catch (err) {
errEl.textContent = err.message || 'Something went wrong.';
errEl.style.display = 'block';
btn.disabled = false; btn.textContent = 'Continue to Payment';
}
});
// ── Entity search ──
var searchTimeout;
document.getElementById('entity-search').addEventListener('input', function() {
clearTimeout(searchTimeout);
var q = this.value.trim();
if (q.length < 3) return;
searchTimeout = setTimeout(function() {
var isDigits = /^\d+$/.test(q);
var params = isDigits && q.length === 10 ? 'frn=' + q : 'q=' + encodeURIComponent(q);
fetch(API + '/api/v1/fcc/search?' + params).then(function(r) { return r.json(); }).then(function(d) {
var results = d.results || [];
var container = document.getElementById('entity-search-results');
container.innerHTML = '';
if (results.length === 0) { container.innerHTML = '<p style="font-size:.82rem;color:#9ca3af">No results found.</p>'; return; }
results.forEach(function(r) {
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'q-card';
btn.innerHTML = '<strong>' + (r.business_name || 'Unknown') + '</strong>' +
(r.frn ? ' <span style="font-size:.78rem;color:#6b7280">FRN: ' + r.frn + '</span>' : '') +
(r.city ? ' <span style="font-size:.78rem;color:#9ca3af">' + r.city + ', ' + (r.state || '') + '</span>' : '');
btn.addEventListener('click', function() {
wizard.frn = r.frn || '';
document.getElementById('ex-legal-name').value = r.business_name || '';
document.getElementById('entity-details').classList.remove('hidden');
// Auto-fill from FCC lookup
if (r.frn) {
fetch(API + '/api/v1/fcc/lookup?frn=' + r.frn + '&quick=1').then(function(resp) { return resp.json(); }).then(function(fcc) {
if (fcc.cores) {
if (fcc.cores.address && !/LLC|Inc|Corp/i.test(fcc.cores.address)) wizard.addrStreet = fcc.cores.address;
if (fcc.cores.city) wizard.addrCity = fcc.cores.city;
if (fcc.cores.state) { wizard.addrState = fcc.cores.state; document.getElementById('ex-formation-state').value = fcc.cores.state; }
if (fcc.cores.zip) wizard.addrZip = fcc.cores.zip;
document.getElementById('addr-street').value = wizard.addrStreet;
document.getElementById('addr-city').value = wizard.addrCity;
document.getElementById('addr-state').value = wizard.addrState;
document.getElementById('addr-zip').value = wizard.addrZip;
}
if (fcc.rmd && fcc.rmd.contact_name) {
wizard.contactName = fcc.rmd.contact_name;
document.getElementById('contact-name').value = fcc.rmd.contact_name;
}
if (fcc.entity_name) document.getElementById('ex-legal-name').value = fcc.entity_name;
}).catch(function() {});
}
});
container.appendChild(btn);
});
}).catch(function() {});
}, 500);
});
// ── Prefill referral code from ?code= / ?ref= query param ──
(function() {
try {
var qs = new URLSearchParams(window.location.search);
var refCode = (qs.get('code') || qs.get('ref') || '').trim().toUpperCase();
if (refCode) {
var input = document.getElementById('discount-code');
if (input) {
input.value = refCode;
wizard.discountCode = refCode; // captured even before the user reaches step 5
}
}
} catch (e) {}
})();
})();
</script>
<!-- Tawk.to Live Chat --><script>var Tawk_API=Tawk_API||{}, Tawk_LoadStart=new Date();(function(){var s1=document.createElement("script"),s0=document.getElementsByTagName("script")[0];s1.async=true;s1.src="https://embed.tawk.to/69d5a9ca0d1c3f1c37998081/1jll9ufph";s1.charset="UTF-8";s0.parentNode.insertBefore(s1,s0);})();</script>
<script defer src="/js/pw-cookie-consent.js"></script><!-- Floating help button --><button type="button" id="support-fab" aria-label="Open support" class="fixed bottom-6 left-6 z-[9999] w-14 h-14 rounded-full bg-pw-700 text-white shadow-lg hover:bg-pw-800 transition-all hover:scale-105 flex items-center justify-center"> <svg id="support-fab-icon-open" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <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.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> </svg> <svg id="support-fab-icon-close" class="w-6 h-6 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path> </svg> </button> <!-- Slide-out panel --> <div id="support-panel" class="fixed bottom-24 left-6 z-[9998] w-[380px] max-w-[calc(100vw-2rem)] bg-white rounded-xl shadow-2xl border border-gray-200 transform translate-y-4 opacity-0 pointer-events-none transition-all duration-200 ease-out"> <div class="px-5 py-4 border-b border-gray-100 bg-gray-50 rounded-t-xl"> <h3 class="text-base font-semibold text-gray-900">How can we help?</h3> <p class="text-xs text-gray-500 mt-0.5">Choose a category and tell us what you need.</p> </div> <!-- Category selector (step 1) --> <div id="support-step-categories" class="p-4 space-y-2"> <button type="button" data-category="question" class="support-cat-btn w-full text-left px-4 py-3 rounded-lg border border-gray-200 hover:border-pw-300 hover:bg-pw-50 transition-colors group"> <div class="flex items-center gap-3"> <span class="flex-shrink-0 w-8 h-8 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center"> <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><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.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> </span> <div> <span class="text-sm font-medium text-gray-900 group-hover:text-pw-700">Ask a Question</span> <span class="block text-xs text-gray-500">About our compliance services or process</span> </div> </div> </button> <button type="button" data-category="support" class="support-cat-btn w-full text-left px-4 py-3 rounded-lg border border-gray-200 hover:border-pw-300 hover:bg-pw-50 transition-colors group"> <div class="flex items-center gap-3"> <span class="flex-shrink-0 w-8 h-8 rounded-full bg-green-100 text-green-600 flex items-center justify-center"> <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"></path></svg> </span> <div> <span class="text-sm font-medium text-gray-900 group-hover:text-pw-700">Get Support</span> <span class="block text-xs text-gray-500">Help with an ongoing engagement</span> </div> </div> </button> <button type="button" data-category="issue" class="support-cat-btn w-full text-left px-4 py-3 rounded-lg border border-gray-200 hover:border-pw-300 hover:bg-pw-50 transition-colors group"> <div class="flex items-center gap-3"> <span class="flex-shrink-0 w-8 h-8 rounded-full bg-red-100 text-red-600 flex items-center justify-center"> <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg> </span> <div> <span class="text-sm font-medium text-gray-900 group-hover:text-pw-700">Report an Issue</span> <span class="block text-xs text-gray-500">Something isn't right with a deliverable</span> </div> </div> </button> <button type="button" data-category="service_request" class="support-cat-btn w-full text-left px-4 py-3 rounded-lg border border-gray-200 hover:border-pw-300 hover:bg-pw-50 transition-colors group"> <div class="flex items-center gap-3"> <span class="flex-shrink-0 w-8 h-8 rounded-full bg-purple-100 text-purple-600 flex items-center justify-center"> <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg> </span> <div> <span class="text-sm font-medium text-gray-900 group-hover:text-pw-700">Request a Service</span> <span class="block text-xs text-gray-500">Start a new compliance engagement</span> </div> </div> </button> <button type="button" data-category="quote" class="support-cat-btn w-full text-left px-4 py-3 rounded-lg border border-gray-200 hover:border-pw-300 hover:bg-pw-50 transition-colors group"> <div class="flex items-center gap-3"> <span class="flex-shrink-0 w-8 h-8 rounded-full bg-amber-100 text-amber-600 flex items-center justify-center"> <svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> </span> <div> <span class="text-sm font-medium text-gray-900 group-hover:text-pw-700">Request a Quote</span> <span class="block text-xs text-gray-500">Get pricing for a complex engagement</span> </div> </div> </button> <!-- Sign-in link (hidden when logged in) --> <div id="support-signin-link" class="pt-2 border-t border-gray-100 mt-2"> <button type="button" id="support-signin-btn" class="w-full flex items-center justify-center gap-2 px-4 py-2 text-xs text-gray-500 hover:text-pw-700 transition-colors"> <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg>
Already a client? <span class="font-medium text-pw-600">Sign in</span> </button> </div> </div> <!-- Ticket form — Report an Issue + guest fallback (step 2a) --> <form id="support-step-form" class="hidden p-4"> <button type="button" id="support-back-btn" class="inline-flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 mb-3"> <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"></path></svg>
Back
</button> <div id="support-category-badge" class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-pw-100 text-pw-700 mb-3"></div> <div class="space-y-3"> <div> <label for="support-name" class="block text-xs font-medium text-gray-700 mb-1">Name <span class="text-gray-400">(optional)</span></label> <input type="text" id="support-name" name="name" placeholder="Your name" maxlength="100" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="support-email" class="block text-xs font-medium text-gray-700 mb-1">Email <span class="text-gray-400">(for follow-up)</span></label> <input type="email" id="support-email" name="email" placeholder="you@company.com" maxlength="200" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="support-subject" class="block text-xs font-medium text-gray-700 mb-1">Subject <span class="text-red-400">*</span></label> <input type="text" id="support-subject" name="subject" required minlength="3" maxlength="200" placeholder="Brief summary" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="support-message" class="block text-xs font-medium text-gray-700 mb-1">Message <span class="text-red-400">*</span></label> <textarea id="support-message" name="message" required minlength="10" maxlength="5000" rows="4" placeholder="Describe your question, issue, or request..." class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow resize-y"></textarea> <p class="text-[10px] text-gray-400 mt-0.5 text-right"><span id="support-char-count">0</span> / 5000</p> </div> <button type="submit" id="support-submit-btn" class="w-full py-2.5 px-4 bg-pw-700 text-white text-sm font-medium rounded-lg hover:bg-pw-800 transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
Submit
</button> </div> </form> <!-- Opportunity form — Request a Service / Request a Quote (step 2b) --> <form id="support-step-opportunity" class="hidden p-4"> <button type="button" id="opportunity-back-btn" class="inline-flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 mb-3"> <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"></path></svg>
Back
</button> <div id="opportunity-category-badge" class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-pw-100 text-pw-700 mb-3"></div> <div class="space-y-3"> <div> <label for="opp-name" class="block text-xs font-medium text-gray-700 mb-1">Name <span class="text-red-400">*</span></label> <input type="text" id="opp-name" name="name" required minlength="2" maxlength="100" placeholder="Your name" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="opp-email" class="block text-xs font-medium text-gray-700 mb-1">Email <span class="text-red-400">*</span></label> <input type="email" id="opp-email" name="email" required placeholder="you@company.com" maxlength="200" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="opp-company" class="block text-xs font-medium text-gray-700 mb-1">Company <span class="text-gray-400">(optional)</span></label> <input type="text" id="opp-company" name="company" placeholder="Company name" maxlength="200" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="opp-phone" class="block text-xs font-medium text-gray-700 mb-1">Phone <span class="text-gray-400">(optional)</span></label> <input type="tel" id="opp-phone" name="phone" placeholder="(555) 555-1234" maxlength="30" class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow"> </div> <div> <label for="opp-service" class="block text-xs font-medium text-gray-700 mb-1">Service interest <span class="text-red-400">*</span></label> <select id="opp-service" name="service_slug" required class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow bg-white"> <option value="" disabled selected>Select a service...</option> <option value="canada-crtc">Canada CRTC Carrier Package</option> <option value="llc-formation">LLC Formation</option> <option value="corporation-formation">Corporation Formation</option> <option value="registered-agent">Registered Agent</option> <option value="compliance-audit">Compliance Audit</option> <option value="other">Other / Not sure</option> </select> </div> <div> <label for="opp-details" class="block text-xs font-medium text-gray-700 mb-1">Details <span class="text-gray-400">(optional)</span></label> <textarea id="opp-details" name="details" maxlength="5000" rows="3" placeholder="Tell us about your project or what you need..." class="w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-pw-500 focus:border-pw-500 outline-none transition-shadow resize-y"></textarea> </div> <button type="submit" id="opp-submit-btn" class="w-full py-2.5 px-4 bg-pw-700 text-white text-sm font-medium rounded-lg hover:bg-pw-800 transition-colors disabled:opacity-50 disabled:cursor-not-allowed">
Submit Request
</button> </div> </form> <!-- Sign-in prompt — Ask a Question / Get Support when not logged in (step 2c) --> <div id="support-step-signin" class="hidden p-6"> <button type="button" id="signin-back-btn" class="inline-flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 mb-4"> <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"></path></svg>
Back
</button> <div class="text-center"> <div class="w-12 h-12 mx-auto mb-3 rounded-full bg-blue-100 flex items-center justify-center"> <svg class="w-6 h-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path></svg> </div> <h4 class="text-sm font-semibold text-gray-900 mb-1">Sign in for support</h4> <p class="text-xs text-gray-500 mb-5">Sign in to access your client portal where you can view your orders, submit support requests, and manage your services.</p> <button type="button" id="signin-auth-btn" class="w-full py-2.5 px-4 bg-pw-700 text-white text-sm font-medium rounded-lg hover:bg-pw-800 transition-colors mb-3">
Sign In
</button> <button type="button" id="signin-guest-btn" class="text-xs text-gray-500 hover:text-gray-700 transition-colors">
Continue as guest &rarr;
</button> </div> </div> <!-- Success state (step 3) --> <div id="support-step-success" class="hidden p-6 text-center"> <div class="w-12 h-12 mx-auto mb-3 rounded-full bg-green-100 flex items-center justify-center"> <svg class="w-6 h-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"></path></svg> </div> <h4 class="text-sm font-semibold text-gray-900 mb-1">Request received</h4> <p id="support-success-message" class="text-xs text-gray-500 mb-4">We'll get back to you within one business day.</p> <p id="support-ticket-id" class="text-xs text-gray-400 mb-4 hidden">Ref: <span></span></p> <button type="button" id="support-new-ticket-btn" class="text-xs text-pw-600 hover:text-pw-700 font-medium">Submit another request</button> </div> </div> <!-- Modal backdrop --><div id="auth-modal-backdrop" class="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm hidden items-center justify-center p-4" role="dialog" aria-modal="true" aria-labelledby="auth-modal-title"> <div id="auth-modal" class="relative w-full max-w-sm bg-white rounded-2xl shadow-2xl overflow-hidden"> <!-- Close --> <button id="auth-modal-close" type="button" aria-label="Close" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors z-10"> <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"> <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"></path> </svg> </button> <!-- Tab bar --> <div id="auth-tabs" class="flex border-b border-gray-100"> <button type="button" id="auth-tab-login" class="auth-tab flex-1 py-4 text-sm font-semibold text-pw-700 border-b-2 border-pw-600 transition-colors">
Sign In
</button> <button type="button" id="auth-tab-register" class="auth-tab flex-1 py-4 text-sm font-semibold text-gray-400 border-b-2 border-transparent hover:text-gray-600 transition-colors">
Create Account
</button> </div> <div class="px-6 py-6"> <!-- ── Login form ──────────────────────────────────────────────────── --> <form id="auth-login-form" class="space-y-4" novalidate> <div> <label for="auth-login-email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> <input type="email" id="auth-login-email" autocomplete="email" required class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-pw-500 focus:border-pw-500 transition-colors" placeholder="you@company.com"> </div> <div> <div class="flex items-center justify-between mb-1"> <label for="auth-login-password" class="text-sm font-medium text-gray-700">Password</label> <button type="button" id="auth-forgot-link" class="text-xs text-pw-600 hover:text-pw-800 underline underline-offset-2">
Forgot password?
</button> </div> <input type="password" id="auth-login-password" autocomplete="current-password" required class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-pw-500 focus:border-pw-500 transition-colors" placeholder="••••••••"> </div> <p id="auth-login-err" class="hidden text-xs text-red-600 font-medium"></p> <button type="submit" id="auth-login-btn" class="w-full py-2.5 rounded-lg bg-pw-700 text-white text-sm font-semibold hover:bg-pw-800 transition-colors disabled:opacity-50">
Sign In
</button> </form> <!-- ── Register form ──────────────────────────────────────────────── --> <form id="auth-register-form" class="space-y-4 hidden" novalidate> <div> <label for="auth-reg-name" class="block text-sm font-medium text-gray-700 mb-1">Your Name</label> <input type="text" id="auth-reg-name" autocomplete="name" class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-pw-500 focus:border-pw-500 transition-colors" placeholder="Full name"> </div> <div> <label for="auth-reg-email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> <input type="email" id="auth-reg-email" autocomplete="email" required class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-pw-500 focus:border-pw-500 transition-colors" placeholder="you@company.com"> </div> <div> <label for="auth-reg-password" class="block text-sm font-medium text-gray-700 mb-1">Password</label> <input type="password" id="auth-reg-password" autocomplete="new-password" required class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-pw-500 focus:border-pw-500 transition-colors" placeholder="At least 8 characters"> </div> <p id="auth-reg-err" class="hidden text-xs text-red-600 font-medium"></p> <button type="submit" id="auth-reg-btn" class="w-full py-2.5 rounded-lg bg-pw-700 text-white text-sm font-semibold hover:bg-pw-800 transition-colors disabled:opacity-50">
Create Account
</button> </form> <!-- ── Forgot password form ────────────────────────────────────────── --> <div id="auth-forgot-form" class="hidden"> <button type="button" id="auth-back-to-login" class="flex items-center gap-1 text-xs text-gray-500 hover:text-gray-700 mb-4"> <svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10.5 19.5L3 12m0 0l7.5-7.5M3 12h18"></path></svg>
Back to sign in
</button> <h3 class="text-base font-semibold text-gray-900 mb-1">Reset your password</h3> <p class="text-sm text-gray-500 mb-4">Enter your email and we'll send you a reset link.</p> <form id="auth-forgot-email-form" class="space-y-4" novalidate> <div> <label for="auth-forgot-email" class="block text-sm font-medium text-gray-700 mb-1">Email</label> <input type="email" id="auth-forgot-email" autocomplete="email" required class="w-full rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-pw-500 focus:border-pw-500 transition-colors" placeholder="you@company.com"> </div> <p id="auth-forgot-err" class="hidden text-xs text-red-600 font-medium"></p> <p id="auth-forgot-ok" class="hidden text-xs text-green-700 font-medium"></p> <button type="submit" id="auth-forgot-btn" class="w-full py-2.5 rounded-lg bg-pw-700 text-white text-sm font-semibold hover:bg-pw-800 transition-colors disabled:opacity-50">
Send reset link
</button> </form> </div> </div> </div> </div>
<script type="module" src="/_astro/hoisted.yFz1BYXO.js"></script>
</body>
</html>