Joshua Wells
SEO audit · LT Generatorsarrow_backBack to the thought starter
ltgenerators.com

LT Generators

SEO and Site Health Audit
AuditedSep 9, 2026 Prepared byJosh Wells Scope163 sitemap URLs, homepage lab runs, live checks
Lighthouse 12.8.2 scores, lab run Sep 9, 2026, mobile first
Performance
51mobile
64 desktop
Accessibility
93mobile
90 desktop
Best Practices
71mobile
74 desktop
SEO
92mobile
92 desktop
Headline lab metrics
Mobile LCP
31.5s
Simulated slow 4G. Observed in the same run: 4.6 s. Desktop 6.3 s.
Mobile FCP
8.4s
Desktop 1.7 s. Render-blocking scripts account for 1.8 s.
Page weight
7.3MB
7,302 KiB mobile, 7,887 KiB desktop.
Requests
272
Mobile homepage load. 278 on desktop.
TTFB
0.8to 2.1 s
Three curl samples of the HTML. Lighthouse root document 710 ms mobile, 310 ms desktop.

All timing figures are lab data from one Lighthouse run per form factor on a Mac in California. No Search Console, Analytics or CrUX field data was available. See Method and limits.

Section 00

Sixty-three findings. Here's how to read them without reading all of them.

This is a full audit of ltgenerators.com as it stood on Sep 9, 2026: every one of the 163 pages in the sitemap, lab runs of the homepage on a phone and a desktop, the structured data, the sitemaps, the robots file, the ad tags, and a snapshot of where the site shows up against the competitors. It is long on purpose. Every finding carries the evidence and the file it came from, so a developer can act on it without redoing the work, and so nothing here has to be taken on faith.

flag

Start with the five things in Section 01

The executive summary ends with the five fixes that matter most. Most of the rest of the document is the proof behind them.

checklist

Severity is about the business, not the lab

Trust and indexing problems rank above page-speed numbers, and page speed ranks above hygiene. Quick win means a WordPress developer can ship it this week without new copy from the owner.

verified

Section 02 is the good news

Nineteen things the site already does right, each with its check. They stay through any rebuild. The foundation is sound; the problems sit on top of it.

schedule

Section 09 turns it into a calendar

The roadmap groups the fixes into weeks, with effort estimates. If you read one section after the summary, read that one.

Critical1High17Medium29Low16· 55 of the 63 are quick wins

boltThe numbers are measured. The order is a conversation. Every timing figure is lab data from one run per device, and the search positions are a snapshot, not tracked rankings. With access to the site's Search Console this gets real impressions and clicks. What to fix first, and who does it, is something we'd work out together.

Section 01

Executive summary

LT Generators has a sound foundation. Every one of the 163 sitemap URLs loads, every page has a canonical and an index directive, the homepage title is right, the layout is stable and the license number is visible everywhere. The problems are in what sits on top of that. The homepage takes 31.5 seconds to show its main image in Lighthouse's mobile simulation and 6.3 seconds on desktop, because the hero is a 471 KB JPEG hidden inside a stylesheet and it shares the connection with a 2 MB booking widget, a 1 MB video player and 830 KB of duplicated Google tags. On a phone there is no number to tap anywhere on the homepage, and the mid-page phone button is not a link. The site tells Google its phone number is +1-800-XXX-XXXX and its address is 'Your Street Address', and spells its own name 'LTGenerator' in the schema on every page. Ten test, staging and demo pages, five identical battery-storage pages and three Malibu tag archives sit in the sitemap and are indexable, while the four real city pages have no links pointing at them. Most of this is settings work in WordPress, Yoast and Cloudflare that a developer can finish in the first two weeks.

The mobile homepage has 0 tappable phone numbers, and 55 of the 63 findings are quick wins.

1 critical, 17 high, 29 medium and 16 low findings follow in Section 03. Severity reflects business effect for a local installer: trust and indexing problems rank above lab performance numbers, and lab performance ranks above hygiene.

The five things to fix first
  1. Put a tap-to-call number in the mobile header and make the red-band phone button a real tel: link.Every mobile visitor can call, and Google Ads can count the calls it currently cannot see.
  2. Delete the placeholder Electrician schema widget and rename the Yoast organization from LTGenerator to LT Generators.Google stops reading a fake 800 number and 'Your Street Address' for a Van Nuys business.
  3. Re-export the hero as a 52 KB WebP with a preload, and load the ServiceTitan scheduler and YouTube player only on click.About 3 MB and 77 requests leave every page load, and both long phases of the LCP chain shrink.
  4. Trash the test pages, 301 the five battery-storage pages and both staging twins, and point robots.txt at the Yoast sitemap index.Demo and Test titles leave the index and the sitemap Google reads describes the real site.
  5. Link the four city pages from the nav, footer and homepage, and fix the WordPress site title and Yoast title template.The money pages get their first internal links, 145 titles shed the site-title boilerplate and 31 lose the misspelled tagline in one save.
Section 02

What is working

Nineteen things the audit found in good order. Each carries the file or check it came from. Keep them through the redesign.

Section 03

Findings

63 findings, numbered in severity order. Evidence is quoted from the data files or a live check dated Sep 9, 2026. Effort is a developer's estimate: Small is under a day, Medium is one to three days, Large is a multi-week content project. A quick win is anything a WordPress developer can ship this week without owner copy.

Index
No.FindingSeverityCategoryEffortQuick win
F01Mobile and tablet visitors get no tap-to-call, and the mid-page phone button is not a linkCriticalLocalSmallYes
F02Hero LCP is a CSS background image the browser cannot discover, mobile lab LCP 31.5 s, and the first screen has no headline or phoneHighPerformanceMediumYes
F03ServiceTitan scheduler pulls 2 MB of uncacheable JS across 65 requests on every page view and is initialized four timesHighPerformanceMediumYes
F04Images served only as PNG and JPEG at original encoding, 1.65 to 2 MB recoverable per pageHighPerformanceSmallYes
F05Ten test, staging and demo pages are indexable and in the Yoast sitemap, two of them near-duplicates of the homepage and residential pageHighIndexationSmallYes
F06Five service URLs serve one identical 3,068-word battery-storage page, self-canonical, at the slugs that should carry maintenance intentHighIndexationSmallYes
F07Four city landing pages have zero inbound internal links, exist only in the sitemap, and lose their own city queries to the homepage and a 2022 blog postHighOn-pageSmallYes
F08City page titles run 151 to 158 characters and end in a misspelled boilerplate suffix that repeats on 31 pagesHighOn-pageSmallYes
F09City landing pages carry under 300 words of real body copy, no city testimonials, and Malibu never mentions GeneracHighContentLargeYes
F10No page targets 'Generac dealer' or 'whole house generator' in a title, H1 or meta while a rival and a generacdealers.com microsite own the dealer SERPHighOn-pageMediumNo
F11Homepage Electrician JSON-LD ships placeholder phone, address and postal code, a downtown-LA geo pin, a 404 image and an @id collision with the Yoast OrganizationHighStructured dataSmallYes
F12Organization name is misspelled 'LTGenerator' on every page of the siteHighStructured dataSmallYes
F13The Google reviews badge sends visitors to a 2018 hardcoded Google Search URL, not the Business Profile, while aggregators hold 1 to 3 slots on every local query and Angi shows a 3.0HighLocalSmallYes
F14Street address exists on six pages only, business hours appear nowhere, and the homepage schema names a different cityHighLocalSmallYes
F15Service-area coverage gap: no pages for Pacific Palisades, Santa Monica, Brentwood, Encino, Calabasas or four named counties while the lead competitor runs 15 geo pagesHighLocalLargeNo
F16Malibu is split across seven indexable URLs, including three tag archives, and Google is choosing the 2022 blog post over the city pageHighIndexationSmallYes
F17Absent from commercial and maintenance SERPs, where All American Generator holds most results, and the maintenance tips post is a 404 with its 2018 PDF still in the advertised sitemapHighCompetitiveMediumNo
F18PSPS is the term Los Angeles homeowners search and it appears on almost none of the site, which has a dozen aging news posts instead of one evergreen outage pageHighContentMediumNo
F19YouTube embed loads the full player, 1 MB and 12 requests, on page load with no facadeMediumPerformanceSmallYes
F20Google tag stack loaded three ways via Site Kit, a 2018 hardcoded gtag block and GTM, including a dead Universal Analytics property, 830 KBMediumAnalyticsSmallYes
F21reCAPTCHA v3 loaded synchronously with three different site keys is the top main-thread cost, and Contact Form 7 assets load on pages without a formMediumPerformanceMediumYes
F22Nine synchronous scripts in the head, 30 sitewide, including a go_portfolio plugin unused on the page, cost 1.8 s of the 8.4 s mobile first paintMediumPerformanceMediumYes
F23Four blog thumbnails 7,300 to 8,700 px below the fold load eagerly at full size with no srcset, 1.5 MBMediumPerformanceSmallYes
F24Cloudflare passes every HTML request to origin and static assets expire in 4 hours, 145 short-lived resourcesMediumPerformanceSmallYes
F25TTFB 0.8 to 2.1 s on the homepage, and the crawl saw HTML responses up to 7.6 s on generator pagesMediumPerformanceMediumYes
F26Web fonts have no font-display, Font Awesome is the largest font cost, and a second Roboto origin comes from third-party embeds, 16 font files, 374 KBMediumPerformanceSmallYes
F27Thirty stylesheets, Font Awesome shipped three ways, three CSS files 98 to 99% unusedMediumPerformanceSmallYes
F28Conversion tracking is degraded: legacy ga() call, dead gtag_report_conversion, triple-bound CTA handler and a head script that throws on every pageMediumAnalyticsMediumYes
F29robots.txt points crawlers at a static 2019 sitemap with dead URLs, slash variants, an ID slug and a PDF, not the live Yoast indexMediumIndexationSmallYes
F30Kohler pages are duplicated, thin and triple-H1 with a typo, in a SERP LT half-owns, and terms and contact pages also have indexable twinsMediumOn-pageSmallYes
F31The /thank-you/ conversion page, two 16-word calendar stubs, a survey and six duplicate lead-form pages are indexable, and the Ads conversion fires on any thank-you pageviewMediumIndexationMediumYes
F32Sitewide title template pushes 149 of 163 titles past 70 characters and stamps 'Archives' on 44 pages with brand-pair duplicatesMediumOn-pageSmallYes
F33One meta description with a 'Belair' typo is shared by 31 pages, 29 pages including all generator-cat and project-install pages have none, 22 run over 160 charactersMediumOn-pageMediumYes
F34Homepage H1 is hard-coded ALL CAPS, omits location and installation, and heading levels jump H1 to H3 to H6 with design fragments as headingsMediumOn-pageSmallYes
F3519 pages have no H1 and 6 have multiple H1s, including About, Contact, Commercial, Financing and two Kohler pages with a typoMediumOn-pageMediumYes
F36Residential and commercial product grids link the identical 34 products, with H4 then H2 heading pairs and a duplicated grid in the DOMMediumOn-pageMediumYes
F37Blog is news-reactive and stale, newest original post June 2024, homepage promotes a 2020 post as news, nothing on the 2025 to 2026 fire cycleMediumContentMediumNo
F38Product schema on all 33 generator pages is ineligible for rich results, and the one priced page states a price and image the page does not showMediumStructured dataMediumYes
F39Entity profile URLs and citations are inconsistent: sameAs disagrees across nodes and footer, two Yelp and Facebook slugs, HomeAdvisor under a legacy name, no GBP link anywhereMediumLocalMediumYes
F40Hand-pasted JSON-LD in three Elementor HTML widgets is the root cause of the placeholder, hidden-FAQ and price-mismatch errorsMediumTechnicalMediumYes
F41Two phone numbers circulate: (818) 787-1470 on the site and most citations, (818) 527-4742 on the Kohler dealer microsite, and that microsite outranks the site for the brand plus city queryMediumLocalSmallYes
F42Testimonials page has 46 city-attributed reviews but no link to any review platform and no way to verify the 200+ claimMediumContentMediumYes
F43Three different largest-installer superlatives, none substantiated on the page that makes themMediumContentSmallYes
F44The Tesla Powerwall vs generator URL is a 404 the advertised sitemap still lists, and the surviving solar post never says PowerwallMediumContentMediumNo
F45No cost guide while a competitor dealer's 2026 cost article ranks for both cost and Malibu queriesMediumContentMediumNo
F46Permit, HOA, noise and fuel-choice guides are missing even though LT holds the three licenses that make it the authorityMediumContentMediumNo
F47Brand red #f20500 fails WCAG AA on 19 to 20 homepage elements including a CTA, review links and the newsletter buttonMediumAccessibilitySmallYes
F48No preconnect hints for any third-party origin, and 3.2 s of mobile main-thread work remainsLowPerformanceSmallYes
F49robots.txt has two User-agent: * groups and contradicts itself on Google-Extended and Applebot-ExtendedLowTechnicalSmallYes
F5047 of 163 sitemap URLs are tag, category, author and generator-cat archives, many near-duplicates, 18 with no meta descriptionLowIndexationMediumYes
F51Every 404 renders a 103 KB uncached full-theme page with 59 scripts and no H1 to H3, including misses for standard icon and manifest filesLowTechnicalSmallYes
F52www URLs take two redirect hops and no HSTS, CSP, frame, referrer or content-type policy headers are sentLowTechnicalSmallYes
F53241 images without alt text sitewide by the crawler's count, 11 content images on the residential page ship alt="", and homepage alts are genericLowAccessibilitySmallYes
F54Footer Home link points at a redirecting URL, the hero CTA is href="#", and blog cards carry empty-text overlay linksLowOn-pageSmallYes
F55FAQPage markup on /faqs/ contains two questions that are not on the page, including a hidden $10,000 to $20,000 price claimLowStructured dataSmallYes
F56Article authorship points at third-party vendor accounts with no bios, bylines or photosLowContentMediumYes
F57Organization logo is 300x73, the homepage primary and share image is a 145x139 icon, and no Open Graph tags are output on any pageLowStructured dataSmallYes
F58License, founding year and dealer credentials are visible in prose but absent from the Organization entityLowStructured dataSmallYes
F59Service schema on 10 project-install pages uses an anonymous LocalBusiness provider instead of the site's Organization entityLowStructured dataSmallYes
F60WebSite schema name and description are raw WordPress settings, including an unverifiable '#1' taglineLowStructured dataSmallYes
F61Icon-only social links have no accessible name and desktop submenu carets are 8 px targetsLowAccessibilitySmallYes
F62Three third-party cookies set on load, including a DoubleClick ad cookie, with no consent mechanismLowTechnicalSmallYes
F63Root favicon.ico is a 43-byte blank GIF and there is no web manifest or root apple-touch-iconLowTechnicalSmallYes

Critical

1
F01 Critical CategoryLocal EffortSmall Quick winYes

Mobile and tablet visitors get no tap-to-call, and the mid-page phone button is not a link

Evidence

homepage.html lines 422 to 423: the header row holding the phone and CTA opens <div class="header--row header-top hide-on-mobile hide-on-tablet layout-full-contained header--row" data-row-id="top" data-show-on="desktop">. Line 434 inside it holds the page's only tel link, <a href="tel:8187871470" class="yellow-border-btn">. grep on homepage.html: href="tel: appears 1 time, (818) 787-1470 appears 2 times. The mobile header row at lines 518 to 624, data-show-on="mobile", contains only the brand logo link, the menu-mobile-toggle button and the slide-out menu panel, with no tel link. Line 1051, the red-band phone button, is <a class="elementor-button elementor-size-md" role="button"> with no href at all, wrapping an fa-phone icon and the text span (818) 787-1470 on line 1056. live-mobile-fold.png shows logo and MENU only in the header, then a Book Free Residential Assessment button and a Generator Maintenance & Services button, with no phone number in the first viewport. crawl-summary.txt line 75 reports tel: links on 161 of 163 pages, which is this same desktop-only header row repeated sitewide. Live check Sep 9, 2026: curl of https://www.googletagmanager.com/gtm.js?id=GTM-N7XTZCZ, container id from domain-tech.txt line 75, 462,725 bytes, contains __awcc 5 times and a tag with "vtp_phoneConversionNumber":"(818) 787-1470", so Google Ads call reporting depends on that number being a visible text element. The tag name "Calls from website" is not exposed in the public container and could not be verified.

Impact

A generator installer sells 24-hour emergency service to a mobile-first audience and the phone is the highest-intent conversion. On phones and tablets the homepage has zero tappable numbers, the one phone-styled button is inert on every device, and the Ads call conversion cannot swap or count a hidden number, so paid data understates calls. The number is still in the DOM for indexing. The cost is lost calls and blind paid attribution, not rankings.

Recommendation

Three edits, in this order, all in the WordPress admin with no code.

1. Neve header, mobile row. Appearance > Customize > Header Builder, switch the builder to the Mobile tab. Add a Button component to the Main row beside the MENU toggle, or to the Bottom row if the Main row is crowded. Label (818) 787-1470, link tel:+18187871470, same tab, CSS class yellow-border-btn so it matches desktop. Add a second Button labeled Get Free Assessment with link # and CSS class open-widget-btn so the existing widget script binds to it. Publish once and it ships to all 163 pages through the shared header.

2. Elementor red band and footer. Open the homepage in Elementor, select the phone button in the get-free section, set Content > Link to tel:+18187871470, Update. Then open Templates > Theme Builder > Footer and add the same tel: button plus the street address, so every page carries a tappable NAP below the fold. The footer currently has no phone at all.

3. Optional sticky bar. Install the Call Now Button plugin, pick the Full width bar type, add Call to tel:+18187871470 and Book to the assessment form, show on mobile only. Skip this if step 1 puts the phone in the first viewport.

Verify on a real phone, not desktop emulation. Tap the header and red-band buttons and confirm the dialer opens. Then in Google Ads go to Goals > Conversions and confirm the phone conversion number to replace reads exactly (818) 787-1470, matching the on-page label. Keep that format identical everywhere so the swap script matches. Place a test call from an ad click and confirm it lands in the conversion within 48 hours.

Same pass, related: the hand-coded Electrician schema on the homepage still carries telephone +1-800-XXX-XXXX and streetAddress Your Street Address. Replace both with the real values so NAP in schema matches the header.

Pages. Header on all 163 pages via the Neve header template, plus the homepage red-band phone button

High

17
F02 High CategoryPerformance EffortMedium Quick winYes

Hero LCP is a CSS background image the browser cannot discover, mobile lab LCP 31.5 s, and the first screen has no headline or phone

Evidence

lighthouse-summary.json mobile: LCP 31.5 s, FCP 8.4 s, speed-index 16.2 s, TTI 35.9 s, performance 51. Desktop LCP 6.3 s, performance 64. LCP element section.elementor-section data-id 1ab25c46, 412x400 on mobile. LCP phases: TTFB 969 ms, Load Delay 15,032 ms at 48%, Load Time 14,461 ms at 46%, Render Delay 1,046 ms at 3%. lcp-discovery-insight: requestDiscoverable false, priorityHinted false. Trace: post-8088.css finishes 2,140 ms, LTG-HP-Banner-Image.jpg requested 2,345 ms, finishes 4,464 ms, 471,431 bytes, initiator path image > post-8088.css > document. Live curl of uploads/elementor/css/post-8088.css confirms the background-image rule on .elementor-element-1ab25c46. homepage.html: 30 rel=preload tags, all as=style, 0 as=image, 0 as=font, 0 fetchpriority across 27 img tags, the JPEG URL does not appear anywhere in the HTML. live-mobile-fold.png: logo, MENU, a 400 px collage, two buttons, no H1, phone, license or since-1982 line in view. The H1 sits at line 756 inside section 29bca5bd, background-color #F20500, below the hero, two button CTAs to /contact-us/ and /services-maintenance/, and two paragraphs. image-delivery-insight puts the banner savings at 349,831 bytes. The same run observed LCP at 4.6 s, observedLargestContentfulPaint 4,618 ms, and the unthrottled desktop profile in perf-profile.json measured 2,248 ms. Scope: /home-staging/ duplicates the section id 1ab25c46, title and H1 per crawl.json, but a live curl shows its hero uses a YouTube background video, so the image preload fix applies to the homepage only.

Impact

LCP is the Core Web Vital Google weights in page experience and every other fix is capped by this chain: HTML, then external CSS, then layout, then a 471 KB JPEG. On the mobile rendering the first screen tells a person nothing about what the business does or where, and there is no phone to tap. Title, H1 and canonical are present, so crawlers still read the page.

Recommendation

Do this in two passes.

Pass 1, about 30 minutes, takes the image off the critical path. Export LTG-HP-Banner-Image.jpg, currently 1520 by 480 and 471 KB, as WebP under 120 KB with Squoosh or ImageOptim, upload it to the Media Library as a new file, and set it as the background of section 1ab25c46 in Elementor under Style > Background. Add a mobile crop while you are there: click the responsive toggle on the background image control and upload a roughly 800 by 800 WebP under 70 KB for the mobile breakpoint, because the section renders 412 by 400 with background-size cover and discards two thirds of the wide file on phones. Then add preloads with WPCode, or in neve-child functions.php, on wp_head at priority 1, guarded by is_front_page: one <link rel="preload" as="image" fetchpriority="high" href="DESKTOP.webp" media="(min-width: 768px)"> and one for the mobile file with media="(max-width: 767px)", matching Elementor's default 767px mobile breakpoint. The href must match the URL in post-8088.css byte for byte, including any query string, or the browser downloads both files and LCP gets worse. The live post-8088.css has no responsive rule for that section today, so a single preload is safe until the mobile crop exists. Skip on-the-fly WebP rewrite plugins for this file: Cloudflare caches by URL and does not vary on Accept on the free plan, so upload real .webp files with their own URLs. Cloudflare Pro Polish with WebP is the alternative if the client wants the whole media library handled at the edge.

Pass 2, half a day, puts the business in the first screen. The hero section is empty, its only column c352f4 has no widgets, and the page's single tel: link sits in a Neve header row carrying hide-on-mobile hide-on-tablet, so mobile visitors cannot tap to call anywhere on the homepage. Inside section 1ab25c46 add a Heading widget set to H1 with the existing headline, demote the current H1 in widget d391a40 to H2, add a one-line Text widget using copy the site already states, "Licensed C10, C36, B1. CA Lic #602073. Since 1982. Generac Premier PowerPro Dealer", and a Button widget linking to tel:8187871470 with 818-787-1470 as its label. Put the text on a solid band or dark overlay so it passes contrast against the collage. If you prefer a real img for discoverability, convert the section to a Container with an Image widget and the text in an overlaid inner container, then confirm Elementor > Settings > Performance > Optimized Image Loading is Enabled so the widget gets fetchpriority=high, per Elementor's help page, and check the rendered HTML for the known bug where it also adds loading=lazy to the same image. WordPress 6.3 and later assign fetchpriority=high to the first large in-content image on their own, so either path works. Keep the preload only if the hero stays a background image.

Reporting and scope. The 31.5 s figure is Lighthouse's slow-4G, 4x CPU simulation. The same run observed LCP at 4.6 s, and the unthrottled desktop profile measured 2.2 s. After the fix, pull field data from Search Console > Core Web Vitals or the CrUX dashboard once the PageSpeed API quota resets, since Google ranks on field p75, not lab numbers. If the origin has no CrUX record there is no ranking effect either way, and the fix stands on Google Ads landing-page experience and phone calls. Leave /home-staging/ to the duplicate-content finding. The render-blocking go_portfolio, jQuery and reCAPTCHA scripts drive the 8.4 s simulated FCP and have their own finding. They account for about 1 s of the LCP chain.

Pages. Homepage. Check every post-*.css for the same background-image hero pattern.

F03 High CategoryPerformance EffortMedium Quick winYes

ServiceTitan scheduler pulls 2 MB of uncacheable JS across 65 requests on every page view and is initialized four times

Evidence

lighthouse-summary.json third_party servicetitan.com transferSize 2,017,123 bytes mobile, 2,016,101 desktop, the largest third party. lighthouse-mobile.report.json network-requests: 65 servicetitan.com requests, 1,970 KB, first at 2,344 ms, last at 5,819 ms. 4.e800495b.js 1,265,166 bytes cacheLifetimeMs 0, iconsVendor 269,755, bundle.css 164,451, reactVendor 125,524, all cacheLifetimeMs 0 in uses-long-cache-ttl, 8 graphql calls, 4 iframe loads of webscheduler-widget.servicetitan.com/?token=a4e51280-5951-4fe4-9d74-cc9c7ec58450, plus 32 Segment requests inside the widget, 24 cdn.segment.com, 8 api.segment.io. homepage.html carries the shim.js init snippet four times at lines 412, 416, 1296 and 1412, the 412 copy registering lowercase stwidgetmanager, the last two inside footer widget_block id="block-27", footer opens line 1249, rendered twice. third_party_cookies: __cf_bm from static.servicetitan.com/webscheduler/shim.js. The widget only opens on click via STWidgetManager('ws-open'), lines 191, 1545, 1564, plus two entity-encoded copies. uses-rel-preconnect wastedMs 375.5 static.servicetitan.com and 393.8 webscheduler-widget.servicetitan.com. Page totals: 7,302 KiB, 7,477,437 bytes, 272 requests mobile.

Impact

27% of page bytes and 24% of requests serve a booking modal nobody has opened. Zero cache lifetime means repeat visitors re-download 1.9 MB per page. Four inits mean duplicate iframe spin-up and GraphQL traffic, and the download competes with the hero image during the LCP window. Lighthouse reports 0 ms blocking time for the entity, so this is bandwidth and mobile conversion, not interactivity.

Recommendation

Fix in three steps, all doable this week.

Step 1, remove the duplicates. There are only two sources, not four. Lines 1296 and 1412 are the same footer widget, block-27, rendered twice by the Neve footer, so deleting that one Custom HTML block under Appearance > Widgets removes both. Lines 412 and 416 both fire on wp_body_open. Grep the Neve child theme for "webscheduler", then check WPCode, Insert Headers and Footers, body snippets and Elementor Pro > Custom Code for anything at "body start". Delete the line 412 copy first. Its lowercase 'stwidgetmanager' is a typo that registers a second global nothing ever calls, but it still injects shim.js and runs a full init. Keep one snippet total.

Step 2, make the survivor click-to-load. Do not inject shim.js on page load. Keep the stub queue and the init call, which cost no network, and inject the script on first user intent. Replace the remaining snippet with this, in WPCode or Elementor Custom Code at body start, all pages:

<script>
(function(w,d){
  var t='STWidgetManager';
  w[t]=w[t]||function(){(w[t].q=w[t].q||[]).push(arguments)};
  w[t].l=1*new Date();
  w[t]('init','a4e51280-5951-4fe4-9d74-cc9c7ec58450');
  var sel='.open-widget-btn,.free-assessment,[onclick*="STWidgetManager"]';
  var done=false;
  function load(){
    if(done)return;done=true;
    ['https://static.servicetitan.com','https://webscheduler-widget.servicetitan.com'].forEach(function(o){
      var l=d.createElement('link');l.rel='preconnect';l.href=o;l.crossOrigin='';d.head.appendChild(l);
    });
    var s=d.createElement('script');s.async=true;
    s.src='https://static.servicetitan.com/webscheduler/shim.js';
    d.head.appendChild(s);
  }
  ['pointerover','touchstart','focusin'].forEach(function(ev){
    d.addEventListener(ev,function(e){if(e.target.closest&&e.target.closest(sel))load();},{passive:true,capture:true});
  });
  d.addEventListener('click',function(e){if(e.target.closest&&e.target.closest(sel))load();},true);
})(window,document);
</script>

Every existing trigger keeps working unchanged. The buttons at lines 842 and 1098 call STWidgetManager('ws-open') inline, and the .open-widget-btn listeners at lines 191, 1545 and 1564 do the same. The stub queues ws-open behind init, the shim drains the queue when it arrives, and the modal opens. Hover and touchstart start the download a few hundred milliseconds before the click lands. Add a brief loading state to the button because a cold 4G tap will wait two to four seconds for the 1.2 MB bundle. This is the facade pattern Lighthouse's third-party-facades audit and web.dev recommend for booking and chat widgets.

Step 3, skip a site-wide preconnect. Once the scheduler loads only on intent, a global preconnect on every page view is a wasted socket for the visitors who never click. The on-demand preconnect inside load() above replaces it. Still open a ServiceTitan support ticket asking for Cache-Control headers on the webscheduler-widget.servicetitan.com bundles, but it is not a blocker.

Verify by reloading the homepage with the Network panel filtered to servicetitan and segment. Expect zero requests until a Get Free Assessment button is hovered or tapped, then one iframe, not four. Re-run Lighthouse mobile and confirm roughly 2 MB and 60 requests are gone from initial load and the __cf_bm third-party cookie audit no longer fires.

Pages. Site-wide, header template and both footer widgets.

F04 High CategoryPerformance EffortSmall Quick winYes

Images served only as PNG and JPEG at original encoding, 1.65 to 2 MB recoverable per page

Evidence

lighthouse-summary.json modern_image_formats, identical on mobile and desktop: PowerLineImage.png 515,130 bytes with 474,354 wasted, LTG-HP-Banner-Image.jpg 471,431 with 383,214 wasted, Earthquakes.png 321,666 with 292,951 wasted, SolarHouseImage.jpg 354,496 with 250,870 wasted, HurricaneHilary.png 332,324 with 241,527 wasted. Full reports: modern-image-formats Est savings 1,650 KiB mobile and 1,988 KiB desktop. uses-optimized-images 418 KiB mobile and 536 KiB desktop. image-delivery-insight 1,485 KiB mobile and 2,407 KiB desktop. Desktop adds service-img1 to img4 at 104,730 to 205,518 bytes each, plus service-san-diego-img-500x302.jpg at 58,192. Image requests: 27 at 2,181 KiB mobile, 33 at 2,796 KiB desktop, against total-byte-weight 7,302 KiB mobile and 7,887 KiB desktop, 29.9% and 35.4%. Live curl Sep 9, 2026 of the hero with Accept: image/avif,image/webp returns content-type image/jpeg, content-length 471431, server cloudflare, cf-cache-status REVALIDATED, cache-control max-age=14400, no cf-polished or cf-resized header. The file is a 1520x480 baseline JPEG saved from Photoshop 22.4. homepage.html contains zero .webp or .avif references. The hero URL does not appear in homepage.html at all. It loads as a CSS background via elementor/css/post-8088.css. Live PNG checks: PowerLineImage.png 768x432 RGBA with 76,896 unique colors and Earthquakes.png 408x353 RGBA with 50,114 unique colors are photographic content stored as PNG. HurricaneHilary.png 875x711 RGBA is the NWS Hurricane Hilary forecast-cone map with 246 unique colors, so its waste is an unpaletted 32-bit PNG, not PNG-for-a-photo. All three PNGs carry a fully opaque alpha channel. A local cwebp test re-encoded the 471 KB hero to 52 KB at 1400 px wide, quality 75.

Impact

Images are 30% of mobile page bytes and nearly all of that is avoidable. The hero alone drops from 471 KB to about 52 KB as WebP, and the hero is the LCP resource. Scripts at 4.2 MB remain the bigger byte bucket.

Recommendation

Aim this at the hero first. The homepage LCP element is the Elementor section 1ab25c46 whose background is LTG-HP-Banner-Image.jpg, set in wp-content/uploads/elementor/css/post-8088.css. A CSS background is invisible to picture-tag delivery, so the plugin path alone misses the one image that matters. Do it in this order this week.

Step 1, re-export the hero. Source is 1520 by 480 JPEG at 471 KB. At 1400 px wide, WebP quality 75, it lands at 52 KB, verified locally with cwebp. Upload the .webp to the media library, WordPress has accepted WebP uploads since 5.8, and set it as the section background in Elementor on the homepage hero. Add an 800 px WebP as the mobile background using the same control's responsive toggle. Then run Elementor, Tools, Regenerate CSS and Data. Repeat for any /generator-installation-*/ page whose hero is also a section background.

Step 2, preload the hero. Format fixes the Load Time phase, which is 46 percent of mobile LCP, but Load Delay is another 48 percent because the browser cannot find a CSS background until post-8088.css parses behind nine render-blocking scripts. Add <link rel="preload" as="image" href="[hero webp URL]" fetchpriority="high"> to the homepage head via Elementor Pro, Custom Code, location head, condition homepage only. Use WPCode if Custom Code is unavailable. If you added the mobile variant, ship two preload tags with media="(max-width: 767px)" and media="(min-width: 768px)".

Step 3, convert the rest of the library with ShortPixel Image Optimizer. Settings: Lossy, Also create WebP versions on, Convert PNG images to JPEG on so Earthquakes and PowerLineImage stop shipping as photographic PNGs, Resize large images to 1600 px max, Deliver next-gen images using the picture tag option. Exclude HurricaneHilary.png from PNG-to-JPEG conversion: it is a map with 246 colors and flat text, so send it to palette PNG or lossless WebP instead. Do not pick the .htaccess rewrite mode. Cloudflare caches by URL and does not vary on Accept on this plan, so a same-URL rewrite can cache a WebP body under a .jpg URL and hand it to clients that never asked for it. Run Bulk ShortPixel. Check the media count first, the one-time 10,000 credit pack is about ten dollars and should cover a site this size.

Step 4, purge Cloudflare cache after the swap. Images sit at the edge with max-age 14400 and cf-cache-status HIT, so the old JPEGs keep serving for four hours otherwise.

Cloudflare Polish with WebP on is a valid zero-code alternative and would cover the CSS background too, but it is a Pro-plan feature at roughly 25 dollars a month and the live headers show it is not on today. Fix the files instead, it is free and survives a plan change. Re-run Lighthouse afterward and expect the mobile Load Time phase to drop by about two thirds. Then move to the render-blocking JS and third-party findings, which own the Load Delay phase.

Pages. Homepage confirmed. The crawl records image counts but not filenames, so other pages carrying these files were not verified.

F05 High CategoryIndexation EffortSmall Quick winYes

Ten test, staging and demo pages are indexable and in the Yoast sitemap, two of them near-duplicates of the homepage and residential page

Evidence

sitemaps.json page-sitemap.xml count 47 includes /test/, /test-2/, /test1/, /gen-test/, /cal-submit/, /cal-list/, /tabs-responsive/, /other-products-original/, /home-staging/, /our-residential-generators-staging/. crawl-summary.txt titles: '/test/ Demo - Backup Generators...', '/test1/ Test1 -...', '/cal-submit/ words 16', '/cal-list/ words 16'. crawl.json: all 163 URLs robots_meta 'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1', every canonical self-referencing on all 163, every status 200. / and /home-staging/ share title 'Standby Generators Installation Los Angeles | LT Generators', 59 chars, H1 'RESIDENTIAL AND COMMERCIAL BACKUP & STANDBY GENERATORS' and the same 140-char meta, 1122 vs 1131 words, 179347 vs 183431 bytes. /our-residential-generators/ and its -staging twin share a 72-char title and 137-char meta, 1485 vs 1290 words, 276524 vs 255851 bytes. The staging twin has zero H1s. Live curl Sep 9, 2026: https://ltgenerators.com/test/ returns 200, title 'Demo - Backup Generators & Installation in So. Cal. - LT Generators Residential & Commerical Backup Generators & Installation.', canonical https://ltgenerators.com/test/. homepage.html links to none of the flagged URLs, so the staging twins are orphans.

Impact

Pages titled Demo and Test1 are indexable under the brand name and sit in the sitemap Google is told to crawl, so a site: search or a brand query can hand a prospect a broken layout. The primary money term is split across two self-canonical URLs. Google clusters near-duplicates and picks one, and the linked homepage is the likely pick, so the ranking risk is modest. The trust exposure is the reason this is high.

Recommendation

Yoast SEO 28.3 free is what the homepage source shows, so there is no Yoast Redirects manager. Use the steps below.

1. Trash the pure test pages. In WP admin go to Pages, then move /test/, /test-2/, /test1/, /gen-test/, /tabs-responsive/ and /other-products-original/ to Trash. WordPress returns 404 for trashed pages and that is all Google needs, a 410 buys nothing worth adding a plugin for. Empty the trash after the client confirms nothing broke.

2. Check /cal-submit/ and /cal-list/ before trashing. They are 16 words, no form, no H1, which looks like a calendar or Constant Contact endpoint rather than an abandoned draft. Open each in Elementor and look for a shortcode or plugin block. If they do a job, keep them published and set Yoast Advanced, Allow search engines to show this Page in search results, to No. If they are empty, trash them with the rest.

3. Retire the two staging twins with 301s. Set /home-staging/ and /our-residential-generators-staging/ to Private, or trash them, so the public URL stops resolving. Then add the redirects with the free Redirection plugin under Tools, Redirection: /home-staging/ to / and /our-residential-generators-staging/ to /our-residential-generators/. The site is already behind Cloudflare, so a Redirect Rule under Rules, Redirect Rules does the same job without a plugin if the dev has dashboard access. Any future working copy belongs in Draft or Private status or on a real staging install, never Published.

4. Leave /thank-you/ alone in this pass. It is a live form redirect target, not a test page, and F31 covers keeping it live and setting it to noindex. Yoast drops noindexed, private and trashed pages from the sitemap on its own.

