<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Smart Cleaning LLC | Best House Cleaning in Olathe &amp; Kansas City</title>
    <meta name="description" content="Family-owned house &amp; commercial cleaning in Olathe, Overland Park, and the Kansas City metro. Get a personalized cleaning quote in about two minutes — no forms, no phone tag. Licensed, insured, satisfaction guaranteed.">
    <meta name="author" content="Smart Cleaning LLC" />

    <meta property="og:type" content="website">
    <meta property="og:title" content="Smart Cleaning — Chat with Maya for an Instant Quote">
    <meta property="og:description" content="Get a personalized cleaning quote in about two minutes. Smart Cleaning — proudly serving the Kansas City metro.">
    <meta property="og:image" content="https://assets.cdn.filesafe.space/vSWgXzbjD3QDvAewlvX8/media/6a19cfc15be84ad64037ae36.png">
    <meta property="og:image:width" content="1200">
    <meta property="og:image:height" content="630">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="twitter:title" content="Smart Cleaning — Chat with Maya for an Instant Quote">
    <meta name="twitter:description" content="Get a personalized cleaning quote in about two minutes. Kansas City metro.">
    <meta name="twitter:image" content="https://assets.cdn.filesafe.space/vSWgXzbjD3QDvAewlvX8/media/6a19cfc15be84ad64037ae36.png">

    <script type="module" crossorigin src="/assets/index-WH4Mjsvb.js"></script>
    <link rel="stylesheet" crossorigin href="/assets/index-yM81r8yS.css">
  </head>


  <body>
    <div id="root"></div>

<!--
  Maya — Smart Cleaning Instant Quote (POPUP VERSION)
  Paste this ONCE into a global/footer Custom HTML block in Vibe.
  Maya stays hidden until any "Get Instant Quote" button is clicked.
  Worker URL is already set. No API keys here — safe to be public.

  HOW BUTTONS TRIGGER MAYA:
  Any element with class "open-maya" OR any link/button whose text
  contains "instant quote" will open the chat. Also exposes window.openMaya().

  ADDRESS AUTOCOMPLETE:
  When Maya asks for the address, typing 3+ characters fetches suggestions
  from the Worker's /autocomplete endpoint (Google Places, server-side key).
  Tapping a suggestion fills the input. Fails silently to manual typing.
