{"id":185,"date":"2026-04-19T17:36:31","date_gmt":"2026-04-19T17:36:31","guid":{"rendered":"https:\/\/shinyflowcleaning.com\/?page_id=185"},"modified":"2026-04-19T19:24:29","modified_gmt":"2026-04-19T19:24:29","slug":"book-a-service","status":"publish","type":"page","link":"https:\/\/shinyflowcleaning.com\/?page_id=185","title":{"rendered":"Book a service"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"185\" class=\"elementor elementor-185\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3e0421d e-con-full e-flex e-con e-parent\" data-id=\"3e0421d\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-cdd236d elementor-widget__width-initial elementor-widget elementor-widget-shortcode\" data-id=\"cdd236d\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">    <style>\n    input,select,textarea{font-size:16px!important;}\n    .sfqw{max-width:660px;margin:0 auto;padding:28px 16px;font-family:Montserrat,sans-serif;}\n    .sfqw h2{font-family:\"Bebas Neue\",sans-serif;font-size:38px;color:#073e64;letter-spacing:2px;margin:0 0 4px;line-height:1;}\n    .sfqw .sfq-sub{font-size:13px;color:#6b7a8d;margin:0 0 24px;}\n    .sfq-steps{display:flex;gap:0;margin-bottom:24px;border-bottom:2px solid #e4e4e4;}\n    .sfq-step-ind{padding:9px 16px;font-family:Montserrat,sans-serif;font-size:11px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:#9aa5b4;border-bottom:2px solid transparent;margin-bottom:-2px;user-select:none;}\n    .sfq-step-ind.active{color:#073e64;border-bottom-color:#073e64;font-weight:800;}\n    .sfq-step-ind.done{color:#25D366;}\n    .sfq-panel{display:none;} .sfq-panel.active{display:block;}\n    .sfq-section-title{font-family:\"Bebas Neue\",sans-serif;font-size:17px;color:#073e64;letter-spacing:1px;margin:0 0 12px;padding-bottom:5px;border-bottom:2px solid #c1ff72;}\n    .sfq-g2{display:grid;grid-template-columns:1fr 1fr;gap:12px;}\n    .sfq-g3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;}\n    @media(max-width:500px){.sfq-g2,.sfq-g3{grid-template-columns:1fr;}}\n    .sfq-field{display:flex;flex-direction:column;gap:4px;margin-bottom:2px;}\n    .sfq-field.full{grid-column:1\/-1;}\n    .sfq-field label{font-size:10px;font-weight:800;color:#073e64;text-transform:uppercase;letter-spacing:1px;}\n    .sfq-field input,.sfq-field textarea{border:1.5px solid #e4e4e4;padding:10px 12px;font-family:Montserrat,sans-serif;font-size:16px!important;color:#073e64;width:100%;background:#fff;outline:none;border-radius:0;box-sizing:border-box;}\n    .sfq-field select{border:1.5px solid #e4e4e4;padding:10px 36px 10px 12px;font-family:Montserrat,sans-serif;font-size:15px;font-weight:700;color:#073e64;width:100%;background:#fff url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%23073e64' stroke-width='2' fill='none' stroke-linecap='round'\/%3E%3C\/svg%3E\") no-repeat right 13px center;-webkit-appearance:none;-moz-appearance:none;appearance:none;outline:none;border-radius:0;box-sizing:border-box;cursor:pointer;}\n    .sfq-field select option{font-family:Montserrat,sans-serif;font-size:14px;font-weight:600;color:#073e64;padding:8px 12px;line-height:2;}\n    .sfq-field select:focus,.sfq-field input:focus,.sfq-field textarea:focus{border-color:#073e64;}\n    .sfq-field textarea{resize:vertical;min-height:72px;}\n    \/* Line items *\/\n    .sfq-line-item{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1.5px solid #e4e4e4;margin-bottom:8px;background:#fff;}\n    .sfq-line-item:hover{border-color:#073e64;background:#fafbfc;}\n    .sfq-line-info{flex:1;min-width:0;}\n    .sfq-line-name{font-size:13px;font-weight:800;color:#073e64;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\n    .sfq-line-price{font-size:11px;color:#9aa5b4;margin-top:2px;}\n    .sfq-line-stepper{display:flex;align-items:center;border:1.5px solid #e4e4e4;background:#fff;height:38px;flex-shrink:0;}\n    .sfq-line-stepper button{width:38px;height:38px;border:none;background:none;font-size:20px;font-weight:700;color:#073e64;cursor:pointer;line-height:1;touch-action:manipulation;-webkit-tap-highlight-color:transparent;padding:0;flex-shrink:0;}\n    .sfq-line-stepper button:hover{background:#073e64;color:#c1ff72;}\n    .sfq-line-stepper span{width:32px;text-align:center;font-size:15px;font-weight:800;color:#073e64;}\n    .sfq-line-subtotal{font-size:13px;font-weight:800;color:#073e64;min-width:52px;text-align:right;flex-shrink:0;}\n    .sfq-no-items{font-size:13px;color:#9aa5b4;padding:16px;text-align:center;border:1.5px dashed #e4e4e4;}\n    \/* Estimate box *\/\n    .sfq-estimate{background:#073e64;color:#fff;padding:14px 18px;margin-top:14px;}\n    .sfq-estimate .est-label{font-size:10px;font-weight:800;letter-spacing:1px;text-transform:uppercase;opacity:.6;margin-bottom:3px;}\n    .sfq-estimate .est-total{font-family:\"Bebas Neue\",sans-serif;font-size:36px;letter-spacing:2px;line-height:1;}\n    .sfq-estimate .est-dep{font-size:12px;color:#c1ff72;font-weight:700;margin-top:3px;}\n    \/* Nav *\/\n    .sfq-nav{display:flex;gap:10px;margin-top:16px;}\n    .sfq-btn-back{background:#f2f4f7;color:#073e64;border:none;padding:14px 20px;font-family:Montserrat,sans-serif;font-size:12px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;cursor:pointer;}\n    .sfq-btn-next{flex:1;padding:14px;font-family:Montserrat,sans-serif;font-size:13px;font-weight:800;letter-spacing:2px;text-transform:uppercase;border:none;cursor:pointer;}\n    .sfq-btn-next:disabled,.sfq-submit:disabled{opacity:.6;cursor:not-allowed;}\n    \/* Summary *\/\n    .sfq-summary{border:1px solid #e4e4e4;margin-bottom:16px;}\n    .sfq-sum-title{background:#073e64;color:#c1ff72;padding:10px 14px;font-family:\"Bebas Neue\",sans-serif;font-size:16px;letter-spacing:1px;}\n    .sfq-srow{display:flex;justify-content:space-between;padding:8px 14px;border-bottom:1px solid #f2f4f7;font-size:13px;}\n    .sfq-srow:last-child{border:none;font-weight:800;}\n    .sfq-srow span:first-child{color:#9aa5b4;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;}\n    \/* Error *\/\n    .sfq-err{background:#fff0f0;border-left:3px solid #e74c3c;padding:10px 14px;font-size:13px;color:#c0392b;display:none;margin-bottom:14px;}\n    .sfq-err.show{display:block;}\n    .sfq-zone-err{font-size:11px;color:#c0392b;display:none;margin-top:4px;font-weight:700;}\n    .sfq-zone-err.show{display:block;}\n    \/* Calendar *\/\n    .sfq-calwrap{position:relative;z-index:10000;}\n    .sfq-cal{position:absolute;top:calc(100% + 4px);left:0;background:#fff;border:1.5px solid #073e64;padding:14px;z-index:10001;display:none;min-width:280px;box-shadow:0 8px 24px rgba(0,0,0,.12);}\n    .sfq-cal.show{display:block;}\n    .sfq-calh{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;}\n    .sfq-calnav{background:none;border:none;font-size:20px;cursor:pointer;color:#073e64;padding:0 6px;font-weight:800;line-height:1;touch-action:manipulation;}\n    .sfq-calt{font-family:\"Bebas Neue\",sans-serif;font-size:17px;color:#073e64;letter-spacing:1px;}\n    .sfq-calg{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;text-align:center;}\n    .sfq-cdn{font-size:9px;font-weight:800;color:#9aa5b4;text-transform:uppercase;padding:3px 0;}\n    .sfq-cd{font-size:12px;padding:7px 2px;cursor:pointer;color:#073e64;user-select:none;}\n    .sfq-cd:hover{background:#f4ffeb;}\n    .sfq-cd.sel{background:#c1ff72;font-weight:800;}\n    .sfq-cd.off{color:#e4e4e4;cursor:not-allowed;pointer-events:none;}\n    .sfq-cd.emp{pointer-events:none;}\n    @media(max-width:500px){.sfq-cal{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:90vw;max-width:320px;z-index:99999;}}\n    .sfq-cdn{font-size:9px;font-weight:800;color:#9aa5b4;text-transform:uppercase;padding:4px 0;}\n    .sfq-cd{font-size:14px;padding:11px 2px;cursor:pointer;color:#073e64;user-select:none;min-height:40px;display:flex;align-items:center;justify-content:center;touch-action:manipulation;-webkit-tap-highlight-color:rgba(193,255,114,.4);}\n    .sfq-cd:active{background:#f4ffeb;}\n    .sfq-cd.sel{background:#c1ff72;font-weight:800;}\n    .sfq-cd.off{color:#e4e4e4;cursor:not-allowed;pointer-events:none;}\n    .sfq-cd.emp{pointer-events:none;}\n    \/* Photo upload *\/\n    .sfq-photo-wrap{border:2px dashed #e4e4e4;padding:20px;text-align:center;cursor:pointer;transition:border-color .2s;position:relative;z-index:1;}\n    .sfq-photo-wrap:hover{border-color:#073e64;}\n    .sfq-photo-wrap input[type=file]{position:absolute;inset:0;opacity:0;cursor:pointer;width:100%;height:100%;}\n    .sfq-photo-preview{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px;}\n    .sfq-photo-thumb{width:72px;height:72px;object-fit:cover;border:1px solid #e4e4e4;}\n    \/* Done *\/\n    .sfq-done{display:none;background:#f4ffeb;border:2px solid #c1ff72;padding:24px;}\n    .sfq-done.show{display:block;}\n    .sfq-done h3{font-family:\"Bebas Neue\",sans-serif;font-size:32px;color:#073e64;letter-spacing:2px;margin:0 0 8px;}\n    \/* Autocomplete *\/\n    .sfq-acwrap{position:relative;}\n    .sfq-sugg{position:absolute;top:100%;left:0;right:0;background:#fff;border:1.5px solid #073e64;border-top:none;z-index:9999;max-height:180px;overflow-y:auto;display:none;}\n    .sfq-sugg.show{display:block;}\n    .sfq-si{padding:9px 12px;font-size:13px;cursor:pointer;border-bottom:1px solid #f0f0f0;}\n    .sfq-si:hover{background:#f4ffeb;}\n    <\/style>\n\n    <div class=\"sfqw\">\n      <h2>Book a Service<\/h2>\n      <p class=\"sfq-sub\">Choose a time, we\u2019ll handle the cleaning.<\/p>\n\n      <div class=\"sfq-steps\">\n        <div class=\"sfq-step-ind active\" id=\"sfqSI1\">1. Services<\/div>\n        <div class=\"sfq-step-ind\" id=\"sfqSI2\">2. Your Details<\/div>\n        <div class=\"sfq-step-ind\" id=\"sfqSI3\">3. Confirm<\/div>\n      <\/div>\n\n      <div class=\"sfq-err\" id=\"sfqErr\"><\/div>\n\n      <div id=\"sfqFormWrap\">\n        <!-- STEP 1 -->\n        <div id=\"sfqPanel1\" class=\"sfq-panel active\">\n                    <div class=\"sfq-section-title\">Select Service<\/div>\n          <div class=\"sfq-field\" style=\"margin-bottom:16px;\">\n            <label>Service *<\/label>\n            <select id=\"sfqSvc\" onchange=\"sfqLoadItems()\">\n              <option value=\"\">\u2014 Select a service \u2014<\/option>\n              <option value=\"Standard Cleaning\">Standard Cleaning<\/option><option value=\"Deep Cleaning\">Deep Cleaning<\/option><option value=\"Move-In Cleaning\">Move-In Cleaning<\/option><option value=\"Move-Out Cleaning\">Move-Out Cleaning<\/option>            <\/select>\n          <\/div>\n                    <div class=\"sfq-section-title\" style=\"margin-top:14px;\">Items & Quantities<\/div>\n          <div id=\"sfqItemsWrap\"><div class=\"sfq-no-items\">Select a service to see available items.<\/div><\/div>\n          <div id=\"sfqEstBox\" style=\"display:none;\">\n            <div class=\"sfq-estimate\">\n              <div class=\"est-label\">Estimated Total<\/div>\n              <div class=\"est-total\" id=\"sfqEstTotal\">$0.00<\/div>\n              <div class=\"est-dep\" id=\"sfqEstDep\">Deposit (50%): $0.00<\/div>\n            <\/div>\n          <\/div>\n          <button id=\"sfqCont1\" class=\"sfq-btn-next\" style=\"display:none;margin-top:14px;width:100%;background:#c1ff72;color:#073e64;\" onclick=\"sfqGoStep(2)\">Continue \u2192<\/button>\n          <div id=\"sfqPriceNote\" style=\"display:none;background:#f2f4f7;border-left:3px solid #9aa5b4;padding:10px 14px;margin-top:10px;font-family:Montserrat,sans-serif;font-size:11px;color:#6b7a8d;line-height:1.7;\">\n            \ud83d\udccb This is the <strong>standard price<\/strong> for this service. If the space requires extra work upon arrival, we will discuss any adjustment with you before proceeding \u2014 you only pay the difference after the service.\n          <\/div>\n        <\/div>\n\n        <!-- STEP 2 -->\n        <div id=\"sfqPanel2\" class=\"sfq-panel\">\n          <div class=\"sfq-section-title\">Contact<\/div>\n          <div class=\"sfq-g2\" style=\"margin-bottom:14px;\">\n            <div class=\"sfq-field\"><label>First Name *<\/label><input type=\"text\" id=\"sfqFn\" autocomplete=\"given-name\" placeholder=\"Jane\"><\/div>\n            <div class=\"sfq-field\"><label>Last Name *<\/label><input type=\"text\" id=\"sfqLn\" autocomplete=\"family-name\" placeholder=\"Doe\"><\/div>\n            <div class=\"sfq-field\"><label>Phone *<\/label><input type=\"tel\" id=\"sfqPh\" autocomplete=\"tel\" placeholder=\"+1 617 000 0000\"><\/div>\n            <div class=\"sfq-field\"><label>Email *<\/label><input type=\"email\" id=\"sfqEm\" autocomplete=\"email\" placeholder=\"jane@email.com\"><\/div>\n          <\/div>\n          <div class=\"sfq-section-title\">Address <span style=\"font-family:Montserrat,sans-serif;font-size:11px;font-weight:600;letter-spacing:0;text-transform:none;color:#9aa5b4;\">(Massachusetts only)<\/span><\/div>\n          <div class=\"sfq-field full\" style=\"margin-bottom:10px;\">\n            <label>Street Address *<\/label>\n            <div class=\"sfq-acwrap\">\n              <input type=\"text\" id=\"sfqAd\" placeholder=\"123 Main Street\" autocomplete=\"off\" oninput=\"sfqAddrIn(this.value)\">\n              <div class=\"sfq-sugg\" id=\"sfqSugg\"><\/div>\n            <\/div>\n            <div class=\"sfq-zone-err\" id=\"sfqZoneErr\">Sorry, we only service Massachusetts.<\/div>\n          <\/div>\n          <div class=\"sfq-g3\" style=\"margin-bottom:14px;\">\n            <div class=\"sfq-field\"><label>Apt \/ Unit<\/label><input type=\"text\" id=\"sfqApt\" placeholder=\"Apt 2B\" autocomplete=\"address-line2\"><\/div>\n            <div class=\"sfq-field\"><label>City *<\/label><input type=\"text\" id=\"sfqCity\" placeholder=\"Boston\" autocomplete=\"address-level2\"><\/div>\n            <div class=\"sfq-field\"><label>Zip Code *<\/label><input type=\"text\" id=\"sfqZp\" placeholder=\"02101\" autocomplete=\"postal-code\"><\/div>\n          <\/div>\n          <input type=\"hidden\" id=\"sfqState\" value=\"Massachusetts\">\n          <div class=\"sfq-section-title\">Appointment<\/div>\n          <div class=\"sfq-field\" style=\"margin-bottom:12px;\">\n            <label>Preferred Date<\/label>\n            <div class=\"sfq-calwrap\">\n              <div id=\"sfqDtTrigger\" style=\"display:flex;align-items:center;border:1.5px solid #e4e4e4;background:#fff;cursor:pointer;padding:0;user-select:none;\" onclick=\"sfqToggleCalendar()\">\n                <input type=\"text\" id=\"sfqDt\" placeholder=\"Select a date\" readonly style=\"cursor:pointer;flex:1;border:none;outline:none;padding:10px 12px;font-family:Montserrat,sans-serif;font-size:16px;background:transparent;pointer-events:none;\">\n              <\/div>\n              <div class=\"sfq-cal\" id=\"sfqCal\">\n                <div class=\"sfq-calh\">\n                  <button class=\"sfq-calnav\" onclick=\"sfqCalPrev(event)\" type=\"button\">&#8249;<\/button>\n                  <span class=\"sfq-calt\" id=\"sfqCalTitle\"><\/span>\n                  <button class=\"sfq-calnav\" onclick=\"sfqCalNext(event)\" type=\"button\">&#8250;<\/button>\n                <\/div>\n                <div class=\"sfq-calg\" id=\"sfqCalGrid\"><\/div>\n              <\/div>\n            <\/div>\n          <\/div>\n          <div class=\"sfq-field\" style=\"margin-bottom:14px;\">\n            <label>Preferred Time<\/label>\n            <select id=\"sfqTm\"><option value=\"\">\u2014 Select time \u2014<\/option><\/select>\n          <\/div>\n          <div class=\"sfq-field full\"><label>Note<\/label><textarea id=\"sfqMsg\" placeholder=\"Special requests, access instructions...\"><\/textarea><\/div>\n          <!-- Optional photo upload for price estimation -->\n          <div class=\"sfq-field full\" style=\"margin-top:6px;\">\n            <label>Photos <span style=\"font-weight:400;text-transform:none;letter-spacing:0;color:#9aa5b4;\">(Optional \u2014 helps us give you a more accurate estimate)<\/span><\/label>\n            <div class=\"sfq-photo-wrap\" id=\"sfqPhotoWrap\">\n              <input type=\"file\" id=\"sfqPhotos\" accept=\"image\/*\" multiple onchange=\"sfqPreviewPhotos(this)\">\n              <div id=\"sfqPhotoPlaceholder\">\n                <div style=\"font-size:28px;margin-bottom:6px;\">\ud83d\udcf7<\/div>\n                <div style=\"font-family:Montserrat,sans-serif;font-size:12px;font-weight:700;color:#9aa5b4;\">Tap to add photos of the space<\/div>\n                <div style=\"font-size:10px;color:#b0b8c4;margin-top:3px;\">JPG, PNG \u2014 up to 5 photos<\/div>\n              <\/div>\n              <div class=\"sfq-photo-preview\" id=\"sfqPhotoPreview\"><\/div>\n            <\/div>\n          <\/div>\n          <div class=\"sfq-nav\">\n            <button class=\"sfq-btn-back\" type=\"button\" onclick=\"sfqGoStep(1)\">\u2190 Back<\/button>\n            <button class=\"sfq-btn-next\" type=\"button\" style=\"background:#c1ff72;color:#073e64;\" onclick=\"sfqGoStep(3)\">Continue \u2192<\/button>\n          <\/div>\n        <\/div>\n\n        <!-- STEP 3 -->\n        <div id=\"sfqPanel3\" class=\"sfq-panel\">\n          <div class=\"sfq-summary\">\n            <div class=\"sfq-sum-title\">Booking Summary<\/div>\n            <div class=\"sfq-srow\"><span>Service<\/span><span id=\"sumSvc\">\u2014<\/span><\/div>\n            <div class=\"sfq-srow\"><span>Items<\/span><span id=\"sumItems\" style=\"text-align:right;max-width:55%;font-size:12px;\">\u2014<\/span><\/div>\n            <div class=\"sfq-srow\"><span>Date & Time<\/span><span id=\"sumDt\">\u2014<\/span><\/div>\n            <div class=\"sfq-srow\"><span>Address<\/span><span id=\"sumAd\" style=\"text-align:right;max-width:55%;font-size:12px;\">\u2014<\/span><\/div>\n            <div class=\"sfq-srow\"><span>Total<\/span><span id=\"sumTot\">\u2014<\/span><\/div>\n            <div class=\"sfq-srow\" id=\"sumDepRow\" style=\"display:none;\"><span>Deposit due now<\/span><span id=\"sumDep\" style=\"color:#073e64;font-weight:800;\">\u2014<\/span><\/div>\n          <\/div>\n          <div style=\"background:#fff8e1;border-left:3px solid #f59e0b;padding:10px 14px;margin-bottom:14px;font-family:Montserrat,sans-serif;font-size:11px;color:#073e64;line-height:1.7;\">\n            \u2705 <strong>Standard Price:<\/strong> This is our normal rate for this service. Your deposit secures the booking. If upon arrival the space requires extra work, we will discuss any additional charge with you directly \u2014 you only pay the difference after the service is done.\n          <\/div>\n\n                              <!-- Stripe Payment Request (Apple Pay \/ Google Pay) -->\n          <div id=\"sfqPrBtn\" style=\"margin-bottom:12px;display:none;\">\n            <div id=\"sfq-payment-request-btn\"><\/div>\n            <div id=\"sfqPrDivider\" style=\"display:none;align-items:center;gap:8px;margin:10px 0;\"><div style=\"flex:1;height:1px;background:#e4e4e4;\"><\/div><span style=\"font-family:Montserrat,sans-serif;font-size:11px;color:#9aa5b4;font-weight:700;\">OR PAY WITH CARD<\/span><div style=\"flex:1;height:1px;background:#e4e4e4;\"><\/div><\/div>\n          <\/div>\n          <!-- Stripe Card Element -->\n          <div id=\"sfqCardWrap\" style=\"margin-bottom:14px;\">\n            <div style=\"font-family:Montserrat,sans-serif;font-size:10px;font-weight:800;color:#073e64;text-transform:uppercase;letter-spacing:1px;margin-bottom:7px;\">Card Details<\/div>\n            <div id=\"sfq-card-el\" style=\"border:1.5px solid #e4e4e4;padding:13px 12px;background:#fff;transition:border-color .2s;\"><\/div>\n            <div id=\"sfq-card-err\" style=\"color:#e74c3c;font-size:12px;margin-top:5px;min-height:14px;\"><\/div>\n            <div style=\"margin-top:6px;font-family:Montserrat,sans-serif;font-size:10px;color:#9aa5b4;display:flex;align-items:center;gap:5px;\">\n              <svg width=\"11\" height=\"11\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#25D366\" stroke-width=\"2.5\"><path d=\"M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z\"\/><\/svg>\n              Secured by Stripe \u2014 your card is <strong style=\"color:#073e64;\">never saved<\/strong> on our servers\n            <\/div>\n          <\/div>\n          \n          <button class=\"sfq-btn-back\" type=\"button\" onclick=\"sfqGoStep(2)\" style=\"width:100%;margin-bottom:10px;\">\u2190 Back<\/button>\n          <button class=\"sfq-submit\" id=\"sfqBtn\" style=\"width:100%;padding:16px;font-family:Montserrat,sans-serif;font-size:14px;font-weight:800;letter-spacing:2px;text-transform:uppercase;border:none;cursor:pointer;background:#c1ff72;color:#073e64;\" onclick=\"sfqSubmit()\">Pay Deposit &amp; Confirm<\/button>\n\n        <\/div>\n      <\/div>\n\n      <div class=\"sfq-done\" id=\"sfqDone\">\n        <h3>BOOKING CONFIRMED! \u2713<\/h3>\n        <p id=\"sfqDoneName\" style=\"font-size:14px;color:#073e64;font-weight:700;margin:0 0 6px;\"><\/p>\n        <p style=\"font-size:12px;color:#9aa5b4;margin:0 0 16px;\">A confirmation email has been sent to you.<\/p>\n        <a href=\"#\" id=\"sfqWaBtn\" target=\"_blank\" style=\"display:none;font-size:13px;font-weight:700;color:#25D366;text-decoration:none;\">\u2192 Send us a message on WhatsApp<\/a>\n      <\/div>\n    <\/div>\n\n    <script>\n    var SFQD={\n        ajax:\"https:\/\/shinyflowcleaning.com\/wp-admin\/admin-ajax.php\",\n        nonce:\"322635e895\",\n        dpct:50,\n        test:false,\n        pub:\"pk_live_51TONRUPYxe0h8TTocrKOHmh3WFlQCRHOV6GlSrOOpPBfPhzBxdyTVmIaI00lCnYrmfI3myyLuNAQTKa0vel56w6N00UgeEQvNM\",\n        presvc:\"\",\n        items:[],total:0,deposit:0,selDate:'',svcMeta:{},\n        stripe:null,cardEl:null,prButton:null,\n        sched:{blocked:[],advance:1,weeks:8,work_days:['mon','tue','wed','thu','fri'],slots:[]},\n        addrTimer:null\n    };\n    var sfqCY=new Date().getFullYear(),sfqCM=new Date().getMonth();\n    var sfqDK=['sun','mon','tue','wed','thu','fri','sat'];\n    var sfqIsMobile=('ontouchstart' in window)||window.innerWidth<=600;\n\n    document.addEventListener('DOMContentLoaded',function(){\n        var fd=new FormData();fd.append('action','sfq_get_sched');\n        fetch(SFQD.ajax,{method:'POST',body:fd}).then(r=>r.json()).then(res=>{if(res.success){SFQD.sched=res.data;sfqFillTime();}});\n        if(SFQD.presvc) sfqLoadItems();\n        \/\/ Init Stripe \u2014 create element but do NOT mount yet (mount only when step 3 becomes visible)\n        if(SFQD.pub){\n            SFQD.stripe=Stripe(SFQD.pub);\n            var _els=SFQD.stripe.elements();\n            SFQD.cardEl=_els.create('card',{\n                style:{base:{fontFamily:'Montserrat,sans-serif',fontSize:'15px',color:'#073e64','::placeholder':{color:'#b0b8c4'}}},\n                hidePostalCode:false\n            });\n        }\n        \/\/ Calendar \u2014 close on outside click\n        document.addEventListener('click',function(e){\n            var c=document.getElementById('sfqCal');\n            var t=document.getElementById('sfqDtTrigger');\n            if(c&&t&&!c.contains(e.target)&&!t.contains(e.target)) c.classList.remove('show');\n        });\n    });\n\n    function sfqPreviewPhotos(inp){\n        var prev=document.getElementById('sfqPhotoPreview');\n        var ph=document.getElementById('sfqPhotoPlaceholder');\n        prev.innerHTML='';\n        var files=Array.from(inp.files).slice(0,5);\n        if(!files.length){ph.style.display='block';return;}\n        ph.style.display='none';\n        files.forEach(function(f){\n            var img=document.createElement('img');\n            img.className='sfq-photo-thumb';\n            var r=new FileReader();\n            r.onload=function(e){img.src=e.target.result;};\n            r.readAsDataURL(f);\n            prev.appendChild(img);\n        });\n    }\n    function sfqLoadItems(){\n        var svc=document.getElementById('sfqSvc')?document.getElementById('sfqSvc').value:SFQD.presvc;\n        SFQD.items=[];SFQD.svcMeta={};sfqRenderItems();if(!svc)return;\n        var fd=new FormData();fd.append('action','sfs_get_pricing');fd.append('service',svc);\n        fetch(SFQD.ajax,{method:'POST',body:fd}).then(r=>r.json()).then(res=>{\n            if(res.success&&res.data.lines&&res.data.lines.length>0){\n                var d=res.data;\n                SFQD.svcMeta={\n                    pricingNote:   d.pricing_note    || '',\n                    showCondition: d.show_condition  === '1',\n                    showPhotos:    d.show_photos     === '1',\n                    condMedPct:    parseInt(d.cond_medium_pct)||20,\n                    condBadPct:    parseInt(d.cond_bad_pct)||40,\n                    condition:     'good'\n                };\n                SFQD.items=d.lines.map(function(l){\n                    return {\n                        name:    l.name,\n                        price:   parseFloat(l.price)||0,\n                        unit:    l.unit||'',\n                        qty:     parseInt(l.min_qty)||0,\n                        min:     parseInt(l.min_qty)||0,\n                        max:     Math.max(1, parseInt(l.max_qty)||20)\n                    };\n                });\n            } else { SFQD.items=[]; SFQD.svcMeta={}; }\n            sfqRenderItems();sfqCalc();\n        }).catch(()=>{SFQD.items=[];sfqRenderItems();});\n    }\n    function sfqRenderItems(){\n        var w=document.getElementById('sfqItemsWrap');\n        if(!SFQD.items||!SFQD.items.length){\n            w.innerHTML='<div class=\"sfq-no-items\">'+(document.getElementById('sfqSvc')&&document.getElementById('sfqSvc').value?'No items for this service.':'Select a service to see items.')+'<\/div>';\n            return;\n        }\n        var h='';\n        \/\/ Condition selector\n        var m=SFQD.svcMeta||{};\n        if(m.showCondition){\n            var cond=m.condition||'good';\n            h+='<div style=\"margin-bottom:14px;\">';\n            h+='<div style=\"font-family:Montserrat,sans-serif;font-size:10px;font-weight:800;color:#073e64;text-transform:uppercase;letter-spacing:1px;margin-bottom:8px;\">Space Condition<\/div>';\n            h+='<div style=\"display:flex;gap:8px;flex-wrap:wrap;\">';\n            [['good','\u2705 Good','Base price'],\n             ['medium','\u26a0\ufe0f Average','+'+m.condMedPct+'%'],\n             ['bad','\ud83d\udd34 Poor','+'+m.condBadPct+'%']].forEach(function(opt){\n                var sel=cond===opt[0];\n                h+='<button type=\"button\" onclick=\"sfqSetCond(\\''+opt[0]+'\\')\" style=\"flex:1;padding:9px 6px;border:1.5px solid '+(sel?'#073e64':'#e4e4e4')+';background:'+(sel?'#073e64':'#fff')+';color:'+(sel?'#fff':'#073e64')+';font-family:Montserrat,sans-serif;font-size:11px;font-weight:800;cursor:pointer;text-align:center;transition:all .12s;\">';\n                h+='<div>'+opt[1]+'<\/div><div style=\"font-size:9px;font-weight:600;opacity:.7;margin-top:2px;\">'+opt[2]+'<\/div>';\n                h+='<\/button>';\n            });\n            h+='<\/div><\/div>';\n        }\n        \/\/ Items\n        SFQD.items.forEach(function(item,i){\n            h+='<div class=\"sfq-line-item\">';\n            h+='<div class=\"sfq-line-info\"><div class=\"sfq-line-name\">'+item.name+'<\/div><div class=\"sfq-line-price\">$'+item.price.toFixed(2)+(item.unit?' \/ '+item.unit:'')+'<\/div><\/div>';\n            h+='<div class=\"sfq-line-stepper\"><button type=\"button\" onclick=\"sfqStep('+i+',-1)\">\u2212<\/button><span id=\"sfq-q-'+i+'\">'+item.qty+'<\/span><button type=\"button\" onclick=\"sfqStep('+i+',1)\">+<\/button><\/div>';\n            h+='<div class=\"sfq-line-subtotal\" id=\"sfq-s-'+i+'\">$'+(item.qty*item.price).toFixed(2)+'<\/div>';\n            h+='<\/div>';\n        });\n        \/\/ Photo upload hint from service setting\n        if(m.showPhotos){\n            document.getElementById('sfqPhotos')&&(document.getElementById('sfqPhotos').closest('.sfq-field').style.display='flex');\n        }\n        \/\/ Pricing note from service\n        if(m.pricingNote){\n            h+='<div style=\"background:#fff8e1;border-left:3px solid #f59e0b;padding:10px 13px;font-family:Montserrat,sans-serif;font-size:11px;color:#073e64;line-height:1.7;margin-top:8px;\">\u26a0\ufe0f '+m.pricingNote+'<\/div>';\n        }\n        w.innerHTML=h;\n    }\n    function sfqSetCond(c){\n        SFQD.svcMeta.condition=c;\n        sfqRenderItems();sfqCalc();\n    }\n    function sfqCondMultiplier(){\n        var m=SFQD.svcMeta||{};\n        if(m.condition==='medium') return 1+(m.condMedPct||20)\/100;\n        if(m.condition==='bad')    return 1+(m.condBadPct||40)\/100;\n        return 1;\n    }\n    function sfqStep(i,dir){\n        var item=SFQD.items[i];\n        item.qty=Math.min(item.max,Math.max(item.min||0,item.qty+dir));\n        var q=document.getElementById('sfq-q-'+i),s=document.getElementById('sfq-s-'+i);\n        var mult=sfqCondMultiplier();\n        if(q)q.textContent=item.qty;\n        if(s)s.textContent='$'+(item.qty*item.price*mult).toFixed(2);\n        sfqCalc();\n    }\n    function sfqCalc(){\n        var mult=sfqCondMultiplier();\n        var base=0;SFQD.items.forEach(function(item){base+=item.qty*item.price;});\n        var total=base*mult;\n        SFQD.total=total;SFQD.deposit=Math.round(total*(SFQD.dpct\/100)*100)\/100;\n        if(total>0){\n            document.getElementById('sfqEstBox').style.display='block';\n            document.getElementById('sfqEstTotal').textContent='$'+total.toFixed(2)+(mult>1?' (includes condition surcharge)':'');\n            document.getElementById('sfqEstDep').textContent='Deposit ('+SFQD.dpct+'%): $'+SFQD.deposit.toFixed(2);\n            document.getElementById('sfqCont1').style.display='block';\n            document.getElementById('sfqPriceNote').style.display='block';\n        } else {\n            document.getElementById('sfqEstBox').style.display='none';\n            document.getElementById('sfqCont1').style.display='none';\n            document.getElementById('sfqPriceNote').style.display='none';\n        }\n    }\n    function sfqGoStep(n){\n        if(n===3){\n            var fn=document.getElementById('sfqFn').value.trim(),ln=document.getElementById('sfqLn').value.trim();\n            var ph=document.getElementById('sfqPh').value.trim(),em=document.getElementById('sfqEm').value.trim();\n            var ad=document.getElementById('sfqAd').value.trim(),ct=document.getElementById('sfqCity').value.trim();\n            var zp=document.getElementById('sfqZp').value.trim();\n            sfqHideErr();\n            if(!fn||!ln||!ph||!em||!ad||!ct||!zp){sfqErr('Please fill in all required fields.');return;}\n            if(!\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(em)){sfqErr('Please enter a valid email address.');return;}\n            if(document.getElementById('sfqZoneErr').classList.contains('show')){sfqErr('Sorry, your address is outside our service zone.');return;}\n            \/\/ Build summary\n            var svc=SFQD.presvc||(document.getElementById('sfqSvc')?document.getElementById('sfqSvc').value:'');\n            var dt=document.getElementById('sfqDt').value.trim(),tm=document.getElementById('sfqTm').value.trim();\n            var apt=document.getElementById('sfqApt').value.trim();\n            var fullAddr=[ad,apt?'Apt '+apt:'',ct,'Massachusetts',zp].filter(Boolean).join(', ');\n            var itemLines=[];SFQD.items.forEach(item=>{if(item.qty>0)itemLines.push(item.qty+'\u00d7 '+item.name);});\n            document.getElementById('sumSvc').textContent=svc;\n            document.getElementById('sumItems').innerHTML=itemLines.length?itemLines.join('<br>'):'\u2014';\n            document.getElementById('sumDt').textContent=(dt||'TBD')+(tm?' at '+tm:'');\n            document.getElementById('sumAd').textContent=fullAddr;\n            document.getElementById('sumTot').textContent='$'+SFQD.total.toFixed(2)+(SFQD.deposit>0?' (deposit: $'+SFQD.deposit.toFixed(2)+')':'');\n        }\n        for(var i=1;i<=3;i++){\n            var p=document.getElementById('sfqPanel'+i),ind=document.getElementById('sfqSI'+i);\n            if(p)p.classList.remove('active');\n            if(ind){ind.classList.remove('active');if(i<n)ind.classList.add('done');else ind.classList.remove('done');}\n        }\n        var ap=document.getElementById('sfqPanel'+n),ai=document.getElementById('sfqSI'+n);\n        if(ap)ap.classList.add('active');if(ai)ai.classList.add('active');\n        window.scrollTo({top:0,behavior:'smooth'});\n        \/\/ When entering step 3, mount Stripe if available\n        if(n===3 && SFQD.stripe && SFQD.cardEl){\n            setTimeout(function(){\n                var cardEl=document.getElementById('sfq-card-el');\n                if(cardEl && !cardEl.dataset.mounted){\n                    SFQD.cardEl.mount('#sfq-card-el');\n                    cardEl.dataset.mounted='1';\n                    SFQD.cardEl.on('change',function(e){\n                        var err=document.getElementById('sfq-card-err');\n                        if(err) err.textContent=e.error?e.error.message:'';\n                    });\n                }\n                \/\/ Payment Request Button\n                if(!SFQD.prButton){\n                    var pr=SFQD.stripe.paymentRequest({\n                        country:'US',currency:'usd',\n                        total:{label:'Shiny Flow Deposit',amount:Math.round(SFQD.deposit*100)},\n                        requestPayerName:true,requestPayerEmail:true\n                    });\n                    var prEl=SFQD.stripe.elements().create('paymentRequestButton',{paymentRequest:pr,style:{paymentRequestButton:{type:'default',theme:'dark',height:'48px'}}});\n                    pr.canMakePayment().then(function(result){\n                        if(result){\n                            var wrap=document.getElementById('sfqPrBtn');\n                            var divider=document.getElementById('sfqPrDivider');\n                            if(wrap){wrap.style.display='block';prEl.mount('#sfq-payment-request-btn');}\n                            if(divider){divider.style.display='flex';}\n                            pr.on('paymentmethod',function(ev){\n                                sfqGetIntentAndConfirm(ev);\n                            });\n                        }\n                    });\n                    SFQD.prButton=pr;\n                }\n                \/\/ Update deposit row in summary\n                var depRow=document.getElementById('sumDepRow');\n                var depEl=document.getElementById('sumDep');\n                if(depRow && SFQD.deposit>0){depRow.style.display='flex';if(depEl)depEl.textContent='$'+SFQD.deposit.toFixed(2);}\n            },100);\n        }\n    }\n    \/\/ Address autocomplete \u2014 Massachusetts ONLY\n    function sfqAddrIn(v){\n        clearTimeout(SFQD.addrTimer);\n        var zErr=document.getElementById('sfqZoneErr');\n        var s=document.getElementById('sfqSugg');\n        zErr.classList.remove('show');\n        if(!v||v.length<3){s.classList.remove('show');return;}\n        SFQD.addrTimer=setTimeout(function(){\n            \/\/ Search restricted to Massachusetts, US\n            var url='https:\/\/nominatim.openstreetmap.org\/search?format=json&addressdetails=1&countrycodes=us&state=Massachusetts&q='+encodeURIComponent(v)+'&limit=6';\n            fetch(url,{headers:{'Accept-Language':'en','User-Agent':'ShinyFlowBooking\/1.0'}})\n            .then(r=>r.json()).then(rs=>{\n                s.innerHTML='';\n                if(!rs||!rs.length){s.classList.remove('show');return;}\n                rs.forEach(function(r){\n                    \/\/ Only show Massachusetts results\n                    var st=(r.address&&r.address.state)||'';\n                    if(st && st.toLowerCase().indexOf('massachusetts')===-1) return;\n                    var d=document.createElement('div');\n                    d.className='sfq-si';\n                    \/\/ Build clean display label\n                    var a=r.address||{};\n                    var street=(a.house_number?(a.house_number+' '):'')+( a.road||'');\n                    var city=a.city||a.town||a.village||a.suburb||'';\n                    var zip=a.postcode||'';\n                    var label=[street,city,'MA',zip].filter(Boolean).join(', ');\n                    if(!label) label=r.display_name;\n                    d.textContent=label;\n                    d.onclick=function(){\n                        \/\/ Auto-fill all fields\n                        document.getElementById('sfqAd').value=street||r.display_name.split(',')[0];\n                        document.getElementById('sfqCity').value=city;\n                        document.getElementById('sfqZp').value=zip;\n                        document.getElementById('sfqState').value='Massachusetts';\n                        s.classList.remove('show');\n                        \/\/ Validate \u2014 must be Massachusetts\n                        if(st && st.toLowerCase().indexOf('massachusetts')===-1){\n                            zErr.textContent='Sorry, we only service Massachusetts.';\n                            zErr.classList.add('show');\n                        } else {\n                            zErr.classList.remove('show');\n                        }\n                    };\n                    s.appendChild(d);\n                });\n                if(s.children.length>0) s.classList.add('show');\n                else s.classList.remove('show');\n            }).catch(()=>{s.classList.remove('show');});\n        },400);\n    }\n    \/\/ Close suggestions on outside click\n    document.addEventListener('click',function(e){\n        var s=document.getElementById('sfqSugg');\n        if(s&&!s.contains(e.target)&&e.target!==document.getElementById('sfqAd')) s.classList.remove('show');\n    });\n    \/\/ Validate state when user manually types without selecting suggestion\n    document.getElementById('sfqCity') && document.getElementById('sfqCity').addEventListener('blur',function(){\n        var ad=document.getElementById('sfqAd').value.trim();\n        var ct=document.getElementById('sfqCity').value.trim();\n        var zErr=document.getElementById('sfqZoneErr');\n        if(ad && ct){\n            \/\/ Quick check: search to confirm MA\n            var url='https:\/\/nominatim.openstreetmap.org\/search?format=json&addressdetails=1&countrycodes=us&q='+encodeURIComponent(ad+' '+ct+' MA')+'&limit=1';\n            fetch(url,{headers:{'Accept-Language':'en'}}).then(r=>r.json()).then(rs=>{\n                if(rs&&rs[0]){\n                    var st=(rs[0].address&&rs[0].address.state)||'';\n                    if(st && st.toLowerCase().indexOf('massachusetts')===-1){\n                        zErr.textContent='Sorry, we only service Massachusetts.';\n                        zErr.classList.add('show');\n                    }\n                }\n            }).catch(()=>{});\n        }\n    });\n    function sfqCheckZone(addr,state){\n        var zErr=document.getElementById('sfqZoneErr');\n        if(state && state.toLowerCase().indexOf('massachusetts')===-1){\n            zErr.textContent='Sorry, we only service Massachusetts.';\n            zErr.classList.add('show'); return;\n        }\n        zErr.classList.remove('show');\n    }\n    function sfqFillTime(){\n        var sel=document.getElementById('sfqTm');\n        sel.innerHTML='<option value=\"\">\u2014 Select time \u2014<\/option>';\n        SFQD.sched.slots.forEach(function(t){\n            var o=document.createElement('option');\n            o.value=t;\n            o.textContent=t;\n            sel.appendChild(o);\n        });\n    }\n    \/\/ Calendar\n    function sfqToggleCalendar(){\n        var c=document.getElementById('sfqCal');\n        if(c.classList.contains('show')){\n            c.classList.remove('show');\n        } else {\n            sfqRenderCal();\n            c.classList.add('show');\n        }\n    }\n    function sfqRenderCal(){\n        var mn=['January','February','March','April','May','June','July','August','September','October','November','December'];\n        document.getElementById('sfqCalTitle').textContent=mn[sfqCM]+' '+sfqCY;\n        var g=document.getElementById('sfqCalGrid');g.innerHTML='';\n        ['Su','Mo','Tu','We','Th','Fr','Sa'].forEach(d=>{var e=document.createElement('div');e.className='sfq-cdn';e.textContent=d;g.appendChild(e);});\n        var first=new Date(sfqCY,sfqCM,1).getDay(),dim=new Date(sfqCY,sfqCM+1,0).getDate();\n        var today=new Date();today.setHours(0,0,0,0);\n        var minD=new Date(today);minD.setDate(minD.getDate()+(SFQD.sched.advance||1));\n        var maxD=new Date(today);maxD.setDate(maxD.getDate()+(SFQD.sched.weeks||8)*7);\n        for(var i=0;i<first;i++){var e=document.createElement('div');e.className='sfq-cd emp';g.appendChild(e);}\n        for(var d=1;d<=dim;d++){\n            var el=document.createElement('div');el.className='sfq-cd';el.textContent=d;\n            var td=new Date(sfqCY,sfqCM,d);\n            var mm=String(sfqCM+1).padStart(2,'0'),dd=String(d).padStart(2,'0');\n            var ds=sfqCY+'-'+mm+'-'+dd,dk=sfqDK[td.getDay()];\n            var bad=SFQD.sched.blocked.indexOf(ds)>=0||SFQD.sched.work_days.indexOf(dk)<0||td<minD||td>maxD;\n            if(bad){el.classList.add('off');}\n            else{\n                if(ds===SFQD.selDate)el.classList.add('sel');\n                (function(x){el.addEventListener('click',function(e){\n                    e.stopPropagation();SFQD.selDate=x;document.getElementById('sfqDt').value=x;\n                    document.getElementById('sfqCal').classList.remove('show');\n                    sfqRenderCal();\n                });})(ds);\n            }\n            g.appendChild(el);\n        }\n    }\n    function sfqCalPrev(e){e.preventDefault();e.stopPropagation();sfqCM--;if(sfqCM<0){sfqCM=11;sfqCY--;}sfqRenderCal();}\n    function sfqCalNext(e){e.preventDefault();e.stopPropagation();sfqCM++;if(sfqCM>11){sfqCM=0;sfqCY++;}sfqRenderCal();}\n\n    \/\/ Collect form data helper\n    function sfqGetFormData(){\n        return {\n            svc:  SFQD.presvc||(document.getElementById('sfqSvc')?document.getElementById('sfqSvc').value:''),\n            fn:   document.getElementById('sfqFn').value.trim(),\n            ln:   document.getElementById('sfqLn').value.trim(),\n            ph:   document.getElementById('sfqPh').value.trim(),\n            em:   document.getElementById('sfqEm').value.trim(),\n            ad:   document.getElementById('sfqAd').value.trim(),\n            apt:  document.getElementById('sfqApt').value.trim(),\n            city: document.getElementById('sfqCity').value.trim(),\n            zp:   document.getElementById('sfqZp').value.trim(),\n            dt:   document.getElementById('sfqDt').value.trim(),\n            tm:   document.getElementById('sfqTm').value.trim(),\n            mg:   document.getElementById('sfqMsg').value.trim()\n        };\n    }\n    function sfqValidate(d){\n        sfqHideErr();\n        if(!d.fn||!d.ln||!d.ph||!d.em||!d.ad||!d.city||!d.zp){sfqErr('Please fill in all required fields.');return false;}\n        if(!\/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(d.em)){sfqErr('Please enter a valid email address.');return false;}\n        if(document.getElementById('sfqZoneErr').classList.contains('show')){sfqErr('Sorry, your address is outside our service zone.');return false;}\n        return true;\n    }\n    function sfqGetItemLines(){\n        var lines=[];\n        var mult=sfqCondMultiplier();\n        var m=SFQD.svcMeta||{};\n        SFQD.items.forEach(function(i){\n            if(i.qty>0) lines.push(i.qty+'x '+i.name+' @ $'+i.price.toFixed(2)+' = $'+(i.qty*i.price).toFixed(2));\n        });\n        if(mult>1&&m.condition){\n            var label=m.condition==='medium'?'Average (+'+m.condMedPct+'%)':'Poor (+'+m.condBadPct+'%)';\n            lines.push('Condition: '+label+' \u2014 subtotal \u00d7 '+mult.toFixed(2));\n        }\n        return lines.join('\\n');\n    }\n    \/\/ Save booking + show success\n    function sfqSaveBooking(pi,d,_retry){\n        var fd=new FormData();\n        fd.append('action','sfq_book');fd.append('nonce',SFQD.nonce);\n        fd.append('service',d.svc);fd.append('fn',d.fn);fd.append('ln',d.ln);fd.append('ph',d.ph);fd.append('em',d.em);\n        fd.append('ad',d.ad);fd.append('apt',d.apt);fd.append('city',d.city);fd.append('state','Massachusetts');fd.append('zp',d.zp);\n        fd.append('items',sfqGetItemLines());fd.append('total',SFQD.total.toFixed(2));fd.append('deposit',SFQD.deposit.toFixed(2));\n        fd.append('date',d.dt);fd.append('time',d.tm);fd.append('msg',d.mg);fd.append('pi',pi||'');\n        \/\/ Attach photos if uploaded\n        var photoInp=document.getElementById('sfqPhotos');\n        if(photoInp&&photoInp.files.length){\n            var files=Array.from(photoInp.files).slice(0,5);\n            files.forEach(function(f,i){fd.append('photo_'+i,f,f.name);});\n            fd.append('photo_count',files.length);\n        }\n        fetch(SFQD.ajax,{method:'POST',body:fd}).then(r=>r.text()).then(raw=>{\n            var res;try{var j=raw.indexOf('{');res=JSON.parse(j>=0?raw.slice(j):raw);}catch(e){sfqErr('Server error.');sfqResetBtn();return;}\n            if(res.success){\n                var r=res.data;\n                document.getElementById('sfqDoneName').textContent='Thank you, '+r.name+'! Your '+r.service+' is booked'+(r.date?' for '+r.date:'.')+'.';\n                document.getElementById('sfqFormWrap').style.display='none';\n                document.getElementById('sfqDone').classList.add('show');\n                if(r.wa_url){\n                    var wb=document.getElementById('sfqWaBtn');\n                    wb.href=r.wa_url;\n                    wb.style.display='inline-block';\n                    \/\/ Auto-open WhatsApp 1.2s after confirmation so user sees the success message first\n                    setTimeout(function(){ window.open(r.wa_url,'_blank'); },1200);\n                }\n                window.scrollTo({top:0,behavior:'smooth'});\n            } else {\n                if(res.data&&res.data.expired&&!_retry){sfqRefreshNonce(function(){sfqSaveBooking(pi,d,true);});return;}\n                sfqErr((res.data&&res.data.msg)?res.data.msg:'Error. Please try again.');\n                sfqResetBtn();\n            }\n        }).catch(()=>{sfqErr('Connection error.');sfqResetBtn();});\n    }\n    function sfqResetBtn(){\n        var btn=document.getElementById('sfqBtn');\n        if(btn){btn.disabled=false;btn.textContent=SFQD.pub&&!SFQD.test?'Pay Deposit & Confirm':'Confirm Booking';}\n    }\n    function sfqSubmit(){\n        var btn=document.getElementById('sfqBtn');\n        var d=sfqGetFormData();\n        \/\/ Validate FIRST \u2014 before disabling button\n        if(!sfqValidate(d)) return;\n        \/\/ Check card filled if Stripe active\n        if(SFQD.pub && !SFQD.test && SFQD.stripe && SFQD.cardEl && SFQD.deposit>0){\n            var cardErr=document.getElementById('sfq-card-err');\n            if(cardErr && cardErr.textContent){sfqErr('Please fix the card error: '+cardErr.textContent);return;}\n        }\n        btn.disabled=true;\n        btn.textContent='Processing...';\n        \/\/ Failsafe: re-enable after 45s if no response\n        var _failsafe=setTimeout(function(){\n            sfqErr('Request timed out. Please check your connection and try again.');\n            sfqResetBtn();\n        },45000);\n        var _origReset=window.sfqResetBtn;\n        window.sfqResetBtn=function(){clearTimeout(_failsafe);window.sfqResetBtn=_origReset;_origReset();};\n        \/\/ Stripe payment path\n        if(SFQD.pub && !SFQD.test && SFQD.stripe && SFQD.cardEl && SFQD.deposit>0){\n            sfqGetIntentAndConfirmCard(d);\n        } else {\n            sfqSaveBooking('',d);\n        }\n    }\n    function sfqRefreshNonce(cb){\n        var fd=new FormData();fd.append('action','sfq_refresh_nonce');\n        fetch(SFQD.ajax,{method:'POST',body:fd}).then(r=>r.json()).then(function(res){\n            if(res.success&&res.data&&res.data.nonce){SFQD.nonce=res.data.nonce;}\n            cb();\n        }).catch(function(){cb();});\n    }\n    \/\/ Stripe: create PaymentIntent then confirm with card element\n    function sfqGetIntentAndConfirmCard(d,_retry){\n        var fd=new FormData();fd.append('action','sfq_pay');fd.append('nonce',SFQD.nonce);\n        fd.append('amount',SFQD.deposit.toFixed(2));fd.append('email',d.em);\n        fetch(SFQD.ajax,{method:'POST',body:fd}).then(r=>r.json()).then(function(res){\n            if(!res.success){\n                if(res.data&&res.data.expired&&!_retry){sfqRefreshNonce(function(){sfqGetIntentAndConfirmCard(d,true);});return;}\n                sfqErr(res.data&&res.data.msg?res.data.msg:'Payment setup failed. Please try again.');sfqResetBtn();return;\n            }\n            var cs=res.data.cs;\n            if(!cs){sfqErr('Payment setup failed. Please try again.');sfqResetBtn();return;}\n            SFQD.stripe.confirmCardPayment(cs,{\n                payment_method:{card:SFQD.cardEl,billing_details:{name:d.fn+' '+d.ln,email:d.em}},\n                return_url: window.location.href\n            }).then(function(r){\n                if(r.error){\n                    sfqErr(r.error.message||'Your card was declined. Please check your details.');\n                    sfqResetBtn();\n                    return;\n                }\n                var pi=r.paymentIntent;\n                if(!pi){sfqErr('No payment response received. Please try again.');sfqResetBtn();return;}\n                if(pi.status==='succeeded'||pi.status==='requires_capture'){\n                    sfqSaveBooking(pi.id,d);\n                } else if(pi.status==='requires_action'){\n                    SFQD.stripe.handleNextAction(cs).then(function(r2){\n                        if(r2.error){sfqErr(r2.error.message||'Authentication failed. Please try again.');sfqResetBtn();return;}\n                        var pi2=r2.paymentIntent;\n                        if(pi2&&(pi2.status==='succeeded'||pi2.status==='requires_capture')){\n                            sfqSaveBooking(pi2.id,d);\n                        } else if(pi2&&pi2.status==='requires_confirmation'){\n                            SFQD.stripe.confirmCardPayment(cs).then(function(r3){\n                                if(r3.error){sfqErr(r3.error.message||'Payment failed after authentication.');sfqResetBtn();}\n                                else if(r3.paymentIntent&&(r3.paymentIntent.status==='succeeded'||r3.paymentIntent.status==='requires_capture')){sfqSaveBooking(r3.paymentIntent.id,d);}\n                                else{sfqErr('Payment could not complete. Please try again or contact us.');sfqResetBtn();}\n                            }).catch(function(){sfqErr('Payment error after authentication. Please try again.');sfqResetBtn();});\n                        } else {\n                            sfqErr('Could not complete payment. Status: '+(pi2?pi2.status:'unknown'));sfqResetBtn();\n                        }\n                    }).catch(function(){sfqErr('Authentication error. Please try again.');sfqResetBtn();});\n                } else {\n                    sfqErr('Payment status: '+pi.status+'. Please contact us if your card was charged.');sfqResetBtn();\n                }\n            }).catch(function(e){\n                sfqErr('Payment error: '+(e&&e.message?e.message:'Please try again.'));sfqResetBtn();\n            });\n        }).catch(function(){sfqErr('Connection error. Please check your internet and try again.');sfqResetBtn();});\n    }\n    \/\/ Stripe: Payment Request (Apple Pay \/ Google Pay) handler\n    function sfqGetIntentAndConfirm(ev){\n        var d=sfqGetFormData();\n        if(!sfqValidate(d)){ev.complete('fail');return;}\n        var fd=new FormData();fd.append('action','sfq_pay');fd.append('nonce',SFQD.nonce);\n        fd.append('amount',SFQD.deposit.toFixed(2));fd.append('email',ev.payerEmail||d.em);\n        fetch(SFQD.ajax,{method:'POST',body:fd}).then(r=>r.json()).then(res=>{\n            if(!res.success){ev.complete('fail');sfqErr(res.data.msg||'Payment error.');return;}\n            SFQD.stripe.confirmCardPayment(res.data.cs,{payment_method:ev.paymentMethod.id},{handleActions:false})\n            .then(function(r){\n                if(r.error){ev.complete('fail');sfqErr(r.error.message);}\n                else{\n                    ev.complete('success');\n                    if(r.paymentIntent.status==='requires_action'){\n                        SFQD.stripe.confirmCardPayment(res.data.cs).then(function(r2){\n                            if(r2.error) sfqErr(r2.error.message);\n                            else sfqSaveBooking(r2.paymentIntent.id,d);\n                        });\n                    } else {\n                        sfqSaveBooking(r.paymentIntent.id,d);\n                    }\n                }\n            });\n        }).catch(()=>{ev.complete('fail');sfqErr('Payment error.');});\n    }\n    function sfqErr(m){var e=document.getElementById('sfqErr');e.textContent=m;e.classList.add('show');e.scrollIntoView({behavior:'smooth',block:'center'});}\n    function sfqHideErr(){document.getElementById('sfqErr').classList.remove('show');}\n    <\/script>\n    <\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"class_list":["post-185","page","type-page","status-publish","hentry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=\/wp\/v2\/pages\/185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=185"}],"version-history":[{"count":4,"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=\/wp\/v2\/pages\/185\/revisions"}],"predecessor-version":[{"id":305,"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=\/wp\/v2\/pages\/185\/revisions\/305"}],"wp:attachment":[{"href":"https:\/\/shinyflowcleaning.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}