The Southwest’s
Dust & Fume Authority

OSHA 1910.1000 • NFPA 660 compliant

Quick-Ship options • Full central systems in 8–10 weeks

Book Free Assessment →

0

CFM of Hazardous Air Captured Daily

0

Combustible Dust Fines for Our Clients

0

Average Emergency Response

0

NFPA 660 Ready Systems

What are realistic price ranges for dust collection systems?

Shop owners ask us this every week — here’s what shops in the Southwest are paying for turn-key systems (equipment + install) as of early 2026.

📖 Full 2026 Cost Breakdown →
💰 Financing Options →
✓ Our Pass-or-Free Guarantee →

Smaller Shops

$8,000 – $50,000

1-6 portable extractors or downdraft tables. Plug in and go — covers main stations.

Small-Medium Shops

$60,000 – $125,000

Basic central cartridge collector with ducting to 4-8 points.

Medium Shops

$125,000 – $250,000

Central baghouse or cartridge collector with ducting. Does include some explosion protection.

Large/High-Volume Shops

$300,000 – $500,000+

Large central cartridge collectors, extensive ductwork, full explosion safeguards — efficient for large/high-volume shops.

Most shops see payback in 2-4 years from lower insurance, fewer sick days, and avoiding fines.
We always give fixed quotes after the free audit — exact number for your shop.
Quick-ship I-Portable unit
Portable unit — quick and affordable for smaller shops
Legend Series baghouse
Legend Series baghouse — perfect for medium shops with wood dust
Central cartridge collector in operation
Central cartridge collector — efficient for large/high-volume shops

How do I make sure my dust collection system will pass an OSHA inspection?

That’s the question we hear most from shop owners across the Southwest — and it’s smart to think about it before an inspector shows up.

OSHA doesn’t have one single “dust collection” standard, but they enforce several that add up to a passing system. The goal is simple: keep dust levels low enough that workers aren’t breathing too much, and prevent buildup that could feed a fire or explosion.

✓ Our Pass-or-Free Compliance Guarantee →
📋 NFPA 660 Compliance Checklist →
🛡️ Explosion Protection Assessments →

Common OSHA Citations We See

  • Housekeeping — dust layers thicker than 1/32 inch over too much area
  • No explosion protection on combustible dust systems
  • Workers exposed above PELs without controls
  • No documentation of hazards or training

OSHA Inspection Prep Checklist

  • Self-audit dust layers
  • Test air quality
  • Train team on procedures
  • Document everything
  • Fix gaps early

Explosion Protection Options

  • Venting — releases pressure outside (cheapest)
  • Flameless venting — safe indoors
  • Chemical suppression — stops explosion early
  • Isolation — prevents spread through ducts

Capture Velocity Basics (Transport in Ducts)

Dust Type Recommended Velocity (FPM)
Fine/light dust 3500
Sawdust 4500
Heavy (metal turnings) 5000
Weld smoke 2500

Housekeeping Schedule Example

Area How Often Method
Floors around machines Daily Housekeeping Port
Beams/ledges Monthly Extendable wand
Ducts inside Yearly Professional clean

We’ve helped shops turn “likely citation” setups into passing ones with simple changes.

If any of this sounds like your shop, a free assessment shows exactly where you stand — no pressure, just facts.

Central baghouse system in clean shop
No dust in the air — central baghouse working hard
Portable extractor capturing welding fumes
Fumes pulled away instantly — portable unit in action
Downdraft table staying clean during work
Bench stays spotless — downdraft doing its job

What do I need to know about NFPA 660 for 2026?

Shop owners ask us this all the time — and now that NFPA 660 is enforceable as of January 1, 2026, it’s no longer a future problem. Here’s the plain-English explanation.

📋 NFPA 660 Compliance Checklist →
📖 Understanding Kst Values →
✓ Our Pass-or-Free Guarantee →

Key Changes in NFPA 660

  • Consolidated standard — one code instead of many
  • Dust Hazard Analysis (DHA) mandatory for combustible dust
  • Clear deflagration protection rules based on Kst
  • Specific housekeeping schedules
  • Better isolation guidance

Common Dust Kst Values

Dust Type Kst Range Severity
Wood 100-200 Weak
Aluminum 400+ Very Strong
Flour 200-300 Strong

Protection Options

  • Venting
  • Flameless venting
  • Chemical suppression
  • Isolation

The good news: if your current system was designed to the old standards, you’re probably close. But many shops we visit still have gaps — and if your DHA turns up problems, we’ve got a step-by-step plan.

🔧 Failed DHA? Here’s the Fix-It Roadmap →
🔍 What We Found Mystery Shopping Other Companies →

We help shops get ready by doing the DHA, updating protection if needed, and providing all the paperwork inspectors want to see. No guesswork.

