Sky Drains - Modern Trenchless Repair Estimator

SKY DRAINS

The Modern Solution for Trenchless Sewer Repair

01Select Your Service

Choose your trenchless repair method. (Min. 20 feet)

02Enter Project Details

ft.
holes

03Customize Your Plan

04Your Real-Time Estimate

Select your preferred payment option to lock in your price for 3 days.

05Human Verification

Enter your phone number to receive your 4-digit confirmation code. This locks in your estimate.

Quote Confirmed

Your personal price is locked in for 3 days. We've sent the following to your phone:

Your Phone

06Lending Submission Form

Please fill out the application for the primary applicant. All fields are required.

Lending Program

All Lending Programs Are Fixed Rates & Can Be Paid Off Early With No Penalty

Personal Information

Your Sky Drains Personal Price

As Shown On Your DL or ID

As Shown On Your ID

Identification

Property & Financial Information

Enter The Full Property Address of The Location Being Serviced

Financing Requires At Least One Property Owner On Application

If Same as Full Property Address Leave Blank

If Retired Type "Retired"

This Sensitive Information Is Confidential

Co-Applicant

By clicking the Submit Button below, the dealer salesperson submitting this credit application hereby represents and agrees that 1) the dealer salesperson made the applicant(s) aware he or she was applying for credit, (2) the applicant(s) have given their permission to share the information to share the information on this credit application with Service Finance Company & or GoodLeap , (3) the information submitted on the application is true and correct in all respects, (4) the applicant(s) have authorized Service Finance Company & or GoodLeap to share the information on this credit application with third parties as it deems appropriate, including but not limited to consumer reporting agencies, (5) the applicant(s) have acknowledged and agreed that the information necessary to verify the applicant(s)'s identity will be collected and submitted to Service Finance & or GoodLeap, and (6) that the dealer salesperson submitting the credit application has delivered to the applicant all disclosures required by applicable federal and state law. Upon completion of this form you are authorizing the submission of a credit application potentially subject to a soft credit check. We look forward to getting you funding for your plumbing project!

07Applicant E-Signature

Please select a font to apply your electronic signature.

Your Name

08Co-Applicant Form

Please fill out the application for the co-applicant. All fields are required.

Co-Applicant Personal Information

Co-Applicant Identification

Co-Applicant Financial Information

By clicking the Submit Button below, the dealer salesperson submitting this credit application hereby represents and agrees that 1) the dealer salesperson made the applicant(s) aware he or she was applying for credit, (2) the applicant(s) have given their permission to share the information to share the information on this credit application with Service Finance Company & or GoodLeap , (3) the information submitted on the application is true and correct in all respects, (4D) the applicant(s) have authorized Service Finance Company & or GoodLeap to share the information on this credit application with third parties as it deems appropriate, including but not limited to consumer reporting agencies, (5) the applicant(s) have acknowledged and agreed that the information necessary to verify the applicant(s)'s identity will be collected and submitted to Service Finance & or GoodLeap, and (6) that the dealer salesperson submitting the credit application has delivered to the applicant all disclosures required by applicable federal and state law. Upon completion of this form you are authorizing the submission of a credit application potentially subject to a soft credit check. We look forward to getting you funding for your plumbing project!

09Co-Applicant E-Signature

Please select a font to apply your electronic signature.

Co-Applicant Name

Application Submitted

Thank you. Your application has been submitted for review. We will contact you shortly with the next steps.

SSchedule My Drain Job

Please confirm your details to schedule your job.

Contact Information

Your Sky Drains Personal Price

Invoices | Receipts | Contracts | Will be Sent to This Email

Address Being Serviced

All Communication Will be Through This Number

SAgreements

Please review and agree to the following authorizations.

AUTHORIZATION OF WORK PAYMENT OF THIS INVOICE/CONTRACT DUE UPON COMPLETION OF WORK. AUTHORIZATION TO PROCEED WITH ABOVE RECOMMENDATION – I, the undersigned, am owner/authorized representative/tenant of the premises at which the work mentioned above is to be done by registered home improvement salesperson. I hereby authorize you to perform Recommendations, and to use such labor and materials as you deem advisable. A monthly service charge of 1 ½ % will be added after 10 days. I agree to pay reasonable attorney’s fee and court costs in the event of legal action. If my check does not clear, I realize I could be liable for 3 times the amount of the check, in no case more than $1,500 and in no case less than $100 as set forth in the California Civil Code Section 1719, plus the face value of the check and court costs. I have read this contract, including the and agree to be bound by all of the terms contained herein. I have received a copy of this contract and Notice to Owner. All parts will be removed from premises and discarded unless otherwise specified herein. I hereby authorize you to proceed with the above work at the Personal Price. I agree that this contract is fully executed by both parties and shall have been received fully executed when it is transmitted by email to customer, and customer agrees that upon the electronic transmission, work can commence on the project. You, the homeowner (buyer) or tenant have the right to require the contractor to furnish you with a performance and payment bond, however the contractor can require you to pay for that bond. Upon satisfactory payment being made for any portion of the work performed, the Contractor shall, prior to any further payment being made, furnish to the person contracting for the home improvement work a full and unconditional release from any claim of mechanic’s lien for any person entitled to make such a claim of lien pursuant to Sections 8400 and 8404 of the Civil Code for that portion of the work for which payment has been made.

Notice of the Three-Day Right to Cancel (For Everyone Except Seniors)
Notice of the Five-Day Right to Cancel (For Seniors)