5. Verify and clean up in Search Console. Load /page-sitemap.xml and confirm the count fell from 47. If the old URLs still show, go to Yoast, Settings, Site features and toggle XML sitemaps off and on to clear the cache. In Search Console use Removals, New request, Temporarily remove URL for /test/, /test-2/, /test1/ and /gen-test/ so the Demo and Test titles disappear within about a day. That tool hides a URL for roughly six months, so the 404 is what makes it permanent. Resubmit sitemap_index.xml under Sitemaps. Check Indexing, Pages in two to three weeks and expect Not found 404 for the trashed pages and Page with redirect for the two staging URLs.

6. Sweep for the rest. Search Pages for test, staging, copy, original, old and demo. The crawl also shows /newsletter/ carrying the same 462 word boilerplate as /test-2/, and five 3068 word pages at /diagnostics/, /emergencies/, /maintenance/, /monitoring/ and /service/ that read as one template published five times. Those have their own finding.

Pages. /test/, /test-2/, /test1/, /gen-test/, /cal-submit/, /cal-list/, /tabs-responsive/, /other-products-original/, /home-staging/, /our-residential-generators-staging/

F06 High CategoryIndexation EffortSmall Quick winYes

Five service URLs serve one identical 3,068-word battery-storage page, self-canonical, at the slugs that should carry maintenance intent

Evidence

crawl.json: /diagnostics/, /emergencies/, /maintenance/, /monitoring/, /service/ all words 3068, h1_count 0, h2_count 16, bytes 160,577 to 160,653, each canonical to itself, robots index, follow, max-image-preview:large. Live curl Sep 9, 2026, all five 200, 160,139 to 160,215 bytes: visible text with the <title> removed is byte-identical across all five, 3013 visible words, difflib ratio 1.0000. With the title included the ratio is 0.9994 to 0.9996 and the only word that differs is the slug name in the title, Maintenance vs Diagnostics vs Emergencies vs Monitoring vs Service. All five carry the same 16 H2s, including 'Welcome to an entirely new way to think about energy.', 'What does a battery mean to me?', 'Can I build an off-grid system?', 'How long do batteries last?', and 'How much experience does Baker have in energy storage?'. Titles 129 to 133 chars, all five share one meta description. The real page /services-maintenance/ has 847 words, title_len 69, and H1 'Generator Maintenance & Services', crawl.json. The live homepage nav links /services-maintenance/ five times and links none of the five slugs. SERP snapshot Sep 9, 2026, US web search, 'Generac generator maintenance service Los Angeles': 8 results, 6 from allamericangenerator.com, none from ltgenerators.com.

Impact

A prospect who lands on /maintenance/ reads a 3,000-word home-battery pitch on a generator installer's site. Google sees six pages competing for the same intent, five with no H1 and a canonical claiming each copy is the original. The pages are orphans reachable only through the sitemap, so the exposure is brand queries and old bookmarks, but this is the largest duplicate cluster on the site and it sits at commercial-intent slugs. LT's absence from the maintenance SERP is a separate content problem on /services-maintenance/.

Recommendation

Redirect first, then trash. Install the free Redirection plugin and add five 301 rules: /diagnostics/, /emergencies/, /maintenance/, /monitoring/ and /service/ each to https://ltgenerators.com/services-maintenance/. Cloudflare returns cf-cache-status DYNAMIC on these URLs, so the redirects go live without a cache purge. Verify each with curl -I and confirm a 301 with the right Location header. Then move the five pages to Trash under Pages in wp-admin, which makes Yoast drop them from page-sitemap.xml on its own. In Search Console, resubmit page-sitemap.xml and run URL Inspection with Request Indexing on /services-maintenance/. Do not use a canonical instead of the redirect. A 3,068-word battery page is not a near-duplicate of an 847-word maintenance page, so Google would treat that canonical as a hint and pick its own, and the five pages have no inbound links or nav placement worth preserving anyway. If the redesign later wants distinct diagnostics, emergency, monitoring or maintenance pages, publish them at the same slugs with one H1 and unique copy and delete that slug's redirect rule. Fold the other sitemap leftovers into the same pass, since page-sitemap.xml also lists /test/, /test-2/, /test1/, /gen-test/, /tabs-responsive/, /home-staging/, /our-residential-generators-staging/, /other-products-original/, /cal-submit/ and /cal-list/, all indexable and self-canonical. Whole pass is about an hour.

Pages. /diagnostics/, /emergencies/, /maintenance/, /monitoring/, /service/, /services-maintenance/

F07 High CategoryOn-page EffortSmall Quick winYes

Four city landing pages have zero inbound internal links, exist only in the sitemap, and lose their own city queries to the homepage and a 2022 blog post

Evidence

homepage.html: 121 anchors, 94 internal, resolving to 25 unique URLs, none to /generator-installation-bel-air/, -beverly-hills/, -los-angeles/ or -malibu/. Those four slugs do not appear anywhere in the 179 KB homepage source. The two <nav> blocks hold 15 unique hrefs, none a city page. The SERVICE & INSTALLATION AREAS section is five unlinked H6 image tiles for Los Angeles, Ventura, Santa Barbara, Orange County and San Diego built from heading, image and text-editor widgets only, with zero <a> tags between its H2 and the next H2. Live fetch Sep 9, 2026 of 41 pages, all HTTP 200: /our-residential-generators/, /our-commercial-generators/, /about-us/, /contact-us/, /careers/, /videos/, /power-outages/, /blog/, /faqs/, /testimonials/, /generators-installation/, /services-maintenance/, /financing/, /malibu-generator-install/, all 23 Article posts from crawl.json and the four city pages themselves. Zero <a> tags to any city page on any of them. Each city page carries 101 anchors, 0 to itself and 0 to the other three. The page URL appears only in its canonical <link> and JSON-LD. crawl.json: each city page is 746 to 828 words with 1 H1, 3 H2s and 57 outbound internal links. The Malibu page H2 reads "Permitting & Coastal Regulations for Malibu Generators" and mentions PSPS twice. /testimonials/ names Malibu 10 times, Beverly Hills 7, Bel Air 5. SERP positions for 'whole house generator Malibu', 'generator installer Beverly Hills', 'generator installation Malibu CA' and Bel Air are a Sep 9, 2026 US search snapshot, not tracked rankings: the homepage held #6 to #9 on those queries while dedicated competitor pages ranked above it, and /generator-installation-bel-air/ took #5 for 'generator installation Bel Air CA'.

Impact

These are LT's best-written pages, the Malibu page has H2s on Coastal permitting and mentions PSPS, and they receive no PageRank from home or nav. Google treats them as low priority, the homepage absorbs every city query at #6 to #9 against dedicated pages from smaller electricians, and users cannot find them. The fix is the largest ranking lever available this week at S effort.

Recommendation

Get each of the four city pages at least five inbound internal links from indexable pages, with at least two in body copy and descriptive anchors such as "generator installation in Malibu", then ask Google to recrawl. Work in this order. One, Appearance > Menus: add a Service Areas item to the primary menu with the four city pages as its dropdown, then confirm the Nav Menu widget in the Elementor Pro header template is pointed at that menu. Two, homepage in Elementor: rebuild the five unlinked area tiles as a Service Areas grid of Image Box widgets, set each box's Link field to its city page, and hold the Ventura, Santa Barbara, Orange County and San Diego tiles until those pages exist rather than shipping tiles that link nowhere. Three, footer template in Elementor Pro Theme Builder: add an Icon List widget headed Areas We Serve with the four links. Four, on each city page add a short Nearby Areas We Serve paragraph linking the other three. Five, add one contextual body link to each city page from /our-residential-generators/, and link the Malibu, Beverly Hills and Bel Air testimonials on /testimonials/ to their city pages. Six, the 2022 post /malibu-generator-install/ is a 1,431 word article that already ranks, so keep it and add a first-paragraph link to /generator-installation-malibu/ instead of redirecting it. In Yoast SEO > Settings > Categories & tags, set Tags to not show in search results so the indexable /tag/malibu-generator-install/ archive stops competing for the same query. Seven, in Search Console run URL Inspection > Request Indexing on the homepage and all four city pages, then check Links > Internal links in two weeks to confirm each city page shows five or more.

Pages. /generator-installation-malibu/, /generator-installation-beverly-hills/, /generator-installation-bel-air/, /generator-installation-los-angeles/, plus homepage, nav and footer

F08 High CategoryOn-page EffortSmall Quick winYes

City page titles run 151 to 158 characters and end in a misspelled boilerplate suffix that repeats on 31 pages

Evidence

crawl.json: /generator-installation-beverly-hills/ title 158 chars 'Generator Installation Beverly Hills - Backup Generators & Installation in So. Cal. - LT Generators Residential & Commerical Backup Generators & Installation.', Bel Air 152, Los Angeles 156, Malibu 151, live curl of the Beverly Hills page on Sep 9, 2026 returns the identical 158-char title. The exact suffix ' - Backup Generators & Installation in So. Cal. - LT Generators Residential & Commerical Backup Generators & Installation.' appears on 31 titles, all 31 also contain the misspelling 'Commerical', including /privacy-policy/ 136, /kohler/ 128, /power-outage-preparation-free-assessment/ 162. The shorter fragment ' - Backup Generators & Installation in So. Cal.' appears on 145 of 163 titles, and crawl-summary.txt reports 149 titles over 70 chars, 4 in the 30 to 60 range. The 31 suffix pages are a subset of the 'Over 70 chars' list. og:title is empty on every crawled page. Google displays roughly 60 characters or about 600 px.

Impact

The SERP shows 'Generator Installation Beverly Hills - Backup Generators & Insta...' and the brand name never appears, or Google rewrites the title from the H1. 'Commerical' is a public spelling error in the title tag of the four money pages, and with Open Graph off, every shared link in iMessage, LinkedIn and Facebook falls back to the full misspelled title. The duplicate suffix dilutes the unique keyword signal on every page.

Recommendation

Confirmed from the crawl: Yoast SEO 28.3 on a Neve child theme with Elementor Pro. The title pattern decodes to a Yoast Pages template of "%%title%% %%sep%% %%sitename%% %%sitedesc%%" with the WordPress Site Title set to "Backup Generators & Installation in So. Cal. - LT Generators" and the Tagline set to "Residential & Commerical Backup Generators & Installation." The Site Title string appears in 145 of 163 crawled titles and the misspelled Tagline in 31, so fix the source fields once rather than editing pages.

Step 1. Settings > General. Site Title = "LT Generators". Tagline = "Standby Generator Installation in Los Angeles" with no trailing period. Elementor Pro exposes the same two fields under Site Settings > Site Identity, so either screen works. This alone shortens 145 titles, including the 14 generator-cat archive titles and the 7 duplicate archive pairs.

Step 2. Yoast SEO > Settings > Content types > Pages > SEO title. Replace the template with "%%title%% %%sep%% %%sitename%%" and delete %%sitedesc%%. Check Posts, the generator custom post type, and Categories & tags for the same variable and remove it there too. Set the separator to "|" under Yoast > Settings > Site basics.

Step 3. Hand-write the four city titles in the Yoast sidebar on each page, reached through Pages > Edit > Yoast SEO box or the Yoast tab inside the Elementor editor. Use one pattern that mirrors the existing H1s, which already read "Premier Standby Generator Installation in {City}, CA". Titles: "Standby Generator Installation Malibu, CA | LT Generators" at 57. "Standby Generator Installation Bel Air, CA | LT Generators" at 58. "Standby Generator Installation Los Angeles | LT Generators" at 58. "Generator Installation Beverly Hills, CA | LT Generators" at 56. Do not mix "Installer" and "Installation" across the four.

Step 4. Purge caches and verify. Cloudflare dashboard > Caching > Purge Everything, plus any WP cache plugin. View source on one city page to confirm the new title. Then Google Search Console > URL Inspection > Request Indexing on the four city URLs and the homepage.

Step 5. og:title is empty on every crawled page, so link previews currently fall back to the full misspelled title tag. Turn on Yoast > Settings > Site features > Social sharing > Open Graph data so previews pick up the corrected titles. The missing Open Graph output has its own finding.

Treat 60 characters as a display target, not a rule. Google truncates by pixel width near 600px and has no character limit. Title length is not a ranking factor. Google's title-link guidance still says avoid repeated boilerplate, keep the brand short, and expect Google to rewrite over-length titles from the H1 or other on-page text.

Pages. The 4 /generator-installation-*/ pages and the 31 pages carrying the long suffix, all inside the crawl-summary.txt 'Over 70 chars' list

F09 High CategoryContent EffortLarge Quick winYes

City landing pages carry under 300 words of real body copy, no city testimonials, and Malibu never mentions Generac

Evidence

crawl.json words: Malibu 746, Beverly Hills 766, Los Angeles 814, Bel Air 828, including header, footer and sidebar. Live fetch Sep 9, 2026 text inside <main>, whitespace split: Malibu 278 words, Beverly Hills 298. Each page per crawl.json: 3 images with 1 missing alt, 3 H2s, 57 internal links out. Inbound: homepage.html contains no link to any city page, and the four pages' own shared nav/footer link only to themselves, never to a sibling city page. Malibu page: 'generac' 0, 'dealer' 0, 'whole house' 0, both inside <main> and across the whole page including header and footer. Beverly Hills: 'generac' 1, in the line "Authorized Generac Premier PowerPro Dealer, plus Kohler". BRIEF.md lines 56 to 60 list public testimonials from Lani H., Ric R. and Patricia S. of Malibu and Andrea Layne of Beverly Hills as appearing on /testimonials/. None of those four names appear anywhere on the live Malibu or Beverly Hills city page. Word-sequence similarity Malibu vs Beverly Hills <main> text is 0.153, difflib SequenceMatcher on word tokens, so the copy is genuinely unique.

Impact

Under 300 words with one photo is thin against national lead-gen sites and Generac's dealer locator for 'generator installer Beverly Hills'. Without the Premier PowerPro dealer line, whole-house wording, local proof or photos, the pages fail to show experience in that city, and the proof already exists on /testimonials/ so leaving it off is a choice, not a constraint.

Recommendation

Fix the orphan before touching copy. The crawl shows the four city pages reach Google only through page-sitemap.xml: the homepage HTML has zero links to them and no crawled page links in. In Appearance > Menus add a Service Areas parent item with Malibu, Beverly Hills, Bel Air and Los Angeles as children, then in Elementor Theme Builder > Footer add a Service areas column with the same four links so every page on the site links to them. Add a one-line "Serving Malibu, Beverly Hills, Bel Air and greater Los Angeles" sentence with the four links to the residential section of the homepage and to /our-residential-generators/, and cross-link the four city pages to each other.

Then on each city page in Elementor, add a Text Editor widget under the H1 that says Generac Premier PowerPro Dealer in plain text, one sentence on whole-house Guardian sizing for that city's homes linking to /generator/generac-guardian-series/ and /our-residential-generators/, and a link to /financing/. Only place the Generac badge image if the client hands over the official file, never a redrawn one. Add a "What [City] homeowners say" section with the verbatim quotes already on /testimonials/, attributed exactly as that page shows them, no stars or invented dates: Lani H., Ric R. and Patricia S. for Malibu, Andrea Layne for Beverly Hills. Ric R.'s quote alone puts "22KW Generac Whole House" on the Malibu page in a customer's words. Bel Air and Los Angeles get the nearest quotes with their true city labels kept.

Drop any word-count target. Google has no word count threshold, so aim for coverage: keep the PSPS, Coastal Zone and noise sections, add a short permits-and-timeline paragraph, and add 3 to 5 questions on permit time, noise limits, installed cost range and install duration using the Elementor Nested Accordion widget. Its FAQ Schema switch is harmless but earns nothing, and do not use the Yoast FAQ block, it is a Gutenberg block and will not render in Elementor. Cost and permit answers need numbers from the owner, so get those by email before the dev starts.

Photos only if they are real installs in that city supplied by the client, exported to WebP under 200 KB with width and height attributes set and lazy loading on below the fold so CLS stays under 0.1. Fix the one missing alt on each page. After publishing, request indexing for the four URLs in Search Console and watch the Performance report filtered by page for four to six weeks. The dev-only work fits in one day since every word of copy already exists on the site. Photos and pricing follow in a second pass.

Pages. /generator-installation-malibu/, /generator-installation-beverly-hills/, /generator-installation-bel-air/, /generator-installation-los-angeles/

F10 High CategoryOn-page EffortMedium Quick winNo

No page targets 'Generac dealer' or 'whole house generator' in a title, H1 or meta while a rival and a generacdealers.com microsite own the dealer SERP

Evidence

crawl.json scan of title, h1 and meta_desc across 163 URLs, all status 200: 'whole house' 0, 'whole-house' 0, 'whole-home' 0, 'whole home' 0. 'dealer' on 3 pages only, and all three hits are meta descriptions reading 'Generac Premier PowerPro Dealer' on non-landing URLs, /generator/generac-diesel-generator-500-600kw/, /category/newsletter/, /tag/standby-generator/. Zero titles and zero H1s contain 'dealer' anywhere on the site. 'installer' only on / and /home-staging/. The four city pages' H1s say 'Premier Standby Generator Installation in <City>, CA' where 'Premier' is an adjective, not the dealer tier. homepage.html visible body text: 'generac dealer' 0, 'premier powerpro' 2, 'generac' 3, 7 in raw body markup counting alt text and JSON-LD, 'whole house' 0. Alts are 'LT Generators generac', 'Kohler Generators Authorized Dealer', 'Premier Dealer Generac Awards'. BRIEF.md line 9 confirms Generac Premier PowerPro Dealer status. Line 51 attributes the 'whole-house coverage' sizing wording to the FAQ. Live curl of /faqs/ on Sep 9, 2026, 200, 144,934 bytes, carries 'whole house coverage' unhyphenated 3 times. Recommendation targets exist in crawl.json: /our-residential-generators/ with H1 'Residential Generators', /generator-cat/generac/ with title 'Generac Archives - Backup Generators & Installation in So. Cal. - LT Generators', H1 'Generac' and an empty meta, which /generator-cat/generac-commercial/ duplicates title for title and H1 for H1, and /generator-installation-malibu/. SERP snapshot Sep 9, 2026 via US web search, not a tracked Google ranking: 'Generac dealer Los Angeles' returned #1 lyfe-power.generacdealers.com, #2 Yelp, #3 generatorsupercenteroforangecounty.com, #4 #5 #7 #8 #9 allamericangenerator.com, #6 generac.com/dealer-locator, ltgenerators.com/ #10. 'Generac PowerPro Premier dealer Los Angeles': allamericangenerator.com at #1, #2, #3, #5, #6 with the snippet 'the leading PowerPro Premier Generac Dealer in Los Angeles, Ventura and Santa Barbara counties', LT #7. Curl confirms lyfe-power.generacdealers.com is live, 200, title 'Lyfe-Power | Los Angeles, CA | Generac Power Systems', and allamericangenerator.com homepage carries 'powerpro premier' twice. ltgenerators, lt-generators, lt-generator and ltgenerator .generacdealers.com all 404 to generacdealers.com/page_not_found.

Impact

Two of the five money terms have no landing page. The dealer tier is LT's strongest differentiator against electricians and it appears mostly as an image badge, while a competitor with the same tier out-ranks LT for the tier's own name by writing it into titles and headings.

Recommendation

1. Create /generac-dealer-los-angeles/ in Elementor. Yoast SEO title: Generac Premier PowerPro Dealer in Los Angeles | LT Generators. Meta description names the tier, the Los Angeles, Ventura and Santa Barbara service area, the 10-year installation warranty, warehouse stock and the free assessment with the phone number. H1: Generac Premier PowerPro Dealer for Los Angeles. Subhead: Family-owned and licensed since 1982, CA Lic. 602073. Do not write "Authorized Generac Dealer since 1982" unless the client confirms the dealer relationship started that year. The founding year is confirmed in the brief, the dealer start year is not. Body of 600 words or more: what Premier PowerPro means for the buyer, the Guardian, Protector and QT series linked to the existing /generator/generac-guardian-series/, /generator/generac-protector-series/ and /generator/generac-qt-series/ posts, warranty terms, stocked inventory, the install and permit process, service area and the free assessment CTA. A page that exists only to carry the phrase is a doorway page and will not hold.

2. Link it from the main menu under Residential in Appearance > Menus, from the Generac badge in the homepage logo carousel, from the intro of /generator-cat/generac/ and from the five Generac series posts. Once published, request indexing in Search Console URL Inspection.

3. Homepage badge alts. Two Generac badges currently read "LT Generators generac" and "Premier Dealer Generac Awards". Set both to "Generac Premier PowerPro Dealer" in the Media Library or the Elementor image widget. Add a text heading above the logo carousel: Generac Premier PowerPro Dealer. Kohler and Cummins Authorized Dealer. There is no brand-row H2 on the live homepage, so this is a new heading. The homepage redesign brief of Sep 9, 2026, DESIGN-BRIEF.md, already carries this as a credentials list item under a "top tier of Generac's dealer network" H2, so if the redesign ships this month the homepage portion is covered by it.

4. Yoast taxonomy titles. In Yoast SEO > Settings > Content types > Generator Categories, change the SEO title template from "%%term_title%% Archives" to "%%term_title%% Standby Generators in Los Angeles %%sep%% LT Generators". That removes "Archives" from all 14 generator-cat pages in one edit. Then override two terms in Generators > Generator Categories > Edit > Yoast box: Generac gets "Generac Standby Generators | Authorized Premier Dealer | LT Generators" and Generac Commercial gets "Generac Commercial Generators | Authorized Dealer | LT Generators". Those two archives share an identical title today. Add a two or three sentence term description to the Generac term so the archive has text above the grid.

5. Whole house. Change the Yoast title on /our-residential-generators/ to "Whole-House Standby Generators for Los Angeles Homes | LT Generators" and its H1 to match. Add an H2 on /generator-installation-malibu/ reading "Whole-house generator coverage in Malibu". Write "whole-house" into the homepage hero paragraph. Do not create a separate whole-house URL. The residential page becomes that landing page.

6. Client action, not a dev ticket. Ask the Generac territory rep to activate LT's dealer microsite on generacdealers.com and point its website link at /generac-dealer-los-angeles/. That domain holds #1 for "Generac dealer Los Angeles" for a rival and LT qualifies at Premier PowerPro tier.

7. Run the new page through PageSpeed Insights on mobile before adding it to the nav. The homepage loads 30 stylesheets and 38 scripts, so the new Elementor page needs its hero image preloaded and dimensioned to hold LCP under 2.5 s and CLS under 0.1. Steps 3, 4 and 5 are under two hours of Yoast and Elementor edits and should ship first.

Pages. New /generac-dealer-los-angeles/, /our-residential-generators/, /generator-cat/generac/, homepage, /generator-installation-malibu/

F11 High CategoryStructured data EffortSmall Quick winYes

Homepage Electrician JSON-LD ships placeholder phone, address and postal code, a downtown-LA geo pin, a 404 image and an @id collision with the Yoast Organization

Evidence

homepage.html second ld+json block inside Elementor HTML widget data-id="bf1d78b" data-widget_type="html.default": "@type": "Electrician", "telephone": "+1-800-XXX-XXXX", "streetAddress": "Your Street Address", "addressLocality": "Los Angeles", "postalCode": "900XX", geo 34.052235 / -118.243683 which is a downtown LA coordinate while the business sits in Van Nuys 91411, "image": https://ltgenerators.com/wp-content/uploads/logo.png returning HTTP 404 on live curl Sep 9, 2026 while /wp-content/uploads/2021/03/logo.png returns 200, sameAs https://www.yelp.com/biz/lt-generators while the homepage body links https://www.yelp.com/biz/lt-generators-van-nuys, areaServed only five strings: Los Angeles, Malibu, Beverly Hills, Bel Air, Pacific Palisades. Its "@id" is https://ltgenerators.com/#organization, identical to the Yoast Organization node in the first ld+json block named "LTGenerator", so one entity carries two types and two names. Same dump in domain-tech.txt line 84, Yoast Organization on line 83. Real NAP confirmed by live curl of /contact-us/ on Sep 9, 2026: 14932 Delano St. Van Nuys, CA 91411 (818) 787-1470. BRIEF.md line 12 carries the phone but not the street address. Electrician appears in schema_types on 1 of 163 URLs in crawl.json, matching crawl-summary.txt lines 4 and 71. 23 URLs in crawl.json carry Article schema whose publisher references #organization.

Impact

Google reads a fake 800 number, a fake street and a downtown geo for a Van Nuys business, contradicting the Google Business Profile and the contact page. The @id collision can merge placeholder values into the Organization entity referenced by 23 Article publisher nodes. Anyone running the Rich Results Test sees 'Your Street Address'. It falls under Google's structured data policy against misleading markup. Map-pack eligibility is driven by the Business Profile, so the local ranking effect is a risk rather than an observed loss.

Recommendation

Two steps, in this order. Step one, today, about 15 minutes. Open the homepage in Elementor, open Navigator, find the HTML widget with data-id bf1d78b, delete it, click Update, then purge everything under Cloudflare Caching and clear any WordPress page cache so the old block stops serving. Reload the homepage source and confirm only the single Yoast ld+json block remains. That one deletion removes the fake 800 number, the placeholder street and postal code, the downtown pin, the 404 image and the @id collision, and it is the only urgent part.

Step two, this week, half a day. Put the local-business detail on the existing Yoast Organization node instead of adding a second node. In Yoast SEO > Settings > Site representation, change the organization name from LTGenerator to LT Generators so it matches the Google Business Profile, keep the 2021/03/logo.png logo after confirming it returns 200, and under Other profiles add the Yelp URL with the lt-generators-van-nuys slug plus any BBB, Angi or HomeAdvisor listings that actually exist, removing any social URL that does not resolve. Then add a wpseo_schema_organization filter in the Code Snippets plugin or the neve-child functions.php that sets @type to ["Organization","Electrician"], telephone +1-818-787-1470, a PostalAddress of 14932 Delano St, Van Nuys, CA 91411, US, geo coordinates copied from the Google Maps pin for that address, openingHoursSpecification matching the GBP hours, priceRange, foundingDate 1982, hasMap set to the GBP Maps share link, and areaServed as a short list of the real cities and counties served. Yoast prints this node on every page, so the Electrician type then appears on all 163 URLs under one @id with no duplicate. Do not add aggregateRating unless real first-party reviews are shown on the page. If the client wants no code, Yoast Local SEO does the same from a settings screen with Electrician selectable as the business type. The corrected node is printed in the Structured data section of this report.

Validate the live homepage in the Rich Results Test and at validator.schema.org after the cache purge, then request indexing of the homepage through Search Console URL Inspection.

Pages. https://ltgenerators.com/ homepage only, but the @id collision affects the Organization entity referenced on all 163 URLs

F12 High CategoryStructured data EffortSmall Quick winYes

Organization name is misspelled 'LTGenerator' on every page of the site

Evidence

homepage.html Yoast @graph, class="yoast-schema-graph", Yoast SEO plugin v28.3, Organization node @id https://ltgenerators.com/#organization: "name": "LTGenerator", logo ImageObject caption "LTGenerator". crawl.json: schema_types includes Organization on 163/163 rows, all status 200. 23 rows include Article. Live curl of https://ltgenerators.com/about-us/ on Sep 9, 2026, HTTP 200, returns the same Organization node with "name":"LTGenerator", so the string propagates beyond the homepage. Visible brand is "LT Generators", 40 occurrences in homepage.html, twitter:site content="@ltgenerators". A separate Elementor HTML-widget JSON-LD block on the homepage only, Electrician appears on 1/163 crawled rows, declares "@type": "Electrician", "name": "LT Generators" with the identical @id https://ltgenerators.com/#organization, so the shared @id carries two different names. domain-tech.txt lines 83-84 record both nodes the same way.

Impact

This string is what Yoast hands Google as publisher of all 23 Article nodes and as the site's Organization entity for site-name and knowledge-panel matching. The typo alone would be medium. Combined with the placeholder Electrician node on the same @id, the entity Google sees carries two names, a fake phone and a fake address on the money page. Fixing the name while leaving the placeholders would make the fake NAP look intentional.

Recommendation

Two JSON-LD nodes on the homepage share the @id https://ltgenerators.com/#organization, so Google merges them into one entity. Fix both in the same session. Step 1: Yoast SEO > Settings > Site representation. Set Organization name to "LT Generators", spelled exactly as the header logo and the Google Business Profile. Put the legal entity in Alternate organization name only if it differs. While there, open Site basics and set Website name to "LT Generators" as well, because that string populates the WebSite node Google reads for site-name display. Step 2: delete the hand-written Electrician JSON-LD block in the homepage's Elementor HTML widget bf1d78b, or replace every placeholder with the real phone, street address, zip and coordinates that match the site footer and the Google Business Profile, keep the @id and the name identical to Yoast's, and add openingHoursSpecification. Do not leave it half fixed. Step 3: purge the WordPress page cache plugin and then Cloudflare cache. Step 4: run the homepage and one blog post through validator.schema.org and Google's Rich Results Test. Confirm one #organization entity named "LT Generators" with no XXX or placeholder strings anywhere, then re-crawl to confirm all 163 pages.

Pages. All 163 crawled URLs

F13 High CategoryLocal EffortSmall Quick winYes

The Google reviews badge sends visitors to a 2018 hardcoded Google Search URL, not the Business Profile, while aggregators hold 1 to 3 slots on every local query and Angi shows a 3.0

Evidence

homepage.html badge anchors: Yelp https://www.yelp.com/biz/lt-generators-van-nuys, Google https://www.google.com/search?num=50&safe=active&rlz=1C1CHBD_enUS785US785&q=LT+Generators&stick=...&ved=2ahUKEwj7r5-TgvrcAhWyd98KHZk1Cp4Q6RMwQHoECAYQBA&biw=1600&bih=773, the ved protobuf carries a timestamp of 1534712651175931 us = 2018-08-19 UTC, and the badge image is /wp-content/uploads/2018/08/google-reviews-105x105.png, HomeAdvisor https://www.homeadvisor.com/rated.LTElectricandLighting.14911913.html, Facebook, two hrefs: facebook.com/ltgenerators and facebook.com/LTGenerators/, Generac badge to https://ltgenerators.com/testimonials/. Homepage text 'More Than 200 positive client Reviews'. Homepage JSON-LD: hasMap count 0, no google.com/maps URL anywhere in the file, Organization sameAs = Facebook/X/LinkedIn/YouTube only, Electrician node sameAs = Facebook + yelp.com/biz/lt-generators, domain-tech.txt line 84. Same node carries placeholder telephone +1-800-XXX-XXXX and 'Your Street Address'. Live curl Sep 9, 2026 /contact-us/: one Elementor google_maps iframe, src https://maps.google.com/maps?q=14932%20Delano%20St.%20Van%20Nuys%2C%20CA%2091411&t=m&z=14&output=embed&iwloc=near, no place link, cid, g.page or maps.app.goo.gl. Live curl /about-us/: 'Read Reviews' appears 8 times and reuses the identical 2018 google.com/search href. crawl.json /testimonials/: schema_types WebPage;ImageObject;BreadcrumbList;WebSite;Organization, so 0 Review or aggregateRating markup, which is correct under policy. Brand query reproduced live Sep 9, 2026 via web search: Yelp title 'LT GENERATORS - Updated July 2026 - 30 Photos & 127 Reviews', Angi snippet 'rated 3.0 overall out of 5', BBB profile A+, HomeAdvisor and Angi both in the result set, no Google Business Profile result surfaced. The per-query local positions come from the Sep 9, 2026 US search snapshot, re-run once, and are in no data file: Yelp #1 and #2 for 'whole house generator Malibu', #2 for 'Generac dealer Los Angeles', #3 for 'generator installer Beverly Hills' and 'home generator Santa Monica', Angi in 4 queries, HomeAdvisor in 2. Read them as a snapshot, not as tracked rankings.

Impact

The one button that should send happy customers to leave a Google review sends them to a stale desktop search with safe search on. On queries where aggregators take the top slots, LT's position inside Yelp, Angi and Google is a ranking surface in its own right, and a visible 3.0 on Angi sits in the brand SERP next to the 200+ claim. Without a GBP link or hasMap the site gives Google no explicit tie to the profile it most needs to reinforce.

Recommendation

Step one, before any site edit: open Google Maps and search LT Generators Van Nuys. Confirm a verified profile exists at 14932 Delano St with 818-787-1470 and visible reviews. If it is missing or suspended, stop and escalate, because the brand SERP snapshot did not show it and every step below depends on it. The snapshot may be a search-tool artifact rather than a missing profile, so check by logging in. If it exists, the owner opens the Business Profile in Google Search, clicks Ask for reviews, and hands the review link to the developer.

Developer, in Elementor, same week. Edit the homepage and open the HTML widget at the top of the page, element id bf1d78b. It holds a hand-pasted Electrician JSON-LD block that is still the vendor template. Delete it, or fill it with the real name, address and phone exactly as the profile shows them, correct the Yelp URL to /biz/lt-generators-van-nuys, and add hasMap with the Google Maps place URL plus that same URL in sameAs. Validate in the Rich Results Test. Then swap the Google badge href on the homepage badge row and the About Us Read Reviews row from the 2018 google.com/search URL to the review link. Keep the HomeAdvisor badge only if the owner claims the Angi listing for LT Electric and Lighting this week, otherwise remove it. On /contact-us/, replace the old maps.google.com output=embed iframe with the embed code from the profile's Share menu in Google Maps and give the iframe loading="lazy".

Do not add a live Google reviews widget. The audit's mobile lab LCP is 31.5 s against the 2.5 s threshold with a performance score of 51, so a Trustindex or similar third-party script above the fold makes the worst problem worse. Elementor Pro's Reviews widget is manual entry anyway, not a live feed. Correct static badges are the right shape until the performance work lands.

Owner, off the site, same week. Claim the Angi and HomeAdvisor listing and reply to the reviews behind the 3.0. Compare the GBP primary category with what the top three in the local pack for generator installation Los Angeles use, most likely Electrician with Generator shop secondary or the reverse, and add the city pages' service areas to GBP and Yelp. Ask the recent installs named in BRIEF.md testimonials for Google reviews first, Angi second. Keep aggregateRating off the site's own LocalBusiness node.

Pages. https://ltgenerators.com/ badge row, /about-us/ Read Reviews row, /contact-us/ map, schema, Angi, Yelp and GBP listings

F14 High CategoryLocal EffortSmall Quick winYes

Street address exists on six pages only, business hours appear nowhere, and the homepage schema names a different city

Evidence

homepage.html: 'Van Nuys' 0, 'Delano' 0, '(818) 787-1470' 2, 'openingHours' 0, no mailto. Footer text is the mission statement, 'CA State Contractor License # 602073' and Quick Links. Homepage Electrician JSON-LD: telephone +1-800-XXX-XXXX, streetAddress 'Your Street Address', addressLocality Los Angeles, postalCode 900XX, also recorded in domain-tech.txt. Live fetch Sep 9, 2026 of /about-us/ and /generator-installation-{bel-air,beverly-hills,los-angeles,malibu}/: '14932' 0, 'Delano' 0, 'Van Nuys' 0, '91411' 0 on all five, phone present as tel: links only. Live sweep of all 163 crawled 200 URLs: the address string '14932 Delano St. Van Nuys, CA 91411' appears as visible text on 5 pages, /contact-us/ and /kohler-contact-us/, with the phone, /kohler/, /kohler-generators/, /home-generator-assessment/, and only inside a Google Maps iframe title/aria-label on /schedule-consultation/. The other 157 URLs have none. Across the same 163 URLs: 'openingHours', 'Hours:', 'Mon-Fri', '8:00 am', '5:00 pm' all 0, and mailto: 0 on every page checked. ltgenerators.kohlergeneratordealer.com returns HTTP/2 200. Directory hours, local.yahoo.com, loc8nearme.com, Mon-Fri 8 to 5, and the 'LT Generators Van Nuys' result snapshot come from a Sep 9, 2026 US web search and were not reproduced with curl. Treat as a snapshot, not verified data.

Impact

Local ranking relies on the site corroborating the GBP name, address, phone and hours. The site presents two conflicting sets for one business: homepage phone with schema in downtown LA, contact page in Van Nuys. Directories and the Kohler dealer microsite become the authoritative NAP sources, and the brand site cannot win its own brand plus city query.

Recommendation

