<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Free Sales Tax Nexus Analyzer - Check All 50 States Instantly</title>
<meta name="description" content="Upload your sales CSV and instantly see which states you've triggered economic nexus in. Free analysis, no signup required.">
<meta property="og:title" content="Free Sales Tax Nexus Analyzer - Check All 50 States Instantly">
<meta property="og:description" content="Upload your sales CSV and instantly see which states you've triggered economic nexus in.">
<meta property="og:type" content="website">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🗺️</text></svg>">
<style>
/* ===== CSS CUSTOM PROPERTIES ===== */
:root {
  --navy-900: #0c1b33;
  --navy-800: #1a2744;
  --navy-700: #1e3a5f;
  --navy-600: #2c5282;
  --white: #ffffff;
  --gray-50: #f8fafc;
  --gray-100: #f1f5f9;
  --gray-200: #e2e8f0;
  --gray-300: #cbd5e1;
  --gray-500: #64748b;
  --gray-700: #334155;
  --gray-900: #0f172a;
  --red-600: #dc2626;
  --red-100: #fee2e2;
  --yellow-500: #eab308;
  --yellow-100: #fef9c3;
  --green-600: #16a34a;
  --green-100: #dcfce7;
  --blue-500: #3b82f6;
  --blue-600: #2563eb;
  --font-primary: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: 'SF Mono', 'Cascadia Code', Consolas, 'Liberation Mono', monospace;
}

/* ===== RESET & BASE ===== */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body { font-family: var(--font-primary); font-size: 16px; line-height: 1.6; color: var(--gray-700); background: var(--white); }
img, svg { display: block; max-width: 100%; }
a { color: var(--blue-600); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ===== TYPOGRAPHY ===== */
h1 { font-size: 3rem; font-weight: 800; line-height: 1.1; color: var(--gray-900); }
h2 { font-size: 2.25rem; font-weight: 700; line-height: 1.2; color: var(--gray-900); }
h3 { font-size: 1.5rem; font-weight: 600; line-height: 1.3; color: var(--gray-900); }
p { margin-bottom: 0; }

/* ===== LAYOUT ===== */
.container { max-width: 1200px; padding: 0 24px; margin: 0 auto; }
section { padding: 80px 0; }

/* ===== NAVIGATION ===== */
.nav {
  position: sticky; top: 0; z-index: 100;
  background: var(--navy-800);
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.nav-inner {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 24px; max-width: 1200px; margin: 0 auto; height: 64px;
}
.nav-brand {
  font-size: 1.25rem; font-weight: 800; color: var(--white);
  text-decoration: none; letter-spacing: -0.5px;
}
.nav-brand span { color: #60a5fa; }
.nav-links { display: flex; gap: 32px; list-style: none; }
.nav-links a { color: var(--gray-300); font-size: 0.9rem; font-weight: 500; text-decoration: none; transition: color 0.2s; }
.nav-links a:hover { color: var(--white); text-decoration: none; }
.nav-cta {
  background: var(--blue-600); color: var(--white) !important;
  padding: 8px 16px; border-radius: 6px; font-weight: 600 !important;
  transition: background 0.2s !important;
}
.nav-cta:hover { background: #1d4ed8 !important; color: var(--white) !important; }
.nav-hamburger {
  display: none; background: none; border: none; cursor: pointer;
  flex-direction: column; gap: 5px; padding: 4px;
}
.nav-hamburger span {
  display: block; width: 24px; height: 2px; background: var(--white); border-radius: 2px; transition: 0.3s;
}
.nav-mobile { display: none; }

/* ===== HERO ===== */
.hero {
  background: linear-gradient(135deg, var(--navy-900) 0%, var(--navy-700) 60%, #1e40af 100%);
  color: var(--white); padding: 96px 0 80px;
}
.hero-inner { max-width: 800px; }
.hero-eyebrow {
  display: inline-flex; align-items: center; gap: 8px;
  background: rgba(220,38,38,0.2); border: 1px solid rgba(220,38,38,0.4);
  color: #fca5a5; font-size: 0.8rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: 1px; padding: 4px 12px; border-radius: 9999px; margin-bottom: 24px;
}
.hero h1 { color: var(--white); margin-bottom: 20px; }
.hero h1 em { color: #fca5a5; font-style: normal; }
.hero-sub { font-size: 1.2rem; color: #cbd5e1; margin-bottom: 40px; max-width: 640px; line-height: 1.7; }
.hero-cta {
  display: inline-flex; align-items: center; gap: 10px;
  background: var(--blue-600); color: var(--white);
  padding: 16px 32px; border-radius: 8px; font-size: 1.1rem; font-weight: 700;
  text-decoration: none; transition: background 0.2s, transform 0.1s; box-shadow: 0 4px 14px rgba(37,99,235,0.4);
}
.hero-cta:hover { background: #1d4ed8; text-decoration: none; transform: translateY(-1px); }
.trust-badges {
  display: flex; gap: 24px; margin-top: 40px; flex-wrap: wrap;
}
.trust-badge {
  display: flex; align-items: center; gap: 8px;
  font-size: 0.875rem; color: #94a3b8;
}
.trust-badge-icon { color: #4ade80; font-size: 1rem; }

/* ===== HOW IT WORKS ===== */
.how-it-works { background: var(--gray-50); }
.section-header { text-align: center; margin-bottom: 56px; }
.section-header h2 { margin-bottom: 12px; }
.section-header p { color: var(--gray-500); font-size: 1.1rem; }
.steps-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 32px; }
.step-card {
  background: var(--white); border-radius: 8px; padding: 32px 24px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
  position: relative; text-align: center;
}
.step-number {
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--navy-700); color: var(--white);
  font-size: 1.1rem; font-weight: 800;
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto 20px;
}
.step-icon { font-size: 2rem; margin-bottom: 16px; }
.step-card h3 { font-size: 1.15rem; margin-bottom: 12px; }
.step-card p { color: var(--gray-500); font-size: 0.95rem; }

/* ===== ANALYZER SECTION ===== */
.analyzer-section { background: var(--white); }
.upload-zone {
  border: 2px dashed var(--gray-300); background: var(--gray-50);
  border-radius: 12px; padding: 56px 32px;
  text-align: center; cursor: pointer; transition: all 0.2s;
  position: relative;
}
.upload-zone:hover, .upload-zone.dragover {
  border-color: var(--blue-500); background: rgba(59,130,246,0.04);
}
.upload-zone.has-file { border-color: var(--green-600); }
.upload-zone.error { border-color: var(--red-600); }
.upload-icon { font-size: 3rem; margin-bottom: 16px; color: var(--gray-500); }
.upload-zone h3 { color: var(--gray-700); margin-bottom: 8px; }
.upload-zone p { color: var(--gray-500); font-size: 0.9rem; }
#file-input { display: none; }
.upload-hint { font-size: 0.8rem; color: var(--gray-500); margin-top: 8px; }

/* Column mapping */
#mapping-section { display: none; margin-top: 32px; }
.mapping-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 20px; }
.mapping-group label { display: block; font-size: 0.875rem; font-weight: 600; color: var(--gray-700); margin-bottom: 6px; }
.mapping-group select {
  width: 100%; padding: 8px 12px; border: 1px solid var(--gray-300);
  border-radius: 6px; font-size: 0.9rem; background: var(--white);
  color: var(--gray-700); cursor: pointer;
}
.mapping-group select:focus { outline: none; border-color: var(--blue-500); }
.preview-table-wrap { overflow-x: auto; margin-bottom: 20px; border-radius: 8px; border: 1px solid var(--gray-200); }
.preview-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.preview-table th { background: var(--gray-50); padding: 10px 12px; text-align: left; font-weight: 600; color: var(--gray-700); border-bottom: 1px solid var(--gray-200); }
.preview-table td { padding: 8px 12px; border-bottom: 1px solid var(--gray-200); color: var(--gray-700); }
.preview-table tr:last-child td { border-bottom: none; }
.preview-table th.detected, .preview-table td.detected { background: rgba(59,130,246,0.08); }

/* Analyze button */
#analyze-btn {
  display: none; width: 100%; padding: 14px;
  background: var(--navy-700); color: var(--white);
  border: none; border-radius: 8px; font-size: 1.05rem; font-weight: 700;
  cursor: pointer; transition: background 0.2s;
}
#analyze-btn:hover { background: var(--navy-900); }
#analyze-btn:disabled { opacity: 0.5; cursor: not-allowed; }

/* Map container */
#map-section { display: none; margin-top: 48px; }
#map-container { width: 100%; background: var(--gray-50); border-radius: 12px; padding: 16px; }
#map-container svg { width: 100%; height: auto; }
.map-legend { display: flex; gap: 24px; justify-content: center; margin-top: 16px; flex-wrap: wrap; }
.legend-item { display: flex; align-items: center; gap: 8px; font-size: 0.85rem; color: var(--gray-500); }
.legend-dot { width: 14px; height: 14px; border-radius: 3px; flex-shrink: 0; }

/* Tooltip */
#map-tooltip {
  position: fixed; z-index: 1000; pointer-events: none;
  background: var(--navy-800); color: var(--white);
  border-radius: 6px; padding: 10px 14px; max-width: 250px;
  box-shadow: 0 4px 6px rgba(0,0,0,0.2); font-size: 0.85rem;
  display: none;
}
#map-tooltip strong { display: block; margin-bottom: 4px; font-size: 0.95rem; }
#map-tooltip .tt-row { display: flex; justify-content: space-between; gap: 16px; margin-top: 4px; }
#map-tooltip .tt-label { color: #94a3b8; }

/* ===== RESULTS SECTION ===== */
#results-section { display: none; background: var(--gray-50); }
.results-summary-banner {
  border-radius: 8px; padding: 24px 28px; margin-bottom: 32px;
  display: flex; align-items: center; gap: 20px;
  background: var(--red-100); border-left: 5px solid var(--red-600);
}
.results-summary-banner.safe { background: var(--green-100); border-left-color: var(--green-600); }
.banner-icon { font-size: 2rem; }
.banner-text h3 { color: var(--gray-900); margin-bottom: 4px; }
.banner-text p { color: var(--gray-500); font-size: 0.95rem; }
.results-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-bottom: 32px; }
.stat-card {
  background: var(--white); border-radius: 8px; padding: 20px 24px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.08); text-align: center; border-top: 3px solid var(--gray-200);
}
.stat-card.red { border-top-color: var(--red-600); }
.stat-card.yellow { border-top-color: var(--yellow-500); }
.stat-card.green { border-top-color: var(--green-600); }
.stat-number { font-size: 2.5rem; font-weight: 800; font-family: var(--font-mono); margin-bottom: 4px; }
.stat-card.red .stat-number { color: var(--red-600); }
.stat-card.yellow .stat-number { color: #92400e; }
.stat-card.green .stat-number { color: var(--green-600); }
.stat-label { font-size: 0.85rem; color: var(--gray-500); font-weight: 500; }
.exposure-card {
  background: var(--navy-800); color: var(--white);
  border-radius: 8px; padding: 24px 28px; margin-bottom: 32px;
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px;
}
.exposure-label { font-size: 0.9rem; color: #94a3b8; margin-bottom: 6px; }
.exposure-amount { font-size: 2rem; font-weight: 800; font-family: var(--font-mono); color: #fca5a5; }
.exposure-note { font-size: 0.8rem; color: #64748b; margin-top: 4px; }
.unlock-inline-btn {
  background: var(--blue-600); color: var(--white);
  border: none; border-radius: 6px; padding: 10px 20px;
  font-size: 0.9rem; font-weight: 600; cursor: pointer; white-space: nowrap;
  transition: background 0.2s;
}
.unlock-inline-btn:hover { background: #1d4ed8; }

/* ===== RESULTS TABLE ===== */
.table-wrap { overflow-x: auto; border-radius: 8px; border: 1px solid var(--gray-200); background: var(--white); }
.results-table { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.results-table th {
  background: var(--gray-50); padding: 12px 16px; text-align: left;
  font-weight: 600; color: var(--gray-700); border-bottom: 1px solid var(--gray-200);
  cursor: pointer; user-select: none; white-space: nowrap;
}
.results-table th:hover { color: var(--gray-900); }
.results-table th.sort-asc::after { content: ' ↑'; color: var(--blue-500); }
.results-table th.sort-desc::after { content: ' ↓'; color: var(--blue-500); }
.results-table td { padding: 12px 16px; border-bottom: 1px solid var(--gray-200); }
.results-table tr:last-child td { border-bottom: none; }
.results-table tr:nth-child(even) td { background: var(--gray-50); }
.results-table tr.row-highlight td { background: rgba(59,130,246,0.1) !important; transition: background 0.5s; }
.results-table .num { font-family: var(--font-mono); text-align: right; }
.bar-wrap { width: 80px; background: var(--gray-200); border-radius: 9999px; height: 6px; display: inline-block; vertical-align: middle; overflow: hidden; }
.bar-fill { height: 100%; border-radius: 9999px; }

/* ===== STATUS BADGES ===== */
.badge {
  display: inline-block; padding: 2px 10px; border-radius: 9999px;
  font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;
}
.badge-red { background: var(--red-100); color: var(--red-600); border: 1px solid var(--red-600); }
.badge-yellow { background: var(--yellow-100); color: #92400e; border: 1px solid var(--yellow-500); }
.badge-green { background: var(--green-100); color: var(--green-600); border: 1px solid var(--green-600); }
.badge-notax { background: var(--gray-100); color: var(--gray-500); border: 1px solid var(--gray-300); }

/* ===== GATED VALUES ===== */
.gated-value { filter: blur(6px); user-select: none; cursor: pointer; transition: filter 0.3s ease; }
.gated-value.unlocked { filter: none; user-select: auto; cursor: default; }

/* ===== CTA BAR ===== */
.results-cta-bar {
  background: linear-gradient(135deg, var(--navy-800), var(--navy-900));
  color: var(--white); border-radius: 12px; padding: 28px 32px;
  display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 20px;
  margin-top: 32px;
}
.results-cta-bar h3 { color: var(--white); margin-bottom: 6px; font-size: 1.15rem; }
.results-cta-bar p { color: #94a3b8; font-size: 0.9rem; }
.cta-btn {
  background: var(--blue-600); color: var(--white);
  border: none; border-radius: 8px; padding: 14px 28px;
  font-size: 1rem; font-weight: 700; cursor: pointer; white-space: nowrap;
  transition: background 0.2s; text-decoration: none; display: inline-block;
}
.cta-btn:hover { background: #1d4ed8; text-decoration: none; color: var(--white); }

/* ===== PRICING ===== */
.pricing-section { background: var(--white); }
.pricing-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; }
.pricing-card {
  border: 1px solid var(--gray-200); border-radius: 12px; padding: 32px 28px;
  position: relative; background: var(--white);
}
.pricing-card.featured {
  border-color: var(--navy-700); border-width: 2px;
  box-shadow: 0 8px 24px rgba(30,58,95,0.12);
}
.pricing-badge {
  position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
  background: var(--navy-700); color: var(--white);
  font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 1px;
  padding: 4px 16px; border-radius: 9999px;
}
.pricing-tier { font-size: 0.85rem; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; color: var(--gray-500); margin-bottom: 8px; }
.pricing-price { font-size: 2.5rem; font-weight: 800; color: var(--gray-900); margin-bottom: 4px; font-family: var(--font-mono); }
.pricing-price span { font-size: 1rem; color: var(--gray-500); font-weight: 400; }
.pricing-desc { font-size: 0.9rem; color: var(--gray-500); margin-bottom: 24px; }
.pricing-features { list-style: none; margin-bottom: 28px; }
.pricing-features li { padding: 8px 0; font-size: 0.9rem; color: var(--gray-700); border-bottom: 1px solid var(--gray-100); display: flex; gap: 10px; align-items: flex-start; }
.pricing-features li::before { content: '✓'; color: var(--green-600); font-weight: 700; flex-shrink: 0; margin-top: 1px; }
.pricing-btn {
  display: block; width: 100%; text-align: center; padding: 12px;
  border-radius: 6px; font-size: 1rem; font-weight: 600; cursor: pointer;
  border: none; transition: all 0.2s; text-decoration: none;
}
.pricing-btn-primary { background: var(--navy-700); color: var(--white); }
.pricing-btn-primary:hover { background: var(--navy-900); color: var(--white); text-decoration: none; }
.pricing-btn-secondary { background: var(--white); color: var(--navy-700); border: 1px solid var(--gray-300); }
.pricing-btn-secondary:hover { background: var(--gray-50); text-decoration: none; }
.pricing-value-note {
  text-align: center; margin-top: 32px;
  background: var(--red-100); border: 1px solid rgba(220,38,38,0.2);
  border-radius: 8px; padding: 16px;
  font-size: 0.9rem; color: var(--red-600);
}

/* ===== FAQ ===== */
.faq-section { background: var(--gray-50); }
.faq-list { max-width: 800px; margin: 0 auto; }
details { background: var(--white); border-radius: 8px; margin-bottom: 12px; border: 1px solid var(--gray-200); }
details[open] { border-color: var(--navy-600); }
summary {
  padding: 20px 24px; font-weight: 600; color: var(--gray-900);
  cursor: pointer; list-style: none; display: flex; justify-content: space-between; align-items: center;
}
summary::-webkit-details-marker { display: none; }
summary::after { content: '+'; font-size: 1.4rem; color: var(--gray-500); transition: transform 0.2s; }
details[open] summary::after { content: '−'; }
.faq-body { padding: 0 24px 20px; color: var(--gray-500); font-size: 0.95rem; line-height: 1.7; }

/* ===== FOOTER ===== */
footer { background: var(--navy-900); color: #94a3b8; padding: 48px 0 32px; }
.footer-grid { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 48px; margin-bottom: 40px; }
.footer-brand-name { font-size: 1.2rem; font-weight: 800; color: var(--white); margin-bottom: 12px; }
.footer-brand-name span { color: #60a5fa; }
.footer-brand p { font-size: 0.875rem; line-height: 1.7; }
.footer-col h4 { color: var(--white); font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; margin-bottom: 16px; }
.footer-col ul { list-style: none; }
.footer-col li { margin-bottom: 10px; }
.footer-col a { color: #94a3b8; font-size: 0.875rem; text-decoration: none; transition: color 0.2s; }
.footer-col a:hover { color: var(--white); }
.footer-bottom { border-top: 1px solid rgba(255,255,255,0.1); padding-top: 24px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; font-size: 0.8rem; }
.footer-bottom a { color: #64748b; }

/* ===== MODAL ===== */
.modal-overlay {
  display: none; position: fixed; inset: 0; z-index: 9999;
  background: rgba(0,0,0,0.5); align-items: center; justify-content: center; padding: 16px;
}
.modal-overlay.active { display: flex; }
.modal-box {
  background: var(--white); max-width: 480px; width: 100%; border-radius: 12px; padding: 32px;
  box-shadow: 0 20px 25px rgba(0,0,0,0.15); position: relative;
  animation: modalIn 0.2s ease;
}
@keyframes modalIn { from { opacity: 0; transform: scale(0.95); } to { opacity: 1; transform: scale(1); } }
.modal-close {
  position: absolute; top: 16px; right: 16px; background: none; border: none;
  font-size: 1.4rem; color: var(--gray-500); cursor: pointer; line-height: 1; padding: 4px;
}
.modal-close:hover { color: var(--gray-900); }
.modal-eyebrow { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; color: var(--blue-600); margin-bottom: 8px; }
.modal-box h2 { font-size: 1.5rem; margin-bottom: 8px; }
.modal-box .modal-sub { color: var(--gray-500); font-size: 0.9rem; margin-bottom: 24px; }
.form-group { margin-bottom: 16px; }
.form-group label { display: block; font-size: 0.875rem; font-weight: 600; color: var(--gray-700); margin-bottom: 6px; }
.form-group input, .form-group select {
  width: 100%; padding: 10px 14px; border: 1px solid var(--gray-300);
  border-radius: 6px; font-size: 0.95rem; color: var(--gray-700); background: var(--white);
  transition: border-color 0.2s;
}
.form-group input:focus, .form-group select:focus { outline: none; border-color: var(--blue-500); }
.form-group input.error-field { border-color: var(--red-600); }
.form-error { font-size: 0.8rem; color: var(--red-600); margin-top: 4px; display: none; }
.form-submit-btn {
  width: 100%; padding: 14px; background: var(--blue-600); color: var(--white);
  border: none; border-radius: 8px; font-size: 1rem; font-weight: 700; cursor: pointer;
  transition: background 0.2s; margin-top: 8px;
}
.form-submit-btn:hover { background: #1d4ed8; }
.form-submit-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.modal-privacy { font-size: 0.75rem; color: var(--gray-500); text-align: center; margin-top: 12px; }
.form-global-error { background: var(--red-100); color: var(--red-600); border-radius: 6px; padding: 10px 14px; font-size: 0.875rem; margin-bottom: 16px; display: none; }

/* ===== MOBILE STICKY CTA ===== */
#mobile-cta {
  display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 500;
  background: var(--navy-800); color: var(--white);
  padding: 12px 16px; box-shadow: 0 -4px 12px rgba(0,0,0,0.2);
  justify-content: space-between; align-items: center; gap: 12px;
}
#mobile-cta p { font-size: 0.875rem; color: #cbd5e1; }
#mobile-cta button { background: var(--blue-600); color: var(--white); border: none; border-radius: 6px; padding: 10px 16px; font-weight: 700; font-size: 0.9rem; cursor: pointer; white-space: nowrap; }
#mobile-cta-close { background: none; border: none; color: #94a3b8; font-size: 1.2rem; cursor: pointer; padding: 4px; }

/* ===== ALERTS ===== */
.alert { border-left: 4px solid; padding: 16px; border-radius: 0 8px 8px 0; margin-bottom: 16px; }
.alert-error { background: var(--red-100); border-color: var(--red-600); color: var(--red-600); }
.alert-info { background: #eff6ff; border-color: var(--blue-500); color: #1d4ed8; }

/* ===== LOADING ===== */
.spinner {
  width: 24px; height: 24px; border: 3px solid rgba(255,255,255,0.3);
  border-top-color: var(--white); border-radius: 50%; animation: spin 0.7s linear infinite; display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ===== ANIMATIONS ===== */
.fade-in { animation: fadeIn 0.4s ease; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: translateY(0); } }

/* ===== PRINT ===== */
@media print {
  .nav, .hero, .how-it-works, .pricing-section, .faq-section, footer,
  #mobile-cta, .modal-overlay, .results-cta-bar, .unlock-inline-btn { display: none !important; }
  #results-section { display: block !important; background: white; }
  .gated-value { filter: none !important; }
  body { font-size: 12px; }
}

/* ===== RESPONSIVE ===== */
@media (max-width: 1024px) {
  .steps-grid { grid-template-columns: repeat(3, 1fr); gap: 20px; }
  .pricing-grid { grid-template-columns: 1fr; max-width: 480px; margin: 0 auto; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
  .footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 768px) {
  h1 { font-size: 2rem; }
  h2 { font-size: 1.75rem; }
  h3 { font-size: 1.25rem; }
  section { padding: 48px 0; }
  .nav-links { display: none; }
  .nav-hamburger { display: flex; }
  .nav-mobile { background: var(--navy-900); padding: 16px 24px; }
  .nav-mobile.active { display: block; }
  .nav-mobile a { display: block; color: #cbd5e1; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.08); text-decoration: none; font-weight: 500; }
  .nav-mobile a:last-child { border-bottom: none; }
  .steps-grid { grid-template-columns: 1fr; max-width: 400px; margin: 0 auto; }
  .mapping-grid { grid-template-columns: 1fr; }
  .results-cards { grid-template-columns: 1fr; }
  .exposure-card { flex-direction: column; }
  .results-cta-bar { flex-direction: column; }
  .footer-grid { grid-template-columns: 1fr; gap: 32px; }
  .footer-bottom { flex-direction: column; align-items: flex-start; }
  .trust-badges { flex-direction: column; gap: 12px; }
  .hero { padding: 64px 0 48px; }
  #mobile-cta { display: flex; }
}
</style>
</head>
<body>

<!-- NAVIGATION -->
<nav class="nav" role="navigation">
  <div class="nav-inner">
    <a href="#top" class="nav-brand">Nexus<span>Audit</span></a>
    <ul class="nav-links">
      <li><a href="#how-it-works">How It Works</a></li>
      <li><a href="#analyzer">Analyzer</a></li>
      <li><a href="#pricing">Pricing</a></li>
      <li><a href="#faq">FAQ</a></li>
      <li><a href="#analyzer" class="nav-cta">Analyze Free</a></li>
    </ul>
    <button class="nav-hamburger" aria-label="Menu" id="hamburger-btn">
      <span></span><span></span><span></span>
    </button>
  </div>
  <div class="nav-mobile" id="nav-mobile">
    <a href="#how-it-works">How It Works</a>
    <a href="#analyzer">Analyzer</a>
    <a href="#pricing">Pricing</a>
    <a href="#faq">FAQ</a>
    <a href="#analyzer">Analyze Free</a>
  </div>
</nav>

<!-- HERO -->
<section class="hero" id="top">
  <div class="container">
    <div class="hero-inner">
      <div class="hero-eyebrow">
        <span>⚠</span> IRS &amp; State Tax Enforcement is Increasing
      </div>
      <h1>Are You Illegally Selling in States You <em>Don't Know About?</em></h1>
      <p class="hero-sub">Upload your sales data. In 30 seconds, see exactly where you have tax obligations - before the states find you first.</p>
      <a href="#analyzer" class="hero-cta">
        <span>Analyze My Sales Data Free</span>
        <span>→</span>
      </a>
      <div class="trust-badges">
        <div class="trust-badge">
          <span class="trust-badge-icon">✓</span>
          <span>Your data never leaves your browser</span>
        </div>
        <div class="trust-badge">
          <span class="trust-badge-icon">✓</span>
          <span>50-state + DC coverage</span>
        </div>
        <div class="trust-badge">
          <span class="trust-badge-icon">✓</span>
          <span>Updated for 2026 thresholds</span>
        </div>
      </div>
    </div>
  </div>
</section>

<!-- HOW IT WORKS -->
<section class="how-it-works" id="how-it-works">
  <div class="container">
    <div class="section-header">
      <h2>How It Works</h2>
      <p>Three steps. Thirty seconds. Complete clarity on your tax exposure.</p>
    </div>
    <div class="steps-grid">
      <div class="step-card">
        <div class="step-number">1</div>
        <div class="step-icon">📂</div>
        <h3>Upload Your Sales CSV</h3>
        <p>Export your sales report from Shopify, Amazon, WooCommerce, or any platform. We accept any CSV with state and revenue columns.</p>
      </div>
      <div class="step-card">
        <div class="step-number">2</div>
        <div class="step-icon">🗺️</div>
        <h3>Instant 50-State Analysis</h3>
        <p>Our engine compares your sales against every state's 2026 economic nexus threshold - revenue and transaction counts - in seconds.</p>
      </div>
      <div class="step-card">
        <div class="step-number">3</div>
        <div class="step-icon">📋</div>
        <h3>Get Your Exposure Report</h3>
        <p>See exactly which states you've triggered nexus in, your exposure amount, and what to do next. Free tier shows state count; enter email to unlock details.</p>
      </div>
    </div>
  </div>
</section>

<!-- ANALYZER -->
<section class="analyzer-section" id="analyzer">
  <div class="container">
    <div class="section-header">
      <h2>Sales Tax Nexus Analyzer</h2>
      <p>Drop your CSV file below to begin - your data is processed entirely in your browser</p>
    </div>

    <div class="upload-zone" id="upload-zone">
      <div class="upload-icon">📁</div>
      <h3>Drop your CSV here or click to browse</h3>
      <p>Accepted: .csv files from Shopify, Amazon, WooCommerce, or any platform</p>
      <p class="upload-hint">Maximum file size: 50MB</p>
      <input type="file" id="file-input" accept=".csv,text/csv">
    </div>
    <div id="upload-error" style="display:none" class="alert alert-error"></div>

    <div id="mapping-section">
      <h3 style="margin-bottom:16px; color:var(--gray-900)">Step 2: Confirm Column Mapping</h3>
      <div class="preview-table-wrap" id="preview-wrap"></div>
      <div class="mapping-grid">
        <div class="mapping-group">
          <label for="col-state">State Column <span style="color:var(--red-600)">*</span></label>
          <select id="col-state"></select>
        </div>
        <div class="mapping-group">
          <label for="col-revenue">Revenue/Amount Column <span style="color:var(--red-600)">*</span></label>
          <select id="col-revenue"></select>
        </div>
        <div class="mapping-group">
          <label for="col-date">Date Column <span style="color:var(--gray-500)">(optional)</span></label>
          <select id="col-date"><option value="-1">Not in this file</option></select>
        </div>
      </div>
      <button id="analyze-btn">Analyze My Sales Data</button>
    </div>

    <div id="map-section">
      <div id="map-container"></div>
      <div class="map-legend">
        <div class="legend-item"><div class="legend-dot" style="background:var(--red-600)"></div>Nexus Triggered</div>
        <div class="legend-item"><div class="legend-dot" style="background:var(--yellow-500)"></div>Approaching (75-99%)</div>
        <div class="legend-item"><div class="legend-dot" style="background:var(--green-600)"></div>Safe</div>
        <div class="legend-item"><div class="legend-dot" style="background:#94a3b8"></div>No Sales Tax</div>
        <div class="legend-item"><div class="legend-dot" style="background:var(--gray-200)"></div>No Data</div>
      </div>
    </div>
  </div>
</section>
<div id="map-tooltip"></div>

<!-- RESULTS -->
<section id="results-section">
  <div class="container">
    <h2 style="margin-bottom:32px">Your Nexus Analysis Results</h2>
    <div id="results-summary-banner" class="results-summary-banner"></div>
    <div id="results-cards" class="results-cards"></div>
    <div class="exposure-card" id="exposure-card">
      <div>
        <div class="exposure-label">Estimated Total Tax Exposure</div>
        <div class="exposure-amount gated-value" id="exposure-amount">$0</div>
        <div class="exposure-note">Estimate based on average state tax rates for triggered states</div>
      </div>
      <button class="unlock-inline-btn" id="unlock-exposure-btn">Unlock Exact Amounts</button>
    </div>
    <div class="table-wrap">
      <table class="results-table" id="results-table">
        <thead>
          <tr>
            <th data-col="name" data-type="str">State</th>
            <th data-col="revenue" data-type="num" class="num">Your Revenue</th>
            <th data-col="revenueThreshold" data-type="num" class="num">Rev. Threshold</th>
            <th data-col="transactions" data-type="num" class="num">Transactions</th>
            <th data-col="txThreshold" data-type="num" class="num">Tx Threshold</th>
            <th data-col="revenuePercent" data-type="num">% of Threshold</th>
            <th data-col="status" data-type="str">Status</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody id="results-tbody"></tbody>
      </table>
    </div>
    <div class="results-cta-bar">
      <div>
        <h3>Get the Full Picture - Unlock Exact Dollar Amounts</h3>
        <p>See unblurred exposure amounts, download a CSV, and get actionable next steps for every triggered state.</p>
      </div>
      <button class="cta-btn" id="unlock-full-btn">Unlock Full Report - Enter Email</button>
    </div>
  </div>
</section>

<!-- PRICING -->
<section class="pricing-section" id="pricing">
  <div class="container">
    <div class="section-header">
      <h2>Simple, Transparent Pricing</h2>
      <p>Start free. Upgrade only when you're ready to take action.</p>
    </div>
    <div class="pricing-grid">
      <div class="pricing-card">
        <div class="pricing-tier">Free</div>
        <div class="pricing-price">$0</div>
        <div class="pricing-desc">Instant nexus check, no signup required</div>
        <ul class="pricing-features">
          <li>50-state + DC analysis</li>
          <li>Interactive map visualization</li>
          <li>Nexus triggered state count</li>
          <li>Approaching threshold warnings</li>
          <li>Status badges (red/yellow/green)</li>
        </ul>
        <a href="#analyzer" class="pricing-btn pricing-btn-secondary">Start Analyzing</a>
      </div>
      <div class="pricing-card featured">
        <div class="pricing-badge">Most Popular</div>
        <div class="pricing-tier">Full Report</div>
        <div class="pricing-price">$1,500 <span>one-time</span></div>
        <div class="pricing-desc">Everything you need to become compliant</div>
        <ul class="pricing-features">
          <li>Exact unblurred dollar amounts</li>
          <li>Branded PDF compliance report</li>
          <li>Per-state actionable recommendations</li>
          <li>Registration deadline guidance</li>
          <li>Penalty exposure estimates</li>
          <li>30-day email support</li>
        </ul>
        <button class="pricing-btn pricing-btn-primary" onclick="showLeadModal()">Get Full Report</button>
      </div>
      <div class="pricing-card">
        <div class="pricing-tier">Registration Service</div>
        <div class="pricing-price">$500 <span>/state</span></div>
        <div class="pricing-desc">We handle state registration for you</div>
        <ul class="pricing-features">
          <li>Full registration management</li>
          <li>State account setup</li>
          <li>Filing frequency determination</li>
          <li>First return filing included</li>
          <li>Ongoing compliance calendar</li>
        </ul>
        <button class="pricing-btn pricing-btn-secondary" onclick="showLeadModal()">Contact Us</button>
      </div>
    </div>
    <div class="pricing-value-note">
      ⚠ Non-compliance penalties average $10,000+ per state. The cost of ignoring nexus far exceeds the cost of getting compliant.
    </div>
  </div>
</section>

<!-- FAQ -->
<section class="faq-section" id="faq">
  <div class="container">
    <div class="section-header">
      <h2>Frequently Asked Questions</h2>
    </div>
    <div class="faq-list">
      <details>
        <summary>What is economic nexus?</summary>
        <div class="faq-body">Economic nexus means you have a tax collection obligation in a state based solely on your sales activity there - even without a physical presence. After the 2018 Supreme Court ruling in South Dakota v. Wayfair, states can require remote sellers to collect and remit sales tax once they exceed certain revenue or transaction thresholds.</div>
      </details>
      <details>
        <summary>Is my sales data safe? Where does it go?</summary>
        <div class="faq-body">Your data never leaves your browser. All CSV parsing and analysis happens entirely on your device using JavaScript. We do not upload, store, or transmit your sales data to any server. The only data that touches our servers is your email address if you choose to unlock the full report.</div>
      </details>
      <details>
        <summary>Which states have sales tax?</summary>
        <div class="faq-body">45 states plus the District of Columbia have a state sales tax. The five states with no state sales tax are: Alaska (though local jurisdictions may tax through ARSSTC), Delaware, Montana, New Hampshire, and Oregon. These appear as "No Sales Tax" in gray on your results map.</div>
      </details>
      <details>
        <summary>What CSV format do I need?</summary>
        <div class="faq-body">Any CSV with at minimum a column for state and a column for revenue/amount. We automatically detect columns from common platforms like Shopify (shipping_province), Amazon (ship-state), WooCommerce (shipping_state), and more. You can also manually select columns using the dropdown menu after uploading.</div>
      </details>
      <details>
        <summary>How current is the threshold data?</summary>
        <div class="faq-body">Thresholds are updated as of January 2026. Most states use $100,000 revenue OR 200 transactions as their threshold, with exceptions for California, Texas, and New York ($500,000+). We recommend verifying current thresholds with your tax advisor before making compliance decisions.</div>
      </details>
      <details>
        <summary>What happens after I trigger nexus in a state?</summary>
        <div class="faq-body">Once you exceed a state's economic nexus threshold, you are legally required to register for a sales tax permit in that state, begin collecting sales tax from customers there, and file regular sales tax returns. Failure to do so can result in back taxes, interest, and penalties. Our Registration Service ($500/state) handles this entire process for you.</div>
      </details>
    </div>
  </div>
</section>

<!-- FOOTER -->
<footer>
  <div class="container">
    <div class="footer-grid">
      <div class="footer-brand">
        <div class="footer-brand-name">Nexus<span>Audit</span></div>
        <p>Free sales tax nexus analysis tool. Your data is processed entirely in your browser - we never see your sales figures.</p>
      </div>
      <div class="footer-col">
        <h4>Tool</h4>
        <ul>
          <li><a href="#how-it-works">How It Works</a></li>
          <li><a href="#analyzer">Nexus Analyzer</a></li>
          <li><a href="#pricing">Pricing</a></li>
          <li><a href="#faq">FAQ</a></li>
        </ul>
      </div>
      <div class="footer-col">
        <h4>Legal</h4>
        <ul>
          <li><a href="#faq">Privacy Policy</a></li>
          <li><a href="#faq">Terms of Service</a></li>
          <li><a href="#" onclick="showLeadModal();return false;">Contact Us</a></li>
        </ul>
      </div>
    </div>
    <div class="footer-bottom">
      <span>© 2026 NexusAudit. For informational purposes only - not legal or tax advice.</span>
      <span>Thresholds last updated: January 2026</span>
    </div>
  </div>
</footer>

<!-- LEAD CAPTURE MODAL -->
<div class="modal-overlay" id="lead-modal">
  <div class="modal-box">
    <button class="modal-close" id="modal-close-btn" aria-label="Close">×</button>
    <div class="modal-eyebrow">Unlock Full Report</div>
    <h2>See Your Exact Exposure Amounts</h2>
    <p class="modal-sub">Enter your details to unlock unblurred dollar amounts and get your complete nexus compliance report.</p>
    <div class="form-global-error" id="form-global-error"></div>
    <form id="lead-form" novalidate>
      <div class="form-group">
        <label for="field-email">Work Email <span style="color:var(--red-600)">*</span></label>
        <input type="email" id="field-email" placeholder="you@company.com" autocomplete="email">
        <div class="form-error" id="err-email">Please enter a valid email address.</div>
      </div>
      <div class="form-group">
        <label for="field-company">Company Name <span style="color:var(--red-600)">*</span></label>
        <input type="text" id="field-company" placeholder="Acme Inc." autocomplete="organization">
        <div class="form-error" id="err-company">Please enter your company name.</div>
      </div>
      <div class="form-group">
        <label for="field-revenue">Annual Revenue Range</label>
        <select id="field-revenue">
          <option value="">Select range...</option>
          <option>Under $500K</option>
          <option>$500K - $1M</option>
          <option>$1M - $5M</option>
          <option>$5M - $25M</option>
          <option>$25M+</option>
        </select>
      </div>
      <div class="form-group">
        <label for="field-phone">Phone <span style="color:var(--gray-500)">(optional)</span></label>
        <input type="tel" id="field-phone" placeholder="+1 (555) 000-0000" autocomplete="tel">
      </div>
      <button type="submit" class="form-submit-btn" id="form-submit-btn">Unlock My Full Report</button>
    </form>
    <p class="modal-privacy">🔒 We'll never share your information. By submitting you agree to our Terms of Service.</p>
  </div>
</div>

<!-- MOBILE STICKY CTA -->
<div id="mobile-cta">
  <p>Unlock exact dollar amounts</p>
  <button onclick="showLeadModal()">Unlock Report</button>
  <button id="mobile-cta-close">×</button>
</div>

<!-- SCHEMA MARKUP -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {"@type":"Question","name":"What is economic nexus?","acceptedAnswer":{"@type":"Answer","text":"Economic nexus means you have a tax collection obligation in a state based solely on your sales activity there - even without a physical presence. After the 2018 Supreme Court ruling in South Dakota v. Wayfair, states can require remote sellers to collect and remit sales tax once they exceed certain revenue or transaction thresholds."}},
    {"@type":"Question","name":"Is my sales data safe?","acceptedAnswer":{"@type":"Answer","text":"Your data never leaves your browser. All CSV parsing and analysis happens entirely on your device using JavaScript."}},
    {"@type":"Question","name":"What CSV format do I need?","acceptedAnswer":{"@type":"Answer","text":"Any CSV with at minimum a column for state and a column for revenue/amount. We automatically detect columns from common platforms like Shopify, Amazon, and WooCommerce."}}
  ]
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebApplication",
  "name": "Sales Tax Nexus Analyzer",
  "description": "Free tool to check sales tax nexus obligations across all 50 US states",
  "applicationCategory": "BusinessApplication",
  "offers": {"@type":"Offer","price":"0","priceCurrency":"USD"}
}
</script>

<script>
// ===== NEXUS THRESHOLD DATA (embedded) =====
const THRESHOLDS = {"AL":{"name":"Alabama","revenue_threshold":250000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"4% state + up to 7% local","has_sales_tax":true,"registration_url":"https://myalabamataxes.alabama.gov","notes":"Simplified Sellers Use Tax Program available"},"AK":{"name":"Alaska","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"0% state + up to 7.5% local","has_sales_tax":false,"registration_url":"https://arsstc.org","notes":"No state sales tax; local jurisdictions may impose tax via ARSSTC"},"AZ":{"name":"Arizona","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"5.6% state + up to 5.6% local","has_sales_tax":true,"registration_url":"https://aztaxes.gov","notes":""},"AR":{"name":"Arkansas","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6.5% state + up to 6.125% local","has_sales_tax":true,"registration_url":"https://atap.arkansas.gov","notes":""},"CA":{"name":"California","revenue_threshold":500000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"7.25% state + up to 3.25% local","has_sales_tax":true,"registration_url":"https://cdtfa.ca.gov","notes":"Highest revenue threshold in the US"},"CO":{"name":"Colorado","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"2.9% state + up to 11.2% local","has_sales_tax":true,"registration_url":"https://tax.colorado.gov","notes":"Home rule jurisdictions may differ"},"CT":{"name":"Connecticut","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6.35% state","has_sales_tax":true,"registration_url":"https://portal.ct.gov/DRS","notes":""},"DE":{"name":"Delaware","revenue_threshold":null,"transaction_threshold":null,"threshold_type":"none","tax_rate_range":"0%","has_sales_tax":false,"registration_url":null,"notes":"No state sales tax"},"FL":{"name":"Florida","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6% state + up to 2% local","has_sales_tax":true,"registration_url":"https://floridarevenue.com","notes":"Remote seller law enacted July 2021"},"GA":{"name":"Georgia","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4% state + up to 5% local","has_sales_tax":true,"registration_url":"https://gtc.dor.ga.gov","notes":""},"HI":{"name":"Hawaii","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4% state + 0.5% county surcharge","has_sales_tax":true,"registration_url":"https://hitax.hawaii.gov","notes":"General Excise Tax (GET)"},"ID":{"name":"Idaho","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6% state + up to 3% local","has_sales_tax":true,"registration_url":"https://tax.idaho.gov","notes":""},"IL":{"name":"Illinois","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6.25% state + up to 4.75% local","has_sales_tax":true,"registration_url":"https://mytax.illinois.gov","notes":""},"IN":{"name":"Indiana","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"7% state","has_sales_tax":true,"registration_url":"https://www.in.gov/dor","notes":""},"IA":{"name":"Iowa","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6% state + up to 2% local","has_sales_tax":true,"registration_url":"https://tax.iowa.gov","notes":"Transaction threshold eliminated Jan 2023"},"KS":{"name":"Kansas","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6.5% state + up to 4% local","has_sales_tax":true,"registration_url":"https://www.ksrevenue.gov","notes":""},"KY":{"name":"Kentucky","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6% state","has_sales_tax":true,"registration_url":"https://revenue.ky.gov","notes":""},"LA":{"name":"Louisiana","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4.45% state + up to 7% local","has_sales_tax":true,"registration_url":"https://latap.revenue.louisiana.gov","notes":"Parish and local taxes may apply separately"},"ME":{"name":"Maine","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"5.5% state","has_sales_tax":true,"registration_url":"https://www.maine.gov/revenue","notes":""},"MD":{"name":"Maryland","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6% state","has_sales_tax":true,"registration_url":"https://interactive.marylandtaxes.gov","notes":""},"MA":{"name":"Massachusetts","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6.25% state","has_sales_tax":true,"registration_url":"https://mtc.dor.state.ma.us","notes":""},"MI":{"name":"Michigan","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6% state","has_sales_tax":true,"registration_url":"https://www.michigan.gov/taxes","notes":""},"MN":{"name":"Minnesota","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6.875% state + up to 2% local","has_sales_tax":true,"registration_url":"https://www.revenue.state.mn.us","notes":""},"MS":{"name":"Mississippi","revenue_threshold":250000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"7% state + up to 1% local","has_sales_tax":true,"registration_url":"https://tap.dor.ms.gov","notes":"Higher $250K threshold"},"MO":{"name":"Missouri","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"4.225% state + up to 5.763% local","has_sales_tax":true,"registration_url":"https://mytax.mo.gov","notes":"Late adopter; effective Jan 2023"},"MT":{"name":"Montana","revenue_threshold":null,"transaction_threshold":null,"threshold_type":"none","tax_rate_range":"0%","has_sales_tax":false,"registration_url":null,"notes":"No state sales tax"},"NE":{"name":"Nebraska","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"5.5% state + up to 2.5% local","has_sales_tax":true,"registration_url":"https://revenue.nebraska.gov","notes":""},"NV":{"name":"Nevada","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6.85% state + up to 1.525% local","has_sales_tax":true,"registration_url":"https://tax.nv.gov","notes":""},"NH":{"name":"New Hampshire","revenue_threshold":null,"transaction_threshold":null,"threshold_type":"none","tax_rate_range":"0%","has_sales_tax":false,"registration_url":null,"notes":"No state sales tax"},"NJ":{"name":"New Jersey","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6.625% state","has_sales_tax":true,"registration_url":"https://www.njportal.com/DOR","notes":""},"NM":{"name":"New Mexico","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"5.125% state + up to 4.3125% local","has_sales_tax":true,"registration_url":"https://tap.state.nm.us","notes":"Gross Receipts Tax (GRT)"},"NY":{"name":"New York","revenue_threshold":500000,"transaction_threshold":100,"threshold_type":"revenue_and_transactions","tax_rate_range":"4% state + up to 4.875% local","has_sales_tax":true,"registration_url":"https://www.tax.ny.gov","notes":"BOTH thresholds must be met"},"NC":{"name":"North Carolina","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4.75% state + up to 3% local","has_sales_tax":true,"registration_url":"https://www.ncdor.gov","notes":""},"ND":{"name":"North Dakota","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"5% state + up to 3.5% local","has_sales_tax":true,"registration_url":"https://www.nd.gov/tax","notes":""},"OH":{"name":"Ohio","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"5.75% state + up to 2.25% local","has_sales_tax":true,"registration_url":"https://gateway.ohio.gov","notes":""},"OK":{"name":"Oklahoma","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"4.5% state + up to 7% local","has_sales_tax":true,"registration_url":"https://oktap.tax.ok.gov","notes":""},"OR":{"name":"Oregon","revenue_threshold":null,"transaction_threshold":null,"threshold_type":"none","tax_rate_range":"0%","has_sales_tax":false,"registration_url":null,"notes":"No state sales tax"},"PA":{"name":"Pennsylvania","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6% state + 2% Philadelphia/Allegheny","has_sales_tax":true,"registration_url":"https://mypath.pa.gov","notes":""},"RI":{"name":"Rhode Island","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"7% state","has_sales_tax":true,"registration_url":"https://www.ri.gov/taxation","notes":""},"SC":{"name":"South Carolina","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6% state + up to 3% local","has_sales_tax":true,"registration_url":"https://mydorway.dor.sc.gov","notes":""},"SD":{"name":"South Dakota","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4.2% state + up to 2% local","has_sales_tax":true,"registration_url":"https://apps.sd.gov/RV23SDTax","notes":"Original Wayfair case state"},"TN":{"name":"Tennessee","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"7% state + up to 2.75% local","has_sales_tax":true,"registration_url":"https://tntap.tn.gov","notes":""},"TX":{"name":"Texas","revenue_threshold":500000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6.25% state + up to 2% local","has_sales_tax":true,"registration_url":"https://mycpa.cpa.state.tx.us","notes":"Higher $500K threshold"},"UT":{"name":"Utah","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4.85% state + up to 2.5% local","has_sales_tax":true,"registration_url":"https://tap.utah.gov","notes":""},"VT":{"name":"Vermont","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6% state + 1% local","has_sales_tax":true,"registration_url":"https://tax.vermont.gov","notes":""},"VA":{"name":"Virginia","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"5.3% state + up to 1% local","has_sales_tax":true,"registration_url":"https://www.tax.virginia.gov","notes":""},"WA":{"name":"Washington","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"6.5% state + up to 4% local","has_sales_tax":true,"registration_url":"https://dor.wa.gov","notes":""},"WV":{"name":"West Virginia","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6% state + up to 1% local","has_sales_tax":true,"registration_url":"https://mytaxes.wvtax.gov","notes":""},"WI":{"name":"Wisconsin","revenue_threshold":100000,"transaction_threshold":null,"threshold_type":"revenue_only","tax_rate_range":"5% state + up to 1.75% local","has_sales_tax":true,"registration_url":"https://tap.revenue.wi.gov","notes":""},"WY":{"name":"Wyoming","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"4% state + up to 2% local","has_sales_tax":true,"registration_url":"https://efile.wyoming.gov","notes":""},"DC":{"name":"District of Columbia","revenue_threshold":100000,"transaction_threshold":200,"threshold_type":"revenue_or_transactions","tax_rate_range":"6% general","has_sales_tax":true,"registration_url":"https://mytax.dc.gov","notes":""}};

// State name -> code mapping
const STATE_NAME_MAP = {"alabama":"AL","alaska":"AK","arizona":"AZ","arkansas":"AR","california":"CA","colorado":"CO","connecticut":"CT","delaware":"DE","florida":"FL","georgia":"GA","hawaii":"HI","idaho":"ID","illinois":"IL","indiana":"IN","iowa":"IA","kansas":"KS","kentucky":"KY","louisiana":"LA","maine":"ME","maryland":"MD","massachusetts":"MA","michigan":"MI","minnesota":"MN","mississippi":"MS","missouri":"MO","montana":"MT","nebraska":"NE","nevada":"NV","new hampshire":"NH","new jersey":"NJ","new mexico":"NM","new york":"NY","north carolina":"NC","north dakota":"ND","ohio":"OH","oklahoma":"OK","oregon":"OR","pennsylvania":"PA","rhode island":"RI","south carolina":"SC","south dakota":"SD","tennessee":"TN","texas":"TX","utah":"UT","vermont":"VT","virginia":"VA","washington":"WA","west virginia":"WV","wisconsin":"WI","wyoming":"WY","district of columbia":"DC","d.c.":"DC","washington dc":"DC","washington d.c.":"DC"};
const VALID_CODES = new Set(Object.keys(THRESHOLDS));

// Average state tax rates (state portion only, for exposure estimate)
const AVG_TAX_RATES = {"AL":0.04,"AZ":0.056,"AR":0.065,"CA":0.0725,"CO":0.029,"CT":0.0635,"FL":0.06,"GA":0.04,"HI":0.04,"ID":0.06,"IL":0.0625,"IN":0.07,"IA":0.06,"KS":0.065,"KY":0.06,"LA":0.0445,"ME":0.055,"MD":0.06,"MA":0.0625,"MI":0.06,"MN":0.06875,"MS":0.07,"MO":0.04225,"NE":0.055,"NV":0.0685,"NJ":0.06625,"NM":0.05125,"NY":0.04,"NC":0.0475,"ND":0.05,"OH":0.0575,"OK":0.045,"PA":0.06,"RI":0.07,"SC":0.06,"SD":0.042,"TN":0.07,"TX":0.0625,"UT":0.0485,"VT":0.06,"VA":0.053,"WA":0.065,"WV":0.06,"WI":0.05,"WY":0.04,"DC":0.06};

// ===== APPLICATION STATE =====
let appState = {
  parsedData: null,
  headers: [],
  rows: [],
  analysisResults: null,
  sortCol: 'status',
  sortDir: 'asc',
  tier: 'free' // 'free' | 'captured'
};

// ===== CSV PARSER ENGINE =====
function parseCSV(text) {
  const lines = text.split(/\r?\n/);
  if (lines.length < 2) return null;

  // Detect delimiter
  const firstLine = lines[0];
  const tabCount = (firstLine.match(/\t/g) || []).length;
  const semiCount = (firstLine.match(/;/g) || []).length;
  const commaCount = (firstLine.match(/,/g) || []).length;
  const delim = tabCount > commaCount ? '\t' : semiCount > commaCount ? ';' : ',';

  function parseLine(line) {
    const result = [];
    let cur = '';
    let inQuote = false;
    for (let i = 0; i < line.length; i++) {
      const ch = line[i];
      if (ch === '"') {
        if (inQuote && line[i+1] === '"') { cur += '"'; i++; }
        else inQuote = !inQuote;
      } else if (ch === delim && !inQuote) {
        result.push(cur.trim()); cur = '';
      } else {
        cur += ch;
      }
    }
    result.push(cur.trim());
    return result;
  }

  const headers = parseLine(lines[0]);
  const rows = [];
  for (let i = 1; i < lines.length; i++) {
    if (lines[i].trim() === '') continue;
    rows.push(parseLine(lines[i]));
  }
  return { headers, rows };
}

function detectColumns(headers) {
  const statePatterns = /state|ship.?to.?state|ship_state|province|shipping.?state|ship.?state|billing.?state|st$|state.?code/i;
  const revenuePatterns = /revenue|amount|total|sales|gross|price|sum|subtotal|order.?total|item.?total|net/i;
  const datePatterns = /date|time|created|order.?date|period|purchased|shipped/i;
  let stateCol = -1, revenueCol = -1, dateCol = -1;
  headers.forEach((h, i) => {
    if (stateCol === -1 && statePatterns.test(h)) stateCol = i;
    if (revenueCol === -1 && revenuePatterns.test(h)) revenueCol = i;
    if (dateCol === -1 && datePatterns.test(h)) dateCol = i;
  });
  return { stateCol, revenueCol, dateCol };
}

function normalizeState(value) {
  if (!value) return null;
  const v = String(value).trim();
  const upper = v.toUpperCase();
  if (upper.length === 2 && VALID_CODES.has(upper)) return upper;
  const lower = v.toLowerCase();
  if (STATE_NAME_MAP[lower]) return STATE_NAME_MAP[lower];
  // Try partial match for names like "New York" in mixed case
  for (const [name, code] of Object.entries(STATE_NAME_MAP)) {
    if (lower.includes(name) || name.includes(lower)) return code;
  }
  return null;
}

function parseCurrency(value) {
  if (!value) return 0;
  const s = String(value).trim();
  const negative = s.startsWith('(') && s.endsWith(')');
  const cleaned = s.replace(/[$,\s()]/g, '');
  const num = parseFloat(cleaned);
  if (isNaN(num)) return 0;
  return negative ? -num : num;
}

// ===== ANALYSIS ENGINE =====
function aggregateByState(rows, stateCol, revenueCol) {
  const stateData = new Map();
  for (const row of rows) {
    const stateCode = normalizeState(row[stateCol]);
    if (!stateCode) continue;
    const revenue = parseCurrency(row[revenueCol]);
    if (!stateData.has(stateCode)) stateData.set(stateCode, { revenue: 0, transactions: 0 });
    const entry = stateData.get(stateCode);
    entry.revenue += revenue;
    entry.transactions++;
  }
  return stateData;
}

function analyzeNexus(stateData) {
  const results = [];
  for (const [code, threshold] of Object.entries(THRESHOLDS)) {
    const data = stateData.get(code) || { revenue: 0, transactions: 0 };
    const { revenue, transactions } = data;
    const hasData = stateData.has(code);

    if (!threshold.has_sales_tax) {
      results.push({
        state: code, name: threshold.name,
        revenue, transactions,
        revenueThreshold: null, txThreshold: null,
        revenuePercent: 0, txPercent: 0,
        status: 'no-tax', hasSalesTax: false,
        notes: threshold.notes, registrationUrl: threshold.registration_url
      });
      continue;
    }

    if (!hasData) {
      results.push({
        state: code, name: threshold.name,
        revenue: 0, transactions: 0,
        revenueThreshold: threshold.revenue_threshold,
        txThreshold: threshold.transaction_threshold,
        revenuePercent: 0, txPercent: 0,
        status: 'green', hasSalesTax: true,
        notes: threshold.notes, registrationUrl: threshold.registration_url
      });
      continue;
    }

    const revPct = threshold.revenue_threshold ? (revenue / threshold.revenue_threshold) * 100 : 0;
    const txPct = threshold.transaction_threshold ? (transactions / threshold.transaction_threshold) * 100 : 0;

    let status;
    const tt = threshold.threshold_type;
    if (tt === 'revenue_only') {
      if (revPct >= 100) status = 'red';
      else if (revPct >= 75) status = 'yellow';
      else status = 'green';
    } else if (tt === 'revenue_or_transactions') {
      const maxPct = Math.max(revPct, txPct);
      if (maxPct >= 100) status = 'red';
      else if (maxPct >= 75) status = 'yellow';
      else status = 'green';
    } else if (tt === 'revenue_and_transactions') {
      // NY: BOTH must exceed
      if (revPct >= 100 && txPct >= 100) status = 'red';
      else if (revPct >= 75 && txPct >= 75) status = 'yellow';
      else status = 'green';
    } else {
      status = 'green';
    }

    results.push({
      state: code, name: threshold.name,
      revenue, transactions,
      revenueThreshold: threshold.revenue_threshold,
      txThreshold: threshold.transaction_threshold,
      revenuePercent: revPct, txPercent: txPct,
      status, hasSalesTax: true,
      notes: threshold.notes, registrationUrl: threshold.registration_url
    });
  }
  return results;
}

function calculateExposure(results) {
  const redStates = results.filter(r => r.status === 'red');
  const yellowStates = results.filter(r => r.status === 'yellow');
  const greenStates = results.filter(r => r.status === 'green');
  let estimatedExposure = 0;
  for (const r of redStates) {
    const rate = AVG_TAX_RATES[r.state] || 0.06;
    estimatedExposure += r.revenue * rate;
  }
  return { redStates: redStates.length, yellowStates: yellowStates.length, greenStates: greenStates.length, estimatedExposure, results };
}

// ===== MAP VISUALIZATION =====
const MAP_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 960 600" id="us-map-inline">
<defs><pattern id="no-tax-stripe" patternUnits="userSpaceOnUse" width="6" height="6" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="#94a3b8" stroke-width="2"/></pattern></defs>
<path id="WA" class="state" d="M110,140 L110,200 L178,200 L178,142 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="OR" class="state" d="M110,200 L112,255 L178,255 L178,202 L145,192 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="CA" class="state" d="M100,250 L105,350 L155,370 L185,310 L185,278 L165,248 L120,235 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="ID" class="state" d="M178,142 L178,255 L215,250 L220,195 L205,155 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NV" class="state" d="M140,245 L143,340 L185,310 L185,278 L165,248 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="AZ" class="state" d="M185,310 L188,420 L270,418 L268,338 L185,335 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MT" class="state" d="M205,155 L218,250 L320,245 L318,150 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="WY" class="state" d="M220,250 L222,300 L318,296 L318,248 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="UT" class="state" d="M185,278 L188,338 L268,338 L266,278 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="CO" class="state" d="M266,278 L268,370 L370,366 L368,275 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NM" class="state" d="M268,338 L270,430 L355,428 L353,336 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="ND" class="state" d="M320,160 L318,238 L445,234 L445,158 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="SD" class="state" d="M318,238 L316,318 L445,314 L445,234 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NE" class="state" d="M316,318 L315,358 L445,354 L445,314 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="KS" class="state" d="M315,358 L316,398 L480,394 L480,354 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="OK" class="state" d="M316,398 L318,438 L490,434 L488,394 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="TX" class="state" d="M318,438 L320,520 L400,540 L470,530 L510,490 L508,434 L370,438 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MN" class="state" d="M445,158 L443,255 L510,253 L514,160 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="IA" class="state" d="M443,255 L441,298 L558,294 L558,253 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MO" class="state" d="M480,298 L480,394 L558,390 L558,294 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="AR" class="state" d="M480,394 L482,440 L588,436 L586,390 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="LA" class="state" d="M482,440 L485,484 L545,490 L590,478 L590,436 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="WI" class="state" d="M510,195 L508,253 L558,251 L558,198 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="IL" class="state" d="M558,253 L556,370 L598,366 L598,251 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MI" class="state" d="M558,198 L556,253 L600,250 L602,198 Z M560,158 L558,195 L610,193 L608,158 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="IN" class="state" d="M598,266 L596,358 L636,355 L636,264 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="OH" class="state" d="M636,264 L634,355 L682,352 L684,263 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="KY" class="state" d="M596,358 L598,394 L684,390 L684,352 L636,355 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="TN" class="state" d="M556,394 L558,424 L690,420 L690,390 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MS" class="state" d="M556,424 L558,484 L594,480 L596,420 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="AL" class="state" d="M596,420 L598,484 L636,480 L638,416 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="GA" class="state" d="M638,416 L640,480 L678,476 L680,412 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="FL" class="state" d="M640,480 L638,490 L660,520 L700,538 L718,520 L702,490 L678,476 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="SC" class="state" d="M680,412 L682,454 L722,448 L720,408 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NC" class="state" d="M690,390 L692,412 L750,406 L748,386 L720,385 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="VA" class="state" d="M684,352 L686,394 L748,390 L750,350 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="WV" class="state" d="M684,300 L684,356 L722,352 L724,300 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="PA" class="state" d="M680,250 L678,300 L750,298 L752,248 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NY" class="state" d="M710,195 L708,250 L752,248 L780,218 L758,197 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MD" class="state" d="M720,300 L722,316 L752,312 L762,300 L752,296 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="DE" class="state" d="M752,266 L754,286 L766,282 L764,264 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NJ" class="state" d="M752,248 L750,266 L768,262 L770,246 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="CT" class="state" d="M760,222 L762,238 L784,234 L782,220 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="RI" class="state" d="M792,214 L794,228 L808,226 L806,212 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="MA" class="state" d="M782,202 L780,218 L820,215 L818,200 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="VT" class="state" d="M775,172 L773,202 L793,200 L793,172 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="NH" class="state" d="M793,162 L791,200 L810,198 L812,164 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<path id="ME" class="state" d="M810,130 L808,180 L840,176 L842,130 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<rect id="DC" class="state" x="726" y="302" width="10" height="10" fill="#e2e8f0" stroke="#fff" stroke-width="1"/>
<!-- Alaska inset -->
<path id="AK" class="state" d="M85,510 L85,570 L200,570 L240,545 L215,510 L165,500 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
<!-- Hawaii inset -->
<path id="HI" class="state" d="M285,548 L300,558 L315,550 L305,540 Z M318,550 L333,560 L348,552 L338,542 Z M352,550 L365,558 L378,550 L368,542 Z" fill="#e2e8f0" stroke="#fff" stroke-width="1.5"/>
</svg>`;

function loadMap() {
  const container = document.getElementById('map-container');
  container.innerHTML = MAP_SVG;
  attachMapEvents();
}

function getStatusColor(status) {
  switch(status) {
    case 'red': return '#dc2626';
    case 'yellow': return '#eab308';
    case 'green': return '#16a34a';
    case 'no-tax': return 'url(#no-tax-stripe)';
    default: return '#e2e8f0';
  }
}

function colorMap(results) {
  const resultMap = {};
  for (const r of results) resultMap[r.state] = r;

  const states = document.querySelectorAll('#us-map-inline .state');
  states.forEach((el, idx) => {
    const code = el.id;
    const result = resultMap[code];
    const color = result ? getStatusColor(result.status) : '#e2e8f0';
    setTimeout(() => {
      el.style.fill = color;
      el.style.transition = 'fill 0.4s ease';
    }, idx * 8);
  });
}

function attachMapEvents() {
  const tooltip = document.getElementById('map-tooltip');
  const states = document.querySelectorAll('#us-map-inline .state');

  states.forEach(el => {
    el.style.cursor = 'pointer';

    el.addEventListener('mouseenter', (e) => {
      const code = el.id;
      const result = appState.analysisResults ? appState.analysisResults.find(r => r.state === code) : null;
      if (!result) { tooltip.style.display = 'none'; return; }

      let html = `<strong>${result.name}</strong>`;
      if (!result.hasSalesTax) {
        html += `<div class="tt-row"><span>No state sales tax</span></div>`;
      } else {
        html += `<div class="tt-row"><span class="tt-label">Your Revenue:</span><span>$${fmtNum(result.revenue)}</span></div>`;
        if (result.revenueThreshold) html += `<div class="tt-row"><span class="tt-label">Rev. Threshold:</span><span>$${fmtNum(result.revenueThreshold)}</span></div>`;
        if (result.txThreshold) html += `<div class="tt-row"><span class="tt-label">Tx Threshold:</span><span>${result.txThreshold}</span></div>`;
        html += `<div class="tt-row"><span class="tt-label">Status:</span><span>${result.status === 'red' ? '🔴 Nexus Triggered' : result.status === 'yellow' ? '🟡 Approaching' : '🟢 Safe'}</span></div>`;
      }
      tooltip.innerHTML = html;
      tooltip.style.display = 'block';
      positionTooltip(e);
      el.style.stroke = '#1e3a5f';
      el.style.strokeWidth = '2.5';
    });

    el.addEventListener('mousemove', positionTooltip);

    el.addEventListener('mouseleave', () => {
      tooltip.style.display = 'none';
      el.style.stroke = '#fff';
      el.style.strokeWidth = '1.5';
    });

    el.addEventListener('click', () => {
      const code = el.id;
      const tbody = document.getElementById('results-tbody');
      if (!tbody) return;
      const row = tbody.querySelector(`[data-state="${code}"]`);
      if (row) {
        row.scrollIntoView({ behavior: 'smooth', block: 'center' });
        row.classList.add('row-highlight');
        setTimeout(() => row.classList.remove('row-highlight'), 1500);
      }
    });
  });
}

function positionTooltip(e) {
  const tooltip = document.getElementById('map-tooltip');
  const tw = tooltip.offsetWidth || 200;
  const th = tooltip.offsetHeight || 80;
  let x = e.clientX + 14;
  let y = e.clientY - 14;
  if (x + tw > window.innerWidth - 8) x = e.clientX - tw - 14;
  if (y + th > window.innerHeight - 8) y = e.clientY - th - 14;
  if (y < 8) y = 8;
  tooltip.style.left = x + 'px';
  tooltip.style.top = y + 'px';
}

// ===== RESULTS DISPLAY =====
function fmtMoney(n) { return '$' + n.toLocaleString('en-US', { maximumFractionDigits: 0 }); }
function fmtNum(n) { return n.toLocaleString('en-US', { maximumFractionDigits: 0 }); }

function statusSort(s) { return s === 'red' ? 0 : s === 'yellow' ? 1 : s === 'green' ? 2 : 3; }

function sortResults(results, col, dir) {
  return [...results].sort((a, b) => {
    let va, vb;
    switch(col) {
      case 'name': va = a.name; vb = b.name; break;
      case 'revenue': va = a.revenue; vb = b.revenue; break;
      case 'revenueThreshold': va = a.revenueThreshold || 0; vb = b.revenueThreshold || 0; break;
      case 'transactions': va = a.transactions; vb = b.transactions; break;
      case 'txThreshold': va = a.txThreshold || 0; vb = b.txThreshold || 0; break;
      case 'revenuePercent': va = a.revenuePercent; vb = b.revenuePercent; break;
      case 'status': va = statusSort(a.status); vb = statusSort(b.status); break;
      default: va = statusSort(a.status); vb = statusSort(b.status);
    }
    if (typeof va === 'string') return dir === 'asc' ? va.localeCompare(vb) : vb.localeCompare(va);
    return dir === 'asc' ? va - vb : vb - va;
  });
}

function renderResults(exposure) {
  const { redStates, yellowStates, greenStates, estimatedExposure, results } = exposure;
  const isGated = appState.tier === 'free';

  // Summary banner
  const banner = document.getElementById('results-summary-banner');
  if (redStates > 0) {
    banner.className = 'results-summary-banner';
    banner.innerHTML = `<div class="banner-icon">⚠️</div><div class="banner-text"><h3>You have potential nexus obligations in ${redStates} state${redStates !== 1 ? 's' : ''}</h3><p>${yellowStates} additional state${yellowStates !== 1 ? 's are' : ' is'} approaching thresholds. Action may be required soon.</p></div>`;
  } else if (yellowStates > 0) {
    banner.className = 'results-summary-banner';
    banner.style.background = 'var(--yellow-100)';
    banner.style.borderLeftColor = 'var(--yellow-500)';
    banner.innerHTML = `<div class="banner-icon">🟡</div><div class="banner-text"><h3>${yellowStates} state${yellowStates !== 1 ? 's are' : ' is'} approaching nexus thresholds</h3><p>Monitor closely - you're at 75%+ of the threshold. No action required yet.</p></div>`;
  } else {
    banner.className = 'results-summary-banner safe';
    banner.innerHTML = `<div class="banner-icon">✅</div><div class="banner-text"><h3>No nexus obligations triggered</h3><p>Your sales are below all state thresholds. Continue monitoring as sales grow.</p></div>`;
  }

  // Stats cards
  const cards = document.getElementById('results-cards');
  cards.innerHTML = `
    <div class="stat-card red"><div class="stat-number">${redStates}</div><div class="stat-label">States with Nexus Triggered</div></div>
    <div class="stat-card yellow"><div class="stat-number">${yellowStates}</div><div class="stat-label">States Approaching (75-99%)</div></div>
    <div class="stat-card green"><div class="stat-number">${greenStates}</div><div class="stat-label">States in Safe Zone</div></div>
  `;

  // Exposure card
  const expAmt = document.getElementById('exposure-amount');
  expAmt.textContent = fmtMoney(estimatedExposure);
  if (!isGated) expAmt.classList.remove('gated-value');
  document.getElementById('unlock-exposure-btn').style.display = isGated ? 'block' : 'none';

  // Table
  renderTable(results);

  // Show section with animation
  const section = document.getElementById('results-section');
  section.style.display = 'block';
  section.classList.add('fade-in');
  section.scrollIntoView({ behavior: 'smooth', block: 'start' });
}

function renderTable(results) {
  const sorted = sortResults(results, appState.sortCol, appState.sortDir);
  const isGated = appState.tier === 'free';
  const tbody = document.getElementById('results-tbody');

  tbody.innerHTML = sorted.map(r => {
    if (!r.hasSalesTax) {
      return `<tr data-state="${r.state}">
        <td><strong>${r.name}</strong> (${r.state})</td>
        <td class="num">-</td><td class="num">-</td><td class="num">-</td><td class="num">-</td>
        <td>-</td>
        <td><span class="badge badge-notax">No Tax</span></td>
        <td><span style="color:var(--gray-500);font-size:0.8rem">${r.notes}</span></td>
      </tr>`;
    }

    const maxPct = Math.max(r.revenuePercent || 0, r.txPercent || 0);
    const barColor = r.status === 'red' ? 'var(--red-600)' : r.status === 'yellow' ? 'var(--yellow-500)' : 'var(--green-600)';
    const barPct = Math.min(maxPct, 100);
    const badge = r.status === 'red' ? '<span class="badge badge-red">Nexus Triggered</span>' :
                  r.status === 'yellow' ? '<span class="badge badge-yellow">Approaching</span>' :
                  '<span class="badge badge-green">Safe</span>';
    const action = r.status === 'red' && r.registrationUrl ?
      `<a href="${r.registrationUrl}" target="_blank" rel="noopener" class="cta-btn" style="padding:6px 12px;font-size:0.8rem">Register - $500</a>` : '-';
    const revenueCell = isGated ? `<span class="gated-value" onclick="showLeadModal()">${fmtMoney(r.revenue)}</span>` : fmtMoney(r.revenue);
    const thresholdCell = r.revenueThreshold ? fmtMoney(r.revenueThreshold) : 'N/A';
    const txCell = r.transactions > 0 ? fmtNum(r.transactions) : '0';
    const txThresholdCell = r.txThreshold ? fmtNum(r.txThreshold) : 'N/A';
    const pctCell = maxPct > 0 ? `<div style="display:flex;align-items:center;gap:8px"><div class="bar-wrap"><div class="bar-fill" style="width:${barPct}%;background:${barColor}"></div></div><span style="font-family:var(--font-mono);font-size:0.85rem">${maxPct.toFixed(0)}%</span></div>` : '0%';

    return `<tr data-state="${r.state}">
      <td><strong>${r.name}</strong> <span style="color:var(--gray-500)">(${r.state})</span></td>
      <td class="num">${revenueCell}</td>
      <td class="num">${thresholdCell}</td>
      <td class="num">${txCell}</td>
      <td class="num">${txThresholdCell}</td>
      <td>${pctCell}</td>
      <td>${badge}</td>
      <td>${action}</td>
    </tr>`;
  }).join('');
}

// Table sort
document.addEventListener('click', (e) => {
  const th = e.target.closest('th[data-col]');
  if (!th || !appState.analysisResults) return;
  const col = th.dataset.col;
  if (appState.sortCol === col) {
    appState.sortDir = appState.sortDir === 'asc' ? 'desc' : 'asc';
  } else {
    appState.sortCol = col;
    appState.sortDir = col === 'status' ? 'asc' : 'desc';
  }
  document.querySelectorAll('.results-table th').forEach(t => t.classList.remove('sort-asc','sort-desc'));
  th.classList.add(appState.sortDir === 'asc' ? 'sort-asc' : 'sort-desc');
  renderTable(appState.analysisResults);
});

// ===== LEAD CAPTURE MODAL =====
function showLeadModal() {
  document.getElementById('lead-modal').classList.add('active');
  setTimeout(() => document.getElementById('field-email').focus(), 100);
}

function hideLeadModal() {
  document.getElementById('lead-modal').classList.remove('active');
}

function unlockTier() {
  appState.tier = 'captured';
  document.querySelectorAll('.gated-value').forEach(el => el.classList.add('unlocked'));
  document.getElementById('unlock-exposure-btn').style.display = 'none';
  document.getElementById('unlock-full-btn').textContent = '📥 Download CSV';
  document.getElementById('unlock-full-btn').onclick = downloadCSV;
  document.getElementById('mobile-cta').style.display = 'none';
}

function downloadCSV() {
  if (!appState.analysisResults) return;
  const rows = [['State','Code','Revenue','Revenue Threshold','Transactions','Tx Threshold','Rev %','Status']];
  for (const r of appState.analysisResults) {
    rows.push([r.name, r.state, r.revenue, r.revenueThreshold || '', r.transactions, r.txThreshold || '', r.revenuePercent.toFixed(1), r.status]);
  }
  const csv = rows.map(r => r.join(',')).join('\n');
  const blob = new Blob([csv], { type: 'text/csv' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url; a.download = 'nexus-analysis.csv'; a.click();
  URL.revokeObjectURL(url);
}

// Form validation & submission
document.getElementById('lead-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const email = document.getElementById('field-email').value.trim();
  const company = document.getElementById('field-company').value.trim();
  let valid = true;

  document.getElementById('err-email').style.display = 'none';
  document.getElementById('err-company').style.display = 'none';
  document.getElementById('form-global-error').style.display = 'none';
  document.getElementById('field-email').classList.remove('error-field');
  document.getElementById('field-company').classList.remove('error-field');

  if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    document.getElementById('err-email').style.display = 'block';
    document.getElementById('field-email').classList.add('error-field');
    valid = false;
  }
  if (!company) {
    document.getElementById('err-company').style.display = 'block';
    document.getElementById('field-company').classList.add('error-field');
    valid = false;
  }
  if (!valid) return;

  const btn = document.getElementById('form-submit-btn');
  btn.disabled = true;
  btn.innerHTML = '<span class="spinner"></span> Sending...';

  const payload = {
    email, company,
    revenueRange: document.getElementById('field-revenue').value,
    phone: document.getElementById('field-phone').value.trim(),
    statesTriggered: appState.analysisResults ? appState.analysisResults.filter(r => r.status === 'red').length : 0
  };

  // Dual lead capture - fire and forget to central API
  fetch('https://api.iamcorpagency.com/api/lead', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email: email,
      page: location.pathname,
      firstName: company || ''
    })
  }).catch(() => {});

  try {
    const res = await fetch('/api/lead-capture', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    if (!res.ok) {
      const err = await res.json().catch(() => ({}));
      throw new Error(err.error || `Request failed (${res.status})`);
    }
    hideLeadModal();
    unlockTier();
  } catch (err) {
    // Fallback: still unlock (client-side capture) and show mailto
    hideLeadModal();
    unlockTier();
    const subject = encodeURIComponent(`Nexus Report Request - ${company}`);
    const body = encodeURIComponent(`Email: ${email}\nCompany: ${company}\nStates Triggered: ${payload.statesTriggered}`);
    window.location.href = `mailto:hello@nexusaudit.com?subject=${subject}&body=${body}`;
  }
  btn.disabled = false;
  btn.innerHTML = 'Unlock My Full Report';
});

document.getElementById('modal-close-btn').addEventListener('click', hideLeadModal);
document.getElementById('lead-modal').addEventListener('click', (e) => {
  if (e.target === document.getElementById('lead-modal')) hideLeadModal();
});
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape') hideLeadModal();
});
document.getElementById('unlock-full-btn').addEventListener('click', showLeadModal);
document.getElementById('unlock-exposure-btn').addEventListener('click', showLeadModal);

// ===== FILE UPLOAD & COLUMN MAPPING =====
const uploadZone = document.getElementById('upload-zone');
const fileInput = document.getElementById('file-input');

uploadZone.addEventListener('click', () => fileInput.click());
uploadZone.addEventListener('dragover', (e) => { e.preventDefault(); uploadZone.classList.add('dragover'); });
uploadZone.addEventListener('dragleave', () => uploadZone.classList.remove('dragover'));
uploadZone.addEventListener('drop', (e) => {
  e.preventDefault();
  uploadZone.classList.remove('dragover');
  const file = e.dataTransfer.files[0];
  if (file) handleFile(file);
});
fileInput.addEventListener('change', () => {
  if (fileInput.files[0]) handleFile(fileInput.files[0]);
});

function showUploadError(msg) {
  const el = document.getElementById('upload-error');
  el.textContent = msg;
  el.style.display = 'block';
  uploadZone.classList.add('error');
}

function clearUploadError() {
  document.getElementById('upload-error').style.display = 'none';
  uploadZone.classList.remove('error');
}

function handleFile(file) {
  clearUploadError();
  if (!file.name.toLowerCase().endsWith('.csv')) {
    showUploadError('Please upload a .csv file.');
    return;
  }
  if (file.size > 50 * 1024 * 1024) {
    showUploadError('File too large. Maximum size is 50MB.');
    return;
  }

  const reader = new FileReader();
  reader.onload = (e) => {
    const parsed = parseCSV(e.target.result);
    if (!parsed || parsed.rows.length === 0) {
      showUploadError('Could not parse the CSV file. Please check the format.');
      return;
    }
    appState.headers = parsed.headers;
    appState.rows = parsed.rows;
    uploadZone.classList.add('has-file');
    uploadZone.querySelector('h3').textContent = `✓ ${file.name} loaded (${fmtNum(parsed.rows.length)} rows)`;
    setupColumnMapping(parsed.headers, parsed.rows);
  };
  reader.onerror = () => showUploadError('Error reading file.');
  reader.readAsText(file);
}

function setupColumnMapping(headers, rows) {
  const { stateCol, revenueCol, dateCol } = detectColumns(headers);

  // Build preview table
  const previewWrap = document.getElementById('preview-wrap');
  const previewRows = rows.slice(0, 5);
  let tableHtml = '<table class="preview-table"><thead><tr>';
  headers.forEach((h, i) => {
    const isDetected = i === stateCol || i === revenueCol || i === dateCol;
    tableHtml += `<th class="${isDetected ? 'detected' : ''}">${h}</th>`;
  });
  tableHtml += '</tr></thead><tbody>';
  previewRows.forEach(row => {
    tableHtml += '<tr>';
    headers.forEach((_, i) => {
      const isDetected = i === stateCol || i === revenueCol || i === dateCol;
      tableHtml += `<td class="${isDetected ? 'detected' : ''}">${row[i] || ''}</td>`;
    });
    tableHtml += '</tr>';
  });
  tableHtml += '</tbody></table>';
  previewWrap.innerHTML = tableHtml;

  // Populate column selectors
  const stateSelect = document.getElementById('col-state');
  const revenueSelect = document.getElementById('col-revenue');
  const dateSelect = document.getElementById('col-date');
  stateSelect.innerHTML = '';
  revenueSelect.innerHTML = '';
  dateSelect.innerHTML = '<option value="-1">Not in this file</option>';

  headers.forEach((h, i) => {
    stateSelect.innerHTML += `<option value="${i}" ${i === stateCol ? 'selected' : ''}>${h}</option>`;
    revenueSelect.innerHTML += `<option value="${i}" ${i === revenueCol ? 'selected' : ''}>${h}</option>`;
    dateSelect.innerHTML += `<option value="${i}" ${i === dateCol ? 'selected' : ''}>${h}</option>`;
  });

  document.getElementById('mapping-section').style.display = 'block';
  document.getElementById('analyze-btn').style.display = 'block';
  document.getElementById('analyze-btn').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}

// Analyze button
document.getElementById('analyze-btn').addEventListener('click', runAnalysis);

function runAnalysis() {
  const stateCol = parseInt(document.getElementById('col-state').value);
  const revenueCol = parseInt(document.getElementById('col-revenue').value);

  if (stateCol < 0 || revenueCol < 0) {
    showUploadError('Please select both state and revenue columns.');
    return;
  }

  const btn = document.getElementById('analyze-btn');
  btn.disabled = true;
  btn.innerHTML = '<span class="spinner"></span> Analyzing...';

  // Defer to allow UI to update
  setTimeout(() => {
    try {
      const stateData = aggregateByState(appState.rows, stateCol, revenueCol);
      const results = analyzeNexus(stateData);
      const exposure = calculateExposure(results);
      appState.analysisResults = results;

      // Show map
      const mapSection = document.getElementById('map-section');
      mapSection.style.display = 'block';
      loadMap();
      colorMap(results);

      // Render results
      renderResults(exposure);

      // Show mobile CTA if on mobile
      if (window.innerWidth < 768 && appState.tier === 'free') {
        document.getElementById('mobile-cta').style.display = 'flex';
      }

    } catch (err) {
      showUploadError('Analysis failed: ' + err.message);
    }
    btn.disabled = false;
    btn.innerHTML = 'Re-analyze';
  }, 50);
}

// ===== NAVIGATION =====
document.getElementById('hamburger-btn').addEventListener('click', () => {
  document.getElementById('nav-mobile').classList.toggle('active');
});

document.querySelectorAll('.nav-mobile a').forEach(a => {
  a.addEventListener('click', () => document.getElementById('nav-mobile').classList.remove('active'));
});

// Mobile CTA close
document.getElementById('mobile-cta-close').addEventListener('click', () => {
  document.getElementById('mobile-cta').style.display = 'none';
});

// Init map in background
loadMap();
</script>
</body>
</html>
