(function(){ /* ═══════════════════════════════════════════════════════ Time to Shine – Website Chatbot Widget Self-contained: CSS + HTML + logic. No dependencies. Drop before . ═══════════════════════════════════════════════════════ */ // ── CSS ── const css = document.createElement('style'); css.textContent = ` #tts-chat-bubble { position: fixed; bottom: 24px; right: 24px; z-index: 9999; width: 60px; height: 60px; border-radius: 50%; background: linear-gradient(135deg, #0d9488, #1b3a5c); color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 4px 20px rgba(0,0,0,.25); transition: transform .2s, box-shadow .2s; font-size: 28px; } #tts-chat-bubble:hover { transform: scale(1.08); box-shadow: 0 6px 28px rgba(0,0,0,.3); } #tts-chat-bubble .badge-dot { position: absolute; top: 2px; right: 2px; width: 14px; height: 14px; background: #f97316; border-radius: 50%; border: 2px solid #fff; animation: tts-pulse 2s infinite; } @keyframes tts-pulse { 0%,100%{ transform: scale(1); opacity: 1; } 50%{ transform: scale(1.3); opacity: .7; } } #tts-chat-window { position: fixed; bottom: 96px; right: 24px; z-index: 9999; width: 370px; max-height: 520px; border-radius: 16px; background: #fff; box-shadow: 0 8px 40px rgba(0,0,0,.18); display: none; flex-direction: column; overflow: hidden; font-family: 'Segoe UI', system-ui, -apple-system, sans-serif; animation: tts-slideUp .25s ease-out; } @keyframes tts-slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } #tts-chat-window.open { display: flex; } .tts-header { background: linear-gradient(135deg, #0f1f3d 0%, #1b3a5c 60%, #0d9488 100%); color: #fff; padding: 16px 18px; display: flex; align-items: center; gap: 12px; flex-shrink: 0; } .tts-header-avatar { width: 40px; height: 40px; border-radius: 50%; background: rgba(255,255,255,.2); display: flex; align-items: center; justify-content: center; font-size: 22px; flex-shrink: 0; } .tts-header-info h3 { font-size: .95rem; font-weight: 700; margin: 0; } .tts-header-info p { font-size: .78rem; opacity: .8; margin: 0; } .tts-close { margin-left: auto; background: none; border: none; color: #fff; font-size: 22px; cursor: pointer; opacity: .7; transition: opacity .2s; padding: 4px; } .tts-close:hover { opacity: 1; } .tts-messages { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; background: #f8fafb; scroll-behavior: smooth; } .tts-msg { max-width: 85%; padding: 10px 14px; border-radius: 12px; font-size: .9rem; line-height: 1.5; word-wrap: break-word; animation: tts-fadeIn .3s ease-out; } @keyframes tts-fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } .tts-msg.bot { background: #fff; border: 1px solid #e2e8f0; align-self: flex-start; border-bottom-left-radius: 4px; color: #1e293b; } .tts-msg.user { background: linear-gradient(135deg, #0d9488, #1b3a5c); color: #fff; align-self: flex-end; border-bottom-right-radius: 4px; } .tts-options { display: flex; flex-wrap: wrap; gap: 8px; align-self: flex-start; animation: tts-fadeIn .3s ease-out; } .tts-opt-btn { background: #f0fdfa; border: 1.5px solid #0d9488; color: #0d9488; padding: 8px 14px; border-radius: 20px; font-size: .82rem; font-weight: 600; cursor: pointer; transition: all .2s; font-family: inherit; } .tts-opt-btn:hover { background: #0d9488; color: #fff; } .tts-opt-btn.gold { background: #fff7ed; border-color: #f97316; color: #ea580c; } .tts-opt-btn.gold:hover { background: #f97316; color: #fff; } .tts-input-area { display: flex; gap: 8px; padding: 12px 14px; border-top: 1px solid #e2e8f0; background: #fff; flex-shrink: 0; } .tts-input-area input { flex: 1; border: 1.5px solid #e2e8f0; border-radius: 20px; padding: 10px 16px; font-size: .88rem; outline: none; font-family: inherit; transition: border-color .2s; } .tts-input-area input:focus { border-color: #0d9488; } .tts-input-area button { background: #0d9488; color: #fff; border: none; border-radius: 50%; width: 38px; height: 38px; cursor: pointer; font-size: 18px; display: flex; align-items: center; justify-content: center; transition: background .2s; flex-shrink: 0; } .tts-input-area button:hover { background: #1b3a5c; } /* phone link inside bot messages */ .tts-msg a.tts-phone { color: #0d9488; font-weight: 700; text-decoration: none; } .tts-msg a.tts-phone:hover { text-decoration: underline; } /* mobile */ @media (max-width: 480px) { #tts-chat-window { right: 0; bottom: 0; left: 0; width: 100%; max-height: 100dvh; border-radius: 0; height: 100dvh; } #tts-chat-bubble { bottom: 16px; right: 16px; } } `; document.head.appendChild(css); // ── HTML ── const bubble = document.createElement('div'); bubble.id = 'tts-chat-bubble'; bubble.innerHTML = '💬
'; bubble.setAttribute('aria-label','Chat with us'); document.body.appendChild(bubble); const win = document.createElement('div'); win.id = 'tts-chat-window'; win.innerHTML = `

Time to Shine

Usually replies instantly

`; document.body.appendChild(win); // ── REFS ── const msgs = document.getElementById('tts-msgs'); const input = document.getElementById('tts-input'); const sendBtn = document.getElementById('tts-send'); const closeBtn = win.querySelector('.tts-close'); let isOpen = false; // ── TOGGLE ── function openChat() { win.classList.add('open'); isOpen = true; bubble.querySelector('.badge-dot').style.display = 'none'; if (!msgs.children.length) showGreeting(); setTimeout(() => input.focus(), 300); } function closeChat() { win.classList.remove('open'); isOpen = false; } bubble.addEventListener('click', () => isOpen ? closeChat() : openChat()); closeBtn.addEventListener('click', closeChat); // ── MESSAGE HELPERS ── function addBot(text) { const d = document.createElement('div'); d.className = 'tts-msg bot'; d.innerHTML = text; msgs.appendChild(d); scrollBottom(); } function addUser(text) { const d = document.createElement('div'); d.className = 'tts-msg user'; d.textContent = text; msgs.appendChild(d); scrollBottom(); } function addOptions(opts) { const wrap = document.createElement('div'); wrap.className = 'tts-options'; opts.forEach(o => { const b = document.createElement('button'); b.className = 'tts-opt-btn' + (o.gold ? ' gold' : ''); b.textContent = o.label; b.addEventListener('click', () => { addUser(o.label); wrap.remove(); setTimeout(() => o.action(), 300); }); wrap.appendChild(b); }); msgs.appendChild(wrap); scrollBottom(); } function scrollBottom() { setTimeout(() => msgs.scrollTop = msgs.scrollHeight, 50); } // ── RESPONSES ── function showGreeting() { addBot("Hey there! 👋 I'm the Time to Shine assistant. How can I help you today?"); setTimeout(() => showMainMenu(), 400); } function showMainMenu() { addOptions([ { label: '🔧 What do you repair?', action: answerAppliances }, { label: '💰 How much does it cost?', action: answerPricing }, { label: '📍 Service area', action: answerArea }, { label: '📅 Book an appointment', action: answerBook, gold: true }, { label: '❓ I have an appliance issue', action: answerIssueMenu }, ]); } function answerAppliances() { addBot("We repair refrigerators, washing machines, dryers, dishwashers, and ovens — all major brands! If your appliance is acting up, we can diagnose and fix it."); setTimeout(() => addOptions([ { label: '📅 Book a repair', action: answerBook, gold: true }, { label: '💰 What does it cost?', action: answerPricing }, { label: '↩ Main menu', action: showMainMenu }, ]), 400); } function answerPricing() { addBot("We charge an $80 diagnostic fee that goes toward the cost of the repair if you decide to move forward. No surprise charges — you'll know the full cost before any work begins."); setTimeout(() => addOptions([ { label: '📅 Book now', action: answerBook, gold: true }, { label: '🔧 What do you fix?', action: answerAppliances }, { label: '↩ Main menu', action: showMainMenu }, ]), 400); } function answerArea() { addBot("We serve Greenville, SC and surrounding areas including Simpsonville, Mauldin, Greer, Taylors, Travelers Rest, Easley, and Piedmont."); setTimeout(() => addOptions([ { label: '📅 Book now', action: answerBook, gold: true }, { label: '↩ Main menu', action: showMainMenu }, ]), 400); } function answerBook() { addBot("Let's get your appliance fixed! 🎉 Click below to pick a day and time that works for you."); setTimeout(() => { addOptions([ { label: '📅 Go to booking page', action: () => window.location.href = '/book', gold: true }, { label: '📞 Call (864) 704-7400', action: () => window.location.href = 'tel:+18647047400' }, { label: '↩ Main menu', action: showMainMenu }, ]); }, 400); } function answerIssueMenu() { addBot("What type of appliance is giving you trouble?"); setTimeout(() => addOptions([ { label: '🧊 Refrigerator', action: () => answerIssue('fridge') }, { label: '👕 Washing machine', action: () => answerIssue('washer') }, { label: '🔥 Dryer', action: () => answerIssue('dryer') }, { label: '🍽 Dishwasher', action: () => answerIssue('dishwasher') }, { label: '♨️ Oven / Range', action: () => answerIssue('oven') }, ]), 400); } function answerIssue(type) { const info = { fridge: "Common fridge issues: not cooling, ice maker problems, water leaks, strange noises, and frost buildup. Whatever's going on, our $80 diagnostic will pinpoint the problem and we'll give you a clear repair quote.", washer: "Common washer issues: not draining, not spinning, leaking, loud noises, vibrating, and error codes. We work on all brands — top load and front load. Our $80 diagnostic covers the full inspection.", dryer: "Common dryer issues: not heating, not tumbling, taking too long, squeaking, and shutting off early. We'll diagnose it for $80 and that fee goes toward the repair.", dishwasher: "Common dishwasher issues: not cleaning well, not draining, leaking, not starting, and strange odors. We'll get it sorted — $80 diagnostic fee goes toward the fix.", oven: "Common oven issues: not heating, uneven cooking, burner problems, temperature issues, and error codes. Our $80 diagnostic covers the full inspection." }; addBot(info[type]); setTimeout(() => addOptions([ { label: '📅 Book a diagnostic', action: answerBook, gold: true }, { label: '📞 Call us now', action: () => window.location.href = 'tel:+18647047400' }, { label: '↩ Main menu', action: showMainMenu }, ]), 400); } function answerHours() { addBot("We're available Monday through Friday. Closed Saturday and Sunday. Book online anytime at your convenience and we'll confirm your slot!"); setTimeout(() => addOptions([ { label: '📅 Book now', action: answerBook, gold: true }, { label: '↩ Main menu', action: showMainMenu }, ]), 400); } function answerContact() { addBot('You can reach us at (864) 704-7400 or email us at timetoshinepws@gmail.com. For the fastest booking, use our online scheduler!'); setTimeout(() => addOptions([ { label: '📅 Book online', action: answerBook, gold: true }, { label: '↩ Main menu', action: showMainMenu }, ]), 400); } function answerDefault() { addBot("I'm not sure I caught that — but I can help with these:"); setTimeout(() => showMainMenu(), 300); } // ── KEYWORD MATCHING ── function handleFreeText(text) { const t = text.toLowerCase(); // booking / schedule if (/\b(book|schedule|appointment|when|available|slot|come out)\b/.test(t)) return answerBook(); // pricing if (/\b(cost|price|charge|fee|how much|rate|diagnostic|expensive|cheap|afford)\b/.test(t)) return answerPricing(); // service area if (/\b(area|where|location|serve|city|greenville|simpsonville|mauldin|greer|taylors|easley|piedmont|travelers)\b/.test(t)) return answerArea(); // what do you fix if (/\b(what do you|what can you|fix|repair|service|work on|appliance)\b/.test(t)) return answerAppliances(); // hours if (/\b(hour|open|close|weekend|saturday|sunday|monday|time)\b/.test(t)) return answerHours(); // contact if (/\b(phone|call|email|contact|reach|number)\b/.test(t)) return answerContact(); // specific appliances if (/\b(fridge|refrigerator|freezer|ice maker|ice machine|not cooling)\b/.test(t)) return answerIssue('fridge'); if (/\b(washer|washing machine|front load|top load|spin|drain|agitat)\b/.test(t)) return answerIssue('washer'); if (/\b(dryer|not heating|not drying|tumbl|lint|squeaking)\b/.test(t)) return answerIssue('dryer'); if (/\b(dishwasher|dish washer|dishes|not cleaning)\b/.test(t)) return answerIssue('dishwasher'); if (/\b(oven|stove|range|burner|bake|broil|not heating up)\b/.test(t)) return answerIssue('oven'); // fallback return answerDefault(); } // ── SEND ── function send() { const text = input.value.trim(); if (!text) return; addUser(text); input.value = ''; // remove any existing option buttons const oldOpts = msgs.querySelectorAll('.tts-options'); oldOpts.forEach(o => o.remove()); setTimeout(() => handleFreeText(text), 500); } sendBtn.addEventListener('click', send); input.addEventListener('keydown', e => { if (e.key === 'Enter') send(); }); // ── AUTO-OPEN AFTER 8s ON FIRST VISIT ── if (!sessionStorage.getItem('tts-chat-opened')) { setTimeout(() => { if (!isOpen) { // just leave the badge pulsing — don't auto-open } }, 8000); } })();