Four moves, all doable this week. 1. Delete the Elementor HTML widget on the homepage, data-id bf1d78b inside section 31f38618, that prints the hand-pasted Electrician JSON-LD. It is an unfilled template. Remove it, do not edit it. 2. Make Yoast the single source of entity data. In Yoast SEO > Settings > Site representation set the organization name to "LT Generators" exactly as it reads on the Google Business Profile, the current value is "LTGenerator". Confirm the logo path resolves. Add the GBP URL and ltgenerators.kohlergeneratordealer.com under Other profiles so sameAs points at the sources currently outranking the site. Then add a short PHP snippet through the Code Snippets or WPCode plugin on the wpseo_schema_organization filter that changes @type to Electrician and adds telephone +1-818-787-1470, a PostalAddress of 14932 Delano St, Van Nuys, CA 91411, US, geo coordinates read from the GBP map pin rather than downtown LA, hasMap set to the GBP Maps link, and openingHoursSpecification Monday to Friday 08:00 to 17:00 after the owner confirms those hours. If nobody wants to touch PHP, the Yoast Local SEO add-on fills the same fields from a settings screen. Do not add a second LocalBusiness node alongside the Yoast one. 3. In Elementor Theme Builder open the Footer template and add one address block that renders on every page: LT Generators, 14932 Delano St, Van Nuys, CA 91411, the phone as a tel: link, and the hours, character for character as on the GBP. If a 24-hour emergency number exists, list it as a separate line, not as opening hours. Put the same hours next to the map on /contact-us/. Google requires structured data to match visible page content, so footer and schema must agree. Skip the mailto, the form is fine and an exposed email only draws scrapers. 4. Publish, purge the Cloudflare cache and regenerate Elementor CSS, then run the homepage and /contact-us/ through the Rich Results Test and confirm exactly one node carries the #organization @id and it says Van Nuys 91411.

Pages. Sitewide footer, https://ltgenerators.com/, https://ltgenerators.com/contact-us/

F15 High CategoryLocal EffortLarge Quick winNo

Service-area coverage gap: no pages for Pacific Palisades, Santa Monica, Brentwood, Encino, Calabasas or four named counties while the lead competitor runs 15 geo pages

Evidence

Homepage text in homepage.html: "Serving Malibu, Beverly Hills, Los Angeles, Palisades, Brentwood, Santa Monica, Encino, and other surrounding areas." plus service tiles for Los Angeles, Ventura, Santa Barbara, Orange County and San Diego. BRIEF.md lines 58 and 61 carry testimonials from Sherman Oaks and Calabasas. sitemaps.json page-sitemap.xml lists 47 pages, and only four are city service pages: /generator-installation-bel-air/, /generator-installation-beverly-hills/, /generator-installation-los-angeles/, /generator-installation-malibu/, at 828, 766, 814 and 746 words per crawl-summary.txt lines 18 to 21. /malibu-generator-install/ is an Article, not a service page. No page exists for Pacific Palisades, Santa Monica, Brentwood, Encino, Calabasas, Sherman Oaks, Ventura County, Santa Barbara County, Orange County or San Diego. City-named URLs that do exist are project posts: /generator/generac-residential-22kw-santa-barbara-ca/, /generator/generac-guardian-22kw-santa-monica-ca/, /generator/generac-residential-11kw-palisades-ca/, /generator/generac-protector-os-series-27kw-brentwood-ca/, /generator/generac-commercial-guardian-22kw-ventura-ca/, all in the 29 missing-meta-description list on crawl-summary.txt line 62. Those posts already carry Service schema with areaServed City, confirmed by a live fetch of the Santa Monica post on Sep 9, 2026, so only the meta description is missing there. allamericangenerator.com/page-sitemap.xml fetched live Sep 9, 2026 has 44 URLs, of which 15 are place-named geo pages plus two service-area hub pages, including /pacific-palisades-los-angeles/, /brentwood-los-angeles/, /ventura-county/, /santa-barbara-county/, /los-angeles-county/, /north-hollywood/, /thousand-oaks-los-angeles/ and three separate Pacific Palisades pages. Web search snapshot Sep 9, 2026, US: for "home generator Santa Monica", powell-electric.com/generators-in-santa-monica-ca/ and generatorsupercenteroforangecounty.com/whole-home-generac-generators-in-santa-monica/ rank ahead of the LT homepage, which sits at position 6 or 7 depending on the snapshot. For "generator installation Pacific Palisades rebuild", allamericangenerator.com/pacific-palisades-los-angeles/ ranks and the LT homepage did not appear in the top 7 of the re-check. The four existing city pages are distinct rather than near-duplicates: live fetch Sep 9, 2026, main-content word-set Jaccard 0.21 to 0.30, 3-gram Jaccard 0.04 to 0.08, term-frequency cosine 0.49 to 0.61, so they share a template but not body copy. No Search Console, GA, CrUX or backlink data was available.

Impact

City pages are the page type that wins these queries. Every neighborhood the homepage promises but has no page for is a query a competitor's dedicated page outranks LT's homepage on, and the affluent Westside and Valley areas plus the Palisades and Malibu post-fire rebuilds are exactly where high-ticket installs happen right now. Nothing is broken, so this is an opportunity cost rather than a defect, but it is time-bound.

Recommendation

Build the missing city pages in two phases and make each one earn its URL with local substance. Google's scaled-content and doorway-page policies treat near-duplicate location pages as spam, so six real pages beat sixteen thin ones. The four existing pages already prove the site can do this, and the URL pattern is settled: match /generator-installation-<city>/ exactly as beverly-hills, los-angeles and malibu already use it.

Dev setup, one day. In Elementor, save the strongest existing city page as a template under Templates then Saved Templates and import it per new city. Register a service_area tag or taxonomy on the /generator/ project post type and tag every existing post by city. Put an Elementor Pro Posts or Loop Grid widget on the city template with its Query filtered by that term so each city page pulls its own install photos and posts automatically. Hyperlink every city name in the homepage "Serving Malibu, Beverly Hills..." line to its page, add a Service Areas footer menu, and confirm Yoast is including the new pages in the XML sitemap. In the same pass, set a meta description template for the /generator/ post type under Yoast Search Appearance so the project posts stop shipping blank. Do not hand-write thirty descriptions, Google rewrites most of them anyway.

Content phase one, the pages that pay, one or two per week. Pacific Palisades, Santa Monica, Brentwood, Calabasas, Encino, Sherman Oaks. Each page needs facts a competitor cannot copy from a template: which utility serves the area, since Palisades, Brentwood, Encino and Sherman Oaks are LADWP while Santa Monica, Malibu and Calabasas are SCE, which office issues the permit, since City of LA neighborhoods go through LADBS while Santa Monica, Calabasas and Malibu run their own building departments, Very High Fire Hazard Severity Zone status and the setback and clearance rules that follow from it, hillside lot and noise ordinance constraints, the local testimonial from BRIEF.md, and on the Palisades page the current rebuild permitting path and how a standby generator fits a rebuild plan. Confirm each utility and permit fact with the installer before publishing. Let the substance set the length. Also fold /malibu-generator-install/ into the existing Malibu city page with a 301 so the two stop competing, or keep it and link it forward as the Malibu finding describes.

Phase two, once phase one is indexed and ranking. Ventura County, Santa Barbara County, Orange County, each anchored on the real project posts already in those areas. Skip San Diego unless the crew installs there monthly.

Schema. Keep one LocalBusiness block of type Electrician sitewide through Yoast, with areaServed listing the cities. Drop the Service schema on project posts. It triggers no rich result and Google does not rank on areaServed.

Google Business Profile. Add the same cities as service areas in the GBP, which is what wins the map pack half of "home generator Santa Monica".

