Skip to content

Instantly share code, notes, and snippets.

@shauryashah94
Last active December 31, 2025 00:47
Show Gist options
  • Select an option

  • Save shauryashah94/55e12fb8de6348c08bd20425ea7e617e to your computer and use it in GitHub Desktop.

Select an option

Save shauryashah94/55e12fb8de6348c08bd20425ea7e617e to your computer and use it in GitHub Desktop.
caulking-selector.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Caulking & Sealant Selector - The Caulking Store</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
* {
font-family: 'Inter', sans-serif;
}
.gradient-bg {
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
}
.primary-gradient {
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
}
.secondary-gradient {
background: linear-gradient(135deg, #10b981 0%, #047857 100%);
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
.option-selected {
border-color: #3b82f6;
background-color: #eff6ff;
}
.option-icon {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
font-size: 24px;
}
.progress-dot {
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #cbd5e1;
}
.progress-dot.active {
background-color: #3b82f6;
}
.progress-dot.completed {
background-color: #10b981;
}
.fade-in {
animation: fadeIn 0.5s ease-in-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.tag {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: 600;
margin-right: 6px;
margin-bottom: 6px;
}
.slide-in-right {
animation: slideInRight 0.5s ease-out;
}
@keyframes slideInRight {
from { transform: translateX(20px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); }
70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
}
.scrollbar-custom {
scrollbar-width: thin;
scrollbar-color: #cbd5e1 transparent;
}
.scrollbar-custom::-webkit-scrollbar {
width: 6px;
}
.scrollbar-custom::-webkit-scrollbar-track {
background: transparent;
}
.scrollbar-custom::-webkit-scrollbar-thumb {
background-color: #cbd5e1;
border-radius: 20px;
}
</style>
</head>
<body class="gradient-bg min-h-screen">
<!-- Header -->
<header class="primary-gradient text-white shadow-lg">
<div class="container mx-auto px-4 py-6">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="flex items-center mb-4 md:mb-0">
<div class="bg-white p-2 rounded-lg mr-4">
<i class="fas fa-tools text-blue-600 text-2xl"></i>
</div>
<div>
<h1 class="text-2xl md:text-3xl font-bold">Caulking & Sealant Finder</h1>
<p class="text-blue-100">Find the perfect product for your project</p>
</div>
</div>
<div class="text-center md:text-right">
<p class="text-lg font-semibold">Powered by</p>
<p class="text-xl font-bold text-white">The Caulking Store</p>
<a href="https://thecaulkingstore.com" target="_blank" class="text-blue-100 hover:text-white underline text-sm">thecaulkingstore.com</a>
</div>
</div>
</div>
</header>
<!-- Main App Container -->
<main class="container mx-auto px-4 py-8">
<!-- Progress Bar -->
<div class="mb-10 max-w-4xl mx-auto">
<div class="flex justify-between mb-4">
<div class="text-center">
<div class="progress-dot completed mx-auto mb-2"></div>
<span class="text-sm font-medium text-gray-700">Application</span>
</div>
<div class="flex-1 mt-1 mx-2">
<div class="h-1 bg-gray-200 rounded-full overflow-hidden">
<div id="progress-bar" class="h-full secondary-gradient transition-all duration-500" style="width: 25%"></div>
</div>
</div>
<div class="text-center">
<div class="progress-dot active mx-auto mb-2"></div>
<span class="text-sm font-medium text-gray-700">Location</span>
</div>
<div class="flex-1 mt-1 mx-2">
<div class="h-1 bg-gray-200 rounded-full"></div>
</div>
<div class="text-center">
<div class="progress-dot mx-auto mb-2"></div>
<span class="text-sm font-medium text-gray-700">Material</span>
</div>
<div class="flex-1 mt-1 mx-2">
<div class="h-1 bg-gray-200 rounded-full"></div>
</div>
<div class="text-center">
<div class="progress-dot mx-auto mb-2"></div>
<span class="text-sm font-medium text-gray-700">Results</span>
</div>
</div>
<div class="text-center mt-2">
<span id="step-indicator" class="font-bold text-blue-600">Step 1 of 4: Choose Application Type</span>
</div>
</div>
<!-- App Content -->
<div id="app-content" class="max-w-6xl mx-auto">
<!-- Question Container -->
<div id="question-container" class="fade-in">
<!-- Question will be loaded here by JavaScript -->
</div>
<!-- Results Container (Hidden Initially) -->
<div id="results-container" class="hidden">
<!-- Results will be loaded here by JavaScript -->
</div>
<!-- Your Selections Summary -->
<div id="selections-summary" class="mt-8 bg-white rounded-xl shadow-lg p-6 max-w-4xl mx-auto hidden">
<h3 class="text-xl font-bold text-gray-800 mb-4">Your Project Details</h3>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Application</p>
<p id="summary-application" class="font-semibold text-gray-800">-</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Location</p>
<p id="summary-location" class="font-semibold text-gray-800">-</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Material</p>
<p id="summary-material" class="font-semibold text-gray-800">-</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Movement</p>
<p id="summary-movement" class="font-semibold text-gray-800">-</p>
</div>
</div>
<div class="mt-4 flex justify-end">
<button id="restart-btn" class="px-6 py-2 bg-gray-200 text-gray-800 rounded-lg font-medium hover:bg-gray-300 transition-colors">
<i class="fas fa-redo-alt mr-2"></i>Start Over
</button>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="bg-gray-900 text-white mt-16 py-8">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 class="text-xl font-bold mb-4">Need Help?</h3>
<p class="text-gray-300 mb-4">Our experts are ready to assist you with your project.</p>
<a href="tel:416-860-8308" class="inline-flex items-center text-blue-300 hover:text-white">
<i class="fas fa-phone mr-2"></i> (416) 860-8308
</a>
</div>
<div>
<h3 class="text-xl font-bold mb-4">Contact Us</h3>
<p class="text-gray-300 mb-2">
<i class="fas fa-envelope mr-2"></i> [email protected]
</p>
<p class="text-gray-300">
<i class="fas fa-globe mr-2"></i> thecaulkingstore.com
</p>
</div>
<div>
<h3 class="text-xl font-bold mb-4">About This Tool</h3>
<p class="text-gray-300 text-sm">
This tool helps you find the right caulking and sealants based on products available at The Caulking Store. Recommendations are based on product specifications and intended applications.
</p>
</div>
</div>
<div class="border-t border-gray-800 mt-8 pt-6 text-center text-gray-400 text-sm">
<p>© 2023 The Caulking Store. This is a product selection tool for informational purposes.</p>
</div>
</div>
</footer>
<!-- JavaScript -->
<script>
// Product database based on The Caulking Store products
const products = [
{
id: 1,
name: "Tremco Dymonic FC",
type: "Polyurethane Sealant",
category: "polyurethane",
application: ["exterior", "interior"],
location: ["control joints", "expansion joints", "perimeter seals"],
material: ["concrete", "metal", "masonry", "wood"],
movement: "High (±50%)",
features: ["UV resistant", "weatherproof", "paintable", "non-sag"],
colors: ["White", "Gray", "Black", "Aluminum", "Limestone"],
description: "High-performance polyurethane sealant for demanding applications with extreme movement",
bestFor: "Expansion joints, plaza decks, parking structures",
image: "polyurethane"
},
{
id: 2,
name: "Tremco Spectrem 1",
type: "Structural Silicone",
category: "silicone",
application: ["exterior"],
location: ["curtain wall", "structural glazing", "glass"],
material: ["glass", "metal", "granite"],
movement: "High (±50%)",
features: ["structural strength", "UV resistant", "weatherproof", "clear"],
colors: ["Clear", "Black", "Gray", "White"],
description: "High-strength structural silicone for glass and metal curtain wall systems",
bestFor: "Structural glazing, curtain wall, glass-to-glass applications",
image: "silicone"
},
{
id: 3,
name: "Tremco Spectrem 2",
type: "Silicone Sealant",
category: "silicone",
application: ["exterior", "interior"],
location: ["windows", "doors", "perimeter seals"],
material: ["glass", "metal", "masonry", "wood"],
movement: "Medium (±25%)",
features: ["weatherproof", "UV resistant", "mold resistant", "paintable"],
colors: ["Clear", "White", "Gray", "Black", "Brown"],
description: "Versatile silicone sealant for general purpose sealing applications",
bestFor: "Window and door perimeters, general caulking",
image: "silicone"
},
{
id: 4,
name: "Tremco 830 Elastomeric",
type: "Acrylic Latex",
category: "acrylic",
application: ["interior", "exterior"],
location: ["trim", "molding", "cracks", "gaps"],
material: ["drywall", "wood", "plaster", "painted surfaces"],
movement: "Low (±12.5%)",
features: ["paintable", "water clean-up", "low odor", "fast drying"],
colors: ["White", "Clear"],
description: "High-quality acrylic latex caulk for interior and exterior trim work",
bestFor: "Trim, molding, baseboards, interior cracks",
image: "acrylic"
},
{
id: 5,
name: "Tremco ExoAir 440",
type: "Air Barrier Sealant",
category: "specialty",
application: ["exterior"],
location: ["air barriers", "weather barriers", "sheathing"],
material: ["OSB", "plywood", "concrete", "metal"],
movement: "High (±50%)",
features: ["air barrier", "water resistant", "UV resistant", "compatible with membranes"],
colors: ["Gray"],
description: "High-performance sealant for air barrier systems and weather sealing",
bestFor: "Air barrier systems, sheathing seams, weather barriers",
image: "specialty"
},
{
id: 6,
name: "Tremco FyreSeal",
type: "Firestop Sealant",
category: "firestop",
application: ["firestop"],
location: ["penetrations", "joints", "gaps"],
material: ["concrete", "drywall", "metal", "pipe"],
movement: "Low (±12.5%)",
features: ["fire rated", "smoke seal", "intumescent", "UL classified"],
colors: ["Red", "Gray"],
description: "Intumescent firestop sealant for through-penetrations and joints",
bestFor: "Fire-rated walls, pipe penetrations, electrical penetrations",
image: "firestop"
},
{
id: 7,
name: "Pecora 890",
type: "Polyurethane Sealant",
category: "polyurethane",
application: ["exterior"],
location: ["concrete joints", "expansion joints", "pavement"],
material: ["concrete", "asphalt", "metal"],
movement: "High (±50%)",
features: ["traffic durable", "fuel resistant", "chemical resistant", "flexible"],
colors: ["Gray", "Black"],
description: "Heavy-duty polyurethane sealant for demanding pavement applications",
bestFor: "Airport runways, bridge decks, industrial floors",
image: "polyurethane"
},
{
id: 8,
name: "Tremco Vulkem 116",
type: "Polyurethane Sealant",
category: "polyurethane",
application: ["exterior"],
location: ["expansion joints", "precast panels", "perimeter seals"],
material: ["concrete", "metal", "glass", "masonry"],
movement: "High (±50%)",
features: ["self-leveling", "gun grade", "toolable", "weatherproof"],
colors: ["Gray", "Black", "White", "Terracotta"],
description: "Premium polyurethane sealant for horizontal and vertical applications",
bestFor: "Horizontal expansion joints, precast panels, vertical joints",
image: "polyurethane"
},
{
id: 9,
name: "Tremsil 600",
type: "Neutral Silicone",
category: "silicone",
application: ["interior"],
location: ["kitchen", "bathroom", "wet areas"],
material: ["tile", "porcelain", "fiberglass", "acrylic"],
movement: "Medium (±25%)",
features: ["mold resistant", "mildew resistant", "waterproof", "flexible"],
colors: ["Clear", "White"],
description: "Neutral cure silicone sealant for kitchen and bathroom applications",
bestFor: "Bathtubs, showers, sinks, kitchen backsplashes",
image: "silicone"
},
{
id: 10,
name: "OSI Quad Max",
type: "Advanced Polymer",
category: "hybrid",
application: ["exterior", "interior"],
location: ["windows", "doors", "siding", "trim"],
material: ["vinyl", "wood", "metal", "fiberglass"],
movement: "High (±50%)",
features: ["paintable", "stainable", "flexible", "adheres to most surfaces"],
colors: ["White", "Clear", "Brown", "Gray"],
description: "Advanced polymer sealant with superior adhesion and flexibility",
bestFor: "Windows, doors, siding, trim, multi-surface applications",
image: "hybrid"
}
];
// User selections
const userSelections = {
application: null,
location: null,
material: null,
movement: null
};
// Current step
let currentStep = 1;
// Questions data
const questions = {
1: {
title: "What type of application?",
subtitle: "Choose where the sealant will be used",
options: [
{
id: "exterior",
name: "Exterior",
description: "Outdoor applications exposed to weather",
icon: "fas fa-sun",
color: "bg-yellow-100 text-yellow-800"
},
{
id: "interior",
name: "Interior",
description: "Indoor applications protected from weather",
icon: "fas fa-home",
color: "bg-blue-100 text-blue-800"
},
{
id: "firestop",
name: "Firestop",
description: "Fire-rated sealing for penetrations and joints",
icon: "fas fa-fire",
color: "bg-red-100 text-red-800"
},
{
id: "wet-area",
name: "Wet Area",
description: "Kitchens, bathrooms, and other wet locations",
icon: "fas fa-tint",
color: "bg-cyan-100 text-cyan-800"
}
]
},
2: {
title: "Where will it be applied?",
subtitle: "Select the specific location",
options: {
exterior: [
{ id: "windows-doors", name: "Windows & Doors", icon: "fas fa-window-maximize" },
{ id: "siding-trim", name: "Siding & Trim", icon: "fas fa-border-all" },
{ id: "expansion-joints", name: "Expansion Joints", icon: "fas fa-expand-alt" },
{ id: "roof-flashing", name: "Roof & Flashing", icon: "fas fa-roofing" },
{ id: "concrete-joints", name: "Concrete Joints", icon: "fas fa-road" },
{ id: "curtain-wall", name: "Curtain Wall", icon: "fas fa-building" }
],
interior: [
{ id: "trim-molding", name: "Trim & Molding", icon: "fas fa-ruler-combined" },
{ id: "drywall-cracks", name: "Drywall Cracks", icon: "fas fa-crop-alt" },
{ id: "baseboards", name: "Baseboards", icon: "fas fa-border-style" },
{ id: "ceilings", name: "Ceilings", icon: "fas fa-border-top" },
{ id: "corners", name: "Corners", icon: "fas fa-vector-square" }
],
firestop: [
{ id: "pipe-penetrations", name: "Pipe Penetrations", icon: "fas fa-wrench" },
{ id: "electrical-penetrations", name: "Electrical Penetrations", icon: "fas fa-plug" },
{ id: "fire-rated-joints", name: "Fire-Rated Joints", icon: "fas fa-fire-extinguisher" },
{ id: "duct-penetrations", name: "Duct Penetrations", icon: "fas fa-wind" }
],
"wet-area": [
{ id: "bathtub-shower", name: "Bathtub & Shower", icon: "fas fa-bath" },
{ id: "kitchen-sink", name: "Kitchen Sink", icon: "fas fa-sink" },
{ id: "backsplash", name: "Backsplash", icon: "fas fa-utensils" },
{ id: "vanity-counter", name: "Vanity & Counter", icon: "fas fa-toilet" }
]
}
},
3: {
title: "What material are you sealing?",
subtitle: "Choose the primary surface material",
options: [
{ id: "concrete", name: "Concrete / Masonry", icon: "fas fa-gopuram", color: "bg-gray-100" },
{ id: "wood", name: "Wood", icon: "fas fa-tree", color: "bg-amber-100" },
{ id: "metal", name: "Metal / Aluminum", icon: "fas fa-cog", color: "bg-gray-200" },
{ id: "glass", name: "Glass", icon: "fas fa-wine-glass-alt", color: "bg-blue-50" },
{ id: "drywall", name: "Drywall / Plaster", icon: "fas fa-th", color: "bg-white" },
{ id: "tile", name: "Tile / Ceramic", icon: "fas fa-border-style", color: "bg-red-50" },
{ id: "vinyl", name: "Vinyl / PVC", icon: "fas fa-cube", color: "bg-purple-50" },
{ id: "plastic", name: "Plastic / Fiberglass", icon: "fas fa-industry", color: "bg-cyan-50" }
]
}
};
// Initialize the app
document.addEventListener('DOMContentLoaded', function() {
loadStep(currentStep);
// Add restart button event listener
document.getElementById('restart-btn').addEventListener('click', restartApp);
});
// Load a step based on the step number
function loadStep(step) {
const container = document.getElementById('question-container');
const progressBar = document.getElementById('progress-bar');
const stepIndicator = document.getElementById('step-indicator');
// Update progress bar
progressBar.style.width = `${(step / 4) * 100}%`;
// Update progress dots
updateProgressDots(step);
if (step <= 3) {
// Show question container, hide results
container.classList.remove('hidden');
document.getElementById('results-container').classList.add('hidden');
document.getElementById('selections-summary').classList.add('hidden');
let html = '';
if (step === 1) {
stepIndicator.textContent = "Step 1 of 4: Choose Application Type";
html = renderQuestion(questions[1]);
}
else if (step === 2) {
stepIndicator.textContent = "Step 2 of 4: Select Location";
if (userSelections.application) {
html = renderQuestion(questions[2], userSelections.application);
} else {
// If no application selected, go back to step 1
currentStep = 1;
loadStep(1);
return;
}
}
else if (step === 3) {
stepIndicator.textContent = "Step 3 of 4: Choose Material";
html = renderQuestion(questions[3]);
}
container.innerHTML = html;
container.classList.remove('fade-in');
void container.offsetWidth; // Trigger reflow
container.classList.add('fade-in');
// Add event listeners to options
setTimeout(() => {
const optionButtons = document.querySelectorAll('.option-btn');
optionButtons.forEach(button => {
button.addEventListener('click', function() {
const value = this.getAttribute('data-value');
handleOptionSelection(step, value);
});
});
}, 100);
}
else if (step === 4) {
// Show results
stepIndicator.textContent = "Step 4 of 4: View Recommendations";
showResults();
}
}
// Render a question
function renderQuestion(question, filter = null) {
let options = question.options;
// If this is step 2, filter options based on application
if (currentStep === 2 && filter) {
options = question.options[filter];
}
let html = `
<div class="bg-white rounded-2xl shadow-xl p-8 card-hover">
<div class="text-center mb-10">
<h2 class="text-3xl md:text-4xl font-bold text-gray-800 mb-3">${question.title}</h2>
<p class="text-gray-600 text-lg max-w-2xl mx-auto">${question.subtitle}</p>
</div>
<div class="grid grid-cols-1 ${options.length > 4 ? 'md:grid-cols-2' : 'md:grid-cols-2 lg:grid-cols-4'} gap-6">
`;
options.forEach(option => {
const isSelected = (
(currentStep === 1 && userSelections.application === option.id) ||
(currentStep === 2 && userSelections.location === option.id) ||
(currentStep === 3 && userSelections.material === option.id)
);
html += `
<button class="option-btn w-full h-full p-6 rounded-xl border-2 text-left transition-all duration-300 card-hover ${isSelected ? 'option-selected border-blue-500 shadow-md' : 'border-gray-200 hover:border-blue-300'}"
data-value="${option.id}">
<div class="flex flex-col items-center text-center">
<div class="option-icon ${option.color || 'bg-blue-100 text-blue-600'} mb-4">
<i class="${option.icon}"></i>
</div>
<h3 class="text-xl font-bold text-gray-800 mb-2">${option.name}</h3>
${option.description ? `<p class="text-gray-600 text-sm mb-4">${option.description}</p>` : ''}
${isSelected ? `
<div class="mt-2 text-blue-600">
<i class="fas fa-check-circle"></i> Selected
</div>
` : ''}
</div>
</button>
`;
});
html += `
</div>
<div class="flex justify-between mt-10 pt-6 border-t border-gray-100">
${currentStep > 1 ? `
<button id="back-btn" class="px-8 py-3 bg-gray-100 text-gray-800 rounded-lg font-semibold hover:bg-gray-200 transition-colors">
<i class="fas fa-arrow-left mr-2"></i> Back
</button>
` : '<div></div>'}
${currentStep < 3 ? `
<button id="next-btn" class="px-8 py-3 bg-gray-200 text-gray-800 rounded-lg font-semibold hover:bg-gray-300 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
${!hasSelectionForCurrentStep() ? 'disabled' : ''}>
Continue <i class="fas fa-arrow-right ml-2"></i>
</button>
` : `
<button id="find-results-btn" class="px-8 py-3 primary-gradient text-white rounded-lg font-semibold hover:opacity-90 transition-all shadow-lg hover:shadow-xl ${!hasSelectionForCurrentStep() ? 'disabled:opacity-50 disabled:cursor-not-allowed' : ''}"
${!hasSelectionForCurrentStep() ? 'disabled' : ''}>
Find Recommendations <i class="fas fa-search ml-2"></i>
</button>
`}
</div>
</div>
`;
return html;
}
// Handle option selection
function handleOptionSelection(step, value) {
if (step === 1) {
userSelections.application = value;
} else if (step === 2) {
userSelections.location = value;
} else if (step === 3) {
userSelections.material = value;
}
// Update the UI
loadStep(currentStep);
// Enable the next button if selection is made
if (hasSelectionForCurrentStep()) {
const nextButton = document.getElementById('next-btn') || document.getElementById('find-results-btn');
if (nextButton) {
nextButton.disabled = false;
nextButton.classList.remove('disabled:opacity-50', 'disabled:cursor-not-allowed');
}
}
// Add event listeners for navigation buttons
setTimeout(() => {
const backButton = document.getElementById('back-btn');
if (backButton) {
backButton.addEventListener('click', goBack);
}
const nextButton = document.getElementById('next-btn');
if (nextButton) {
nextButton.addEventListener('click', goNext);
}
const findResultsButton = document.getElementById('find-results-btn');
if (findResultsButton) {
findResultsButton.addEventListener('click', goNext);
}
}, 100);
}
// Check if user has made a selection for current step
function hasSelectionForCurrentStep() {
if (currentStep === 1) {
return userSelections.application !== null;
} else if (currentStep === 2) {
return userSelections.location !== null;
} else if (currentStep === 3) {
return userSelections.material !== null;
}
return false;
}
// Go to next step
function goNext() {
if (currentStep < 4) {
currentStep++;
loadStep(currentStep);
}
}
// Go back to previous step
function goBack() {
if (currentStep > 1) {
currentStep--;
loadStep(currentStep);
}
}
// Update progress dots
function updateProgressDots(current) {
const dots = document.querySelectorAll('.progress-dot');
dots.forEach((dot, index) => {
dot.classList.remove('active', 'completed');
if (index + 1 < current) {
dot.classList.add('completed');
} else if (index + 1 === current) {
dot.classList.add('active');
}
});
}
// Show results based on user selections
function showResults() {
const questionContainer = document.getElementById('question-container');
const resultsContainer = document.getElementById('results-container');
const summaryContainer = document.getElementById('selections-summary');
// Hide question container, show results and summary
questionContainer.classList.add('hidden');
resultsContainer.classList.remove('hidden');
summaryContainer.classList.remove('hidden');
// Update summary
updateSummary();
// Find matching products
const matches = findMatchingProducts();
// Render results
let html = `
<div class="bg-white rounded-2xl shadow-xl p-8 mb-8">
<div class="text-center mb-8">
<h2 class="text-3xl md:text-4xl font-bold text-gray-800 mb-3">Recommended Products</h2>
<p class="text-gray-600 text-lg max-w-3xl mx-auto">Based on your project requirements, here are the best products for your needs</p>
</div>
${matches.length === 0 ? `
<div class="bg-yellow-50 border-l-4 border-yellow-400 p-6 rounded-r-lg mb-8">
<div class="flex">
<div class="flex-shrink-0">
<i class="fas fa-exclamation-triangle text-yellow-400 text-2xl"></i>
</div>
<div class="ml-4">
<h3 class="text-lg font-bold text-yellow-800">No perfect matches found</h3>
<p class="text-yellow-700 mt-2">We couldn't find products that match all your criteria. Try adjusting your selections or contact our experts for personalized recommendations.</p>
<button class="mt-4 px-6 py-2 bg-yellow-100 text-yellow-800 rounded-lg font-medium hover:bg-yellow-200 transition-colors" onclick="restartApp()">
Try Different Options
</button>
</div>
</div>
</div>
` : ''}
<div class="space-y-8">
`;
// Sort matches by relevance (more matches = higher relevance)
matches.sort((a, b) => b.matchScore - a.matchScore);
// Show top 6 matches
const topMatches = matches.slice(0, 6);
topMatches.forEach((match, index) => {
const product = match.product;
const matchPercentage = Math.min(100, Math.round((match.matchScore / 4) * 100));
html += `
<div class="border border-gray-200 rounded-2xl overflow-hidden slide-in-right" style="animation-delay: ${index * 0.1}s">
${index === 0 ? `
<div class="bg-gradient-to-r from-green-500 to-green-600 text-white p-3 text-center font-bold">
<i class="fas fa-crown mr-2"></i> Best Match (${matchPercentage}% Match)
</div>
` : index === 1 ? `
<div class="bg-gradient-to-r from-blue-500 to-blue-600 text-white p-3 text-center font-bold">
<i class="fas fa-medal mr-2"></i> Excellent Match (${matchPercentage}% Match)
</div>
` : `
<div class="bg-gradient-to-r from-gray-600 to-gray-700 text-white p-3 text-center font-bold">
Good Match (${matchPercentage}% Match)
</div>
`}
<div class="p-6">
<div class="flex flex-col lg:flex-row lg:items-start gap-6">
<div class="lg:w-1/4">
<div class="bg-gradient-to-br from-gray-100 to-gray-200 rounded-xl p-6 h-full flex flex-col items-center justify-center">
<div class="text-5xl mb-4 text-gray-700">
${getProductIcon(product.category)}
</div>
<h3 class="text-xl font-bold text-gray-800 text-center mb-2">${product.type}</h3>
<div class="text-center">
<span class="tag ${getCategoryColor(product.category)}">${product.category}</span>
</div>
</div>
</div>
<div class="lg:w-3/4">
<div class="flex flex-col md:flex-row md:items-start md:justify-between mb-4">
<div>
<h3 class="text-2xl font-bold text-gray-800 mb-2">${product.name}</h3>
<p class="text-gray-700 text-lg mb-4">${product.description}</p>
</div>
<div class="flex-shrink-0 mt-4 md:mt-0">
<div class="bg-blue-50 p-4 rounded-xl border border-blue-200">
<div class="text-3xl font-bold text-blue-700 text-center">${matchPercentage}%</div>
<div class="text-sm text-blue-600 text-center font-medium">Match Score</div>
</div>
</div>
</div>
<div class="mb-6">
<h4 class="font-bold text-gray-800 mb-3 flex items-center">
<i class="fas fa-bullseye mr-2 text-blue-600"></i> Best For:
</h4>
<p class="text-gray-700">${product.bestFor}</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
<h4 class="font-bold text-gray-800 mb-3 flex items-center">
<i class="fas fa-check-circle mr-2 text-green-600"></i> Key Features:
</h4>
<div class="flex flex-wrap gap-2">
${product.features.slice(0, 4).map(feature => `
<span class="tag bg-green-100 text-green-800">${feature}</span>
`).join('')}
</div>
</div>
<div>
<h4 class="font-bold text-gray-800 mb-3 flex items-center">
<i class="fas fa-palette mr-2 text-purple-600"></i> Available Colors:
</h4>
<div class="flex flex-wrap gap-2">
${product.colors.slice(0, 3).map(color => `
<span class="tag bg-purple-100 text-purple-800">${color}</span>
`).join('')}
${product.colors.length > 3 ? `<span class="tag bg-gray-100 text-gray-800">+${product.colors.length - 3} more</span>` : ''}
</div>
</div>
</div>
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 pt-4 border-t border-gray-200">
<div class="text-sm text-gray-600">
<div class="flex items-center">
<i class="fas fa-arrows-alt-h mr-2"></i>
<span>Movement Capacity: <strong>${product.movement}</strong></span>
</div>
</div>
<div class="flex gap-3">
<button class="px-5 py-2 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors" onclick="showProductDetails(${product.id})">
<i class="fas fa-info-circle mr-2"></i> Details
</button>
<a href="https://thecaulkingstore.com/search?q=${encodeURIComponent(product.name)}" target="_blank" class="px-5 py-2 primary-gradient text-white rounded-lg font-medium hover:opacity-90 transition-all shadow-md">
<i class="fas fa-shopping-cart mr-2"></i> View Product
</a>
</div>
</div>
</div>
</div>
</div>
</div>
`;
});
html += `
</div>
<div class="mt-12 bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200 rounded-2xl p-8">
<div class="text-center mb-6">
<h3 class="text-2xl font-bold text-gray-800 mb-3">Need Professional Advice?</h3>
<p class="text-gray-700 max-w-2xl mx-auto">Our caulking experts are available to help you choose the right product for your specific project.</p>
</div>
<div class="flex flex-col md:flex-row justify-center items-center gap-6">
<a href="tel:416-860-8308" class="flex items-center px-6 py-3 bg-white text-blue-600 rounded-xl font-bold hover:bg-blue-50 transition-all border-2 border-blue-200 shadow-md">
<i class="fas fa-phone mr-3 text-xl"></i>
<div>
<div class="text-sm">Call Us</div>
<div class="text-lg">(416) 860-8308</div>
</div>
</a>
<a href="mailto:[email protected]" class="flex items-center px-6 py-3 bg-white text-blue-600 rounded-xl font-bold hover:bg-blue-50 transition-all border-2 border-blue-200 shadow-md">
<i class="fas fa-envelope mr-3 text-xl"></i>
<div>
<div class="text-sm">Email Us</div>
<div class="text-lg">[email protected]</div>
</div>
</a>
<a href="https://thecaulkingstore.com" target="_blank" class="flex items-center px-6 py-3 primary-gradient text-white rounded-xl font-bold hover:opacity-90 transition-all shadow-lg">
<i class="fas fa-external-link-alt mr-3 text-xl"></i>
<div>
<div class="text-sm">Visit</div>
<div class="text-lg">The Caulking Store</div>
</div>
</a>
</div>
</div>
</div>
`;
resultsContainer.innerHTML = html;
resultsContainer.classList.remove('fade-in');
void resultsContainer.offsetWidth; // Trigger reflow
resultsContainer.classList.add('fade-in');
}
// Find products that match user selections
function findMatchingProducts() {
const matches = [];
products.forEach(product => {
let matchScore = 0;
let matchReasons = [];
// Check application match
if (product.application.includes(userSelections.application)) {
matchScore += 1;
matchReasons.push(`✓ Suitable for ${userSelections.application} applications`);
}
// Check location match (simplified - checking if any location matches)
let locationMatch = false;
for (const loc of product.location) {
if (loc.toLowerCase().includes(userSelections.location)) {
locationMatch = true;
break;
}
}
if (locationMatch) {
matchScore += 1;
matchReasons.push(`✓ Can be used for ${userSelections.location}`);
}
// Check material match
if (product.material.includes(userSelections.material)) {
matchScore += 1;
matchReasons.push(`✓ Compatible with ${userSelections.material}`);
}
// Add to matches if at least one criteria matches
if (matchScore > 0) {
matches.push({
product,
matchScore,
matchReasons
});
}
});
return matches;
}
// Update the summary with user selections
function updateSummary() {
const applicationMap = {
'exterior': 'Exterior',
'interior': 'Interior',
'firestop': 'Firestop',
'wet-area': 'Wet Area'
};
const locationMap = {
'windows-doors': 'Windows & Doors',
'siding-trim': 'Siding & Trim',
'expansion-joints': 'Expansion Joints',
'roof-flashing': 'Roof & Flashing',
'concrete-joints': 'Concrete Joints',
'curtain-wall': 'Curtain Wall',
'trim-molding': 'Trim & Molding',
'drywall-cracks': 'Drywall Cracks',
'baseboards': 'Baseboards',
'ceilings': 'Ceilings',
'corners': 'Corners',
'pipe-penetrations': 'Pipe Penetrations',
'electrical-penetrations': 'Electrical Penetrations',
'fire-rated-joints': 'Fire-Rated Joints',
'duct-penetrations': 'Duct Penetrations',
'bathtub-shower': 'Bathtub & Shower',
'kitchen-sink': 'Kitchen Sink',
'backsplash': 'Backsplash',
'vanity-counter': 'Vanity & Counter'
};
const materialMap = {
'concrete': 'Concrete / Masonry',
'wood': 'Wood',
'metal': 'Metal / Aluminum',
'glass': 'Glass',
'drywall': 'Drywall / Plaster',
'tile': 'Tile / Ceramic',
'vinyl': 'Vinyl / PVC',
'plastic': 'Plastic / Fiberglass'
};
document.getElementById('summary-application').textContent =
applicationMap[userSelections.application] || '-';
document.getElementById('summary-location').textContent =
locationMap[userSelections.location] || '-';
document.getElementById('summary-material').textContent =
materialMap[userSelections.material] || '-';
document.getElementById('summary-movement').textContent = 'All types';
}
// Get product icon based on category
function getProductIcon(category) {
const icons = {
'polyurethane': 'fas fa-expand-alt',
'silicone': 'fas fa-droplet',
'acrylic': 'fas fa-paint-roller',
'firestop': 'fas fa-fire',
'specialty': 'fas fa-star',
'hybrid': 'fas fa-blend'
};
return icons[category] || 'fas fa-box';
}
// Get category color class
function getCategoryColor(category) {
const colors = {
'polyurethane': 'bg-blue-100 text-blue-800',
'silicone': 'bg-purple-100 text-purple-800',
'acrylic': 'bg-green-100 text-green-800',
'firestop': 'bg-red-100 text-red-800',
'specialty': 'bg-yellow-100 text-yellow-800',
'hybrid': 'bg-indigo-100 text-indigo-800'
};
return colors[category] || 'bg-gray-100 text-gray-800';
}
// Show product details modal
function showProductDetails(productId) {
const product = products.find(p => p.id === productId);
if (!product) return;
// Create modal
const modalHtml = `
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
<div class="bg-white rounded-2xl max-w-4xl w-full max-h-[90vh] overflow-y-auto">
<div class="p-6 border-b border-gray-200 flex justify-between items-center">
<h3 class="text-2xl font-bold text-gray-800">${product.name}</h3>
<button onclick="closeModal()" class="text-gray-500 hover:text-gray-700 text-2xl">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-6">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div>
<div class="mb-6">
<h4 class="text-lg font-bold text-gray-800 mb-2">Product Description</h4>
<p class="text-gray-700">${product.description}</p>
</div>
<div class="mb-6">
<h4 class="text-lg font-bold text-gray-800 mb-2">Best For</h4>
<p class="text-gray-700">${product.bestFor}</p>
</div>
<div class="mb-6">
<h4 class="text-lg font-bold text-gray-800 mb-3">Key Features</h4>
<div class="space-y-2">
${product.features.map(feature => `
<div class="flex items-start">
<i class="fas fa-check text-green-500 mt-1 mr-3"></i>
<span class="text-gray-700">${feature}</span>
</div>
`).join('')}
</div>
</div>
</div>
<div>
<div class="mb-6">
<h4 class="text-lg font-bold text-gray-800 mb-3">Technical Specifications</h4>
<div class="space-y-4">
<div class="flex justify-between border-b border-gray-100 pb-2">
<span class="text-gray-600">Type</span>
<span class="font-medium">${product.type}</span>
</div>
<div class="flex justify-between border-b border-gray-100 pb-2">
<span class="text-gray-600">Movement Capacity</span>
<span class="font-medium">${product.movement}</span>
</div>
<div class="flex justify-between border-b border-gray-100 pb-2">
<span class="text-gray-600">Applications</span>
<span class="font-medium">${product.application.join(', ')}</span>
</div>
<div class="flex justify-between border-b border-gray-100 pb-2">
<span class="text-gray-600">Compatible Materials</span>
<span class="font-medium">${product.material.join(', ')}</span>
</div>
<div class="flex justify-between border-b border-gray-100 pb-2">
<span class="text-gray-600">Available Colors</span>
<span class="font-medium">${product.colors.join(', ')}</span>
</div>
</div>
</div>
<div class="bg-blue-50 rounded-xl p-5 border border-blue-200">
<h4 class="text-lg font-bold text-gray-800 mb-3">Purchase Options</h4>
<p class="text-gray-700 mb-4">This product is available at The Caulking Store in various sizes and formulations.</p>
<div class="flex flex-col sm:flex-row gap-3">
<a href="https://thecaulkingstore.com/search?q=${encodeURIComponent(product.name)}" target="_blank" class="flex-1 px-5 py-3 primary-gradient text-white rounded-lg font-bold text-center hover:opacity-90 transition-all">
<i class="fas fa-external-link-alt mr-2"></i> View on Website
</a>
<a href="tel:416-860-8308" class="flex-1 px-5 py-3 bg-white text-blue-600 rounded-lg font-bold text-center hover:bg-blue-50 transition-all border-2 border-blue-200">
<i class="fas fa-phone mr-2"></i> Call to Order
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
const modalContainer = document.createElement('div');
modalContainer.innerHTML = modalHtml;
document.body.appendChild(modalContainer);
}
// Close modal
function closeModal() {
const modal = document.querySelector('.fixed.inset-0.bg-black');
if (modal) {
modal.remove();
}
}
// Restart the app
function restartApp() {
// Reset selections
userSelections.application = null;
userSelections.location = null;
userSelections.material = null;
userSelections.movement = null;
// Reset to step 1
currentStep = 1;
// Reload the app
loadStep(currentStep);
}
// Make functions available globally for HTML event handlers
window.showProductDetails = showProductDetails;
window.closeModal = closeModal;
window.restartApp = restartApp;
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment