Advertise with Us

In the competitive world of commercial landscaping, getting your brand in front of the right audience is essential, and Turf is here to help you do just that. We understand the challenges manufacturers, suppliers, and service providers face when trying to connect with landscape professionals, and we offer targeted marketing solutions designed to make an impact.

From reaching engaged industry decision-makers to generating qualified leads and building brand awareness, our multichannel marketing opportunities across print, digital, and custom content are designed to put your products and services in front of the professionals who matter most.

Ready to grow your reach and get more from your marketing? Simply fill out the form below to request the Turf Media Planner and discover how we can help you connect with your audience and achieve your marketing goals.

Image

2027 Turf Media Planner Request

"*" indicates required fields

This field is for validation purposes and should be left unchanged.
Name*
State / Province / Region*

`; if (isWallpaper) { container.classList.remove('is-active'); iframe.addEventListener('load', () => { requestAnimationFrame(() => { container.classList.add('is-active'); }); }, { once: true }); } renderTarget.appendChild(iframe); container.setAttribute('data-active-id', String(spot.id)); // Avoid inline display:block overriding .gcm-sponsored-by.is-filled // (whose flex layout centers the two rows). container.style.display = isSponsoredBy ? '' : 'block'; if (!isWallpaper) { requestAnimationFrame(() => { container.classList.add('is-active'); }); } } function clearSlot(container) { const activeId = container.getAttribute('data-active-id'); if (activeId) { loadedAdKeys.delete(container.id + ':' + activeId); } clearRefreshTimer(container.id); container.classList.remove('is-active'); container.style.display = 'none'; container.setAttribute('data-active-id', ''); if (container.id === 'aje_gcm_sb_ads') { const holder = container.querySelector('.gcm-sponsored-by__creative'); if (holder) holder.innerHTML = ''; container.classList.remove('is-filled'); container.setAttribute('aria-hidden', 'true'); container.dataset.gcmSponsorStatus = 'waiting'; delete container.dataset.gcmSponsorStarted; } else { container.innerHTML = ''; } } function startRefreshTimer(slot, spot) { clearRefreshTimer(slot.containerId); const timer = setInterval(() => { if (!canRefreshAds()) return; const container = document.getElementById(slot.containerId); if (!container) { clearRefreshTimer(slot.containerId); return; } if (!window.matchMedia(spot.query).matches) { clearRefreshTimer(slot.containerId); clearSlot(container); return; } injectIframeAd(container, spot, { force: true }); }, REFRESH_MS); refreshTimers.set(slot.containerId, timer); } function processSlot(slot) { const container = document.getElementById(slot.containerId); if (!container) return; if (slot.containerId === 'aje_gcm_sb_ads' && !IS_SINGLE_POST) return; const match = slot.responsive.find( res => res.id && window.matchMedia(res.query).matches ); if (match) { injectIframeAd(container, match); // Sponsored By only becomes visible after a real creative is found. if (slot.containerId !== 'aje_gcm_sb_ads') { container.style.display = 'block'; } else { container.style.display = ''; } if (REFRESH_SLOT_IDS.has(slot.containerId)) { startRefreshTimer(slot, match); } else { clearRefreshTimer(slot.containerId); } } else { clearSlot(container); } } function checkSlots(mode = 'all') { adSlots.forEach(slot => { const isCritical = CRITICAL_SLOT_IDS.has(slot.containerId); const isScrollBound = SCROLL_SLOT_IDS.has(slot.containerId); if (mode === 'critical') { if (!isCritical) return; } else if (mode === 'delayed') { if (isCritical || isScrollBound) return; } else if (mode === 'scroll') { if (!isScrollBound) return; } processSlot(slot); }); } function debounce(fn, wait) { let t; return function() { clearTimeout(t); t = setTimeout(fn, wait); }; } /** * Handle viewport / breakpoint changes. * * Only process: * - critical slots * - scroll slots that have already activated * - delayed slots that IntersectionObserver already activated * * This prevents resize/orientation events from loading * every below-fold advertisement. */ const handleChange = debounce(() => { adSlots.forEach(slot => { const isCritical = CRITICAL_SLOT_IDS.has(slot.containerId); const isScrollBound = SCROLL_SLOT_IDS.has(slot.containerId); const isActivatedDelayed = activatedDelayedSlots.has(slot.containerId); /** * Critical ads may always react to * responsive breakpoint changes. */ if (isCritical) { processSlot(slot); return; } /** * Adhesion / scroll-bound placements should * only react after they have been activated. */ if (isScrollBound) { if (scrollStarted) { processSlot(slot); } return; } /** * Below-fold delayed ads should only react * after IntersectionObserver activates * that specific placement. */ if (isActivatedDelayed) { processSlot(slot); } }); }, 150); /** * Watch responsive breakpoints. */ function bindMediaListeners() { const allQueries = [ ...new Set( adSlots.flatMap( slot => slot.responsive.map( res => res.query ) ) ) ]; allQueries.forEach(query => { const mql = window.matchMedia(query); if ( typeof mql.addEventListener === 'function' ) { mql.addEventListener( 'change', handleChange ); } else if ( typeof mql.addListener === 'function' ) { mql.addListener( handleChange ); } }); window.addEventListener( 'resize', handleChange, { passive: true } ); window.addEventListener( 'orientationchange', handleChange, { passive: true } ); } /** * Observe non-critical, non-adhesion ads. * * Ads begin loading when they come within * approximately 800px of the viewport. */ function setupDelayedAds() { const delayedSlots = adSlots.filter(slot => { return ( !CRITICAL_SLOT_IDS.has( slot.containerId ) && !SCROLL_SLOT_IDS.has( slot.containerId ) ); }); if (!delayedSlots.length) { return; } /** * Fallback for browsers without * IntersectionObserver. */ if (!('IntersectionObserver' in window)) { setTimeout(() => { delayedSlots.forEach(slot => { activatedDelayedSlots.add( slot.containerId ); processSlot(slot); }); }, DELAY_MS); return; } const observer = new IntersectionObserver( entries => { entries.forEach(entry => { if (!entry.isIntersecting) { return; } // For a collapsed Sponsored By wrapper the observer // watches a 1x1 sentinel with a pointer to its slot. const target = entry.target; const containerId = target.dataset.gcmSlotContainer || target.id; const container = document.getElementById(containerId); const slot = delayedSlots.find( item => item.containerId === containerId ); if (!slot || !container) { observer.unobserve(target); return; } /** * Record that this specific * placement is now allowed * to load and respond to * breakpoint changes. */ activatedDelayedSlots.add( slot.containerId ); processSlot(slot); /** * Only trigger this placement * once. */ observer.unobserve(target); if (target !== container) target.remove(); }); }, { /** * Begin requesting the ad before * it actually enters the viewport. */ rootMargin: '800px 0px', threshold: 0 } ); delayedSlots.forEach(slot => { const container = document.getElementById( slot.containerId ); if (container) { if (slot.containerId === 'aje_gcm_sb_ads') { // The unit must occupy no space on no-fill, but a zero-height // box isn't a reliable IntersectionObserver target. const sentinel = document.createElement('span'); sentinel.dataset.gcmSlotContainer = container.id; sentinel.setAttribute('aria-hidden', 'true'); sentinel.style.cssText = 'position:absolute;top:0;left:0;width:1px;height:1px;opacity:0;pointer-events:none;'; container.appendChild(sentinel); container.dataset.gcmSponsorStatus = 'waiting'; observer.observe(sentinel); } else { observer.observe(container); } } }); } /** * Activate adhesion ads after the * visitor begins scrolling. */ function startScrollAds() { if (scrollStarted) { return; } scrollStarted = true; checkSlots('scroll'); } /** * Trigger adhesion inventory after * 200px of page scroll. */ function setupScrollTrigger() { const handleScroll = () => { if (window.scrollY >= 200) { startScrollAds(); window.removeEventListener( 'scroll', handleScroll ); } }; window.addEventListener( 'scroll', handleScroll, { passive: true } ); } /** * Pause adhesion refresh while the * page is hidden or does not have focus. */ function setupFocusVisibilityRefresh() { document.addEventListener( 'visibilitychange', () => { pageHasFocus = document.hasFocus(); if (!canRefreshAds()) { REFRESH_SLOT_IDS.forEach( clearRefreshTimer ); return; } adSlots.forEach(slot => { if ( !REFRESH_SLOT_IDS.has( slot.containerId ) ) { return; } processSlot(slot); }); } ); window.addEventListener( 'focus', () => { pageHasFocus = true; adSlots.forEach(slot => { if ( !REFRESH_SLOT_IDS.has( slot.containerId ) ) { return; } processSlot(slot); }); } ); window.addEventListener( 'blur', () => { pageHasFocus = false; REFRESH_SLOT_IDS.forEach( clearRefreshTimer ); } ); } /** * Normalize Sponsored By shortcode output before slot discovery. * * Existing sites may still have the original shortcode markup using * aje_gcm_sponsored_by. Migrate that legacy ID to aje_gcm_sb_ads so the * NEW standard ad slot works without editing the article-layout call. * Recommended long-term: update shortcode registration in functions.php. */ function normalizeSponsoredByContainer() { if (!IS_SINGLE_POST) return; let container = document.getElementById('aje_gcm_sb_ads'); if (!container) { container = document.getElementById('aje_gcm_sponsored_by'); if (container) container.id = 'aje_gcm_sb_ads'; } if (!container) return; container.classList.add('gcm-sponsored-by'); container.setAttribute('aria-hidden', 'true'); if (!container.querySelector('.gcm-sponsored-by__label')) { const label = document.createElement('span'); label.className = 'gcm-sponsored-by__label'; label.textContent = 'Sponsored By'; container.prepend(label); } if (!container.querySelector('.gcm-sponsored-by__creative')) { const holder = document.createElement('div'); holder.className = 'gcm-sponsored-by__creative'; container.appendChild(holder); } } /** * Initialize NUI advertisements. */ function init() { normalizeSponsoredByContainer(); bindMediaListeners(); setupFocusVisibilityRefresh(); /** * Revenue-critical / above-fold inventory. */ checkSlots('critical'); /** * Adhesion inventory activates after * the visitor scrolls 200px. */ setupScrollTrigger(); /** * All remaining advertisements activate * only as they approach the viewport. */ if ( document.visibilityState === 'visible' ) { setupDelayedAds(); } else { document.addEventListener( 'visibilitychange', function onVisible() { if ( document.visibilityState !== 'visible' ) { return; } document.removeEventListener( 'visibilitychange', onVisible ); setupDelayedAds(); } ); } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init, { once: true }); } else { init(); } })();