Want to know where your shop stands? The free assessment includes a full NFPA 660 review — we’ll tell you exactly what you need (or don’t need).

How does the whole process work from assessment to installed system?

We get asked this every week — here’s exactly how it goes, step by step.

Step 1: Free On-Site Assessment

We come to your shop (no charge) with calibrated meters and take air samples. We measure capture velocities at each station, check dust accumulation, test for combustible dust if needed, and look at your layout. Takes 1-2 hours.

Step 2: Fixed-Price Proposal

Within 5 business days you get a complete proposal with drawings, equipment specs, installation timeline, and exact cost. No surprises.

Step 3: Design Approval & Permitting

We handle all permit drawings and fire marshal coordination. You just review and sign.

Step 4: Fabrication & Delivery

Equipment built to your specs. Quick-ship portables arrive in a week. Central systems ready in 8-10 weeks.

Step 5: Installation

Our crews handle rigging, structural supports, ductwork fabrication, and startup. Minimal downtime — we work weekends if needed.

Step 6: Commissioning & Testing

We test differential pressures, verify explosion venting, and run independent lab air samples. You get the passing report in hand.

Step 7: Training & Maintenance

We train your team on daily checks and pulse cleaning. Optional maintenance plans keep everything running strong.

The whole process is designed to fit around your production schedule. Most shops are fully operational during install.

Have questions about any step? That’s what the free assessment is for — we walk through it together.

5 Signs Your Dust Collector is Failing (Before OSHA Shows Up)

Your dust collector is dying. Here’s how to know before it’s a big problem:

1. Visible dust settling on equipment

Means filters are loaded and airflow’s dropping — dust isn’t getting captured like it should.

2. Team complaining more about the air

Headaches, coughing, stuffy shop — exposure is creeping up, and it can turn into a retention problem fast.

3. Pressure gauge reading higher than normal

Filters clogged, system working overtime — motor wears out quicker, efficiency tanks.

4. Dust building up inside the ductwork

Big fire or explosion risk — combustible dust loves hiding there.

5. System running all day but the shop still feels dusty

Often means it’s undersized for your current production or filters are shot.

We’ve seen shops ignore these until an inspection hits with a big fine. Catch ’em early, fix or upgrade, and you avoid the headache.

If any of these sound familiar, a quick check can tell you a lot.

What’s one you’ve noticed in your shop? Happy to help think through it.

Want to Dig Deeper?

Real answers about dust collection — costs, compliance, and what other companies won’t tell you.

2026 Cost Guide →
Kst Values Explained →
Mystery Shop Results →
Failed DHA Roadmap →

Browse All Guides in the Learning Center →


Welding and Metal Fabrication Dust Collection
Welding / Metal Fabrication


Wood Manufacturing Dust Collection
Wood Manufacturing


CNC Machining Dust and Mist Collection
Machining / CNC


Food and Supplement Manufacturing Air Filtration
Food & Supplement


Aerospace Composite Dust Collection
Aerospace Manufacturing

Have questions about your shop’s dust collection?

Free site assessment — zero obligation — we’ll give you straight answers and exact numbers.

Schedule Now →