You, the buyer have the right to cancel this contract within five business days. You may cancel by e-mailing, mailing, faxing, or delivering a written notice to the contractor at the contractor’s place of business by midnight of the third business day after you received a signed and dated copy of the contract that includes this notice. Include your name, your address, and the date you received the signed copy of the contract and this notice. If you cancel, the contractor must return to you anything you paid within 10 days of receiving the notice of cancellation. For your part, you must make available to the contractor at your residence, in substantially as good condition as you received them, any goods delivered to you under this contract or sale. Or you may, if you wish, comply with the contractor’s instructions on how to return the goods at the contractor’s expense and risk. If you do not make the goods available to the contractor and the contractor does not pick them up within 20 days of the date of your notice of cancellation, you may keep them without any further obligation. If you fail to make the goods available to the contractor, or if you agree to return the goods to the contractor and fail to do so, then you remain liable for the performance of all obligations under the contract. The law requires that the contractor give you a notice explaining your right to cancel. Initial the checkbox if the contractor has given you a "Notice of the Five-Day Right to Cancel."

SSchedule My Job

Select your preferred date and time.

:

SJob E-Signature

Please select a font to apply your electronic signature for the job agreement.

Your Name

$Down Payment

To secure your job schedule, a 10% down payment is required.

Down Payment: $0.00

$Payment E-Signature

Please sign to authorize the down payment of $0.00.

Your Name

Job Scheduled

Thank you. Your job has been submitted for scheduling and your down payment has been processed. We will contact you shortly to confirm your selected date and time.

