-->
<!-- Visualization & Content Choices:
- Budget Allocation (Donut Chart): Shows the ratio of spending between Cleaning, Repairs, and Projects. Goal: Inform/Compare.
- Service Intensity (Bar Chart): Visualizes estimated hours/effort per category. Goal: Inform.
- Dynamic Text: The proposal letter injects the client name and specific services selected. Goal: Personalization.
- Interaction: Sliders and Toggles. Immediate feedback loop.
- Library: Chart.js for reliable, responsive canvas charts.
-->
<!-- CONFIRMATION: NO SVG graphics used. NO Mermaid JS used. -->
</head>
<body class="bg-slate-50 min-h-screen flex flex-col">
<!-- Header -->
<header class="bg-white border-b border-slate-200 sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-20 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-sky-600 rounded flex items-center justify-center text-white font-bold text-xl">S</div>
<div>
<h1 class="text-xl font-bold text-slate-800 tracking-tight">Skyline Facility Group</h1>
<p class="text-xs text-slate-500 uppercase tracking-wider">Commercial Services</p>
</div>
</div>
<div class="hidden md:block text-right">
<p class="text-sm font-semibold text-slate-700">David Caneccio</p>
<p class="text-xs text-slate-500">Owner & Operator</p>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-grow max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 w-full">
<div class="mb-8">
<h2 class="text-3xl font-bold text-slate-800 mb-2">Custom Service Proposal</h2>
<p class="text-slate-600 max-w-3xl">
Configure your facility needs below to generate a preliminary service agreement and cost estimate.
Skyline Facility Group specializes in comprehensive office maintenance, form general cleaning to specialized marble and metal polishing.
</p>
</div>
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
<!-- Left Column: Input Form -->
<div class="lg:col-span-5 space-y-6">
<!-- Client Details -->
<div class="section-card p-6">
<h3 class="text-lg font-semibold text-slate-800 mb-4 border-b pb-2">1. Client Information</h3>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Company Name</label>
<input type="text" id="companyName" placeholder="e.g. Acme Corp" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-sky-500 transition-colors">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Facility Size (Sq. Ft.)</label>
<input type="number" id="sqFt" value="5000" min="1000" step="500" class="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-sky-500 transition-colors">
</div>
</div>
</div>
<!-- Janitorial Services -->
<div class="section-card p-6">
<div class="flex items-center justify-between mb-4 border-b pb-2">
<h3 class="text-lg font-semibold text-slate-800">2. Janitorial Services</h3>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="janitorialActive" class="sr-only peer" checked>
<div class="w-11 h-6 bg-slate-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-sky-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-sky-600"></div>
</label>
</div>
<div id="janitorialControls" class="space-y-4 transition-all duration-300">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Cleaning Frequency (Days/Week): <span id="freqVal" class="text-sky-600 font-bold">3</span></label>
<input type="range" id="frequency" min="1" max="7" value="3" class="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer">
</div>
<div class="flex items-center gap-2">
<input type="checkbox" id="supplies" class="w-4 h-4 text-sky-600 border-gray-300 rounded focus:ring-sky-500">
<label for="supplies" class="text-sm text-slate-700">Include Consumable Supplies (Paper/Soap)</label>
</div>
</div>
</div>
<!-- Maintenance & Repair -->
<div class="section-card p-6">
<div class="flex items-center justify-between mb-4 border-b pb-2">
<h3 class="text-lg font-semibold text-slate-800">3. General Repairs & Paint</h3>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="repairActive" class="sr-only peer">
<div class="w-11 h-6 bg-slate-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-sky-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-sky-600"></div>
</label>
</div>
<div id="repairControls" class="space-y-4 opacity-50 pointer-events-none transition-all duration-300">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Maintenance Hours / Month: <span id="hoursVal" class="text-sky-600 font-bold">5</span></label>
<input type="range" id="repairHours" min="0" max="40" value="5" class="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer">
</div>
<div class="grid grid-cols-2 gap-2">
<div class="flex items-center gap-2">
<input type="checkbox" id="painting" class="w-4 h-4 text-sky-600 border-gray-300 rounded focus:ring-sky-500">
<label for="painting" class="text-sm text-slate-700">Painting Touch-ups</label>
</div>
<div class="flex items-center gap-2">
<input type="checkbox" id="fixtures" class="w-4 h-4 text-sky-600 border-gray-300 rounded focus:ring-sky-500">
<label for="fixtures" class="text-sm text-slate-700">Fixture Install</label>
</div>
</div>
</div>
</div>
<!-- Specialized Services -->
<div class="section-card p-6">
<div class="flex items-center justify-between mb-4 border-b pb-2">
<h3 class="text-lg font-semibold text-slate-800">4. Specialized Polishing</h3>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox" id="specialActive" class="sr-only peer">
<div class="w-11 h-6 bg-slate-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-sky-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-sky-600"></div>
</label>
</div>
<div id="specialControls" class="space-y-4 opacity-50 pointer-events-none transition-all duration-300">
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Marble Floor Area (Sq Ft): <span id="marbleVal" class="text-sky-600 font-bold">0</span></label>
<input type="range" id="marbleSqFt" min="0" max="2000" step="100" value="0" class="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer">
</div>
<div>
<label class="block text-sm font-medium text-slate-700 mb-1">Metal Fixtures Polishing (Hrs): <span id="metalVal" class="text-sky-600 font-bold">0</span></label>
<input type="range" id="metalHours" min="0" max="20" step="1" value="0" class="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer">
</div>
</div>
</div>
</div>
<!-- Right Column: Live Proposal Preview -->
<div class="lg:col-span-7 space-y-6">
<!-- Financial Summary -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Total Cost -->
<div class="section-card p-6 flex flex-col justify-center items-center text-center bg-sky-50 border-sky-100 border">
<h4 class="text-sm font-medium text-sky-800 uppercase tracking-wide mb-1">Estimated Monthly Investment</h4>
<div class="text-4xl font-bold text-slate-900 mb-2" id="totalCostDisplay">$0.00</div>
<p class="text-xs text-slate-500">*Estimate based on standard rates. Final quote subject to site walkthrough.</p>
</div>
<!-- Breakdown Chart -->
<div class="section-card p-4">
<h4 class="text-sm font-medium text-slate-700 mb-2 text-center">Cost Allocation</h4>
<div class="chart-container" style="height: 180px;">
<canvas id="costChart"></canvas>
</div>
</div>
</div>
<!-- Proposal Document -->
<div class="section-card p-8 bg-white border border-slate-200 relative overflow-hidden">
<!-- Decorative background element -->
<div class="absolute top-0 right-0 w-32 h-32 bg-sky-50 rounded-bl-full -z-0 opacity-50"></div>
<div class="relative z-10">
<div class="flex justify-between items-start mb-8">
<div>
<h2 class="text-2xl font-bold text-slate-900">Service Proposal</h2>
<p class="text-sm text-slate-500" id="proposalDate">January 1, 2026</p>
</div>
<div class="text-right">
<p class="text-sm font-bold text-slate-800">PREPARED FOR:</p>
<p class="text-lg text-sky-700 font-medium" id="clientNameDisplay">[Client Name]</p>
</div>
</div>
<div class="prose prose-slate max-w-none">
<p class="mb-4">
Dear <span id="clientSalutation">Valued Partner</span>,
</p>
<p class="mb-4">
Thank you for considering <strong>Skyline Facility Group</strong> for your property management needs.
Based on the specifications provided for your facility, we have designed a comprehensive service package
tailored to maintain the highest standards of cleanliness and operational efficiency.
</p>
<h4 class="text-lg font-semibold text-slate-800 mt-6 mb-2">Scope of Work Summary:</h4>
<ul class="list-disc pl-5 space-y-1 mb-6 text-slate-700" id="scopeList">
<!-- Dynamic List Items -->
<li>Configure services on the left to see details here.</li>
</ul>
<h4 class="text-lg font-semibold text-slate-800 mt-6 mb-2">Service Intensity Analysis:</h4>
<p class="text-sm text-slate-600 mb-4">The chart below illustrates the projected labor distribution across different service categories.</p>
<div class="chart-container mb-6 border rounded-lg p-4 bg-slate-50">
<canvas id="serviceChart"></canvas>
</div>
<p class="mb-8">
We are committed to providing "General Repairs," "Painting," "Fixture Installations," and specialized "Marble & Metal Polishing"
with the utmost professionalism. I personally oversee all major accounts to ensure quality.
</p>
<div class="border-t border-slate-200 pt-6 mt-8 flex flex-col sm:flex-row justify-between items-center gap-4">
<div class="text-center sm:text-left">
<p class="font-signature text-2xl text-sky-800 mb-1" style="font-family: 'Times New Roman', serif; font-style: italic;">David Caneccio</p>
<p class="text-sm font-bold text-slate-900">David Caneccio</p>
<p class="text-xs text-slate-500">Owner, Skyline Facility Group</p>
</div>
<button onclick="alert('Proposal Sent to Skyline Facility Group! We will contact you shortly.')" class="bg-sky-600 hover:bg-sky-700 text-white font-semibold py-2 px-6 rounded-lg shadow transition-colors">
Request Formal Quote
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Footer -->
<footer class="bg-slate-900 text-slate-300 py-8 mt-12">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col md:flex-row justify-between items-center gap-4">
<div class="text-center md:text-left">
<h3 class="font-bold text-white text-lg">Skyline Facility Group</h3>
<p class="text-sm">Office Buildings • General Repairs • Painting • Fixtures • Polishing</p>
</div>
<div class="text-center md:text-right text-sm">
<p>© 2026 Skyline Facility Group. All rights reserved.</p>
<p>Owner: David Caneccio</p>
</div>
</div>
</footer>
<script>
// --- State Management ---
const state = {
companyName: '',
sqFt: 5000,
janitorial: { active: true, frequency: 3, supplies: false },
repairs: { active: false, hours: 5, painting: false, fixtures: false },
special: { active: false, marbleSqFt: 0, metalHours: 0 }
};
// --- Pricing Logic (Dummy Data for Demo) ---
const rates = {
cleaningPerSqFt: 0.05, // Monthly rate per sq ft per cleaning day
suppliesFlat: 150,
repairHourly: 85,
paintFlat: 200, // Monthly retainer for touchups
fixtureFlat: 150, // Per install retainer
marblePerSqFt: 1.50, // One-time amortized or monthly maintenance
metalHourly: 95
};
// --- DOM Elements ---
const inputs = {
name: document.getElementById('companyName'),
sqFt: document.getElementById('sqFt'),
// Janitorial
janActive: document.getElementById('janitorialActive'),
janFreq: document.getElementById('frequency'),
janSupplies: document.getElementById('supplies'),
janControls: document.getElementById('janitorialControls'),
janDisplay: document.getElementById('freqVal'),
// Repairs
repActive: document.getElementById('repairActive'),
repHours: document.getElementById('repairHours'),
repPaint: document.getElementById('painting'),
repFix: document.getElementById('fixtures'),
repControls: document.getElementById('repairControls'),
repDisplay: document.getElementById('hoursVal'),
// Special
specActive: document.getElementById('specialActive'),
specMarble: document.getElementById('marbleSqFt'),
specMetal: document.getElementById('metalHours'),
specControls: document.getElementById('specialControls'),
marbleDisplay: document.getElementById('marbleVal'),
metalDisplay: document.getElementById('metalVal')
};
const display = {
total: document.getElementById('totalCostDisplay'),
clientName: document.getElementById('clientNameDisplay'),
salutation: document.getElementById('clientSalutation'),
scopeList: document.getElementById('scopeList'),
date: document.getElementById('proposalDate')
};
// --- Chart Instances ---
let costChartInstance = null;
let serviceChartInstance = null;
// --- Initialization ---
function init() {
// Set Date
const today = new Date();
display.date.textContent = today.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
// Event Listeners
inputs.name.addEventListener('input', (e) => { state.companyName = e.target.value; updateUI(); });
inputs.sqFt.addEventListener('input', (e) => { state.sqFt = parseInt(e.target.value) || 0; updateUI(); });
// Janitorial Listeners
inputs.janActive.addEventListener('change', (e) => { toggleSection('janitorial', e.target.checked); updateUI(); });
inputs.janFreq.addEventListener('input', (e) => {
state.janitorial.frequency = parseInt(e.target.value);
inputs.janDisplay.textContent = state.janitorial.frequency;
updateUI();
});
inputs.janSupplies.addEventListener('change', (e) => { state.janitorial.supplies = e.target.checked; updateUI(); });
// Repair Listeners
inputs.repActive.addEventListener('change', (e) => { toggleSection('repairs', e.target.checked); updateUI(); });
inputs.repHours.addEventListener('input', (e) => {
state.repairs.hours = parseInt(e.target.value);
inputs.repDisplay.textContent = state.repairs.hours;
updateUI();
});
inputs.repPaint.addEventListener('change', (e) => { state.repairs.painting = e.target.checked; updateUI(); });
inputs.repFix.addEventListener('change', (e) => { state.repairs.fixtures = e.target.checked; updateUI(); });
// Special Listeners
inputs.specActive.addEventListener('change', (e) => { toggleSection('special', e.target.checked); updateUI(); });
inputs.specMarble.addEventListener('input', (e) => {
state.special.marbleSqFt = parseInt(e.target.value);
inputs.marbleDisplay.textContent = state.special.marbleSqFt;
updateUI();
});
inputs.specMetal.addEventListener('input', (e) => {
state.special.metalHours = parseInt(e.target.value);
inputs.metalDisplay.textContent = state.special.metalHours;
updateUI();
});
// Initial Draw
initCharts();
updateUI();
}
function toggleSection(section, isActive) {
state[section].active = isActive;
const el = inputs[section === 'janitorial' ? 'janControls' : section === 'repairs' ? 'repControls' : 'specControls'];
if (isActive) {
el.classList.remove('opacity-50', 'pointer-events-none');
} else {
el.classList.add('opacity-50', 'pointer-events-none');
}
}
// --- Calculation Logic ---
function calculateCosts() {
let cleaningCost = 0;
let repairCost = 0;
let specialCost = 0;
if (state.janitorial.active) {
// Base calculation: SqFt * Rate * Frequency Multiplier
cleaningCost = (state.sqFt * rates.cleaningPerSqFt * state.janitorial.frequency);
if (state.janitorial.supplies) cleaningCost += rates.suppliesFlat;
}
if (state.repairs.active) {
repairCost = (state.repairs.hours * rates.repairHourly);
if (state.repairs.painting) repairCost += rates.paintFlat;
if (state.repairs.fixtures) repairCost += rates.fixtureFlat;
}
if (state.special.active) {
// Marble assumes monthly maintenance cost for demo purposes
specialCost += (state.special.marbleSqFt * 0.25); // Maintenance rate
specialCost += (state.special.metalHours * rates.metalHourly);
}
return { cleaningCost, repairCost, specialCost, total: cleaningCost + repairCost + specialCost };
}
// --- UI Updates ---
function updateUI() {
const costs = calculateCosts();
// Text Updates
display.total.textContent = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(costs.total);
const cName = state.companyName.trim() || '[Client Company Name]';
display.clientName.textContent = cName;
display.salutation.textContent = state.companyName.trim() ? state.companyName.trim() : 'Valued Partner';
// List Update
let listHTML = '';
if (state.janitorial.active) {
listHTML += `<li class="font-medium text-slate-800">Office Cleaning Services</li>`;
listHTML += `<ul class="list-circle pl-4 mb-2 text-sm text-slate-600">
<li>Frequency: ${state.janitorial.frequency} days per week</li>
<li>Area: ${state.sqFt.toLocaleString()} sq. ft.</li>
${state.janitorial.supplies ? '<li>Includes Consumable Supply Restocking</li>' : ''}
</ul>`;
}
if (state.repairs.active) {
listHTML += `<li class="font-medium text-slate-800">General Repair & Maintenance</li>`;
listHTML += `<ul class="list-circle pl-4 mb-2 text-sm text-slate-600">
<li>Dedicated Hours: ${state.repairs.hours} hrs/month</li>
${state.repairs.painting ? '<li>Includes Periodic Painting Touch-ups</li>' : ''}
${state.repairs.fixtures ? '<li>Includes Basic Fixture Installation</li>' : ''}
</ul>`;
}
if (state.special.active) {
listHTML += `<li class="font-medium text-slate-800">Specialized Finishing</li>`;
listHTML += `<ul class="list-circle pl-4 mb-2 text-sm text-slate-600">
${state.special.marbleSqFt > 0 ? `<li>Marble Floor Polishing: ${state.special.marbleSqFt} sq. ft.</li>` : ''}
${state.special.metalHours > 0 ? `<li>Metal Polishing Service: ${state.special.metalHours} hrs/month</li>` : ''}
</ul>`;
}
if (!state.janitorial.active && !state.repairs.active && !state.special.active) {
listHTML = '<li class="text-slate-400 italic">No services selected. Please configure your proposal.</li>';
}
display.scopeList.innerHTML = listHTML;
// Chart Updates
updateCharts(costs);
}
// --- Chart.js Implementations ---
function initCharts() {
// Cost Distribution Donut
const ctxCost = document.getElementById('costChart').getContext('2d');
costChartInstance = new Chart(ctxCost, {
type: 'doughnut',
data: {
labels: ['Cleaning', 'Repairs', 'Specialized'],
datasets: [{
data: [1, 1, 1],
backgroundColor: ['#0ea5e9', '#64748b', '#e2e8f0'],
borderWidth: 0,
hoverOffset: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'right', labels: { boxWidth: 12, font: { size: 11 } } },
tooltip: { callbacks: { label: (ctx) => ' ' + ctx.parsed.toLocaleString('en-US', {style:'currency', currency:'USD'}) } }
},
cutout: '70%'
}
});
// Service Intensity Bar
const ctxService = document.getElementById('serviceChart').getContext('2d');
serviceChartInstance = new Chart(ctxService, {
type: 'bar',
data: {
labels: ['Janitorial (Days)', 'Repair (Hrs)', 'Polish (Hrs)'],
datasets: [{
label: 'Service Intensity',
data: [0, 0, 0],
backgroundColor: ['#0ea5e9', '#64748b', '#94a3b8'],
borderRadius: 4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: { beginAtZero: true, grid: { color: '#f1f5f9' } },
x: { grid: { display: false } }
},
plugins: {
legend: { display: false }
}
}
});
}
function updateCharts(costs) {
// Update Cost Chart
if (costChartInstance) {
costChartInstance.data.datasets[0].data = [costs.cleaningCost, costs.repairCost, costs.specialCost];
// If all zero, show placeholder
if (costs.total === 0) costChartInstance.data.datasets[0].data = [1, 0, 0];
costChartInstance.update();
}
// Update Intensity Chart
if (serviceChartInstance) {
const janIntensity = state.janitorial.active ? state.janitorial.frequency : 0;
const repIntensity = state.repairs.active ? state.repairs.hours : 0;
const specIntensity = state.special.active ? state.special.metalHours + (state.special.marbleSqFt/200) : 0; // rough eq
serviceChartInstance.data.datasets[0].data = [janIntensity, repIntensity, specIntensity];
serviceChartInstance.update();
}
}
// Initialize App
init();
</script>
</body>
</html>
Sample Page
This is an example page. It’s different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:
Hi there! I’m a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin’ caught in the rain.)
…or something like this:
The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.
As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!