(function() { 'use strict'; // ─── Short alias ───────────────────────────────────────── var h = React.createElement; var useState = React.useState; var useEffect = React.useEffect; // ─── Brand colors ──────────────────────────────────────── var C = { navy:"#1B2A4A", steel:"#2D4A6F", accent:"#E8913A", light:"#F4F6F9", white:"#FFFFFF", text:"#2C3E50", muted:"#7A8B9A", border:"#D1D9E0", success:"#2E8B57", warning:"#D4A017", cardBg:"#FAFBFC" }; // ─── Data ──────────────────────────────────────────────── var DUST_TYPES = [ { id:"wood", label:"Wood Dust", combustible:true, cfmPerPoint:1700, velocity:4500, icon:"🪵" }, { id:"metal_grinding", label:"Metal Grinding / Deburring", combustible:true, cfmPerPoint:1000, velocity:4500, icon:"⚙️" }, { id:"welding", label:"Welding Fume", combustible:false, cfmPerPoint:625, velocity:2500, icon:"🔥" }, { id:"silica", label:"Silica / Concrete", combustible:false, cfmPerPoint:900, velocity:4500, icon:"🧱" }, { id:"food", label:"Food / Supplement Powder", combustible:true, cfmPerPoint:700, velocity:3500, icon:"🌾" }, { id:"pharma", label:"Pharmaceutical Powder", combustible:true, cfmPerPoint:600, velocity:3500, icon:"💊" }, { id:"plastic", label:"Plastic Dust / Fines", combustible:true, cfmPerPoint:750, velocity:3500, icon:"♻️" }, { id:"battery", label:"Battery / Lithium", combustible:true, cfmPerPoint:500, velocity:4000, icon:"🔋" }, { id:"other", label:"Other / Not Sure", combustible:null, cfmPerPoint:800, velocity:3500, icon:"❓" } ]; var INDUSTRIES = [ "Woodworking / Cabinetry","Welding / Metal Fabrication","Aerospace Manufacturing", "Automotive / Fleet Service","Food / Supplement Processing","Pharmaceutical Manufacturing", "Plastics Manufacturing","Concrete / Silica Processing","CNC / Machining", "Battery Manufacturing","Mining / Minerals","Vocational / Trade School","Other" ]; var STATES = ["Arizona","California","Nevada","New Mexico","Utah"]; var STATE_ABBR = {"Arizona":"AZ","California":"CA","Nevada":"NV","New Mexico":"NM","Utah":"UT"}; var DUCT_RANGES = [ { id:"short", label:"Under 25 ft", factor:1.00 }, { id:"medium", label:"25–50 ft", factor:1.15 }, { id:"long", label:"50–100 ft", factor:1.35 }, { id:"vlong", label:"100+ ft", factor:1.55 } ]; var COLLECTION_POINTS = [ { id:"1-3", label:"1–3 points", avg:2, tier:"portable" }, { id:"4-6", label:"4–6 points", avg:5, tier:"small_central" }, { id:"7-12", label:"7–12 points", avg:9, tier:"medium_central" }, { id:"13-15", label:"13–15 points", avg:14, tier:"large_central" }, { id:"15-20", label:"15–20 points", avg:17, tier:"xl_central" }, { id:"20-25", label:"20–25 points", avg:22, tier:"xl_central" }, { id:"25-30", label:"25–30 points", avg:28, tier:"mega_central" }, { id:"50-75", label:"50–75 points", avg:62, tier:"mega_central" }, { id:"75-100", label:"75–100 points", avg:85, tier:"mega_central" }, { id:"100-125", label:"100–125 points", avg:112, tier:"mega_central" }, { id:"150+", label:"150+ points", avg:175, tier:"mega_central" } ]; // ─── Estimate engine ───────────────────────────────────── function calcEstimate(d) { var dust = DUST_TYPES.filter(function(x){ return x.id === d.dustType; })[0] || DUST_TYPES[8]; var pts = COLLECTION_POINTS.filter(function(x){ return x.id === d.collectionPoints; })[0] || COLLECTION_POINTS[0]; var duct = DUCT_RANGES.filter(function(x){ return x.id === d.ductRun; })[0] || DUCT_RANGES[0]; var cfm = dust.cfmPerPoint * pts.avg; var isComb = dust.combustible === true || d.combustible === "yes"; var isWet = d.dustType === "metal_grinding"; var isBag = d.dustType === "wood" || d.dustType === "battery"; var cLabel = isWet ? "Wet Collection System" : isBag ? "Central Baghouse System" : "Central Cartridge Collector"; var cDetail = isWet ? "wet collector with continuous wash-down, sludge management, and corrosion-resistant construction" : isBag ? "baghouse dust collector with pulse-jet cleaning, automated dust discharge, and bag/cage filtration" : "cartridge dust collector with pulse-jet cleaning, automated dust discharge, and high-efficiency cartridge filtration"; var systemType, systemDesc; if (pts.tier === "portable") { systemType = "Portable / Source Capture"; systemDesc = pts.avg + " portable extractors or source capture arms. Plug-and-play — no ductwork required for most setups."; } else { var prefix = pts.tier === "xl_central" ? "XL " : pts.tier === "mega_central" ? "Multi-Module " : pts.tier === "large_central" ? "Large " : ""; systemType = prefix + cLabel; systemDesc = prefix + cLabel + " handling " + cfm.toLocaleString() + " CFM across " + pts.avg + " collection points. Includes " + cDetail + "."; } var eqLow = cfm * 6.00, eqHigh = cfm * 8.25; var dwLow = pts.tier === "portable" ? 0 : cfm * 3.25 * duct.factor; var dwHigh = pts.tier === "portable" ? 0 : cfm * 6.50 * duct.factor; var exLow=0, exHigh=0; if (isComb && pts.tier !== "portable") { if (pts.tier==="small_central") { exLow=25000; exHigh=35000; } else if (pts.tier==="medium_central") { exLow=35000; exHigh=55000; } else if (pts.tier==="large_central") { exLow=55000; exHigh=85000; } else { exLow=85000; exHigh=175000; } } var caFactor = d.state === "California" ? 1.25 : 1.0; var inLow = pts.tier==="portable" ? 500*pts.avg : cfm*6.25*caFactor; var inHigh = pts.tier==="portable" ? 1500*pts.avg : cfm*10.75*caFactor; var wksLow = cfm<=30000 ? Math.ceil(cfm/12500) : Math.ceil(cfm/7500); var wksHigh = cfm<=30000 ? Math.ceil(cfm/6250) : Math.ceil(cfm/5000); var ctLow=0, ctHigh=0; if (pts.tier==="small_central") { ctLow=5000; ctHigh=15000; } else if (pts.tier==="medium_central") { ctLow=15000; ctHigh=35000; } else if (pts.tier==="large_central") { ctLow=30000; ctHigh=55000; } else if (pts.tier==="xl_central") { ctLow=35000; ctHigh=75000; } else if (pts.tier==="mega_central") { ctLow=75000; ctHigh=125000; } var totLow = Math.round((eqLow+dwLow+exLow+inLow+ctLow)/1000)*1000; var totHigh = Math.round((eqHigh+dwHigh+exHigh+inHigh+ctHigh)/1000)*1000; var fanHP = Math.ceil(cfm/(isBag ? 250 : 306)); var r=0.08/12, rh=0.10/12, m=60; var moLow = Math.round(totLow*(r*Math.pow(1+r,m))/(Math.pow(1+r,m)-1)); var moHigh = Math.round(totHigh*(rh*Math.pow(1+rh,m))/(Math.pow(1+rh,m)-1)); return { cfm:cfm, velocity:dust.velocity, isComb:isComb, systemType:systemType, systemDesc:systemDesc, fanHP:fanHP, wksLow:wksLow, wksHigh:wksHigh, bd:{ eq:{l:Math.round(eqLow),h:Math.round(eqHigh)}, dw:{l:Math.round(dwLow),h:Math.round(dwHigh)}, ex:{l:exLow,h:exHigh}, ins:{l:Math.round(inLow),h:Math.round(inHigh)}, ct:{l:ctLow,h:ctHigh} }, totLow:totLow, totHigh:totHigh, moLow:moLow, moHigh:moHigh }; } // ─── Helpers ───────────────────────────────────────────── var fmt = function(n) { return "$" + n.toLocaleString(); }; function useWidth() { var s = useState(typeof window !== "undefined" ? window.innerWidth : 768); var w = s[0], setW = s[1]; useEffect(function() { var handler = function() { setW(window.innerWidth); }; window.addEventListener("resize", handler); return function() { window.removeEventListener("resize", handler); }; }, []); return w; } // ─── Sub-components ────────────────────────────────────── function ProgressDots(props) { var step = props.step, total = props.total; return h('div', {style:{display:"flex",gap:6,marginBottom:28}}, Array.from({length:total}).map(function(_,i) { return h('div', { key:i, style:{flex:1,height:4,borderRadius:2, background: i<=step ? C.accent : C.border, transition:"background 0.3s"} }); }) ); } function StepHead(props) { return h('div', {style:{marginBottom:24}}, h('div', {style:{display:"flex",alignItems:"center",gap:10,marginBottom:4}}, h('span', {style:{background:C.accent,color:"#fff",width:26,height:26,borderRadius:"50%", display:"flex",alignItems:"center",justifyContent:"center", fontSize:12,fontWeight:700,flexShrink:0}}, props.n), h('h2', {style:{margin:0,fontSize:18,fontWeight:700,color:C.navy}}, props.title) ), props.sub ? h('p', {style:{margin:0,paddingLeft:36,fontSize:13,color:C.muted}}, props.sub) : null ); } function Card(props) { return h('button', { onClick: props.onClick, style: Object.assign({ display:"flex",alignItems:"center",gap:10,padding:"12px 14px",borderRadius:10, border:"2px solid " + (props.selected ? C.accent : C.border), background: props.selected ? "#FEF5EB" : C.white, cursor:"pointer",textAlign:"left",width:"100%", transition:"all 0.15s",boxSizing:"border-box" }, props.style || {}) }, props.icon ? h('span', {style:{fontSize:20,flexShrink:0}}, props.icon) : null, h('div', {style:{minWidth:0}}, h('div', {style:{fontSize:13,fontWeight:600,color:C.navy,wordBreak:"break-word"}}, props.label), props.sub ? h('div', {style:{fontSize:11,color:C.muted,marginTop:1}}, props.sub) : null ) ); } function NavBtns(props) { return h('div', {style:{display:"flex",justifyContent:"space-between",marginTop:28}}, props.onBack ? h('button', { onClick: props.onBack, style:{padding:"10px 20px",borderRadius:8,border:"1px solid "+C.border, background:C.white,color:C.text,fontSize:13,fontWeight:600,cursor:"pointer"} }, "← Back") : h('div', null), h('button', { onClick: props.onNext, disabled: !!props.disabled, style:{padding:"10px 28px",borderRadius:8,border:"none", background: props.disabled ? C.border : C.accent, color:"#fff",fontSize:13,fontWeight:700, cursor: props.disabled ? "default" : "pointer"} }, props.nextLabel || "Next →") ); } function Bar(props) { var pct = props.maxH > 0 ? (props.high / props.maxH) * 100 : 0; return h('div', {style:{marginBottom:12}}, h('div', {style:{display:"flex",justifyContent:"space-between",fontSize:12,marginBottom:3}}, h('span', {style:{color:C.text,fontWeight:600}}, props.label), h('span', {style:{color:C.muted}}, fmt(props.low) + " – " + fmt(props.high)) ), h('div', {style:{height:7,background:C.light,borderRadius:4}}, h('div', {style:{height:7,borderRadius:4,background: props.color || C.steel, width: Math.max(pct,5) + "%",transition:"width 0.5s"}}) ) ); } // ─── Link button helper ───────────────────────────────── function LinkBtn(props) { return h('a', { href: props.href, target: props.external ? "_blank" : undefined, style:{display:"inline-block",background:"rgba(0,255,136,0.15)",color:"#00ff88", fontWeight:"bold",fontSize:"1rem",padding:"8px 18px",borderRadius:8, border:"2px solid rgba(0,255,136,0.4)",textDecoration:"none"} }, props.label); } // ─── Main Estimator ────────────────────────────────────── function Estimator() { var wResult = useWidth(); var mobile = wResult < 580; var xs = wResult < 380; var blank = {industry:"",dustType:"",combustible:"",collectionPoints:"", ductRun:"",collectorLocation:"",state:"",timeline:"", name:"",email:"",phone:"",company:""}; var stepState = useState(0); var step = stepState[0]; var setStep = stepState[1]; var dataState = useState(blank); var data = dataState[0]; var setData = dataState[1]; var resultState = useState(null); var result = resultState[0]; var setResult = resultState[1]; var contactState = useState(false); var showContact = contactState[0]; var setShowContact = contactState[1]; function up(field, val) { setData(function(d) { return Object.assign({}, d, {[field]: val}); }); } function next() { if (step < 3) { setStep(function(s){ return s+1; }); } else { setResult(calcEstimate(data)); setStep(4); } } function ok() { if (step===0) return data.industry && data.dustType; if (step===1) return !!data.collectionPoints; if (step===2) return data.ductRun && data.state; return true; } // ── Grid styles ──────────────────────────────────────── var g2 = {display:"grid",gridTemplateColumns:"1fr 1fr",gap:8}; var gState = {display:"grid",gridTemplateColumns: xs?"1fr 1fr":mobile?"repeat(3,1fr)":"repeat(5,1fr)",gap:8}; var gSpecs = {display:"grid",gridTemplateColumns: mobile?"1fr 1fr":"repeat(4,1fr)",gap:10,marginBottom:20}; var gCTA = {display:"grid",gridTemplateColumns: xs?"1fr":"1fr 1fr",gap:10}; var gLoc = {display:"grid",gridTemplateColumns: mobile?"1fr 1fr":"repeat(3,1fr)",gap:8,marginBottom:18}; // ── Current dust info for live CFM display ────────────── var currentDust = DUST_TYPES.filter(function(x){ return x.id===data.dustType; })[0] || DUST_TYPES[8]; var currentPts = data.collectionPoints ? COLLECTION_POINTS.filter(function(x){ return x.id===data.collectionPoints; })[0] : null; // ── Render ────────────────────────────────────────────── return h('div', {style:{maxWidth:680,margin:"0 auto",fontFamily:"'DM Sans',sans-serif"}}, // Progress dots (steps 0-3) step < 4 ? h(ProgressDots, {step:step, total:4}) : null, h('div', {style:{background:C.white,borderRadius:14, padding: mobile ? "18px 14px" : "26px 26px 22px", boxShadow:"0 2px 12px rgba(27,42,74,0.07)"}}, // ══ STEP 0 ═══════════════════════════════════════ step === 0 ? h('div', null, h(StepHead, {n:1, title:"Your Operation", sub:"Tell us what you make and what kind of dust you're dealing with."}), // Industry dropdown h('div', {style:{marginBottom:18}}, h('label', {style:{display:"block",fontSize:12,fontWeight:600, color:C.text,marginBottom:6}}, "What industry are you in?"), h('select', { value: data.industry, onChange: function(e){ up("industry", e.target.value); }, style:{width:"100%",padding:"11px 12px",borderRadius:8, border:"1px solid "+C.border,fontSize:13, color: data.industry ? C.text : C.muted, background:C.white,boxSizing:"border-box"} }, h('option', {value:""}, "Select your industry..."), INDUSTRIES.map(function(ind) { return h('option', {key:ind, value:ind}, ind); }) ) ), // Dust type grid h('label', {style:{display:"block",fontSize:12,fontWeight:600, color:C.text,marginBottom:6}}, "What's your primary dust or fume type?"), h('div', {style:g2}, DUST_TYPES.map(function(dt) { return h(Card, { key: dt.id, selected: data.dustType === dt.id, onClick: function() { up("dustType", dt.id); if (dt.combustible !== null) up("combustible", dt.combustible ? "yes" : "no"); }, icon: dt.icon, label: dt.label, sub: dt.combustible === true ? "Combustible" : dt.combustible === false ? "Non-combustible" : null }); }) ), // Combustible follow-up for "other" data.dustType === "other" ? h('div', {style:{marginTop:14}}, h('label', {style:{display:"block",fontSize:12,fontWeight:600, color:C.text,marginBottom:6}}, "Is your dust combustible?"), h('div', {style:{display:"flex",gap:8,flexWrap:"wrap"}}, ["yes","no","not_sure"].map(function(v) { return h(Card, { key:v, selected: data.combustible === v, onClick: function(){ up("combustible", v); }, label: v==="yes" ? "Yes" : v==="no" ? "No" : "Not Sure", style:{flex:"1 1 70px"} }); }) ) ) : null, h(NavBtns, {onNext:next, disabled:!ok()}) ) : null, // ══ STEP 1 ═══════════════════════════════════════ step === 1 ? h('div', null, h(StepHead, {n:2, title:"Your Collection Points", sub:"How many machines, hoods, or stations need dust pickup?"}), h('div', {style:{display:"grid",gap:7}}, COLLECTION_POINTS.map(function(cp) { return h(Card, { key: cp.id, selected: data.collectionPoints === cp.id, onClick: function(){ up("collectionPoints", cp.id); }, label: cp.label, sub: cp.tier==="portable" ? "Portable or small central system" : cp.tier==="small_central" ? "Small central collector with ductwork" : cp.tier==="medium_central" ? "Medium central system" : "Large central system with full ductwork network" }); }) ), // Live CFM display data.collectionPoints && currentPts ? h('div', { style:{marginTop:16,padding:14,background:"#F0F7FF", borderRadius:10,border:"1px solid #C8DEF5"} }, h('div', {style:{fontSize:12,color:C.steel,fontWeight:600,marginBottom:3}}, "Estimated Total CFM"), h('div', {style:{fontSize:26,fontWeight:800,color:C.navy}}, (currentDust.cfmPerPoint * currentPts.avg).toLocaleString(), h('span', {style:{fontSize:13,fontWeight:600,color:C.muted,marginLeft:4}}, "CFM") ), h('div', {style:{fontSize:11,color:C.muted,marginTop:3}}, "~" + currentDust.cfmPerPoint + " CFM per " + currentDust.label.toLowerCase() + " collection point") ) : null, h(NavBtns, {onBack:function(){setStep(0);}, onNext:next, disabled:!ok()}) ) : null, // ══ STEP 2 ═══════════════════════════════════════ step === 2 ? h('div', null, h(StepHead, {n:3, title:"Your Facility", sub:"A few details about your space to estimate ductwork and installation."}), h('label', {style:{display:"block",fontSize:12,fontWeight:600, color:C.text,marginBottom:6}}, "Average ductwork run from machines to collector"), h('div', {style:Object.assign({}, g2, {marginBottom:18})}, DUCT_RANGES.map(function(dr) { return h(Card, { key:dr.id, selected:data.ductRun===dr.id, onClick:function(){ up("ductRun", dr.id); }, label:dr.label }); }) ), h('label', {style:{display:"block",fontSize:12,fontWeight:600, color:C.text,marginBottom:6}}, "Where will the collector sit?"), h('div', {style:gLoc}, ["Inside","Outside","Rooftop"].map(function(loc) { return h(Card, { key:loc, selected:data.collectorLocation===loc, onClick:function(){ up("collectorLocation", loc); }, label:loc }); }) ), h('label', {style:{display:"block",fontSize:12,fontWeight:600, color:C.text,marginBottom:6}}, "What state is your facility in?"), h('div', {style:gState}, STATES.map(function(st) { return h(Card, { key:st, selected:data.state===st, onClick:function(){ up("state", st); }, label: STATE_ABBR[st] || st, sub: st }); }) ), h(NavBtns, {onBack:function(){setStep(1);}, onNext:next, disabled:!ok()}) ) : null, // ══ STEP 3 ═══════════════════════════════════════ step === 3 ? h('div', null, h(StepHead, {n:4, title:"Your Timeline", sub:"When do you need this operational?"}), h('div', {style:{display:"grid",gap:8,marginBottom:18}}, [ {id:"asap", label:"ASAP — We have a compliance deadline or citation", icon:"🚨"}, {id:"3-6mo", label:"3–6 months — Planning ahead", icon:"📅"}, {id:"6-12mo", label:"6–12 months — Budget cycle / new build", icon:"🏗️"}, {id:"research", label:"Just researching — No rush", icon:"🔍"} ].map(function(t) { return h(Card, { key:t.id, selected:data.timeline===t.id, onClick:function(){ up("timeline", t.id); }, icon:t.icon, label:t.label }); }) ), h(NavBtns, { onBack:function(){setStep(2);}, onNext:next, nextLabel:"See My Estimate →" }) ) : null, // ══ STEP 4 — RESULTS ═════════════════════════════ step === 4 && result ? h('div', null, // Header h('div', {style:{textAlign:"center",marginBottom:20}}, h('div', {style:{fontSize:10,fontWeight:700,textTransform:"uppercase", letterSpacing:2,color:C.success,marginBottom:5}}, "Your Estimate Is Ready"), h('h2', {style:{margin:0,fontSize: mobile?18:22,fontWeight:800,color:C.navy}}, result.systemType), h('p', {style:{margin:"5px auto 0",fontSize:12,color:C.muted,maxWidth:480}}, result.systemDesc) ), // Price hero h('div', {style:{background:"linear-gradient(135deg,"+C.navy+" 0%,"+C.steel+" 100%)", borderRadius:12,padding: mobile?"18px 14px":"22px 26px", textAlign:"center",marginBottom:20}}, h('div', {style:{fontSize:11,color:"rgba(255,255,255,0.6)",fontWeight:600, textTransform:"uppercase",letterSpacing:1}}, "Estimated Total Investment"), h('div', {style:{fontSize: mobile?26:34,fontWeight:800,color:"#fff",margin:"6px 0"}}, fmt(result.totLow) + " – " + fmt(result.totHigh)), h('div', {style:{fontSize:12,color:"rgba(255,255,255,0.7)"}}, "Turn-key: equipment + ductwork + installation + controls"), result.moLow > 0 ? h('div', {style:{marginTop:10,padding:"7px 14px", background:"rgba(232,145,58,0.2)", borderRadius:8,display:"inline-block"}}, h('span', {style:{color:C.accent,fontWeight:700,fontSize:13}}, "~" + fmt(result.moLow) + " – " + fmt(result.moHigh) + "/mo"), h('span', {style:{color:"rgba(255,255,255,0.6)",fontSize:11,marginLeft:6}}, "60-month financing at 8–10% APR") ) : null ), // Specs strip h('div', {style:gSpecs}, [ {label:"Total CFM", value: result.cfm.toLocaleString(), unit:"CFM"}, {label:"Transport Velocity", value: result.velocity.toLocaleString(), unit:"FPM"}, {label:"Est. Fan Size", value: result.fanHP, unit:"HP"}, {label:"Est. Install Time", value: result.wksLow===result.wksHigh ? result.wksLow : result.wksLow+"–"+result.wksHigh, unit:"wks"} ].map(function(s) { return h('div', {key:s.label, style:{background:C.cardBg,borderRadius:10,padding:"12px 10px", textAlign:"center",border:"1px solid "+C.border}}, h('div', {style:{fontSize:10,color:C.muted,fontWeight:600, textTransform:"uppercase",letterSpacing:0.5}}, s.label), h('div', {style:{fontSize: mobile?16:20,fontWeight:800,color:C.navy,marginTop:2}}, s.value, h('span', {style:{fontSize:10,fontWeight:600,color:C.muted}}, " " + s.unit) ) ); }) ), // Cost breakdown h('div', {style:{marginBottom:20}}, h('h3', {style:{fontSize:14,fontWeight:700,color:C.navy,marginBottom:12}}, "Cost Breakdown"), h(Bar, {label:"Equipment", low:result.bd.eq.l, high:result.bd.eq.h, maxH:result.totHigh, color:C.steel}), result.bd.dw.h > 0 ? h(Bar, {label:"Ductwork", low:result.bd.dw.l, high:result.bd.dw.h, maxH:result.totHigh, color:"#5B8DB8"}) : null, result.bd.ex.h > 0 ? h(Bar, {label:"Safety Equipment", low:result.bd.ex.l, high:result.bd.ex.h, maxH:result.totHigh, color:C.warning}) : null, h(Bar, {label:"Installation", low:result.bd.ins.l, high:result.bd.ins.h, maxH:result.totHigh, color:C.accent}), result.bd.ct.h > 0 ? h(Bar, {label:"Controls & Electrical", low:result.bd.ct.l, high:result.bd.ct.h, maxH:result.totHigh, color:"#7A8B9A"}) : null ), // Combustible dust warning result.isComb ? h('div', { style:{background:"#FFF8E7",border:"1px solid #F0D68C",borderRadius:10, padding:14,marginBottom:14,display:"flex",gap:10,alignItems:"flex-start"} }, h('span', {style:{fontSize:18}}, "⚠️"), h('div', null, h('div', {style:{fontSize:12,fontWeight:700,color:C.text}}, "Combustible Dust — Safety Equipment Required"), h('div', {style:{fontSize:11,color:C.muted,marginTop:2}}, "NFPA 660 (effective January 2026) requires a dust hazard analysis (DHA) and engineered safety equipment for your dust type. This estimate includes explosion venting, suppression, isolation valves, abort gates, and spark detection based on your system size.") ) ) : null, // Pass-or-free guarantee h('div', { style:{background:"#F0FFF4",border:"1px solid #A8E6C3",borderRadius:10, padding:14,marginBottom:20,display:"flex",gap:10,alignItems:"flex-start"} }, h('span', {style:{fontSize:18}}, "✅"), h('div', null, h('div', {style:{fontSize:12,fontWeight:700,color:C.text}}, "Your system passes inspection — or we fix it at no charge"), h('div', {style:{fontSize:11,color:C.muted,marginTop:2}}, "That's the pass-or-free guarantee. Every system we install must meet OSHA and NFPA 660 requirements. If it doesn't pass, we make it right.") ) ), // Disclaimer h('div', {style:{fontSize:10,color:C.muted,textAlign:"center", marginBottom:18,lineHeight:1.5}}, "Ballpark estimate based on typical Southwest installations. Your exact quote depends on site conditions, equipment selection, and engineering requirements. We always provide a fixed quote after a free on-site assessment — no surprises."), // CTAs !showContact ? h('div', {style:{display:"grid",gap:10}}, h('button', { onClick: function(){ setShowContact(true); }, style:{padding:"14px 22px",borderRadius:10,border:"none", background:C.accent,color:"#fff",fontSize:14, fontWeight:700,cursor:"pointer",width:"100%"} }, "Email This Estimate to Me →"), h('div', {style:gCTA}, h('button', { onClick: function(){ window.location.href = "https://industrialcleanairproducts.com/book-online/"; }, style:{padding:"12px 16px",borderRadius:10,border:"2px solid "+C.navy, background:C.white,color:C.navy,fontSize:12,fontWeight:700,cursor:"pointer"} }, "Book Free Assessment"), h('button', { onClick: function(){ setStep(0); setResult(null); setShowContact(false); setData(blank); }, style:{padding:"12px 16px",borderRadius:10,border:"2px solid "+C.border, background:C.white,color:C.text,fontSize:12,fontWeight:700,cursor:"pointer"} }, "Start Over") ) ) : h('div', { style:{background:C.cardBg,borderRadius:12,padding:18,border:"1px solid "+C.border} }, h('h3', {style:{margin:"0 0 4px",fontSize:14,fontWeight:700,color:C.navy}}, "Get your estimate + a personalized follow-up"), h('p', {style:{margin:"0 0 14px",fontSize:11,color:C.muted}}, "We'll email your estimate along with equipment recommendations for your setup. No spam — just the numbers."), ["name","email","phone","company"].map(function(f) { return h('input', { key: f, type: f==="email" ? "email" : f==="phone" ? "tel" : "text", placeholder: f==="name" ? "Your Name" : f==="email" ? "Email Address" : f==="phone" ? "Phone (optional)" : "Company Name", value: data[f], onChange: function(e){ up(f, e.target.value); }, style:{width:"100%",padding:"11px 12px",borderRadius:8, border:"1px solid "+C.border,fontSize:13,marginBottom:8, boxSizing:"border-box"} }); }), h('button', { disabled: !data.name || !data.email, style:{width:"100%",padding:"13px 22px",borderRadius:10,border:"none", background: (!data.name || !data.email) ? C.border : C.accent, color:"#fff",fontSize:13,fontWeight:700,marginTop:4, cursor: (!data.name || !data.email) ? "default" : "pointer"} }, "Send My Estimate →") ) ) : null // end step 4 ), // end white card // Footer h('div', {style:{textAlign:"center",marginTop:16,fontSize:11,color:C.muted}}, "Industrial Clean Air Products · (602) 456-9661 · Serving AZ · CA · NV · NM · UT") ); } // ─── Mount ─────────────────────────────────────────────── // defer guarantees DOM is ready, but DOMContentLoaded guard is belt-and-suspenders function mountEstimator() { var container = document.getElementById("icap-estimator-root"); if (container && window.React && window.ReactDOM) { ReactDOM.createRoot(container).render(React.createElement(Estimator, null)); } } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", mountEstimator); } else { mountEstimator(); } })();