© Sky Drains. Serving Los Angeles & Orange County.
### 2. `style.css` (The Design) ```css /* --- ROOT VARIABLES & GLOBAL STYLES --- */ :root { --bg-dark-primary: #0a0f1e; /* Deep blue-black */ --bg-dark-secondary: #1a2a45; /* Dark blue-gray */ --primary-accent: #87CEEB; /* Soft Sky Blue */ --primary-accent-dark: #6495ED; /* Deeper Cornflower Blue */ --glass-bg: rgba(20, 30, 55, 0.7); /* More solid, darker glass */ --glass-border: rgba(135, 206, 235, 0.2); /* Soft blue border */ --text-primary: #FFFFFF; /* Pure white for max contrast */ --text-secondary: #B0C4DE; /* Light steel blue */ --text-heading: #FFFFFF; --error-red: #ff6b6b; --success-green: #90EE90; } * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: 'Poppins', sans-serif; font-weight: 400; background-color: var(--bg-dark-primary); color: var(--text-primary); line-height: 1.6; overflow-x: hidden; } .background-animation { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; background: linear-gradient(230deg, var(--bg-dark-primary), var(--bg-dark-secondary), var(--bg-dark-primary)); background-size: 400% 400%; animation: gradientShift 18s ease infinite; } @keyframes gradientShift { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } h1, h2, h3 { font-family: 'Poppins', sans-serif; font-weight: 600; color: var(--text-heading); margin-bottom: 0.5rem; } h2 { font-size: 1.8rem; color: var(--primary-accent); display: flex; align-items: center; border-bottom: 1px solid var(--glass-border); padding-bottom: 1rem; margin-bottom: 1.5rem; } .step-num { font-size: 1.2rem; font-weight: 700; color: var(--bg-dark-primary); background-color: var(--primary-accent); border-radius: 50%; width: 35px; height: 35px; display: inline-flex; align-items: center; justify-content: center; margin-right: 1rem; } .step-sub { font-size: 0.95rem; color: var(--text-secondary); margin-bottom: 1.5rem; max-width: 600px; font-weight: 300; } /* --- MAIN ESTIMATOR CONTAINER --- */ .estimator-container { max-width: 800px; margin: 3rem auto; padding: 2.5rem; background: var(--glass-bg); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid var(--glass-border); border-radius: 16px; box-shadow: 0 16px 48px rgba(0, 0, 0, 0.3); transition: all 0.3s ease; } .main-header { text-align: center; margin-bottom: 2.5rem; } .main-header .logo-svg { width: 60px; height: 60px; color: var(--primary-accent); margin-bottom: 0.5rem; } .main-header h1 { font-size: 2.5rem; font-weight: 700; color: var(--text-heading); letter-spacing: 1px; } .main-header p { font-size: 1.1rem; font-weight: 300; color: var(--primary-accent); } /* --- FORM STEPS --- */ .form-step { display: none; animation: fadeIn 0.5s ease-in-out; } .form-step.active { display: block; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } /* --- CARD OPTIONS (Service & Price) --- */ .card-option-group { display: grid; grid-template-columns: 1fr; gap: 1rem; } .card-option-group input[type="radio"] { display: none; } .card-option { display: block; background: rgba(255, 255, 255, 0.03); border: 1px solid var(--glass-border); border-radius: 12px; padding: 1.5rem; cursor: pointer; transition: all 0.2s ease-out; position: relative; } .card-option:hover { background: rgba(255, 255, 255, 0.08); transform: translateY(-3px); box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2); } .card-option-group input[type="radio"]:checked + .card-option { background: rgba(135, 206, 235, 0.1); border: 2px solid var(--primary-accent); padding: calc(1.5rem - 1px); } .card-option h3 { color: var(--text-heading); font-size: 1.2rem; font-weight: 600; } .card-option p { color: var(--text-secondary); font-size: 0.9rem; font-weight: 300; } .price-card { text-align: center; } .price-tag { display: block; font-family: 'Poppins', sans-serif; font-size: 2.2rem; font-weight: 700; color: var(--success-green); margin-bottom: 0.5rem; } /* --- INPUT FIELDS & GROUPS --- */ .input-group { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem; } /* ::: NEW: 3-Column Grid ::: */ .input-group.three-col { grid-template-columns: 2fr 1fr 1.2fr; gap: 1rem; } .input-field { margin-bottom: 1.5rem; position: relative; } /* ::: NEW: Margin-Bottom Utility ::: */ .input-field.no-mb { margin-bottom: 0; } #step-2 .input-field { margin-bottom: 0; } .input-field label { display: block; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-secondary); font-size: 0.9rem; } .field-description { font-size: 0.85rem; font-weight: 300; color: var(--text-secondary); margin-top: -0.25rem; margin-bottom: 0.5rem; } input[type="number"], input[type="tel"], input[type="text"], input[type="email"], select { width: 100%; padding: 0.8rem 1rem; font-size: 1rem; font-family: 'Poppins', sans-serif; font-weight: 400; color: var(--text-primary); background: rgba(0, 0, 0, 0.5); border: 1px solid var(--glass-border); border-radius: 8px; transition: all 0.2s ease; -webkit-appearance: none; -moz-appearance: none; appearance: none; } select { background-image: url('data:image/svg+xml;utf8,'); background-repeat: no-repeat; background-position: right 0.7rem top 50%; background-size: 1.5rem; padding-right: 2.5rem; } input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } input[type="number"] { -moz-appearance: textfield; } input[type="number"]:focus, input[type="tel"]:focus, input[type="text"]:focus, input[type="email"]:focus, select:focus { outline: none; border-color: var(--primary-accent); box-shadow: 0 0 0 2px var(--primary-accent); } input[readonly] { background-color: rgba(0, 0, 0, 0.2); color: var(--text-secondary); cursor: not-allowed; } .input-field.error input, .input-field.error select, .input-field.error .time-picker-group { border-color: var(--error-red); box-shadow: 0 0 0 2px var(--error-red); } .checkbox-group.error label { color: var(--error-red); } .input-field.error #card-element { border-color: var(--error-red); box-shadow: 0 0 0 2px var(--error-red); } .input-unit { position: absolute; right: 1rem; top: 50%; transform: translateY(5px); color: var(--text-secondary); font-weight: 600; } /* --- SEGMENTED CONTROL --- */ .segmented-control { display: flex; width: 100%; border: 1px solid var(--glass-border); border-radius: 10px; overflow: hidden; } .segmented-control input[type="radio"] { display: none; } .segmented-control label { flex: 1; padding: 0.8rem 1rem; text-align: center; cursor: pointer; background: rgba(0, 0, 0, 0.3); color: var(--text-secondary); transition: all 0.2s ease; margin: 0; font-weight: 400; } .segmented-control label:not(:last-child) { border-right: 1px solid var(--glass-border); } .segmented-control input[type="radio"]:checked + label { background: var(--primary-accent); color: var(--bg-dark-primary); font-weight: 600; } /* --- BUTTONS --- */ .cta-button { display: block; width: 100%; padding: 1rem; font-family: 'Poppins', sans-serif; font-size: 1.1rem; font-weight: 700; color: var(--bg-dark-primary); background: var(--primary-accent); border: none; border-radius: 10px; cursor: pointer; transition: all 0.3s ease; text-transform: uppercase; letter-spacing: 1px; margin-top: 1.5rem; } .cta-button:hover:not(:disabled) { background: var(--text-primary); color: var(--bg-dark-primary); transform: translateY(-2px); box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2); } .cta-button:disabled { background: var(--bg-dark-secondary); color: var(--text-secondary); cursor: not-allowed; opacity: 0.6; box-shadow: none; transform: none; } .button-group { display: flex; justify-content: space-between; align-items: center; gap: 1rem; margin-top: 1.5rem; } .button-group .cta-button { flex-grow: 1; width: auto; margin-top: 0; } .button-group .back-button { background: none; border: none; color: var(--text-secondary); cursor: pointer; font-family: 'Poppins', sans-serif; font-weight: 600; font-size: 0.9rem; padding: 1rem; border-radius: 10px; transition: all 0.2s ease; flex-grow: 0; flex-shrink: 0; text-transform: uppercase; letter-spacing: 1px; } .button-group .back-button:hover { color: var(--primary-accent); background: rgba(255, 255, 255, 0.05); } /* --- VERIFICATION & FINAL STEP --- */ .hidden { display: none !important; } #code-entry-section { margin-top: 2rem; } hr { border: none; height: 1px; background-color: var(--glass-border); margin-bottom: 2rem; } .error-text { color: var(--error-red); text-align: center; margin-top: 1rem; font-weight: 600; } .form-step > .error-text { margin-top: -0.5rem; margin-bottom: 1.5rem; text-align: left; } /* --- MOCK PHONE CONSOLE --- */ #mock-phone-console { background: #000000; border: 1px solid var(--glass-border); border-radius: 12px; margin: 2rem 0; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); } .console-header { background: rgba(255, 255, 255, 0.1); padding: 0.5rem 1rem; font-weight: 600; border-bottom: 1px solid var(--glass-border); border-radius: 12px 12px 0 0; color: var(--primary-accent); } .console-body { padding: 1.5rem; height: 200px; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; } .console-message { background: var(--primary-accent-dark); color: white; padding: 0.8rem 1rem; border-radius: 20px 20px 20px 5px; align-self: flex-start; max-width: 80%; animation: slideIn 0.3s ease-out; } @keyframes slideIn { from { opacity: 0; transform: translateX(-20px); } to { opacity: 1; transform: translateX(0); } } /* --- LENDING/SCHEDULE FORM STYLES --- */ .form-section { border-bottom: 1px solid var(--glass-border); padding-bottom: 1.5rem; margin-bottom: 1.5rem; } .form-section:last-of-type { border-bottom: none; padding-bottom: 0; } .form-section h3 { color: var(--text-heading); font-weight: 600; margin-bottom: 1rem; } .form-section > label { display: block; font-weight: 600; margin-bottom: 0.5rem; color: var(--text-secondary); font-size: 0.9rem; } .radio-group { display: flex; align-items: center; margin-bottom: 0.75rem; } .radio-group label { margin-left: 0.75rem; font-weight: 400; color: var(--text-primary); } input[type="radio"] { -webkit-appearance: none; -moz-appearance: none; appearance: none; width: 20px; height: 20px; border: 2px solid var(--primary-accent); border-radius: 50%; cursor: pointer; transition: all 0.2s ease; flex-shrink: 0; } input[type="radio"]:checked { background-color: var(--primary-accent); border: 2px solid var(--primary-accent); box-shadow: inset 0 0 0 4px var(--bg-dark-primary); } .other-group { display: flex; align-items: center; } .other-group .other-text-input { flex-grow: 1; margin-left: 0.5rem; } .other-text-input:disabled { background: rgba(0, 0, 0, 0.2); opacity: 0.5; } .terms-section .terms-text { font-size: 0.8rem; font-weight: 300; color: var(--text-secondary); background: rgba(0, 0, 0, 0.2); border: 1px solid var(--glass-border); border-radius: 8px; padding: 1rem; height: 150px; overflow-y: auto; margin-bottom: 1rem; } .checkbox-group { display: flex; align-items: flex-start; padding: 0.5rem; border-radius: 8px; transition: all 0.2s ease; } .checkbox-group.error { background-color: rgba(255, 107, 107, 0.1); border: 1px solid var(--error-red); } .checkbox-group label { margin-left: 0.75rem; font-size: 0.9rem; color: var(--text-secondary); line-height: 1.4; } input[type="checkbox"] { -webkit-appearance: none; -moz-appearance: none; appearance: none; width: 20px; height: 20px; border: 2px solid var(--primary-accent); border-radius: 4px; cursor: pointer; transition: all 0.2s ease; margin-top: 1px; flex-shrink: 0; } input[type="checkbox"]:checked { background-color: var(--primary-accent); background-image: url('data:image/svg+xml;utf8,'); background-size: 18px; background-position: center; background-repeat: no-repeat; } /* --- E-SIGNATURE STYLES --- */ .signature-display-box { width: 100%; height: 150px; background: rgba(0, 0, 0, 0.2); border: 1px dashed var(--glass-border); border-radius: 8px; display: flex; justify-content: center; align-items: center; font-size: 3.5rem; color: var(--text-primary); margin-bottom: 1.5rem; padding: 1rem; transition: font-family 0.3s ease; overflow: hidden; text-align: center; } .font-dancing { font-family: 'Dancing Script', cursive; font-size: 3rem; } .font-great-vibes { font-family: 'Great Vibes', cursive; font-size: 3.5rem; } .font-sacramento { font-family: 'Sacramento', cursive; font-size: 4rem; } .segmented-control.signature-fonts label { transition: font-family 0.3s ease; } /* --- TIME PICKER STYLES --- */ .time-picker-group { display: flex; align-items: center; gap: 0.5rem; background: rgba(0, 0, 0, 0.5); border: 1px solid var(--glass-border); border-radius: 8px; padding: 0.8rem 1rem; transition: all 0.2s ease; } .time-picker-group:focus-within { border-color: var(--primary-accent); box-shadow: 0 0 0 2px var(--primary-accent); } .time-picker-group input[type="number"] { width: 3.5rem; text-align: center; background: none; border: none; padding: 0; box-shadow: none; font-size: 1.1rem; } .time-picker-group input[type="number"]:focus { box-shadow: none; } .time-picker-group span { font-size: 1.1rem; font-weight: 600; color: var(--text-secondary); } .time-picker-group select { width: auto; background: none; border: none; padding: 0; box-shadow: none; font-size: 1.1rem; font-weight: 600; background-position: right 0 top 50%; padding-right: 1.8rem; } /* --- PAYMENT FORM STYLES --- */ .payment-amount-display { font-size: 1.5rem; font-weight: 600; color: var(--text-primary); text-align: center; margin-bottom: 1.5rem; } .payment-amount-display span { color: var(--success-green); } #card-element { background: rgba(0, 0, 0, 0.5); border: 1px solid var(--glass-border); border-radius: 8px; padding: 1rem; transition: all 0.2s ease; } #card-element--focus { border-color: var(--primary-accent); box-shadow: 0 0 0 2px var(--primary-accent); } #card-errors { text-align: left; margin-top: 1rem; } /* --- FOOTER --- */ .main-footer { text-align: center; padding: 2rem; font-size: 0.9rem; color: var(--text-secondary); font-weight: 300; } /* --- RESPONSIVENESS --- */ @media (max-width: 850px) { .estimator-container { margin: 1rem; padding: 1.5rem; } } @media (max-width: 600px) { .input-group, .input-group.three-col { /* ::: UPDATED ::: */ grid-template-columns: 1fr; gap: 1rem; } #step-2 .input-group { gap: 1.5rem; } .card-option-group { grid-template-columns: 1fr; } .segmented-control { flex-direction: column; } .segmented-control label:not(:last-child) { border-right: none; border-bottom: 1px solid var(--glass-border); } .signature-display-box { font-size: 2.5rem; } .font-dancing { font-size: 2.2rem; } .font-great-vibes { font-size: 2.5rem; } .font-sacramento { font-size: 3rem; } .time-picker-group { flex-wrap: wrap; } .time-picker-group input[type="number"] { width: 3rem; } .time-picker-group select { margin-left: auto; } } } { type: uploaded file fileName: script.js fullContent: // Wait for the DOM to be fully loaded before executing document.addEventListener('DOMContentLoaded', () => { // --- I. STATE & CONSTANTS --- const state = { service: null, linearFeet: 20, potholes: 1, warranty: null, permit: true, finalPrices: { finance: 0, cashPermit: 0, cashNoPermit: 0 }, chosenPriceType: null, chosenPriceValue: 0, verification: { code: null, tries: 0, maxTries: 3 }, coApplicant: false, applicantName: '', coApplicantName: '', cashApplicantName: '', downPaymentAmount: 0 }; const marketPricing = { Liner: { base: 12562.50, perFoot: 250.00 }, Epoxy: { base: 11984.40, perFoot: 320.00 }, Burst: { base: 12732.60, perFoot: 142.00 }, potholeCost: 2500.00, permitCost: 250.00, cashPermitDiscount: 1000.00, cashNoPermitDiscountPercent: 0.18 }; const warrantyData = { Liner: [{ text: '10-Year', value: '10yrLinerWarranty', cost: 0 },{ text: '15-Year', value: '15yrLinerWarranty', cost: 1500.00 },{ text: '30-Year', value: '30yrLinerWarranty', cost: 2000.00 }], Epoxy: [{ text: '10-Year', value: '10yrEpoxyWarranty', cost: 0 },{ text: '12-Year', value: '12yrEpoxyWarranty', cost: 1500.00 },{ text: '15-Year', value: '15yrEpoxyWarranty', cost: 2000.00 }], Burst: [{ text: '30-Year', value: '30yrBurstWarranty', cost: 0 },{ text: '50-Year', value: '50yrBurstWarranty', cost: 1500.00 },{ text: '75-Year', value: '75yrBurstWarranty', cost: 2000.00 }] }; // --- II. DOM ELEMENT SELECTORS --- const form = document.getElementById('sky-drain-estimator'); const steps = document.querySelectorAll('.form-step'); // Step 1-5 (Core) const serviceRadios = document.querySelectorAll('input[name="service"]'); const linearFeetInput = document.getElementById('linearFeet'); const potholesInput = document.getElementById('potholes'); const step2NextBtn = document.getElementById('step2-next-btn'); const step2BackBtn = document.getElementById('step2-back-btn'); const warrantyContainer = document.getElementById('warranty-options-container'); const permitRadios = document.querySelectorAll('input[name="permit"]'); const calculateBtn = document.getElementById('calculate-btn'); const step3BackBtn = document.getElementById('step3-back-btn'); const priceRadios = document.querySelectorAll('input[name="finalPrice"]'); const financeTotalEl = document.getElementById('finance-total'); const cashPermitTotalEl = document.getElementById('cash-permit-total'); const cashNoPermitTotalEl = document.getElementById('cash-no-permit-total'); const confirmQuoteBtn = document.getElementById('confirm-quote-btn'); const step4BackBtn = document.getElementById('step4-back-btn'); const phoneInput = document.getElementById('phoneNumber'); const sendCodeBtn = document.getElementById('send-code-btn'); const codeEntrySection = document.getElementById('code-entry-section'); const verificationCodeInput = document.getElementById('verificationCode'); const verifyCodeBtn = document.getElementById('verify-code-btn'); const errorMessage = document.getElementById('error-message'); // Step 6 (Cash Thanks) const consoleMessages = document.getElementById('console-messages'); const scheduleJobBtn = document.getElementById('schedule-job-btn'); // --- LENDING FLOW SELECTORS (7-11) --- const personalPriceInput = document.getElementById('personalPrice'); const firstNameInput = document.getElementById('firstName'); const lastNameInput = document.getElementById('lastName'); const applicantNameInTerms = document.getElementById('applicant-name-in-terms'); const ownerOtherRadio = document.getElementById('owner-other'); const ownerOtherText = document.getElementById('owner-other-text'); const ownerRadios = document.querySelectorAll('input[name="propertyOwner"]'); const coApplicantRadios = document.querySelectorAll('input[name="addCoApplicant"]'); const termsAgreeCheckbox = document.getElementById('terms-agree'); const step7Error = document.getElementById('step-7-error'); const step7NextBtn = document.getElementById('step7-next-btn'); const signatureDisplay = document.getElementById('signature-display'); const signatureFontRadios = document.querySelectorAll('input[name="sigFont"]'); const step8BackBtn = document.getElementById('step8-back-btn'); const step8NextBtn = document.getElementById('step8-next-btn'); const coFirstNameInput = document.getElementById('co-firstName'); const coLastNameInput = document.getElementById('co-lastName'); const coApplicantNameInTerms = document.getElementById('co-applicant-name-in-terms'); const coTermsAgreeCheckbox = document.getElementById('co-terms-agree'); const step9Error = document.getElementById('step-9-error'); const step9BackBtn = document.getElementById('step9-back-btn'); const step9NextBtn = document.getElementById('step9-next-btn'); const coSignatureDisplay = document.getElementById('co-signature-display'); const coSignatureFontRadios = document.querySelectorAll('input[name="coSigFont"]'); const step10BackBtn = document.getElementById('step10-back-btn'); const submitApplicationBtn = document.getElementById('submit-application-btn'); const finishBtn = document.getElementById('finish-btn'); // --- CASH SCHEDULE FLOW SELECTORS (CASH 1-7) --- const cashPersonalPrice = document.getElementById('cash-personalPrice'); const cashFirstName = document.getElementById('cash-firstName'); const cashLastName = document.getElementById('cash-lastName'); const stepCash1Error = document.getElementById('step-cash-1-error'); const stepCash1NextBtn = document.getElementById('step-cash-1-next-btn'); const stepCash2Error = document.getElementById('step-cash-2-error'); const stepCash2BackBtn = document.getElementById('step-cash-2-back-btn'); const stepCash2NextBtn = document.getElementById('step-cash-2-next-btn'); const stepCash3Error = document.getElementById('step-cash-3-error'); const stepCash3BackBtn = document.getElementById('step-cash-3-back-btn'); const stepCash3NextBtn = document.getElementById('step-cash-3-next-btn'); const cashSignatureDisplay = document.getElementById('cash-signature-display'); const cashSignatureFontRadios = document.querySelectorAll('input[name="cashSigFont"]'); const stepCash4BackBtn = document.getElementById('step-cash-4-back-btn'); const stepCash4NextBtn = document.getElementById('step-cash-4-next-btn'); const finishScheduleBtn = document.getElementById('finish-schedule-btn'); const downPaymentAmountEl = document.getElementById('down-payment-amount'); const paymentBtnAmountEl = document.getElementById('payment-btn-amount'); const cardElementDiv = document.getElementById('card-element'); const cardErrors = document.getElementById('card-errors'); const stepCash5BackBtn = document.getElementById('step-cash-5-back-btn'); const submitPaymentBtn = document.getElementById('submit-payment-btn'); const paymentSigAmountEl = document.getElementById('payment-sig-amount'); const cashPaymentSignatureDisplay = document.getElementById('cash-payment-signature-display'); const cashPaymentSignatureFontRadios = document.querySelectorAll('input[name="cashPaymentSigFont"]'); const stepCash6BackBtn = document.getElementById('step-cash-6-back-btn'); const submitScheduleBtn = document.getElementById('submit-schedule-btn'); // Footer document.getElementById('current-year').textContent = new Date().getFullYear(); // --- STRIPE INITIALIZATION --- const stripe = Stripe('pk_test_51HqIeVEgS3rWeosm2C8L7YlYF04o9e8c4q4y9YqJj8kM8YJ8Yj8Yj8Yj8Yj8Yj8Yj8Yj8Yj8Yj8Yj8Yj8Y'); // Public test key const elements = stripe.elements(); const cardElement = elements.create('card', { style: { base: { color: '#FFFFFF', fontFamily: '"Poppins", sans-serif', fontSmoothing: 'antialiased', fontSize: '16px', '::placeholder': { color: '#B0C4DE' } }, invalid: { color: '#ff6b6b', iconColor: '#ff6b6b' } } }); cardElement.mount('#card-element'); cardElement.on('change', (event) => { if (event.error) { cardErrors.textContent = event.error.message; cardErrors.classList.remove('hidden'); } else { cardErrors.textContent = ''; cardErrors.classList.add('hidden'); } }); // --- III. HELPER FUNCTIONS --- function showStep(stepId) { steps.forEach(step => step.classList.remove('active')); document.querySelector(stepId).classList.add('active'); window.scrollTo(0, 0); } function formatCurrency(amount) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(amount); } function updateWarrantyOptions() { if (!state.service) return; const options = warrantyData[state.service]; warrantyContainer.innerHTML = ''; options.forEach((opt, index) => { const input = document.createElement('input'); input.type = 'radio'; input.id = `warranty-${opt.value}`; input.name = 'warranty'; input.value = opt.value; input.required = true; if (index === 0) { input.checked = true; state.warranty = opt.value; } const label = document.createElement('label'); label.htmlFor = `warranty-${opt.value}`; label.textContent = opt.text; warrantyContainer.appendChild(input); warrantyContainer.appendChild(label); }); // Add listeners to newly created radio buttons warrantyContainer.querySelectorAll('input[name="warranty"]').forEach(radio => { radio.addEventListener('change', (e) => { state.warranty = e.target.value; }); }); } function calculateQuote() { const { service, linearFeet, potholes, warranty, permit } = state; const pricing = marketPricing[service]; let basePrice1, basePrice2, basePrice3, basePrice4; if (linearFeet <= 20) basePrice1 = pricing.base; else basePrice1 = ((linearFeet - 20) * pricing.perFoot) + pricing.base; if (potholes <= 1) basePrice2 = basePrice1; else basePrice2 = ((potholes - 1) * marketPricing.potholeCost) + basePrice1; const warrantyCost = warrantyData[service].find(w => w.value === warranty)?.cost || 0; basePrice3 = basePrice2 + warrantyCost; if (permit) basePrice4 = basePrice3; else basePrice4 = basePrice3 - marketPricing.permitCost; state.finalPrices.finance = basePrice4; state.finalPrices.cashPermit = basePrice4 - marketPricing.cashPermitDiscount; state.finalPrices.cashNoPermit = basePrice4 - (basePrice4 * marketPricing.cashNoPermitDiscountPercent); } function displayResults() { const { finance, cashPermit, cashNoPermit } = state.finalPrices; financeTotalEl.textContent = formatCurrency(finance); cashPermitTotalEl.textContent = formatCurrency(cashPermit); cashNoPermitTotalEl.textContent = formatCurrency(cashNoPermit); document.getElementById('price-one').dataset.price = finance; document.getElementById('price-two').dataset.price = cashPermit; document.getElementById('price-three').dataset.price = cashNoPermit; showStep('#step-4'); } function logToMockPhone(message, delay = 0) { setTimeout(() => { const msgElement = document.createElement('div'); msgElement.className = 'console-message'; msgElement.textContent = message; consoleMessages.appendChild(msgElement); consoleMessages.scrollTop = consoleMessages.scrollHeight; }, delay); } function handleSuccessfulVerification() { let myOrder = `Your Sky Drains Personal Price: ${formatCurrency(state.chosenPriceValue)}`; if (state.chosenPriceType === 'One') { // LENDING FLOW personalPriceInput.value = formatCurrency(state.chosenPriceValue); showStep('#step-7-applicant'); } else { // CASH FLOW cashPersonalPrice.value = formatCurrency(state.chosenPriceValue); logToMockPhone(myOrder, 500); logToMockPhone('Your estimate QR code (valid for 3 days): [QR Code Generated]', 1500); logToMockPhone("Click 'Schedule Your Repair' below to continue.", 2500); showStep('#step-6-cash-thanks'); } } function validateStep(stepSelector, errorSelector) { let isValid = true; const errorEl = document.querySelector(errorSelector); const fields = document.querySelectorAll(`${stepSelector} [required]`); fields.forEach(field => { if (!field.closest('.form-step.active')) return; const fieldContainer = field.closest('.input-field') || field.closest('.checkbox-group') || field.closest('.radio-group'); let fieldValid = true; if (field.type === 'checkbox') fieldValid = field.checked; else if (field.type === 'radio') { const radioGroup = document.querySelectorAll(`input[name="${field.name}"]`); fieldValid = Array.from(radioGroup).some(radio => radio.checked); } else fieldValid = field.value.trim() !== ''; if (!fieldValid) { isValid = false; if (fieldContainer) fieldContainer.classList.add('error'); } else { if (fieldContainer) fieldContainer.classList.remove('error'); } }); if (!isValid) errorEl.classList.remove('hidden'); else errorEl.classList.add('hidden'); return isValid; } // --- LENDING SIGNATURE FUNCTIONS --- function updateApplicantSignature() { const firstName = firstNameInput.value || ''; const lastName = lastNameInput.value || ''; state.applicantName = `${firstName} ${lastName}`; const displayName = state.applicantName.trim() ? state.applicantName : 'Your Name'; signatureDisplay.textContent = displayName; applicantNameInTerms.textContent = displayName; } function updateCoApplicantSignature() { const firstName = coFirstNameInput.value || ''; const lastName = coLastNameInput.value || ''; state.coApplicantName = `${firstName} ${lastName}`; const displayName = state.coApplicantName.trim() ? state.coApplicantName : 'Co-Applicant Name'; coSignatureDisplay.textContent = displayName; coApplicantNameInTerms.textContent = displayName; } function submitApplication() { console.log("--- LENDING APPLICATION SUBMITTED (SIMULATED) ---"); showStep('#step-11-thanks'); } // --- CASH SCHEDULE SIGNATURE FUNCTIONS --- function updateScheduleSignature() { const firstName = cashFirstName.value || ''; const lastName = cashLastName.value || ''; state.cashApplicantName = `${firstName} ${lastName}`; const displayName = state.cashApplicantName.trim() ? state.cashApplicantName : 'Your Name'; cashSignatureDisplay.textContent = displayName; cashPaymentSignatureDisplay.textContent = displayName; } function submitSchedule() { console.log("--- SCHEDULE SUBMITTED (SIMULATED) ---"); showStep('#step-cash-7-thanks'); } // --- IV. EVENT LISTENERS --- // Step 1: Service serviceRadios.forEach(radio => { radio.addEventListener('change', (e) => { state.service = e.target.value; updateWarrantyOptions(); showStep('#step-2'); }); }); // Step 2: Details linearFeetInput.addEventListener('change', () => { state.linearFeet = Math.max(20, parseInt(linearFeetInput.value) || 20); if (linearFeetInput.value && parseInt(linearFeetInput.value) < 20) linearFeetInput.value = 20; }); potholesInput.addEventListener('change', () => { state.potholes = Math.max(1, parseInt(potholesInput.value) || 1); if (potholesInput.value && parseInt(potholesInput.value) < 1) potholesInput.value = 1; }); step2NextBtn.addEventListener('click', () => { state.linearFeet = Math.max(20, parseInt(linearFeetInput.value) || 20); state.potholes = Math.max(1, parseInt(potholesInput.value) || 1); showStep('#step-3'); }); step2BackBtn.addEventListener('click', () => showStep('#step-1')); // Step 3: Options permitRadios.forEach(radio => { radio.addEventListener('change', (e) => { state.permit = (e.target.value === 'Yes'); }); }); calculateBtn.addEventListener('click', () => { if (!state.service || !state.warranty) { alert('Please make sure all selections are made.'); return; } calculateQuote(); displayResults(); }); step3BackBtn.addEventListener('click', () => showStep('#step-2')); // Step 4: Results priceRadios.forEach(radio => { radio.addEventListener('change', (e) => { state.chosenPriceType = e.target.value; state.chosenPriceValue = parseFloat(e.target.dataset.price); confirmQuoteBtn.disabled = false; }); }); confirmQuoteBtn.addEventListener('click', () => showStep('#step-5')); step4BackBtn.addEventListener('click', () => showStep('#step-3')); // Step 5: Verification sendCodeBtn.addEventListener('click', () => { const phone = phoneInput.value; if (phone.length < 10) { alert('Please enter a valid phone number.'); return; } state.verification.code = Math.floor(1000 + Math.random() * 9000).toString(); state.verification.tries = 0; console.log(`--- SIMULATION: Sending code ${state.verification.code} to ${phone} ---`); alert(`A (simulated) verification code has been sent to your phone: ${state.verification.code}`); sendCodeBtn.disabled = true; sendCodeBtn.textContent = 'Code Sent'; codeEntrySection.classList.remove('hidden'); errorMessage.classList.add('hidden'); }); verifyCodeBtn.addEventListener('click', () => { const inputCode = verificationCodeInput.value; if (inputCode === state.verification.code) { errorMessage.classList.add('hidden'); handleSuccessfulVerification(); } else { state.verification.tries++; if (state.verification.tries >= state.verification.maxTries) { errorMessage.textContent = 'Too many attempts. Please call us to confirm your quote.'; errorMessage.classList.remove('hidden'); verifyCodeBtn.disabled = true; verificationCodeInput.disabled = true; } else { errorMessage.textContent = `Invalid code. ${state.verification.maxTries - state.verification.tries} tries remaining.`; errorMessage.classList.remove('hidden'); } } }); // Step 6: Cash Thanks scheduleJobBtn.addEventListener('click', () => { showStep('#step-cash-1-details'); }); // --- LENDING FLOW (STEPS 7-11) --- firstNameInput.addEventListener('input', updateApplicantSignature); lastNameInput.addEventListener('input', updateApplicantSignature); ownerRadios.forEach(radio => { radio.addEventListener('change', () => { if (ownerOtherRadio.checked) { ownerOtherText.disabled = false; ownerOtherText.focus(); } else { ownerOtherText.disabled = true; ownerOtherText.value = ''; } }); }); coApplicantRadios.forEach(radio => { radio.addEventListener('change', (e) => { state.coApplicant = (e.target.value === 'Yes'); }); }); step7NextBtn.addEventListener('click', () => { if (validateStep('#step-7-applicant', '#step-7-error')) { showStep('#step-8-applicant-signature'); } }); signatureFontRadios.forEach(radio => { radio.addEventListener('change', (e) => { signatureDisplay.classList.remove('font-dancing', 'font-great-vibes', 'font-sacramento'); signatureDisplay.classList.add(e.target.value); }); }); step8BackBtn.addEventListener('click', () => showStep('#step-7-applicant')); step8NextBtn.addEventListener('click', () => { if (state.coApplicant) { showStep('#step-9-co-applicant'); } else { submitApplication(); } }); coFirstNameInput.addEventListener('input', updateCoApplicantSignature); coLastNameInput.addEventListener('input', updateCoApplicantSignature); step9BackBtn.addEventListener('click', () => showStep('#step-8-applicant-signature')); step9NextBtn.addEventListener('click', () => { if (validateStep('#step-9-co-applicant', '#step-9-error')) { showStep('#step-10-co-applicant-signature'); } }); coSignatureFontRadios.forEach(radio => { radio.addEventListener('change', (e) => { coSignatureDisplay.classList.remove('font-dancing', 'font-great-vibes', 'font-sacramento'); coSignatureDisplay.classList.add(e.target.value); }); }); step10BackBtn.addEventListener('click', () => showStep('#step-9-co-applicant')); submitApplicationBtn.addEventListener('click', () => { submitApplication(); }); finishBtn.addEventListener('click', () => { location.reload(); }); // --- CASH SCHEDULE FLOW (STEPS CASH 1-7) --- // Step Cash-1: Details cashFirstName.addEventListener('input', updateScheduleSignature); cashLastName.addEventListener('input', updateScheduleSignature); stepCash1NextBtn.addEventListener('click', () => { // ::: BUG FIX: Changed selector from #step-7-applicant to #step-cash-1-details ::: if (validateStep('#step-cash-1-details', '#step-cash-1-error')) { showStep('#step-cash-2-agreements'); } }); // Step Cash-2: Agreements stepCash2BackBtn.addEventListener('click', () => showStep('#step-cash-1-details')); stepCash2NextBtn.addEventListener('click', () => { if (validateStep('#step-cash-2-agreements', '#step-cash-2-error')) { showStep('#step-cash-3-schedule'); } }); // Step Cash-3: Schedule stepCash3BackBtn.addEventListener('click', () => showStep('#step-cash-2-agreements')); stepCash3NextBtn.addEventListener('click', () => { const hr = document.getElementById('cash-time-hr').value; const min = document.getElementById('cash-time-min').value; if (validateStep('#step-cash-3-schedule', '#step-cash-3-error') && hr && min) { showStep('#step-cash-4-job-signature'); } else { document.getElementById('step-cash-3-error').classList.remove('hidden'); } }); // Step Cash-4: Job Signature cashSignatureFontRadios.forEach(radio => { radio.addEventListener('change', (e) => { cashSignatureDisplay.classList.remove('font-dancing', 'font-great-vibes', 'font-sacramento'); cashSignatureDisplay.classList.add(e.target.value); }); }); stepCash4BackBtn.addEventListener('click', () => showStep('#step-cash-3-schedule')); stepCash4NextBtn.addEventListener('click', () => { state.downPaymentAmount = state.chosenPriceValue * 0.10; const formattedAmount = formatCurrency(state.downPaymentAmount); downPaymentAmountEl.textContent = formattedAmount; paymentBtnAmountEl.textContent = formattedAmount; paymentSigAmountEl.textContent = formattedAmount; showStep('#step-cash-5-payment'); }); // Step Cash-5: Payment stepCash5BackBtn.addEventListener('click', () => showStep('#step-cash-4-job-signature')); submitPaymentBtn.addEventListener('click', async (e) => { e.preventDefault(); submitPaymentBtn.disabled = true; submitPaymentBtn.textContent = 'Processing...'; // --- SIMULATION --- setTimeout(() => { console.log("--- PAYMENT SIMULATED SUCCESSFULLY ---"); cardErrors.classList.add('hidden'); showStep('#step-cash-6-payment-signature'); submitPaymentBtn.disabled = false; submitPaymentBtn.innerHTML = `Pay ${formatCurrency(state.downPaymentAmount)}`; }, 1500); }); // Step Cash-6: Payment Signature cashPaymentSignatureFontRadios.forEach(radio => { radio.addEventListener('change', (e) => { cashPaymentSignatureDisplay.classList.remove('font-dancing', 'font-great-vibes', 'font-sacramento'); cashPaymentSignatureDisplay.classList.add(e.target.value); }); }); stepCash6BackBtn.addEventListener('click', () => showStep('#step-cash-5-payment')); submitScheduleBtn.addEventListener('click', () => { submitSchedule(); }); // Step Cash-7: Thanks finishScheduleBtn.addEventListener('click', () => { location.reload(); }); }); }