Pages. New pages under /generator-installation-<city>/ and the /generator/*-ca/ project posts

F16 High CategoryIndexation EffortSmall Quick winYes

Malibu is split across seven indexable URLs, including three tag archives, and Google is choosing the 2022 blog post over the city page

Evidence

crawl.json Malibu URLs, all robots index,follow with self-canonicals: /generator-installation-malibu/ 746 words, /malibu-generator-install/ 1431 words, Article schema, live page dateModified 2022-07-19T16:18:41+00:00, datePublished 2019-06-12, /mandatory-blackouts-approved-for-malibu/ 710 words, sm-sitemap.xml lastmod 2019-02-25, /generator/generac-residential-22kw-malibu-ca/ 556 words meta_len 0, /tag/malibu-generator-install/ 564, /tag/malibu-generators/ 562, /tag/standby-generators-malibu/ 510. All seven appear in sitemaps.json. Web search snapshot Sep 9, 2026, US, for 'generator installation Malibu CA': https://ltgenerators.com/malibu-generator-install/ is the 7th result after malibu.org, angi.com, two yelp.com results, malibumobilemechanic.com and generatorhookupservice.com. /generator-installation-malibu/ absent. Snapshot, not a tracked ranking. Live curl of /malibu-generator-install/, HTTP 200, contains 0 links to /generator-installation-malibu/. Same pattern for Bel Air, /generator-installation-bel-air/ 828 words plus /generator/generac-residential-22kw-bel-air-ca/ 559 words meta_len 0, and Santa Monica, only /generator/generac-guardian-22kw-santa-monica-ca/ 558 words meta_len 0, no city page. crawl-summary.txt line 74: 28 /tag/ archives in the sitemap.

Impact

Google is not splitting signal across seven URLs, it is choosing the page it trusts most for the query, and today that is the 1431-word 2022 article because the 746-word city page is the weaker Malibu document with no inbound links. The strongest page for LT's flagship luxury market does not rank for its own head term.

Recommendation

Make /generator-installation-malibu/ the strongest Malibu page, then fold the cannibal into it.

Step 1, five minutes. Yoast SEO 28.3 is installed. Go to Yoast SEO, Settings, Categories and tags, Tags, and turn off Show tags in search results. That sets noindex on all 28 /tag/ archives and drops post_tag-sitemap.xml from the sitemap index in one move. Do not 301 the tag URLs unless Search Console shows impressions or external links to them. Leave the generator-cat brand archives indexed.

Step 2, two to three hours in Elementor. Merge the unique Malibu material from /malibu-generator-install/ into /generator-installation-malibu/: the SCE public safety shutoff context, Woolsey fire history, coastal salt-air enclosure and propane versus natural gas notes, LA County permitting, plus a Recent Malibu installs section that carries the photos and specs from /generator/generac-residential-22kw-malibu-ca/. Target 1500 or more words of Malibu-specific copy, not the shared city template. Write a unique meta description in the Yoast sidebar.

Step 3, thirty minutes. Install the free Redirection plugin. 301 /malibu-generator-install/ and /generator/generac-residential-22kw-malibu-ca/ to /generator-installation-malibu/. Update every internal link that pointed at those two URLs so nothing depends on the redirect. Keep /mandatory-blackouts-approved-for-malibu/ live as a news post and add one in-body link with the anchor generator installation in Malibu pointing to the city page. Put the four city pages in the footer or a Service Areas nav item so each is one click from home. If the team prefers to keep the 2022 article live, the alternative in the city-pages finding is to link it forward to the city page from its first paragraph.

Step 4. Request indexing of the city page in Search Console URL Inspection and read the query in the Performance report after three to four weeks.

Repeat steps 2 and 3 for Bel Air with /generator/generac-residential-22kw-bel-air-ca/ and for Beverly Hills. Santa Monica has no city page, so it needs a new page built before anything is redirected. Scope that separately and do not redirect the Santa Monica project post to the Los Angeles page.

Caution. The three city pages share the title pattern Premier Standby Generator Installation in City, CA at 746 to 828 words. Measured similarity is low, so they are not doorway pages today. Step 2 keeps them that way.

Pages. The seven Malibu URLs above, plus 28 /tag/ archives

F17 High CategoryCompetitive EffortMedium Quick winNo

Absent from commercial and maintenance SERPs, where All American Generator holds most results, and the maintenance tips post is a 404 with its 2018 PDF still in the advertised sitemap

Evidence

Sep 9, 2026 US search snapshot, re-run once, 'commercial generator installation Los Angeles': 6 results, airtro.com, suncoast-energy.com, backupgeneratorinstall.com, deltaqualityelectric.com, allamericangenerator.com x2, no ltgenerators.com. 'Generac generator maintenance service Los Angeles': 8 results, allamericangenerator.com x6, expertelectricgroup.com whose snippet publishes 'Ongoing maintenance averages $350 to $425 per year', generatorsupercenteroforangecounty.com x1, no ltgenerators.com. LT assets, crawl-summary.txt, crawl.json, live curl Sep 9, 2026: /our-commercial-generators/ 1201 words, 2 H1s, 'Commercial & Industrial Generators' and 'Protect Your Business With Commercial & Industrial Generators', 21 H2s that are almost all product names, 23 of 97 images missing alt. /services-maintenance/ 847 words, 6 H2s, the first two being 'Need help with your generator?' and 'Call Today (818) 787-1470'. /generator-maintenance-top-10-tips/ returns 404 with no redirect, live curl, yet sits in the legacy sm-sitemap.xml, line 81, lastmod 2019-02-25, alongside the PDF /wp-content/uploads/2018/10/Maintenance-Tips-for-your-Automatic-Home-Backup-Generator-MarcoFinal.pdf, line 276, lastmod 2019-01-21, which still serves 200 application/pdf, 123,786 bytes. Neither URL is in the Yoast sitemap index children, so the crawl's '163 URLs all 200' did not cover them. BRIEF.md facts confirmed present: 'Factory trained and certified to work on Generac products, even if you didn't purchase from us', 'Maintenance Programs + 24-hour emergency service', '24/7/365 remote monitoring', 'We warranty our installations up to 10 years'. None of these appear as a heading in the 163 crawled H1s, the homepage H1-H3, homepage.html, or the live H1-H3 of /services-maintenance/ and /our-commercial-generators/. H2 text on the other 159 pages was not captured by the crawl.

Impact

Commercial jobs and recurring maintenance contracts are the highest-margin lines and LT's pages for them do not appear at all, while a competitor publishes a price and takes six of eight maintenance results. Five identical indexable 3068-word pages at /maintenance/, /service/, /monitoring/, /diagnostics/ and /emergencies/ cannibalize the maintenance page and are a more likely cause than heading wording.

Recommendation

Do the hygiene slice first, it takes about an hour. The live /sitemap.xml is a static xml-sitemaps.com export from February 2019 sitting in the web root, and robots.txt points Google at it. It lists the 404 post and the 2018 PDF and omits all four city landing pages. Delete that file over SFTP so Yoast's virtual sitemap takes over, change the Sitemap line in Yoast SEO > Tools > File editor > robots.txt to https://ltgenerators.com/sitemap_index.xml, then in Search Console remove the old sitemap.xml submission and submit sitemap_index.xml. Install the free Redirection plugin and 301 /generator-maintenance-top-10-tips/ to /services-maintenance/ until the rebuilt post is live. Leave the PDF alone, it returns 200 and stops appearing in any sitemap once the static file is gone. In the same pass 301 the five identical 3068-word pages /maintenance/, /service/, /monitoring/, /diagnostics/ and /emergencies/ to /services-maintenance/ and trash them.

Commercial page, in Elementor: open the second H1 heading widget and set Content > HTML Tag to H2 so the page has one H1 reading Commercial Generator Installation in Los Angeles. Set the Yoast SEO title to Commercial Generator Installation Los Angeles | LT Generators. Add H2 sections for medical and dental offices, restaurants and grocery, data and IT rooms, multifamily and HOA, and warehouse and manufacturing, each with the kW range and fuel LT typically installs for that facility. Add a short paragraph on SCAQMD Rule 1470 permitting for diesel units only if LT installs diesel. Fill the 23 missing alt attributes in the Media Library. Skip Service schema, Google has no rich result for it and Yoast free cannot emit it without a code snippet.

Maintenance page: Yoast title Generac Generator Maintenance & Repair in Los Angeles, with a matching H1. Convert the two CTA headings to Text Editor widgets or set their HTML tag to p so they stop counting as H2s. Add H2s for the annual maintenance plan with a published from-price or range approved by the owner, We service Generac units we didn't install, 24/7/365 remote monitoring, and 24-hour emergency service. Rebuild the ten tips as a WordPress post at the original slug from the PDF text, link the PDF as a download inside it, and remove the temporary redirect once it publishes. Link both pages from the homepage and from each of the four city pages with descriptive anchor text. Measure in Search Console with query filters for commercial and maintenance and expect movement in six to twelve weeks. The SERP evidence is one snapshot without the local pack, so confirm with Search Console impressions before promising a ranking outcome.

Pages. /our-commercial-generators/, /services-maintenance/, /generator-maintenance-top-10-tips/

F18 High CategoryContent EffortMedium Quick winNo

PSPS is the term Los Angeles homeowners search and it appears on almost none of the site, which has a dozen aging news posts instead of one evergreen outage page

Evidence

Live grep Sep 9, 2026 for 'PSPS', case-insensitive, curl of each URL: homepage 0, /power-outages/ 0, /faqs/ 0, /our-residential-generators/ 0, /power-shut-offs-grid-failure-preparation/ 0, a 2099-word post per crawl.csv with post-sitemap.xml lastmod 2018-12-20T21:01:17, /mandatory-power-shutoffs-are-here-to-stay/ 0, only /generator-installation-malibu/ has 2. homepage.html: 'wildfire' 0, 'Santa Ana' 0, 'earthquake' 0 in copy, the one case-insensitive hit is the image filename Earthquakes.png, not text. 'outage' appears ten times. /power-outages/, the nav item "Current Power Outages", 2 occurrences in homepage.html, is 548 words with 0 H1, crawl.csv and live, and links to two LADWP URLs and one SCE URL: the LADWP jsessionid URL returns HTTP 404, the plain LADWP outageinformation URL 301s to ladwp.com/node/2471, and sce.com/outage-center/check-outage-status 301s to /outages-safety/outage-center/check-outage-status. Six shutoff posts in post-sitemap.xml, datePublished 2018-06-10 through 2019-01-24, edison-to-cut-power, california-utilities-shut-off-power-in-extremely-windy-weather, edison-to-shut-down-power, mandatory-blackouts-approved-for-malibu, power-shut-offs-grid-failure-preparation, mandatory-power-shutoffs-are-here-to-stay, all with 0 PSPS. The wider shutoff, wildfire and blackout cluster in the crawl is 12 posts plus eight tag archives. Web search snapshot Sep 9, 2026, US, 'PSPS public safety power shutoff generator Los Angeles homeowners': cpuc.ca.gov, psehealthyenergy.org, libertyutilities.com, lacounty PDF, airtro.com PSPS guide, sce.com, pse.com, homelinksolar.com, no LT. Zoom Electricians page 'Is Your Malibu Home Ready for the Next Power Shutoff?' exists at zoomelectricians.com/blog/backup-generator-installation-malibu/. Its #5 position for 'generator installation Malibu CA' is a one-time snapshot and was not reproduced.

Impact

PSPS season is the demand spike for this business and the site has 2018 to 2019 news posts instead of one evergreen page using the term buyers type. An HVAC company and a solar company occupy the only non-utility slots. The audit landed September 9 and Santa Ana wind season opens in about four weeks, and the nav item people click during a shutoff is a 548-word page with no H1 and two utility links that redirect or 404.

Recommendation

Keep the /power-outages/ URL, it is in the main nav and already has internal links, and rebuild it in place as the one evergreen PSPS page. Do the structural fixes first, in one day. In Elementor set the hero heading widget to H1 and use the title "PSPS and Power Outage Preparation for Los Angeles Homes", change the Yoast title and meta to lead with PSPS, and replace the two dead utility links with the canonical pages, sce.com/psps or sce.com/outage-center and ladwp.com/outages, no jsessionid, no 301 chains. Then write the content for commercial intent, not the definition. Utilities and the CPUC own "what is a PSPS" and the AI Overview cites them, so the installer's job is to rank for "generator for PSPS", "standby generator SCE power shutoff", and "backup power PSPS Malibu, Bel Air, Calabasas, Topanga". Sections in order: who gets shut off in LA County and which of the four service cities sit in SCE High Fire Risk Areas, how much notice SCE gives before de-energizing, how a natural gas standby starts and runs through a multi-day shutoff, what it costs and how financing works, and a link block to every city page. For LADWP, check the current LADWP wildfire mitigation plan before writing. This audit did not verify whether LADWP runs a de-energization program, so do not claim LADWP runs PSPS the way SCE does unless the plan says so. Put a visible "Updated for the 2026 fire season" date on the page and refresh it every September. Add a short visible PSPS FAQ, but do not sell FAQPage schema as a deliverable. It earns nothing for this site class. Consolidate the old cluster. The crawl shows 12 shutoff, wildfire, and blackout posts from 2018 to 2019, several with Cyrillic homoglyphs in their meta descriptions, which reads as spun text. Check Search Console for any with impressions or backlinks in the last 16 months, keep those as posts with a link to the hub, and 301 the rest to /power-outages/ using the Redirection plugin. Also 301 /power-outage-preparation-free-assessment/ to the hub. Noindex the eight wildfire and outage tag archives in Yoast under Settings, Categories and tags, Tags, Show in search results, No. Add one PSPS paragraph with a link to the hub on each of the four city pages. Add one homepage H2 that uses the word PSPS and links to the hub. Ship the structural fixes and redirects before October 1. The content can follow a week later.

Pages. https://ltgenerators.com/power-outages/ plus the 2018 to 2019 shutoff posts

Medium

29
F19 Medium CategoryPerformance EffortSmall Quick winYes

YouTube embed loads the full player, 1 MB and 12 requests, on page load with no facade

Evidence

lighthouse-summary.json mobile third_party YouTube transferSize 1,033,241 bytes, 13.8% of the 7,477,436-byte total in lighthouse-mobile.report.json network-requests. 12 youtube.com and i.ytimg.com requests totalling 1,009 KB, embed sk2Nh8p2ZCU requested at 5,434 ms, player base.js 483,659 bytes, 472 KB, third in largest_requests behind the ServiceTitan bundle at 1,236 KB and PowerLineImage.png at 504 KB, ytembeds js 224,054 and 158,817, www-player.css 58,423, sddefault.webp poster 20,839. homepage.html line 782: Elementor video widget data-settings contains only youtube_url, video_type and controls, no lazy_load key, the file's single lazy_load string is the global lazy_load_background_images setting on line 1706, and zero elementor-custom-embed-image-overlay elements. YouTube blocking time 0 ms, main-thread time under 10 ms.

Impact

14% of page weight goes to a video most visitors never play. The player script is the third largest single request on the page and it is requested at 5.4 s, inside the LCP load window, so on a throttled connection it competes with the hero image download. INP and CLS are untouched.

Recommendation

Facade the YouTube embed in Elementor, per widget, this week. Open the homepage in the Elementor editor and select the Video widget under the H1, element id f38d49a. Under Content > Video set Privacy Mode = Yes so the click-loaded iframe uses youtube-nocookie.com and stops setting the third-party cookies Lighthouse already flags. Under Content > Image Overlay set Image Overlay = Yes, choose a poster from the media library as a 16:9 WebP or JPG under 60 KB at Image Size = Medium Large, set Play Icon = Yes, and turn on the Lazy Load toggle that appears once the overlay is on. Both toggles are required. Overlay alone still fetches the YouTube iframe API on page load, and Lazy Load is what defers it to click. Keep Lightbox off so the video plays inline where it sits. Save, then view source and confirm data-settings now contains "lazy_load":"yes" and an elementor-custom-embed-image-overlay div, and that no youtube.com or ytimg.com request appears in DevTools Network until the poster is clicked. Repeat on every Video widget on /videos/ and any post. There is no site-wide Elementor switch, so if there are more than a handful of embeds, or any were pasted as Gutenberg or oEmbed blocks that Elementor cannot reach, install one plugin to catch them all: WP Rocket Media > Replace YouTube iframe with preview image, or Perfmatters Lazy Loading > YouTube Preview Thumbnails. No caching plugin is present today and Cloudflare does not facade video, so one of those is the fallback. Re-test on PageSpeed Insights mobile. Expect YouTube to vanish from the third-party summary, 12 fewer requests and roughly 1 MB less transfer. Do not expect LCP to pass from this alone. If the homepage video is not earning plays in GA4, move it to /videos/ and drop it from the homepage entirely.

Pages. Homepage, /videos/ and any post embedding YouTube.

F20 Medium CategoryAnalytics EffortSmall Quick winYes

Google tag stack loaded three ways via Site Kit, a 2018 hardcoded gtag block and GTM, including a dead Universal Analytics property, 830 KB

Evidence

lighthouse-mobile.report.json network-requests, googletagmanager.com: gtag/js?id=G-BVPGS29PEC twice at 187,443 and 187,498 bytes transfer, gtag/js?id=AW-800825679 162,278, gtm.js?id=GTM-N7XTZCZ 158,697, gtag/js?id=UA-120927605-3 133,617 with 69,565 bytes unused. third-party-summary Google Tag Manager transferSize 829,533 bytes, mainThreadTime 554 ms, blockingTime 132 ms. unused-javascript wasted bytes across the five files sum to 390,143. Mobile performance score 0.51. Google CDN entity, 1,151 ms main thread, of which recaptcha__en.js is 1,132 ms, is the only third party above it. homepage.html line 132 comment "Google tag (gtag.js) snippet added by Site Kit", line 135 loader gtag/js?id=G-BVPGS29PEC, lines 141 to 142 gtag("config","G-BVPGS29PEC") and gtag("config","AW-800825679"). Line 373 comment "as of 10-23-18 with Analytics Global site tag (gtag.js) - Google Ads: 800825679", line 375 second loader gtag/js?id=AW-800825679, line 381 gtag('config','AW-800825679'), line 382 gtag('config','UA-120927605-3'), line 384 "End Google gtag", line 374 inside that range is the reCAPTCHA api.js loader, not part of the gtag block. Line 388 comment "Google Tag Manager as of 10-23-18", line 393 GTM-N7XTZCZ. Live gtm.js?id=GTM-N7XTZCZ fetched Sep 9, 2026, 462,725 bytes: one __googtag tag with vtp_tagId "AW-800825679" and six __awct conversion tags. Zero occurrences of UA-120927605-3 or G-BVPGS29PEC in the container, so the UA and second G- loads, which carry &cx=c&gtm= parameters, are triggered by the on-page gtag('config') calls. domain-tech.txt lines 74 to 76: 25 gtag, 1 GTM-N7XTZCZ, 1 UA-120927605-3. perf-profile.json generatorMeta "Site Kit by Google 1.187.0".

Impact

GA4 and Google Ads libraries download and evaluate twice, and a UA property that stopped processing hits in 2023 still costs 134 KB. Second largest main-thread consumer after reCAPTCHA. Site Kit's gtag and the container both configure G-BVPGS29PEC on the same page, which is Google's documented double-tagging case, so GA4 page_views are inflated and Ads leads may double count depending on each action's counting setting. That is paid-media hygiene worth fixing this week. The performance score will barely move.

Recommendation

Consolidate on GTM-N7XTZCZ, which already fires GA4 and Ads. Do not add a GA4 tag in GTM. The request gtag/js?id=G-BVPGS29PEC&cx=c&gtm=4e6941 in the Lighthouse trace proves the container already has a Google tag for that property, and adding another would make a third copy of it.

Step 1, GTM. Open GTM-N7XTZCZ and confirm the Google tag for G-BVPGS29PEC and the Google tag or conversion linker for AW-800825679 are present. The live container carries no UA-120927605-3 reference, so there is nothing to remove there. Add Custom Event triggers named conversion, financing_widget_open and wpcf7_submission if the existing tags do not already catch those events in Preview mode. Publish.

Step 2, Site Kit 1.187. Settings > Connected Services > Analytics > Edit, switch off the toggle that lets Site Kit place the Google tag on the site, save. Settings > Connected Services > Ads > Disconnect, because that module exists only to print the AW- config. Dashboard reports keep working since Site Kit reads GA4 through the API. Optional in the same visit: connect Site Kit's Tag Manager module to GTM-N7XTZCZ so the container snippet becomes plugin-managed and the hardcoded copy can go too.

Step 3, theme. Find the block commented "as of 10-23-18" in the neve-child-master header.php, or in WPCode or Elementor > Custom Code if it was pasted there, and delete lines 373 and 375 to 384 only. Leave line 374, the synchronous reCAPTCHA v3 loader, for the separate reCAPTCHA ticket. Removing this block alone kills the UA load. Keep one line ahead of the GTM snippet: window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}. Without it, gtag_report_conversion at line 171, the financing_widget_open click handler at line 194 and the wpcf7_submission event at line 1502 throw ReferenceError once Site Kit stops defining gtag, and the Ads conversion on the form is lost.

Step 4, verify. Tag Assistant should show one GA4 instance and one Ads instance. DevTools Network filtered to googletagmanager should show gtm.js once, gtag/js?id=G-BVPGS29PEC once, at most one AW-800825679 load and no UA- request. GA4 Realtime should record one page_view per pageview. Watch Google Ads conversions for two weeks. If the Sign Up From Website action roughly halves, the old setup was double counting and historical cost per lead should be re-read.

Expected result: 830 KB of Google tag JS drops to roughly 350 to 510 KB and about 200 ms of mobile main thread comes back. Once tags live only in GTM, Cloudflare Zaraz could take GA4 and Ads off the browser entirely, but that is a later ticket.

Pages. Site-wide, header.

F21 Medium CategoryPerformance EffortMedium Quick winYes

reCAPTCHA v3 loaded synchronously with three different site keys is the top main-thread cost, and Contact Form 7 assets load on pages without a form

Evidence

homepage.html: three synchronous reCAPTCHA loaders, none with async or defer. The hardcoded one is at line 374 in the head, </head> is line 409, directly under the comment "as of 10-23-18 with Analytics Global site tag (gtag.js)": recaptcha/api.js?render=6Ldo35sU... The other two are in the body footer: line 1567 id="recaptcha-lib-js" api.js?render=6LfyUVIp...&ver=2.21.0, Constant Contact Forms, and line 1660 id="google-recaptcha-js" api.js?render=6LfRcbEU...&ver=3.0, Contact Form 7. lighthouse-summary.json mobile bootup: recaptcha__en.js 1,131.7 ms total, 893.7 ms scripting, the largest entry, desktop: 209.2 ms total, 151.7 ms scripting, also largest. unused_js lists recaptcha__en.js three times at 352,704 bytes each, wasted 254,679 / 254,679 / 169,689. third_party "Google CDN" blockingTime 443 ms, mainThreadTime 1,151 ms. Full mobile report third-party-summary tbtImpact 288 ms. render_blocking api.js?render=6Ldo35sU wastedMs 881 mobile, 322 desktop. perf-profile.json mobile.non200: two api2/pat?k=... requests, 6LfyUVIp and 6LfRcbEU keys, returned 401, which is reCAPTCHA's normal Private Access Token probe answer from a non-Apple client. third_party_cookies: _GRECAPTCHA set by api2/reload?k=6LfRcbEU... Best Practices 71 mobile, 74 desktop. The only <form> elements on the homepage are two Constant Contact forms, class ctct-form ctct-form-1816 has-recaptcha, yet the string wpcf7 appears 21 times and CF7 assets load: swv/js/index.js?ver=6.1.7, line 1580, contact-form-7/modules/recaptcha/index.js?ver=6.1.7, wpcf7-redirect frontend-script.js and .css. live-mobile-fold.png shows the reCAPTCHA badge overlapping the "LT Generators is the largest full-service..." body copy at bottom right. Mobile mainthread-work-breakdown 3.2 s, 3,241 ms, TBT 220 ms.

Impact

v3 runs on load, so three registrations evaluate the same 353 KB library three times on every pageview, about 1.1 s of the 3.2 s mobile main thread and most of the 220 ms TBT. It also sets a third-party cookie Lighthouse flags under Best Practices, and the badge covers text on phones. It is under a second of the LCP chain, so it moves FCP and interactivity, not the LCP failure.

Recommendation

Step one, today: delete the orphan reCAPTCHA tag at homepage.html line 374, key 6Ldo35sU. It is the only one of the three that sits inside head and it is the script Lighthouse flags as render-blocking at 881 ms on mobile. The key appears nowhere else in the page, so nothing depends on it. It lives next to the 2018 gtag block, so look in the Neve child theme header.php, Appearance > Customize > Custom Header Code, or a header-scripts plugin, and remove the gtag UA block while you are there.

Step two: replace reCAPTCHA with Cloudflare Turnstile. The site is already on Cloudflare and Turnstile is free with no badge and no cookie. Create a widget at Cloudflare dashboard > Turnstile, install the free Simple Cloudflare Turnstile plugin, paste the site and secret keys, tick the Contact Form 7 integration. Then go to Contact > Integration > reCAPTCHA and click Remove Keys. That single click is what stops CF7 from enqueuing google-recaptcha on every page. The wpcf7_load_js filter does not do that, the CF7 reCAPTCHA module enqueues api.js independently of that filter.

Step three: turn off reCAPTCHA in Constant Contact Forms. In Contact Form > Settings, Spam Control, set reCAPTCHA to off. The two homepage forms already render a honeypot and a timing check, ctct_usage_field and ctct_time are both in the markup, and Akismet covers the rest if it is installed. Simple Cloudflare Turnstile does not integrate with that plugin, so this is the practical path. If the owner wants a captcha on those two forms, rebuild them as CF7 forms so the whole site runs one form plugin and one Turnstile.

Step four, if CF7 stays installed: stop its assets loading on pages without a form. Install Asset CleanUp Lite, open the homepage and any service page, and unload contact-form-7 CSS, contact-form-7 JS, swv, and wpcf7-redirect everywhere except the contact page. The wpcf7-redirect files come from the separate Redirection for Contact Form 7 plugin, so unload that one too, or remove it if the redirect is unused.

Do not hide the badge with visibility:hidden as a fix. Google only permits that with its attribution text, and once Turnstile is in there is no badge to hide and the mobile overlap goes away on its own.

Then re-run Lighthouse mobile and expect roughly 1 MB less script parse, about 0.9 s less main-thread work, TBT falling from 220 ms to under the 200 ms good mark, the render-blocking head script gone, and the third-party cookie flag cleared from Best Practices.

Pages. Site-wide, every page loads all three keys.

F22 Medium CategoryPerformance EffortMedium Quick winYes

Nine synchronous scripts in the head, 30 sitewide, including a go_portfolio plugin unused on the page, cost 1.8 s of the 8.4 s mobile first paint

Evidence

homepage.html: 37 script tags with src, 7 with async or defer, 30 synchronous. Only 9 of the 30 are in the head, lines 120 to 374, and those are the ones that block first paint: jquery.min.js 3.7.1, jquery-migrate 3.4.1, go_portfolio magnific-popup, isotope, carouFredSel-6.2.1, touchSwipe and go_portfolio_scripts.js, font-awesome v4-shims.min.js, and recaptcha api.js. The other 21 synchronous scripts, wp-polyfill, both CF7 bundles, Elementor, Elementor Pro, EAEL, Constant Contact, jquery-ui core, swiper, load in the footer at lines 1567 to 1711 and do not block first paint. go_portfolio also preloads two stylesheets at line 67, magnific-popup and go_portfolio_styles.css, onload swap, not render-blocking. Body lines 411 to 1720 contain zero go-portfolio, isotope, carouFredSel, magnific or touchSwipe markup. lighthouse-summary.json mobile render_blocking, 9 items: jquery.min.js wastedMs 2,808, carouFredSel 2,142, isotope 1,809, magnific-popup 1,809, go_portfolio_scripts 1,809, touchSwipe 1,476, v4-shims 1,476, jquery-migrate 1,476, recaptcha api.js 881. lighthouse-mobile.report.json render-blocking-resources overallSavingsMs 1,848, desktop 330. mainthread-work-breakdown 3,241 ms mobile, scriptEvaluation 1,833 ms, scriptParseCompile 470 ms. Mobile FCP 8.4 s, 8,387 ms, desktop FCP 1.7 s.

Impact

Nothing paints until these parse and execute. go_portfolio alone adds five blocking scripts and two stylesheets for a widget absent from the homepage, and it is a 2013-era carousel plugin shipped on all 163 pages. Lighthouse attributes 1,848 ms of the 8.4 s mobile FCP to render-blocking resources. The rest is page weight. The site runs Google Ads and a scheduler, so an 8.4 s mobile first paint is burning paid clicks.

Recommendation

Do it in this order, all on staging first, then production.

1. Go Portfolio. Confirm nothing uses it with WP-CLI: wp db search '[go_portfolio' --all-tables. Also open the plugin's admin screen and check for saved portfolios. Crawl metadata shows it enqueued on all 163 pages with zero portfolio markup on the homepage, but the crawl did not capture body markup on the other 162 pages, and the residential and commercial product grids are the likely place it lives. If unused, deactivate and delete it. It is v1.4.0 bundling carouFredSel 6.2.1 from 2013. If exactly one or two pages use it, keep it active and in neve-child-master functions.php dequeue the seven handles on wp_enqueue_scripts at priority 100, guarded by a negated is_page() check for those slugs, covering go-portfolio-magnific-popup-styles, go-portfolio-styles and the five script handles.

2. Font Awesome 4 shim. Elementor > Settings > Advanced > Load Font Awesome 4 Support set to No. If Elementor > Tools still offers the Font Awesome 4 Upgrade, run it first. Then spot-check icons on the homepage, service pages and footer. That removes v4-shims.min.js and v4-shims.min.css from every page.

3. jquery-migrate and defer. No optimization plugin is installed, so buy Perfmatters, single site, and turn on Remove jQuery Migrate, Defer JavaScript, and Also Defer Inline JS. Do not tick Exclude jQuery. No inline script in the head calls jQuery, and the only inline body call is WP core's jQuery.uiBackCompat line before jquery-ui-core, which the inline deferral handles. If the client refuses a paid plugin, remove migrate with a six-line wp_default_scripts filter in the child theme that strips jquery-migrate from the jquery handle's deps on the front end, and defer with the free Flying Scripts plugin or a script_loader_tag filter that adds defer to every first-party handle except the reCAPTCHA one.

4. Elementor. The Improved Asset Loading experiment no longer exists in Elementor 4.2.3. Skip it. Open Elementor > Settings > Performance and enable Element Caching while you are there.

5. Cloudflare. Leave Rocket Loader off. It breaks Elementor and the ServiceTitan scheduler widget, and the WordPress-level defer above does the same job safely.

6. Verify. Rerun Lighthouse mobile and confirm render-blocking-resources savings fall to near zero and FCP drops by roughly 2 s. Test both contact forms with a live submission, the ServiceTitan scheduler button, and the mobile menu on a real phone, since defer changes script order.

Set expectations: this pass fixes first paint and trims about 85 KB of parse work on every page. It will not move LCP toward the 2.5 s threshold. That number is owned by the hero image finding, which should ship in the same week.

Pages. Site-wide, header enqueue.

F23 Medium CategoryPerformance EffortSmall Quick winYes

Four blog thumbnails 7,300 to 8,700 px below the fold load eagerly at full size with no srcset, 1.5 MB

Evidence

lighthouse-mobile.report.json offscreen-images overallSavingsBytes 1,709,123 across 15 images. The four blog thumbnails account for 1,523,616 bytes, about 1.5 MB: PowerLineImage.png 515,130 at top 8,658 px, SolarHouseImage.jpg 354,496 at 7,820 px, HurricaneHilary.png 332,324 at 8,179 px, Earthquakes.png 321,666 at 7,345 px, all inside div.eael-grid-post-holder-inner > div.eael-entry-media > div.eael-entry-thumbnail > img. The remaining 185,507 bytes are award logos in the swiper plus a 100 KB Shutterstock section background. Markup in homepage.html: <img decoding="async" src=".../PowerLineImage.png" alt="A Power Plan is Crucial" /> with no loading, srcset or sizes. Page-wide 27 img, 7 loading=lazy, 5 srcset, 0 fetchpriority. perf-profile.json oversizedImgs, desktop run: SolarHouseImage 1024x576 rendered 263x148, HurricaneHilary 875x711 rendered 263x214, PowerLineImage 768x432 rendered 263x148. Mobile run renders the same three at 368 px wide and adds logo.png 300x73 rendered 120x29. lighthouse-desktop.report.json uses-responsive-images Est savings 1,764 KiB across 9 images, of which the four thumbnails are 1,246 KiB. Mobile network log: all four request at 2,340 ms with priority Low and finish by 4,788 ms, while the 472 KB hero banner starts at 2,345 ms at High priority. LCP at 31,509 ms. Live HEAD on PowerLineImage.png returns content-length 515130, cache-control max-age=14400.

Impact

1.5 MB downloads during the LCP window for content 9 to 10 screens down on a phone, competing with the hero for slow 4G bandwidth. Lighthouse credits deferring them with only 40 ms of LCP savings on its own model, so this is bandwidth contention rather than the root cause, which is the late-discovered hero. It is still 21 percent of page weight on every mobile visit.

Recommendation

Do this inside the LCP sprint, not as the headline fix. Step one, in the Elementor editor open the homepage, select the Essential Addons Post Grid, and under Content > Layout change Image Size from Full to Medium Large, 768 px. That alone drops the four thumbnails from 1.52 MB to roughly a quarter of that, and 768 px covers the 263 to 380 px card at 2x retina. Step two, EA Lite still prints a bare <img decoding="async" src alt> with no srcset, sizes, width, height or loading attribute after that change, and WordPress core cannot add them because the markup never passes through wp_filter_content_tags. The site already runs Elementor Pro, so swap the EA Post Grid for the Elementor Pro Posts widget, Classic or Cards skin, Image Size Medium Large. Pro's Posts widget renders through wp_get_attachment_image, which emits srcset, sizes, intrinsic width and height, and loading="lazy" from core with no extra plugin. Step three, three of the four thumbnails are PNGs, so resizing helps less than it should. Bulk convert the media library to WebP with ShortPixel or Converter for Media in the same pass, or turn on Cloudflare Polish if the account is on Pro. Step four, verify on the live page that the four thumbnails carry loading="lazy" and a srcset, and that offscreen-images and uses-responsive-images no longer list them in Lighthouse. Expected result is about 1.3 to 1.5 MB removed from the initial mobile download.

Pages. Homepage and any page using the EA Post Grid.

F24 Medium CategoryPerformance EffortSmall Quick winYes

Cloudflare passes every HTML request to origin and static assets expire in 4 hours, 145 short-lived resources

Evidence

headers-timings.txt homepage: cache-control: max-age=3, must-revalidate, cf-cache-status: DYNAMIC, vary: Accept-Encoding,Cookie, alt-svc: h3=":443"; ma=86400. Live curl Sep 9, 2026: https://ltgenerators.com/wp-content/uploads/2024/05/LTG-HP-Banner-Image.jpg, /wp-content/plugins/elementor/assets/css/frontend.min.css?ver=4.2.3, /wp-content/uploads/favicon-LTGenerators-Installer.ico and /wp-content/uploads/2021/07/cropped-icon1-180x180.png all cache-control: max-age=14400, cf-cache-status HIT. /wp-includes/js/jquery/jquery.min.js?ver=3.7.1 cache-control: max-age=14400, cf-cache-status MISS, meaning the 4-hour edge copy had already expired and the request went to origin. lighthouse-summary.json long_cache_ttl_count 145 mobile, 151 desktop. lighthouse-mobile.report.json uses-long-cache-ttl numericValue 4,006,600 wasted bytes, cache-insight displayValue "Est savings of 3,913 KiB". Desktop report: 4,431,634 wasted bytes, "Est savings of 4,328 KiB". The top five entries in the long-cache-ttl sample are ServiceTitan files totaling about 1.8 MB with cacheLifetimeMs 0, vendor-controlled. perf-profile.json desktop noCache 166 of 272 responses, mobile 165 of 266. domain-tech.txt whois Registrar: Bluehost Inc., likely shared origin. Live curl Sep 9, 2026 of /does-not-exist-404-test/: HTTP/2 404, cache-control: no-cache, must-revalidate, max-age=0, no-store, private, cf-cache-status: DYNAMIC. crawl-summary.txt: average HTML response 1,694 ms, maximum 7,630 ms.

Impact

Every HTML request runs origin PHP, and the crawl measured average HTML response at 1,694 ms across all 163 pages against Google's 800 ms TTFB guidance. Static assets are already served from the Cloudflare edge within a session. The 4-hour TTL only costs visitors who return later. Crawl budget is not a concern at 163 URLs. This work fixes TTFB and repeat-visit bytes. Lighthouse puts TTFB at 3 percent of mobile LCP and 7 percent of desktop LCP, so it does not fix the LCP failure.

Recommendation

Fix the two caching layers in this order. Together they take a WordPress developer about an hour.

1. Static assets, 10 minutes, no plugin. Cloudflare dashboard > Caching > Cache Rules > Create rule. Expression: Hostname equals ltgenerators.com AND (URI Path starts with /wp-content/ OR URI Path starts with /wp-includes/). Cache eligibility: Eligible for cache. Edge TTL: Ignore cache-control header and use this TTL, 1 month. Browser TTL: Override origin, 1 year. Save and deploy. This replaces the zone default of 4 hours that is producing max-age=14400 today. It is safe because WP core, Elementor and plugin files carry ?ver= query strings that change on update, and WordPress never rewrites an upload in place. Nothing needs to change at origin.

2. HTML, 30 minutes. Install the official Cloudflare WordPress plugin, connect it with an API token, and turn on Automatic Platform Optimization. APO is included on Pro and is a 5 dollar per month add-on on Free. It caches HTML at the edge, bypasses for wordpress_logged_in and wp-postpass cookies, and purges on publish. Do not use a hand-built Cache Everything rule here. 160 of the 163 pages carry a form, the site runs reCAPTCHA and the ServiceTitan scheduler, and a raw Cache Everything rule will serve stale nonces and break submissions. If the client will not pay for APO, install WP Rocket at origin and enable its page cache instead. Either route should bring average HTML response from the crawl's 1,694 ms to under Google's 800 ms TTFB guidance.

3. While in the dashboard, Speed > Optimization > Content Optimization > Early Hints, toggle on. Free and harmless.

Leave ServiceTitan out of this finding. Its main.0c4e1ea0.js bundle sends no Cache-Control header at all, that is vendor-controlled, and the fix is loading the scheduler on click, which belongs in the third-party finding.

Pages. Site-wide, all 163 URLs and all static assets.

F25 Medium CategoryPerformance EffortMedium Quick winYes

TTFB 0.8 to 2.1 s on the homepage, and the crawl saw HTML responses up to 7.6 s on generator pages

Evidence

headers-timings.txt three homepage HTML samples: ttfb 2.106 s, 0.949 s, 0.788 s, connect 0.770 s and tls 1.182 s on the first. The cf-ray suffixes DUB and NRT show those samples were served from Dublin and Tokyo Cloudflare colos, so handshake time inflates them. lighthouse-summary.json server-response-time 710 ms mobile, 310 ms desktop, LCP TTFB phase 969 ms mobile, 425 ms desktop. lighthouse-mobile.report.json network-server-latency 480 ms, desktop 150 ms. perf-profile.json desktop ttfb 1,399 ms, load 3,606 ms. crawl-summary.txt: avg HTML response 1,694 ms, max 7,630 ms, single sample per URL, measured as total HTML response time not isolated TTFB. Median across the 163 URLs about 1.6 s with 42 pages over 1.8 s. Slowest: /generator/kohler-diesel-generators-101-399kw/ 7,630 ms, /generator/cummins-diesel-generators-150-750kw/ 5,227 ms, /power-outage-preparation-free-assessment/ 4,363 ms, /generator/generac-transfer-switch/ 3,558 ms, /generator/kohler-transfer-switch/ 3,373 ms, /generator/briggs-stratton-air-cooled/ 3,177 ms. Live recheck Sep 9, 2026 of the kohler-diesel page: ttfb 1.358 s, total 1.550 s. A second recheck of the Kohler and Cummins pages gave 0.80 and 0.87 s, then 1.62 and 0.54 s, so the crawl spikes were transient, likely a cold cache after the origin's 3 second max-age plus crawl concurrency on shared hosting.

Impact

Google's guidance is TTFB under 800 ms. The homepage root document clears it in Lighthouse at 310 ms desktop and 710 ms mobile, but the site-wide median of 1.6 s and 42 of 163 pages over 1.8 s spend most of the 2.5 s LCP budget before a byte arrives. TTFB is a diagnostic, not a Core Web Vital, and the 31.5 s lab LCP is owned by page weight and the hero, not the server.

Recommendation

Fix in this order, all doable this week.

1. Page cache at the origin. The registrar and the SPF record point to Bluehost shared hosting, so LiteSpeed Cache will not run its page cache here. Install WP Rocket. Turn on Mobile Cache, set Cache Lifespan to 24 hours, turn on sitemap-based Preload so the 45 /generator/ pages are warm before a visitor or Googlebot arrives, and exclude /wp-json/, /thank-you/, and any ServiceTitan scheduler URL. If the Bluehost plugin's own caching is on, set it to Off so only one page cache runs. Free fallback is Cache Enabler.

2. Edge cache the HTML. Every HTML response is cf-cache-status DYNAMIC and the origin sends cache-control max-age=3, so Cloudflare never keeps it. Simplest path: install the official Cloudflare WordPress plugin, add an API token, and turn on Automatic Platform Optimization, which costs about 5 dollars a month on the Free plan, caches HTML at the edge, and purges on publish. Free path: one Cache Rule that makes requests eligible for cache when the hostname is ltgenerators.com, the URI path does not start with /wp-admin, and the cookie does not contain wordpress_logged_in or comment_author, with Edge TTL set to Override origin at 1 day. Pair it with WP Rocket's Cloudflare add-on so saving a page purges the edge.

3. Then profile the origin. Log in, open any /generator/ single with Query Monitor, and look for a slow query or remote HTTP call in the go_portfolio or Elementor template. Target under 600 ms uncached, which is the line Lighthouse's server-response-time audit uses. Confirm PHP 8.2 or newer and OPcache on in the Bluehost panel.

4. Verify with three curl runs each on the homepage and three /generator/ pages from a US location. Expect cf-cache-status HIT and TTFB under 300 ms. Field data was unavailable during the audit because the PSI API returned 429, so watch the Search Console Core Web Vitals report over the next 28 days instead of claiming a CWV outcome now.

Pages. All 163 sitemap URLs, worst on /generator/* and /power-outage-preparation-free-assessment/.

F26 Medium CategoryPerformance EffortSmall Quick winYes

Web fonts have no font-display, Font Awesome is the largest font cost, and a second Roboto origin comes from third-party embeds, 16 font files, 374 KB

Evidence

lighthouse-summary.json network_by_type, mobile and desktop: Font 16 requests, 374 KB. font_display mobile: roboto-kfo7cnqeu92fr1me7ksn66agldtyluama3yuba.woff2 wastedMs 1,030 and roboto-kfo5cnqeu92fr1mu53zec9_vu3r1gihoszmkbnka.woff2 wastedMs 1,019, desktop 536 and 536. lighthouse-mobile.report.json font-display-insight: "Est savings of 1,730 ms", FCP metricSavings 1,750 ms, driven mostly by Elementor's Font Awesome webfonts, fa-solid-900.woff2 1,725 ms, fa-regular-400 1,115 ms, fa-brands-400 1,070 ms, plus the two local Roboto files, 1,030 and 1,015 ms. Mobile network-requests: fonts.gstatic.com 7 requests 98 KB, Roboto v48 woff2 x3 and Nunito Sans v19 x4, fonts.googleapis.com 4 requests 1 KB, all the Nunito Sans css?family= stylesheet, ltgenerators.com 7 font-related requests 258 KB from uploads/elementor/google-fonts and plugins/elementor font-awesome. Live curl Sep 9, 2026 of uploads/elementor/google-fonts/css/roboto.css?ver=1745848284: HTTP 200, 103,842 bytes, 162 @font-face rules, 0 font-display, robotoslab.css: 63 @font-face, 0 font-display. domain-tech.txt line 39 generator meta: "Elementor 4.2.3. google_font-enabled, font_display-auto". homepage.html contains no fonts.googleapis.com or fonts.gstatic.com link from the Neve theme. The only font links are Elementor's two local preload stylesheets. The remote Roboto is a bare fonts.gstatic.com request with no Roboto stylesheet from googleapis, consistent with the reCAPTCHA iframes, and Nunito Sans comes from the ServiceTitan scheduler widget. Initiator could not be proven from the Lighthouse report, so the second origin is attributed to third-party embeds, not the Neve Customizer.

Impact

font-display auto hides text up to 3 s per font on slow connections, delaying FCP and the hero headline. The two Roboto files each cost about 1 s of invisible headline text on mobile and the Font Awesome files up to 1.7 s. None of this moves LCP, which is the hero background image, and CLS is 0.002.

Recommendation

Step 1, Roboto. Elementor > Settings > Performance, set Google Fonts Load to Swap, save. Then Elementor > Tools > Regenerate Files & Data so the local CSS is rewritten. Curl https://ltgenerators.com/wp-content/uploads/elementor/google-fonts/css/roboto.css and confirm it now contains font-display: swap. If it still shows zero, delete wp-content/uploads/elementor/google-fonts over SFTP and load the homepage once so Elementor refetches the CSS with the display parameter.

Step 2, Font Awesome. Elementor > Settings > Features, turn on Inline Font Icons. This removes fa-solid-900, fa-regular-400 and fa-brands-400 woff2, 169 KB and the three largest font-display penalties in the trace at 1,725, 1,115 and 1,070 ms, and renders icons as inline SVG. Click through the homepage and the contact page afterward, Essential Addons widgets occasionally need the font and would show blank squares.

Step 3, preload the one file the hero H1 uses. In neve-child-master functions.php, hook wp_head at priority 1 and echo <link rel="preload" href="/wp-content/uploads/elementor/google-fonts/fonts/roboto-kfo7cnqeu92fr1me7ksn66agldtyluama3yuba.woff2" as="font" type="font/woff2" crossorigin>. Skip the italic file. Re-check the filename after step 1 in case regeneration changed it. Every stylesheet on the site is loaded async through rel=preload as=style onload, so without this hint the browser cannot see the font until roboto.css has downloaded and applied.

Step 4, re-run Lighthouse mobile and confirm the font-display insight lists only fonts.gstatic.com and servicetitan.com files. Those remaining two belong to the reCAPTCHA and ServiceTitan findings. No theme or Elementor setting touches them.

Pages. Site-wide.

F27 Medium CategoryPerformance EffortSmall Quick winYes

Thirty stylesheets, Font Awesome shipped three ways, three CSS files 98 to 99% unused

Evidence

homepage.html: 30 stylesheet <link> tags, but every one of them is rel='preload' as='style' with an onload swap to rel='stylesheet', 0 plain rel=stylesheet links, so they are async-loaded, not render-blocking. Lighthouse mobile render_blocking lists 9 scripts and zero stylesheets. Font Awesome loads three ways: elementor all.min.css?ver=4.2.3 plus v4-shims.min.css and v4-shims.min.js, ver=4.2.3, the JS is render-blocking at 4,820 bytes and 1,476 ms wasted, separately fontawesome.min.css, solid, brands, regular all ver=5.15.3, plus eicons 5.53.0. unused_css, mobile: eael-8088.css 17,788 bytes with 17,537 wasted, 98.6%, fontawesome.min.css 13,742 with 13,599 wasted, 99.0%, all.min.css 13,709 with 13,537 wasted, 98.7%. network_by_type mobile: Stylesheet 46 requests, 379 KB. font-display-insight, mobile full report: fa-solid-900.woff2 wastedMs 1,725, fa-regular-400.woff2 1,115, fa-brands-400.woff2 1,070. Those three woff2 files transfer 78,691 + 82,102 + 13,731 = 174,524 bytes, about 170 KB. swiper.min.js 43,135 bytes with 25,177 wasted, mobile. The page uses about 40 icon tags and zero inline SVGs, so Inline Font Icons is off.

Impact

The stylesheets cost requests and bytes rather than render blocking, since all 30 use the preload-and-swap pattern. Three icon-font families download about 170 KB of woff2 tied to 1.7 s of font-display delay, for a handful of icons, and one shim script is genuinely render-blocking. FCP is 8.4 s on mobile, and this is a real share of that, but it cannot move LCP on its own.

Recommendation

Keep this as one ticket, about two hours including regression checks. Do it on staging or during a quiet hour, then clear the Cloudflare cache.

1. Elementor > Settings > Advanced > Load Font Awesome 4 Support = No. This drops all.min.css at 13.7 KB and 98.7% unused, v4-shims.min.css, and v4-shims.min.js, which Lighthouse lists as a render-blocking script worth about 1.5 s on mobile. Safety check first: search Elementor content for legacy "fa fa-" classes. The homepage only uses fas, far and fab prefixes, so it is safe there. Spot-check the service and contact pages before flipping it.

2. Elementor > Settings > Performance > Inline Font Icons = Active. On older builds this lives under Settings > Features. The page uses about 11 distinct icons, mostly fa-star for reviews and the four social brands. Elementor will render them as inline SVG and stop loading fontawesome.min.css, solid.min.css, brands.min.css and regular.min.css plus three woff2 files, about 175 KB of fonts. Then run Elementor > Tools > Regenerate CSS & Data and eyeball the review stars and footer social icons.

3. Improved CSS Loading is already on, so skip it. The per-widget files like widget-heading.min.css and the conditional e-swiper.min.css only appear when that feature is active.

4. Essential Addons > Elements: untick every widget not used on the site, then Essential Addons > Tools > Regenerate Assets so eael-8088.css, 17.8 KB and 98.6% unused, rebuilds smaller.

5. While in Elementor > Settings > Performance, set Google Fonts Load to Swap, which the fonts finding also asks for.

6. Do not install a combine or concatenate CSS option. The site is on HTTP/2 through Cloudflare and concatenation only breaks caching. If the client wants the next step after this week, add Perfmatters and use its Script Manager to unload go_portfolio's magnific-popup.css and go_portfolio_styles.css, constant-contact-forms style.css and the Contact Form 7 CSS on pages that do not use them. Leave elementor-icons.min.css alone, Elementor's lightbox and video widgets still need it.

Expected result: 7 fewer stylesheet requests, one fewer render-blocking script, about 44 KB of unused CSS and 175 KB of icon fonts gone on every page. Re-run PageSpeed Insights mobile afterward and expect FCP to improve. Do not expect LCP to pass.

Pages. Site-wide.

F28 Medium CategoryAnalytics EffortMedium Quick winYes

Conversion tracking is degraded: legacy ga() call, dead gtag_report_conversion, triple-bound CTA handler and a head script that throws on every page

Evidence

homepage.html lines 397 to 401: an inline <script async> in <head> registers document.addEventListener('wpcf7mailsent', ...) which calls ga('send','event','Contact Form','submit'). The page HTML never loads analytics.js or defines ga, but Lighthouse network-requests on both devices show https://www.google-analytics.com/analytics.js requested at runtime, status 200, 21,409 bytes transferred, and perf-profile.json lists www.google-analytics.com with one request on both devices, so analytics.js is injected at runtime by GTM-N7XTZCZ, not by the theme, and whether window.ga exists when wpcf7mailsent fires was not probed. Either way the call targets Universal Analytics, which stopped processing hits in July 2023. Line 1502 fires gtag('event','wpcf7_submission') on wpcf7mailsent, and Site Kit loads G-BVPGS29PEC at line 135, so that event does reach GA4 today. Lines 381 to 382 gtag('config') only AW-800825679 and UA-120927605-3. Line 171 defines gtag_report_conversion(url) for AW-800825679/Wj0hCJCJp5IBEM_C7v0C, its only other mention is the Google comment at line 169, nothing calls it. Three DOMContentLoaded blocks open at lines 184, 1536 and 1560 and bind .open-widget-btn at lines 186, 1538 and 1561, each calling STWidgetManager('ws-open') at lines 191, 1545 and 1564. Only the first fires gtag event financing_widget_open at line 194, the second POSTs admin-ajax action=send_widget_email at line 1553, so one click on the single .open-widget-btn element at line 434 opens the widget three times and sends one admin-ajax email but logs one financing_widget_open event. Two other Get Free Assessment buttons at lines 842 and 1098 use inline onclick="STWidgetManager("ws-open")" with no analytics event. Lines 403 to 408 in <head>: document.getElementById('notify-btn').addEventListener on a page with no notify-btn element. lighthouse-summary.json console_errors, both devices: TypeError: Cannot read properties of null (reading 'addEventListener') at https://ltgenerators.com/:404:38. Best Practices 71 mobile, 74 desktop. Live curl of https://ltgenerators.com/about-us/ on Sep 9, 2026, HTTP 200, 151,811 bytes, body class wp-child-theme-neve-child-master: same notify-btn script at line 369 with no notify-btn element, same ga('send') at line 364, same three .open-widget-btn bindings at lines 151, 1180 and 1203, same gtag_report_conversion definition at line 136.

Impact

Lead attribution is degraded rather than absent: GA4 receives the CF7 event through the line 1502 listener, but the ga() call targets a dead property, the Ads conversion helper is dead code, two of three homepage assessment CTAs are invisible to analytics, the tracked one logs an event named after financing, and an unconditional exception costs Best Practices points on every page. Nothing here affects rankings or indexing.

Recommendation

Keep the line 1502 listener and delete the ga() one. Do the cleanup in the neve-child-master theme. Open it in Appearance > Theme File Editor or over SFTP, grep header.php, footer.php and functions.php for these blocks and delete them: the ga('send') listener at homepage lines 398 to 401, the notify-btn script at 403 to 408, the two duplicate .open-widget-btn DOMContentLoaded blocks at 1538 and 1561, the gtag_report_conversion function at 170 to 182, and the 2018 manual gtag loader at 373 to 384, leaving line 374, the reCAPTCHA loader, to its own ticket. That loader configs UA-120927605-3, a Universal Analytics property Google stopped processing in July 2023, and it downloads 133 KB of dead gtag/js on every page per the Lighthouse unused_js audit. Site Kit already configs both G-BVPGS29PEC and AW-800825679 at lines 141 to 142, so nothing is lost and the duplicate AW config that can double-count page-load conversions goes away. If the notify-btn feature is real, move that script to the footer of the one page that has the button, wrap it in DOMContentLoaded and guard with if, btn. Its current position inside head means it throws even on a page that has the button, because it runs before the body parses.

Keep the single .open-widget-btn handler at line 185. Add e.preventDefault(), rename the event from financing_widget_open to schedule_widget_open, and make the two Get Free Assessment buttons at lines 842 and 1098 use it by editing their Elementor HTML widgets to carry class="open-widget-btn" and dropping the inline onclick. Take the admin-ajax send_widget_email POST out of the click handler unless the office wants an email on every scheduler open, because right now it fires per click, not per booking.

Replace the dead Ads snippet with the current path. In GA4 Admin > Events, mark wpcf7_submission as a key event. In Google Ads > Goals > Conversions > New conversion action > Import > Google Analytics 4 properties > Web, import that key event. The real lead for this business is a ServiceTitan booking, not a widget open. Check the Scheduler Pro settings in ServiceTitan for booking-confirmation conversion tracking and add the GA4 measurement ID there so a completed booking becomes the key event.

GTM-N7XTZCZ: open Tag Assistant on the homepage. If the container fires nothing that Site Kit does not already fire, remove the GTM snippet at lines 388 to 394 so the site runs one tag loader. If it fires live tags, leave it alone this week and do not add new tags there. Coordinate with the tag-stack finding, which proposes the opposite consolidation onto GTM. Pick one loader and keep the gtag stub either way.

Verify in GA4 DebugView with the browser console open. One CF7 submit shows one wpcf7_submission, one scheduler click shows one schedule_widget_open, and the console shows zero errors. Rerun Lighthouse and confirm the errors-in-console audit passes.

Pages. Site-wide, injected by the neve-child theme into every page.

F29 Medium CategoryIndexation EffortSmall Quick winYes

robots.txt points crawlers at a static 2019 sitemap with dead URLs, slash variants, an ID slug and a PDF, not the live Yoast index

Evidence

robots.txt last line: Sitemap: https://ltgenerators.com/sitemap.xml. That URL returns sm-sitemap.xml, comment 'created with Free Online Sitemap Generator www.xml-sitemaps.com', 63 URLs, every lastmod between 2019-01-21 and 2019-02-25, content-type application/xml where Yoast's sitemap_index.xml serves text/xml; charset=UTF-8. The live Yoast index is sitemap_index.xml with 8 children, newest lastmod Sep 2, 2026, page-sitemap.xml, 163 URLs total. Live check Sep 9, 2026: of 63 URLs, 56 return 200, 5 return 301, 2 return 404, /tesla-powerwall-vs-standby-generator/ and /generator-maintenance-top-10-tips/. The 5 redirects already resolve: /financing and /testimonials 301 to their slash forms, /generator/2078/ 301s to /generator/generac-gaseous-generator-130-150kw/, the two old kohler slugs, 24-48kw and 14-20kw, 301 to /generator/kohler-liquid-cooled-generator-naturalgas-propane-24-60kw/ and /generator/kohler-standby-generator-8-20kw/. Eight URLs exist only in the static file: the two 404s, the two old kohler slugs, /generator/2078/, /financing, /testimonials, and the 2018 PDF Maintenance-Tips-for-your-Automatic-Home-Backup-Generator-MarcoFinal.pdf, returns 200. It lists both /financing/ and /financing, both /testimonials/ and /testimonials, while homepage.html links only the slash forms, 8 hrefs to /financing/, 3 to /testimonials/, zero no-slash. sm-wp-sitemap.xml is 0 bytes. generator-sitemap.xml lastmods include 2026-08-25 entries, so the 2026 generator pages are only in the Yoast index. The static file omits all four city landing pages.

Impact

Any crawler discovering sitemaps through robots.txt gets a seven-year-old list with 404s, redirect hops and duplicate variants, while the 163-URL Yoast index reflecting the 2026 catalog is never referenced. Nothing is blocked and a 163-URL site with internal links gets crawled without any sitemap, so the cost is slower discovery, a self-contradicting lastmod signal for the new generator pages, and a bad signal to Bing and AI crawlers that read the robots.txt directive directly.

Recommendation

Fix in one pass, under an hour, in this order. 1. Via SFTP or the host file manager, delete the physical files sitemap.xml, sm-sitemap.xml and the 0-byte sm-wp-sitemap.xml from the WordPress web root. Nothing else generates them, and once the physical file is gone Yoast's built-in rewrite takes over. Confirm with curl -I https://ltgenerators.com/sitemap.xml, which should now return a 301 to https://ltgenerators.com/sitemap_index.xml. 2. Change the robots.txt Sitemap line to https://ltgenerators.com/sitemap_index.xml. If robots.txt is a physical file, edit it in Yoast SEO > Tools > File editor or over SFTP. If it is a virtual WordPress robots.txt, remove whatever filter or plugin is injecting the old line, since Yoast adds the correct sitemap_index.xml line on its own. 3. Purge both URLs in Cloudflare > Caching > Custom Purge so crawlers see the change immediately. 4. In Google Search Console > Sitemaps, delete any sitemap.xml entry and submit sitemap_index.xml. Do the same in Bing Webmaster Tools, which leans harder on the robots.txt directive and feeds Copilot and DuckDuckGo. 5. For the two 404s, /tesla-powerwall-vs-standby-generator/ and /generator-maintenance-top-10-tips/, check Search Console Links and Performance first. If either has backlinks or recent impressions, 301 it to the closest live post with the free Redirection plugin. If neither does, leave the 404 in place. Google treats a 404 for a removed page as correct, and both URLs drop out of every sitemap signal once step 1 lands. No action is needed on /financing, /testimonials or /generator/2078/. All three already 301 to their canonical slash URLs. 6. Two weeks later, open Search Console > Pages and confirm the 45 URLs in generator-sitemap.xml show Indexed rather than Discovered, currently not indexed. Raise this finding to high only if Search Console shows sitemap_index.xml was never submitted and the generator pages are sitting in Discovered.

Pages. /robots.txt, /sitemap.xml, /sitemap_index.xml, /financing, /testimonials, /generator/2078/, the 2018 PDF

F30 Medium CategoryOn-page EffortSmall Quick winYes

Kohler pages are duplicated, thin and triple-H1 with a typo, in a SERP LT half-owns, and terms and contact pages also have indexable twins

Evidence

crawl.json: /kohler/ 225 words, /kohler-generators/ 651 words, both with the same 3 H1s 'Dependable Power Suppy | Why kohler? | What Our Client Says' including the Suppy typo, titles 128 and 139 chars. /kohler-contact-us/ 664 words, 141-char title, zero H1, vs /contact-us/ 697 words, also zero H1. /terms-conditions/ 1645 words, 140-char title, zero H1, vs /terms-of-services/ 1403, both robots index,follow. homepage.html links to /contact-us/ and /terms-conditions/ and to no Kohler page at all, so both Kohler pages live only in Yoast's page-sitemap.xml. Sep 9, 2026 US search snapshot, re-run once, 'Kohler generator dealer Los Angeles': linkedin.com/company/ltgenerators #1, yellowpages.com #2 and #3, bcew.com, Bay City Electric Works, Kohler dealer, #4, greencat.ninja Kohler page, Placentia, titled 'Los Angeles & Orange County', #5, Kohler dealer locator #6, ltgenerators.com/ #7, /our-commercial-generators/ #8. Neither LT Kohler page appears. BRIEF.md: Kohler Authorized Dealer, 8 to 150 kW, new Kohler colors. /generator-cat/kohler/ and /generator-cat/kohler-commercial/ are 748 and 908 words of different products sharing an auto-generated title.

Impact

Two Kohler pages with the same three H1s split the brand query between a 225-word stub and the fuller page while LT ranks with its LinkedIn profile. Neither Kohler page has a single navigational link, so consolidation without new inbound links changes nothing. 'Suppy' is a live typo in the H1 of a brand-partner page. Duplicate terms and contact pages are hygiene that never converts from search.

Recommendation

Consolidate the Kohler pages first, since "Kohler generator dealer Los Angeles" is a commercial query LT is credentialed to win. Leave the terms and contact twins for the same pass but treat them as hygiene.

1. In Google Search Console, filter Performance by page for /kohler/ and /kohler-generators/ over 12 months. Keep whichever has impressions. Default to /kohler-generators/, 651 words. Trash the other in WordPress and add the 301 to the keeper with the free Redirection plugin. Purge Cloudflare cache after.

2. On the keeper page in Elementor, open each Heading widget and set HTML Tag. One H1: "Kohler Generator Dealer in Los Angeles". Change "Why Kohler?" and "What Our Clients Say" to H2. Fix "Suppy" to "Supply" and capitalize Kohler everywhere. In the Yoast box set SEO title to "Kohler Generator Dealer Los Angeles | LT Generators" and write a 150-character meta description that names Authorized Dealer, 8 to 150 kW, and residential plus commercial. Add short H2 sections for the 8 to 150 kW home line, commercial gaseous and diesel, colors, and warranty, each linking to the matching /generator/kohler-* product page.

3. Give the page inbound links. Appearance > Menus: add a Brands dropdown with Kohler and Generac. Link to it from /generator-cat/kohler/, /generator-cat/kohler-commercial/, and the Kohler product pages. Without internal links the page will not rank regardless of the on-page fixes.

4. Fix the root cause of every 128 to 141 character title: Yoast > Settings > Content types > Pages, change the title template to "%%title%% | LT Generators" and drop %%sitename%% %%sep%% %%tagline%%. Do the same under Categories & tags for the generator-cat taxonomy: "%%term_title%% Generators Los Angeles | LT Generators", then give the kohler-commercial term its own Yoast title so the two archives stop sharing one. Do not merge those two archives, they hold different product sets.

5. /kohler-contact-us/: if it is the landing page for a Kohler co-op or paid campaign, set Yoast "Allow search engines to show this page" to No. Yoast drops it from the sitemap automatically. If it is not in use, 301 it to /contact-us/, not to the Kohler brand page, because a form should redirect to a form.

6. Terms: the footer already links /terms-conditions/, so that is the live one. Have the owner confirm the text, then trash /terms-of-services/ and 301 it to /terms-conditions/.

Pages. /kohler/, /kohler-generators/, /kohler-contact-us/, /contact-us/, /terms-conditions/, /terms-of-services/, /generator-cat/kohler/, /generator-cat/kohler-commercial/

F31 Medium CategoryIndexation EffortMedium Quick winYes

The /thank-you/ conversion page, two 16-word calendar stubs, a survey and six duplicate lead-form pages are indexable, and the Ads conversion fires on any thank-you pageview

Evidence

sitemaps.json page-sitemap.xml lists all ten URLs. crawl.json rows: /cal-submit/ and /cal-list/ 16 words each, zero H1, no form. /post-generator-installation-survey/ 560 words, zero H1. /thank-you/ 522 words, zero H1. /service-request/ 496. /schedule-consultation/ 639. /install-assessment/ 528 with two H1s. /home-generator-assessment/ 930. /power-outage-preparation-free-assessment/ 558 at 4,363 ms response. /kohler-contact-us/ 664, zero H1. All status 200, robots meta "index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1". homepage.html loads wpcf7-redirect, frontend-script.js and frontend-script.css from wp-content/plugins/wpcf7-redirect/, which sends CF7 submissions to a page after submit. Live fetch of googletagmanager.com/gtm.js?id=GTM-N7XTZCZ on Sep 9, 2026, ID from domain-tech.txt line 75: six __awct Google Ads conversion tags, all on conversion ID 800825679, each with vtp_url bound to macro 4, a __u Page URL variable, plus one __fsl form submit listener, tag_id 33, wired to no tag. Rule 5 fires conversion label Wj0hCJCJp5IBEM_C7v0C on any pageview whose URL contains /thank-you with no referrer or submit condition. Rules 1 and 2 fire two more labels on /thank-you/ keyed only to referrer. crawl-summary.txt line 75: 160 of 163 pages carry a form. The three without are /cal-submit/, /cal-list/ and /kohler/.

Impact

Google Ads only attributes a conversion when a gclid cookie is present, so an organic visitor does not become a lead in Ads reporting. The real inflation is refreshes and back-forward loads under Every counting, and past ad clickers who revisit inside the conversion window, which feeds Smart Bidding bad data for a paid-dependent installer. Six pages asking for the same assessment split internal links and let Google pick which ranks, none with a distinct H1 or angle.

Recommendation

Two passes. This week, in Yoast on each page under Advanced, set "Allow search engines to show this page in search results" to No for /thank-you/, /cal-submit/, /cal-list/, /post-generator-installation-survey/ and /kohler-contact-us/. Yoast drops noindexed pages from page-sitemap.xml on its own. Do the same in the same sitting for /test/, /test-2/, /test1/, /gen-test/, /tabs-responsive/ and /other-products-original/ if they are not trashed under the junk-pages finding. Leave /service-request/ indexable, it owns a distinct service-and-repair intent. Do not touch robots.txt, a Disallow would hide the noindex from Googlebot.

Same day, fix the Ads trigger in GTM-N7XTZCZ. Add a Custom HTML tag on All Pages that listens for the wpcf7mailsent DOM event and pushes a cf7_submit event with e.detail.contactFormId to the dataLayer. Create a Custom Event trigger on cf7_submit and move the three thank-you Ads tags onto it, keeping the two landing-page variants distinct by filtering on the form id instead of the referrer. In the Redirection for Contact Form 7 plugin set a 1 second delay on the redirect action so the beacon leaves before navigation. Leave the phone-click and ServiceTitan ws-widget=open tags as they are. Turn on enhanced conversions for leads on the awct tags now that the submit event has the email field in hand. GA4 runs through Site Kit as G-BVPGS29PEC outside GTM, so have the same Custom HTML tag also call gtag event cf7_submit, then mark cf7_submit as a key event in GA4 Admin. Do not rely on Enhanced Measurement form_submit, it counts failed CF7 validations.

Second pass, after the Ads owner confirms final URLs. Do not 301 /home-generator-assessment/ or /california-utilities-shuts-off-power/ this week, they are live Ads landing pages and the container keys on them. Keep /home-generator-assessment/ as the one indexable assessment page, it has the most content and is already the paid destination. Give it a real H1 in place of "More Than 200 Positive Reviews" and a unique meta description. Check GSC clicks and Ads final URLs for /install-assessment/, /schedule-consultation/ and /power-outage-preparation-free-assessment/, then 301 them to /home-generator-assessment/ with the Redirection plugin and update any Ads final URLs in the same change. Point every "Free Assessment" link and the header button at that one URL.

Pages. /thank-you/, /cal-submit/, /cal-list/, /post-generator-installation-survey/, /service-request/, /schedule-consultation/, /install-assessment/, /home-generator-assessment/, /power-outage-preparation-free-assessment/, /kohler-contact-us/

F32 Medium CategoryOn-page EffortSmall Quick winYes

Sitewide title template pushes 149 of 163 titles past 70 characters and stamps 'Archives' on 44 pages with brand-pair duplicates

Evidence

crawl-summary.txt title lengths: 30-60 chars 4, 61-70 10, over 70 149. crawl.json recount: 71-100 chars 86 pages, 101-140 53, over 140 10. 145 titles contain 'Backup Generators & Installation in So. Cal.', 44 contain 'Archives', for example /generator-cat/generac/ 79 chars, /tag/emergency-backup-generator-fuel-options/ 111. Duplicate titles on /generator-cat/generac/ and /generator-cat/generac-commercial/, same for Kohler, Cummins, Champion, Briggs & Stratton. The Briggs & Stratton and Champion pairs also return identical word counts, 576/576 and 533/533. 21 of 24 post-sitemap URLs exceed 70 chars, the 3 under are /blog/ 67, /san-andreas-fault-line/ 56, /back-up-generator-may-not-be-available/ 56, for example /california-utilities-shuts-off-power/ 158. 31 titles end in the misspelled site tagline 'Residential & Commerical Backup Generators & Installation.' Live curl Sep 9, 2026 of /generator-cat/generac/ returns <title>Generac Archives - Backup Generators & Installation in So. Cal. - LT Generators</title>.

Impact

Every product, category, tag and blog title is truncated in results or rewritten by Google, and reads as a WordPress default. The five brand category pairs are how a Los Angeles installer gets found for Generac or Kohler queries, and they share identical titles, so Google may fold one into the other. A misspelled site title is printed in 31 search-result titles. Title length itself is not a ranking factor.

Recommendation

Fix the source strings first, then the templates, then the ten brand terms by hand. Half a day in Yoast, no code.

1. Settings > General: set Site Title to exactly "LT Generators". The crawl shows 31 titles ending in "LT Generators Residential & Commerical Backup Generators & Installation." with the typo Commerical. That string is the WordPress site title and tagline, not a Yoast template, and it is showing in Google results today.

2. Yoast SEO > Settings > Site basics: separator = |. Then Content types > Pages, Posts, and Generators: SEO title = "%%title%% %%sep%% %%sitename%%". Delete %%sitedesc%% from every template so "Backup Generators & Installation in So. Cal." stops appearing in 145 titles.

3. Yoast > Settings > Categories & tags > Generator categories: SEO title = "%%term_title%% Standby Generators Los Angeles %%sep%% %%sitename%%". Keep the geo modifier, these are the pages that should rank for brand plus Los Angeles queries. Remove "Archives" from the Categories, Tags, Formats and Author and Date archive templates on the same screen.

4. Yoast > Settings > Categories & tags > Tags: turn "Show tags in search results" off. 28 tags for 23 posts, median 510 words, all indexed. Noindex them, do not write titles for them. Do the same for Format archives.

5. The five brand pairs: do not rename or re-slug the -commercial terms, that changes URLs. Edit each -commercial term under Generators > Generator categories and set the Yoast SEO title by hand: "Generac Commercial Generators Los Angeles | LT Generators", same pattern for Kohler, Cummins, Champion, Briggs & Stratton. Write a distinct meta description for each. Before doing that, check whether the pair actually lists different products. If the commercial term shows the same items as the residential one, set its canonical to the residential URL in the Yoast Advanced tab instead of giving it a second title.

6. Blog posts: step 1 and 2 alone drop 21 of 24 under 70 characters. Hand-write SEO titles only for posts with clicks in Search Console over the last 3 months, aim under 60 characters, lead with the topic.

7. Leave the homepage alone, "Standby Generators Installation Los Angeles | LT Generators" at 59 characters is already right. Recrawl after the change and spot-check five brand pages with Search Console URL inspection. Google truncates by pixel width around 600px and rewrites boilerplate-heavy titles, so judge by uniqueness and lead words, not a character count.

Pages. 149 URLs over 70 chars, all 13 /generator-cat/, 28 /tag/, 2 /category/ pages, 24 blog posts

F33 Medium CategoryOn-page EffortMedium Quick winYes

One meta description with a 'Belair' typo is shared by 31 pages, 29 pages including all generator-cat and project-install pages have none, 22 run over 160 characters

Evidence

crawl.json: 31 pages share the exact meta 'Top Backup Generators Installation in So. Cal. Get a Free Residential Generator Assessment in Beverly Hills, Malibu, Belair, Brentwood, & Los Angeles.' including all four /generator-installation-*/ pages, bel-air, beverly-hills, los-angeles, malibu, /kohler/, /kohler-generators/, /privacy-policy/, /thank-you/, /test/, /cal-submit/. A 32nd page, /faqs/, carries a variant with the same 'Belair' spelling. crawl-summary.txt line 62 and crawl.json: meta missing on 29 pages: /generator/, all 13 /generator-cat/ terms, /generator-tag/project-installs/, /tag/training/, 3 /author/ pages, and 10 project-install posts such as /generator/generac-residential-22kw-malibu-ca/ and /generator/generac-guardian-22kw-santa-monica-ca/, a live curl of /generator-cat/project-installs/ on Sep 9, 2026 lists exactly those 10 posts, so every project-install post lacks a meta. 22 pages exceed 160 chars, the longest /generator/generac-diesel-generator-500-600kw/ at 173. 11 of the 22 are /tag/ or /category/ archives.