-->
<style>
  #maya-overlay{position:fixed;inset:0;height:100dvh;background:rgba(15,42,63,.45);backdrop-filter:blur(3px);z-index:99998;display:none;align-items:center;justify-content:center;padding:16px;}
  #maya-overlay.open{display:flex;}
  #maya-modal{position:relative;width:420px;max-width:100%;height:640px;max-height:100%;}
  #maya-close{position:absolute;top:8px;right:8px;z-index:99999;width:34px;height:34px;border-radius:50%;background:rgba(255,255,255,.92);border:none;font-size:20px;line-height:1;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.2);color:#0f2a3f;}
  #maya-overlay *{box-sizing:border-box;}
  @media (max-width:520px){
    #maya-overlay{padding:0;align-items:stretch;justify-content:stretch;}
    #maya-modal{width:100%;height:100%;max-height:100%;}
    .mw{border-radius:0;border:none;}
  }
  .mw{font-family:-apple-system,"Segoe UI",system-ui,sans-serif;position:relative;height:100%;display:flex;flex-direction:column;background:#f4fafb;border:1px solid #d8e6e6;border-radius:18px;box-shadow:0 20px 60px rgba(15,42,63,.3);overflow:hidden;}
  .mw-head{background:linear-gradient(135deg,#1f8a8a,#136b6b);color:#fff;padding:16px 18px;display:flex;align-items:center;gap:12px;}
  .mw-av{width:42px;height:42px;border-radius:50%;background:#f5b54a;color:#0f2a3f;display:grid;place-items:center;font-weight:700;font-size:18px;box-shadow:0 2px 8px rgba(0,0,0,.18);}
  .mw-head h3{margin:0;font-size:16px;font-weight:650;} .mw-head p{margin:2px 0 0;font-size:12px;opacity:.85;}
  .mw-dot{width:8px;height:8px;border-radius:50%;background:#6ee7b7;box-shadow:0 0 0 3px rgba(110,231,183,.25);margin-left:auto;}
  .mw-body{flex:1;overflow-y:auto;padding:18px 14px;display:flex;flex-direction:column;gap:10px;}
  .mw-msg{max-width:80%;padding:10px 14px;font-size:14.5px;line-height:1.45;border-radius:16px;white-space:pre-wrap;animation:mwpop .25s ease;}
  @keyframes mwpop{from{opacity:0;transform:translateY(6px);}to{opacity:1;transform:translateY(0);}}
  .mw-bot{background:#fff;color:#0f2a3f;align-self:flex-start;border:1px solid #d8e6e6;border-bottom-left-radius:5px;}
  .mw-user{background:#1f8a8a;color:#fff;align-self:flex-end;border-bottom-right-radius:5px;}
  .mw-foot{border-top:1px solid #d8e6e6;padding:10px;display:flex;gap:8px;background:#fff;}
  .mw-in{flex:1;border:1px solid #d8e6e6;border-radius:22px;padding:11px 16px;font-size:16px;outline:none;}
  .mw-in:focus{border-color:#1f8a8a;}
  .mw-send{background:#1f8a8a;color:#fff;border:none;width:42px;height:42px;min-width:42px;flex-shrink:0;border-radius:50%;cursor:pointer;font-size:18px;}
  .mw-send:hover{background:#136b6b;} .mw-send:disabled{opacity:.5;}
  .mw-typing{display:flex;gap:4px;padding:12px 16px;align-self:flex-start;}
  .mw-typing span{width:7px;height:7px;background:#1f8a8a;border-radius:50%;animation:mwblink 1.2s infinite;}
  .mw-typing span:nth-child(2){animation-delay:.2s;} .mw-typing span:nth-child(3){animation-delay:.4s;}
  @keyframes mwblink{0%,60%,100%{opacity:.25;}30%{opacity:1;}}
  .mw-sug{display:none;position:absolute;left:10px;right:10px;bottom:66px;background:#fff;border:1px solid #d8e6e6;border-radius:12px;box-shadow:0 10px 28px rgba(15,42,63,.2);overflow:hidden;max-height:225px;overflow-y:auto;z-index:6;}
  .mw-sug.show{display:block;}
  .mw-sug-item{padding:12px 14px;font-size:14px;line-height:1.35;color:#0f2a3f;cursor:pointer;border-bottom:1px solid #eef4f5;}
  .mw-sug-item:last-child{border-bottom:none;}
  .mw-sug-item:hover,.mw-sug-item:active{background:#f0f8f8;}
</style>

<div id="maya-overlay">
  <div id="maya-modal">
    <button id="maya-close" aria-label="Close">×</button>
    <div class="mw">
      <div class="mw-head"><div class="mw-av">M</div><div><h3>Maya · Smart Cleaning</h3><p>Instant Quote Specialist</p></div><div class="mw-dot"></div></div>
      <div class="mw-body" id="mwBody"></div>
      <div class="mw-sug" id="mwSug"></div>
      <div class="mw-foot"><input class="mw-in" id="mwIn" placeholder="Type your message…" autocomplete="off"><button class="mw-send" id="mwSend">↑</button></div>
    </div>
  </div>
</div>

<script>
(function(){
  const WORKER_URL = "https://smart-cleaning-maya.atlaslogic.workers.dev";

  const overlay=document.getElementById('maya-overlay');
  const body=document.getElementById('mwBody'), inp=document.getElementById('mwIn'), btn=document.getElementById('mwSend');
  const sug=document.getElementById('mwSug');
  let history=[], busy=false, started=false;
  let addressMode=false, sugTimer=null;

  function bubble(t,who){const d=document.createElement('div');d.className='mw-msg '+(who==='user'?'mw-user':'mw-bot');d.textContent=t;body.appendChild(d);body.scrollTop=body.scrollHeight;}
  function typingOn(){const t=document.createElement('div');t.className='mw-typing';t.id='mwTyping';t.innerHTML='<span></span><span></span><span></span>';body.appendChild(t);body.scrollTop=body.scrollHeight;}
  function typingOff(){const t=document.getElementById('mwTyping');if(t)t.remove();}

  // ----- Address autocomplete -----
  // Active only when Maya's latest message asks about the address.
  function setAddressMode(botText){
    addressMode=/address/i.test(botText||'');
    if(!addressMode) hideSug();
  }
  function hideSug(){ sug.classList.remove('show'); sug.innerHTML=''; }
  function showSug(list){
    if(!list||!list.length){ hideSug(); return; }
    sug.innerHTML='';
    list.forEach(t=>{
      const d=document.createElement('div');
      d.className='mw-sug-item'; d.textContent=t;
      d.addEventListener('click',()=>{ inp.value=t; hideSug(); inp.focus(); });
      sug.appendChild(d);
    });
    sug.classList.add('show');
  }
  inp.addEventListener('input',()=>{
    if(!addressMode){ hideSug(); return; }
    const v=inp.value.trim();
    if(v.length<3){ hideSug(); return; }
    clearTimeout(sugTimer);
    sugTimer=setTimeout(async()=>{
      try{
        const r=await fetch(WORKER_URL+'/autocomplete',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({partial:v})});
        const d=await r.json();
        if(inp.value.trim()===v) showSug(d.suggestions);
      }catch(e){ hideSug(); }
    },250);
  });

  async function send(){
    const text=inp.value.trim(); if(!text||busy)return;
    inp.value=''; hideSug(); bubble(text,'user'); busy=true; btn.disabled=true; typingOn();
    history.push({role:'user',content:text});
    try{
      const r=await fetch(WORKER_URL,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({messages:history})});
      const data=await r.json(); typingOff();
      bubble(data.reply||"Sorry, I didn't catch that — could you say it again? 😊",'bot');
      setAddressMode(data.reply);
      if(data.messages) history=data.messages;
    }catch(e){ typingOff(); bubble("Sorry, something hiccupped — mind trying again? 😊",'bot'); }
    busy=false; btn.disabled=false; inp.focus();
  }
  btn.onclick=send;
  inp.addEventListener('keydown',e=>{if(e.key==='Enter')send();});

  async function greet(){
    if(started)return; started=true; typingOn();
    try{
      const r=await fetch(WORKER_URL,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({messages:[{role:'user',content:'__START__'}]})});
      const data=await r.json(); typingOff();
      bubble(data.reply||"Hi there! 👋 I'm Maya — what's your name? 😊",'bot');
      setAddressMode(data.reply);
      if(data.messages) history=data.messages;
    }catch(e){ typingOff(); bubble("Hi there! 👋 I'm Maya, your Smart Cleaning quote specialist! What's your name? 😊",'bot'); }
  }

  function openMaya(){ overlay.classList.add('open'); lockZoom(); greet(); setTimeout(()=>{inp.focus(); fitToViewport();},100); }
  function closeMaya(){ overlay.classList.remove('open'); unlockZoom(); hideSug(); }
  window.openMaya=openMaya;

  // Prevent iOS from zooming the page when the input is focused.
  function lockZoom(){
    let m=document.querySelector('meta[name=viewport]');
    if(!m){ m=document.createElement('meta'); m.name='viewport'; document.head.appendChild(m); }
    m.dataset.mayaPrev=m.getAttribute('content')||'';
    m.setAttribute('content','width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no');
  }
  function unlockZoom(){
    const m=document.querySelector('meta[name=viewport]');
    if(m && m.dataset.mayaPrev!==undefined){ m.setAttribute('content', m.dataset.mayaPrev || 'width=device-width, initial-scale=1'); }
  }

  // iOS keyboard fix: size the OVERLAY to the visible viewport (not the full
  // window) so the modal — and its input row — always sits in the area the
  // keyboard isn't covering. Resizing the overlay (not transforming the modal)
  // is far more reliable on iOS Safari/WebKit.
  function fitToViewport(){
    const vv=window.visualViewport;
    if(!vv){ return; }
    if(window.innerWidth>520){
      overlay.style.height=''; overlay.style.top=''; overlay.style.left=''; overlay.style.width='';
      return;
    }
    overlay.style.position='fixed';
    overlay.style.top = vv.offsetTop + 'px';
    overlay.style.left = vv.offsetLeft + 'px';
    overlay.style.width = vv.width + 'px';
    overlay.style.height = vv.height + 'px';
  }
  if(window.visualViewport){
    window.visualViewport.addEventListener('resize', fitToViewport);
    window.visualViewport.addEventListener('scroll', fitToViewport);
  }

  document.getElementById('maya-close').onclick=closeMaya;
  overlay.addEventListener('click',e=>{ if(e.target===overlay) closeMaya(); });

  // Wire up triggers: any .open-maya element, or any button/link mentioning "instant quote"
  function wire(){
    document.querySelectorAll('.open-maya').forEach(el=>{ el.addEventListener('click',e=>{e.preventDefault();openMaya();}); });
    document.querySelectorAll('a,button').forEach(el=>{
      const t=(el.textContent||'').toLowerCase();
      if(t.includes('instant quote')||t.includes('get my instant quote')){ el.addEventListener('click',e=>{e.preventDefault();openMaya();}); }
    });
  }
  if(document.readyState!=='loading') wire(); else document.addEventListener('DOMContentLoaded',wire);
})();
</script>


  </body>
</html>
