Instant Oil Change Quote
Use your VIN or enter your vehicle details to get a recommended oil type & estimated price.
We quote standard gasoline U.S. passenger vehicles. Lexus & Acura are supported (including hybrids).
No EVs, diesels, most luxury/high-end brands, or fleet/commercial vehicles.
Option 1: VIN Search
Option 2: No VIN?
Enter your vehicle info (gasoline passenger vehicles only).
EV, diesel, most luxury/high-end brands, and fleet/commercial vehicles are not quoted online.
<script>
(function() {
const BOOKING_URL = "/schedule";
// ==== YOUR MARGINS (unchanged) ====
const COST_PER_QUART = {
conventional: 5.50,
syntheticBlend: 6.40,
fullSynthetic: 7.25,
highMileage: 8.50
};
const OIL_FILTER_COST_DEFAULT = 15;
const CABIN_FILTER_COST_DEFAULT = 15;
const ENGINE_FILTER_COST_DEFAULT = 15;
const WIPER_BLADE_COST_DEFAULT = 40;
const OIL_CHANGE_PROFIT = 40;
const ADD_ON_PROFIT = 10;
const TIRE_ROTATION_PRICE = 10;
const HIGH_MILEAGE_THRESHOLD = 75000;
const OIL_LABELS = {
conventional: "Conventional",
syntheticBlend: "Synthetic Blend",
fullSynthetic: "Full Synthetic",
highMileage: "High Mileage Full Synthetic"
};
// Exclusions (unchanged)
const EXCLUDED_MAKES = [
"TESLA","LUCID","RIVIAN","POLESTAR","VOLVO",
"MERCEDES-BENZ","MERCEDES","BMW","AUDI","PORSCHE","JAGUAR",
"LAND ROVER","RANGE ROVER","MASERATI","BENTLEY","ROLLS-ROYCE",
"FERRARI","LAMBORGHINI","ASTON MARTIN","MCLAREN","MAYBACH",
"ALFA ROMEO","GENESIS","LEXUS","INFINITI","ACURA","CADILLAC","LINCOLN"
];
function isExcluded(make, fuelType) {
const m = (make || "").toUpperCase().trim();
const f = (fuelType || "").toLowerCase();
if (EXCLUDED_MAKES.includes(m)) return true;
if (f.includes("electric") || f.includes("ev") || f.includes("hybrid") || f.includes("diesel")) return true;
return false;
}
function parseMileage(raw) {
if (!raw) return null;
const miles = parseInt(raw.replace(/,/g, "").trim(), 10);
return isNaN(miles) ? null : miles;
}
// === Local safety net: known overrides when you want to guarantee accuracy ===
// Keyed by VIN OR by a coarse YMME signature.
const CAPACITY_OVERRIDES = {
// VIN-specific override (your example)
"VIN:1HGCM82633A004352": { quarts: 4.6, note: "Local override: 2003 Accord 3.0 V6" },
// Example signature fallback: "YEAR|MAKE|MODEL|DISPLACEMENT_L"
"2003|HONDA|ACCORD|3.0": { quarts: 4.6, note: "Local override: 2003 Accord 3.0 V6" }
};
function signatureKey({year, make, model, displacementL}) {
const y = String(year||"").trim();
const mk = String(make||"").toUpperCase().trim();
const md = String(model||"").toUpperCase().trim();
const dl = displacementL ? String(displacementL).trim() : "";
return `${y}|${mk}|${md}|${dl}`;
}
// === CALL YOUR SERVERLESS PROXY to fetch accurate oil capacity by VIN/YMM ===
// Implement a backend at /api/oil-capacity that accepts VIN or (year,make,model,engine)
// and returns { quarts: Number, viscosity: "0W-20" } when available.
async function fetchOilCapacity({ vin, year, make, model, engineCyl, displacementL }) {
// 1) Check strict VIN override
if (vin && CAPACITY_OVERRIDES["VIN:"+vin]) return CAPACITY_OVERRIDES["VIN:"+vin];
// 2) Try serverless proxy (add your endpoint URL below)
const url = new URL("/api/oil-capacity", window.location.origin);
if (vin) url.searchParams.set("vin", vin);
if (year) url.searchParams.set("year", year);
if (make) url.searchParams.set("make", make);
if (model) url.searchParams.set("model", model);
if (engineCyl) url.searchParams.set("engineCyl", engineCyl);
if (displacementL) url.searchParams.set("displacementL", displacementL);
try {
const res = await fetch(url.toString(), { method: "GET" });
if (res.ok) {
const data = await res.json();
if (data && typeof data.quarts === "number" && data.quarts > 0) {
return { quarts: data.quarts, note: "API capacity", viscosity: data.viscosity || null };
}
}
} catch(e) {
// swallow and fallback
}
// 3) Check coarse signature override
const sig = signatureKey({year, make, model, displacementL});
if (CAPACITY_OVERRIDES[sig]) return CAPACITY_OVERRIDES[sig];
// 4) Last resort: estimate by cylinder count (your current heuristic)
let est;
const cyl = parseInt(engineCyl || 4, 10);
if (cyl <= 4) est = 5;
else if (cyl <= 6) est = 6; // note: many V6s are ~4.5–6.0, this is only a fallback
else if (cyl <= 8) est = 7;
else est = 6;
return { quarts: est, note: "Estimated capacity (fallback)" };
}
function determineRecommendedOilType(fuelType, year, mileage) {
const yr = parseInt(year, 10) || 2015;
const fuel = (fuelType || "gasoline").toLowerCase();
let oilTypeKey = "syntheticBlend";
if (yr >= 2018) oilTypeKey = "fullSynthetic";
if (mileage && mileage >= HIGH_MILEAGE_THRESHOLD) oilTypeKey = "highMileage";
if (fuel.includes("diesel")) oilTypeKey = "fullSynthetic";
return oilTypeKey;
}
function calculateOilChangePrice(quarts, oilTypeKey, oilFilterCost) {
const costPerQt = COST_PER_QUART[oilTypeKey] || COST_PER_QUART.syntheticBlend;
const oilCost = quarts * costPerQt;
const partsCost = oilCost + oilFilterCost;
const customerPrice = partsCost + OIL_CHANGE_PROFIT;
return { partsCost, customerPrice };
}
function getAddOns(cabinCost, engineCost, wiperCost) {
return [
{ id: "tireRotation", label: "Tire Rotation", displayPrice: TIRE_ROTATION_PRICE },
{ id: "cabinFilter", label: "Cabin Air Filter Replacement", displayPrice: cabinCost + ADD_ON_PROFIT },
{ id: "engineFilter", label: "Engine Air Filter Replacement", displayPrice: engineCost + ADD_ON_PROFIT },
{ id: "wipers", label: "Windshield Wiper Blade Replacement (pair)", displayPrice: wiperCost + ADD_ON_PROFIT }
];
}
// (optional) Hidden "Instant Quote Log" submission (unchanged from your last version)
function findInstantQuoteLogForm() {
const contexts = document.querySelectorAll(".sqs-form-block-context");
for (const script of contexts) {
try {
const cfg = JSON.parse(script.textContent.trim());
if (cfg.formName === "Instant Quote Log") {
const block = script.closest(".sqs-block-form, .sqs-block-website-component");
if (!block) continue;
const form = block.querySelector("form");
if (!form) continue;
let quoteFieldId = null;
if (Array.isArray(cfg.formFields)) {
const fd = cfg.formFields.find(f => f.title === "Quote Details");
if (fd && fd.id) quoteFieldId = fd.id + "-field";
}
return { form, quoteFieldId };
}
} catch(e){}
}
return null;
}
function fillAndSubmitInstantQuoteLog(quoteSummary) {
const info = findInstantQuoteLogForm();
if (!info) return false;
let quoteField = info.quoteFieldId ? document.getElementById(info.quoteFieldId) : null;
if (!quoteField) {
const items = info.form.querySelectorAll(".form-item.field.textarea");
for (const item of items) {
const span = item.querySelector("label span");
if (span && span.textContent.trim() === "Quote Details") {
const ta = item.querySelector("textarea");
if (ta) { quoteField = ta; break; }
}
}
}
if (!quoteField) {
const tas = info.form.querySelectorAll("textarea");
if (tas.length === 1) quoteField = tas[0];
}
if (!quoteField) return false;
quoteField.value = quoteSummary;
quoteField.dispatchEvent(new Event("input", { bubbles: true }));
quoteField.dispatchEvent(new Event("change", { bubbles: true }));
const submitBtn = info.form.querySelector('button[type="submit"], .form-submit-button');
if (submitBtn) submitBtn.click();
else info.form.dispatchEvent(new Event("submit", { bubbles: true, cancelable: true }));
return true;
}
async function renderQuote({ sourceLabel, year, make, model, trim, engineCyl, fuelType, mileage, vin, displacementL }) {
const container = document.getElementById("quoteResult");
container.innerHTML = "";
if (isExcluded(make, fuelType)) {
container.innerHTML = '<p style="color:red;">We do not provide instant quotes for EV, hybrid, diesel, or luxury/high-end vehicles.</p>';
return;
}
if (!mileage) {
container.innerHTML = '<p style="color:red;">Mileage is required to generate an accurate quote.</p>';
return;
}
// === NEW: get precise quarts ===
const cap = await fetchOilCapacity({ vin, year, make, model, engineCyl, displacementL });
const quarts = cap.quarts;
const recommendedOilTypeKey = determineRecommendedOilType(fuelType, year, mileage);
let selectedOilTypeKey = recommendedOilTypeKey;
const oilFilterCost = OIL_FILTER_COST_DEFAULT;
const cabinFilterCost = CABIN_FILTER_COST_DEFAULT;
const engineFilterCost = ENGINE_FILTER_COST_DEFAULT;
const wiperCost = WIPER_BLADE_COST_DEFAULT;
let quote = calculateOilChangePrice(quarts, selectedOilTypeKey, oilFilterCost);
const addOns = getAddOns(cabinFilterCost, engineFilterCost, wiperCost);
const vehicleLabel = [year, make, model, trim].filter(Boolean).join(" ");
const mileageLabel = mileage.toLocaleString() + " miles";
const vehicleSummaryHtml = `
<h3>Estimated Quote</h3>
<p style="font-size:0.9rem;color:#555;">Source: ${sourceLabel}${cap.note ? " · " + cap.note : ""}</p>
<p><strong>Vehicle:</strong> ${vehicleLabel || "Not provided"}</p>
<p><strong>Engine:</strong> ${engineCyl ? engineCyl + "-cylinder" : "Not specified"}${displacementL ? " · " + displacementL + "L" : ""}</p>
<p><strong>Mileage:</strong> ${mileageLabel}</p>
<p><strong>Oil Capacity:</strong> ${quarts.toFixed(1)} qt</p>
${vin ? `<p><strong>VIN:</strong> ${vin}</p>` : "" }
`;
const highMileageNote = (selectedOilTypeKey === "highMileage")
? `<p style="color:#2563eb;font-size:0.85rem;">Based on your mileage (≥ ${HIGH_MILEAGE_THRESHOLD.toLocaleString()}), we recommend a high mileage full synthetic.</p>`
: "";
const oilOptionsHtml = `
<h4>Select Oil Type</h4>
<p style="font-size:0.85rem;">We recommend <strong>${OIL_LABELS[recommendedOilTypeKey]}</strong>, but you can choose another option.</p>
<div>
${["conventional","syntheticBlend","fullSynthetic","highMileage"].map(key => `
<label style="display:block;margin:2px 0;font-size:0.9rem;">
<input type="radio" name="oilTypeOption" value="${key}" ${key === selectedOilTypeKey ? "checked" : ""}>
${OIL_LABELS[key]}${key === recommendedOilTypeKey ? " (Recommended)" : ""}
</label>
`).join("")}
</div>
`;
const breakdownHtml = `
<h4>Oil Change Price Breakdown</h4>
<ul id="breakdownList" style="list-style:none;padding-left:0;font-size:0.9rem;">
<li>• Oil capacity used: ${quarts.toFixed(1)} qt</li>
<li>• Selected oil type: ${OIL_LABELS[selectedOilTypeKey]}</li>
<li>• Estimated oil change total (incl. parts, labor & mobile service): $${quote.customerPrice.toFixed(2)}</li>
</ul>
`;
const addOnsHtml = `
<h4>Optional Add-On Services</h4>
${addOns.map(a => `
<label style="display:block;margin:4px 0;font-size:0.9rem;">
<input type="checkbox" class="addon-checkbox" data-label="${a.label}" data-price="${a.displayPrice}">
${a.label} (+$${a.displayPrice.toFixed(2)})
</label>
`).join("")}
`;
container.innerHTML = `
${vehicleSummaryHtml}
${highMileageNote}
${oilOptionsHtml}
${breakdownHtml}
${addOnsHtml}
<h2 style="margin-top:15px;font-size:1.4rem;">
Your Estimated Total: $<span id="finalTotal">${quote.customerPrice.toFixed(2)}</span>
</h2>
<p style="font-size:0.75rem;color:#777;">*Estimate. Final pricing confirmed on-site.</p>
<button id="bookServiceBtn"
style="padding:10px 20px;border:none;border-radius:4px;cursor:pointer;background-color:#111827;color:#fff;font-size:1rem;">
Continue to Schedule & Book
</button>
`;
const radios = container.querySelectorAll('input[name="oilTypeOption"]');
const addOnCheckboxes = container.querySelectorAll(".addon-checkbox");
const totalEl = document.getElementById("finalTotal");
const breakdownList = document.getElementById("breakdownList");
const bookBtn = document.getElementById("bookServiceBtn");
let currentBaseTotal = quote.customerPrice;
function getAddOnsTotal() {
let t = 0;
addOnCheckboxes.forEach(cb => { if (cb.checked) t += parseFloat(cb.dataset.price) || 0; });
return t;
}
function getSelectedAddOnsLabels() {
const labels = [];
addOnCheckboxes.forEach(cb => { if (cb.checked) labels.push(cb.dataset.label); });
return labels;
}
function updateTotals() {
const grand = currentBaseTotal + getAddOnsTotal();
totalEl.textContent = grand.toFixed(2);
breakdownList.innerHTML = `
<li>• Oil capacity used: ${quarts.toFixed(1)} qt</li>
<li>• Selected oil type: ${OIL_LABELS[selectedOilTypeKey]}</li>
<li>• Estimated oil change total (incl. parts, labor & mobile service): $${currentBaseTotal.toFixed(2)}</li>
`;
}
radios.forEach(r => {
r.addEventListener("change", () => {
const checked = container.querySelector('input[name="oilTypeOption"]:checked');
if (!checked) return;
selectedOilTypeKey = checked.value;
const newQuote = calculateOilChangePrice(quarts, selectedOilTypeKey, OIL_FILTER_COST_DEFAULT);
currentBaseTotal = newQuote.customerPrice;
updateTotals();
});
});
addOnCheckboxes.forEach(cb => cb.addEventListener("change", updateTotals));
updateTotals();
// Send log + redirect with URL params
bookBtn.addEventListener("click", () => {
const finalTotal = totalEl.textContent;
const oilLabel = OIL_LABELS[selectedOilTypeKey];
const addonsSelected = getSelectedAddOnsLabels();
const quoteLines = [
"Instant Quote Details:",
vehicleLabel ? "Vehicle: " + vehicleLabel : "",
engineCyl ? "Engine: " + engineCyl + "-cylinder" : "",
displacementL ? "Displacement: " + displacementL + "L" : "",
mileageLabel ? "Mileage: " + mileageLabel : "",
"Oil Capacity: " + quarts.toFixed(1) + " qt",
oilLabel ? "Oil Type: " + oilLabel : "",
addonsSelected.length ? "Selected Add-Ons: " + addonsSelected.join(", ") : "",
finalTotal ? "Estimated Total: $" + finalTotal : "",
vin ? "VIN: " + vin : "",
sourceLabel ? "Quote Source: " + sourceLabel : ""
].filter(Boolean);
const quoteSummary = quoteLines.join("\n");
// (optional) fire hidden logging form
fillAndSubmitInstantQuoteLog(quoteSummary);
const params = new URLSearchParams({
vehicle: vehicleLabel || "",
engine: engineCyl ? engineCyl + "-cylinder" : "",
displacementL: displacementL || "",
mileage: mileageLabel || "",
oilCapacityQt: quarts.toFixed(1),
oil: oilLabel || "",
addons: addonsSelected.join(", "),
total: finalTotal || "",
source: sourceLabel || ""
});
if (vin) params.append("vin", vin);
window.location.href = BOOKING_URL + "?" + params.toString();
});
}
// VIN handler
const vinInput = document.getElementById("vinInput");
const vinMileageInput = document.getElementById("vinMileageInput");
const vinButton = document.getElementById("vinButton");
const vinMessage = document.getElementById("vinMessage");
vinButton.addEventListener("click", () => {
const vin = (vinInput.value || "").trim().toUpperCase();
const mileage = parseMileage(vinMileageInput.value);
const result = document.getElementById("quoteResult");
result.innerHTML = "";
vinMessage.style.color = "#333";
vinMessage.textContent = "";
if (vin.length !== 17) {
vinMessage.style.color = "red";
vinMessage.textContent = "Please enter a valid 17-character VIN.";
return;
}
if (!mileage) {
vinMessage.style.color = "red";
vinMessage.textContent = "Please enter current mileage.";
return;
}
vinMessage.textContent = "Decoding VIN and calculating your quote...";
fetch("https://vpic.nhtsa.dot.gov/api/vehicles/DecodeVinValuesExtended/" + vin + "?format=json")
.then(res => res.json())
.then(async (data) => {
const info = data && data.Results && data.Results[0];
if (!info || (!info.Make && !info.Model)) {
vinMessage.style.color = "red";
vinMessage.textContent = "We couldn't decode that VIN. Please double-check and try again.";
return;
}
vinMessage.textContent = "";
const year = info.ModelYear || "";
const make = info.Make || "";
const model = info.Model || "";
const engineCyl = info.EngineCylinders || "";
const fuelType = info.FuelTypePrimary || "Gasoline";
const displacementL = info.DisplacementL || info.DisplacementCI || ""; // VPIC may provide L
renderQuote({
sourceLabel: "VIN Decoded",
year, make, model, trim: "",
engineCyl, fuelType, mileage, vin, displacementL
});
})
.catch(() => {
vinMessage.style.color = "red";
vinMessage.textContent = "Error connecting to VIN service. Please try again.";
});
});
// Manual handler
const manYear = document.getElementById("manYear");
const manMake = document.getElementById("manMake");
const manModel = document.getElementById("manModel");
const manTrim = document.getElementById("manTrim");
const manEngine = document.getElementById("manEngine");
const manMileage = document.getElementById("manMileage");
const manButton = document.getElementById("manButton");
const manMessage = document.getElementById("manMessage");
manButton.addEventListener("click", () => {
const result = document.getElementById("quoteResult");
result.innerHTML = "";
manMessage.style.color = "#333";
manMessage.textContent = "";
const year = manYear.value.trim();
const make = manMake.value.trim();
const model = manModel.value.trim();
const trim = manTrim.value.trim();
const engineRaw = manEngine.value.trim();
const mileage = parseMileage(manMileage.value);
if (!year || !make || !model || !engineRaw) {
manMessage.style.color = "red";
manMessage.textContent = "Please enter year, make, model, and engine size/cylinders.";
return;
}
if (!mileage) {
manMessage.style.color = "red";
manMessage.textContent = "Please enter current mileage.";
return;
}
let engineCyl = parseInt(engineRaw, 10);
if (isNaN(engineCyl)) engineCyl = 4;
if (isExcluded(make, "gasoline")) {
manMessage.style.color = "red";
manMessage.textContent = "We do not provide instant quotes for this vehicle. Please contact us directly.";
return;
}
renderQuote({
sourceLabel: "Manual Vehicle Entry",
year, make, model, trim,
engineCyl, fuelType: "Gasoline",
mileage, vin: "", displacementL: "" // you can add a field to capture liters if desired
});
});
})();
</script>