Impact

Four city pages promising different cities all describe Beverly Hills in the snippet, suppressing click-through on the exact local queries they exist for, and 'Belair' misspells the neighborhood one of them targets. Missing metas let Google pick body text, which it does adequately for archives. Meta descriptions carry no ranking weight, so this is a click-through and sloppiness issue on the money pages, not an indexing one.

Recommendation

Fix the source, not 31 pages. The shared "Top Backup Generators Installation in So. Cal... Belair..." string sits on pages with unrelated titles including /test/ and /cal-submit/, so it is the site-wide default in Yoast SEO > Settings > Content types > Pages > Meta description. Replace that static text with %%excerpt%% or leave it blank. One save removes the boilerplate and the typo from all 31 pages, and Google writes a better fallback from body text than a Beverly Hills sentence ever will. Purge the Cloudflare cache afterward or the old head will keep serving.

Then hand-write the four money pages in the Yoast box on each /generator-installation-*/ page, 120 to 155 characters, opening with the city name spelled "Bel Air", plus "Generac Premier PowerPro dealer since 1982" and (818) 787-1470, all three confirmed on the homepage. /faqs/ has its own custom meta with "Belair", fix that one by hand too. That is the entire typo footprint, 32 pages.

For the ten project-install posts and every future product page, set a template once in Yoast > Settings > Content types > Generators > Meta description, for example "%%title%% installed by LT Generators, Los Angeles' Generac Premier PowerPro dealer since 1982. Free site assessment: (818) 787-1470." Existing custom metas on product pages keep overriding it. For the 13 generator-cat archives use Yoast > Settings > Categories & tags > Generator categories > Meta description with a %%term_title%% template rather than writing 13 term descriptions, and hold this step until the archive finding decides whether the duplicate -commercial terms stay indexed at all.

Skip three things. Do not write descriptions for /test/, /test1/, /test-2/, /gen-test/, /tabs-responsive/, /cal-submit/, /cal-list/, /other-products-original/, /thank-you/, /our-residential-generators-staging/, or the five identical 3068-word pages. Those belong to the noindex-or-delete cleanup. Do not write descriptions for author archives, /tag/training/ or /generator-tag/. Turn those archive types off in Yoast instead, under Settings > Advanced > Author archives, and Categories & tags > Tags > Show in search results. Do not run a separate pass on the 22 descriptions over 160 characters. Google publishes no length limit and truncates by pixel width, so 161 to 173 characters loses a few trailing words at most. Trim only when a page is already open for another edit.

Verify by viewing source on a city page and on /test/ to confirm the Belair string is gone, then request indexing on the four city pages in Search Console.

Pages. 31 pages with the shared meta, 29 with none, 22 over 160 chars, lists in crawl-summary.txt

F34 Medium CategoryOn-page EffortSmall Quick winYes

Homepage H1 is hard-coded ALL CAPS, omits location and installation, and heading levels jump H1 to H3 to H6 with design fragments as headings

Evidence

homepage.html: <h1 class="elementor-heading-title elementor-size-default">RESIDENTIAL AND COMMERCIAL BACKUP &amp. STANDBY GENERATORS</h1>, uppercase in source, live curl of https://ltgenerators.com/ on Sep 9, 2026 returns the same H1 and title. Title tag: 'Standby Generators Installation Los Angeles | LT Generators', crawl-summary.txt: title_len 59, h1_count 1, 1122 words. Full sequence of 30 headings: H1, H3 'Your Neighborhood Emergency Generator Solution', H3 'Protecting Your Home or Business From Power Failures', H2 'Get Peace of Mind Residential & Commercial Backup & Standby Generators', H2 'GET YOUR', H2 'Free On-Site Residential Installation Assessment', H2 'SERVICE & INSTALLATION AREAS', H6 x5, Los Angeles, Ventura, Santa Barbara, Orange County, San Diego, H2 'RECENT Generator InstallationS', H5 'More Than 200 positive client Reviews', H2 'BLOG NEWS', H3 x4 all empty, H2 'Reaching Out With a Helping Hand to Natural Disaster Victims', H2 'Careers', H3 'Join Our Team!', H5 'Plumbers, Electricians, Contractors...', H6 'CONTACT US to APPLY', footer H4s, About Us, Recent Posts, Newsletter, duplicated twice. lighthouse-summary.json heading_order, mobile and desktop, fails with four flagged nodes: h3, h6, h5, h5, audit title 'Heading elements are not in a sequentially-descending order', score 0. Body text counts, scripts/styles stripped: 'los angeles' 5, 'generator installation' 4, 'generac dealer' 0, 'whole house' 0.

Impact

The H1 carries no target query or place name, so the strongest on-page slot after the title is spent on a generic phrase that also duplicates a nearby H2. 'GET YOUR' reads as a heading to Google and screen readers. The title tag already carries the target query, so the ranking effect is small. The empty blog H3s and doubled footer are sloppy markup a reviewer or screen-reader user would notice.

Recommendation

Fix it in Elementor in one pass, then purge cache.

1. Hero H1. Open the hero Heading widget, Content tab. Title: "Standby Generator Installation in Los Angeles". HTML Tag: H1. If the design wants caps, Style tab, Typography, Transform: Uppercase. Move "Residential and Commercial Backup and Standby Generators" into a Text Editor widget under it as the subhead. Rewrite the nearby H2 "Get Peace of Mind Residential & Commercial Backup & Standby Generators" so it stops duplicating the hero, for example "Whole-house and commercial backup power, installed and serviced locally". Only add "authorized Generac dealer" to the subhead in the form the brief confirms, Generac Premier PowerPro Dealer.

2. "GET YOUR" fragment. Delete that Heading widget and make the next one read "Get Your Free On-Site Installation Assessment" as a single H2. Use an inline span in the title field for the styled emphasis.

3. The two H3s under the hero: set HTML Tag to p. They are taglines, not section heads.

4. The five city tiles: HTML Tag p, or a plain list. Leave the H2 "Service and Installation Areas" above them. Use H3 only if each tile heads real content.

5. H5 "More Than 200 positive client Reviews" and the Careers H5 and H6 "Contact Us to Apply": HTML Tag p.

6. Blog section. The four H3s are empty. In the Posts or Loop Grid widget, Content, turn the title on and set Title HTML Tag to H3 so the heading carries the post title.

7. Footer. Elementor, Templates, Theme Builder, Footer. Set each widget-title Heading to p or div. If the titles come from WordPress sidebar widgets, filter before_title and after_title in the child theme functions.php or the Code Snippets plugin. The footer set renders twice, which means two footer templates or a desktop and mobile copy are both live. Keep one and fix the display conditions or responsive visibility on the other.

8. Publish, purge the WP cache plugin, then Cloudflare, Caching, Purge Everything. Re-run Lighthouse Accessibility to confirm heading-order passes.

Pages. https://ltgenerators.com/

F35 Medium CategoryOn-page EffortMedium Quick winYes

19 pages have no H1 and 6 have multiple H1s, including About, Contact, Commercial, Financing and two Kohler pages with a typo

Evidence

crawl-summary.txt line 65: zero-H1 pages 19, multi-H1 pages 6. Zero list, line 66, includes /about-us/, /contact-us/, /careers/, /videos/, /power-outages/, /privacy-policy/, /post-generator-installation-survey/ plus /cal-submit/, /cal-list/, /diagnostics/, /emergencies/, /gen-test/, /maintenance/, /monitoring/, /service/, /terms-conditions/, /kohler-contact-us/, /our-residential-generators-staging/, /thank-you/. Live fetch Sep 9, 2026: /about-us/ first heading H2 'About Us | Our Vision', /contact-us/ H2 'Full-Service Generator CompanyContact Info' run together, /careers/ H2 'Generator Careers - Join Our Team!', /videos/ H2 'How Installing a Generator Works', /power-outages/ H2 'Power Outages Los Angeles & Surrounding Areas', /privacy-policy/ H3 'Privacy Policy'. Multi-H1, line 67 and live: /our-commercial-generators/ 2 H1s 'Commercial & Industrial Generators' and 'Protect Your Business With Commercial & Industrial Generators', /financing/ 2, 'Residential Financing', 'Commercial Financing', /kohler/ and /kohler-generators/ 3 each including 'Dependable Power Suppy', 'Why kohler?', 'What Our Client Says'. The other two multi-H1 pages are /california-utilities-shuts-off-power/ and /install-assessment/ with 2 each. Every H1 on the site is an Elementor Heading widget, confirmed by the elementor-heading-title class on the live pages.

Impact

A missing H1 is not a penalty and multiple H1s are fine for Google, so the ranking upside is small. 12 of the 19 zero-H1 URLs are test, staging, form-endpoint and thank-you pages that should be noindexed or removed, leaving 7 real pages of which About and Contact carry customer weight. What keeps this at medium is the live 'Dependable Power Suppy' typo in the H1 of two Kohler brand-partner pages.

Recommendation

Fix today, before anything else in this finding: on /kohler/ and /kohler-generators/ open the hero Heading widget in Elementor, change the text to "Dependable Power Supply", and leave its HTML Tag at H1. Set the "Why kohler?" and "What Our Client Says" widgets to H2 and capitalize Kohler. Better still, /kohler/ is a 225-word duplicate of /kohler-generators/, so 301 it to /kohler-generators/ with the Redirection plugin and fix only one page.

Same week, one-H1 pass on the four real multi-H1 pages. /our-commercial-generators/: keep "Commercial & Industrial Generators" as H1, demote "Protect Your Business With Commercial & Industrial Generators" to H2. /financing/: add one H1 above the two sections reading "Generator Financing for Homes and Businesses" and set "Residential Financing" and "Commercial Financing" to H2. /california-utilities-shuts-off-power/ is a blog post, demote its second H1 to H2. /install-assessment/ belongs to the junk-URL cleanup, not this fix.

Same week, add an H1 to the seven real zero-H1 pages: /about-us/, /contact-us/, /careers/, /videos/, /power-outages/, /privacy-policy/, /terms-conditions/. In each page's first Heading widget, Content tab, change HTML Tag from H2 to H1. Keep the copy plain and descriptive: About "About LT Generators: Family-Owned Generator Installers in Los Angeles", Contact "Contact LT Generators for a Free Generator Assessment", Careers "Careers at LT Generators", Videos "Generator Videos", Power Outages "Power Outages in Los Angeles", Privacy and Terms use the page name. Do not turn Neve's page title back on in Elementor Page Settings, it would print a second visible title above the hero. Because Neve sets a global h1 font size, lock the widget's Style > Typography to the size it has now so the hero does not jump when the tag changes.

Do not write H1s for the other 12 zero-H1 URLs in crawl-summary.txt: /cal-submit/, /cal-list/, /diagnostics/, /emergencies/, /gen-test/, /maintenance/, /monitoring/, /service/, /kohler-contact-us/, /post-generator-installation-survey/, /our-residential-generators-staging/, /thank-you/. They are test, staging, form-endpoint and thank-you pages. Noindex or delete them under the sitemap-junk finding, which fixes 12 of the 19 for free.

After publishing, Elementor > Tools > Regenerate CSS & Data, then Cloudflare > Caching > Purge Everything. Verify with a Screaming Frog crawl, free up to 500 URLs, filtered on H1 Missing and H1 Multiple, or rerun the crawl script and confirm zero-H1 drops to 0 on indexable pages.

Pages. /about-us/, /contact-us/, /careers/, /videos/, /power-outages/, /privacy-policy/, /our-commercial-generators/, /financing/, /kohler/, /kohler-generators/ and the other 13 zero-H1 URLs in crawl-summary.txt

F36 Medium CategoryOn-page EffortMedium Quick winYes

Residential and commercial product grids link the identical 34 products, with H4 then H2 heading pairs and a duplicated grid in the DOM

Evidence

Live fetch Sep 9, 2026, curl, both pages 200: unique /generator/ link targets on /our-residential-generators/ 34, on /our-commercial-generators/ 34, sets identical. Residential links to /generator/generac-diesel-generator-750-800kw/ and /generator/kohler-diesel-generators-400-699kw/. Commercial H2 list includes 'Kohler Standby Generator 8-20KW', 'Kohler Colors', 'Champion Generator' and no diesel or industrial unit, the 15 diesel/industrial targets are reached only through image links, never a heading. Each go_portfolio card renders H4 then H2 with the same linked name, for example 'Generac Guardian Series'. That name appears as a heading element 4 times on residential, 2 H4 + 2 H2, and 2 times on commercial, 1 H4 + 1 H2. Residential renders the whole grid twice: 38 gw-gopf-post-col cards vs 19 on commercial, both copies inside section 7ac8697 with no elementor-hidden class, so the duplicate is a second go_portfolio instance, not a breakpoint-hidden section. The breakpoint-hidden pairs, elementor-hidden-mobile x2 on sections a7ad07f and 4b204eb, elementor-hidden-desktop/tablet x2 on 78cacd7 and e83dc0e, are the how-it-works info-tab sections on residential, which is why H3 '2. Your Generator Senses A Problem' and '4. Your Electricity Is Restored' each appear twice. Commercial has zero hidden-breakpoint classes and a single grid, but 2 H1s. crawl-summary.txt: residential 141 images, 49 missing alt, 91 internal links, 1 H1. Commercial 97 images, 23 missing alt, 91 internal links, 2 H1.

Impact

The two pages send the same catalog signals and the commercial page reads as a residential catalog, weakening it for 'commercial generator Los Angeles', a real revenue line. The duplicated grid and breakpoint-hidden sections double headings, links and images on the residential page only. DOM size is a Lighthouse diagnostic rather than a Core Web Vital, and heading levels are hygiene.

Recommendation

Step 1, residential page, about an hour. In Elementor, open /our-residential-generators/ and delete the second go_portfolio instance inside section 7ac8697 so the grid renders once, then delete the breakpoint-hidden copies of the how-it-works sections: a7ad07f and 4b204eb carry elementor-hidden-mobile, 78cacd7 and e83dc0e carry elementor-hidden-desktop and elementor-hidden-tablet. Keep one of each pair and set columns per device with the section's responsive column controls. This removes the duplicate H4/H2 pairs, the repeated "2. Your Generator Senses A Problem" and "4. Your Electricity Is Restored" H3s, and roughly 40 images and links from the DOM.

Step 2, split the catalog, about two hours. The generator-cat taxonomy already has paired terms, for example /generator-cat/generac/ and /generator-cat/generac-commercial/, same for Kohler, Cummins, Briggs & Stratton, Champion. In WP admin go to Go Portfolio, duplicate the existing portfolio, set its item selection to the five -commercial terms, and swap its shortcode into the commercial page. Set the original instance to the five residential terms. Then check that each -commercial term actually contains the diesel and industrial products, and reassign any that sit only in the residential term.

Step 3, headings, thirty minutes while the widget is open. Go Portfolio prints each title twice, an H4 on the thumbnail overlay and an H2 in the caption. Turn off the overlay title in the portfolio's style settings and set the caption title tag to H3. If this plugin version has no tag option, override the item template in the child theme. Treat this as hygiene, not ranking work.

Step 4, commercial copy, one to two hours. This is the only step that earns rank. Above the grid on /our-commercial-generators/, add short H2 blocks that link to the pages the business already has: Generac Diesel 500-800kW, Generac Industrial Gaseous 100-750kW, Kohler Diesel 101-699kW, Cummins Diesel 150-750kW. Two or three sentences each on typical commercial applications in Los Angeles. While in the page, collapse the two H1s the crawl shows into one, "Commercial & Industrial Generators Los Angeles," and demote the other to a paragraph.

Skip adding a second grid plugin. Essential Addons is loaded on these pages but not used for the grid, and a second grid plugin is churn.

Pages. /our-residential-generators/, /our-commercial-generators/

F37 Medium CategoryContent EffortMedium Quick winNo

Blog is news-reactive and stale, newest original post June 2024, homepage promotes a 2020 post as news, nothing on the 2025 to 2026 fire cycle

Evidence

Live post-sitemap.xml Sep 9, 2026, 24 URLs including /blog/ itself. lastmod by year: 2018 x3, 2019 x8, 2020 x3, 2022 x3, 2023 x1 on 2023-08-22, 2024-04-06, 2024-06-11, and four entries stamped 2026-08-07, which are /blog/ plus three 2018 posts whose Article datePublished is 2018-08-15, 2018-08-15 and 2018-09-03, so touched, not new. Newest original post is /we-mean-to-shake-up-your-awareness/, datePublished 2024-06-11. The homepage BLOG NEWS grid in homepage.html shows four posts, Article datePublished on each live page: 2024-06-11, 2024-04-06, 2023-08-20, 2020-05-08. The fifth post, A Back-Up Generator May Not Be Available, datePublished 2020-03-23, sits in the footer Recent Posts widget, not in BLOG NEWS. Live /blog/ Sep 9, 2026 lists 12 posts and links to /blog/page/2/, HTTP 200 with its own canonical, which lists the other 11, so all 23 posts are reachable and pagination works. Live grep of /blog/: Palisades 0, Eaton 0, 2025 0, PSPS 0, wildfire 11 case-insensitive, all from the 2019 post An Emergency Standby Generator Can Save You From Wildfire Season and its tag class. Dated news titles per crawl.json: PG&E Issues A Mea Culpa!, Southern California Incoming Hurricane Hilary...What?, CALIFORNIA UTILITIES SHUT OFF POWER IN 'EXTREMELY' WINDY WEATHER. Sep 9, 2026 US search snapshot, re-run once, for generator installation Pacific Palisades rebuild: allamericangenerator.com/pacific-palisades-los-angeles/ appears, live HTTP 200, title Generator Services in Pacific Palisades | Installation & Repair, and no ltgenerators.com URL appears in the returned results.

Impact

A 2020 article labelled news on the homepage is a visible trust wobble for every visitor. Age alone is not a demotion, so fifteen months without a post is theoretical for a generator installer. The Palisades and Altadena rebuilds are the biggest concentrated demand for standby power in the service area, a competitor already ranks a dedicated page, and LT has no content on them. That gap is an opportunity cost, not a defect.

Recommendation

Keep the blog but stop running it as a news feed. Same-day items for the WP dev: open the homepage in Elementor, edit the Posts widget under BLOG NEWS, rename the heading to Guides for LA Homeowners, switch Query Source to Manual Selection and pin the three most evergreen posts, keep the visible dates. Pagination on /blog/ already works, so no change there. Content items, owner plus dev, two to three weeks: build two service-area money pages rather than blog posts, at /generator-installation-pacific-palisades/ and /generator-installation-altadena/. Make them genuinely different pages so they clear Google's doorway-page rule: LADBS rebuild permitting and LADWP service for Palisades, LA County permitting and SCE for Altadena, natural gas versus propane while gas mains are still being restored, placement setbacks on the new footprint, how the install slots into the GC's schedule, local job photos. Link both from the homepage service-area block and the main nav, add them to areaServed in the LocalBusiness schema. Consolidate the 2018 to 2020 PSPS, shutoff and PG&E posts into one evergreen guide, Public Safety Power Shutoffs in Los Angeles: SCE and LADWP, and 301 the old URLs to it with the Redirection plugin, or fold them into the rebuilt /power-outages/ page per the PSPS finding. Fold the Hurricane Hilary post into a storm-season page or 301 it as well. Refresh the earthquake post with the seismic gas shutoff valve content and let the modified date update. After that, one new or refreshed guide per quarter timed to August fire season and January storms, not one per month. Do not bulk-save posts to bump lastmod. Google only trusts sitemap lastmod when it reflects a real content change, and the three posts touched 2026-08-07 already look like an empty bump.

Pages. /blog/, homepage BLOG NEWS section, the 24 posts in post-sitemap.xml

F38 Medium CategoryStructured data EffortMedium Quick winYes

Product schema on all 33 generator pages is ineligible for rich results, and the one priced page states a price and image the page does not show

Evidence

crawl.json lists 45 /generator/ URLs, 33 of which carry Product in schema_types, the other 12 are Service, CollectionPage or the archive. Live re-sweep Sep 9, 2026 of those 33: each page emits exactly one Product node with one Offer. 32 Offers have no numeric price: 30 omit price entirely and 2, /generator/kohler-standby-generator-8-10kw/ and /generator/briggs-stratton-air-cooled/, put the literal string 'Call for pricing' in offers.price. All 32 carry priceSpecification {valueAddedTaxIncluded:false, description:'Call for pricing'} with priceCurrency USD. All 33 declare availability https://schema.org/InStock. 0 of 33 carry review or aggregateRating. The one numeric-priced page, /generator/generac-guardian-series/, declares name 'Generac Guardian 22kW Standby Generator', price '6249.00' USD, brand Generac, image https://ltgenerators.com/images/generac-22kw.jpg, curl returns 404. Its visible text, scripts and styles stripped, contains no '$' and no '6249', and mentions 9, 11, 16, 20 and 22 kW. That JSON-LD sits inside an Elementor widget with data-id="5e657f6" data-widget_type="html.default". The recommended replacement image /wp-content/uploads/2018/08/GuardianGenerator-Res.png returns 200.

Impact

Google's product snippet rules require a numeric price plus priceCurrency, so none of the 33 pages can earn a price or availability rich result, and LT does not sell online so none was reachable before the bug either. A text string in the price field is a validation error. A price and image in markup the page does not show violates the structured data content guidelines and is the pattern named in the spammy structured markup manual action, which strips rich results but does not touch rankings.

Recommendation

Default path, since LT does not publish prices: on all 33 /generator/ pages open the Elementor HTML widget that holds the hand-pasted Product JSON-LD, on the Guardian page it is data-id 5e657f6, and delete the whole offers object, including availability InStock and the priceSpecification carrying 'Call for pricing'. That also clears the two pages with the text string in offers.price. Keep Product with name, description, brand and one absolute image URL under https://ltgenerators.com/wp-content/uploads/ that the page actually displays. Add sku or model number only where the page shows it. On the Guardian page set name to 'Generac Guardian Series' to match the H1, point image at https://ltgenerators.com/wp-content/uploads/2018/08/GuardianGenerator-Res.png, and remove the 6249.00 price. Leave the Yoast SEO 28.3 graph alone, it is a separate script block and is valid. Deleting the whole widget is an equally acceptable fix, since Product with no offers and no reviews earns no rich result anyway and the Yoast WebPage graph still describes the page. After the edits purge the Cloudflare cache and any WordPress page cache, spot-check three URLs in the Rich Results Test, then in Search Console under Enhancements > Product snippets click Validate Fix. Do not add self-written aggregateRating to regain eligibility, Google's review snippet policy only allows reviews of the product that are shown on the page. If LT later decides to publish prices, use numeric offers.price with priceCurrency 'USD' and print the same figure in visible copy, or AggregateOffer lowPrice and highPrice on a series page.

Pages. 33 URLs under /generator/ listed in crawl.json with Product

F39 Medium CategoryLocal EffortMedium Quick winYes

Entity profile URLs and citations are inconsistent: sameAs disagrees across nodes and footer, two Yelp and Facebook slugs, HomeAdvisor under a legacy name, no GBP link anywhere

Evidence

homepage.html Yoast Organization.sameAs, slash-escaped JSON, so raw grep undercounts: facebook.com/ltgenerators, x.com/ltgenerators, linkedin.com/company/ltgenerators/, youtube.com/channel/UCa-mSQ8KpzRDHQap2k6aZvg. Electrician.sameAs: facebook.com/ltgenerators, yelp.com/biz/lt-generators. HTML body: facebook.com/LTGenerators/ x4, header social list, Elementor social-icons widget, footer x2, and facebook.com/ltgenerators x1, reviews badge. Twitter.com/ltgenerators x4 vs x.com in schema. Linkedin.com/company/ltgenerators x4, no trailing slash, schema has one. Badge row links yelp.com/biz/lt-generators-van-nuys x1 and homeadvisor.com/rated.LTElectricandLighting.14911913.html x1. 'google.com/maps' 0, 'g.page' 0, 'maps.google' 0, 'goo.gl/maps' 0 on the homepage. Live curl Sep 9, 2026: x.com 200, linkedin.com/company/ltgenerators/ 200, youtube.com/channel/UCa-mSQ8KpzRDHQap2k6aZvg 200, youtube.com/@ltgenerators 404, facebook.com/ltgenerators and /LTGenerators/ both 400, both Yelp slugs 403, HomeAdvisor 403, the 400 and 403 responses are bot blocks against curl, not broken links. Sep 9, 2026 US web-search snapshot: bbb.org path /van-nuys/profile/electrician/lt-generators-1216-100064638, chamberofcommerce.com path /van-nuys/general-contractor/2005753391-lt-generators, homeyou.com title 'LT Generators - Van Nuys Electricians', plumbersworldwide.com title 'LT Generators in Los Angeles, CA - Plumbing Portal USA', Yelp title 'LT GENERATORS ... 14932 Delano St, Van Nuys ... Generator Installation/Repair' at slug lt-generators-van-nuys. The address 14932 Delano St, Van Nuys, CA 91411 used in the recommendation comes from those listings and the live /contact-us/ page, not from BRIEF.md, BRIEF.md has only the phone.

Impact

sameAs is how Google reconciles the entity across the web, though it is not a documented ranking input and Facebook paths are case-insensitive. The items with real value are a possible duplicate Yelp listing, a HomeAdvisor profile under 'LT Electric and Lighting' that may split reviews, and the absence of any Business Profile link. What earns medium is the pasted Electrician node beneath it: placeholder phone and address sharing Yoast's #organization @id, beside a Yoast Organization named LTGenerator.

Recommendation

Do this in order this week. 1. Edit the homepage in Elementor, find HTML widget bf1d78b and delete it. 2. Yoast SEO > Settings > Site representation: set the Organization name to LT Generators and enter one set of Other profiles: facebook.com/LTGenerators, x.com/ltgenerators, linkedin.com/company/ltgenerators/, youtube.com/channel/UCa-mSQ8KpzRDHQap2k6aZvg, plus whichever Yelp URL Yelp itself canonicalizes to. 3. If you want address and phone in the graph, add the Yoast Local SEO add-on and enter 14932 Delano St, Van Nuys, CA 91411, 818-787-1470, business type Electrician, or have the dev add the same fields through the wpseo_schema_organization filter in a small mu-plugin with hasMap set to the Google Maps place URL in the form google.com/maps?cid=<CID>. Do not use a g.page short link, Google retired short names in 2021. 4. In the Elementor footer template and badge row, point every Facebook badge at facebook.com/LTGenerators, swap twitter.com for x.com, and use the one Yelp slug. 5. Open both Yelp URLs in a browser. If lt-generators redirects to lt-generators-van-nuys, that is the canonical one. If both are separate live listings, file a Yelp duplicate merge. 6. Search Google Maps for "LT Electric and Lighting" Van Nuys. If a legacy listing exists, claim it and mark it a duplicate of the LT Generators profile so the reviews merge. 7. Email Angi/HomeAdvisor support to rename the profile to LT Generators. Skip a paid citation sweep and leave the BBB, chamberofcommerce and homeyou categories alone for now. They do not move the local pack.

Pages. All 163 URLs via the Organization node, homepage footer and badge row, off-site citations

F40 Medium CategoryTechnical EffortMedium Quick winYes

Hand-pasted JSON-LD in three Elementor HTML widgets is the root cause of the placeholder, hidden-FAQ and price-mismatch errors

Evidence

Electrician block in homepage widget data-id="bf1d78b", homepage.html, byte offset 75520, telephone "+1-800-XXX-XXXX", streetAddress "Your Street Address", FAQPage in /faqs/ widget "bb6716f", Product in /generator/generac-guardian-series/ widget "5e657f6", each data-widget_type="html.default", live curl Sep 9, 2026, both 200. Every one of the 33 Product pages in crawl.json carries exactly one html.default widget. The Guardian block is the only one of the 33 whose Offer uses a bare "price": "6249.00" with no priceSpecification and the only one pointing at /images/generac-22kw.jpg, live HEAD returns 404. The other 32 all carry priceSpecification, 29 plain, 1 adds mpn, 2 add a "price" field. Exactly two pages carry "price": "Call for pricing" inside JSON-LD: /generator/kohler-standby-generator-8-10kw/ and /generator/briggs-stratton-air-cooled/. Yoast SEO v28.3 emits the separate site-wide graph on each page, so the hand-pasted widgets are a second, unvalidated schema source.

Impact

No single source of truth, no validation on save, and drift on every edit. This is how '+1-800-XXX-XXXX' and a 404 image reached production, and the pasted Electrician node reuses Yoast's #organization @id so the trusted Yoast graph has a contradictory twin on the money page. The rich results at stake are out of reach regardless: FAQ rich results are gone for this class of site and Product snippets need a price LT does not publish.

Recommendation

Delete the three hand-pasted HTML widgets this week and stop writing JSON-LD by hand on this site. Yoast SEO 28.3 is installed and already emits a validated Organization graph on every page. Remove widget bf1d78b from the homepage, bb6716f from /faqs/ and 5e657f6 from /generator/generac-guardian-series/.

Then enter the real identity once in Yoast SEO > Settings > Site representation with type Organization: business name, an uploaded logo that actually resolves, the phone number and email fields, and the Facebook and Yelp URLs under social profiles. That covers everything the Electrician block was trying to say. If the owner wants address, hours and the Electrician business type in schema, buy the Yoast Local SEO add-on and enter the location there, or apply the corrected node printed in the Structured data section through the wpseo_schema_organization filter. Do not hand-write a LocalBusiness node again.

Do not replace the FAQPage block. Google limited FAQ rich results to government and health sites in August 2023 and has since retired them, so the markup earns nothing for an installer, and a Q&A node that is not visible on the page is a guideline violation. Keep the visible FAQ copy for users and AI Overviews. If the team wants FAQ markup anyway, turn on the FAQ Schema switch in Elementor Pro's Nested Accordion widget so it is generated only from the visible items and cannot drift.

