1<!DOCTYPE html> 2<html lang="en"> 3<head> 4<meta charset="utf-8"> 5<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> 6<title>Weapons.com â Training, certification and careers</title> 7<meta name="description" content="The national directory for firearms training, carry permits, professional certifications and industry careers, organized by state."> 8<link rel="canonical" href="https://weapons.com/">
9<script type="application/ld+json"> 10{ 11 "@context": "https://schema.org", 12 "@graph": [ 13 { 14 "@type": "WebSite", 15 "@id": "https://weapons.com/#website", 16 "name": "Weapons.com", 17 "url": "https://weapons.com/", 18 "description": "The national directory for firearms training, carry permits, professional certifications and industry careers, organized by state. Listings are launching state by state.", 19 "publisher": { 20 "@id": "https://weapons.com/#organization" 21 } 22 }, 23 { 24 "@type": "Organization", 25 "@id": "https://weapons.com/#organization", 26 "name": "Weapons.com", 27 "url": "https://weapons.com/", 28 "description": "Weapons.com provides educational information and a directory of third-party providers.", 29 "logo": "https://weapons.com/weapons-brand-logo.png", 30 "sameAs": [ 31 "https://x.com/weapons_com", 32 "https://www.instagram.com/weapons_com/" 33 ] 34 } 35 ] 36} 37</script>
37 38<meta property="og:type" content="website"> 39<meta property="og:site_name" content="Weapons.com"> 40<meta property="og:locale" content="en_US"> 41<meta property="og:url" content="https://weapons.com/"> 42<meta property="og:title" content="Weapons.com â Trained. Licensed. Hired."> 43<meta property="og:description" content="Explore firearms training, carry permits, certifications and careers by state. Listings are launching state by state."> 44<meta property="og:image" content="https://weapons.com/social-preview-brand.png"> 45<meta property="og:image:secure_url" content="https://weapons.com/social-preview-brand.png"> 46<meta property="og:image:type" content="image/png"> 47<meta property="og:image:width" content="1200"> 48<meta property="og:image:height" content="630"> 49<meta property="og:image:alt" content="Weapons.com â Trained. Licensed. Hired. Launching state by state."> 50<meta name="twitter:card" content="summary_large_image"> 51<meta name="twitter:title" content="Weapons.com â Trained. Licensed. Hired."> 52<meta name="twitter:description" content="Explore firearms training, carry permits, certifications and careers by state. Listings are launching state by state."> 53<meta name="twitter:image" content="https://weapons.com/social-preview-brand.png"> 54<meta name="twitter:image:alt" content="Weapons.com â Trained. Licensed. Hired. Launching state by state."> 55<link rel="icon" type="image/png" sizes="192x192" href="/weapons-brand-icon.png"> 56<link rel="shortcut icon" href="/favicon.ico"> 57<link rel="apple-touch-icon" href="/weapons-brand-icon.png"> 58<link rel="preconnect" href="https://fonts.googleapis.com"> 59<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 60<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700;800&family=Barlow:wght@400;500;600&display=swap" rel="stylesheet"> 61<style> 62:root{ 63 --steel:#1A232A; --steel-2:#232F37; --steel-3:#2E3C45; 64 --fog:#EDEFEC;
64 --panel:#FFFFFF; --ink:#1A232A; --muted:#5A6770; --line:#CDD3CF; 65 --brass:#C8973A; --brass-hi:#E2B764; --brass-ink:#7A5716; --olive:#55653E; --olive-soft:#E2E7D8; 66 --on-dark:#E9ECE8; --on-dark-muted:#A7B2B8; --focus:#3D7FC2; --error:#B3261E; 67 --display:"Barlow Condensed","Arial Narrow",Arial,sans-serif; 68 --body:"Barlow","Helvetica Neue",Arial,sans-serif; 69 box-sizing:border-box; 70 padding-top:env(safe-area-inset-top,0px); 71 padding-bottom:env(safe-area-inset-bottom,0px); 72} 73@media (prefers-color-scheme:dark){ 74 :root:not([data-theme="light"]){ 75 --fog:#12191E; --panel:#1C262D; --ink:#E6EAE6; --muted:#9AA6AD; --line:#33424B; 76 --brass-ink:#E2B764; --olive:#A6B887; --olive-soft:#26301F; --focus:#7DB1E8; --error:#FFB4AB; 77 } 78} 79:root[data-theme="dark"]{ 80 --fog:#12191E; --panel:#1C262D; --ink:#E6EAE6; --muted:#9AA6AD; --line:#33424B; 81 --brass-ink:#E2B764; --olive:#A6B887; --olive-soft:#26301F; --focus:#7DB1E8; --error:#FFB4AB; 82} 83*,*::before,*::after{box-sizing:inherit} 84html{scroll-padding-top:calc(104px + env(safe-area-inset-top,0px));-webkit-text-size-adjust:100%} 85body{margin:0;background:var(--fog);color:var(--ink);font:400 17px/1.55 var(--body)} 86img,svg{max-width:100%} 87a{color:inherit} 88:focus-visible{outline:3px solid var(--focus);outline-offset:2px} 89.wrap{max-width:1180px;margin:0 auto;padding:0 22px} 90.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)} 91.bar{position:absolute;top:env(safe-area-inset-top,0px);left:0;right:0;z-index:5} 92.bar .wrap{display:flex;align-items:center;justify-content:space-between;gap:16px;height:104px} 93.logo{display:block;flex:none;text-decoration:none} 94.logo img{display:block;width:280px;height:auto} 95.bar nav a{font-weight:500;text-decoration:none;margin-left:26px;color:var(--on-dark-muted)} 96.bar nav a:hover{color:var(--on-dark)} 97.bar nav a.cta{white-space:nowrap;color:var(--steel);background:var(--brass-hi);padding:9px 16px;border-radius:3px;font-weight:600} 98.bar nav a.cta .sm{display:none} 99@media (max-width:760px){.bar .wrap{height:88px;gap:12px}.bar .logo img{width:clamp(170px,54vw,220px)}.bar nav a:not(.cta){display:none}.bar nav a.cta{font-size:14px;padding:8px 12px;margin-left:0}.bar nav a.cta .lg{display:none}.bar nav a.cta .sm{display:inline}} 100.hero{position:relative;background:var(--steel);color:var(--on-dark);overflow:hidden;isolation:isolate} 101.hero .photo{position:absolute;inset:0;z-index:-2;background: 102 radial-gradient(ellipse at 88% 38%,rgba(200,151,58,.20),transparent 37%), 103 repeating-linear-gradient(145deg,transparent 0 65px,rgba(226,183,100,.025) 66px 67px), 104 linear-gradient(135deg,#25343d 0%,#1a232a 48%,#10181e 100%)} 105.hero .shade{position:absolute;inset:0;z-index:-1;background:linear-gradient(100deg,rgba(26,35,42,.38),transparent 70%),linear-gradient(0deg,var(--steel),transparent 30%)} 106.hero .rings{position:absolute;z-index:-1;right:-180px;top:50%;width:900px;height:900px;transform:translateY(-50%);pointer-events:none} 107.hero .inner{display:grid;grid-template-columns:1.05fr .95fr;gap:48px;align-items:center;padding-top:144px;padding-bottom:80px} 108@media (max-width:980px){.hero .inner{grid-template-columns:1fr;padding-top:120px;padding-bottom:56px;gap:32px}.hero .rings{right:-420px}} 109.hero h1{font:800 clamp(52px,8.4vw,108px)/.88 var(--display);margin:0 0 22px;letter-spacing:-.01em;text-transform:uppercase} 110.hero h1 .line{display:block} 111.hero h1 .line:nth-child(2){color:transparent;-webkit-text-stroke:2px var(--brass-hi)} 112.hero p.lede{font-size:19px;color:var(--on-dark-muted);max-width:46ch;margin:0 0 30px} 113.proof{display:flex;flex-wrap:wrap;gap:12px 28px;margin:0;padding:0;list-style:none} 114.proof li{display:flex;gap:10px;align-items:center;color:var(--on-dark);font-weight:500} 115.proof svg{width:20px;height:20px;flex:none;color:var(--brass-hi)} 116@media (prefers-reduced-motion:no-preference){.hero h1 .line{animation:slam .6s cubic-bezier(.2,.8,.2,1) both}
116.hero h1 .line:nth-child(2){animation-delay:.08s}.hero h1 .line:nth-child(3){animation-delay:.16s}.hero .rings circle{animation:draw 1.6s ease-out both}} 117@keyframes slam{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:none}} 118@keyframes draw{from{stroke-dashoffset:2400}to{stroke-dashoffset:0}} 119.intake{background:var(--panel);color:var(--ink);border-radius:6px;padding:26px;box-shadow:0 30px 60px -20px rgba(0,0,0,.55);border-top:4px solid var(--brass)} 120.progress{display:flex;gap:6px;margin-bottom:16px} 121.progress i{flex:1;height:4px;border-radius:2px;background:var(--line)} 122.progress i.on{background:var(--brass)} 123.qhead{font:700 28px/1.1 var(--display);margin:0 0 4px} 124.qsub{color:var(--muted);margin:0 0 16px;font-size:15px} 125.options{display:grid;grid-template-columns:1fr 1fr;gap:8px} 126@media (max-width:460px){.options{grid-template-columns:1fr}} 127.opt{all:unset;cursor:pointer;display:flex;gap:12px;align-items:flex-start;border:1.5px solid var(--line);border-radius:4px;padding:12px;background:var(--fog);transition:border-color .15s,transform .15s} 128.opt:hover{border-color:var(--brass);transform:translateY(-1px)} 129.opt:focus-visible{outline:3px solid var(--focus);outline-offset:2px} 130.opt svg{width:26px;height:26px;flex:none;color:var(--brass-ink);margin-top:2px} 131.opt b{display:block;font:600 19px/1.15 var(--display)} 132.opt small{display:block;color:var(--muted);font-size:13px;margin-top:2px;line-height:1.35} 133.chips{display:flex;flex-wrap:wrap;gap:8px} 134.chip{all:unset;cursor:pointer;border:1.5px solid var(--line);border-radius:999px;padding:8px 14px;font-weight:500;font-size:15px} 135.chip:hover{border-color:var(--brass)} 136.chip[aria-pressed="true"]{background:var(--ink);color:var(--panel);border-color:var(--ink)} 137.chip:focus-visible{outline:3px solid var(--focus);outline-offset:2px} 138.field{margin:0 0 16px} 139.field label{display:block;font-weight:600;margin-bottom:6px} 140select,input[type=email]{font:inherit;color:var(--ink);background:var(--fog);border:1.5px solid var(--line);border-radius:4px;padding:10px 12px;width:100%;max-width:340px} 141.actions{display:flex;gap:10px;align-items:center;margin-top:18px;flex-wrap:wrap} 142.btn{all:unset;cursor:pointer;font:700 19px/1 var(--display);letter-spacing:.02em;padding:14px 22px;border-radius:3px;background:var(--brass);color:#1B1406;text-transform:uppercase} 143.btn:hover{background:var(--brass-hi)} 144.btn:disabled{opacity:.4;cursor:not-allowed} 145.btn.ghost{background:transparent;color:var(--muted);padding:14px 6px;font:500 15px var(--body);text-transform:none} 146.btn:focus-visible{outline:3px solid var(--focus);outline-offset:2px} 147.result h3{font:700 20px/1.2 var(--display);margin:16px 0 6px;color:var(--brass-ink)} 148.result ul{margin:0;padding-left:18px} 149.result li{margin:3px 0} 150.note{font-size:14px;color:var(--muted)} 151.ok{color:var(--olive);font-weight:600} 152.err{color:var(--error);font-weight:600} 153.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden} 154@media (prefers-reduced-motion:no-preference){.step{animation:rise .25s ease-out}} 155@keyframes rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}} 156.how{background:var(--steel-2);color:var(--on-dark)} 157.how ol{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,1fr)} 158.how li{padding:26px 26px 26px 0;display:grid;grid-template-columns:auto 1fr;gap:16px;align-items:start;border-right:1px solid var(--steel-3)} 159.how li+li{padding-left:26px}.how li:last-child{border-right:0} 160@media (max-width:760px){.how ol{grid-template-columns:1fr}.how li,.how li+li{padding:18px 0;border-right:0;border-bottom:1px solid var(--steel-3)}} 161.how .num{font:800 46px/.8 var(--display);color:var(--brass-hi)} 162.how b{display:block;font:700 22px/1.1 var(--display);margin-bottom:4px} 163.how p{margin:0;color:var(--on-dark-muted);font-size:15px} 164section.block{padding:84px 0} 165.shead{display:flex;justify-content:space-between;align-items:end;gap:20px;flex-wrap:wrap;margin-bottom:30px} 166.shead h2{font:800 clamp(36px,5.4vw,60px)/.95 var(--display);margin:0;text-transform:uppercase} 167.shead p{margin:10px 0 0;color:var(--muted);max-width:52ch} 168.tiles{display:grid;grid-template-columns:repeat(4,1fr);gap:14px} 169.directory-category summary{box-sizing:border-box;min-height:100%;list-style:none} 170.directory-category[data-featured="true"]{grid-column:span 2} 171@media (max-width:520px){.directory-category[data-featured="true"]{grid-column:auto}} 172.directory-category summary::-webkit-details-marker{display:none} 173.directory-category[open] summary{min-height:0} 174.directory-category ul{padding-left:20px} 175.directory-category li{margin:8px 0} 176.st[href]{text-decoration:none} 177@media (max-width:980px){.tiles{grid-template-columns:repeat(2,1fr)}} 178@media (max-width:520px){.tiles{grid-template-columns:1fr}} 179.tile{all:unset;cursor:pointer;position:relative;background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:22px 20px 20px;display:flex;flex-direction:column;gap:10px;min-height:190px;overflow:hidden;transition:border-color .15s,transform .15s} 180.tile:hover{border-color:var(--brass);transform:translateY(-2px)} 181.tile:focus-visible{outline:3px solid var(--focus);outline-offset:2px} 182.tile[aria-expanded="true"]{border-color:var(--brass);box-shadow:inset 0 -4px 0 var(--brass)} 183.tile svg.ic{width:40px;height:40px;color:var(--brass-ink)} 184.tile h3{font:700 25px/1.05 var(--display);margin:0} 185.tile p{margin:0;color:var(--muted);font-size:14.5px;line-height:1.45} 186.tile .count{margin-top:auto;font-size:13px;font-weight:600;color:var(--olive)} 187.tile.featured{grid-column:span 2;background:var(--steel);color:var(--on-dark);border-color:var(--steel)} 188.tile.featured p{color:var(--on-dark-muted)} 189.tile.featured svg.ic,.tile.featured .count{color:var(--brass-hi)} 190.tile.dark{grid-column:auto} 191.tile.featured .bg{position:absolute;right:-60px;bottom:-80px;width:280px;height:280px;opacity:.12;color:var(--brass-hi);pointer-events:none} 192@media (max-width:520px){.tile.featured{grid-column:auto}} 193.panel{grid-column:1/-1;background:var(--panel);border:1px solid var(--brass);border-radius:6px;padding:22px} 194.panel h4{font:700 24px/1.1 var(--display);margin:0 0 12px} 195.sub{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:2px 28px}
196.sub a{display:flex;justify-content:space-between;gap:10px;padding:10px 0;border-bottom:1px dashed var(--line);text-decoration:none} 197.sub a:hover{color:var(--brass-ink)} 198.sub a em{font-style:normal;font-size:12.5px;color:var(--muted);white-space:nowrap} 199.mapwrap{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,.6fr);gap:40px;align-items:center;min-width:0} 200@media (max-width:900px){.mapwrap{grid-template-columns:1fr}} 201.mapwrap>div{min-width:0} 202.map-scroller{width:100%;max-width:100%;overflow-x:auto;overscroll-behavior-inline:contain} 203.tilemap{display:grid;grid-template-columns:repeat(11,minmax(44px,1fr));gap:5px} 204.map-hint{display:none;color:var(--muted);font-size:13px;margin:8px 0 0} 205@media (max-width:900px){.tilemap{min-width:550px}.map-hint{display:block}.st{font-size:15px}} 206.st{all:unset;cursor:pointer;aspect-ratio:1;display:grid;place-items:center;font:700 clamp(11px,1.5vw,16px)/1 var(--display);background:var(--panel);border:1px solid var(--line);border-radius:3px;color:var(--muted);transition:background .12s,color .12s} 207.st:hover{background:var(--olive-soft);color:var(--ink);border-color:var(--olive)} 208.st:focus-visible{outline:3px solid var(--focus);outline-offset:1px} 209.st[aria-pressed="true"]{background:var(--brass);border-color:var(--brass);color:#1B1406} 210.stateinfo h3{font:800 44px/.95 var(--display);margin:0 0 10px;text-transform:uppercase} 211.stateinfo p{color:var(--muted);margin:0 0 18px} 212.stateinfo ul{list-style:none;padding:0;margin:0 0 22px} 213.stateinfo li{padding:9px 0;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;gap:10px} 214.stateinfo li span{color:var(--muted);font-size:14px} 215.pro{background:var(--steel);color:var(--on-dark);position:relative;overflow:hidden;isolation:isolate} 216.pro .inner{display:grid;grid-template-columns:1fr 1fr;gap:40px;padding-top:84px;padding-bottom:84px;align-items:center} 217@media (max-width:860px){.pro .inner{grid-template-columns:1fr;padding-top:60px;padding-bottom:60px}} 218.pro h2{font:800 clamp(38px,5.4vw,62px)/.92 var(--display);margin:0 0 16px;text-transform:uppercase} 219.pro p{color:var(--on-dark-muted);max-width:46ch;margin:0 0 26px} 220.pro ul{list-style:none;padding:0;margin:0;display:grid;gap:12px} 221.pro li{background:var(--steel-2);border-left:3px solid var(--brass-hi);padding:16px 18px;border-radius:0 4px 4px 0} 222.pro li b{display:block;font:700 21px/1.1 var(--display);margin-bottom:2px} 223.pro li span{color:var(--on-dark-muted);font-size:15px} 224.pro .grid-bg{position:absolute;inset:0;z-index:-1;opacity:.07;background-image:linear-gradient(var(--on-dark) 1px,transparent 1px),linear-gradient(90deg,var(--on-dark) 1px,transparent 1px);background-size:44px 44px} 225footer{background:var(--steel);color:var(--on-dark-muted);border-top:1px solid var(--steel-3);padding:40px 0 48px;font-size:14px} 226footer .row{display:flex;justify-content:space-between;gap:24px;flex-wrap:wrap;margin-bottom:20px} 227footer .logo img{width:260px;max-width:100%} 228footer nav a{margin-right:20px;text-decoration:none} 229footer nav a:hover{color:var(--on-dark)} 230footer .social-links{display:flex;gap:10px;align-items:center} 231footer .social-links a{display:grid;place-items:center;width:44px;height:44px;margin:0;border:1px solid var(--steel-3);border-radius:4px;background:var(--steel-2);color:var(--on-dark-muted)} 232footer .social-links a:hover{color:var(--brass-hi);border-color:var(--brass)} 233footer .social-links svg{width:21px;height:21px} 234footer p{max-width:80ch;margin:0} 235.toast{position:fixed;left:50%;bottom:0;transform:translateX(-50%);margin-bottom:calc(16px + env(safe-area-inset-bottom,0px));background:var(--ink);color:var(--panel);padding:11px 18px;border-radius:4px;font-size:15px;opacity:0;pointer-events:none;transition:opacity .2s;z-index:10} 236.toast.show{opacity:1} 237</style> 238</head> 239<body> 240<svg width="0" height="0" style="position:absolute" aria-hidden="true"> 241 <defs> 242 <symbol id="i-target" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="9.5"/><circle cx="12" cy="12" r="5.5"/><circle cx="12" cy="12" r="1.6" fill="currentColor"/><path d="M12 0v4M12 20v4M0 12h4M20 12h4"/></g></symbol>
243 <symbol id="i-card" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2" y="5" width="20" height="14" rx="2"/><circle cx="8" cy="11" r="2.3"/><path d="M4.8 16c.6-1.8 1.8-2.6 3.2-2.6s2.6.8 3.2 2.6M14 10h5M14 13.5h4"/></g></symbol> 244 <symbol id="i-badge" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><path d="M12 2l8 3v6c0 5-3.4 9-8 11-4.6-2-8-6-8-11V5z"/><path d="M8.5 12l2.4 2.4L15.8 9.5"/></g></symbol> 245 <symbol id="i-pin" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><path d="M12 22s-7-6.2-7-12a7 7 0 0114 0c0 5.8-7 12-7 12z"/><circle cx="12" cy="10" r="2.6"/></g></symbol> 246 <symbol id="i-case" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><rect x="2.5" y="7" width="19" height="13" rx="2"/><path d="M8.5 7V5a2 2 0 012-2h3a2 2 0 012 2v2M2.5 12.5h19M11 12.5v2h2v-2"/></g></symbol> 247 <symbol id="i-scale" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><path d="M12 3v18M7 21h10M4 6h16M6.5 6L3 13a3.5 3.5 0 007 0zM17.5 6L14 13a3.5 3.5 0 007 0z"/></g></symbol> 248 <symbol id="i-cross" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="7.5"/><path d="M12 1.5v6M12 16.5v6M1.5 12h6M16.5 12h6"/></g></symbol> 249 <symbol id="i-store" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><path d="M3 9l1.5-5h15L21 9M3 9h18v2.2a2.8 2.8 0 01-5.2 1.4 3 3 0 01-5.4 0A2.8 2.8 0 013 11.2zM4.5 13.5V21h15v-7.5M10 21v-5h4v5"/></g></symbol> 250 <symbol id="i-compass" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-width="1.6"><circle cx="12" cy="12" r="9.5"/><path d="M15.5 8.5l-2 5-5 2 2-5z"/></g></symbol> 251 <symbol id="i-check" viewBox="0 0 24 24"><path d="M4 12.5l5 5L20 6.5" fill="none" stroke="currentColor" stroke-width="2.4"/></symbol> 252 </defs> 253</svg> 254<header class="bar"> 255 <div class="wrap"> 256 <a class="logo" href="#top" aria-label="Weapons.com home"><img src="/weapons-brand-logo.png" width="1410" height="400" alt="Weapons.com â National directory: training, permits, careers"></a> 257 <nav aria-label="Main"> 258 <a href="#directory">Directory</a> 259 <a href="#states">States</a> 260 <a href="#directory" data-jump="careers">Careers</a> 261 <a class="cta" href="#pros"><span class="lg">List your business</span><span class="sm">For pros</span></a> 262 </nav> 263 </div> 264</header> 265<main id="top"> 266<section class="hero" aria-labelledby="h1"> 267 <div class="photo" aria-hidden="true"></div> 268 <div class="shade" aria-hidden="true"></div> 269 <svg class="rings" viewBox="0 0 900 900" aria-hidden="true"> 270 <g fill="none" stroke="#E2B764" stroke-dasharray="2400"> 271 <circle cx="450" cy="450" r="440" stroke-width="1" opacity=".22"/> 272 <circle cx="450" cy="450" r="360" stroke-width="1" opacity=".28"/> 273 <circle cx="450" cy="450" r="280" stroke-width="1.2" opacity=".34"/> 274 <circle cx="450" cy="450" r="200" stroke-width="1.4" opacity=".4"/> 275 <circle cx="450" cy="450" r="120" stroke-width="1.6" opacity=".5"/> 276 <circle cx="450" cy="450" r="44" stroke-width="2" opacity=".6"/> 277 </g> 278 <g stroke="#E2B764" opacity=".3"><path d="M450 0v380M450 520v380M0 450h380M520 450h380"/></g> 279 </svg> 280 <div class="wrap inner"> 281 <div> 282 <h1 id="h1"><span class="line">Trained.</span><span class="line">Licensed.</span><span class="line">Hired.</span></h1> 283 <p class="lede">The national directory for firearms training, carry permits, professional certifications and industry careers. Tell us what you need and we'll map the path for your state.</p> 284 <ul class="proof"> 285 <li><svg><use href="#i-check"/></svg>All 50 states and DC</li> 286 <li><svg><use href="#i-check"/></svg>Listings opening state by state</li> 287 <li><svg><use href="#i-check"/></svg>Free to explore</li> 288 </ul> 289 </div> 290 <div class="intake" id="intake" aria-live="polite"> 291 <div class="step"> 292 <div class="progress" aria-hidden="true"><i class="on"></i><i></i><i></i></div> 293 <p class="qhead">What brings you here?</p> 294 <p class="qsub">Three taps to your personalized path.</p> 295 <div class="options"> 296 <a class="opt" href="#directory" data-intent="permit"><svg aria-hidden="true"><use href="#i-card"/></svg><span><b>Get my carry permit</b><small>First permit, renewal or non-resident</small></span></a> 297 <a class="opt" href="#directory" data-intent="training"><svg aria-hidden="true"><use href="#i-target"/></svg><span><b>Find a class</b><small>Instructors and skill-building</small></span></a> 298 <a class="opt" href="#directory" data-intent="cert"><svg aria-hidden="true"><use href="#i-badge"/></svg><span><b>Get certified</b><small>Security, instructor, RSO, LE</small></span></a> 299 <a class="opt" href="#directory" data-intent="job"><svg aria-hidden="true"><use href="#i-case"/></svg><span><b>Find a job</b><small>Security, training, ranges, retail</small></span></a> 300 <a class="opt" href="#directory" data-intent="laws"><svg aria-hidden="true"><use href="#i-scale"/></svg><span><b>Check the laws</b><small>My state or travel</small></span></a> 301 <a class="opt" href="#directory" data-intent="gear"><svg aria-hidden="true"><use href="#i-cross"/></svg><span><b>Compare prices</b><small>Ammo, optics, gear</small></span></a> 302 <a class="opt" href="#pros" data-intent="business"><svg aria-hidden="true"><use href="#i-store"/></svg><span><b>List my business</b><small>Instructors, ranges, employers</small></span></a> 303 <a class="opt" href="#directory" data-intent="explore"><svg aria-hidden="true"><use href="#i-compass"/></svg><span><b>Just looking</b><small>History, collecting, curiosity</small></span></a> 304 </div> 305 <noscript><p class="note">Explore the directory topics below. Enable JavaScript for a personalized state path and launch notifications.</p></noscript> 306 </div> 307 </div> 308 </div> 309</section>
310<section class="how" aria-label="How it works"> 311 <div class="wrap"><ol> 312 <li><span class="num">1</span><div><b>Pick your goal</b><p>Permit, class, certification, job or the law.</p></div></li> 313 <li><span class="num">2</span><div><b>Choose your state</b><p>Requirements change at every state line.</p></div></li> 314 <li><span class="num">3</span><div><b>Get your path</b><p>Explore the relevant topics and get notified when listings open.</p></div></li> 315 </ol></div> 316</section> 317<section class="block" id="directory" aria-labelledby="dirh"> 318 <div class="wrap"> 319 <div class="shead"> 320 <div> 321 <h2 id="dirh">The directory</h2> 322 <p>Explore licenses, certifications, programs and careers by state. Provider listings are opening soon.</p> 323 </div> 324 <div class="field" style="margin:0"> 325 <label for="dirstate">Filter by state</label> 326 <select id="dirstate" disabled> 327 <option value="">All states</option> 328 <option>Alabama</option><option>Alaska</option><option>Arizona</option><option>Arkansas</option> 329 <option>California</option><option>Colorado</option><option>Connecticut</option><option>Delaware</option> 330 <option>District of Columbia</option><option>Florida</option><option>Georgia</option><option>Hawaii</option> 331 <option>Idaho</option><option>Illinois</option><option>Indiana</option><option>Iowa</option> 332 <option>Kansas</option><option>Kentucky</option><option>Louisiana</option><option>Maine</option> 333 <option>Maryland</option><option>Massachusetts</option><option>Michigan</option><option>Minnesota</option> 334 <option>Mississippi</option><option>Missouri</option><option>Montana</option><option>Nebraska</option> 335 <option>Nevada</option><option>New Hampshire</option><option>New Jersey</option><option>New Mexico</option> 336 <option>New York</option><option>North Carolina</option><option>North Dakota</option><option>Ohio</option> 337 <option>Oklahoma</option><option>Oregon</option><option>Pennsylvania</option><option>Rhode Island</option> 338 <option>South Carolina</option><option>South Dakota</option><option>Tennessee</option><option>Texas</option> 339 <option>Utah</option><option>Vermont</option><option>Virginia</option><option>Washington</option> 340 <option>West Virginia</option><option>Wisconsin</option><option>Wyoming</option> 341 </select> 342 </div> 343 </div> 344 <!-- Initial HTML is the source for the enhanced directory taxonomy. --> 345 <div class="tiles" id="tiles"> 346 <details class="directory-category" data-cat="licenses" data-icon="i-card" data-featured="true" id="cat-licenses"> 347 <summary class="tile featured"><svg class="bg" aria-hidden="true"><use href="#i-card"/></svg><svg class="ic" aria-hidden="true"><use href="#i-card"/></svg><h3>Licenses and permits</h3><p>Carry permits, purchase permits and safety certificates. Explore the requirements and steps in your state.</p><span class="count">7 topics</span></summary> 348 <div class="panel"><p>Provider listings are opening soon. Topics to explore:</p><ul> 349 <li>Concealed carry permit (CCW / CPL / LTC)</li> 350 <li>Carry permit renewal</li> 351 <li>Non-resident carry permits</li> 352 <li>Purchase and ownership permits</li> 353 <li>Firearm safety certificates</li> 354 <li>Carry reciprocity by state</li> 355 <li>Permitless carry states</li> 356 </ul></div> 357 </details> 358 <details class="directory-category" data-cat="certs" data-icon="i-badge" id="cat-certs"> 359 <summary class="tile"><svg class="ic" aria-hidden="true"><use href="#i-badge"/></svg><h3>Professional certifications</h3><p>Credentials for people who carry, teach or supervise for a living.</p><span class="count">7 topics</span></summary> 360 <div class="panel"><p>Provider listings are opening soon. Topics to explore:</p><ul> 361 <li>Armed security officer license</li> 362 <li>Armed guard requalification</li> 363 <li>Firearms instructor certification</li> 364 <li>Range safety officer</li> 365 <li>Law enforcement firearms qualification</li> 366 <li>Executive protection</li> 367 <li>Armorer certification</li> 368 </ul></div> 369 </details> 370 <details class="directory-category" data-cat="programs" data-icon="i-target" id="cat-programs"> 371 <summary class="tile"><svg class="ic" aria-hidden="true"><use href="#i-target"/></svg><h3>Training programs</h3><p>Classes from first-time owner to advanced tactics.</p><span class="count">
37110 topics</span></summary> 372 <div class="panel"><p>Provider listings are opening soon. Topics to explore:</p><ul> 373 <li>Basic pistol</li> 374 <li>Defensive handgun</li> 375 <li>Rifle and carbine</li> 376 <li>Shotgun</li> 377 <li>Low-light shooting</li> 378 <li>Force-on-force scenarios</li> 379 <li>Trauma and bleeding control</li> 380 <li>Hunter education</li> 381 <li>Women-only classes</li> 382 <li>Youth programs</li> 383 </ul></div> 384 </details> 385 <details class="directory-category" data-cat="providers" data-icon="i-pin" id="cat-providers"> 386 <summary class="tile"><svg class="ic" aria-hidden="true"><use href="#i-pin"/></svg><h3>Instructors and ranges</h3><p>Find instructors, training companies and ranges as listings open.</p><span class="count">6 topics</span></summary> 387 <div class="panel"><p>Provider listings are opening soon. Topics to explore:</p><ul> 388 <li>Find an instructor</li> 389 <li>Find a training company</li> 390 <li>Indoor ranges</li> 391 <li>Outdoor ranges</li> 392 <li>Private lessons</li> 393 <li>Group and corporate training</li> 394 </ul></div> 395 </details> 396 <details class="directory-category" data-cat="careers" data-icon="i-case" data-dark="true" id="cat-careers"> 397 <summary class="tile featured dark"><svg class="ic" aria-hidden="true"><use href="#i-case"/></svg><h3>Careers</h3><p>Explore jobs that require firearms credentials and pathways to qualify.</p><span class="count">7 topics</span></summary> 398 <div class="panel"><p>Provider listings are opening soon. Topics to explore:</p><ul> 399 <li>Armed security officer</li> 400 <li>Firearms instructor</li> 401 <li>Range safety officer</li> 402 <li>Gunsmith</li> 403 <li>Armorer</li> 404 <li>Firearms retail and FFL staff</li> 405 <li>Executive protection agent</li> 406 </ul></div> 407 </details> 408 <details class="directory-category" data-cat="laws" data-icon="i-scale" id="cat-laws"> 409 <summary class="tile"><svg class="ic" aria-hidden="true"><use href="#i-scale"/></svg><h3>Laws and travel</h3><p>Plain-language topics for carry, storage and transport.</p><span class="count">5 topics</span></summary> 410 <div class="panel"><p>Provider listings are opening soon. Topics to explore:</p><ul> 411 <li>State carry laws</li> 412 <li>Transporting firearms across states</li> 413 <li>Flying with firearms</li> 414 <li>Storage requirements</li> 415 <li>Where carry is prohibited</li> 416 </ul></div> 417 </details> 418 <details class="directory-category" data-cat="gear" data-icon="i-cross" id="cat-gear"> 419 <summary class="tile"><svg class="ic" aria-hidden="true"><use href="#i-cross"/></svg><h3>Gear and ammo</h3><p>Price tracking and comparisons, opening after launch.</p><span class="count">4 topics</span></summary> 420 <div class="panel"><p>Coming soon after launch. Topics to explore:</p><ul> 421 <li>Ammo price tracker</li> 422 <li>Optics comparisons</li> 423 <li>Holsters and carry gear</li> 424 <li>Safes and storage</li> 425 </ul></div> 426 </details> 427 </div> 428 </div> 429</section> 430<section class="block" id="states" aria-labelledby="sth" style="padding-top:0"> 431 <div class="wrap"> 432 <div class="shead"><div><h2 id="sth">Start with your state</h2><p>Carry, purchase and training rules differ across all 50 states and DC. Pick yours.</p></div></div> 433 <div class="mapwrap"> 434 <div> 435 <div class="map-scroller" tabindex="0" aria-label="Scroll to see more states"> 436 <!-- Real in-page links without JavaScript; enhanced to state selectors. --> 437 <div class="tilemap" id="tilemap" role="group" aria-label="Choose a state"> 438 <a class="st" href="#stateinfo" data-name="Alaska" data-col="0" data-row="0" style="grid-column:1;grid-row:1" aria-label="Alaska" title="Alaska">AK</a> 439 <a class="st" href="#stateinfo" data-name="Maine" data-col="10" data-row="0" style="grid-column:11;grid-row:1" aria-label="Maine" title="Maine">ME</a> 440 <a class="st" href="#stateinfo" data-name="Wisconsin" data-col="5" data-row="1" style="grid-column:6;grid-row:2" aria-label="Wisconsin" title="Wisconsin">WI</a> 441 <a class="st" href="#stateinfo" data-name="Vermont" data-col="9" data-row="1" style="grid-column:10;grid-row:2" aria-label="Vermont" title="Vermont">VT</a> 442 <a class="st" href="#stateinfo" data-name="New Hampshire" data-col="10" data-row="1" style="grid-column:11;grid-row:2" aria-label="New Hampshire" title="New Hampshire">NH</a> 443 <a class="st" href="#stateinfo" data-name="Washington" data-col="0" data-row="2" style="grid-column:1;grid-row:3" aria-label="Washington" title="Washington">WA</a> 444 <a class="st" href="#stateinfo" data-name="Idaho" data-col="1" data-row="2" style="grid-column:2;grid-row:3" aria-label="Idaho" title="Idaho">ID</a> 445 <a class="st" href="#stateinfo" data-name="Montana" data-col="2" data-row="2" style="grid-column:3;grid-row:3" aria-label="Montana" title="Montana">
445MT</a> 446 <a class="st" href="#stateinfo" data-name="North Dakota" data-col="3" data-row="2" style="grid-column:4;grid-row:3" aria-label="North Dakota" title="North Dakota">ND</a> 447 <a class="st" href="#stateinfo" data-name="Minnesota" data-col="4" data-row="2" style="grid-column:5;grid-row:3" aria-label="Minnesota" title="Minnesota">MN</a> 448 <a class="st" href="#stateinfo" data-name="Illinois" data-col="5" data-row="2" style="grid-column:6;grid-row:3" aria-label="Illinois" title="Illinois">IL</a> 449 <a class="st" href="#stateinfo" data-name="Michigan" data-col="6" data-row="2" style="grid-column:7;grid-row:3" aria-label="Michigan" title="Michigan">MI</a> 450 <a class="st" href="#stateinfo" data-name="New York" data-col="8" data-row="2" style="grid-column:9;grid-row:3" aria-label="New York" title="New York">NY</a> 451 <a class="st" href="#stateinfo" data-name="Massachusetts" data-col="9" data-row="2" style="grid-column:10;grid-row:3" aria-label="Massachusetts" title="Massachusetts">MA</a> 452 <a class="st" href="#stateinfo" data-name="Oregon" data-col="0" data-row="3" style="grid-column:1;grid-row:4" aria-label="Oregon" title="Oregon">OR</a> 453 <a class="st" href="#stateinfo" data-name="Nevada" data-col="1" data-row="3" style="grid-column:2;grid-row:4" aria-label="Nevada" title="Nevada">NV</a> 454 <a class="st" href="#stateinfo" data-name="Wyoming" data-col="2" data-row="3" style="grid-column:3;grid-row:4" aria-label="Wyoming" title="Wyoming">WY</a> 455 <a class="st" href="#stateinfo" data-name="South Dakota" data-col="3" data-row="3" style="grid-column:4;grid-row:4" aria-label="South Dakota" title="South Dakota">SD</a> 456 <a class="st" href="#stateinfo" data-name="Iowa" data-col="4" data-row="3" style="grid-column:5;grid-row:4" aria-label="Iowa" title="Iowa">IA</a> 457 <a class="st" href="#stateinfo" data-name="Indiana" data-col="5" data-row="3" style="grid-column:6;grid-row:4" aria-label="Indiana" title="Indiana">IN</a> 458 <a class="st" href="#stateinfo" data-name="Ohio" data-col="6" data-row="3" style="grid-column:7;grid-row:4" aria-label="Ohio" title="Ohio">OH</a> 459 <a class="st" href="#stateinfo" data-name="Pennsylvania" data-col="7" data-row="3" style="grid-column:8;grid-row:4" aria-label="Pennsylvania" title="Pennsylvania">PA</a> 460 <a class="st" href="#stateinfo" data-name="New Jersey" data-col="8" data-row="3" style="grid-column:9;grid-row:4" aria-label="New Jersey" title="New Jersey">NJ</a> 461 <a class="st" href="#stateinfo" data-name="Connecticut" data-col="9" data-row="3" style="grid-column:10;grid-row:4" aria-label="Connecticut" title="Connecticut">CT</a> 462 <a class="st" href="#stateinfo" data-name="Rhode Island" data-col="10" data-row="3" style="grid-column:11;grid-row:4" aria-label="Rhode Island" title="Rhode Island">RI</a> 463 <a class="st" href="#stateinfo" data-name="California" data-col="0" data-row="4" style="grid-column:1;grid-row:5" aria-label="California" title="California">CA</a> 464 <a class="st" href="#stateinfo" data-name="Utah" data-col="1" data-row="4" style="grid-column:2;grid-row:5" aria-label="Utah" title="Utah">UT</a> 465 <a class="st" href="#stateinfo" data-name="Colorado" data-col="2" data-row="4" style="grid-column:3;grid-row:5" aria-label="Colorado" title="Colorado">CO</a> 466 <a class="st" href="#stateinfo" data-name="Nebraska" data-col="3" data-row="4" style="grid-column:4;grid-row:5" aria-label="Nebraska" title="Nebraska">NE</a> 467 <a class="st" href="#stateinfo" data-name="Missouri" data-col="4" data-row="4" style="grid-column:5;grid-row:5" aria-label="Missouri" title="Missouri">MO</a> 468 <a class="st" href="#stateinfo" data-name="Kentucky" data-col="5" data-row="4" style="grid-column:6;grid-row:5" aria-label="Kentucky" title="Kentucky">KY</a> 469 <a class="st" href="#stateinfo" data-name="West Virginia" data-col="6" data-row="4" style="grid-column:7;grid-row:5" aria-label="West Virginia" title="West Virginia">WV</a> 470 <a class="st" href="#stateinfo" data-name="Virginia" data-col="7" data-row="4" style="grid-column:8;grid-row:5" aria-label="Virginia" title="Virginia">VA</a> 471 <a class="st" href="#stateinfo" data-name="Maryland" data-col="8" data-row="4" style="grid-column:9;grid-row:5" aria-label="Maryland" title="Maryland">MD</a> 472 <a class="st" href="#stateinfo" data-name="Delaware" data-col="9" data-row="4" style="grid-column:10;grid-row:5" aria-label="Delaware" title="Delaware">DE</a> 473 <a class="st" href="#stateinfo" data-name="Arizona" data-col="1" data-row="5" style="grid-column:2;grid-row:6" aria-label="Arizona" title="Arizona">AZ</a> 474 <a class="st" href="#stateinfo" data-name="New Mexico" data-col="2" data-row="5" style="grid-column:3;grid-row:6" aria-label="New Mexico" title="New Mexico">NM</a> 475 <a class="st" href="#stateinfo" data-name="Kansas" data-col="3" data-row="5" style="grid-column:4;grid-row:6" aria-label="Kansas" title="Kansas">KS</a> 476 <a class="st" href="#stateinfo" data-name="Arkansas" data-col="4" data-row="5" style="grid-column:5;grid-row:6" aria-label="Arkansas" title="Arkansas">AR</a> 477 <a class="st" href="#stateinfo" data-name="Tennessee" data-col="5" data-row="5" style="grid-column:6;grid-row:6" aria-label="Tennessee" title="Tennessee">TN</a> 478 <a class="st" href="#stateinfo" data-name="North Carolina" data-col="6" data-row="5" style="grid-column:7;grid-row:6" aria-label="North Carolina" title="North Carolina">NC</a> 479 <a class="st" href="#stateinfo" data-name="South Carolina" data-col="7" data-row="5" style="grid-column:8;grid-row:6" aria-label="South Carolina" title="South Carolina">SC</a> 480 <a class="st" href="#stateinfo" data-name="District of Columbia" data-col="8" data-row="5" style="grid-column:9;grid-row:6" aria-label="District of Columbia" title="District of Columbia">DC</a> 481 <a class="st" href="#stateinfo" data-name="Oklahoma" data-col="3" data-row="6" style="grid-column:4;grid-row:7" aria-label="Oklahoma" title="Oklahoma">OK</a> 482 <a class="st" href="#stateinfo" data-name="Louisiana" data-col="4" data-row="6" style="grid-column:5;grid-row:7" aria-label="Louisiana" title="Louisiana">LA</a> 483 <a class="st" href="#stateinfo" data-name="Mississippi" data-col="5" data-row="6" style="grid-column:6;grid-row:7" aria-label="Mississippi" title="Mississippi">MS</a> 484 <a class="st" href="#stateinfo" data-name="Alabama" data-col="6" data-row="6" style="grid-column:7;grid-row:7" aria-label="Alabama" title="Alabama">AL</a> 485 <a class="st" href="#stateinfo" data-name="Georgia" data-col="7" data-row="6" style="grid-column:8;grid-row:7" aria-label="Georgia" title="Georgia">GA</a> 486 <a class="st" href="#stateinfo" data-name="Hawaii" data-col="0" data-row="7" style="grid-column:1;grid-row:8" aria-label="Hawaii" title="Hawaii">HI</a> 487 <a class="st" href="#stateinfo" data-name="Texas" data-col="3" data-row="7" style="grid-column:4;grid-row:8" aria-label="Texas" title="Texas">TX</a> 488 <a class="st" href="#stateinfo" data-name="Florida" data-col="8" data-row="7" style="grid-column:9;grid-row:8" aria-label="Florida" title="Florida">FL</a> 489 </div> 490 </div> 491 <p class="map-hint">Swipe sideways to see all states.</p> 492 </div>
493 <div class="stateinfo" id="stateinfo" aria-live="polite"> 494 <h3>Pick a state</h3> 495 <p>Select any state to explore topics and set the filter across the site. Topics to explore as listings go live across all 50 states and DC:</p> 496 <ul><li>Carry and purchase permits<span>Requirements and steps</span></li><li>Training<span>Classes and instructors</span></li><li>Professional licenses<span>Security and instructor</span></li><li>Industry jobs<span>Career paths</span></li></ul> 497 <noscript><p>State links lead to this shared topic overview. Enable JavaScript to select a state and personalize your path. Provider listings are opening state by state.</p></noscript> 498 </div> 499 </div> 500 </div> 501</section> 502<section class="pro" id="pros" aria-labelledby="proh"> 503 <div class="grid-bg" aria-hidden="true"></div> 504 <div class="wrap inner"> 505 <div> 506 <h2 id="proh">Teach, run a range or hire?</h2> 507 <p>Listings are planned state by state. Tell us about your business and get notified when your state opens.</p> 508 <button class="btn" id="listBtn">Register interest</button> 509 </div> 510 <ul> 511 <li><b>Instructors</b><span>Connect with students searching in your area when listings open.</span></li> 512 <li><b>Ranges and training companies</b><span>Show your courses, schedule and facilities when available.</span></li> 513 <li><b>Security companies and employers</b><span>Reach candidates looking for roles in the industry.</span></li> 514 </ul> 515 </div> 516</section> 517</main> 518<footer> 519 <div class="wrap"> 520 <div class="row"> 521 <a class="logo" href="#top" aria-label="Weapons.com home"><img src="/weapons-brand-logo.png" width="1410" height="400" alt="Weapons.com â National directory: training, permits, careers" loading="lazy"></a> 522 <nav aria-label="Footer"><a href="#directory">Directory</a><a href="#states">States</a><a href="#pros">List your business</a><a href="/privacy">Privacy</a></nav> 523 <nav class="social-links" aria-label="Social media"> 524 <a href="https://x.com/weapons_com" target="_blank" rel="noopener noreferrer" aria-label="Weapons.com on X (@weapons_com), opens in a new tab" title="X: @weapons_com"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.639 7.584H.473l8.6-9.835L0 1.154h7.594l5.243 6.932 6.064-6.933ZM17.61 20.644h2.039L6.486 3.24H4.298L17.61 20.644Z"/></svg></a> 525 <a href="https://www.instagram.com/weapons_com/" target="_blank" rel="noopener noreferrer" aria-label="Weapons.com on Instagram (@weapons_com), opens in a new tab" title="Instagram: @weapons_com"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="1" fill="currentColor" stroke="none"/></svg></a> 526 </nav> 527 </div> 528 <p>Weapons.com provides educational information and a directory of third-party providers. Laws vary by state and change often; confirm requirements with your state agency before you apply, buy or carry.</p> 529 </div> 530</footer> 531<div class="toast" id="toast" role="status"></div> 532
533<script src="/analytics.js"></script>
vendor: 1 bytes, line 533
533
534<script> 535/* ---------- Data ---------- */ 536// Read the initial HTML before enhancing it, so crawler and interactive content stay in sync. 537const TILES=[...document.querySelectorAll("#tilemap .st")].map(t=>[ 538 t.textContent.trim(),Number(t.dataset.col),Number(t.dataset.row),t.dataset.name 539]); 540const STATES=TILES.map(t=>t[3]).sort(); 541 542const CATEGORIES=[...document.querySelectorAll("#tiles .directory-category")].map(c=>({ 543 id:c.dataset.cat,icon:c.dataset.icon,title:c.querySelector("h3").textContent, 544 featured:c.dataset.featured==="true",dark:c.dataset.dark==="true", 545 desc:c.querySelector("summary p").textContent, 546 items:[...c.querySelectorAll("li")].map(li=>li.textContent) 547})); 548 549const INTENTS=[ 550 {id:"permit",icon:"i-card",label:"Get my carry permit",hint:"First permit, renewal or non-resident", 551 q:"Where are you in the process?",chips:["Haven't started","Need the class","Applied, waiting","Renewing","Non-resident permit"],cats:["licenses","programs","providers"]}, 552 {id:"training",icon:"i-target",label:"Find a class",hint:"Instructors and skill-building", 553 q:"What level are you at?",chips:["Never fired a gun","New owner","Some experience","Advanced","Instructor-level"],cats:["programs","providers"]}, 554 {id:"cert",icon:"i-badge",label:"Get certified",hint:"Security, instructor, RSO, LE", 555 q:"Which credential?",chips:["Armed security","Firearms instructor","Range safety officer","Law enforcement","Executive protection","Not sure yet"],cats:["certs","providers","careers"]}, 556 {id:"job",icon:"i-case",label:"Find a job",hint:"Security, training, ranges, retail", 557 q:"What kind of role?",chips:["Armed security","Instructor","Range staff","Gunsmith / armorer","Retail / FFL","Protection work"],cats:["careers","certs"]}, 558 {id:"laws",icon:"i-scale",label:"Check the laws",hint:"My state or travel", 559 q:"What do you need to know?",chips:["Carry rules","Traveling with a firearm","Buying a firearm","Storage","Reciprocity"],cats:["laws","licenses"]}, 560 {id:"gear",icon:"i-cross",label:"Compare prices",hint:"Ammo, optics, gear", 561 q:"What are you shopping for?",chips:["Ammo","Handgun","Rifle","Optics","Holsters","Safes"],cats:["gear"]}, 562 {id:"business",icon:"i-store",label:"List my business",hint:"Instructors, ranges, employers", 563 q:"What do you run?",chips:["Independent instructor","Training company","Range","Security company","Retailer / FFL"],cats:["providers","careers"]}, 564 {id:"explore",icon:"i-compass",label:"Just looking",hint:"History, collecting, curiosity", 565 q:"What interests you most?",chips:["Military history","Collecting","Knives and edged weapons","Archery","Martial arts weapons"],cats:["laws"]} 566]; 567 568/* ---------- Recording ---------- */ 569let session; 570try{session=sessionStorage.getItem("wc_session")}catch(e){} 571if(!session){session=Math.random().toString(36).slice(2,10)+Date.now().toString(36);try{sessionStorage.setItem("wc_session",session)}catch(e){}} 572function recordEvent(type,data){ 573 const names={intent:"goal_selected",detail:"path_generated",dir_state:"state_selected",result_link:"result_topic_clicked",category_open:"category_opened",topic_click:"topic_clicked",list_click:"business_interest_started"}; 574 if(names[type])window.trackSiteEvent(names[type],type==="dir_state"?{state:data.state,source:data.topic}:data); 575 const body=JSON.stringify({type,session,referrer:document.referrer||null,...data}); 576 try{ 577 fetch("/api/events",{method:"POST",headers:{"Content-Type":"application/json"},body,keepalive:true}).catch(()=>{}); 578 }catch(e){} 579} 580async function saveSignup(payload){ 581 const dimensions={intent:payload.intent,detail:payload.detail,state:payload.state}; 582 const trackSignup=(name,extra={})=>
582{if(!payload.website)window.trackSiteEvent(name,{...dimensions,...extra})}; 583 trackSignup("notification_signup_started"); 584 try{ 585 const r=await fetch("/api/signup",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...payload,session})}); 586 const j=await r.json().catch(()=>({})); 587 if(r.ok&&j.ok){trackSignup("notification_signup_completed");return{ok:true}} 588 trackSignup("notification_signup_failed",{reason:"server_response"}); 589 return{ok:false,error:j.error||"Your signup didn't save. Try again in a minute."}; 590 }catch(e){trackSignup("notification_signup_failed",{reason:"network"});return{ok:false,error:"Couldn't reach the server. Check your connection and try again."}} 591} 592 593/* ---------- Shared state ---------- */ 594const state={step:1,intent:null,chip:null,st:"",email:""}; 595const $=id=>document.getElementById(id); 596const box=$("intake"); 597function el(html){const t=document.createElement("template");t.innerHTML=html.trim();return t.content} 598function bar(){return `<div class="progress" aria-hidden="true">${[1,2,3].map(n=>`<i class="${n<=state.step?"on":""}"></i>`).join("")}</div>`} 599function stateOptions(sel){return `<option value="">All states</option>`+STATES.map(s=>`<option ${s===sel?"selected":""}>${s}</option>`).join("")} 600function setState(name,source){ 601 const changed=state.st!==name; 602 if(changed&&state.step===3){state.email=$("em")?.value.trim()||state.email} 603 state.st=name;$("dirstate").value=name; 604 document.querySelectorAll(".st").forEach(b=>b.setAttribute("aria-pressed",String(b.dataset.name===name))); 605 renderStateInfo();if(openId)renderTiles(); 606 const sel=$("st");if(sel)sel.value=name; 607 const next=$("next");if(next)next.disabled=!(state.chip&&state.st); 608 if(changed&&state.step===3)render(); 609 if(source)recordEvent("dir_state",{state:name,topic:source}); 610} 611 612/* ---------- Intake ---------- */ 613function render(){ 614 box.innerHTML=""; 615 if(state.step===1){ 616 box.append(el(`<div class="step">${bar()} 617 <h2 class="qhead">What brings you here?</h2> 618 <p class="qsub">Three taps to your personalized path.</p> 619 <div class="options">${INTENTS.map(i=>`<button class="opt" data-intent="${i.id}"><svg aria-hidden="true"><use href="#${i.icon}"/></svg><span><b>${i.label}</b><small>${i.hint}</small></span></button>`).join("")}</div></div>`)); 620 box.querySelectorAll("[data-intent]").forEach(b=>b.onclick=()=>{ 621 state.intent=b.dataset.intent;state.chip=null;state.step=2; 622 recordEvent("intent",{intent:state.intent});render(); 623 }); 624 }else if(state.step===2){ 625 const it=INTENTS.find(i=>i.id===state.intent); 626 box.append(el(`<div class="step">${bar()} 627 <h2 class="qhead">${it.q}</h2> 628 <p class="qsub">${it.label}</p> 629 <div class="chips" role="group" aria-label="${it.q}">${it.chips.map(c=>`<button class="chip" aria-pressed="${state.chip===c}">${c}</button>`).join("")}</div> 630 <div class="field" style="margin-top:18px"><label for="st">Which state?</label><select id="st">${stateOptions(state.st).replace("All states","Choose a state")}</select></div> 631 <div class="actions"><button class="btn" id="next" ${state.chip&&state.st?"":"disabled"}>Show my path</button><button class="btn ghost" id="back">Back</button></div></div>`)); 632 const next=box.querySelector("#next"); 633 const upd=()=>{next.disabled=!(state.chip&&state.st)}; 634 box.querySelectorAll(".chip").forEach(c=>c.onclick=()=>{ 635 state.chip=c.textContent;box.querySelectorAll(".chip").forEach(x=>x.setAttribute("aria-pressed",x===c));upd(); 636 }); 637 box.querySelector("#st").onchange=e=>{setState(e.target.value,"intake");upd()}; 638 box.querySelector("#back").onclick=()=>{state.step=1;render()}; 639 next.onclick=()=>{ 640 recordEvent("detail",{intent:state.intent,detail:state.chip,state:state.st}); 641 setState(state.st); 642 state.step=3;render(); 643 }; 644 }else{ 645 const it=INTENTS.find(i=>i.id===state.intent); 646 const cats=it.cats.map(id=>CATEGORIES.find(c=>c.id===id)); 647 box.append(el(`<div class="step result">${bar()} 648 <h2 class="qhead">Your path in ${state.st}</h2> 649 <p class="qsub">${it.label}: ${state.chip}</p> 650 ${cats.map(c=>`<h3>${c.title}</h3><ul>${c.items.slice(0,3).map(x=>`<li><a href="#directory" data-open="${c.id}">${x}</a></li>`).join("")}</ul>`).join("")} 651 <h3>Be first when ${state.st} goes live</h3> 652 <p class="note">Provider listings are opening state by state. Leave your email for updates when your state goes live. <a href="/privacy">How we handle your information</a>.</p> 653 <div class="hp" aria-hidden="true"><label>Website <input type="text" id="website" tabindex="-1" autocomplete="off"></label></div> 654 <div class="actions"><input type="email" id="em" placeholder="[email protected]" aria-label="Email address" value="${state.email}"><button class="btn" id="notify">Notify me</button><button class="btn ghost" id="restart">Start over</button></div> 655 <p class="note" id="msg"></p></div>`)); 656 box.querySelectorAll("[data-open]").forEach(a=>a.onclick=e=>{e.preventDefault();recordEvent("result_link",{intent:state.intent,category:a.dataset.open,topic:a.textContent,state:state.st});openCat(a.dataset.open)}); 657 box.querySelector("#restart").onclick=()=>{Object.assign(state,{step:1,intent:null,chip:null});render()}; 658 box.querySelector("#notify").onclick=async(ev)=>{ 659 const btn=ev.currentTarget;const v=box.querySelector("#em").value.trim();const msg=box.querySelector("#msg"); 660 if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)){msg.textContent="Enter a valid email address, like [email protected].";msg.className="note err";return} 661 state.email=v;btn.disabled=true;btn.textContent="Savingâ¦";
662 const r=await saveSignup({intent:state.intent,detail:state.chip,state:state.st,email:v,website:box.querySelector("#website").value}); 663 btn.disabled=false;btn.textContent="Notify me"; 664 if(r.ok){msg.textContent="You're on the list. We'll email you when "+state.st+" goes live.";msg.className="note ok";btn.disabled=true} 665 else{msg.textContent=r.error;msg.className="note err"} 666 }; 667 } 668} 669 670/* ---------- Directory tiles ---------- */ 671let openId=null; 672const reduced=()=>matchMedia("(prefers-reduced-motion: reduce)").matches; 673function renderTiles(){ 674 const wrap=$("tiles"); 675 wrap.innerHTML=CATEGORIES.map(c=>`<button class="tile${c.featured?" featured":""}${c.dark?" featured dark":""}" id="cat-${c.id}" data-cat="${c.id}" aria-expanded="${openId===c.id}" aria-controls="panel"> 676 ${c.featured?`<svg class="bg" aria-hidden="true"><use href="#${c.icon}"/></svg>`:""} 677 <svg class="ic" aria-hidden="true"><use href="#${c.icon}"/></svg> 678 <h3>${c.title}</h3><p>${c.desc}</p><span class="count">${c.items.length} topics</span></button>`).join(""); 679 wrap.querySelectorAll(".tile").forEach(t=>t.onclick=()=>{ 680 const id=t.dataset.cat;openId=openId===id?null:id; 681 if(openId)recordEvent("category_open",{category:id,state:$("dirstate").value}); 682 renderTiles(); 683 if(openId){ 684 $("cat-"+id).focus({preventScroll:true}); 685 $("panel").scrollIntoView({behavior:reduced()?"auto":"smooth",block:"nearest"}); 686 } 687 else $("cat-"+id).focus({preventScroll:true}); 688 }); 689 if(openId){ 690 const c=CATEGORIES.find(x=>x.id===openId); 691 const tile=$("cat-"+openId); 692 const st=$("dirstate").value; 693 const panel=el(`<div class="panel" id="panel" role="region" aria-label="${c.title} topics"><h4>${c.title}${st?" in "+st:""}</h4><div class="sub">${c.items.map(x=>`<a href="#" data-item="${x}" data-cat="${c.id}">${x}<em>${c.id==="gear"?"Coming soon":"Listings soon"}</em></a>`).join("")}</div></div>`); 694 // Insert the panel after the last tile in this row. 695 const tiles=[...wrap.querySelectorAll(".tile")];const top=tile.offsetTop; 696 let after=tile;for(const t of tiles){if(t.offsetTop===top)after=t} 697 after.after(panel); 698 wrap.querySelectorAll("[data-item]").forEach(a=>a.onclick=e=>{ 699 e.preventDefault();const s=$("dirstate").value; 700 recordEvent("topic_click",{category:a.dataset.cat,topic:a.dataset.item,state:s}); 701 toast((s?s+": ":"")+a.dataset.item+" listings are opening soon."); 702 }); 703 } 704} 705function openCat(id){ 706 if(!CATEGORIES.some(c=>c.id===id))return; 707 openId=id;recordEvent("category_open",{category:id,state:$("dirstate").value});renderTiles(); 708 $("cat-"+id).scrollIntoView({behavior:reduced()?"auto":"smooth",block:"start"}); 709 $("cat-"+id).focus({preventScroll:true}); 710} 711 712/* ---------- State tile map ---------- */ 713function renderMap(){ 714 const m=$("tilemap"); 715 m.innerHTML=TILES.map(([ab,c,r,name])=>`<button class="st" style="grid-column:${c+1};grid-row:${r+1}" data-name="${name}" aria-pressed="${state.st===name}" aria-label="${name}" title="${name}">${ab}</button>`).join(""); 716 m.querySelectorAll(".st").forEach(b=>b.onclick=()=>setState(b.dataset.name,"map")); 717}
718function renderStateInfo(){ 719 const s=state.st;const box2=$("stateinfo"); 720 if(!s){box2.innerHTML=`<h3>Pick a state</h3><p>Select any state to explore topics and set the filter across the site. Topics to explore as listings go live across all 50 states and DC:</p> 721 <ul><li>Carry and purchase permits<span>Requirements and steps</span></li><li>Training<span>Classes and instructors</span></li><li>Professional licenses<span>Security and instructor</span></li><li>Industry jobs<span>Career paths</span></li></ul>`;return} 722 box2.innerHTML=`<h3>${s}</h3><p>Topics to explore as ${s} listings go live:</p> 723 <ul><li>Carry and purchase permits<span>Requirements and steps</span></li><li>Training<span>Classes and instructors</span></li><li>Professional licenses<span>Security and instructor</span></li><li>Industry jobs<span>Career paths</span></li></ul> 724 <button class="btn" id="startState">Start my ${s} path</button>`; 725 $("startState").onclick=()=>{ 726 window.trackSiteEvent("state_path_started",{state:s,source:"map"}); 727 state.step=state.intent?2:1;render(); 728 $("intake").scrollIntoView({behavior:reduced()?"auto":"smooth",block:"start"}); 729 }; 730} 731 732/* ---------- Wiring ---------- */ 733$("dirstate").innerHTML=stateOptions(""); 734$("dirstate").disabled=false; 735$("dirstate").onchange=e=>setState(e.target.value,"filter"); 736document.querySelectorAll("[data-jump]").forEach(a=>a.onclick=e=>{e.preventDefault();openCat(a.dataset.jump)}); 737$("listBtn").onclick=()=>{ 738 recordEvent("list_click",{}); 739 Object.assign(state,{step:2,intent:"business",chip:null});render(); 740 $("intake").scrollIntoView({behavior:reduced()?"auto":"smooth",block:"start"}); 741 setTimeout(()=>$("st")?.focus({preventScroll:true}),reduced()?0:550); 742}; 743 744let tt;function toast(t){const e=$("toast");e.textContent=t;e.classList.add("show");clearTimeout(tt);tt=setTimeout(()=>e.classList.remove("show"),2600)} 745let rt;addEventListener("resize",()=>{clearTimeout(rt);rt=setTimeout(()=>{if(openId)renderTiles()},150)}); 746 747render();renderTiles();renderMap();renderStateInfo(); 748</script>
vendor: 1 bytes, line 748
748
749<script src="https://i.replit.com/script.js" async="" data-website-id="be04f150-6b9d-4b13-80f6-0fa435715126"></script>
749</body> 750</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.