export function landscapingCalculator(form: FormTs) {
// Project type base costs
const projectBaseCosts: Record<string, number> = {
'design-only': 500,
'basic': 2500,
'standard': 7500,
'premium': 15000,
'luxury': 35000
};
// Yard size multipliers
const sizeMultipliers: Record<string, number> = {
'small': 0.7, // Under 1,000 sq ft
'medium': 1.0, // 1,000-5,000 sq ft
'large': 1.5, // 5,000-10,000 sq ft
'xlarge': 2.2, // 10,000+ sq ft
'acreage': 3.5 // 1+ acres
};
// Terrain difficulty multipliers
const terrainMultipliers: Record<string, number> = {
'flat': 1.0,
'slight': 1.15,
'moderate': 1.35,
'steep': 1.60,
'complex': 2.0
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Landscaping Project Estimate',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Service Location Section
const locationSection = form.addSubform('serviceLocation', { title: '📍 Project Location' });
locationSection.addRow(row => {
row.addAddress('projectAddress', {
label: 'Property Address',
placeholder: 'Enter the project address...',
showMap: true,
showDistance: true,
referenceAddress: {
formattedAddress: 'Service Center, Denver, CO',
coordinates: { lat: 39.7392, lng: -104.9903 }
},
restrictToCountries: ['US', 'CA'],
distanceUnit: 'miles',
isRequired: true
});
});
locationSection.addRow(row => {
row.addTextPanel('travelZoneInfo', {
computedValue: () => {
const addressField = locationSection.address('projectAddress');
const miles = addressField?.distance();
if (miles == null) return '📍 Enter address to check service area';
if (miles <= 20) return '📍 Within service area - Standard pricing';
if (miles <= 40) return '📍 Extended area - 5% distance surcharge';
if (miles <= 60) return '📍 Remote area - 10% distance surcharge';
return '📍 Long distance - 15%+ distance surcharge';
},
customStyles: { 'font-size': '0.9rem', 'color': '#15803d', 'background': '#dcfce7', 'padding': '10px', 'border-radius': '6px' }
});
});
// Project Overview Section
const projectSection = form.addSubform('projectOverview', { title: '📋 Project Overview' });
projectSection.addRow(row => {
row.addRadioButton('projectLevel', {
label: 'Project Scope',
options: [
{ id: 'design-only', name: 'Design Only ($500+) - Plans and consultation' },
{ id: 'basic', name: 'Basic ($2,500+) - Simple plantings, mulch, basic cleanup' },
{ id: 'standard', name: 'Standard ($7,500+) - New beds, plants, pathway, irrigation' },
{ id: 'premium', name: 'Premium ($15,000+) - Full front/back yard transformation' },
{ id: 'luxury', name: 'Luxury ($35,000+) - Complete outdoor living space' }
],
defaultValue: 'standard',
orientation: 'vertical',
isRequired: true
});
});
projectSection.addRow(row => {
row.addDropdown('yardSize', {
label: 'Yard/Project Size',
options: [
{ id: 'small', name: 'Small (under 1,000 sq ft)' },
{ id: 'medium', name: 'Medium (1,000-5,000 sq ft)' },
{ id: 'large', name: 'Large (5,000-10,000 sq ft)' },
{ id: 'xlarge', name: 'Extra Large (10,000+ sq ft)' },
{ id: 'acreage', name: 'Acreage (1+ acres)' }
],
defaultValue: 'medium',
isRequired: true
}, '1fr');
row.addDropdown('terrain', {
label: 'Terrain',
options: [
{ id: 'flat', name: 'Flat/Level' },
{ id: 'slight', name: 'Slight Slope (+15%)' },
{ id: 'moderate', name: 'Moderate Slope (+35%)' },
{ id: 'steep', name: 'Steep/Hillside (+60%)' },
{ id: 'complex', name: 'Complex/Multi-level (+100%)' }
],
defaultValue: 'flat'
}, '1fr');
});
// Softscape Section
const softscapeSection = form.addSubform('softscape', { title: '🌿 Softscape (Plants & Lawn)' });
softscapeSection.addRow(row => {
row.addCheckbox('newLawn', {
label: 'New Lawn Installation',
defaultValue: false
}, '1fr');
row.addDropdown('lawnType', {
label: 'Lawn Type',
options: [
{ id: 'seed', name: 'Seeding ($0.15-0.30/sq ft)' },
{ id: 'sod', name: 'Sod Installation ($1-2/sq ft)' },
{ id: 'artificial', name: 'Artificial Turf ($8-15/sq ft)' }
],
defaultValue: 'sod',
isVisible: () => softscapeSection.checkbox('newLawn')?.value() === true
}, '1fr');
});
softscapeSection.addRow(row => {
row.addSlider('lawnSqFt', {
label: 'Lawn Area',
min: 100,
max: 10000,
step: 100,
defaultValue: 2000,
showValue: true,
unit: 'sq ft',
isVisible: () => softscapeSection.checkbox('newLawn')?.value() === true
}, '1fr');
});
softscapeSection.addRow(row => {
row.addCheckbox('plantBeds', {
label: 'New Planting Beds',
defaultValue: true
}, '1fr');
row.addDropdown('plantingLevel', {
label: 'Planting Density',
options: [
{ id: 'sparse', name: 'Sparse - Basic foundation plants' },
{ id: 'moderate', name: 'Moderate - Full beds, good variety' },
{ id: 'lush', name: 'Lush - Dense, layered plantings' },
{ id: 'premium', name: 'Premium - Specimen plants, mature sizes' }
],
defaultValue: 'moderate',
isVisible: () => softscapeSection.checkbox('plantBeds')?.value() === true
}, '1fr');
});
softscapeSection.addRow(row => {
row.addCheckbox('trees', {
label: 'Tree Planting',
defaultValue: false
}, '1fr');
row.addInteger('treeCount', {
label: 'Number of Trees',
min: 1,
max: 20,
defaultValue: 3,
isVisible: () => softscapeSection.checkbox('trees')?.value() === true
}, '1fr');
});
softscapeSection.addRow(row => {
row.addDropdown('treeSize', {
label: 'Tree Size',
options: [
{ id: 'small', name: 'Small/Container ($150-400)' },
{ id: 'medium', name: 'Medium 2-3" caliper ($400-800)' },
{ id: 'large', name: 'Large 3-4" caliper ($800-1,500)' },
{ id: 'specimen', name: 'Specimen 4"+ ($1,500-5,000)' }
],
defaultValue: 'medium',
isVisible: () => softscapeSection.checkbox('trees')?.value() === true
}, '1fr');
});
softscapeSection.addRow(row => {
row.addCheckbox('mulch', {
label: 'Mulch Installation ($3-5/sq ft)',
defaultValue: true
}, '1fr');
row.addSlider('mulchSqFt', {
label: 'Mulch Area',
min: 50,
max: 3000,
step: 50,
defaultValue: 500,
showValue: true,
unit: 'sq ft',
isVisible: () => softscapeSection.checkbox('mulch')?.value() === true
}, '1fr');
});
// Hardscape Section
const hardscapeSection = form.addSubform('hardscape', { title: '🧱 Hardscape (Patios, Walkways, Walls)' });
hardscapeSection.addRow(row => {
row.addCheckbox('patio', {
label: 'Patio/Outdoor Living Area',
defaultValue: false
}, '1fr');
row.addDropdown('patioMaterial', {
label: 'Patio Material',
options: [
{ id: 'concrete', name: 'Stamped Concrete ($10-20/sq ft)' },
{ id: 'pavers', name: 'Pavers ($15-30/sq ft)' },
{ id: 'flagstone', name: 'Natural Flagstone ($20-40/sq ft)' },
{ id: 'composite', name: 'Composite Decking ($25-50/sq ft)' }
],
defaultValue: 'pavers',
isVisible: () => hardscapeSection.checkbox('patio')?.value() === true
}, '1fr');
});
hardscapeSection.addRow(row => {
row.addSlider('patioSqFt', {
label: 'Patio Area',
min: 50,
max: 1000,
step: 25,
defaultValue: 200,
showValue: true,
unit: 'sq ft',
isVisible: () => hardscapeSection.checkbox('patio')?.value() === true
}, '1fr');
});
hardscapeSection.addRow(row => {
row.addCheckbox('walkway', {
label: 'Walkways/Pathways',
defaultValue: false
}, '1fr');
row.addSlider('walkwayLength', {
label: 'Walkway Length',
min: 10,
max: 200,
step: 5,
defaultValue: 40,
showValue: true,
unit: 'linear ft',
isVisible: () => hardscapeSection.checkbox('walkway')?.value() === true
}, '1fr');
});
hardscapeSection.addRow(row => {
row.addCheckbox('retainingWall', {
label: 'Retaining Wall',
defaultValue: false
}, '1fr');
row.addSlider('wallLength', {
label: 'Wall Length',
min: 10,
max: 200,
step: 5,
defaultValue: 30,
showValue: true,
unit: 'linear ft',
isVisible: () => hardscapeSection.checkbox('retainingWall')?.value() === true
}, '1fr');
});
hardscapeSection.addRow(row => {
row.addDropdown('wallHeight', {
label: 'Wall Height',
options: [
{ id: 'low', name: 'Low (under 2 ft) - $20-40/linear ft' },
{ id: 'medium', name: 'Medium (2-4 ft) - $40-80/linear ft' },
{ id: 'high', name: 'High (4-6 ft) - $80-150/linear ft' },
{ id: 'tall', name: 'Tall (6+ ft) - $150-300/linear ft' }
],
defaultValue: 'medium',
isVisible: () => hardscapeSection.checkbox('retainingWall')?.value() === true
}, '1fr');
});
// Water Features Section
const waterSection = form.addSubform('waterFeatures', { title: '💧 Water Features & Irrigation' });
waterSection.addRow(row => {
row.addCheckbox('irrigation', {
label: 'Irrigation System',
defaultValue: false
}, '1fr');
row.addDropdown('irrigationType', {
label: 'Irrigation Type',
options: [
{ id: 'basic', name: 'Basic Drip ($1-2/sq ft)' },
{ id: 'sprinkler', name: 'Sprinkler System ($2-4/sq ft)' },
{ id: 'smart', name: 'Smart System ($3-6/sq ft)' }
],
defaultValue: 'sprinkler',
isVisible: () => waterSection.checkbox('irrigation')?.value() === true
}, '1fr');
});
waterSection.addRow(row => {
row.addCheckbox('waterFeature', {
label: 'Decorative Water Feature',
defaultValue: false
}, '1fr');
row.addDropdown('waterType', {
label: 'Feature Type',
options: [
{ id: 'fountain', name: 'Fountain ($500-3,000)' },
{ id: 'pond', name: 'Pond ($2,000-10,000)' },
{ id: 'waterfall', name: 'Waterfall ($3,000-15,000)' },
{ id: 'stream', name: 'Stream Feature ($5,000-20,000)' }
],
defaultValue: 'fountain',
isVisible: () => waterSection.checkbox('waterFeature')?.value() === true
}, '1fr');
});
// Outdoor Living Section
const outdoorSection = form.addSubform('outdoorLiving', { title: '🏡 Outdoor Living Features' });
outdoorSection.addRow(row => {
row.addCheckbox('lighting', {
label: 'Landscape Lighting (+$2,000-10,000)',
defaultValue: false
}, '1fr');
row.addCheckbox('fireFeature', {
label: 'Fire Pit/Fireplace (+$1,500-15,000)',
defaultValue: false
}, '1fr');
});
outdoorSection.addRow(row => {
row.addCheckbox('outdoorKitchen', {
label: 'Outdoor Kitchen (+$5,000-30,000)',
defaultValue: false
}, '1fr');
row.addCheckbox('pergola', {
label: 'Pergola/Shade Structure (+$3,000-20,000)',
defaultValue: false
}, '1fr');
});
outdoorSection.addRow(row => {
row.addCheckbox('fence', {
label: 'Fencing',
defaultValue: false
}, '1fr');
row.addSlider('fenceLength', {
label: 'Fence Length',
min: 20,
max: 500,
step: 10,
defaultValue: 100,
showValue: true,
unit: 'linear ft',
isVisible: () => outdoorSection.checkbox('fence')?.value() === true
}, '1fr');
});
// Site Prep Section
const prepSection = form.addSubform('sitePrep', { title: '🚜 Site Preparation' });
prepSection.addRow(row => {
row.addCheckbox('grading', {
label: 'Grading/Drainage Work (+$1,000-5,000)',
defaultValue: false
}, '1fr');
row.addCheckbox('demolition', {
label: 'Existing Landscape Removal (+$500-3,000)',
defaultValue: false
}, '1fr');
});
prepSection.addRow(row => {
row.addCheckbox('treeRemoval', {
label: 'Tree Removal',
defaultValue: false
}, '1fr');
row.addInteger('treesToRemove', {
label: 'Trees to Remove',
min: 1,
max: 10,
defaultValue: 2,
isVisible: () => prepSection.checkbox('treeRemoval')?.value() === true
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Helper to calculate distance surcharge multiplier
const getDistanceSurchargeMultiplier = () => {
const addressField = locationSection.address('projectAddress');
const miles = addressField?.distance();
if (miles == null || miles <= 20) return 1.0;
if (miles <= 40) return 1.05;
if (miles <= 60) return 1.10;
return 1.15 + Math.floor((miles - 60) / 20) * 0.05;
};
// Price Summary Section
const summarySection = form.addSubform('summary', { title: '💰 Your Estimate', isCollapsible: false });
const getBasePrice = () => {
const level = projectSection.radioButton('projectLevel')?.value() || 'standard';
const size = projectSection.dropdown('yardSize')?.value() || 'medium';
const terrain = projectSection.dropdown('terrain')?.value() || 'flat';
const base = projectBaseCosts[level] || 7500;
const sizeMult = sizeMultipliers[size] || 1;
const terrainMult = terrainMultipliers[terrain] || 1;
return base * sizeMult * terrainMult;
};
summarySection.addRow(row => {
row.addPriceDisplay('basePrice', {
label: 'Base Project Cost',
computedValue: () => Math.round(getBasePrice()),
variant: 'default'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('softscapeCost', {
label: 'Softscape',
computedValue: () => {
let total = 0;
// Lawn
if (softscapeSection.checkbox('newLawn')?.value()) {
const sqft = softscapeSection.slider('lawnSqFt')?.value() || 2000;
const type = softscapeSection.dropdown('lawnType')?.value() || 'sod';
const rates: Record<string, number> = { 'seed': 0.25, 'sod': 1.50, 'artificial': 11 };
total += sqft * (rates[type] || 1.50);
}
// Plant beds
if (softscapeSection.checkbox('plantBeds')?.value()) {
const density = softscapeSection.dropdown('plantingLevel')?.value() || 'moderate';
const costs: Record<string, number> = {
'sparse': 1500, 'moderate': 3500, 'lush': 6000, 'premium': 12000
};
total += costs[density] || 3500;
}
// Trees
if (softscapeSection.checkbox('trees')?.value()) {
const count = softscapeSection.integer('treeCount')?.value() || 3;
const size = softscapeSection.dropdown('treeSize')?.value() || 'medium';
const treeCosts: Record<string, number> = {
'small': 275, 'medium': 600, 'large': 1100, 'specimen': 3000
};
total += count * (treeCosts[size] || 600);
}
// Mulch
if (softscapeSection.checkbox('mulch')?.value()) {
const sqft = softscapeSection.slider('mulchSqFt')?.value() || 500;
total += sqft * 4;
}
return Math.round(total);
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('hardscapeCost', {
label: 'Hardscape',
computedValue: () => {
let total = 0;
// Patio
if (hardscapeSection.checkbox('patio')?.value()) {
const sqft = hardscapeSection.slider('patioSqFt')?.value() || 200;
const material = hardscapeSection.dropdown('patioMaterial')?.value() || 'pavers';
const rates: Record<string, number> = {
'concrete': 15, 'pavers': 22, 'flagstone': 30, 'composite': 38
};
total += sqft * (rates[material] || 22);
}
// Walkway
if (hardscapeSection.checkbox('walkway')?.value()) {
const length = hardscapeSection.slider('walkwayLength')?.value() || 40;
total += length * 35; // Average 3ft wide walkway
}
// Retaining wall
if (hardscapeSection.checkbox('retainingWall')?.value()) {
const length = hardscapeSection.slider('wallLength')?.value() || 30;
const height = hardscapeSection.dropdown('wallHeight')?.value() || 'medium';
const wallRates: Record<string, number> = {
'low': 30, 'medium': 60, 'high': 115, 'tall': 225
};
total += length * (wallRates[height] || 60);
}
return Math.round(total);
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('waterCost', {
label: 'Water Features & Irrigation',
computedValue: () => {
let total = 0;
// Irrigation
if (waterSection.checkbox('irrigation')?.value()) {
const size = projectSection.dropdown('yardSize')?.value() || 'medium';
const sqftEstimates: Record<string, number> = {
'small': 800, 'medium': 3000, 'large': 7500, 'xlarge': 12000, 'acreage': 25000
};
const sqft = sqftEstimates[size] || 3000;
const type = waterSection.dropdown('irrigationType')?.value() || 'sprinkler';
const rates: Record<string, number> = { 'basic': 1.5, 'sprinkler': 3, 'smart': 4.5 };
total += sqft * (rates[type] || 3);
}
// Water feature
if (waterSection.checkbox('waterFeature')?.value()) {
const type = waterSection.dropdown('waterType')?.value() || 'fountain';
const costs: Record<string, number> = {
'fountain': 1750, 'pond': 6000, 'waterfall': 9000, 'stream': 12500
};
total += costs[type] || 1750;
}
return Math.round(total);
},
variant: 'default',
prefix: '+'
}, '1fr');
row.addPriceDisplay('outdoorCost', {
label: 'Outdoor Living Features',
computedValue: () => {
let total = 0;
if (outdoorSection.checkbox('lighting')?.value()) total += 5000;
if (outdoorSection.checkbox('fireFeature')?.value()) total += 6000;
if (outdoorSection.checkbox('outdoorKitchen')?.value()) total += 15000;
if (outdoorSection.checkbox('pergola')?.value()) total += 10000;
if (outdoorSection.checkbox('fence')?.value()) {
const length = outdoorSection.slider('fenceLength')?.value() || 100;
total += length * 35; // Average fence cost
}
return Math.round(total);
},
variant: 'default',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addPriceDisplay('prepCost', {
label: 'Site Preparation',
computedValue: () => {
let total = 0;
if (prepSection.checkbox('grading')?.value()) total += 2500;
if (prepSection.checkbox('demolition')?.value()) total += 1500;
if (prepSection.checkbox('treeRemoval')?.value()) {
const trees = prepSection.integer('treesToRemove')?.value() || 2;
total += trees * 800;
}
return Math.round(total);
},
variant: 'default',
prefix: '+',
isVisible: () => {
return prepSection.checkbox('grading')?.value() ||
prepSection.checkbox('demolition')?.value() ||
prepSection.checkbox('treeRemoval')?.value();
}
});
});
summarySection.addRow(row => {
row.addPriceDisplay('distanceSurcharge', {
label: 'Distance Surcharge',
computedValue: () => {
const multiplier = getDistanceSurchargeMultiplier();
if (multiplier <= 1) return 0;
return Math.round((multiplier - 1) * 100);
},
variant: 'default',
prefix: '+',
suffix: '%',
isVisible: () => getDistanceSurchargeMultiplier() > 1
});
});
summarySection.addSpacer({ showLine: true, lineStyle: 'solid', lineColor: '#e2e8f0' });
summarySection.addRow(row => {
row.addPriceDisplay('totalEstimate', {
label: 'Total Estimated Investment',
computedValue: () => {
let total = getBasePrice();
// Softscape
if (softscapeSection.checkbox('newLawn')?.value()) {
const sqft = softscapeSection.slider('lawnSqFt')?.value() || 2000;
const type = softscapeSection.dropdown('lawnType')?.value() || 'sod';
const rates: Record<string, number> = { 'seed': 0.25, 'sod': 1.50, 'artificial': 11 };
total += sqft * (rates[type] || 1.50);
}
if (softscapeSection.checkbox('plantBeds')?.value()) {
const density = softscapeSection.dropdown('plantingLevel')?.value() || 'moderate';
const costs: Record<string, number> = {
'sparse': 1500, 'moderate': 3500, 'lush': 6000, 'premium': 12000
};
total += costs[density] || 3500;
}
if (softscapeSection.checkbox('trees')?.value()) {
const count = softscapeSection.integer('treeCount')?.value() || 3;
const size = softscapeSection.dropdown('treeSize')?.value() || 'medium';
const treeCosts: Record<string, number> = {
'small': 275, 'medium': 600, 'large': 1100, 'specimen': 3000
};
total += count * (treeCosts[size] || 600);
}
if (softscapeSection.checkbox('mulch')?.value()) {
const sqft = softscapeSection.slider('mulchSqFt')?.value() || 500;
total += sqft * 4;
}
// Hardscape
if (hardscapeSection.checkbox('patio')?.value()) {
const sqft = hardscapeSection.slider('patioSqFt')?.value() || 200;
const material = hardscapeSection.dropdown('patioMaterial')?.value() || 'pavers';
const rates: Record<string, number> = {
'concrete': 15, 'pavers': 22, 'flagstone': 30, 'composite': 38
};
total += sqft * (rates[material] || 22);
}
if (hardscapeSection.checkbox('walkway')?.value()) {
const length = hardscapeSection.slider('walkwayLength')?.value() || 40;
total += length * 35;
}
if (hardscapeSection.checkbox('retainingWall')?.value()) {
const length = hardscapeSection.slider('wallLength')?.value() || 30;
const height = hardscapeSection.dropdown('wallHeight')?.value() || 'medium';
const wallRates: Record<string, number> = {
'low': 30, 'medium': 60, 'high': 115, 'tall': 225
};
total += length * (wallRates[height] || 60);
}
// Water
if (waterSection.checkbox('irrigation')?.value()) {
const size = projectSection.dropdown('yardSize')?.value() || 'medium';
const sqftEstimates: Record<string, number> = {
'small': 800, 'medium': 3000, 'large': 7500, 'xlarge': 12000, 'acreage': 25000
};
const sqft = sqftEstimates[size] || 3000;
const type = waterSection.dropdown('irrigationType')?.value() || 'sprinkler';
const rates: Record<string, number> = { 'basic': 1.5, 'sprinkler': 3, 'smart': 4.5 };
total += sqft * (rates[type] || 3);
}
if (waterSection.checkbox('waterFeature')?.value()) {
const type = waterSection.dropdown('waterType')?.value() || 'fountain';
const costs: Record<string, number> = {
'fountain': 1750, 'pond': 6000, 'waterfall': 9000, 'stream': 12500
};
total += costs[type] || 1750;
}
// Outdoor
if (outdoorSection.checkbox('lighting')?.value()) total += 5000;
if (outdoorSection.checkbox('fireFeature')?.value()) total += 6000;
if (outdoorSection.checkbox('outdoorKitchen')?.value()) total += 15000;
if (outdoorSection.checkbox('pergola')?.value()) total += 10000;
if (outdoorSection.checkbox('fence')?.value()) {
const length = outdoorSection.slider('fenceLength')?.value() || 100;
total += length * 35;
}
// Prep
if (prepSection.checkbox('grading')?.value()) total += 2500;
if (prepSection.checkbox('demolition')?.value()) total += 1500;
if (prepSection.checkbox('treeRemoval')?.value()) {
const trees = prepSection.integer('treesToRemove')?.value() || 2;
total += trees * 800;
}
// Apply distance surcharge
total *= getDistanceSurchargeMultiplier();
return Math.round(total);
},
variant: 'large'
});
});
const finalSection = form.addSubform('final', {
title: '🧾 Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('totalEstimate', {
label: 'Total Estimated Investment',
computedValue: () => {
let total = getBasePrice();
if (softscapeSection.checkbox('newLawn')?.value()) {
const sqft = softscapeSection.slider('lawnSqFt')?.value() || 2000;
const type = softscapeSection.dropdown('lawnType')?.value() || 'sod';
const rates: Record<string, number> = { 'seed': 0.25, 'sod': 1.50, 'artificial': 11 };
total += sqft * (rates[type] || 1.50);
}
if (softscapeSection.checkbox('plantBeds')?.value()) {
const density = softscapeSection.dropdown('plantingLevel')?.value() || 'moderate';
const costs: Record<string, number> = {
'sparse': 1500, 'moderate': 3500, 'lush': 6000, 'premium': 12000
};
total += costs[density] || 3500;
}
if (softscapeSection.checkbox('trees')?.value()) {
const count = softscapeSection.integer('treeCount')?.value() || 3;
const size = softscapeSection.dropdown('treeSize')?.value() || 'medium';
const treeCosts: Record<string, number> = {
'small': 275, 'medium': 600, 'large': 1100, 'specimen': 3000
};
total += count * (treeCosts[size] || 600);
}
if (softscapeSection.checkbox('mulch')?.value()) {
const sqft = softscapeSection.slider('mulchSqFt')?.value() || 500;
total += sqft * 4;
}
if (hardscapeSection.checkbox('patio')?.value()) {
const sqft = hardscapeSection.slider('patioSqFt')?.value() || 200;
const material = hardscapeSection.dropdown('patioMaterial')?.value() || 'pavers';
const rates: Record<string, number> = {
'concrete': 15, 'pavers': 22, 'flagstone': 30, 'composite': 38
};
total += sqft * (rates[material] || 22);
}
if (hardscapeSection.checkbox('walkway')?.value()) {
const length = hardscapeSection.slider('walkwayLength')?.value() || 40;
total += length * 35;
}
if (hardscapeSection.checkbox('retainingWall')?.value()) {
const length = hardscapeSection.slider('wallLength')?.value() || 30;
const height = hardscapeSection.dropdown('wallHeight')?.value() || 'medium';
const wallRates: Record<string, number> = {
'low': 30, 'medium': 60, 'high': 115, 'tall': 225
};
total += length * (wallRates[height] || 60);
}
if (waterSection.checkbox('irrigation')?.value()) {
const size = projectSection.dropdown('yardSize')?.value() || 'medium';
const sqftEstimates: Record<string, number> = {
'small': 800, 'medium': 3000, 'large': 7500, 'xlarge': 12000, 'acreage': 25000
};
const sqft = sqftEstimates[size] || 3000;
const type = waterSection.dropdown('irrigationType')?.value() || 'sprinkler';
const rates: Record<string, number> = { 'basic': 1.5, 'sprinkler': 3, 'smart': 4.5 };
total += sqft * (rates[type] || 3);
}
if (waterSection.checkbox('waterFeature')?.value()) {
const type = waterSection.dropdown('waterType')?.value() || 'fountain';
const costs: Record<string, number> = {
'fountain': 1750, 'pond': 6000, 'waterfall': 9000, 'stream': 12500
};
total += costs[type] || 1750;
}
if (outdoorSection.checkbox('lighting')?.value()) total += 5000;
if (outdoorSection.checkbox('fireFeature')?.value()) total += 6000;
if (outdoorSection.checkbox('outdoorKitchen')?.value()) total += 15000;
if (outdoorSection.checkbox('pergola')?.value()) total += 10000;
if (outdoorSection.checkbox('fence')?.value()) {
const length = outdoorSection.slider('fenceLength')?.value() || 100;
total += length * 35;
}
if (prepSection.checkbox('grading')?.value()) total += 2500;
if (prepSection.checkbox('demolition')?.value()) total += 1500;
if (prepSection.checkbox('treeRemoval')?.value()) {
const trees = prepSection.integer('treesToRemove')?.value() || 2;
total += trees * 800;
}
// Apply distance surcharge
total *= getDistanceSurchargeMultiplier();
return Math.round(total);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Detailed proposal requires on-site consultation.',
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'font-style': 'italic' }
});
});
form.configureSubmitButton({
label: 'Schedule Consultation'
});
}