For the 33 generator posts, "Call for pricing" is invalid because Offer.price must be numeric, and a Product node without a real price or reviews is not eligible for any rich result. The safest move is to drop Product markup on the generator post type. If the owner insists on keeping it, put a single HTML widget in the Elementor Theme Builder single template for the generator post type, fill name, brand, model and image with Dynamic Tags from the post, and omit the Offer node, so all 33 pages share one code path. Skip a custom mu-plugin route. It is more custom code than this site should have to maintain.

After the edits run Elementor > Tools > Regenerate Files & Data, then Cloudflare > Caching > Purge Everything, then run the homepage, /faqs/ and one generator URL through the Rich Results Test and confirm a single Organization node with the real phone. Add that test to the publish checklist and check Search Console > Enhancements a week later.

Pages. /, /faqs/, 33 /generator/ pages

F41 Medium CategoryLocal EffortSmall Quick winYes

Two phone numbers circulate: (818) 787-1470 on the site and most citations, (818) 527-4742 on the Kohler dealer microsite, and that microsite outranks the site for the brand plus city query

Evidence

Site: tel:8187871470 once and (818) 787-1470 twice in homepage.html. crawl-summary.txt: "PAGES WITH tel: LINK: 161/163". Homepage JSON-LD Electrician node telephone is the placeholder +1-800-XXX-XXXX, domain-tech.txt line 84. Live Sep 9, 2026: https://ltgenerators.kohlergeneratordealer.com/, title "LT Generators in Van Nuys, CA", shows (818) 527-4742 four times with tel:(818) 527-4742 links. /about-us shows it three times. Its website attribution field is empty and the page has no link to ltgenerators.com. gendealers.com/dealer/lt-generators-van-nuys-ca does NOT show 527-4742: the live page shows 818-787-1470 three times and tel:+18187871470, and that site is an independent directory, not Generac's locator. ZoomInfo returns 403 to fetches. An exact-phrase search for "LT Generators" "527-4742" returns zoominfo.com/c/lt-generators/355683263, which supports but does not directly verify that listing. BBB and Yahoo Local pages show (818) 787-1470. Chamberofcommerce.com's search title reads "LT Generators in Van Nuys, CA 91411 - 818-787...", page 403 to direct fetch. Yelp returned 403 and yasabe.com redirected to its homepage, so neither was verifiable. Brand query "LT Generators Van Nuys" on Sep 9, 2026: the Kohler microsite was result 2 in the first snapshot and result 3 in a re-run. Ltgenerators.com was absent from all 9 links in both. The Yoast Organization node carries no telephone, no address and four sameAs links.

Impact

Google anchors the local entity on the Business Profile phone, and the second number is confined to the Kohler-hosted microsite and ZoomInfo, so the NAP-split ranking argument is small. The real defect is that the site's own Organization node names the company 'LTGenerator' with no phone or address, which leaves Google nothing to tie the brand to Van Nuys and lets a Kohler-hosted duplicate own the brand plus city query.

Recommendation

This week, in order:

1. Call 818-527-4742 and note who answers. The Kohler microsite is run by Rehlko with Salesforce lead attribution on its quote forms, so this is almost certainly a Kohler-provisioned tracking line. If it rings LT and LT controls the number, add it to the Google Business Profile as Additional phone and keep 787-1470 as Primary. If Kohler owns the line, do not add it anywhere LT controls. If nobody answers, ask the Kohler dealer program rep to correct the microsite.

2. Fix the site's own schema, which is the real defect. In Yoast SEO > Settings > Site representation: set the name to "LT Generators", fill Phone with +1-818-787-1470 and the email under Additional organization info, and add Yelp, BBB, HomeAdvisor, Instagram, Nextdoor and the Business Profile URL under Other profiles. Then add a wpseo_schema_organization filter in a WPCode snippet or the child theme that sets @type to ["Organization","Electrician"], adds the PostalAddress for 14932 Delano St, Van Nuys, CA 91411, and areaServed for Los Angeles, Ventura, Santa Barbara and Orange counties. One telephone value only, matching the Business Profile. Never put 527-4742 in the schema or on the site. Change every tel: link from tel:8187871470 to tel:+18187871470 in the Elementor header and footer templates.

3. Ask Kohler, through the dealer portal or the Rehlko dealer program rep, to add https://ltgenerators.com as the dealer website on the microsite. Its website attribution field is empty today. Do not link to the microsite from ltgenerators.com.

4. Claim or correct the ZoomInfo profile to 787-1470. Ten minutes, low value.

5. To take back the "LT Generators Van Nuys" query, put "Van Nuys, CA" in the Contact page title and mark up the footer address. The Kohler microsite wins that query because it is the only page in the results with the brand and the city in its title.

Pages. Off-site citations, https://ltgenerators.kohlergeneratordealer.com/, the schema node on https://ltgenerators.com/

F42 Medium CategoryContent EffortMedium Quick winYes

Testimonials page has 46 city-attributed reviews but no link to any review platform and no way to verify the 200+ claim

Evidence

Live fetch Sep 9, 2026 of /testimonials/: HTTP 200, 149,988 bytes where crawl.json recorded 150,157 since page size drifts a few hundred bytes per load, 5368 words per crawl.json, ', CA' 46 times, external link hosts only gmpg.org, quinntechco.com, twitter.com, facebook.com, linkedin.com, youtube.com, crawl.json external_links 6. JSON-LD @type set is WebPage, ImageObject, BreadcrumbList, WebSite, Organization, ListItem, SearchAction, ReadAction, EntryPoint, PropertyValueSpecification. Review 0, AggregateRating 0. No mention of yelp, google maps, homeadvisor, bbb or angi anywhere in the page source. Homepage claim 'More Than 200 positive client Reviews', homepage.html, BRIEF.md line 14. Yelp title in a US web search Sep 9, 2026: 'LT GENERATORS - Updated July 2026 - 30 Photos & 127 Reviews - 14932 Delano St, Van Nuys' at yelp.com/biz/lt-generators-van-nuys. The homepage's only Yelp reference is two award badge images and a JSON-LD sameAs to yelp.com/biz/lt-generators, a different slug than the live listing. BBB and Angi as review sources are not in BRIEF.md or any data file and are unverified.

Impact

A 5,000-word wall of unsourced quotes reads as marketing copy to users and Google. The strongest E-E-A-T asset, 127 public Yelp reviews plus HomeAdvisor, is disconnected from the page built to show trust, and nowhere on the site asks a customer to leave a Google review, the one review action with ranking effect for a local installer. The 200+ claim is unverified rather than shown false.

Recommendation

Keep the ruling against Review and AggregateRating markup. Google still treats reviews a business hosts about itself as self-serving and they earn no rich result, so leave the testimonials page schema alone.

Day one, in this order. 1. Copy the existing homepage badge row onto the top of /testimonials/ and add a third badge for Google that links to the Business Profile listing on Maps. The homepage row already links Yelp and HomeAdvisor. The testimonials page links to nothing. 2. Add a "Review us on Google" button at the top and bottom of /testimonials/ using the review link from the Business Profile dashboard. Review velocity on the Business Profile is what moves the Local Pack. The testimonials page itself ranks for nothing. 3. Rewrite the homepage heading "More Than 200 positive client Reviews" to the real number. Add the Google, Yelp and HomeAdvisor counts on the day and print that sum with the three logos linked underneath. If the sum is under 200 the claim has to change. Set a quarterly reminder to refresh the number. 4. Install one review plugin for Google only. Widgets for Google Reviews by RichPlugins, or Trustindex connected to the Business Profile account so it pulls every review rather than the five the Places API returns. Turn on the plugin's caching and lazy load and place the widget below the fold. The page already carries reCAPTCHA scripts and two gtag loads, and a synchronous widget above the fold would push mobile LCP further past 2.5 s. Do not use a Yelp feed plugin. Yelp's API returns three excerpts and its terms forbid scraping, so use Yelp's own Review Badge embed or a linked logo. 5. Skip BBB and Angi badges unless the business holds an active BBB accreditation and a claimed Angi profile with reviews. A link to an empty profile hurts more than no link.

Phase two, separate ticket. Do not regroup the 46 quotes by city on /testimonials/. Pull three to five city-matched quotes into a static Elementor testimonial block on each service-area page instead, so the Malibu, Sherman Oaks and Thousand Oaks pages carry their own local proof.

Blocking dependency for any schema work. The homepage Electrician JSON-LD is placeholder text. Do not add sameAs entries for the review profiles until that block is deleted or carries the real name, address, phone and geo.

Pages. https://ltgenerators.com/testimonials/, homepage review badge row

F43 Medium CategoryContent EffortSmall Quick winYes

Three different largest-installer superlatives, none substantiated on the page that makes them

Evidence

homepage.html: 'LT Generators is the largest full-service Los Angeles Standby Generators Installation & servicing company' and 'As the largest residential generator installer, we inventory product and parts in our warehouse'. Neither is followed by an install count, unit count, or customer count anywhere on the page. Live /about-us/, curl Sep 9, 2026, HTTP 200: 'is the largest installer in Southern California and holds Electrical (C10), Plumbing (C36), and General (B1) Contractor Licenses', and it repeats the 'largest residential generator installer' line. A fourth version lives on a directory listing: local.yahoo.com/info-85564579-lt-generators-van-nuys/, curl, HTTP 200, reads 'As the largest Generac and Kohler Standby Generator Installer/Dealer in Southern CA'. Other directories carry the site's other variants: buildzoom.com/contractor/lt-generators has 'LTG is the largest installer in Southern California' and nextdoor.com/pages/lt-generators/ has the 'largest full-service Los Angeles' line. The Yoast WebSite description adds a fifth variant, 'Your #1 Neighborhood Emergency Generator Solution'. Competitor allamericangenerator.com, curl, HTTP 200, states 'As a PowerPro Premier Generac Dealer' on its homepage, so the Premier PowerPro badge does not by itself distinguish LT Generators. Yelp and HomeAdvisor returned 403 to curl and were not checked directly.

Impact

Unbacked superlatives are an FTC substantiation exposure and a credibility drag in the luxury-home market the redesign targets, and the homepage redesign draft of Sep 9, 2026, index-v2.html, re-ships the exact 'largest' sentence as a section lede. Inconsistent wording across site, schema and citations weakens the entity description Google assembles.

Recommendation

Drop the size superlative and replace it everywhere with the checkable proof the site already owns. The canonical line is: "Family-owned and CSLB-licensed since 1982, License #602073. Generac Premier PowerPro Dealer and Kohler Authorized Dealer. We stock generators and parts in our own warehouse." This week, in this order. First, in the homepage redesign draft of Sep 9, 2026, index-v2.html, rewrite the section lede so it opens with that line instead of "The largest full-service standby generator installation and servicing company in Los Angeles." Second, on the live site, open the Elementor text widgets on the homepage and /about-us/ and replace both "largest" sentences with the same line. Third, in Yoast SEO 28.3, go to Yoast SEO, Settings, Site representation, and set the organization description to the same line, and change the WordPress Tagline so the WebSite node stops emitting "Your #1 Neighborhood Emergency Generator Solution." Fourth, paste the same line as the first sentence of the Google Business Profile "From the business" description and the Yelp "About the Business" bio, replacing the Generac and Kohler "largest dealer" variant. Only if the owner can hand over a documented number, such as installs completed or years at Premier PowerPro tier, add it as a second sentence. If no number exists, do not fall back to "one of the largest." It is still unverifiable and adds nothing a luxury-home buyer trusts. Keep the dealer badges and CSLB logo in the trust row so the claim and the proof sit together on the page.

Pages. https://ltgenerators.com/, https://ltgenerators.com/about-us/, GBP and directory descriptions

F44 Medium CategoryContent EffortMedium Quick winNo

The Tesla Powerwall vs generator URL is a 404 the advertised sitemap still lists, and the surviving solar post never says Powerwall

Evidence

sm-sitemap.xml, the /sitemap.xml that robots.txt's Sitemap line points at, lists /tesla-powerwall-vs-standby-generator/ with lastmod 2019-02-25. The Yoast index children that the 163-URL crawl used never contained it, so the "all 200" digest does not cover it. Live curl -I Sep 9, 2026: HTTP/2 404, served by Cloudflare. No page on the site links to the dead URL. The closest live asset /the-truth-about-solar-batteries-and-emergency-standby-generators/ is 1524 words per crawl.json, lastmod 2024-04-06T14:59:29Z per the live post-sitemap.xml, 1 H1, 0 H2s in crawl.json and live, 0 H3s live, and on the live page 0 occurrences of 'Powerwall', 'Tesla', 'kWh' or any dollar figure. SERP 'Tesla Powerwall vs standby generator California', US web search Sep 9, 2026: infinitysolar.net, solartechnologies.com, phoenixexteriors.com, solartopps.com, storpwr.com, sesre.com, divineelectricandplumbing.com, photonbrothers.com, plus solarpaneltalk.com. No ltgenerators.com.

Impact

This is the one comparison query where a generator installer must show up to defend the sale, and the results are owned by solar companies arguing for batteries. LT's only page on the topic is dead and its fallback has no headings or numbers a snippet could use. Google drops sitemap 404s without penalty, so the cost is the content gap, not the 404.

Recommendation

Publish a real comparison at /tesla-powerwall-vs-standby-generator/ as a new WordPress post with that exact slug. Until it is live, add a 301 from that URL to /the-truth-about-solar-batteries-and-emergency-standby-generators/ using the free Redirection plugin under Tools > Redirection, because Yoast free has no redirect manager. Do the same for /generator-maintenance-top-10-tips/, the other dead URL in the same sitemap, pointing it at /services-maintenance/. Write the new post as question H2s a homeowner types: what a Powerwall costs installed vs a 22 kW standby unit in LA, how long 13.5 kWh runs a house vs natural gas, what happens on day two of a PSPS, noise and permitting, and when a battery plus generator stack makes sense. Put a direct two or three sentence answer under each H2 before the detail, since that is what AI Overviews and featured snippets lift. Use LT's own installed price ranges and a real install photo, with a byline naming the licensed installer and CSLB number. Skip FAQPage schema. Yoast's default Article schema is enough. Link to the new post from /faqs/, from the solar post, and from /our-residential-generators/. In the same Elementor session, break the solar post into H2 sections and add a paragraph that names Powerwall and kWh with a link to the new post. The advertised sitemap is /sitemap.xml, a static xml-sitemaps.com export with 63 URLs, two 404s and five 301s. The robots.txt finding replaces it, so this finding needs only the redirect and the article.

Pages. /tesla-powerwall-vs-standby-generator/, /the-truth-about-solar-batteries-and-emergency-standby-generators/

F45 Medium CategoryContent EffortMedium Quick winNo

No cost guide while a competitor dealer's 2026 cost article ranks for both cost and Malibu queries

Evidence

Live curl Sep 9, 2026 for 4+ digit dollar figures: homepage 0, /faqs/ 2, /our-residential-generators/ 0. The two /faqs/ figures, $10,000 to $20,000+, sit only inside the FAQPage JSON-LD, not in visible page text. Live SERP Sep 9, 2026 'whole house generator cost Los Angeles 2026': angi.com x2, homeguide.com, 30apropertycollective.com, rcgeneratorsandelectric.com/blog/whole-house-generator-cost-california, pelicancoastelectric.com, generatorbudget.com, no LT. The RC Generators guide is about 1,500 words, 1,521 in the main container, 1,686 whole page, bylined 'Published Jun 30, 2026', title 'Whole-House Generator Cost in California (2026): An Authorized Dealer's Honest Breakdown', with H2s 'The 30-Second Answer', 'Pricing by Generator Size', 'How to Spot a Quote That's Too Low', figures $14,000 to $28,000 all-in, permit fees $300 to $1,200, panel upgrades $3,500 to $6,500. It ranked #3 for 'whole house generator Malibu' behind two Yelp results. LT's homepage appeared at #9 in the same SERP. Angi review of LT, angi.com/companylist/us/ca/van-nuys/lt-generators-reviews-6396601.htm, direct fetch returns 403, per search snapshot: customer questioned being charged over $36,000 with about $22,000 for installation alone. BRIEF.md line 53: Synchrony Financial, 0% APR if paid in full within 18 months, no money down, 5.99% and 9.99% APR fixed options, footnote 'subject to credit approval'.

Impact

Cost is the top informational query in the funnel and a competitor's honest breakdown is winning both the cost query and a city query with it. LT's pricing silence leaves the Angi price complaint as the only cost signal in its brand SERP. The cost SERP is held by Angi twice, HomeGuide and GeneratorBudget, so a dealer page is not assured of page one. The near-term return is long-tail cost queries plus a page the sales team can send before quotes.

Recommendation

Publish one pillar page at /standby-generator-cost-los-angeles/ with the H1 "What a Standby Generator Costs in Los Angeles in 2026" and keep the year out of the slug so the URL survives annual updates. Open with a two-sentence answer that states LT's real installed range, then a native HTML table built in the Elementor Table widget or a Text Editor widget, never an image, with rows for 14, 18, 22 and 26 kW and columns for unit, transfer switch, pad, gas line, 200A panel upgrade, permit and labor. Every figure must come from LT's own 2025 to 2026 jobs, not from the RC Generators article. Add a section titled "Why some LA installs pass $30,000" covering panel upgrades, hillside and canyon access, long gas runs and seismic shutoff valves, with one or two anonymized real jobs named by neighborhood. That section answers the Angi complaint on LT's own terms. Cover terrain instead of cities: one block for hillside and coastal areas such as Malibu, Topanga and Bel Air, one for flatland, and do not spin off per-city cost pages. For financing, paste the exact Synchrony dealer disclosure already on /financing/ rather than paraphrasing it. The 0% for 18 months offer is a deferred-interest promotion and needs the "interest will be charged from the purchase date if not paid in full" and "subject to credit approval" language verbatim. Skip FAQPage schema. Keep six to eight visible questions in an Elementor Accordion or plain H3s, and let Yoast output Article schema with a named author who works at LT, a short bio, datePublished and dateModified. Set the title tag to "Standby Generator Cost in Los Angeles in 2026 | LT Generators". Link to the page from the homepage, both dollar mentions on /faqs/, /financing/ and every city page, and end with the free assessment CTA plus a click-to-call number. Give the hero and any job photos explicit width and height so the page holds CLS under 0.1. Each January update the figures and dateModified along with the year. Check Search Console at week six for the cost and Malibu queries. Not a quick win: the Elementor build is a day, but accurate pricing needs sign-off from whoever quotes jobs and the financing copy has to match the Synchrony agreement.

Pages. New /standby-generator-cost-los-angeles/, https://ltgenerators.com/financing/

F46 Medium CategoryContent EffortMedium Quick winNo

Permit, HOA, noise and fuel-choice guides are missing even though LT holds the three licenses that make it the authority

Evidence

SERP snapshots Sep 9, 2026, US web search, with no ltgenerators.com in any result: 'standby generator permit Los Angeles County requirements', 7 results: UpCodes, LA County PW FAQ, saiyanelectric.com, duthiepower.com, permitio.ai, Municode, CEQAnet, 'generator noise ordinance Los Angeles HOA rules standby generator', 8 results: Bluetti, towergenerator.com, expertelectricgroup.com, bridgelegal.org, priorityelectricandlighting.com, standbygenpros.com, thelegalguide.org, thehomereviews.com, 'natural gas vs propane standby generator California' where toweryelectric.com, a Marin electrician, holds three of seven results, /natural-gas-vs-propane-generator-marin/, /standby-generator-fuel-comparison/, /generator-fuel-options-compared-marin/. Live grep Sep 9, 2026: 'permit' 1 on /faqs/, inside the FAQPage JSON-LD answer "local permitting requirements", 0 in visible copy, 5 on /generator-installation-malibu/, 0 on homepage, 0 on /our-residential-generators/, 0 on /generator-accessories/. 'HOA' 0 on every page sampled. 'noise' 1, one sentence on diesel's "higher noise levels", and 'decibel' 0, on /standby-generator-fuel-options/: 1173 words, 93-char title, h2_count 0, crawl.json, lastmod 2019-03-13, live post-sitemap.xml and Article dateModified. The older sm-sitemap.xml capture shows 2019-02-25 for the same URL. BRIEF.md line 7: Electrical (C10), Plumbing (C36), General (B1) licenses, CA State Contractor License # 602073. BRIEF.md line 48: "Other products: propane back-up tanks, whole-house surge protectors, UPS systems, earthquake valves", served at /generator-accessories/, line 62.

Impact

These are mid-funnel questions estate managers and HOA boards ask before choosing an installer. A contractor that pulls its own electrical, gas and building permits is the natural author and holds none of the results, while a Marin electrician holding three of seven LA results shows the SERP is thin and winnable. Opportunity gap, not a defect.

Recommendation

Use the site's existing Yoast Article schema, set a named licensed person as author with CSLB 602073 in the byline, and link the author box to the CSLB license lookup. Do not build the guides around FAQPage markup and do not set a word-count floor. These pages get cited by AI Overviews when the first 150 words answer the question directly and the detail lives in tables and H2s.

Week one, the refresh: edit /standby-generator-fuel-options/ in place at the same URL. Set the Yoast SEO title by hand to "Natural Gas vs Propane vs Diesel Standby Generators in Los Angeles" and turn off the site-wide title suffix for this page so it lands under 60 characters. Add a fuel comparison table, a PSPS section that explains gas keeps flowing during a shutoff but an earthquake valve closes it after a quake, and in-body links to the propane tank and earthquake valve products on /generator-accessories/. Republish so dateModified updates.

Week two, the permit guide: a WP post in a new Guides category titled "Standby Generator Permits in Los Angeles, LA County, Malibu and Beverly Hills". One H2 per jurisdiction, LADBS for the city, LA County Public Works for unincorporated areas, and the Malibu and Beverly Hills building departments, each stating which electrical, plumbing and building permits LT pulls under C10, C36 and B1. Add an H2 on SCAQMD, whose emergency-generator rules apply to diesel units and are the question no competitor answers. LT's licensed staff verifies every threshold before it goes live.

Week three, one guide not two: "HOA Approval and Noise Rules for a Standby Generator in Los Angeles", with a submittal checklist and a dB table by Generac and Kohler model from manufacturer spec sheets, plus the LAMC Chapter XI and LA County Title 12 noise limits, verified by LT.

Link every guide from /faqs/, the residential page and the four location pages, add a Guides link to the footer, confirm each URL is in the Yoast sitemap, and request indexing in Search Console. The retitle, internal links and product links on the fuel page are an under-a-day WP task. The guides need the licensed contractor's input and run about three weeks part-time.

Pages. New guide URLs, /standby-generator-fuel-options/, /faqs/, /generator-accessories/

F47 Medium CategoryAccessibility EffortSmall Quick winYes

Brand red #f20500 fails WCAG AA on 19 to 20 homepage elements including a CTA, review links and the newsletter button

Evidence

lighthouse-summary.json color_contrast: 19 items on mobile, 20 on desktop, the active nav item is flagged on desktop only. #ffffff on #f20500 = 4.37 on paragraphs, h5, h6, the mailto link, the ServiceTitan Get Free Assessment button, the VIEW ALL POSTS button and the Constant Contact Subscribe input, threshold 4.5. #f5db03 on #f20500 = 3.13 on the GENERATOR MAINTENANCE & SERVICES button at 18px bold. #f20500 on white = 4.37 on five Read Reviews labels and the /videos/ link. #c40704 on #000000 = 3.37 on the active nav item, desktop. #f20500 on #232021 = 3.69 on the footer About Us link. Accessibility 93 mobile, 90 desktop. homepage.html Elementor global colors nvprimaryaccent #f20500 and nvsecondaryaccent #c40704. BRIEF.md line 40 names WCAG AA contrast as a target. Recommendation math: white on #c40000 = 6.27, black on #f5db03 = 15.02, #c40000 on #232021 = 2.57, #c40000 on black = 3.35, white on #232021 = 16.15, #f5db03 on #232021 = 11.55.

Impact

Not a ranking factor, but the failures land on the elements that carry conversion: the maintenance CTA, review links, booking button and newsletter submit. Yellow on red at 3.13 is hard to read in daylight on a phone, it blocks the WCAG AA target in the redesign brief, and web accessibility demand letters against Los Angeles small businesses routinely cite contrast failures.

Recommendation

Split the brand red into two jobs. Keep #f20500 for the logo, icons, dividers and any non-text surface. For every place red carries text or sits behind text, use #c40000, 6.27 white-on-red and red-on-white. #e60000 at 4.81 is the lightest shade that passes if the owner wants a smaller shift. Never put yellow #f5db03 on any red. Use black on yellow, 15.02. On dark grounds do not darken the red, it fails harder: #c40000 on the #232021 footer is 2.57 and on the black header 3.35. Set the active nav item and the footer About Us link to white text, 16.15 on #232021, or #f5db03, 11.55, with red reserved for the underline or hover state. Execution in WordPress this week: 1) Elementor > Site Settings > Global Colors, change the red global to #c40000 and confirm the flagged headings, paragraphs, mailto link and buttons are linked to the global rather than hardcoded per widget, relink any that are not. 2) Appearance > Customize > Colors & Background in Neve, update the matching palette slot, then Header Builder > Primary Menu > Active color to white, and Footer Builder link color to white. 3) Constant Contact: open the sign-up form in the CC form builder and set the Subscribe button background to #c40000 with white text there, since the embed injects its own inline styles. 4) ServiceTitan Get Free Assessment button: change the background in the ServiceTitan Web Scheduler branding settings if the button is theirs, otherwise a scoped rule in the neve-child stylesheet. Re-run Lighthouse on mobile and desktop and expect the color_contrast audit to clear. Get the owner to sign off on the #c40000 swatch before the dev touches the global, that approval is the only real blocker.

Pages. Homepage and every template using the Elementor global red

Low

16
F48 Low CategoryPerformance EffortSmall Quick winYes

No preconnect hints for any third-party origin, and 3.2 s of mobile main-thread work remains

Evidence

homepage.html has zero rel=preconnect hints. It does carry two dns-prefetch hints, //www.google.com and //www.googletagmanager.com, which resolve DNS only and do not open the TCP/TLS connection, so www.google.com still appears in the preconnect audit. The head also has 30 rel=preload as=style tags used to defer CSS, two of which are the Elementor local Google Font CSS files roboto.css and robotoslab.css, and zero as=font preloads. Lighthouse mobile uses-rel-preconnect Est savings 390 ms across 11 origins: webscheduler-widget.servicetitan.com 394 ms, ad.doubleclick.net 388, googleads.g.doubleclick.net 383, www.googleadservices.com 380, static.servicetitan.com 376, www.google.com 373, stats.g.doubleclick.net 343, analytics.google.com 342, www.google-analytics.com 337, www.gstatic.com 336, api.scheduler.servicetitan.com 307. Desktop Est savings 160 ms. mainthread-work-breakdown 3,241 ms mobile vs 646 ms desktop, of which mobile Script Evaluation 1,833 ms and Script Parsing 470 ms. bootup-time 1.9 s mobile, 0.2 s desktop. TBT 219 ms mobile, shown as 220 ms, vs 0 ms desktop. max-potential-fid 318 ms mobile. 13 long tasks on mobile, 8 of them attributed to recaptcha__en.js or googletagmanager.com gtag/gtm scripts, none on desktop. Third-party code blocked the main thread for 580 ms on mobile, Google CDN 443 ms plus Google Tag Manager 132 ms, 0 ms on desktop. network-rtt 183 ms mobile, 89 ms desktop.

Impact

TBT sits 20 ms over the 200 ms good threshold and its cause is three separate reCAPTCHA v3 loads plus GTM and two gtag loaders, which have their own findings. The 390 ms preconnect estimate is spread across eleven origins, seven of them ad and analytics endpoints that never touch the LCP element, so preconnecting to scripts that the other findings delay would waste the socket.

Recommendation

Drop preconnect as a standalone task and fold it into the third-party cleanup. Preconnect only pays off for an origin on the path to the LCP element, and Chrome closes an unused preconnected socket after about ten seconds. None of the eleven origins Lighthouse lists touch the hero. This week, in this order. In Perfmatters open Assets, then JavaScript, turn on Delay JavaScript and add recaptcha/api.js, gtm.js, gtag/js, servicetitan.com and youtube.com to the delay list. Set Delay Timeout to 5 seconds so the GA4 and Google Ads tags still fire on passive sessions, then check GA4 realtime and Ads conversions the next morning. WP Rocket users do the same under File Optimization, Delay JavaScript execution. Next, consolidate the three reCAPTCHA v3 site keys to one or swap to Cloudflare Turnstile through the Simple Cloudflare Turnstile plugin, per the reCAPTCHA finding. Then in Perfmatters under Fonts set Display Swap to swap. Only after those land, and only if the ServiceTitan scheduler still loads eagerly on a booking page, add a single preconnect for static.servicetitan.com under Perfmatters, Preconnect. Never add hints for doubleclick, googleadservices or google-analytics. Re-run Lighthouse on the Moto G profile targeting TBT under 200 ms and third-party blocking near zero. PSI returns no CrUX data for this URL or origin, so INP will read as unavailable. Track TBT as a user-experience number, not a ranking number.

Pages. Site-wide.

F49 Low CategoryTechnical EffortSmall Quick winYes

robots.txt has two User-agent: * groups and contradicts itself on Google-Extended and Applebot-Extended

Evidence

robots.txt, captured Sep 9, 2026, live curl on Sep 9, 2026 is byte-identical: Cloudflare managed block, lines 27 to 60, has Applebot-Extended Disallow: /, lines 36 to 37, and Google-Extended Disallow: /, lines 51 to 52. Site block below has Google-Extended Allow: /, lines 79 to 80, and Applebot-Extended Allow: /, lines 85 to 86. Two separate User-agent: * groups, at line 29, followed by line 30 'Content-Signal: search=yes,ai-train=no,use=reference' and Allow: /, and at line 65, Allow: /. Site comment at line 63 reads 'Standard crawling rules + explicit allow for AI answer/citation bots', yet CCBot, line 42, ClaudeBot, line 45, and GPTBot, line 54, stay disallowed in the Cloudflare block and are not re-allowed in the site block. lighthouse-summary.json: robots-txt audit failed on both mobile and desktop, 'robots.txt is not valid', detail index 30, line 'Content-Signal: search=yes,ai-train=no,use=reference', message 'Unknown directive'.

Impact

Googlebot and Bingbot crawl normally, so rankings are safe. Crawlers merge same-agent groups and Allow wins ties, so the Cloudflare AI block is silently overridden for Google-Extended and Applebot-Extended while the site's stated allow policy is silently blocked for GPTBot and ClaudeBot. Nobody can tell which policy is in force, and the Lighthouse failure is cosmetic since RFC 9309 parsers ignore unknown lines.

Recommendation

Make WordPress the single owner of robots.txt and turn the Cloudflare layer off. In the Cloudflare dashboard for the zone, open AI Crawl Control > Settings, Security > Bots > Manage AI bots on the older dashboard, and switch off both Managed robots.txt and Content Signals Policy. Purge Cloudflare cache for /robots.txt. Then in WordPress, Yoast SEO > Tools > File editor, rewrite the file so it has exactly one User-agent: * group with the WordPress defaults, Disallow: /wp-admin/, Allow: /wp-admin/admin-ajax.php, and the Sitemap line pointing at the Yoast index. Delete the 'explicit allow for AI answer/citation bots' comment and the Google-Extended and Applebot-Extended lines unless the owner actually wants a stated AI policy. If they do want one, write it once, in this file only: allow the answer and citation agents, OAI-SearchBot, ChatGPT-User, PerplexityBot, Claude-SearchBot, Claude-User, and, if the owner wants to opt out of model training, disallow the training agents, GPTBot, ClaudeBot, CCBot, Bytespider, Google-Extended, Applebot-Extended. Then check Cloudflare AI Crawl Control > Crawlers and make sure none of the allowed answer bots is set to Block at the edge, because a Cloudflare 403 overrides anything robots.txt says. Verify in Google Search Console > Settings > Crawling > robots.txt report, which shows the version Google actually fetched and parsed. Ignore the Lighthouse robots-txt audit as a pass/fail target, though it will pass once the Content-Signal line is gone.

Pages. /robots.txt

F50 Low CategoryIndexation EffortMedium Quick winYes

47 of 163 sitemap URLs are tag, category, author and generator-cat archives, many near-duplicates, 18 with no meta description

Evidence

crawl-summary.txt: tags 28, generator-cat 13, generator-tag 1, author 3, category 2 in the sitemap, 47 of 163 URLs. 28 tags on 23 posts, post-sitemap.xml lists 24 URLs, one of which is the /blog/ index page. Near-duplicate pairs in post_tag-sitemap.xml: backup-generator/backup-generators, home-generator/home-generators, emergency-standby-generator/emergency-standby-generators, earthquake-preparedness/earthquake-tips, plus prepare-for-an-earthquake, three Malibu tags, malibu-generator-install, malibu-generators, standby-generators-malibu. crawl.json: /tag/home-generator/ and /tag/home-generators/ both 508 words. /tag/earthquake-preparedness/ and /tag/earthquake-tips/ both 500 words. Duplicate titles 'Generac Archives...' on /generator-cat/generac/ and /generator-cat/generac-commercial/, same for Kohler, Cummins, Champion, Briggs & Stratton. Meta description missing, meta_len 0, on all 13 generator-cat, /generator-tag/project-installs/, 3 author pages and /tag/training/, 18 of the 47 archives. The other 27 tag and 2 category pages carry Yoast auto-generated descriptions of 130 to 169 chars. Author archives expose usernames such as aqltg4305. Live curl Sep 9, 2026 of /generator-cat/generac-commercial/: title 'Generac Archives - Backup Generators & Installation in So. Cal. - LT Generators', no meta description tag, robots index,follow.

Impact

Nearly a third of the crawlable site is boilerplate archives with one or two posts each, identical titles in pairs and no descriptions. Google folds thin WordPress archives without penalty and crawl budget does not apply at 163 URLs, so the visible cost is Archives-style titles and a username slug showing up on brand searches.

Recommendation

Treat this as a 30-minute Yoast pass. In Yoast SEO > Settings > Categories & tags, set Tags to Show in search results Off, do the same for the generator-tag taxonomy, and confirm Format archives are disabled. That one toggle adds noindex and drops the URLs from the Yoast sitemap. In Yoast > Settings > Advanced > Author archives, turn Enable author archives Off so the three username URLs 301 to the homepage. The usernames still appear at /wp-json/wp/v2/users, so if the client cares about that, block enumeration with Wordfence or the Stop User Enumeration plugin. For generator-cat, decide this week whether these are landing pages. If nobody will write a real intro for each term, set generator-cat to Show in search results Off as well and move on. If they will, give each of the thirteen terms a unique SEO title, meta description and a two-sentence intro in the term editor, set the five -commercial terms' titles by hand per the title-template finding, and check that the Elementor archive template actually prints the term description. Skip merging the plural tag pairs. They are noindexed after step one and the time is better spent on the generator-cat pages. Leave /category/blog/ and /category/newsletter/ indexed only if each gets a distinct intro, and note that /category/newsletter/ overlaps the /newsletter/ page, so keep one and redirect the other. After saving, purge the Cloudflare cache so the old meta robots tags stop being served, then resubmit sitemap_index.xml in Search Console and watch the Excluded by noindex count over the following month.

Pages. 28 /tag/ URLs, 13 /generator-cat/ URLs, /generator-tag/project-installs/, 3 /author/ URLs, /category/blog/, /category/newsletter/

F51 Low CategoryTechnical EffortSmall Quick winYes

Every 404 renders a 103 KB uncached full-theme page with 59 scripts and no H1 to H3, including misses for standard icon and manifest files

Evidence

headers-timings.txt: /does-not-exist-404-test/ returns 404, so the status is correct. Live check Sep 9, 2026, curl, Chrome UA: HTTP/2 404, 103,116 bytes, cache-control no-cache, must-revalidate, max-age=0, no-store, private, cf-cache-status DYNAMIC, title 'Page not found - Backup Generators & Installation in So. Cal. - LT Generators', meta robots noindex, follow, 3 forms, 1 Neve search form plus 2 Constant Contact newsletter forms with reCAPTCHA, 59 script tags, 10 stylesheets, no h1, h2 or h3, only six footer-widget h4s: About Us, Recent Posts, Newsletter, repeated twice. domain-tech.txt: apple-touch-icon.png, site.webmanifest, manifest.json and llms.txt each 404 at 103,102 to 103,113 bytes. crawl-summary.txt: avg HTML response 1694 ms across 163 URLs. homepage.html already declares rel="apple-touch-icon" to the 180x180 upload in the head.

Impact

The status code is a correct 404 and the page carries noindex, so nothing here blocks indexing. Scanner probes for root icon and manifest files cost a full PHP render through an origin already averaging 1,694 ms, and a lost visitor sees no heading, only a search box. 404 volume will rise briefly when the junk slugs are removed.

Recommendation

Build a light 404 template in Elementor Pro, which is installed: Templates > Theme Builder > Error 404 > Add New, display condition "404 Page". Body: H1 "Page not found", one sentence, the phone number as a tel: link, and four buttons to Residential Generators, Commercial Generators, Service and Contact. Keep the global header and footer but put nothing else on the template. No ServiceTitan scheduler widget, no Contact Form 7 or reCAPTCHA, no YouTube embed, no Go Portfolio, no second search form. After publishing, confirm with curl -I that the URL still returns HTTP 404 and the noindex robots meta is still there. Skip the root apple-touch-icon.png and site.webmanifest files. The WordPress Site Icon already emits a rel="apple-touch-icon" link to the 180x180 upload in the head, and no manifest is linked, so only scanners request those root paths. If you want to stop the PHP render on the two icon probes, add a free Cloudflare Single Redirect: URI path equals /apple-touch-icon.png or /apple-touch-icon-precomposed.png, 301 to https://ltgenerators.com/wp-content/uploads/2021/07/cropped-icon1-180x180.png. Do not try to cache 404s at Cloudflare in isolation. Cache Rules match on the request, not the response status, so caching a 404 HTML page means making all HTML cache-eligible and overriding WordPress's no-store header. That decision belongs to the site-wide HTML caching fix, and if that fix ships, add a status-code Edge TTL of 404 = 5 minutes there. For the junk and staging URLs in the sitemap, 301 only where a real equivalent exists, /home-staging/ to /, /our-residential-generators-staging/ to /our-residential-generators/. Let /test/, /test-2/, /test1/, /gen-test/, /tabs-responsive/, /cal-submit/ and /cal-list/ return 404 or 410 after unpublishing. Do not redirect them to the homepage, Google treats unrelated redirects as soft 404s.

Pages. Every unmatched URL, /apple-touch-icon.png, /site.webmanifest, /manifest.json

F52 Low CategoryTechnical EffortSmall Quick winYes

www URLs take two redirect hops and no HSTS, CSP, frame, referrer or content-type policy headers are sent

Evidence

headers-timings.txt redirects: http://www.ltgenerators.com/ 301 to https://www.ltgenerators.com/ then 301 to https://ltgenerators.com/, reproduced live Sep 9, 2026 with curl: num_redirects=2, hop 1 Location https://www.ltgenerators.com/, hop 2 location https://ltgenerators.com/. Security headers section lists only server, cache-control, nel, cf-cache-status, report-to, cf-ray. A live curl -I of https://ltgenerators.com/ returned no Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, Referrer-Policy, X-Content-Type-Options, Permissions-Policy or Cross-Origin-* header. Lighthouse 12 full reports, both devices: has-hsts 'No HSTS header found', csp-xss 'No CSP found in enforcement mode', clickjacking-mitigation 'No frame control policy found', origin-isolation 'No COOP header found', each severity High. These four audits are informative, score 1, scoreDisplayMode informative, and do not lower the Best Practices scores, which are 71 mobile and 74 desktop for other reasons. domain-tech.txt: TLS issuer C=US, O=Google Trust Services, CN=WE1. Nameservers MARIAH.NS.CLOUDFLARE.COM and YEW.NS.CLOUDFLARE.COM. MX record 0 mail.ltgenerators.com, which is the subdomain the includeSubDomains caution refers to. crawl.csv, crawl.json, homepage.html and both sitemap files contain zero references to www.ltgenerators.com.

Impact

The two-hop path only fires for legacy http://www inbound links, https://www is already a single hop, and Google follows up to ten hops and passes signals through 301s. Security headers are not a ranking factor and the site already forces HTTPS with a valid certificate. Without HSTS a first visit can be downgraded, and without a frame policy the homepage can be embedded on a scraper's domain. Hygiene, thirty minutes in Cloudflare.

Recommendation

Do the redirect, HSTS and three static headers in Cloudflare this week, and skip CSP, COOP and Trusted Types entirely.

1. Collapse the www chain. Cloudflare dashboard > Rules > Redirect Rules > Create rule. Custom filter expression: (http.host eq "www.ltgenerators.com"). Then: Dynamic redirect, expression concat("https://ltgenerators.com", http.request.uri.path), status 301, tick Preserve query string. Single Redirects run in the first request phase, ahead of Always Use HTTPS, so both http://www and https://www land on the apex in one hop. Leave Always Use HTTPS on for the apex. Verify with: curl -sI http://www.ltgenerators.com/services/ | grep -i location. You should see one Location line pointing at https://ltgenerators.com/services/ and a second curl of that URL should return 200.

2. HSTS. SSL/TLS > Edge Certificates > HTTP Strict Transport Security > Enable. Max Age 6 months. Include subdomains OFF. mail.ltgenerators.com is the MX host and is not served over Cloudflare HTTPS, so includeSubDomains would lock anyone using webmail out for six months. Preload OFF. No-Sniff Header ON, which sends X-Content-Type-Options: nosniff for you.

3. Remaining headers. Rules > Transform Rules > Modify Response Header > Create rule, match All incoming requests, action Set static, three headers: X-Frame-Options: SAMEORIGIN. Referrer-Policy: strict-origin-when-cross-origin. Permissions-Policy: camera=(), microphone=(), geolocation=(). Do not use the one-click "Add security headers" managed transform. It sets Referrer-Policy: same-origin and adds the deprecated X-XSS-Protection and Expect-CT headers. The homepage has no iframes and ServiceTitan, reCAPTCHA, YouTube and GTM all load as scripts from their own hosts, so none of these three headers can break anything on the page.

4. Skip CSP. This is a Neve child theme with Elementor, Elementor Pro, Essential Addons, Site Kit, GTM, reCAPTCHA and the ServiceTitan scheduler. Every one of those injects inline styles and scripts. Any CSP that lets the site render needs 'unsafe-inline', which gives you nothing. X-Frame-Options already covers the clickjacking item. COOP and Trusted Types are for web apps with logged-in users and do not apply here.

Fallback if the dev has WordPress access but not Cloudflare: Really Simple Security, free tier, Settings > Security Headers, switch on the Essential Security Headers toggles and HSTS. Cloudflare is preferred because it also stamps the headers on cached static files and handles the redirect in the same place.

Pages. All responses at the Cloudflare edge, http://www.ltgenerators.com/*, https://www.ltgenerators.com/*

F53 Low CategoryAccessibility EffortSmall Quick winYes

241 images without alt text sitewide by the crawler's count, 11 content images on the residential page ship alt="", and homepage alts are generic

Evidence

crawl-summary.txt: 241 of 1213 images missing alt across 40 pages, residential 49/141, commercial 23/97, /generator-accessories/ 10/16, each of the four city pages 1/3, homepage 0/27. Live fetch of /our-residential-generators/ on Sep 9, 2026: 141 img tags, 0 with no alt attribute, 49 with alt="", 38 of them the decorative icon_link.png. The other 11 are content images: home-step-1.jpg x1, home-step-2.jpg x2, home-step-3.jpg x1, home-step-4.jpg x2, quiet-connect-correct-size-1.png x2, PowerConnect-1-1.png x2, ResidentialHouseIcon-Red-150x150.png x1. homepage.html: alts present on 27/27 but generic: 'LT Generators' x3, 'generators install' x2, 'LT Generators yelp' x1, 'LT Generators google' x1, plus 'LT Generators generac', 'LT Generators homeadvisor', 'LT Generators facebook'. crawl.json field img_missing_alt: 40 pages nonzero, the four named pages plus 36 others including /our-residential-generators-staging/ with 47 and /gen-test/ with 21. The crawler counts a present alt="" as missing.

Impact

Product photos of the Cummins QuietConnect and PowerConnect units and the four how-it-works photos carry no text for image search or screen readers, and generic homepage alts waste the image-search signal on the hero. The headline number is inflated: 38 of the residential 49 are a decorative icon that correctly ships alt="", and the staging and test pages hold 68 or more of the 241. Real remaining scope is roughly 20 Media Library fields.

Recommendation

Every empty-alt content image on the live site is rendered from the Media Library. The img tags carry wp-image-480 through wp-image-483, wp-image-1588, wp-image-589 and the Go Portfolio wp-post-image class, so the fix is the Alt Text field in Media Library, not Elementor edits. About 20 attachments in total.

1. home-step-1.jpg to home-step-4.jpg, attachment IDs 480 to 483. Write one alt per photo describing what is actually shown, for example "Technician setting a Generac standby generator on a concrete pad beside a Los Angeles home". Match the real photo content, do not template it.
2. quiet-connect-correct-size-1.png and PowerConnect-1-1.png. These are the featured images on two Go Portfolio items. Set "Cummins QuietConnect residential standby generator" and "Cummins PowerConnect commercial standby generator" on the attachments. The same attachments serve the 300x188 crop on the service pages, so that clears those too.
3. The eight blog featured images used as thumbnails on /service/, /monitoring/, /maintenance/, /emergencies/ and /diagnostics/. Files are unplug-from-the-electric-company, piggy-bank, gears, road-ahead, energy-storage-during-a-blackout, san-diego-energy-storage-money-savings, learn-how-solar-can-save-you-money_0 and the QuietConnect crop. Eight Media Library edits clear 40 of the 241, though those five pages are slated for redirect anyway.
4. Homepage hero generators-install.png. Describe the photo, for example "Generac standby generator installed beside a Los Angeles home". Only name the brands that appear in the picture. Listing Generac, Kohler and Cummins in one alt is keyword stuffing under Google's image SEO guidance.
5. Linked badge images. Change "LT Generators yelp" to "Read LT Generators reviews on Yelp" and "LT Generators google" to "Read LT Generators reviews on Google". Alt on a linked image should describe the destination.
6. Leave alt="" on icon_link.png, the Go Portfolio hover overlay icon that accounts for 38 of the 49 on the residential page, and on ResidentialHouseIcon-Red-150x150.png. Empty alt on decorative images is correct under WCAG 2.2 and Google's guidance. Do not install an auto-alt feature or an AI alt plugin for this job. Filenames like home-step-1 would generate junk and it would also stamp text onto the decorative icons.
7. Alt is baked into cached HTML. After saving, purge Cloudflare under Caching, Configuration, Purge Everything, and purge any WordPress page cache plugin.
8. Re-crawl with a corrected metric: report content images with empty or absent alt on indexable pages, and pull /our-residential-generators-staging/, /gen-test/ and /home-staging/ out of the number. Target after this fix: zero content images with empty alt on the residential, commercial, accessories, service and home pages.

Pages. /our-residential-generators/, /our-commercial-generators/, /generator-accessories/, homepage, 36 other pages in crawl.json img_missing_alt

F54 Low CategoryOn-page EffortSmall Quick winYes

Footer Home link points at a redirecting URL, the hero CTA is href="#", and blog cards carry empty-text overlay links

Evidence

homepage.html footer Quick Links item menu-item-1648 <a href="https://ltgenerators.com/generators-installation/">Home</a> appears twice, and live curl -I Sep 9, 2026 20:36 UTC returned HTTP/2 301 with location: https://ltgenerators.com/. Hero CTA: <a href="#" class="red-border-btn open-widget-btn"><span style="color: #fff">Get Free Assessment</span></a>, the only href="#" anchor on the page. Four blog cards, Essential Addons post grid, widget 526aec8, each carry a completely empty overlay anchor, for example <a href="https://ltgenerators.com/we-mean-to-shake-up-your-awareness/"></a> inside div.eael-entry-overlay, with no text and no child element. The thumbnail <img> is a sibling in div.eael-entry-thumbnail and already has the post title as alt, and each card also has a text link, a.eael-grid-post-link, to the same post. 'Read Reviews' is the anchor text for 5 links: 4 external destinations, yelp.com, google.com/search, homeadvisor.com, facebook.com, plus internal /testimonials/. crawl.json records internal_links 64 for https://ltgenerators.com/, resolving to 25 unique URLs, parsing homepage.html confirms 25 unique internal hrefs. That ratio is normal header and footer repetition on a small site. /contact-us/ is already linked five times on the homepage.

Impact

Google follows a single 301 and passes full signal, so the redirecting Home link is housekeeping. The href="#" CTA and the empty-text overlay links are WCAG 2.4.4 accessibility and conversion issues, not ranking issues. The Google review link being a stale search-results URL instead of a Business Profile link is a small credibility smell on a page whose job is selling trust.

Recommendation

Treat this as a one-morning housekeeping and accessibility pass. 1. Appearance > Menus, open the footer Quick Links menu and delete both Home items that point at /generators-installation/. That page is the static front page, so WordPress canonical-redirects its slug to the root. Re-add Home from the Pages tab or as a Custom Link to https://ltgenerators.com/. 2. Edit the hero Get Free Assessment button in Elementor. Set Link to https://ltgenerators.com/contact-us/ and keep open-widget-btn in Advanced > CSS Classes so the existing click handler still opens the lead widget. Confirm the handler calls preventDefault, then test with JS disabled: the click should land on the contact page. Do not switch this button to the Elementor Pro popup Dynamic Tag, it writes a non-URL href. 3. The four blog overlay links wrap no text. Each card already has a title link to the same post, so Google reads the anchor fine. For accessibility add aria-hidden="true" tabindex="-1" to the overlay anchor via a two-line snippet in the Code Snippets plugin, or rebuild the cards as an Elementor Pro Loop Grid with a single link per card. 4. Rewrite the five identical Read Reviews anchors to name the destination: Read our Yelp reviews, Read our Google reviews, Read our HomeAdvisor reviews, Read our Facebook reviews, and Read customer testimonials for the internal /testimonials/ link. Replace the Google target, currently a pasted google.com/search URL carrying a 2018 ved token plus num=50 and safe=active parameters, with the review link from Google Business Profile > Ask for reviews. 5. In the same pass give the twelve header and footer social icon anchors an aria-label per network or a screen-reader-only span.

Pages. https://ltgenerators.com/ and every page carrying the footer Quick Links widget

F55 Low CategoryStructured data EffortSmall Quick winYes

FAQPage markup on /faqs/ contains two questions that are not on the page, including a hidden $10,000 to $20,000 price claim

Evidence

Live curl of https://ltgenerators.com/faqs/, HTTP 200, 144,934 bytes, Sep 9, 2026: one FAQPage JSON-LD block sits inside an Elementor HTML widget, data-id="bb6716f", data-widget_type="html.default". It has exactly 2 Questions: 'How much does a whole-house generator installation cost in Los Angeles?' answered 'Turnkey whole-house generator installation typically ranges from $10,000 to $20,000+ depending on unit kilowatt (kW) size, electrical panel upgrades, and local permitting requirements.' and 'Does LT Generators offer financing?' answered 'Yes, we partner with top lenders to offer flexible monthly payment options for residential and commercial generator installations.' Neither question string appears in the page's non-script text, and '10,000' appears only inside that script block. The 13 visible questions are 13 separate Elementor Accordion widgets, data-widget_type="accordion.default", class elementor-accordion-title, not toggles, and none of them is in the markup. One visible accordion, 'What type of financing options do you offer?', covers the same topic as the second marked-up question but with different wording. crawl-summary.txt line 11 independently lists FAQPage among the schema types on /faqs/.

Impact

Google requires every marked-up Q&A to be visible. Since 2023-08-08 Google shows FAQ rich results only for well-known, authoritative government and health websites, per its Search Central post at developers.google.com/search/blog/2023/08/howto-faq-changes, read live Sep 9, 2026, so a generator installer's FAQPage markup earns nothing and the manual-action scenario is theoretical. The one real residue is a price range the business publishes to machines but not to people.

Recommendation

Delete the Elementor HTML widget with data-id bb6716f on /faqs/ and do not replace it. Open the page in the Elementor editor, find the single HTML widget in the Navigator panel, delete it, click Update, then purge the Cloudflare cache and confirm in view-source that the FAQPage block is gone. Do not fix this by switching on FAQ Schema in the 13 existing accordion widgets. They are the deprecated pre-3.15 Accordion widget and each one emits its own FAQPage block, so you would ship 13 FAQPage blocks on one page. Yoast's FAQ block is a Gutenberg block and cannot be placed on an Elementor-built page, so drop that option. If the owner wants FAQ markup kept for Bing and AI answer engines, rebuild the 13 questions as one Nested Accordion widget, turn on Content tab > Additional Options > FAQ Schema, and delete the 13 legacy widgets. That is optional and about half a day. If the $10,000 to $20,000+ installation range is meant to be public, write it as a visible 14th accordion item first and only then let schema carry it.

Pages. https://ltgenerators.com/faqs/

F56 Low CategoryContent EffortMedium Quick winYes

Article authorship points at third-party vendor accounts with no bios, bylines or photos

Evidence

crawl.json: 23 posts carry Article + Person, also the total Article count. Live /malibu-generator-install/: Article.author 'Alex B', Person.sameAs https://quinntechco.com/, whose live title reads 'Quinn Tech Consulting - IT Support & Online Marketing Services'. /author/mpaxtonltg/ Person 'Marianne Paxton' sameAs primedomainhost.com, live: a 1.1 KB broken page leaking a raw WP_TOOLKIT_API_TOKEN define, a dead or misconfigured hosting domain. /author/brittneyjltg/ 'B Johnson' sameAs oneoriginals.com, live: 'One Originals Photography by Brittney Johnson', a photo print shop. All three Person images are secure.gravatar.com URLs with d=blank. No author archive has a bio or Person.description, none has a meta description tag, crawl.json meta_len 0. crawl-summary.txt line 62 lists all three among the 29 missing, and all three sit in author-sitemap.xml, sitemaps.json lines 250 to 252. The post's visible text names no author, has no rel=author link, and the only meta class is 'meta date posted-on'.

Impact

The machine-readable claim is that LT's expertise content is written by web and hosting contractors while the visible page names nobody. No visitor sees a byline, author markup is not a ranking input, and every affected URL is supporting news content, so the effect is small. The license number and 1982 founding date are strong trust facts the site is not attaching to anyone.

Recommendation

Fix in one pass, roughly three hours of WP admin work plus a headshot and bio from the client.

1. Create one real author. Users > Add New, the owner or lead installer, role Editor, display name set to the full real name. Biographical Info: installing standby generators in Southern California since 1982, CA Contractor License #602073, Generac Premier PowerPro Dealer, service area. Set the Website field to the site's About page so Yoast's Person.url resolves to a real page instead of a vendor domain. Install Simple Local Avatars, 10up, free, and upload a real headshot on the profile so Person.image stops emitting the blank Gravatar.

2. Move the 23 posts. Posts > All Posts > Screen Options > 50 per page > select all > Bulk actions: Edit > Author > new user > Update. If the three vendor accounts are no longer needed, skip bulk edit and do Users > Delete on each one with "Attribute all content to" the new author. That reassigns the posts and 404s the vendor archives in a single step.

3. If any vendor account must stay, drop it to Subscriber and clear the Website field plus every Yoast social-profile field on the profile. Yoast builds Person.sameAs from those fields.

4. Show the author on the page. Appearance > Customize > Layout > Single Post: under Post Meta enable Author, under Elements Order toggle Author Box on with avatar and archive link. The crawl shows Neve's posted-on markup, so Neve is rendering singles, not an Elementor Pro theme-builder template. If that changes, use the Elementor Pro Author Box widget instead.

5. Author archives. Yoast SEO > Settings > Advanced > Author archives: turn off "Show author archives in search results" and turn off "Show archives for authors without posts." Yoast drops noindexed archives from the sitemap on its own. If you would rather send the author-box link to the About page, disable author archives entirely on the same screen.

6. Verify one post in Google's Rich Results Test. Article.author should be the new Person with a real name, image, and sameAs limited to ltgenerators.com. Resubmit the sitemap in Search Console.

Do not use an "LT Generators Team" user. Google's helpful-content guidance asks who wrote the content and a company byline gives nothing to link to. A named person with a license number does.

Pages. 23 blog posts, /author/aqltg4305/, /author/mpaxtonltg/, /author/brittneyjltg/

F57 Low CategoryStructured data EffortSmall Quick winYes

Organization logo is 300x73, the homepage primary and share image is a 145x139 icon, and no Open Graph tags are output on any page

Evidence

homepage.html Organization.logo /wp-content/uploads/2021/03/logo.png declared 300x73 in JSON-LD, confirmed by sips, 300x73, 15,604 bytes, HTTP 200. WebPage primaryImageOfPage, image and thumbnailUrl, plus the ImageObject #primaryimage and the twitter:image meta, all point at /wp-content/uploads/2021/11/LTG-Logo-LighteningBolt-1.png, declared 145x139 in JSON-LD and confirmed by sips at 145x139, 3,697 bytes. grep of homepage.html: 0 og: meta tags vs 5 twitter: tags, card, title, description, image, site. crawl.csv: og_image and og_title empty on all 163 rows. Google's Organization structured-data doc states "The image must be 112x112px, at minimum."

Impact

A 73 px tall logo fails the 112 px minimum, forfeiting brand-logo eligibility in knowledge panels, though the panel this business gets is the Business Profile panel, which pulls its logo from GBP. Every social share renders a tiny icon that is below X's 144x144 summary-card minimum and Facebook's 200x200 floor, and with no og: tags Facebook, LinkedIn, iMessage and Nextdoor scrape the page instead of reading declared values. Cosmetic, but three Yoast settings.

Recommendation

Three Yoast settings, about 30 minutes, then a cache purge. First, Yoast SEO > Settings > Site features > Social sharing, switch on Open Graph data. Nothing else on the site outputs og: tags, so there is no duplication risk. Second, Yoast SEO > Settings > Site basics > Site image, upload a 1200x630 JPG built from a real install photo with the wordmark on it, so all 163 URLs get a proper og:image and twitter:image fallback instead of the 145x139 bolt icon. Then edit the homepage and the four generator-installation city pages and set a page-specific image in the Yoast sidebar Social tab where a better photo exists. Third, Yoast SEO > Settings > Site representation > Organization logo, upload a square PNG of 600x600 or larger, wordmark centered on a solid white background, and confirm in Google's Rich Results Test that the logo ImageObject now reports width and height of at least 112. Use the logo that is live today. If the redesign changes the mark, redo this step at launch rather than publishing a new logo on the old site. Finish by purging the Cloudflare cache, then re-scrape ltgenerators.com in Facebook Sharing Debugger and LinkedIn Post Inspector so their cached previews refresh.

Pages. Logo and Open Graph affect all 163 URLs, primary and share image affects https://ltgenerators.com/

F58 Low CategoryStructured data EffortSmall Quick winYes

License, founding year and dealer credentials are visible in prose but absent from the Organization entity

Evidence

homepage.html copy: 'since 1982. Fully Licensed installer in Southern California and hold Electrical (C10), Plumbing (C36), and General (B1) Contractor Licenses.', text spans an inline span and non-breaking spaces, and footer 'CA State Contractor License # 602073' x2. Yoast Organization node, @id #organization, keys are exactly @type, @id, name, url, logo, image, sameAs. No foundingDate, hasCredential, email, telephone or address in that node, the separate hand-added Electrician node carries only placeholder telephone +1-800-XXX-XXXX and streetAddress 'Your Street Address'. Live curl Sep 9, 2026: /generator/kohler-standby-generator-12kw/, Product name 'Kohler 12RES Standby Generator 12kW', and /generator/cummins-quietconnect-series/ each contain 'licensed since 1982 (CA Contractor License #602073)' inside the Product JSON-LD description field, plus the footer license line x2, 602073 appears 3 times per page. BRIEF.md lines 7 and 9 confirm the C10/C36/B1 licenses, license # 602073, and Generac Premier PowerPro Dealer status.

Impact

E-E-A-T is judged from the visible page, where the license, founding year and dealer status already appear, so the site is not missing a trust signal, it is missing a machine-readable copy of one. Google does not consume hasCredential or award. What has value is the LocalBusiness typing plus address and phone, and a CSLB link a homeowner about to spend ten thousand dollars can click.

Recommendation

Fold this into the Organization schema work already planned. Google's Organization structured data documentation supports name, legalName, url, logo, sameAs, foundingDate, telephone, email and address. In the wpseo_schema_organization filter, via the Code Snippets plugin or the child theme functions.php: set @type to ['Organization','Electrician'] so the entity is a LocalBusiness subtype, then add legalName, foundingDate '1982', telephone, email, a PostalAddress with the real street address, and areaServed. hasCredential is valid schema.org and costs nothing to include, but Google reads nothing from it. If the client would rather not run code, the Yoast Local SEO add-on sets the LocalBusiness type, address and phone from its own settings screen. Separately, in the Elementor Theme Builder footer template, turn 'CA State Contractor License # 602073' into a link to https://www.cslb.ca.gov/OnlineServices/CheckLicenseII/LicenseDetail.aspx?LicNum=602073 opening in a new tab, and repeat that link on the About and Contact pages. Validate the homepage in Google's Rich Results Test and the Schema Markup Validator after deploying. One filter, one footer edit, about two hours.

Pages. All 163 URLs

F59 Low CategoryStructured data EffortSmall Quick winYes

Service schema on 10 project-install pages uses an anonymous LocalBusiness provider instead of the site's Organization entity

Evidence

Live /generator/generac-residential-22kw-malibu-ca/, curl Sep 9, 2026: a standalone Service JSON-LD block has provider {"@type":"LocalBusiness","name":"LT Generators","telephone":"(818) 787-1470"} with no @id link to https://ltgenerators.com/#organization and no address, while the same page's Yoast graph already defines an Organization node at that #organization @id. crawl.json shows 10 pages with Service in schema_types, all meta_len 0, 556 to 562 words each. 8 of the 10 slugs end in a city -ca suffix. /generator/generac-residential-16kw/ and /generator/generac-protector-os-series-48kw/ do not.

Impact

Each page declares a fresh unlinked business rather than reinforcing the one entity, and Yoast spells that entity 'LTGenerator' while the Service provider says 'LT Generators'. Google has never shown a rich result for Service, so this is entity hygiene with no indexing or trust effect.

Recommendation

The site runs Yoast, and Yoast already emits the Organization node with @id https://ltgenerators.com/#organization on every page, so the Service block is coming from somewhere else. Search the theme, Elementor HTML widgets, and any Code Snippets or schema plugin for "@type":"Service" and find the one template that prints it. Replace the inline provider object with "provider":{"@id":"https://ltgenerators.com/#organization"} and delete the name and telephone keys there. Yoast's graph then carries the identity. While in there, set Yoast SEO > Settings > Site representation > Organization name to the exact Google Business Profile name, LT Generators, and type a meta description into the Yoast box on each of the 10 pages, or set the Generators post-type template per the meta description finding. Validate one URL in the Rich Results Test and the Schema Markup Validator and check that the provider resolves to the Organization node with no orphan entity. Leave the question of whether to enrich or consolidate these 10 near-duplicate project pages to the content findings. Under an hour once the dev finds where the Service block is printed.

Pages. 10 /generator/ install pages listed in crawl.json with Service, 8 of them *-ca/ slugs

F60 Low CategoryStructured data EffortSmall Quick winYes

WebSite schema name and description are raw WordPress settings, including an unverifiable '#1' tagline

Evidence

homepage.html, Yoast SEO v28.3 graph, class="yoast-schema-graph", WebSite node: "name": "Backup Generators &amp. Installation in So. Cal. - LT Generators", "description": "Your #1 Neighborhood Emergency Generator Solution". crawl-summary.txt title distribution: <30: 0, 30-60: 4, 61-70: 10, >70: 149 of 163. crawl.json: 143 of those 149 over-70 titles contain the site-name string "Backup Generators & Installation in So. Cal.", 145 of all 163 do, and 113 end with the full "Backup Generators & Installation in So. Cal. - LT Generators" string. Six over-70 titles use other templates, for example /our-residential-generators/ "...by LT Generators", 72 chars. The homepage itself already uses the short form "Standby Generators Installation Los Angeles | LT Generators", 59 chars. The header logo aria-label reads the site title plus the tagline, so screen readers hear the '#1' line.

Impact

Google's site-name feature reads WebSite.name, so a keyword string instead of the brand can produce a long or fallback site name in results. The fallback would be ltgenerators.com, which reads fine. Google does not display WebSite.description, so the '#1' superlative reaches the public only through the header aria-label. Fixing the source string also shortens 143 titles at once.

Recommendation

One settings pass in WP admin, then a cache purge. 1. Settings > General: Site Title = "LT Generators". Tagline = "Standby generator installation and service in Los Angeles since 1982". The homepage already says since 1982, so the claim is sourced. This is the field Yoast reads for WebSite.name, WebSite.description and the %%sitename%% title variable, so it fixes the schema and the title suffix on 143 pages in one save. With "| LT Generators" as the suffix, 128 of them land under 70 characters with no per-page editing. 2. Yoast > Settings > Site basics: confirm Website name shows "LT Generators". Leave Alternate website name blank unless customers actually call the business LTG. Set Title separator to the pipe so every page matches the homepage's existing "| LT Generators". 3. Yoast > Settings > Site representation: Organization name is currently "LTGenerator". Change it to "LT Generators" spelled exactly as on the Google Business Profile. 4. Purge Cloudflare and any WP page cache, then run ltgenerators.com through the Rich Results Test and confirm WebSite.name, WebSite.description and Organization.name all read as expected. Request indexing of the homepage in Search Console. Google refreshes site names on its own schedule, so do not expect the SERP label to change the same day. Open Graph is off on all 163 pages and has its own finding.

Pages. All 163 URLs

F61 Low CategoryAccessibility EffortSmall Quick winYes

Icon-only social links have no accessible name and desktop submenu carets are 8 px targets

Evidence

lighthouse-desktop.report.json link-name, score 0: 8 anchors with no discernible name, all matching div.header-social-links > ul > li > a. Four are in the header custom-HTML block, with rel="noopener", and four repeat in the footer, without rel, for example homepage.html: <a href="https://www.facebook.com/LTGenerators/" target="_blank" rel="noopener"><i class="fab fa-facebook-f"></i></a>, plus twitter.com/ltgenerators, linkedin.com/company/ltgenerators and youtube.com/channel/UCa-mSQ8KpzRDHQap2k6aZvg. lighthouse-mobile.report.json link-name: 4 of the same, the footer set. target-size on desktop, score 0: three <div role="button" aria-pressed="false" aria-label="Open Submenu" tabindex="0" class="caret-wrap caret N"> under li#menu-item-148 for Residential, li#menu-item-881 for Commercial and li#menu-item-1273 for About us, each 8px by 8px, should be at least 24px by 24px, safe clickable space 18px. target-size passes on mobile, score 1. heading-order fails on both devices and is covered in the heading findings.

Impact

Screen readers announce eight links as link with no destination. The submenu toggles cannot be hit reliably with a mouse on desktop, where Neve opens submenus on hover and the parent link is clickable, so the practical cost is small. Nothing here touches ranking or Core Web Vitals. Automated-scan failures like these are what accessibility demand letters cite.

Recommendation

Fix the eight social links by editing the markup. In Appearance > Customize > Header Builder, open the HTML component that holds div.header-social-links and give each anchor an aria-label, for example aria-label="LT Generators on Facebook", aria-label="LT Generators on X", aria-label="LT Generators on LinkedIn", aria-label="LT Generators on YouTube". Add aria-hidden="true" to each <i class="fab ..."> so the icon font glyph is not read aloud. Keep target="_blank" rel="noopener". Repeat the same edit in Footer Builder for the second HTML component. Do not swap to Neve's Social Icons component, that component is Neve Pro only and the HTML edit takes fifteen minutes.

Fix the carets with CSS, since free Neve has no caret size control. In Appearance > Customize > Additional CSS add: .nv-nav-wrap .caret-wrap { min-width: 24px; min-height: 24px; display: inline-flex; align-items: center; justify-content: center; } That satisfies the 24 x 24 CSS pixel minimum Lighthouse checks under WCAG 2.5.8. Do not make the whole parent menu item the toggle, parent items such as Services link to real money pages and desktop submenus already open on hover.

Verify by re-running Lighthouse on the homepage and confirming link-name and target-size pass on desktop and mobile. Purge Cloudflare cache first so the audit hits the new HTML.

Pages. Header and footer on all pages

F62 Low CategoryTechnical EffortSmall Quick winYes

Three third-party cookies set on load, including a DoubleClick ad cookie, with no consent mechanism

Evidence

lighthouse-summary.json third_party_cookies, identical on mobile and desktop: __cf_bm from https://static.servicetitan.com/webscheduler/shim.js, test_cookie from https://googleads.g.doubleclick.net/pagead/viewthroughconversion/800825679/, whose query string carries en=gtag.config so it fires as the AW config on load, gcd=13l3l3l3l1l1 for consent mode absent and dma=0 for visitors not treated as EEA, _GRECAPTCHA from https://www.google.com/recaptcha/api2/reload?k=6LfRcbEUAAAAACIZKFlzXuiPGWHhnpyvmYHIykva. The summary's failed_audits lists third-party-cookies as "Uses third-party cookies". The full lighthouse-mobile.report.json and lighthouse-desktop.report.json give displayValue "3 cookies found", score 0. Case-insensitive grep of homepage.html for consent, complianz, cookieyes or gdpr: 0 matches, and a live curl of https://ltgenerators.com/ on Sep 9, 2026 with a Chrome UA also returned 0. AW-800825679 appears 4 times hardcoded in the HTML alongside GTM-N7XTZCZ, G-BVPGS29PEC and UA-120927605-3. Shim.js is referenced 4 times and three separate recaptcha/api.js loads are present. perf-profile.json records static.cloudflareinsights.com as a third-party host, 1 request, and 1 beacon-type resource, while the saved homepage.html has 0 matches for cloudflareinsights. The live curl with a browser UA did return a static.cloudflareinsights.com/beacon.min.js script tag in the served HTML, confirming Cloudflare injects it at the edge rather than WordPress emitting it.

Impact

Two of the three cookies are functional security cookies, __cf_bm is Cloudflare bot management on ServiceTitan's CDN and _GRECAPTCHA is anti-abuse. test_cookie is a 15-minute DoubleClick probe. This is a US-only local business, so Consent Mode v2 does not apply and CPRA almost certainly does not at this size. Chrome kept third-party cookies on by default in 2025, so attribution is not degraded there. The finding's value is the tag sprawl it exposes, which the performance findings already address.

Recommendation

Do not add a cookie banner. A CMP would cost conversions and add a plugin for no ranking or legal gain. Treat the finding as tag hygiene and fix the causes, which a WordPress dev can do in a day.

1. Consolidate tags into GTM-N7XTZCZ, per the tag-stack finding, so one loader owns GA4 and Ads and the dead UA line goes.

2. Cut reCAPTCHA to one loader, or replace it with Cloudflare Turnstile through the Simple Cloudflare Turnstile plugin, per the reCAPTCHA finding. Turnstile sets no third-party cookie, so _GRECAPTCHA disappears without a dequeue script.

3. Load the ServiceTitan scheduler on demand, per the ServiceTitan finding, or with Flying Scripts using servicetitan and gtag/js?id=AW in its keyword list. The __cf_bm cookie and roughly 2 MB of scheduler JS leave the initial load, and the Ads tag still fires before any call click or form conversion.

4. Turn off the Cloudflare Web Analytics beacon if GA4 is the reporting source. In the Cloudflare dashboard open the site, then Analytics & Logs, then Web Analytics, and disable it. There is no Cloudflare WordPress plugin on the site, so this is a dashboard toggle only.

5. Confirm the privacy policy at /privacy-policy/ names Google Ads remarketing and points to Google's Ads Settings for opt-out. Google's Personalized Advertising policy requires that disclosure on any site running the AW tag, and it is the one real compliance item here. Ten minutes of copy.

Re-run Lighthouse afterward. The third-party-cookies audit should list one cookie at most and the Best Practices score will move. Rankings will not.

Pages. All pages

F63 Low CategoryTechnical EffortSmall Quick winYes

Root favicon.ico is a 43-byte blank GIF and there is no web manifest or root apple-touch-icon

Evidence

domain-tech.txt lines 25-28: favicon.ico 200 at 43 bytes, apple-touch-icon.png 404, 103,113-byte 404 page, site.webmanifest 404, manifest.json 404. Live curl Sep 9, 2026: HTTP/2 200, content-type image/x-icon, content-length 43, last-modified 22 Mar 2020, bytes begin GIF89a with a 1x1 logical screen, 01 00 01 00, served by Cloudflare with cache-control max-age=14400, cf-cache-status varies by edge, HIT on one fetch and MISS on another. homepage.html head declares working icons: rel=shortcut icon /wp-content/uploads/favicon-LTGenerators-Installer.ico, 200, 15,086 bytes, rel=icon cropped-icon1-32x32.png, 200, 419 bytes, and cropped-icon1-192x192.png, 200, 3,713 bytes, rel=apple-touch-icon cropped-icon1-180x180.png, 200, 8,069 bytes. BRIEF.md line 32: "Favicon: pixelated yellow bolt."

Impact

Google reads the declared link rel=icon, and the 192 px size satisfies the multiple-of-48 rule, so the search result icon and indexing are unaffected. Any client falling back to /favicon.ico gets a blank pixel, and the root apple-touch-icon fallback serves a 103 KB 404 page. The current icon reads as low-res.

Recommendation

