A visitor who doesn’t fill out the contact form isn’t a lost lead — they’re a lead you haven’t captured yet. The modal is the second chance.
A visitor who lands on a listing page, doesn’t fill out the contact form, and navigates away is not a lost lead — they’re a lead you haven’t captured yet. The site-wide modal is a second chance at capture. It shows up when behavior signals suggest the visitor is about to leave: mouse leaving the viewport, 70% scroll depth without form interaction, or 45 seconds elapsed without engagement.
The Anchor site’s main contact form was on individual listing pages. A buyer browsing the inventory grid — not yet committed to a specific unit — had no prompted lead capture path. Visitors who reached 70%+ scroll depth on the homepage or browse page were interested enough to scroll; they just hadn’t been asked for their contact information.
The modal HTML is injected globally across every page via wp_footer. No per-page modal, no duplication. Suppressed on the contact page and for visitors who have already submitted a lead:
add_action( 'wp_footer', '[client]_render_global_modal' );
function [client]_render_global_modal(): void {
if ( is_page( [ 'contact', 'credit-application' ] ) ) {
return;
}
if ( isset( $_COOKIE['[client]_lead_submitted'] ) ) {
return;
}
?>
<div id="[client]-modal" class="site-modal" role="dialog"
aria-modal="true" aria-hidden="true">
<div class="site-modal__overlay" id="[client]-modal-overlay"></div>
<div class="site-modal__box">
<button class="site-modal__close" id="[client]-modal-close"
aria-label="Close">×</button>
<div class="site-modal__inner">
<?php [client]_render_modal_lead_form(); ?>
</div>
</div>
</div>
<?php
}
The modal fires on the first of three conditions. Once triggered, a 7-day cookie prevents re-triggering:
(function() {
var MODAL_COOKIE = '[client]_modal_dismissed';
var COOKIE_DAYS = 7;
function getCookie(name) {
var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
return match ? decodeURIComponent(match[2]) : null;
}
function setCookie(name, value, days) {
var exp = new Date(Date.now() + days * 864e5).toUTCString();
document.cookie = name + '=' + value + '; expires=' + exp + '; path=/; SameSite=Lax';
}
var modal = document.getElementById('[client]-modal');
var overlay = document.getElementById('[client]-modal-overlay');
var closeBtn = document.getElementById('[client]-modal-close');
if (!modal || getCookie(MODAL_COOKIE)) return;
var triggered = false;
function trigger() {
if (triggered) return;
triggered = true;
modal.setAttribute('aria-hidden', 'false');
modal.classList.add('is-open');
setCookie(MODAL_COOKIE, '1', COOKIE_DAYS);
}
function dismiss() {
modal.setAttribute('aria-hidden', 'true');
modal.classList.remove('is-open');
}
// Trigger 1: Exit intent (cursor leaving viewport upward)
document.addEventListener('mouseleave', function(e) {
if (e.clientY <= 0) trigger();
});
// Trigger 2: Scroll depth (70%)
window.addEventListener('scroll', function() {
var scrolled = window.scrollY + window.innerHeight;
var docHeight = document.documentElement.scrollHeight;
if (scrolled / docHeight >= 0.70) trigger();
}, { passive: true });
// Trigger 3: Time delay (45 seconds)
setTimeout(trigger, 45000);
if (closeBtn) closeBtn.addEventListener('click', dismiss);
if (overlay) overlay.addEventListener('click', dismiss);
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') dismiss();
});
})();
The modal form submits to the same AJAX handler as the main inquiry forms. No duplicate CRM creation code:
add_action( 'wp_ajax_nopriv_[client]_modal_lead', '[client]_handle_modal_lead' );
add_action( 'wp_ajax_[client]_modal_lead', '[client]_handle_modal_lead' );
function [client]_handle_modal_lead(): void {
check_ajax_referer( '[client]_modal_lead', 'modal_nonce' );
$lead_id = [client]_create_lead( [
'first_name' => sanitize_text_field( $_POST['first_name'] ?? '' ),
'phone' => sanitize_text_field( $_POST['phone'] ?? '' ),
'source' => 'site_modal',
'status' => 'new',
] );
[client]_enroll_in_sequence( $lead_id, 'modal_capture' );
// Suppress modal for this visitor for 90 days after submission
setcookie( '[client]_lead_submitted', '1', time() + ( 90 * DAY_IN_SECONDS ), '/' );
wp_send_json_success( [ 'message' => 'Got it. We\'ll be in touch.' ] );
}
The modal form converts differently than the main inquiry form for a structural reason: the main inquiry form asks “are you interested in this specific unit?” The modal asks “can we stay in touch?” The ask is smaller. The commitment is lower. The conversion rate is higher.
The source attribution on modal leads is important. Modal leads have different conversion rates than listing-page leads. Without source tracking, you can’t measure the modal’s actual impact on qualified pipeline.
Modal conversion rate: 4.2% of non-suppressed page views. Exit intent was the most common trigger (58%), scroll depth second (31%), time delay last (11%). Modal leads converted to appointment at 1.4× the rate of cold-browse leads — the intent signal from scroll depth filtering was meaningful.
site_modal as a source lets you measure this conversion layer independently from listing-page and direct inquiries.Every lesson stays free — no account, no paywall, no email gate, ever. But if you’d rather have this system standing on your business than wire all 48 lessons yourself, leave your email. We’ll send you a direct line to a build — and you’ll be first to hear when we add new tools to the curriculum.
None of this gates a single lesson. The curriculum was free before you got here and it stays that way.
You came here to understand the system, and now you do. If you’d rather have it standing on your business than spend the next three months wiring it yourself, GAP Concierge is the same architecture from these lessons — a white-label AI agent that knows your catalog and captures your leads — set up for you, from $97/mo.
See GAP Concierge →