Fold this into the brand refresh rather than opening a standalone ticket. When the redesigned bolt monogram is final, a WordPress dev does it in one pass, under an hour. Step 1. Appearance > Customize > Site Identity > Site Icon, upload a 512x512 PNG of the new mark. WordPress generates the 32, 180 and 192 px sizes and rewrites the rel=icon and apple-touch-icon tags, which is what Google reads for the result icon. If Elementor Site Settings > Site Identity > Site Favicon is also set, point it at the same image so the two sources match. Step 2. Generate a favicon.ico with 16, 32 and 48 px layers from the same source using RealFaviconGenerator or ImageMagick, then SFTP it to the web root over the 43-byte host placeholder. Copy the 180 px PNG to /apple-touch-icon.png at the root at the same time. Step 3. Purge both URLs in Cloudflare under Caching > Configuration > Custom Purge. The blank GIF is cached at the edge and will keep serving after the upload until you purge it. Step 4. Verify with curl -I on /favicon.ico and /apple-touch-icon.png, expecting content-length above 1 KB, then confirm the new icon in the browser tab and a Google result once recrawled. Skip the web manifest and SVG icon for now. A manifest only matters for installable PWAs and has no effect on rankings or the search result icon for a local installer.

Pages. Site root and the head of every page

Section 04

Performance deep dive

Lighthouse 12.8.2 ran once per form factor on Sep 9, 2026. Mobile emulates a Moto G Power on slow 4G with 4x CPU slowdown. Desktop is unthrottled. Both runs agree on the diagnosis: a late-discovered hero image on a 7.3 MB page.

Lighthouse category scores, mobile against desktop
Performance
51 mobile
64 desktop
Accessibility
93 mobile
90 desktop
Best Practices
71 mobile
74 desktop
SEO
92 mobile
92 desktop
Homepage page weight by resource type, mobile, KB
Script
4,183 KB
Image
2,181 KB
Stylesheet
379 KB
Font
374 KB
Document
156 KB
XHR
19 KB
Fetch
6 KB
Other
4 KB
Ping
1 KB

Total 7,302 KiB across 272 requests, lighthouse-summary.json network_by_type. Scripts are 57% of bytes. 108 script requests. Images are 30%.

Third-party transfer by entity, mobile, KB
ServiceTitan scheduler
1,970 KB
YouTube
1,009 KB
Google Tag Manager and gtag
810 KB
Google CDN for reCAPTCHA
411 KB
Google Fonts
99 KB
Other Google APIs
86 KB
Segment
46 KB
Google Analytics
22 KB
Cloudflare
10 KB
Google Ads / DoubleClick
9 KB

Third parties account for about 4.5 MB of the 7.3 MB. The scheduler alone is 27% of page bytes and 24% of requests for a modal nobody has opened.

The LCP story

The largest contentful paint element on both devices is the Elementor section with data-id 1ab25c46, a 412 by 400 px block on mobile and 1,335 by 400 px on desktop. It has no content of its own. Its paint is a CSS background image, LTG-HP-Banner-Image.jpg, 471,431 bytes, 1520 by 480 px, set in the external stylesheet uploads/elementor/css/post-8088.css. The browser cannot start that download until it has parsed the HTML, fetched the stylesheet behind nine render-blocking scripts, and laid out the page. Lighthouse's lcp-discovery-insight marks it requestDiscoverable false and priorityHinted false. Homepage.html has 30 preload tags, all for stylesheets, none for an image or a font, and no fetchpriority on any image.

PhaseMobile msMobile shareDesktop msDesktop share
Time to first byte9693%4257%
Resource load delay15,03348%3,01148%
Resource load time14,46246%2,64142%
Element render delay1,0463%2424%
Total LCP31,5106,320

Why 31.5 s on mobile against 6.3 s on desktop: the mobile run is a Lantern simulation at 1.6 Mbps with a 4x CPU slowdown, so every byte on the 7.3 MB page is priced at slow-4G rates and the hero queues behind 2 MB of scheduler JavaScript, 1 MB of YouTube player and the blog thumbnails that all start downloading at 2,340 ms. The same trace observed the paint at 4,618 ms before simulation, and the unthrottled Chrome profile in perf-profile.json measured LCP at 2,248 ms on desktop. Real phone users on LTE most likely land between 4.6 s and the simulated figure, still well past Google's 2.5 s good threshold. Field data could not be checked because the PageSpeed Insights API returned 429 on both runs.

The fix is two-part and both parts are in the findings: make the image small and discoverable (F02, F04), then stop the third parties competing for the same connection (F03, F19). Render-blocking scripts (F22) and caching (F24, F25) move first paint and TTFB, not this number.

Third parties
EntityTransfer bytesMain thread msBlocking msNote
servicetitan.com2,017,123510Booking modal, 65 requests, 4 inits, cacheLifetimeMs 0 on every bundle
YouTube1,033,241100Full player for one embed, 12 requests, base.js 472 KB
Google Tag Manager829,533554132gtm.js plus four gtag loaders, G- twice, AW- and a dead UA property
Google CDN420,8801,151443recaptcha__en.js evaluated three times, 1,132 ms of the main-thread time
Google Fonts101,83000Roboto and Nunito Sans pulled by reCAPTCHA and the scheduler widget, not the theme
Other Google APIs/SDKs87,74980
Segment46,83400Inside the ServiceTitan widget
Google Analytics22,054250analytics.js injected by the container
Cloudflare10,338160Web Analytics beacon injected at the edge
Google/Doubleclick Ads9,41160Conversion probe, sets test_cookie
Largest requests, mobile
RequestKBType
webscheduler-widget.servicetitan.com/4.e800495b.js1,236Script
uploads/2020/05/PowerLineImage.png504Image
youtube.com/s/player/.../base.js472Script
uploads/2024/05/LTG-HP-Banner-Image.jpg461Image, LCP resource
uploads/2023/12/SolarHouseImage.jpg347Image
gstatic.com/recaptcha/releases/.../recaptcha__en.js345Script
uploads/2023/08/HurricaneHilary.png325Image
uploads/2024/06/Earthquakes.png315Image
webscheduler-widget.servicetitan.com/iconsVendor.7b4da818.js263Script
youtube.com/s/_/ytembeds/_/js/...base219Script
googletagmanager.com/gtag/js?id=G-BVPGS29PEC183Script
googletagmanager.com/gtag/js?id=G-BVPGS29PEC&cx=c&gtm=4e6941183Script, second copy
webscheduler-widget.servicetitan.com/4.6d8a57f2.bundle.css161Stylesheet
googletagmanager.com/gtag/js?id=AW-800825679158Script
googletagmanager.com/gtm.js?id=GTM-N7XTZCZ155Script
Caching and TTFB

Every HTML response carries cache-control: max-age=3, must-revalidate and cf-cache-status DYNAMIC, so Cloudflare passes every page view to origin PHP. Static assets get the zone default of max-age=14400, four hours, and Lighthouse counts 145 resources with a short cache lifetime on mobile, 151 desktop, worth about 3.9 MB of repeat-visit transfer. The crawl measured average HTML response at 1,694 ms across all 163 URLs with a maximum of 7,630 ms. The two worst outliers did not reproduce on retest and the homepage root document clears Google's 800 ms guidance in Lighthouse at 710 ms mobile and 310 ms desktop. The three curl samples in headers-timings.txt, 2.11 s, 0.95 s, 0.79 s, were served from Dublin and Tokyo Cloudflare colos and include 0.3 to 1.2 s of handshake, so true origin time on the homepage is closer to 0.45 to 0.9 s. A page cache at origin and edge caching of HTML are the fix (F24, F25).

Projected after fixes. Estimate, not a measurement.

If F02, F03, F04, F19 and F20 ship as written, the initial mobile download drops from 7.3 MB to roughly 2 to 2.5 MB and from 272 requests to under 150, because the scheduler at 1,970 KB and 65 requests, the YouTube player at 1,009 KB and 12 requests, about 1,650 KB of image re-encoding and roughly 320 to 480 KB of duplicate tag loads come off the critical path. With the hero at about 52 KB and preloaded, both long LCP phases collapse. Under the same slow-4G simulation the mobile LCP should land in the 3 to 5 second range, and reaching the 2.5 s threshold will also need the render-blocking script pass (F22) and HTML caching (F24). These are estimates from Lighthouse's own savings figures. Re-run Lighthouse after each ticket and read Search Console's Core Web Vitals report once the origin has field data.

Section 05

Indexation and site structure

163 URLs in the Yoast sitemap index, all live and all indexable. About a third of them should not be. Sources: sitemaps.json, sitemap-children.txt, crawl.json, crawl-summary.txt, robots.txt, sm-sitemap.xml.

Sitemap composition
SitemapURLsWhat it holdsNote
page-sitemap.xml47PagesIncludes 10 test, staging and demo URLs, 5 identical battery-storage pages, and 6 duplicate lead-form pages
generator-sitemap.xml45Generator products and project posts33 carry Product schema, 10 carry Service schema, 11 have no meta description, the /generator/ archive plus the 10 project posts
post_tag-sitemap.xml28Tag archives28 tags for 23 posts. Plural and singular pairs. Three Malibu tags
post-sitemap.xml24Blog posts plus /blog/Newest original post June 2024
generator-cat-sitemap.xml13Brand and type archivesFive residential and commercial brand pairs with identical titles. All 13 lack a meta description
author-sitemap.xml3Author archivesExpose usernames such as aqltg4305. Vendor accounts
category-sitemap.xml2Post categories/category/newsletter/ duplicates the /newsletter/ page
generator-tag-sitemap.xml1Generator tag archiveNo meta description
Total163All 163 return 200, self-canonical, index,follow, crawl.json
URLs to remove, redirect or noindex
URLWordsWhat it isAction
/test/463Title 'Demo - Backup Generators...'Trash
/test-2/462Title 'Test'Trash
/test1/466Title 'Test1'Trash
/gen-test/678Test page, 21 images without altTrash
/tabs-responsive/471Layout testTrash
/other-products-original/543Old copy of /generator-accessories/Trash
/cal-submit/1616 words, no form, no H1Check for a plugin endpoint, then trash or noindex
/cal-list/1616 words, no form, no H1Check for a plugin endpoint, then trash or noindex
/home-staging/1,131Near-duplicate of the homepage, same title, H1 and metaPrivate, then 301 to /
/our-residential-generators-staging/1,290Near-duplicate of the residential page, zero H1sPrivate, then 301 to /our-residential-generators/
/thank-you/522Form redirect target, Ads conversion fires on any loadNoindex, keep live
/diagnostics/, /emergencies/, /maintenance/, /monitoring/, /service/3,068 eachOne identical battery-storage page at five slugs, zero H1s301 all five to /services-maintenance/, then trash
/post-generator-installation-survey/560Customer survey, zero H1Noindex
/kohler-contact-us/664Duplicate contact form, zero H1Noindex or 301 to /contact-us/
/install-assessment/, /schedule-consultation/, /power-outage-preparation-free-assessment/528 to 639Duplicate assessment forms. Some are live Ads landing pagesConsolidate onto /home-generator-assessment/ after the Ads owner confirms final URLs
/kohler/225Thin duplicate of /kohler-generators/ with a typo in the H1301 to /kohler-generators/
/terms-of-services/1,403Duplicate of /terms-conditions/301 to /terms-conditions/
/newsletter/462Same boilerplate as /test-2/. Overlaps /category/newsletter/Keep one, redirect the other
28 /tag/ archives, 3 /author/ archives500 to 564Thin archives, vendor usernamesYoast: tags and author archives out of search results
Duplicate titles
Shared titleURLs
Standby Generators Installation Los Angeles | LT Generators/ and /home-staging/
Residential Generators & Installation | Backup, Standby by LT Generators/our-residential-generators/ and /our-residential-generators-staging/
Generac Archives - Backup Generators & Installation in So. Cal. - LT Generators/generator-cat/generac/ and /generator-cat/generac-commercial/
Kohler Archives .../generator-cat/kohler/ and /generator-cat/kohler-commercial/
Cummins Archives .../generator-cat/cummins/ and /generator-cat/cummins-commercial/
Champion Archives .../generator-cat/champion/ and /generator-cat/champion-commercial/, 533 words each
Briggs & Stratton Archives .../generator-cat/briggs-stratton/ and /generator-cat/briggs-stratton-commercial/, 576 words each
Two sitemaps and a self-contradicting robots.txt

robots.txt ends with Sitemap: https://ltgenerators.com/sitemap.xml. That URL is a static file generated by xml-sitemaps.com in February 2019 with 63 URLs: two now return 404, /tesla-powerwall-vs-standby-generator/ and /generator-maintenance-top-10-tips/, five are 301 hops, /financing and /testimonials without slashes, /generator/2078/, two old Kohler slugs, and it lists a 2018 PDF and both slash and no-slash variants of two pages. It omits all four city landing pages and every generator page added in 2026. The live Yoast index at /sitemap_index.xml is current to Sep 2, 2026 and is never referenced. sm-wp-sitemap.xml is a 0-byte file. Delete the static files, point the Sitemap line at sitemap_index.xml, and resubmit in Search Console and Bing Webmaster Tools (F29).

The robots.txt itself has two User-agent: * groups. The Cloudflare managed block at the top disallows Google-Extended and Applebot-Extended and carries a Content-Signal line Lighthouse flags as an unknown directive. The site block below re-allows the same two agents while leaving GPTBot, ClaudeBot and CCBot disallowed despite a comment saying AI answer bots are allowed. Googlebot and Bingbot are unaffected. Pick one owner for the file (F49).

Section 06

On-page table for the key pages

From crawl-summary.txt, key page table. Word counts include header, nav and footer text. The crawler counts a present but empty alt attribute as missing, which inflates the residential page figure, 38 of its 49 are one decorative icon.

URLWordsTitle charsMeta charsH1 countImages missing alt
/1,1225914010/27
/about-us/9836714002/26
/contact-us/6976713300/2
/our-residential-generators/1,48572137149/141
/our-commercial-generators/1,20164135223/97
/services-maintenance/8476912710/3
/financing/1,7096613221/5
/faqs/1,7236713011/3
/testimonials/5,3686813910/3
/careers/4857114200/5
/videos/6387014000/2
/power-outages/5487213600/13
/generator-accessories/1,20369142110/16
/blog/1,0256714110/14
/generator-installation-bel-air/82815215011/3
/generator-installation-beverly-hills/76615815011/3
/generator-installation-los-angeles/81415615011/3
/generator-installation-malibu/74615115011/3
/malibu-generator-install/1,4317215210/4
/kohler/22512815030/6
/kohler-generators/65113915030/7
/generator/generac-guardian-series/8938614710/3
/generator-cat/generac/60579none10/6
/generator/91681none12/14

Sitewide: 149 of 163 titles exceed 70 characters, 4 sit in the 30 to 60 range. 29 pages have no meta description, 22 exceed 160 characters, 31 share one boilerplate description with a 'Belair' typo. 19 pages have no H1 and 6 have more than one. 241 of 1,213 images lack alt text by the crawler's count across 40 pages.

Section 07

Structured data

Two JSON-LD blocks on the homepage claim the same @id, https://ltgenerators.com/#organization. Yoast's block names the business 'LTGenerator'. A hand-pasted Elementor HTML widget, data-id bf1d78b, names it 'LT Generators' and fills the rest with a vendor template. Google merges nodes that share an @id, so the entity it reads carries both names, a fake phone and a fake address.

The placeholder Electrician block, verbatim from homepage.html
{
  "@context": "https://schema.org",
  "@type": "Electrician",
  "name": "LT Generators",
  "image": "https://ltgenerators.com/wp-content/uploads/logo.png",
  "@id": "https://ltgenerators.com/#organization",
  "url": "https://ltgenerators.com",
  "telephone": "+1-800-XXX-XXXX",
  "priceRange": "$$$",
  "address": {
  "@type": "PostalAddress",
  "streetAddress": "Your Street Address",
  "addressLocality": "Los Angeles",
  "addressRegion": "CA",
  "postalCode": "900XX",
  "addressCountry": "US"
  },
  "geo": {
  "@type": "GeoCoordinates",
  "latitude": 34.052235,
  "longitude": -118.243683
  },
  "areaServed": [
  "Los Angeles", "Malibu", "Beverly Hills", "Bel Air", "Pacific Palisades"
  ],
  "sameAs": [
  "https://www.facebook.com/ltgenerators",
  "https://www.yelp.com/biz/lt-generators"
  ]
}

Everything marked X is a template value. The geo pair is a downtown Los Angeles coordinate. The business is in Van Nuys. The image URL returns 404, the working logo is /wp-content/uploads/2021/03/logo.png. The Yelp slug differs from the live listing, yelp.com/biz/lt-generators-van-nuys. Yoast's own Organization node on the same page reads "name": "LTGenerator" with a logo caption "LTGenerator", and that node is the publisher referenced by all 23 Article posts, crawl.json, domain-tech.txt lines 83 to 84.

The corrected node for the developer

Delete the Elementor widget first. Then apply this through Yoast SEO, Settings, Site representation, name, logo, profiles, plus a wpseo_schema_organization filter in a WPCode snippet or neve-child functions.php for the type, phone, address, founding date and credential, so Yoast prints one node on all 163 pages. Yoast Local SEO does the same from a settings screen if the client prefers no code. Facts come from BRIEF.md, homepage.html and the live /contact-us/ page. The geo pair is the one TODO. It was not on the captured pages, so copy it from the Business Profile map pin and confirm the whole address block against the profile before publishing.

{
  "@context": "https://schema.org",
  "@type": ["Organization", "Electrician"],
  "@id": "https://ltgenerators.com/#organization",
  "name": "LT Generators",
  "url": "https://ltgenerators.com/",
  "logo": {
    "@type": "ImageObject",
    "url": "https://ltgenerators.com/wp-content/uploads/2021/03/logo.png"
  },
  "image": "https://ltgenerators.com/wp-content/uploads/2021/03/logo.png",
  "telephone": "+1-818-787-1470",
  "foundingDate": "1982",
  "address": {
    "@type": "PostalAddress",
    "streetAddress": "14932 Delano St",
    "addressLocality": "Van Nuys",
    "addressRegion": "CA",
    "postalCode": "91411",
    "addressCountry": "US"
  },
  "geo": {
    "@type": "GeoCoordinates",
    "latitude": "TODO: copy from the Business Profile map pin",
    "longitude": "TODO: copy from the Business Profile map pin"
  },
  "areaServed": [
    "Los Angeles", "Malibu", "Beverly Hills", "Bel Air", "Pacific Palisades",
    "Brentwood", "Santa Monica", "Encino", "Los Angeles County",
    "Ventura County", "Santa Barbara County", "Orange County"
  ],
  "hasCredential": {
    "@type": "EducationalOccupationalCredential",
    "credentialCategory": "license",
    "name": "California State Contractor License #602073, classifications C10, C36 and B1",
    "recognizedBy": {
      "@type": "Organization",
      "name": "California Contractors State License Board"
    }
  },
  "sameAs": [
    "https://www.facebook.com/LTGenerators",
    "https://x.com/ltgenerators",
    "https://www.linkedin.com/company/ltgenerators/",
    "https://www.youtube.com/channel/UCa-mSQ8KpzRDHQap2k6aZvg",
    "https://www.yelp.com/biz/lt-generators-van-nuys"
  ]
}

The street address is the one the live /contact-us/ page showed on Sep 9, 2026, "14932 Delano St. Van Nuys, CA 91411 (818) 787-1470". Confirm it against the Business Profile character for character before publishing. hasCredential is valid schema.org and harmless, but Google does not read it. The CSLB link in the footer is what a homeowner can click. Do not add aggregateRating. Validate the homepage in the Rich Results Test and validator.schema.org after purging Cloudflare.

Other schema items in the findings

Product on 33 generator pages with no numeric price and a 'Call for pricing' string in two price fields, plus one page declaring a $6,249 price and a 404 image the page does not show (F38). FAQPage on /faqs/ with two questions that are not on the page (F55). Service on 10 project pages with an anonymous provider (F59). WebSite name and description set to raw WordPress strings including a '#1' tagline (F60). Organization logo 300x73, below Google's 112 px minimum, and no Open Graph tags on any page (F57). All are hand-pasted or default strings, which F40 traces to one root cause.

Section 08

Local SEO and competitive snapshot

Every position below comes from a web search made on Sep 9, 2026 from the United States. It is a snapshot from a search tool that returns six to ten results per query without the local pack, not tracked Google rankings, and absence from a list is weak evidence. Use it to see the shape of the competition, then confirm with Search Console.

Search snapshot, Sep 9, 2026, US
QueryWhat returned, in orderRead
standby generator installation Los Angelesltgenerators.com/ #6 of 9Homepage holds it. No dedicated page needed
Generac dealer Los Angeles#1 lyfe-power.generacdealers.com, #2 yelp.com, #3 generatorsupercenteroforangecounty.com, #4 #5 #7 #8 #9 allamericangenerator.com, #6 generac.com/dealer-locator, #10 ltgenerators.com/No LT page targets the term. A rival owns the generacdealers.com microsite slot (F10)
Generac PowerPro Premier dealer Los Angelesallamericangenerator.com #1 #2 #3 #5 #6, ltgenerators.com/ #7Same tier, written into titles and headings by the competitor
Generac generator maintenance service Los Angeles8 results: allamericangenerator.com x6, expertelectricgroup.com, generatorsupercenteroforangecounty.com. No LTexpertelectricgroup.com publishes a price in its snippet (F17)
commercial generator installation Los Angelesairtro.com, suncoast-energy.com, backupgeneratorinstall.com, deltaqualityelectric.com, allamericangenerator.com x2. No LT/our-commercial-generators/ has two H1s and product-name H2s (F17)
generator installation Malibu CAmalibu.org, angi.com, yelp.com x2, malibumobilemechanic.com, generatorhookupservice.com, ltgenerators.com/malibu-generator-install/ #7. City page absentGoogle prefers the 2022 post over the city page (F16)
whole house generator Malibuyelp.com #1 #2, rcgeneratorsandelectric.com cost guide #3, ltgenerators.com/ #9A competitor cost article outranks LT for a city term (F45)
generator installer Beverly Hillsltgenerators.com/ #6. Dedicated competitor pages aboveCity page has zero inbound links (F07)
generator installation Bel Air CAltgenerators.com/generator-installation-bel-air/ #5The city template ranks when Google finds it
home generator Santa Monicapowell-electric.com/generators-in-santa-monica-ca/, generatorsupercenteroforangecounty.com/whole-home-generac-generators-in-santa-monica/, ltgenerators.com/ #6 or #7No Santa Monica page exists (F15)
generator installation Pacific Palisades rebuildallamericangenerator.com/pacific-palisades-los-angeles/ ranks. LT homepage #7 in one snapshot, absent from the top 7 in the re-checkNo Palisades page exists (F15, F37)
Kohler generator dealer Los Angeleslinkedin.com/company/ltgenerators #1, yellowpages.com #2 #3, bcew.com #4, greencat.ninja #5, Kohler locator #6, ltgenerators.com/ #7, /our-commercial-generators/ #8Neither LT Kohler page appears (F30)
whole house generator cost Los Angeles 2026angi.com x2, homeguide.com, 30apropertycollective.com, rcgeneratorsandelectric.com/blog/whole-house-generator-cost-california, pelicancoastelectric.com, generatorbudget.com. No LTNo cost page (F45)
Tesla Powerwall vs standby generator Californiainfinitysolar.net, solartechnologies.com, phoenixexteriors.com, solartopps.com, storpwr.com, sesre.com, divineelectricandplumbing.com, photonbrothers.com. No LTLT's page on the topic is a 404 (F44)
PSPS public safety power shutoff generator Los Angeles homeownerscpuc.ca.gov, psehealthyenergy.org, libertyutilities.com, LA County PDF, airtro.com, sce.com, pse.com, homelinksolar.com. No LTThe term appears on one LT page (F18)
LT Generators Van Nuysltgenerators.kohlergeneratordealer.com at #2 or #3. Ltgenerators.com absent from all 9 linksThe Kohler microsite is the only result with brand and city in its title (F41)
LT Generators, brand queryYelp 'Updated July 2026 - 30 Photos &amp. 127 Reviews', Angi 'rated 3.0 overall out of 5', BBB A+, HomeAdvisor. No Business Profile surfacedVerify the GBP by logging in (F13)
The missing city pages

The site has four city pages: Malibu, Beverly Hills, Bel Air and Los Angeles. The homepage promises seven more neighborhoods and five regions. allamericangenerator.com's page-sitemap.xml, fetched live Sep 9, 2026, has 44 URLs of which 15 are place-named geo pages plus two service-area hubs.

Area named or servedWhere it appears todayCompetitor page
Pacific PalisadesHomepage sentence, one project post, Electrician areaServedallamericangenerator.com has three Palisades pages
Santa MonicaHomepage sentence, one project postpowell-electric.com and generatorsupercenteroforangecounty.com
BrentwoodHomepage sentence, one project postallamericangenerator.com/brentwood-los-angeles/
EncinoHomepage sentencenone checked
CalabasasTestimonial only, BRIEF.md line 61none checked
Sherman OaksTestimonial only, BRIEF.md line 58none checked
AltadenaNowherenone checked. 2025 fire rebuild demand
Ventura CountyHomepage tile, one project postallamericangenerator.com/ventura-county/
Santa Barbara CountyHomepage tile, one project postallamericangenerator.com/santa-barbara-county/
Orange CountyHomepage tilenone checked
San DiegoHomepage tileskip unless the crew installs there monthly
Google Business Profile and citations

The Business Profile never surfaced in any snapshot, which may be a search-tool artifact. Confirm it exists at 14932 Delano St with (818) 787-1470 by logging in before any other local work. Then: get the review link from the profile and put it behind the Google badge on the homepage and the eight Read Reviews links on /about-us/, which today point at a 2018 desktop Google Search URL with safe search on (F13). Add the four city pages' areas and the new ones as GBP service areas. Compare the primary category with the top three in the local pack for 'generator installation Los Angeles'.

Citations: the site's phone (818) 787-1470 matches Yelp, BBB, Yahoo Local and the chamber. The Kohler dealer microsite at ltgenerators.kohlergeneratordealer.com carries (818) 527-4742 and no link back to the site. Call that number to learn who owns it, then ask Kohler to add the website link (F41). HomeAdvisor lists the business under 'LT Electric and Lighting' and Angi shows a 3.0. Claim both and reply to the reviews behind the 3.0 (F13, F39). Two Yelp slugs exist in the site's own markup. Find out which one Yelp canonicalizes to and use it everywhere (F39). Business hours appear nowhere on the site while directories publish Mon to Fri 8 to 5. The owner confirms the hours and the footer and schema carry them (F14). Do not buy a citation sweep. The categories on BBB, chamberofcommerce and homeyou do not move the local pack.

Section 09

Roadmap

Ordered by payoff per hour. Owner types: WP dev works in WordPress, Yoast and Elementor. Cloudflare is dashboard work. Content needs written copy. Owner means a business decision or an account login. Finding numbers in parentheses point to the detail.

Week 1, quick wins

TaskOwnerEffortExpected effect
Mobile header tel: button and Get Free Assessment button in the Neve Header Builder. Red-band phone button gets href tel:+18187871470 (F01)WP devSMobile visitors can call. Ads call conversion can swap the visible number
Delete Elementor HTML widget bf1d78b. Yoast Site representation name to LT Generators. Purge Cloudflare (F11, F12)WP devSPlaceholder NAP and the LTGenerator typo leave the entity on all 163 pages
Hero re-exported as WebP, about 52 KB, mobile crop, preload with media queries (F02, F04)WP devSBoth long LCP phases shrink. The LCP resource drops from 471 KB
ServiceTitan: delete three duplicate init snippets, replace the survivor with the click-to-load stub (F03)WP devMAbout 2 MB and 60 requests off every page load
YouTube widget: Privacy Mode, Image Overlay, Lazy Load (F19)WP devSAbout 1 MB and 12 requests off the homepage
Trash six test pages. 301 the five battery pages and two staging twins. Noindex /thank-you/, survey and calendar stubs (F05, F06, F31)WP devSDemo and Test titles leave the index. The maintenance slugs point at the real page
Delete static sitemap.xml files, point robots.txt at sitemap_index.xml, resubmit in Search Console and Bing (F29)WP devSCrawlers read the 2026 sitemap instead of the 2019 one
Service Areas menu, footer Areas We Serve list, homepage tiles linked to the four city pages (F07, F09)WP devSThe money pages get their first internal links
Site Title to LT Generators, tagline rewritten, Yoast title templates without %%sitedesc%%, four city titles by hand (F08, F32)WP devS145 titles shed the site-title boilerplate and 31 lose the misspelled tagline. The four money pages fit the SERP
Remove the Yoast default meta description. Write the four city page metas (F33)WP dev, contentSThe Belair typo leaves 32 pages. City snippets describe the right city
Confirm the Google Business Profile exists and get the review link (F13)OwnerSUnblocks the review badge and hasMap work

Day 30

TaskOwnerEffortExpected effect
/power-outages/ gets an H1, PSPS in title and copy, fixed utility links. Ship before October 1 (F18)WP dev, contentMThe nav page people click during a shutoff works and uses the buyer's term
Cloudflare Cache Rule for /wp-content/ and /wp-includes/, 1 month edge, 1 year browser. APO or WP Rocket for HTML (F24, F25)Cloudflare, WP devSHTML served from the edge. Average response from 1.7 s toward under 800 ms
Remove the orphan head reCAPTCHA tag. Turnstile for Contact Form 7. Constant Contact reCAPTCHA off. Unload CF7 assets where unused (F21)WP devMAbout 1.1 s of mobile main thread and the third-party cookie flag go away
One tag loader: drop the 2018 gtag block and the UA property, keep a gtag stub, verify in Tag Assistant (F20, F28)WP devSGA4 page views stop double counting. 320 to 480 KB less JS
Go Portfolio deactivated or dequeued. Font Awesome 4 shim off. Inline Font Icons on. Google Fonts Load set to Swap. jquery-migrate removed and scripts deferred (F22, F26, F27)WP devMRender-blocking savings of about 1.8 s on mobile first paint
Blog thumbnails to Medium Large. EA Post Grid swapped for the Elementor Pro Posts widget (F23)WP devSAbout 1.3 to 1.5 MB off the initial mobile download
Homepage H1 rewritten with location. Fragment headings demoted. H1s added to About, Contact, Careers, Videos, Power Outages. Kohler 'Suppy' fixed (F34, F35)WP devSClean outline. Visible typo gone
Yoast: tags and author archives out of search results. Open Graph on. 1200x630 site image. 600 px square logo (F16, F50, F57)WP devS28 tag and 3 author URLs leave the sitemap. Shared links get a real preview
Product schema offers removed on 33 pages. Guardian price and image fixed. FAQPage widget deleted (F38, F40, F55)WP devMNo invalid or hidden markup on the money pages
Google badge and Read Reviews links repointed at the Business Profile review link. Contact map embed refreshed (F13, F54)WP devSHappy customers land on the review form
Footer NAP block with hours. Yoast Organization filter adds Electrician type, phone, address, foundingDate (F14, F58)WP devSSite and schema corroborate the Business Profile
City pages: Premier PowerPro line, testimonials from /testimonials/, cross-links, product and financing links (F09)WP dev, contentMLocal proof on the pages that convert
Claim HomeAdvisor/Angi under LT Generators. Check both Yelp slugs. Call (818) 527-4742 (F39, F41)OwnerSReviews consolidate under one name

Day 60

TaskOwnerEffortExpected effect
Malibu: merge the 2022 post's unique material into the city page, 301 the post and project post, or link the post forward (F16)Content, WP devSOne strong Malibu page instead of seven weak URLs
/generac-dealer-los-angeles/ page with real dealer content. Badge alts and a dealer heading on the homepage. generator-cat titles retemplated (F10)Content, WP devMA landing page for the tier LT already holds
Commercial page: one H1, facility-type H2s, alts filled. Maintenance page: retitled with plan, monitoring and emergency H2s. Tips post rebuilt from the PDF (F17)Content, WP devMPages for the two margin lines that currently rank nowhere
Cost guide at /standby-generator-cost-los-angeles/ with LT's own ranges and the verbatim Synchrony disclosure (F45)Content, ownerMAnswers the top informational query and the Angi price complaint
Kohler pages consolidated onto /kohler-generators/ with links from a Brands menu. Terms and contact duplicates redirected (F30)WP devSOne Kohler page with inbound links
Brand red text color to #c40000, nav and footer link text to white. Constant Contact button restyled (F47)Owner approval, WP devSWCAG AA contrast on every CTA
Light 404 template. Www redirect collapsed to one hop. HSTS and three static headers in Cloudflare (F51, F52)WP dev, CloudflareSHygiene
One real author with bio and headshot. 23 posts reassigned. Vendor accounts removed (F56)WP dev, ownerMArticle schema names a licensed person
Superlatives replaced with the license, founding year and dealer tiers on the site, GBP and Yelp (F43)Content, ownerSClaims the business can substantiate
Testimonials page gets the badge row and a Review us on Google button. Homepage review count verified (F42)WP dev, ownerSTrust page links to public proof

Day 90

TaskOwnerEffortExpected effect
City pages phase one: Pacific Palisades, Santa Monica, Brentwood, Calabasas, Encino, Sherman Oaks, with utility, permit and fire-zone facts confirmed by the installer (F15, F37)Content, ownerLDedicated pages for the queries competitors currently win
Altadena page for the 2025 rebuild market (F37)Content, ownerLPresence where concentrated demand is
Guides: fuel-options refresh, permits by jurisdiction with SCAQMD, HOA and noise (F46)Content, ownerMMid-funnel authority the licenses justify
Tesla Powerwall vs standby generator comparison at the original slug (F44)Content, ownerMDefends the sale on the one comparison query
Residential and commercial product grids split by generator-cat term. Duplicate grid and hidden sections removed (F36)WP devMThe commercial page stops reading as a residential catalog
Two service-area pages for Ventura and Santa Barbara counties anchored on existing project posts (F15)ContentMCoverage for the tiles the homepage already shows
Re-run Lighthouse mobile and desktop. Read Search Console Core Web Vitals, Performance and Pages reports. Re-crawl the sitemapWP dev, ownerSMeasures the effect of the first 60 days before the next round
Section 10

Method and limits

Tools

Lighthouse 12.8.2 run locally from a Mac in California on Sep 9, 2026, one run per form factor: mobile emulating a Moto G Power on simulated slow 4G with 4x CPU slowdown, and desktop unthrottled. Run-to-run variance was not sampled. KB and MB in this report are the 1,024-based figures Lighthouse prints, so 7,302 KiB reads as 7.3 MB. A real Chrome load through headless Chrome, perf-profile.cjs, captured TTFB, DOM content loaded, load, LCP, CLS, request counts, transfer bytes, DOM nodes, fonts, third-party hosts, oversized images, uncompressed responses, uncached responses and the detected plugin and theme set. curl captured redirect chains, response headers, three TTFB samples, compression ratio and the security header set. A Python crawler walked the Yoast sitemap index and its eight children, fetched all 163 URLs once, and recorded status, final URL, words, title, meta, H1 through H3 counts, image and alt counts, internal and external link counts, canonical, robots meta and schema types. whois, dig and openssl captured the domain, DNS and certificate facts. The GTM container was read from the public gtm.js. Live spot checks with curl on Sep 9, 2026 verified individual claims and are labeled as such in the evidence.

Not measured

No Google Search Console access, so nothing here is a measured impression, click, indexation status or Core Web Vitals field result. No Google Analytics 4 access, so double counting and conversion volumes are inferred from the page code, not confirmed. The PageSpeed Insights public API returned 429, over quota, on both runs, so there is no CrUX field data. Every timing number is lab data and the mobile figures are simulated. Backlink and domain-authority metrics need a paid tool and were not measured for LT or any competitor. Cloudflare plan tier is unknown, so Polish and APO availability could not be confirmed. Hosting is inferred from the Bluehost registrar and SPF record. Rich Results Test was not run. Schema eligibility statements apply Google's published requirements to the live JSON-LD. Yelp, HomeAdvisor, Angi, Facebook and ZoomInfo returned 400 or 403 to curl, so their listing details rest on search snippets. The crawler counts alt="" as missing alt and records schema types rather than full nodes, and word counts include header, nav and footer text.

Search snapshots

Every search-result observation in this report came from a web search tool run from the United States on Sep 9, 2026. The tool returns six to ten results per query, does not show the local pack, and is not a faithful Google proxy: a site-restricted query returned mostly unrelated noise. Positions are the order the tool returned. They are a snapshot of the competitive shape, not tracked rankings, and a page being absent from a list is weak evidence. Confirm with Search Console before promising a ranking outcome.

AI crawler policy is ambiguous

robots.txt carries a Cloudflare managed block that disallows Google-Extended, Applebot-Extended, GPTBot, ClaudeBot, CCBot, Bytespider, Amazonbot and meta-externalagent, followed by a site block that re-allows Google-Extended and Applebot-Extended and explicitly allows OAI-SearchBot, ChatGPT-User and PerplexityBot. Crawlers that merge same-agent groups will treat the re-allowed agents as allowed and leave GPTBot and ClaudeBot blocked, so the file's stated intent and its effect differ. Which policy is actually in force also depends on Cloudflare's edge bot settings, which were not visible to this audit. Googlebot and Bingbot are unaffected either way.