PageSourceSearch

https://www.solarsurvey.ai/

html solarsurvey.ai collected 2026-10-05 14:22:22 UTC 155,884 bytes, 1,895 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4<meta charset="utf-8">
5<meta name="viewport" content="width=device-width,initial-scale=1">
6<title>Solar Survey AI | The field data layer for solar</title>
7<meta name="description" content="On-site solar surveys, stamped engineering, land surveying and aerial thermal inspection of solar farms. Residential, C&amp;I and utility-scale, nationwide. Every survey makes the next one sharper.">
8<link rel="canonical" href="https://www.solarsurvey.ai/">
9<meta property="og:type" content="website">
10<meta property="og:site_name" content="Solar Survey AI">
11<meta property="og:title" content="Solar Survey AI | The field data layer for solar">
12<meta property="og:description" content="In-person site surveys, engineering and computer-vision inspection for residential, C&I and utility-scale solar. Every survey makes the next one sharper.">
13<meta property="og:url" content="https://www.solarsurvey.ai/">
14<meta property="og:image" content="https://www.solarsurvey.ai/og.png?v=2">
15<meta property="og:image:width" content="1200">
16<meta property="og:image:height" content="630">
17<meta property="og:image:alt" content="Solar Survey AI: every solar decision is a bet on data someone else collected.">
18<meta name="twitter:card" content="summary_large_image">
19<meta name="twitter:title" content="Solar Survey AI | The field data layer for solar">
20<meta name="twitter:description" content="In-person site surveys, engineering and computer-vision inspection for residential, C&I and utility-scale solar.">
21<meta name="twitter:image" content="https://www.solarsurvey.ai/og.png?v=2">
22<link rel="preconnect" href="https://fonts.googleapis.com">
23<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
24<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
25<style>:root{--amber:#FF6A00;--amber-hi:#FFA33D;--ember:#FF3D00;--ember-dk:#C42E00;--hot:#FFE9D2;--bg:#08080A;--bg-2:#0D0D11;--surface:#131317;--surface-2:#1A1A20;--line:#26262C;--line-2:#3A3A44;--tx:#FBF9F7;--tx-2:#B6B0A8;--tx-3:#78716A;--c1:#D76902;--c2:#3284D0;--c3:#C44085;--c4:#8459C3;--glow:0 0 40px -6px rgba(255,106,0,.55);--max:1180px;--r:14px}*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--bg);color:var(--tx);font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;font-size:17px;line-height:1.6;-webkit-font-smoothing:antialiased;overflow-x:hidden}h1,h2,h3,h4{line-height:1.1;letter-spacing:-.022em;margin:0;font-weight:600}h1{font-size:clamp(2.5rem,6.2vw,4.6rem);letter-spacing:-.035em}h2{font-size:clamp(1.9rem,4vw,3.05rem);letter-spacing:-.03em}h3{font-size:1.26rem}p{margin:0}a{color:inherit}.wrap{width:100%;max-width:var(--max);margin:0 auto;padding:0 26px}.aurora{position:fixed;inset:0;pointer-events:none;z-index:0;opacity:1}.aurora i{position:absolute;border-radius:50%;filter:blur(88px);display:block}.aurora i:nth-child(1){width:54vw;height:54vw;left:-15vw;top:-14vw;background:radial-gradient(circle,rgba(255,61,0,.30),transparent 68%)}.aurora i:nth-child(2){width:48vw;height:48vw;right:-13vw;top:30vh;background:radial-gradient(circle,rgba(245,166,35,.19),transparent 68%)}.aurora i:nth-child(3){width:46vw;height:46vw;left:10vw;bottom:-16vw;background:radial-gradient(circle,rgba(255,140,20,.24),transparent 68%)}.grid-bg{position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.34;background-image:linear-gradient(rgba(255,255,255,.034) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.034) 1px,transparent 1px);background-size:64px 64px;mask-image:radial-gradient(ellipse 92% 62% at 50% 0%,#000 24%,transparent 78%);-webkit-mask-image:radial-gradient(ellipse 92% 62% at 50% 0%,#000 24%,transparent 78%)}header{position:fixed;top:0;left:0;right:0;z-index:60;transition:background .3s,border-color .3s;border-bottom:1px solid transparent}header.stuck{background:rgba(11,12,15,.86);backdrop-filter:blur(14px);border-bottom-color:var(--line)}.nav{display:flex;align-items:center;justify-content:space-between;height:70px}.logo{display:flex;align-items:center;gap:10px;text-decoration:none}.logo svg{width:30px;height:30px;flex:0 0 30px;
25display:block}.logo .wm{display:flex;flex-direction:column;line-height:1}.logo .wm .w1{font-weight:600;font-size:.96rem;letter-spacing:-.015em}.logo .wm .w2{font-size:.6rem;letter-spacing:.26em;text-transform:uppercase;color:var(--tx-3);margin-top:3px}.logo .wm .w1 em{font-style:normal;color:var(--amber)}.logo:hover .rt{stroke:var(--amber)}.rt{transition:stroke .3s}.logo .sweep{transform-origin:16px 16px;animation:lgsweep 4.5s ease-in-out infinite}@keyframes lgsweep{0%,100%{opacity:.15;transform:translateY(-5px)}50%{opacity:1;transform:translateY(5px)}}@media (prefers-reduced-motion:reduce){.logo .sweep{animation:none;opacity:.8}}.navlinks{display:flex;align-items:center;gap:28px;font-size:.93rem;color:var(--tx-2)}.navlinks a{text-decoration:none;transition:color .2s}.navlinks a:hover{color:var(--tx)}@media(max-width:900px){.navlinks .hide-sm{display:none}}.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;border-radius:11px;padding:13px 22px;font-size:.95rem;font-weight:500;text-decoration:none;cursor:pointer;border:1px solid transparent;font-family:inherit;transition:transform .16s,box-shadow .22s,background .2s,border-color .2s}.btn:active{transform:translateY(1px)}.btn-p{background:linear-gradient(180deg,var(--amber-hi),var(--amber));color:#231603;font-weight:600;box-shadow:0 8px 26px -10px rgba(245,166,35,.75)}.btn-p:hover{box-shadow:0 12px 34px -10px rgba(245,166,35,.95)}.btn-s{background:rgba(255,255,255,.035);border-color:var(--line-2);color:var(--tx)}.btn-s:hover{background:rgba(255,255,255,.07);border-color:#4A4F58}.btn-sm{padding:10px 17px;font-size:.9rem;border-radius:9px}.signin{position:relative}.signin .short{display:none}@media(max-width:760px){.signin .full{display:none}.signin .short{display:inline}}.signin-menu{position:absolute;right:0;top:calc(100% + 10px);min-width:248px;padding:8px;z-index:80;background:var(--surface-2);border:1px solid var(--line-2);border-radius:13px;box-shadow:0 24px 60px -20px rgba(0,0,0,.9),0 0 40px -18px rgba(255,106,0,.5);opacity:0;visibility:hidden;transform:translateY(-6px);transition:opacity .22s,transform .22s,visibility .22s}.signin.open .signin-menu{opacity:1;visibility:visible;transform:none}.signin-menu a{display:flex;flex-direction:column;gap:2px;padding:11px 13px;border-radius:9px;text-decoration:none;font-size:.92rem;font-weight:500;color:var(--tx);transition:background .18s}.signin-menu a:hover{background:rgba(255,106,0,.10)}.signin-menu a span{font-size:.74rem;font-weight:400;color:var(--tx-3);letter-spacing:.04em}.signin-menu a:hover span{color:#FF8A2B}.signin-menu .mh{padding:7px 13px 5px;font-size:.68rem;letter-spacing:.14em;text-transform:uppercase;color:var(--tx-3)}.ph-r{display:flex;align-items:center;gap:14px;flex-wrap:wrap}.plogin{display:inline-flex;align-items:center;gap:6px;font-size:.84rem;color:var(--tx-2);text-decoration:none;padding:7px 12px;border:1px solid var(--line-2);border-radius:9px;transition:all .2s;white-space:nowrap}.plogin:hover{color:#FF9433;border-color:rgba(255,106,0,.55);box-shadow:0 0 22px -10px rgba(255,106,0,.8)}.plogin svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}#spineHost{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1}#spine{position:absolute;top:0;left:calc(50% - 640px);overflow:visible}@media(max-width:1279px){#spineHost{display:none}}.spine-track{fill:none;stroke:#1E2126;stroke-width:2}.spine-live{fill:none;stroke:url(#spineGrad);stroke-width:2.5;stroke-linecap:round}.spine-node{fill:var(--bg);stroke:#3A3F47;stroke-width:2;transition:stroke .5s,fill .5s}.spine-node.on{stroke:var(--amber);fill:#301F04}main{position:relative;z-index:2}section{position:relative;padding:118px 0}.eyebrow{display:inline-flex;align-items:center;gap:9px;font-size:.76rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--amber);margin-bottom:22px}.eyebrow:before{content:"";width:22px;height:1px;background:linear-gradient(90deg,transparent,var(--amber))}.lede{font-size:clamp(1.05rem,1.9vw,1.26rem);color:var(--tx-2);max-width:62ch;line-height:1.62;font-weight:300}.reveal{opacity:0;transform:translateY(26px);transition:opacity .8s cubic-bezier(.16,1,.3,1),transform .8s cubic-bezier(.16,1,.3,1)}.reveal.in{opacity:1;transform:none}#hero{padding-top:168px;padding-bottom:96px;min-height:96vh;display:flex;align-items:center}#heroCanvas{position:absolute;inset:0;width:100%;height:100%;z-index:0;opacity:.5;pointer-events:none}#hero .wrap{position:relative;z-index:1}.h1-line{display:block;overflow:hidden}body.h1done .h1-line{overflow:visible}.h1-line>span{display:block;transform:translateY(105%);transition:transform 1s cubic-bezier(.16,1,.3,1)}body.loaded .h1-line>span{transform:none}.h1-line:nth-child(2)>
25span{transition-delay:.09s}.h1-line:nth-child(3)>span{transition-delay:.18s}.grad{background:linear-gradient(100deg,var(--ember) 0%,var(--amber) 55%,var(--amber-hi) 100%);-webkit-background-clip:text;background-clip:text;color:transparent}.hero-sub{margin-top:30px;max-width:58ch}.hero-cta{display:flex;gap:14px;margin-top:38px;flex-wrap:wrap}.hero-meta{display:flex;gap:34px;margin-top:56px;flex-wrap:wrap;color:var(--tx-3);font-size:.85rem}.hero-meta b{display:block;color:var(--tx);font-size:1.45rem;font-weight:600;letter-spacing:-.02em;margin-bottom:2px}.scrollcue{position:absolute;bottom:26px;right:30px;left:auto;transform:none;color:var(--tx-3);font-size:.74rem;letter-spacing:.18em;text-transform:uppercase;display:flex;flex-direction:column;align-items:center;gap:10px;z-index:1}.scrollcue i{width:1px;height:36px;background:linear-gradient(var(--tx-3),transparent);animation:cue 2.2s ease-in-out infinite}@keyframes cue{0%,100%{opacity:.25;transform:scaleY(.6)}50%{opacity:1;transform:scaleY(1)}}@media(max-height:760px),(max-width:700px){.scrollcue{display:none}}.cards{display:grid;gap:18px;margin-top:50px}.c3{grid-template-columns:repeat(3,1fr)}@media(max-width:900px){.c3{grid-template-columns:1fr}}.card{background:linear-gradient(180deg,var(--surface),rgba(21,23,28,.5));border:1px solid var(--line);border-radius:var(--r);padding:30px;transition:border-color .3s,transform .3s}.card:hover{border-color:var(--line-2);transform:translateY(-3px)}.card h3{margin-bottom:11px}.card p{color:var(--tx-2);font-size:.98rem;font-weight:300}.card .ix{width:40px;height:40px;border-radius:11px;display:grid;place-items:center;margin-bottom:20px;background:rgba(245,166,35,.09);border:1px solid rgba(245,166,35,.22)}.card .ix svg{width:20px;height:20px;stroke:var(--amber);fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}.picker{display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-top:46px}@media(max-width:900px){.picker{grid-template-columns:1fr}}.vopt{position:relative;text-align:left;cursor:pointer;border-radius:var(--r);padding:28px 26px 26px;background:linear-gradient(180deg,var(--surface),rgba(21,23,28,.5));border:1px solid var(--line);color:var(--tx);font-family:inherit;font-size:1rem;transition:all .28s cubic-bezier(.16,1,.3,1)}.vopt:hover{border-color:var(--line-2);transform:translateY(-3px)}.vopt[aria-pressed="true"]{border-color:rgba(245,166,35,.55);background:linear-gradient(180deg,rgba(245,166,35,.09),rgba(21,23,28,.6));box-shadow:0 0 0 1px rgba(245,166,35,.2),0 18px 46px -22px rgba(245,166,35,.5)}.vopt .tick{position:absolute;top:20px;right:20px;width:23px;height:23px;border-radius:50%;border:1.5px solid var(--line-2);display:grid;place-items:center;transition:all .28s}.vopt[aria-pressed="true"] .tick{background:var(--amber);border-color:var(--amber)}.vopt .tick svg{width:12px;height:12px;stroke:#201604;stroke-width:3;fill:none;stroke-linecap:round;stroke-linejoin:round;opacity:0;transform:scale(.5);transition:all .28s}.vopt[aria-pressed="true"] .tick svg{opacity:1;transform:none}.vopt .vt{font-weight:600;font-size:1.15rem;letter-spacing:-.02em;margin-bottom:7px;padding-right:34px}.vopt .vd{color:var(--tx-2);font-size:.93rem;font-weight:300;line-height:1.55}.vopt .vs{margin-top:16px;font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;color:var(--tx-3)}.pickhint{margin-top:20px;font-size:.9rem;color:var(--tx-3);display:flex;align-items:center;gap:9px}.pickhint svg{width:15px;height:15px;stroke:var(--tx-3);fill:none;stroke-width:1.7}.vpanel{border:1px solid var(--line);border-radius:18px;margin-top:20px;background:linear-gradient(180deg,var(--surface),rgba(16,17,22,.6));transition:opacity .45s,filter .45s}.vpanel.dim{opacity:.34;filter:saturate(.3)}.vpanel>.vpanel-h{border-radius:17px 17px 0 0}.vpanel>.vstats{border-radius:0 0 17px 17px}.vpanel>.vstats{overflow:hidden}.vpanel-h{display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:28px;border-bottom:1px solid var(--line);flex-wrap:wrap}.vh-t{font-size:1.32rem;font-weight:600;letter-spacing:-.02em}.vh-s{color:var(--tx-2);font-size:.95rem;font-weight:300;margin-top:6px;max-width:62ch}.badge{font-size:.72rem;letter-spacing:.12em;text-transform:uppercase;padding:6px 12px;border-radius:999px;border:1px solid var(--line-2);color:var(--tx-2);white-space:nowrap}.badge.on{border-color:rgba(245,166,35,.5);color:var(--amber);background:rgba(245,166,35,.08)}.scrolly{position:relative}.scrolly>.dgm{position:sticky;top:74px;min-height:min(700px,calc(100vh - 120px))}.scrolly>.dgm>.dgm-stage{min-height:min(700px,calc(100vh - 120px))}@media(min-width:981px){.scrolly[data-steps="3"]{height:300vh}.scrolly[data-steps="4"]{height:390vh}.scrolly[data-steps="5"]{height:480vh}}.sprog{display:flex;gap:6px;margin-bottom:18px}.sprog i{height:2px;flex:1;background:var(--line-2);border-radius:2px;overflow:hidden;position:relative}.sprog i:after{content:"";position:absolute;inset:0;width:0;background:linear-gradient(90deg,#FF3D00,#FF6A00);box-shadow:0 0 12px rgba(255,106,0,.9);transition:width .25s linear}.sprog i.done:after{width:100%}.sprog i.now:after{width:var(--pw,0%)}@media(max-width:980px){.sprog{display:none}}.dgm{display:grid;grid-template-columns:1.15fr .85fr;gap:0;border-bottom:1px solid var(--line)}@media(max-width:980px){.dgm{grid-template-columns:1fr}}.dgm-stage{position:relative;background:radial-gradient(ellipse 80% 70% at 50% 40%,#141115,#08090B);border-right:1px solid var(--line);min-height:340px;display:grid;place-items:center;padding:14px;overflow:hidden}@media(max-width:980px){.dgm-stage{border-right:none;border-bottom:1px solid var(--line)}}.dgm-stage:before{content:"";position:absolute;inset:0;pointer-events:none;opacity:.5;background-image:linear-gradient(rgba(255,106,0,.075) 1px,transparent 1px),linear-gradient(90deg,rgba(255,106,0,.075) 1px,transparent 1px);background-size:28px 28px}.dgm-stage svg{width:100%;height:auto;max-height:420px;position:relative;z-index:1;overflow:visible}.dgm-side{padding:26px 28px;display:flex;flex-direction:column}.steps{display:flex;flex-direction:column;gap:2px;flex:1}.step{display:flex;gap:14px;padding:14px 14px 14px 0;border:none;background:none;color:inherit;font-family:inherit;text-align:left;cursor:pointer;border-top:1px solid var(--line);position:relative;transition:opacity .3s}.step:first-child{border-top:none}.step .sn{width:26px;height:26px;flex:0 0 26px;border-radius:50%;display:grid;place-items:center;font-size:.72rem;font-weight:600;border:1px solid var(--line-2);color:var(--tx-3);transition:all .35s;margin-top:2px}.step[aria-current="true"] .sn{background:var(--amber);border-color:var(--amber);color:#231603}.step .st{display:block;font-weight:500;font-size:1rem;margin-bottom:3px;transition:color .3s}.step .sd{display:block;color:var(--tx-3);font-size:.89rem;font-weight:300;line-height:1.5;max-height:0;overflow:hidden;transition:max-height .45s cubic-bezier(.16,1,.3,1),opacity .35s,margin .35s;opacity:0}
25.step[aria-current="true"] .sd{max-height:320px;opacity:1;margin-top:4px}.step[aria-current="true"] .st{color:var(--amber)}.modes{display:flex;gap:8px;margin-bottom:20px;flex-wrap:wrap}.scrollhint{font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--tx-3);display:flex;align-items:center;gap:9px;margin-bottom:16px}.scrollhint b{color:#FF7F1F;font-weight:600;letter-spacing:.06em}@media(max-width:980px){.scrollhint{display:none}}.mode{padding:9px 15px;border-radius:9px;border:1px solid var(--line-2);background:rgba(255,255,255,.03);color:var(--tx-2);font-family:inherit;font-size:.86rem;cursor:pointer;transition:all .25s;font-weight:500}.mode:hover{border-color:#37506A;color:var(--tx)}.mode[aria-pressed="true"]{background:var(--amber);border-color:var(--amber);color:#231603;font-weight:600}.dgm-note{margin-top:auto;padding-top:18px;border-top:1px solid var(--line);color:var(--tx-3);font-size:.85rem;line-height:1.55}.readout{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78rem;color:var(--tx-3);letter-spacing:.04em;margin-top:14px;min-height:1.2em}.readout b{color:var(--amber);font-weight:600}.vstats{display:flex;gap:38px;padding:22px 28px;flex-wrap:wrap}.vstats div{font-size:.82rem;color:var(--tx-3)}.vstats b{display:block;color:var(--tx);font-size:1.2rem;font-weight:600;letter-spacing:-.02em;margin-bottom:1px}.fly{display:grid;grid-template-columns:1.05fr .95fr;gap:56px;align-items:center;margin-top:54px}@media(max-width:960px){.fly{grid-template-columns:1fr;gap:40px}}.flystage{position:relative;aspect-ratio:1;max-width:500px;width:100%;margin:0 auto}.flystage svg{width:100%;height:100%;overflow:visible}.fw-ring{fill:none;stroke:#1E2126;stroke-width:1.5}.fw-arc{fill:none;stroke:url(#flyGrad);stroke-width:2.5;stroke-linecap:round}.fw-node circle{fill:var(--bg-2);stroke:#3A3F47;stroke-width:1.8;transition:stroke .45s,fill .45s}.fw-node.on circle{stroke:var(--amber);fill:#2A1D05}.fw-node text{fill:var(--tx-3);font-size:11.5px;font-weight:500;letter-spacing:.055em;text-transform:uppercase;transition:fill .45s}.fw-node.on text{fill:var(--tx)}.fw-core .k{fill:var(--tx);font-size:38px;font-weight:600;letter-spacing:-.02em;text-anchor:middle}.fw-core .l{fill:var(--tx-3);font-size:11px;letter-spacing:.14em;text-transform:uppercase;text-anchor:middle}.flyrow{display:flex;gap:16px;padding:15px 0;border-top:1px solid var(--line)}.flyrow .n{font-size:.76rem;color:var(--amber);font-weight:600;letter-spacing:.1em;padding-top:3px;min-width:26px}.flyrow .t{font-weight:500;margin-bottom:3px;font-size:1rem}.flyrow .d{color:var(--tx-2);font-size:.92rem;font-weight:300;line-height:1.55}#calc{padding-bottom:126px}.calcbox{margin-top:50px;border:1px solid var(--line);border-radius:20px;overflow:hidden;background:linear-gradient(180deg,var(--surface),rgba(16,17,22,.78));box-shadow:0 40px 90px -50px rgba(0,0,0,.9)}.calcgrid{display:grid;grid-template-columns:1fr 1fr}@media(max-width:960px){.calcgrid{grid-template-columns:1fr}}.calcin{padding:36px;border-right:1px solid var(--line)}@media(max-width:960px){.calcin{border-right:none;border-bottom:1px solid var(--line)}}.calcout{padding:36px;background:rgba(9,10,13,.55)}.field{margin-bottom:28px}.field label{display:flex;align-items:baseline;justify-content:space-between;gap:14px;font-size:.93rem;color:var(--tx-2);margin-bottom:13px}.field .val{font-size:1.1rem;font-weight:600;color:var(--tx);letter-spacing:-.02em;font-variant-numeric:tabular-nums}input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:3px;border-radius:3px;background:var(--line-2);outline:none}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:20px;height:20px;border-radius:50%;background:var(--amber);cursor:pointer;border:3px solid var(--bg);box-shadow:0 0 0 1px var(--amber),0 4px 14px -3px rgba(245,166,35,.8)}input[type=range]::-moz-range-thumb{width:20px;height:20px;border-radius:50%;background:var(--amber);cursor:pointer;border:3px solid var(--bg)}.hero-fig{font-size:clamp(2.9rem,6.4vw,4rem);font-weight:600;letter-spacing:-.038em;line-height:1;background:linear-gradient(100deg,var(--amber-hi),var(--amber));-webkit-background-clip:text;background-clip:text;color:transparent}.hero-fig-l{font-size:.78rem;letter-spacing:.15em;text-transform:uppercase;color:var(--tx-3);margin-bottom:14px}.hero-fig-s{color:var(--tx-2);font-size:.93rem;font-weight:300;margin-top:14px;line-height:1.6}.bd{margin-top:30px}.bdrow{margin-bottom:16px}.bdrow .bl{display:flex;justify-content:space-between;align-items:baseline;gap:12px;font-size:.88rem;margin-bottom:7px}.bdrow .bl .nm{color:var(--tx-2);display:flex;align-items:center;gap:9px}.bdrow .bl .nm i{width:9px;height:9px;border-radius:2px;display:block;flex:0 0 9px}.bdrow .bl .amt{color:var(--tx);font-weight:600;font-variant-numeric:tabular-nums}.track{height:9px;background:rgba(255,255,255,.05);border-radius:0 4px 4px 0;overflow:hidden}.track i{display:block;height:100%;border-radius:0 4px 4px 0;transition:width .55s cubic-bezier(.16,1,.3,1)}.assump{margin-top:24px;padding-top:20px;border-top:1px solid var(--line);font-size:.82rem;color:var(--tx-3);line-height:1.6}.assump summary{cursor:pointer;color:var(--tx-2);font-size:.86rem;margin-bottom:10px}.gate{border-top:1px solid var(--line);padding:32px 36px;background:rgba(245,166,35,.04)}.gt{font-size:1.2rem;font-weight:600;letter-spacing:-.02em;margin-bottom:6px}.gs{color:var(--tx-2);font-size:.94rem;font-weight:300;margin-bottom:22px;max-width:66ch}.gform{display:grid;grid-template-columns:1.2fr 1fr auto;gap:12px}@media(max-width:800px){.gform{grid-template-columns:1fr}}.gform input{background:var(--bg);border:1px solid var(--line-2);border-radius:11px;padding:14px 16px;color:var(--tx);font-family:inherit;font-size:.96rem;outline:none;transition:border-color .2s,box-shadow .2s;width:100%}.gform input:focus{border-color:var(--amber);box-shadow:0 0 0 3px rgba(245,166,35,.13)}.gform input::placeholder{color:var(--tx-3)}.gnote{margin-top:14px;font-size:.8rem;color:var(--tx-3);display:flex;align-items:center;gap:8px}.gnote svg{width:14px;
25height:14px;stroke:var(--tx-3);fill:none;stroke-width:1.7}.gdone{display:none}.gdone.on{display:block}.calcout{position:relative}.calcout.locked .hero-fig,.calcout.locked .hero-fig-s,.calcout.locked .bd{filter:blur(16px);opacity:.75;pointer-events:none;user-select:none;transition:filter .6s,opacity .6s}.calcout:not(.locked) .hero-fig,.calcout:not(.locked) .hero-fig-s,.calcout:not(.locked) .bd{filter:blur(0);opacity:1;transition:filter .6s,opacity .6s}.lockveil{position:absolute;inset:0;z-index:2;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;gap:12px;padding:34px}.calcout:not(.locked) .lockveil{display:none}.lockveil svg{width:30px;height:30px;filter:drop-shadow(0 0 14px rgba(255,106,0,.6))}.lv-t{font-weight:600;font-size:1.18rem;letter-spacing:-.02em}.lv-s{color:var(--tx-2);font-size:.92rem;font-weight:300;max-width:36ch;line-height:1.55}.gdone .dt{font-size:1.22rem;font-weight:600;letter-spacing:-.02em;margin-bottom:8px;color:var(--amber)}.gdone .ds{color:var(--tx-2);font-size:.95rem;font-weight:300}.recap{margin-top:18px;display:flex;flex-wrap:wrap;gap:8px}.chip{font-size:.78rem;padding:6px 12px;border-radius:999px;border:1px solid var(--line-2);color:var(--tx-2);background:rgba(255,255,255,.02)}.proof{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--line);border:1px solid var(--line);border-radius:var(--r);overflow:hidden;margin-top:50px}@media(max-width:800px){.proof{grid-template-columns:repeat(2,1fr)}}.proof div{background:var(--surface);padding:30px 26px}.proof b{display:block;font-size:2rem;font-weight:600;letter-spacing:-.03em;margin-bottom:5px;background:linear-gradient(100deg,var(--ember),var(--amber-hi));-webkit-background-clip:text;background-clip:text;color:transparent}.proof span{color:var(--tx-3);font-size:.85rem}.finale{text-align:center;padding:116px 0 136px}.finale h2{max-width:20ch;margin:0 auto 22px}.finale .lede{margin:0 auto}.finale .hero-cta{justify-content:center}footer{border-top:1px solid var(--line);padding:44px 0;color:var(--tx-3);font-size:.87rem}.frow{display:flex;justify-content:space-between;gap:24px;flex-wrap:wrap;align-items:center}body{background:radial-gradient(ellipse 120% 70% at 50% -10%,rgba(255,106,0,.16),transparent 60%),radial-gradient(ellipse 90% 50% at 100% 40%,rgba(255,61,0,.09),transparent 65%),var(--bg);background-attachment:fixed}.grad{background:linear-gradient(100deg,#FF3D00 0%,#FF6A00 42%,#FFA33D 72%,#FFD9A8 100%);background-size:220% 100%;-webkit-background-clip:text;background-clip:text;color:transparent;animation:gshift 7s ease-in-out infinite;filter:drop-shadow(0 0 34px rgba(255,106,0,.42))}@keyframes gshift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}.eyebrow{color:#FF7F1F;text-shadow:0 0 22px rgba(255,106,0,.55)}.eyebrow:before{background:linear-gradient(90deg,transparent,#FF6A00);box-shadow:0 0 12px rgba(255,106,0,.8)}.btn-p{background:linear-gradient(180deg,#FF9433,#FF6A00 55%,#FF5200);color:#1A0A00;box-shadow:0 0 0 1px rgba(255,150,60,.5),0 10px 34px -8px rgba(255,106,0,.85),0 0 60px -14px rgba(255,106,0,.9)}.btn-p:hover{transform:translateY(-2px);box-shadow:0 0 0 1px rgba(255,170,90,.7),0 16px 46px -8px rgba(255,106,0,1),0 0 80px -12px rgba(255,106,0,1)}.btn-s:hover{border-color:rgba(255,106,0,.55);box-shadow:0 0 30px -12px rgba(255,106,0,.8);transform:translateY(-2px)}.hero-meta b{background:linear-gradient(100deg,#FF6A00,#FFA33D);-webkit-background-clip:text;background-clip:text;color:transparent;filter:drop-shadow(0 0 18px rgba(255,106,0,.4))}.card{position:relative}.card:after{content:"";position:absolute;inset:0;border-radius:var(--r);pointer-events:none;opacity:0;transition:opacity .35s;box-shadow:inset 0 1px 0 rgba(255,150,60,.28),0 0 44px -12px rgba(255,106,0,.75)}.card:hover:after{opacity:1}.card:hover{border-color:rgba(255,106,0,.42);transform:translateY(-5px)}.card .ix{background:linear-gradient(180deg,rgba(255,106,0,.22),rgba(255,61,0,.07));border-color:rgba(255,106,0,.45);box-shadow:0 0 26px -8px rgba(255,106,0,.75)}.card .ix svg{stroke:#FF8A2B}.vopt:hover{border-color:rgba(255,106,0,.45);box-shadow:0 0 40px -16px rgba(255,106,0,.8);transform:translateY(-5px)}.vopt[aria-pressed="true"]{border-color:rgba(255,106,0,.85);background:linear-gradient(180deg,rgba(255,106,0,.16),rgba(19,19,23,.7));box-shadow:0 0 0 1px rgba(255,106,0,.55),0 0 70px -18px rgba(255,106,0,1)}.vopt[aria-pressed="true"] .tick{box-shadow:0 0 22px rgba(255,106,0,.95)}.vopt[aria-pressed="true"] .vt{color:#FF9433}.badge.on{border-color:rgba(255,106,0,.75);color:#FF9433;background:rgba(255,106,0,.14);box-shadow:0 0 24px -8px rgba(255,106,0,.85)}.mode[aria-pressed="true"]{background:linear-gradient(180deg,#FF9433,#FF6A00);border-color:#FF6A00;color:#1A0A00;box-shadow:0 0 34px -8px rgba(255,106,0,.95)}.mode:hover{border-color:rgba(255,106,0,.5);box-shadow:0 0 24px -12px rgba(255,106,0,.8)}.step[aria-current="true"] .sn{box-shadow:0 0 22px rgba(255,106,0,.9)}.step[aria-current="true"] .st{color:#FF9433}.readout b{color:#FF8A2B;text-shadow:0 0 16px rgba(255,106,0,.6)}.vpanel{transition:opacity .45s,filter .45s,box-shadow .45s,border-color .45s}.vpanel:not(.dim){border-color:rgba(255,106,0,.20);box-shadow:0 0 90px -50px rgba(255,106,0,.9)}.dgm-stage{background:radial-gradient(ellipse 78% 68% at 50% 42%,#1A1113,#08080A)}.dgm-stage:before{background-image:linear-gradient(rgba(255,106,0,.08) 1px,transparent 1px),linear-gradient(90deg,rgba(255,106,0,.08) 1px,transparent 1px);opacity:.6}.hero-fig{background:linear-gradient(100deg,#FF3D00,#FF6A00 45%,#FFA33D);-webkit-background-clip:text;background-clip:text;color:transparent;filter:drop-shadow(0 0 40px rgba(255,106,0,.6))}.hero-fig.bump{animation:bump .45s cubic-bezier(.34,1.56,.64,1)}@keyframes bump{0%{transform:scale(1)}38%{transform:scale(1.055)}100%{transform:scale(1)}}.calcbox{border-color:rgba(255,106,0,.22);box-shadow:0 40px 120px -60px rgba(255,106,0,.75),0 0 0 1px rgba(255,106,0,.07)}input[type=range]{background:linear-gradient(90deg,rgba(255,106,0,.55),var(--line-2))}input[type=range]::-webkit-slider-thumb{background:#FF6A00;box-shadow:0 0 0 1px #FF6A00,0 0 22px rgba(255,106,0,1)}input[type=range]::-moz-range-thumb{background:#FF6A00;box-shadow:0 0 18px rgba(255,106,0,.9)}.track i{box-shadow:0 0 18px -3px currentColor}.gate{background:linear-gradient(180deg,rgba(255,106,0,.09),rgba(255,106,0,.02))}.gform input:focus{border-color:#FF6A00;box-shadow:0 0 0 3px rgba(255,106,0,.22),0 0 30px -10px rgba(255,106,0,.9)}.chip{border-color:rgba(255,106,0,.35);color:#FFB877;background:rgba(255,106,0,.09)}.gdone .dt{color:#FF8A2B;text-shadow:0 0 26px rgba(255,106,0,.6)}.proof b{background:linear-gradient(100deg,#FF3D00,#FFA33D);-webkit-background-clip:text;background-clip:text;color:transparent;filter:drop-shadow(0 0 22px rgba(255,106,0,.45))}.proof div{transition:background .3s,box-shadow .3s}.proof div:hover{background:#191419;box-shadow:inset 0 0 40px -18px rgba(255,106,0,.9)}.logo .wm .w1 em{color:#FF7F1F;text-shadow:0 0 18px rgba(255,106,0,.6)}.spine-live{stroke-width:3;filter:drop-shadow(0 0 8px rgba(255,106,0,.9))}.spine-node.on{fill:#3A1200;filter:drop-shadow(0 0 10px rgba(255,106,0,.9))}.fw-arc{stroke-width:3.5;filter:drop-shadow(0 0 10px rgba(255,106,0,.8))}.fw-node.on circle{fill:#3A1200;filter:drop-shadow(0 0 12px rgba(255,106,0,1))}.fw-core .k{fill:#FF8A2B;filter:drop-shadow(0 0 26px rgba(255,106,0,.7))}.flyrow .n{color:#FF7F1F}.scrollcue i{background:linear-gradient(#FF6A00,transparent);box-shadow:0 0 12px rgba(255,106,0,.8)}
25#toTop{position:fixed;bottom:26px;right:26px;z-index:70;width:46px;height:46px;border-radius:50%;display:grid;place-items:center;background:rgba(26,26,32,.88);border:1px solid var(--line-2);color:#FF8A2B;backdrop-filter:blur(8px);opacity:0;visibility:hidden;transform:translateY(10px);transition:opacity .3s,transform .3s,visibility .3s,border-color .2s,box-shadow .2s}#toTop.show{opacity:1;visibility:visible;transform:none}#toTop:hover{border-color:rgba(255,106,0,.6);box-shadow:0 0 26px -8px rgba(255,106,0,.8)}#toTop svg{width:20px;height:20px}@media (prefers-reduced-motion:reduce){.grad{animation:none}*{animation-duration:.001ms!important;transition-duration:.001ms!important}html{scroll-behavior:auto}.reveal{opacity:1;transform:none}.h1-line>span{transform:none}}@media(max-width:980px){.aurora{overflow:hidden}.scrolly>.dgm{position:static;min-height:0;display:block}.vpanel{margin-left:-26px;margin-right:-26px;border-radius:0;border-left:none;border-right:none}.vpanel>.vpanel-h,.vpanel>.vstats{border-radius:0}.scrolly>.dgm>.dgm-stage{position:sticky;top:64px;z-index:4;min-height:0;height:auto;aspect-ratio:460/320;padding:2px;border-right:none;border-bottom:1px solid var(--line);box-shadow:0 18px 30px -26px #000}.dgm-stage svg{max-height:none;height:100%;width:100%}.dgm-side{padding:18px 18px 24px}.steps{gap:0}.step{padding:15px 0;opacity:.45;transition:opacity .35s}.step[aria-current="true"]{opacity:1}.step .sd{max-height:none;opacity:1;margin-top:5px}.scrollhint{display:flex;margin:14px 0 12px}.sprog{display:flex}.readout{white-space:normal;line-height:1.5}.vstats{gap:18px 26px}.steps .step{min-height:58vh;align-items:flex-start}.dgm-side{padding-bottom:40px}.scrolly[data-steps="3"]>.dgm{min-height:210vh}}@media(max-width:760px){.field label{flex-wrap:wrap;gap:2px 12px}.field .val{margin-left:auto}#toTop{bottom:14px;right:14px;width:40px;height:40px}#toTop svg{width:17px;height:17px}.calcin,.calcout{padding:26px 20px}.gate{padding:26px 20px}.lockveil{padding:24px 18px}.vpanel-h{padding:22px 20px}}section[id],#dx,#gate,#top{scroll-margin-top:88px}#dx{display:none;margin-top:26px}#dx.on{display:block;animation:dxIn .7s cubic-bezier(.16,1,.3,1) both}@keyframes dxIn{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}.dx-head{margin-bottom:26px}.dx-t{font-size:clamp(1.5rem,3.2vw,2.15rem);font-weight:600;letter-spacing:-.03em;line-height:1.15}.dx-t b{background:linear-gradient(100deg,#FF3D00,#FFA33D);-webkit-background-clip:text;background-clip:text;color:transparent;font-weight:600}.dx-s{color:var(--tx-2);font-size:1.02rem;font-weight:300;margin-top:12px;max-width:70ch;line-height:1.6}.dx-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}@media(max-width:900px){.dx-grid{grid-template-columns:1fr}}.dx-card{background:linear-gradient(180deg,var(--surface),rgba(16,17,22,.6));border:1px solid var(--line);border-radius:var(--r);padding:26px 24px;position:relative;overflow:hidden}.dx-card:before{content:"";position:absolute;left:0;top:0;bottom:0;width:2px;background:linear-gradient(180deg,#FF3D00,#FFA33D);opacity:.85}.dx-k{font-size:.72rem;letter-spacing:.15em;text-transform:uppercase;color:var(--tx-3);margin-bottom:12px}.dx-v{font-size:1.16rem;font-weight:600;letter-spacing:-.02em;color:#FF9433;margin-bottom:9px;line-height:1.25}.dx-card p{color:var(--tx-2);font-size:.93rem;font-weight:300;line-height:1.6}.dx-foot{margin-top:22px;display:flex;gap:14px;align-items:center;flex-wrap:wrap;padding:22px 24px;border:1px solid var(--line);border-radius:var(--r);background:linear-gradient(180deg,rgba(255,106,0,.08),rgba(255,106,0,.02))}.dx-foot .ft{font-weight:600;font-size:1.04rem;letter-spacing:-.02em;flex:1;min-width:240px}.dx-foot .fs{color:var(--tx-2);font-size:.9rem;font-weight:300;margin-top:4px}.dx-note{margin-top:16px;font-size:.8rem;color:var(--tx-3);line-height:1.6;max-width:80ch}@media(max-width:760px){.dx-card{padding:22px 20px}.dx-foot{padding:20px}}.navlinks{gap:20px}.navlinks a{white-space:nowrap}@media (max-width:1199px){.navlinks .hide-md{display:none}}.pbeta{position:relative;display:inline-flex;align-items:center;gap:8px;font-size:.84rem;font-weight:600;color:#FFE9D2;text-decoration:none;padding:7px 13px;border-radius:9px;white-space:nowrap;background:linear-gradient(180deg,rgba(255,106,0,.20),rgba(255,61,0,.10));border:1px solid rgba(255,140,60,.55);box-shadow:0 0 0 1px rgba(255,106,0,.12),0 0 26px -6px rgba(255,106,0,.7);transition:all .25s}.pbeta:hover{color:#fff;border-color:#FFA33D;box-shadow:0 0 0 1px rgba(255,163,61,.4),0 0 34px -4px rgba(255,106,0,.95);transform:translateY(-1px)}.pbeta i{width:7px;height:7px;border-radius:50%;background:#FF6A00;box-shadow:0 0 10px #FF6A00;animation:pbPulse 1.8s ease-in-out infinite}.pbeta svg{width:12px;height:12px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}@keyframes pbPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.35;transform:scale(.7)}}@media (prefers-reduced-motion:reduce){.pbeta i{animation:none}}.c4{grid-template-columns:repeat(4,1fr)}@media (max-width:980px){.c4{grid-template-columns:1fr 1fr}}@media (max-width:620px){.c4{grid-template-columns:1fr}}.plive{position:absolute;top:30px;right:26px;
25display:inline-flex;align-items:center;gap:7px;font-size:.66rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#FFB266}.plive i{width:7px;height:7px;border-radius:50%;background:#FF6A00;box-shadow:0 0 10px #FF6A00;animation:pbPulse 1.8s ease-in-out infinite}.plive.soon{color:#FFA33D}.plive.soon i{background:transparent;border:1px solid #FFA33D;box-shadow:none;animation:none}#platform .card h3{margin-bottom:10px}#platform .card p{color:var(--tx-2);font-size:.95rem;font-weight:300;line-height:1.6}.plat-top{display:grid;grid-template-columns:1.1fr .9fr;gap:48px;align-items:center}.plat-dome{max-width:440px}@media (max-width:980px){.plat-top{grid-template-columns:1fr}.plat-dome{justify-self:center;max-width:400px}}</style>
26<!-- ============================================================
27     Solar Survey AI - unified measurement tag
28     Paste this block immediately before the closing head tag on EVERY page.
29     Built 2026-09-21. Multi-touch revision.
30
31     Contents:
32       1. PostHog (direct endpoints; the t.solarsurvey.ai reverse
33          proxy does not resolve, do not put it back)
34       2. Google Ads gtag
35       3. LinkedIn Insight Tag, partner 5964764
36       4. Multi-touch journey engine
37       5. Funnel events
38       6. Calendly link decoration
39
40     Event names are the ruled set: the names PostHog has actually
41     received and that the 48 existing insights are built on.
42     ============================================================ -->
43
44<!-- 1. PostHog ------------------------------------------------ -->
45<script src="/attribution.js"></script>
vendor: 1 bytes, line 45
45
46<script>
47!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
48
49posthog.init('phc_QBBcSNauSKwPTl00EpKP6uJufDLLrbFe6r7WyHaqEQu', {
50  api_host: 'https://us.i.posthog.com',
51  ui_host: 'https://us.posthog.com',
52  defaults: '2026-03-30',
53  person_profiles: 'identified_only',
54
55  // Each already has historical data in project 322256, so the
56  // existing insights depend on them. Leave them on.
57  capture_pageview:  true,
58  capture_pageleave: true,
59  autocapture:       true,
60  capture_performance: { web_vitals: true },
61  capture_exceptions: true,
62  session_recording: { maskAllInputs: true },
63
64  persistence: 'localStorage+cookie'
65});
66</script>
66
67
68<!-- 2. Google Ads --------------------------------------------- -->
vendor: 64 bytes, lines 68-69
68
69<script async src="https://www.googletagmanager.com/gtag/js?id=
69AW-10835937242
vendor: 12 bytes, line 69
69"></script>
70<script>
71  
vendor: 133 bytes, lines 71-74
71window.dataLayer = window.dataLayer || [];
72  function gtag(){dataLayer.push(arguments);}
73  gtag('js', new Date());
74  gtag('config', '
74AW-10835937242
vendor: 6 bytes, lines 74-75
74');
75  
75gtag('config', 'G-DQQ8Q96C5M');
76</script>
76
77
78<!-- 3. LinkedIn Insight Tag ------------------------------------ -->
79<!-- Partner 5964764. Conversions, Insight Tag event-specific:
80     31104009 booking CTA click (intent), 31104001 booking. -->
81<script type="text/javascript">
82_linkedin_partner_id = "5964764";
83window._linkedin_data_partner_ids = window._linkedin_data_partner_ids || [];
84window._linkedin_data_partner_ids.push(_linkedin_partner_id);
85</script>
vendor: 1 bytes, line 85
85
86<script type="text/javascript">
87(function(l){if(!l){window.lintrk=function(a,b){window.lintrk.q.push([a,b])};window.lintrk.q=[]}
88var s=document.getElementsByTagName("script")[0];var b=document.createElement("script");
89b.type="text/javascript";b.async=true;
90b.src="https://snap.licdn.com/li.lms-analytics/insight.min.js";
91s.parentNode.insertBefore(b,s);})(window.lintrk);
92</script>
92
93<noscript><img height="1" width="1" style="display:none;" alt=""
94src="https://px.ads.linkedin.com/collect/?pid=5964764&fmt=gif" /></noscript>
95
96<!-- 4-6. Multi-touch journey, funnel events, Calendly ---------- -->
97<script>
98(function () {
99  "use strict";
100
101  // ============================================================
102  // FULL FUNNEL EVENTS
103  // The ruled set: what PostHog has received and what the 48
104  // existing insights are built on. Do not rename.
105  //
106  //   1  $pageview                (automatic)
107  //   2  landing_page_viewed      reached a page that sells
108  //   3  page_engaged             50% scroll or 30s active
109  //   4  booking_section_reached  a booking CTA scrolled into view
110  //   5  booking_cta_click        clicked through to Calendly
111  //   6  lead_submitted           gave an email to the calculator
112  //   7  booking_confirmation_page_viewed  diagnostic only; no confirmed conversion
113  //
114  // Sales outcome integration: owner dependency, not verified by website:
115  //   crm_lead_created, crm_opportunity_created, crm_opportunity_won
116  //
117  // Also: scroll_depth, time_on_page, portal_signin_click
118  // ============================================================
119
120  var JOURNEY_KEY = "ssai_journey";
121  var SESSION_KEY = "ssai_sess";
122  var MAX_TOUCHES = 30;
123
124  var CLICK_IDS = ["gclid","wbraid","gbraid","li_fat_id","msclkid",
125                   "fbclid","ttclid","twclid","epik","rdt_cid"];
126
127  // click id -> the channel it proves, when no utm_source is present
128  var CLICK_ID_CHANNEL = {
129    gclid: "google", wbraid: "google", gbraid: "google",
130    li_fat_id: "linkedin", msclkid: "bing", fbclid: "facebook",
131    ttclid: "tiktok", twclid: "x", epik: "pinterest", rdt_cid: "reddit"
132  };
133
134  var REFERRER_MAP = [
135    [/(^|\.)linkedin\.com$/,    "linkedin"],
136    [/(^|\.)lnkd\.in$/,         "linkedin"],
137    [/(^|\.)youtube\.com$/,     "youtube"],
138    [/(^|\.)youtu\.be$/,        "youtube"],
139    [/(^|\.)facebook\.com$/,    "facebook"],
140    [/(^|\.)instagram\.com$/,   "instagram"],
141    [/(^|\.)(x|twitter)\.com$/, "x"],
142    [/(^|\.)t\.co$/,            "x"],
143    [/(^|\.)reddit\.com$/,      "reddit"],
144    [/(^|\.)tiktok\.com$/,      "tiktok"],
145    [/(^|\.)google\./,          "google-organic"],
146    [/(^|\.)bing\.com$/,        "bing-organic"],
147    [/(^|\.)duckduckgo\.com$/,  "duckduckgo"],
148    [/(^|\.)perplexity\.ai$/,   "perplexity"],
149    [/(^|\.)chatgpt\.com$/,     "chatgpt"],
150    [/(^|\.)claude\.ai$/,       "claude"]
151  ];
152
153  function read(k, d) {
154    try { var v = localStorage.getItem(k); return v ? JSON.parse(v) : d; }
155    catch (e) { return d; }
156  }
157  function write(k, v) {
158    try { localStorage.setItem(k, JSON.stringify(v)); } catch (e) {}
159  }
160
161  var params = new URLSearchParams(window.location.search);
162  function q(n) { return window.SSAI_ATTR ? window.SSAI_ATTR.query(n) : null; }
163
164  var path = window.location.pathname
165               .replace(/\/index\.html$/, "/")
166               .replace(/\.html$/, "")
167               .replace(/(.)\/+$/, "$1") || "/";
168
169  function referrerHost() {
170    var r = document.referrer;
171    if (!r) return null;
172    try {
173      var h = new URL(r).hostname.replace(/^www\./, "");
174      return h === window.location.hostname.replace(/^www\./, "") ? null : h;
175    } catch (e) { return null; }
176  }
177  function referrerChannel() {
178    var h = referrerHost();
179    if (!h) return null;
180    for (var i = 0; i < REFERRER_MAP.length; i++) {
181      if (REFERRER_MAP[i][0].test(h)) return REFERRER_MAP[i][1];
182    }
183    return h;
184  }
185
186  // ---- classify this hit -------------------------------------
187  var clickIds = {};
188  CLICK_IDS.forEach(function (c) { var v = q(c); if (v) clickIds[c] = v; });
189  var clickIdKeys = Object.keys(clickIds);
190
191  var utmSource = q("utm_source");
192  var clickChannel = null;
193  for (var i = 0; i < clickIdKeys.length; i++) {
194    if (CLICK_ID_CHANNEL[clickIdKeys[i]]) { clickChannel = CLICK_ID_CHANNEL[clickIdKeys[i]]; break; }
195  }
196
197  // Normalise. Platforms append whatever case they like: LinkedIn's own
198  // account-level tracking parameter sends utm_source=LinkedIn, which would
199  // otherwise split LinkedIn into two channels in every report and make
200  // ever_organic_social false, since that test compares against lowercase.
201  function norm(s) { return String(s || "").trim().toLowerCase(); }
202  var channel = norm(utmSource || clickChannel || referrerChannel() || "direct");
203  var medium  = norm(q("utm_medium") || (clickIdKeys.length ? "cpc"
204                : (referrerHost() ? "referral" : "none")));
205  var postId  = q("post_id") || q("ssai_pid");
206  var tagged  = !!(utmSource || clickIdKeys.length);
207
208  var touch = {
209    t:    new Date().toISOString(),
210    ch:   channel,
211    med:  medium,
212    camp: q("utm_campaign"),
213    term: q("utm_term"),
214    cont: q("utm_content"),
215    post: postId,
216    ref:  referrerHost(),
217    lp:   path,
218    ids:  clickIdKeys.length ? clickIds : null,
219    claim: q("claim_id"), variant: q("asset_variant"),
220    raw_utm: window.SSAI_ATTR ? window.SSAI_ATTR.parse(window.location.search).duplicates : {}
221  };
222
223  // ---- decide whether this is a NEW touch --------------------
224  // A touch is a distinct arrival, not a page view. Record when:
225  //   a) there is no history yet
226  //   b) this hit carries tracking params AND differs from the last
227  //   c) it is a new browser session arriving from somewhere external
228  var journey = read(JOURNEY_KEY, null) || { touches: [], first_seen: touch.t };
229  if (!Array.isArray(journey.touches)) journey.touches = [];
230  journey.touches = journey.touches.filter(function(t) { return t && typeof t === "object"; });
231  var last = journey.touches.length ? journey.touches[journey.touches.length - 1] : null;
232  var newSession = true;
233  try { newSession = !sessionStorage.getItem(SESSION_KEY); } catch (e) {}
234  try { sessionStorage.setItem(SESSION_KEY, "1"); } catch (e) {}
235
236  function differs(a, b) {
237    if (!a) return true;
238    return a.ch !== b.ch || a.med !== b.med || a.camp !== b.camp || a.post !== b.post || a.cont !== b.cont || a.claim !== b.claim || a.variant !== b.variant || a.term !== b.term || JSON.stringify(a.ids || {}) !== JSON.stringify(b.ids || {}) || JSON.stringify(a.raw_utm || {}) !== JSON.stringify(b.raw_utm || {});
239  }
240
241  var isNewTouch =
242      !last ||
243      (tagged && differs(last, touch)) ||
244      (newSession && referrerHost() && differs(last, touch));
245
246  if (isNewTouch) {
247    journey.touches.push(touch);
248    if (journey.touches.length >
248 MAX_TOUCHES) {
249      // keep the first touch, drop the oldest middle one
250      journey.touches.splice(1, 1);
251      journey.truncated = true;
252    }
253    write(JOURNEY_KEY, journey);
254  }
255
256  // ---- derive the properties that answer real questions -------
257  var touches = journey.touches;
258  window.__ssaiJourney = journey;
259  var first = (window.SSAI_ATTR && window.SSAI_ATTR.firstTouch(journey)) || touch;
260  var lastT = touches[touches.length - 1] || touch;
261
262  // every click id ever seen, across all touches
263  var allIds = {};
264  touches.forEach(function (t) {
265    if (t.ids) Object.keys(t.ids).forEach(function (k) { allIds[k] = t.ids[k]; });
266  });
267
268  // distinct channels, in order, deduped consecutively
269  var seq = [];
270  touches.forEach(function (t) { if (seq[seq.length - 1] !== t.ch) seq.push(t.ch); });
271
272  var uniqChannels = seq.filter(function (v, i, a) { return a.indexOf(v) === i; });
273  var daysSinceFirst = Math.round(
274      (Date.now() - new Date(journey.first_seen).getTime()) / 86400000);
275
276  var props = {
277    // first touch
278    first_touch_channel:  first.ch,
279    first_touch_medium:   first.med,
280    first_touch_campaign: first.camp,
281    first_touch_post_id:  first.post,
282    first_touch_landing:  first.lp,
283    first_touch_at:       journey.first_seen,
284
285    // last touch
286    last_touch_channel:   lastT.ch,
287    last_touch_medium:    lastT.med,
288    last_touch_campaign:  lastT.camp,
289    last_touch_post_id:   lastT.post,
290
291    // the whole journey
292    touch_count:    touches.length,
293    channel_path:   seq.join(" > "),          // "linkedin > google > direct"
294    channels_seen:  uniqChannels.sort().join(","),
295    days_since_first_touch: daysSinceFirst,
296    is_multi_touch: uniqChannels.length > 1,
297
298    // has this person EVER arrived via each paid channel
299    ever_google_ads:   !!(allIds.gclid || allIds.wbraid || allIds.gbraid),
300    ever_linkedin_ads: !!allIds.li_fat_id,
301    ever_microsoft_ads:!!allIds.msclkid,
302    ever_meta_ads:     !!allIds.fbclid,
303    ever_paid:         Object.keys(allIds).length > 0,
304    ever_organic_social: uniqChannels.some(function (c) {
305      return ["linkedin","youtube","facebook","instagram","x","reddit","tiktok"].indexOf(c) !== -1;
306    }),
307
308    // click ids, for the offline conversion upload back to the platforms
309    gclid:     allIds.gclid || allIds.wbraid || allIds.gbraid || null,
310    li_fat_id: allIds.li_fat_id || null,
311    msclkid:   allIds.msclkid || null,
312
313    // legacy flat utms, kept so older insights keep working
314    utm_source:   lastT.ch,
315    utm_medium:   lastT.med,
316    utm_campaign: lastT.camp,
317    utm_content:  lastT.cont,
318    utm_term:     lastT.term
319  };
320
321  if (window.posthog) {
322    try {
323      posthog.register(props);
324      // set-once names the existing insights are built on
325      posthog.register_once({
326        initial_utm_source:   first.ch,
327        initial_utm_medium:   first.med,
328        initial_utm_campaign: first.camp,
329        initial_utm_content:  first.cont,
330        initial_utm_term:     first.term,
331        initial_referrer:     first.ref,
332        initial_landing_page: first.lp,
333        ssai_first_channel:   first.ch,
334        ssai_first_post_id:   first.post
335      });
336      // the full ordered journey, as one event, only when it grows
337      if (isNewTouch && touches.length > 1) {
338        posthog.capture("touch_recorded", {
339          touch_number: touches.length,
340          channel: touch.ch,
341          previous_channel: last ? last.ch : null,
342          channel_path: props.channel_path,
343          days_since_first_touch: daysSinceFirst
344        });
345      }
346    } catch (e) {}
347  }
348
349  // ============================================================
350  // FUNNEL EVENTS
351  // ============================================================
352  function cap(name, p) { try { posthog.capture(name, p || {}); } catch (e) {} }
353
354  function ctaPosition(el) {
355    try {
356      var top = el.getBoundingClientRect().top + window.scrollY;
357      var pct = top / (document.documentElement.scrollHeight || 1);
358      return pct < 0.25 ? "hero" : (pct > 0.75 ? "footer" : "mid");
359    } catch (e) { return "unknown"; }
360  }
361
362  var OFFER_PAGES = ["/", "/about", "/commercial-industrial", "/commercial-industrial/design-engineering", "/commercial-industrial/electroluminescence", "/commercial-industrial/site-survey", "/commercial-industrial/thermal-inspection", "/commercial-industrial/visual-inspection", "/company-facts", "/compare", "/features", "/features/3d-photo-maps", "/features/3d-roof-designer", "/features/3d-roof-models", "/features/cash-loan-scenarios", "/features/client-portal", "/features/company-checklists", "/features/company-team-roles", "/features/contextual-job-comments", "/features/display-accessibility", "/features/electricity-market-explorer", "/features/electricity-use-estimates", "/features/equipment-catalog", "/features/equipment-locations", "/features/field-delivery-deadlines", "/features/jarvis-assistant", "/features/jarvis-memory", "/features/mobile-field-capture", "/features/module-layouts", "/features/operations-dashboard", "/features/photo-quality-review", "/features/project-pipeline", "/features/proposal-sharing", "/features/sales-studio", "/features/solar-stock-directory", "/features/sun-shadow-simulator", "/features/survey-pdf-dxf-exports", "/features/survey-sharing", "/features/workspace-navigation", "/inspection", "/land-survey", "/permitting", "/platform", "/residential", "/residential/attic", "/residential/design-engineering", "/residential/electrical", "/residential/roof", "/services", "/shading-analysis", "/site-survey", "/solar-answers", "/solar-site-survey", "/solar-site-survey/arizona", "/solar-site-survey/arizona/phoenix", "/solar-site-survey/arkansas", "/solar-site-survey/arkansas/little-rock", "/solar-site-survey/california", "/solar-site-survey/california/los-angeles", "/solar-site-survey/colorado", "/solar-site-survey/colorado/denver", "/solar-site-survey/connecticut", "/solar-site-survey/connecticut/hartford", "/solar-site-survey/florida", "/solar-site-survey/florida/miami", "/solar-site-survey/florida/tampa", "/solar-site-survey/georgia", "/solar-site-survey/georgia/atlanta", "/solar-site-survey/illinois", "/solar-site-survey/illinois/chicago", "/solar-site-survey/indiana", "/solar-site-survey/indiana/indianapolis", "/solar-site-survey/maine", "/solar-site-survey/maine/portland-maine", "/solar-site-survey/maryland", "/solar-site-survey/maryland/baltimore", "/solar-site-survey/massachusetts", "/solar-site-survey/massachusetts/boston", "/solar-site-survey/michigan", "/solar-site-survey/michigan/detroit", "/solar-site-survey/minnesota", "/solar-site-survey/minnesota/minneapolis", "/solar-site-survey/missouri", "/solar-site-survey/missouri/st-louis", "/solar-site-survey/nevada", "/solar-site-survey/nevada/las-vegas", "/solar-site-survey/new-jersey", "/solar-site-survey/new-jersey/jersey-city", "/solar-site-survey/new-mexico", "/solar-site-survey/new-mexico/albuquerque", "/solar-site-survey/new-yor
362k", "/solar-site-survey/new-york/long-island", "/solar-site-survey/new-york/new-york-city", "/solar-site-survey/north-carolina", "/solar-site-survey/north-carolina/charlotte", "/solar-site-survey/north-carolina/raleigh", "/solar-site-survey/ohio", "/solar-site-survey/ohio/columbus", "/solar-site-survey/oregon", "/solar-site-survey/oregon/portland", "/solar-site-survey/pennsylvania", "/solar-site-survey/pennsylvania/philadelphia", "/solar-site-survey/pennsylvania/pittsburgh", "/solar-site-survey/south-carolina", "/solar-site-survey/south-carolina/charleston", "/solar-site-survey/texas", "/solar-site-survey/texas/houston", "/solar-site-survey/utah", "/solar-site-survey/utah/salt-lake-city", "/solar-site-survey/virginia", "/solar-site-survey/virginia/richmond", "/solar-site-survey/wisconsin", "/solar-site-survey/wisconsin/milwaukee", "/survey-methodology", "/utility-scale", "/utility-scale/construction-monitoring", "/utility-scale/lidar-topography", "/utility-scale/thermal-el-inspection"];
363  // Offer pages only. Field Notes are content, not sell pages, so they are
364  // deliberately excluded here: they still emit $pageview, page_engaged,
365  // scroll_depth, time_on_page, booking_section_reached and booking_cta_click.
366  // ADD ANY NEW SELL PAGE TO THIS ARRAY OR IT WILL NOT ENTER THE FUNNEL.
367  if (OFFER_PAGES.indexOf(path) !== -1) cap("landing_page_viewed", { page: path });
368
369  // engagement
370  var engaged = false, activeSeconds = 0, depthsSeen = {};
371  function markEngaged(why) {
372    if (engaged) return;
373    engaged = true;
374    cap("page_engaged", { page: path, trigger: why });
375  }
376  window.addEventListener("scroll", function () {
377    var scrollable = document.documentElement.scrollHeight - window.innerHeight;
378    if (scrollable <= 0) return;
379    var pct = Math.round((window.scrollY / scrollable) * 100);
380    [25, 50, 75, 100].forEach(function (m) {
381      if (pct >= m && !depthsSeen[m]) {
382        depthsSeen[m] = true;
383        cap("scroll_depth", { page: path, depth: m });
384        if (m >= 50) markEngaged("scroll_50");
385      }
386    });
387  }, { passive: true });
388
389  var ticker = setInterval(function () {
390    if (document.visibilityState !== "visible") return;
391    activeSeconds += 5;
392    if (activeSeconds >= 30) { markEngaged("time_30s"); clearInterval(ticker); }
393  }, 5000);
394
395  // booking section reached
396  var sectionSeen = false;
397  function watchBookingSection() {
398    if (!window.IntersectionObserver) return;
399    var targets = document.querySelectorAll('a[href*="calendly.com"]');
400    if (!targets.length) return;
401    var io = new IntersectionObserver(function (entries) {
402      for (var i = 0; i < entries.length; i++) {
403        if (entries[i].isIntersecting && !sectionSeen) {
404          sectionSeen = true;
405          cap("booking_section_reached", { page: path });
406          io.disconnect();
407          return;
408        }
409      }
410    }, { threshold: 0.5 });
411    Array.prototype.forEach.call(targets, function (t) { io.observe(t); });
412  }
413
414  // clicks
415  document.addEventListener("click", function (ev) {
416    var t = ev.target;
417    if (!t || !t.closest) return;
418
419    var cal = t.closest('a[href*="calendly.com"]');
420    if (cal) {
421      cap("booking_cta_click", {
422        link_text: (cal.textContent || "").trim().slice(0, 80),
423        page: path,
424        cta_position: ctaPosition(cal),
425        engaged_first: engaged,
426        channel_path: props.channel_path,
427        touch_count: props.touch_count
428      });
429      try { window.lintrk && window.lintrk("track", { conversion_id: 31104009 }); } catch (e) {}
430      return;
431    }
432
433    // CrewLink is the Solar Survey AI client portal, so these are returning clients
434    // signing in, not prospects leaking away. Kept out of funnel metrics on purpose.
435    var crew = t.closest('a[href*="app.crewlink.ai"], a[href*="app.solarsurvey.ai"]');
436    if (crew) {
437      cap("portal_signin_click", {
438        page: path, link_text: (crew.textContent || "").trim().slice(0, 80)
439      });
440      return;
441    }
442
443    var ext = t.closest('a[href^="http"]');
444    if (ext) {
445      try {
446        var h = new URL(ext.href).hostname.replace(/^www\./, "");
447        if (h !== window.location.hostname.replace(/^www\./, "")) {
448          cap("outbound_click", { page: path, to_host: h });
449        }
450      } catch (e) {}
451    }
452  }, true);
453
454  // calculator / form submit
455  document.addEventListener("submit", function (ev) {
456    var form = ev.target;
457    if (!form || !form.querySelector) return;
458    var email = form.querySelector('input[type="email"]');
459    if (!email) return;
460    if (form.closest("#ssai-bf")) return;
461    var isCalc = !!form.closest("#calc");
462    cap(isCalc ? "lead_submitted" : "lead_form_submitted", {
463      page: path,
464      form_id: form.id || null,
465      channel_path: props.channel_path,
466      touch_count: props.touch_count,
467      days_since_first_touch: props.days_since_first_touch
468    });
469  }, true);
470
471  // time on page
472  var sentTime = false;
473  function sendTimeOnPage() {
474    if (sentTime) return;
475    sentTime = true;
476    var marks = Object.keys(depthsSeen).map(Number);
477    cap("time_on_page", {
478      page: path,
479      seconds: activeSeconds,
480      max_scroll: marks.length ? Math.max.apply(null, marks) : 0
481    });
482  }
483  window.addEventListener("pagehide", sendTimeOnPage);
484  document.addEventListener("visibilitychange", function () {
485    if (document.visibilityState === "hidden") sendTimeOnPage();
486  });
487
488  // ============================================================
489  // CALENDLY LINK DECORATION
490  // Carries the journey into the booking record, and from there
491  // into Close, so a booking knows its whole path.
492  // ============================================================
493  function decorate() {
494    Array.prototype.forEach.call(
495      document.querySelectorAll('a[href*="calendly.com"]'), function (a) {
496        var url;
497        try { url = new URL(a.href); } catch (e) { return; }
498
499        a.href = window.SSAI_ATTR.decorate(url.toString(), window.SSAI_ATTR.current());
500      });
501  }
502
503  function boot() { decorate(); watchBookingSection(); }
504  if (document.readyState === "loading") {
505    document.addEventListener("DOMContentLoaded", boot);
506  } else { boot(); }
507  setTimeout(boot, 1500);
508})();
509</script>
509
510<!-- ===================== end measurement tag ================== -->
511
512
513
514
515
516
517
518
519<script type="application/ld+json">
520{
521 "@context": "https://schema.org",
522 "@graph": [
523  {
524   "@type": "Organization",
525   "@id": "https://www.solarsurvey.ai/#org",
526   "name": "Solar Survey AI",
527   "url": "https://www.solarsurvey.ai/",
528   "logo": "https://www.solarsurvey.ai/icon-512.png",
529   "description": "Site surveys, engineering and computer-vision inspection for residential, C&I and utility-scale solar.",
530   "areaServed": {
531    "@type": "Country",
532    "name": "United States"
533   }
534  },
535  {
536   "@type": "WebSite",
537   "@id": "https://www.solarsurvey.ai/#website",
538   "url": "https://www.solarsurvey.ai/",
539   "name": "Solar Survey AI",
540   "publisher": {
541    "@id": "https://www.solarsurvey.ai/#org"
542   }
543  }
544 ]
545}
546</script>
546
547<link rel="stylesheet" href="/immersive.css">
548<link rel="stylesheet" href="/skin.css">
549<link rel="icon" href="/favicon.ico" sizes="48x48">
550<link rel="icon" href="/favicon.svg" type="image/svg+xml">
551<link rel="apple-touch-icon" href="/apple-touch-icon.png">
552<link rel="manifest" href="/site.webmanifest">
553<meta name="theme-color" content="#0B0C0F">
554</head>
555<body>
556<div class="aurora"><i></i><i></i><i></i></div>
557<div class="grid-bg"></div>
558<header id="hdr"><div class="wrap nav">
559<a class="logo" href="#top" aria-label="Solar Survey AI home">
560<svg viewBox="0 0 32 32" fill="none" aria-hidden="true">
561<defs>
562<linearGradient id="lgG" x1="6" y1="6" x2="26" y2="26" gradientUnits="userSpaceOnUse">
563<stop stop-color="#FF3D00"/><stop offset="1" stop-color="#FFA33D"/>
564</linearGradient>
565</defs>
566<g class="rt" stroke="#4A4640" stroke-width="1.6" stroke-linecap="round">
567<path d="M2 8V3.6A1.6 1.6 0 0 1 3.6 2H8"/><path d="M24 2h4.4A1.6 1.6 0 0 1 30 3.6V8"/>
568<path d="M30 24v4.4a1.6 1.6 0 0 1-1.6 1.6H24"/><path d="M8 30H3.6A1.6 1.6 0 0 1 2 28.4V24"/>
569</g>
570<rect x="7" y="7" width="18" height="18" rx="3" stroke="url(#lgG)" stroke-width="1.9"/>
571<path d="M16 7.6v16.8M7.6 16h16.8" stroke="url(#lgG)" stroke-width="1.1" opacity=".55"/>
572<rect x="16.9" y="8.9" width="6.2" height="6.2" rx="1.4" fill="#FFE9D2"/>
573<path class="sweep" d="M8.4 16h15.2" stroke="#FF6A00" stroke-width="1.6" stroke-linecap="round"/>
574</svg>
575<span class="wm"><span class="w1">Solar Survey <em>AI</em></span><span class="w2">Field data for solar</span></span>
576</a>
577<nav class="navlinks">
578<span class="hide-sm"><div class="sx-dd"><button class="sx-ddb" aria-haspopup="true" aria-expanded="false">Solutions <svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></svg></button><div class="sx-ddm wide" role="menu"><div class="sx-col"><div class="sx-mh">Residential</div><a href="/residential">Overview<span>Rooftop PV at volume</span></a><a href="/residential/roof">Roof &amp; drone<span>Planes measured on the roof</span></a><a href="/residential/attic">Attic &amp; structure<span>Rafters recorded</span></a><a href="/residential/electrical">Electrical<span>Panel and interconnection</span></a><a href="/residential/design-engineering">Design &amp; engineering<span>Model to stamped set</span></a></div><div class="sx-col"><div class="sx-mh">Commercial &amp; Industrial</div><a href="/commercial-industrial">Overview<span>Big roofs, real structure</span></a><a href="/commercial-industrial/site-survey">On-site survey<span>Roof, structure, switchgear</span></a><a href="/commercial-industrial/design-engineering">Design &amp; engineering<span>PE stamped</span></a><a href="/commercial-industrial/visual-inspection">Visual inspection<span>Module resolution</span></a><a href="/commercial-industrial/thermal-inspection">Thermal inspection<span>Faults as heat</span></a><a href="/commercial-industrial/electroluminescence">Electroluminescence<span>Cell-level cracks</span></a></div><div class="sx-col"><div class="sx-mh">Utility-Scale</div><a href="/utility-scale">Overview<span>Thousands of acres</span></a><a href="/utility-scale/lidar-topography">LiDAR topography<span>Terrain before earthwork</span></a><a href="/land-survey">Land surveying<span>Boundary and topographic</span></a><a href="/utility-scale/construction-monitoring">Construction monitoring<span>Progress, measured</span></a><a href="/utility-scale/thermal-el-inspection">Thermal &amp; EL<span>Every string</span></a></div><div class="sx-col"><div class="sx-mh">Across every scale</div><a href="/site-survey">Site surveys<span>Roof and electrical, one visit</span></a><a href="/permitting">Permitting &amp; engineering<span>Stamped plan sets</span></a><a href="/inspection">Inspection<span>Aerial thermal and EL</span></a><a href="/shading-analysis">Shading analysis<span>SA, TOF and TSRF</span></a><a href="/solar-site-survey">Survey locations<span>State and metro guides</span></a></div><a class="sx-ddall" href="/services">All services &rarr;</a></div></div></span>
579<span class="hide-sm"><div class="sx-dd"><button class="sx-ddb" aria-haspopup="true" aria-expanded="false">Platform <svg viewBox="0 0 24 24">
579<path d="m6 9 6 6 6-6"/></svg></button><div class="sx-ddm" role="menu"><div class="sx-col"><div class="sx-mh">The platform</div><a href="/platform">Platform overview<span>Portal, 3D models, live data</span></a><a href="/features">Feature guides<span>Who it serves and how it works</span></a><a href="/shading-analysis">Shading analysis<span>Move the sun yourself</span></a><a href="/#calc">Cost calculator<span>What bad field data costs you</span></a></div><div class="sx-col"><div class="sx-mh">Sign in</div><a href="https://app.solarsurvey.ai/login">Vanguard Access<span>Invite only · survey data portal</span></a><a href="https://app.crewlink.ai/sign-in">Scheduling &amp; Dispatch<span>CrewLink · all verticals</span></a></div><a class="sx-ddall" href="/platform">Platform overview &rarr;</a></div></div></span>
580<span class="hide-sm"><div class="sx-dd"><button class="sx-ddb" aria-haspopup="true" aria-expanded="false">Resources <svg viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></svg></button><div class="sx-ddm wide" role="menu"><div class="sx-col"><div class="sx-mh">Solar reference</div><a href="/solar-answers">Solar questions answered<span>Residential, commercial and utility-scale</span></a><a href="/survey-methodology">Survey methodology<span>Sources, measurements and limits</span></a><a href="/company-facts">Company facts<span>Official services and capabilities</span></a></div><div class="sx-col"><div class="sx-mh">Field Notes · build vs. buy</div><a href="/field-notes/build-vs-buy">What a survey really costs<span>Part 01 · the full model</span></a><a href="/field-notes/utilization">Utilization is the whole game<span>Part 02 · a real Tuesday</span></a><a href="/field-notes/break-even-headcount">The break-even headcount<span>When hiring pays off</span></a><a href="/field-notes/two-technicians">Two technicians, two roofs<span>Variance, measured</span></a><a href="/field-notes/hybrid-crew">The hybrid crew<span>In-house plus outsourced</span></a></div><div class="sx-col"><div class="sx-mh">Field Notes · reference</div><a href="/field-notes/solar-site-survey-checklist">Site survey checklist<span>Built backwards from rework</span></a><a href="/field-notes/how-long-solar-site-survey">How long a survey takes<span>Hour by hour</span></a><a href="/field-notes/do-solar-panels-need-permit">Do panels need a permit?<span>What goes in the package</span></a><a href="/field-notes/solar-storage-site-survey">Storage site surveys<span>The capture list</span></a><a href="/compare">Compare survey vendors<span>Questions to ask any vendor</span></a></div><a class="sx-ddall" href="/field-notes">All Field Notes &rarr;</a></div></div></span>
581<a href="/about" class="hide-sm hide-md">About</a>
582<a href="#calc" class="btn btn-s btn-sm hide-sm">Cost calculator</a>
583<div class="signin" id="signin">
584<button class="btn btn-p btn-sm" id="signinBtn" aria-haspopup="true" aria-expanded="false">
585<span class="full">Client sign-in</span><span class="short">Sign in</span>
586<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>
587</button>
588<div class="signin-menu" role="menu" aria-label="Client sign-in">
589<a role="menuitem" class="vg" href="https://app.solarsurvey.ai/login"><b><i></i>Vanguard Access<em>Invite only</em></b><span>Survey data portal · app.solarsurvey.ai</span></a>
590<div class="mdiv"></div>
591<a role="menuitem" href="https://app.crewlink.ai/sign-in">Scheduling &amp; Dispatch<span>Residential · C&amp;I · Utility-Scale · app.crewlink.ai</span></a>
592</div>
593</div>
594<button class="sx-burger" aria-label="Menu" aria-expanded="false"><svg viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h16"/></svg></button>
595</nav>
596</div></header>
597<div class="sx-mob"><h4>Residential</h4><a href="/residential">Overview<span>Rooftop PV at volume</span></a><a href="/residential/roof">Roof &amp; drone<span>Planes measured on the roof</span></a><a href="/residential/attic">Attic &amp; structure<span>Rafters recorded</span></a><a href="/residential/electrical">Electrical<span>Panel and interconnection</span></a><a href="/residential/design-engineering">Design &amp; engineering<span>Model to stamped set</span></a><h4>Commercial &amp; Industrial</h4><a href="/commercial-industrial">Overview<span>Big roofs, real structure</span></a><a href="/commercial-industrial/site-survey">On-site survey<span>Roof, structure, switchgear</span></a><a href="/commercial-industrial/design-engineering">Design &amp; engineering<span>PE stamped</span></a><a href="/commercial-industrial/visual-inspection">Visual inspection<span>Module resolution</span></a><a href="/commercial-industrial/thermal-inspection">Thermal inspection<span>Faults as heat</span></a><a href="/commercial-industrial/electroluminescence">Electroluminescence<span>Cell-level cracks</span></a><h4>Utility-Scale</h4><a href="/utility-scale">Overview<span>Thousands of acres</span></a><a href="/utility-scale/lidar-topography">LiDAR topography<span>Terrain before earthwork</span></a><a href="/land-survey">Land surveying<span>Boundary and topographic</span></a><a href="/utility-scale/construction-monitoring">Construction monitoring<span>Progress, measured</span></a><a href="/utility-scale/thermal-el-inspection">Thermal &amp; EL<span>Every string</span></a><h4>Across every scale</h4><a href="/site-survey">Site surveys<span>Roof and electrical, one visit</span></a><a href="/permitting">Permitting &amp; engineering<span>Stamped plan sets</span></a><a href="/inspection">Inspection<span>Aerial thermal and EL</span></a><a href="/shading-analysis">Shading analysis<span>SA, TOF and TSRF</span></a><a href="/solar-site-survey">Survey locations<span>State and metro guides</span></a><h4>The platform</h4><a href="/platform">Platform overview<span>Portal, 3D models, live data</span></a><a href="/features">Feature guides<span>Who it serves and how it works</span></a><a href="/shading-analysis">Shading analysis<span>Move the sun yourself</span></a><a href="/#calc">Cost calculator<span>What bad field data costs you</span></a><h4>Sign in</h4><a href="https://app.solarsurvey.ai/login">Vanguard Access<span>Invite only · survey data portal</span></a><a href="https://app.crewlink.ai/sign-in">Scheduling &amp; Dispatch<span>CrewLink · all verticals</span></a><h4>Solar reference</h4><a href="/solar-answers">Solar questions answered<span>Residential, commercial and utility-scale</span></a><a href="/survey-methodology">Survey methodology<span>Sources, measurements and limits</span></a><a href="/company-facts">Company facts<span>Official services and capabilities</span></a><h4>Field Notes · build vs. buy</h4><a href="/field-notes/build-vs-buy">What a survey really costs<span>Part 01 · the full model</span></a><a href="/field-notes/utilization">Utilization is the whole game<span>Part 02 · a real Tuesday</span></a><a href="/field-notes/break-even-headcount">The break-even headcount<span>When hiring pays off</span></a><a href="/field-notes/two-technicians">Two technicians, two roofs<span>Variance, measured</span></a><a href="/field-notes/hybrid-crew">The hybrid crew<span>In-house plus outsourced</span></a><h4>Field Notes · reference</h4><a href="/field-notes/solar-site-survey-checklist">Site survey checklist<span>Built backwards from rework</span></a><a href="/field-notes/how-long-solar-site-survey">How long a survey takes<span>Hour by hour</span></a><a href="/field-notes/do-solar-panels-need-permit">Do panels need a permit?<span>What goes in the package</span></a><a href="/field-notes/solar-storage-site-survey">Storage site surveys<span>The capture list</span></a><a href="/compare">Compare survey vendors<span>Questions to ask any vendor</span></a><h4>Company</h4><a href="/services">All services</a><a href="/field-notes">All Field Notes</a><a href="/about">About</a><a href="https://calendly.com/carson-solarsurvey/discover-solar-survey-ai">Book a 15-min call</a></div>
598<div id="page">
599<div id="spineHost">
600<svg id="spine" xmlns="http://www.w3.org/2000/svg">
601<defs>
602<linearGradient id="spineGrad" x1="0" y1="0" x2="0" y2="1">
603<stop offset="0%" stop-color="#FF3D00"/><stop offset="55%" stop-color="#FF6A00"/><stop offset="100%" stop-color="#FFD9A8"/>
604</linearGradient>
605<filter id="glow" x="-140%" y="-140%" width="380%" height="380%">
606<feGaussianBlur stdDeviation="9" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
607</filter>
608</defs>
609<path id="spineTrack" class="spine-track"></path>
610<path id="spineLive" class="spine-live"></path>
611<g id="spineNodes"></g>
612<g id="orb"><circle filter="url(#glow)" r="7" fill="#FF6A00"></circle><circle r="3" fill="#fff"></circle></g>
613</svg>
614</div>
615<main id="top">
616<section id="hero" class="xhero">
617<canvas id="heroCanvas"></canvas>
618<div class="wrap xhero-grid">
619<div>
620<div class="eyebrow">Residential · Commercial &amp; Industrial · Utility-Scale</div>
621<h1>
622<span class="h1-line"><span>Every solar decision</span></span>
623<span class="h1-line"><span>is a bet on data</span></span>
624<span class="h1-line"><span class="grad">someone else collected.</span></span>
625</h1>
626<p class="lede hero-sub">We collect it properly: surveys, engineering and computer-vision inspection across the whole asset lifecycle. Each handoff keeps the captured evidence tied to the site, so designers can review the source of the data they use.</p>
627<div class="hero-cta">
628<a href="#calc" class="btn btn-p">See what bad data costs you
629<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></a>
630<a href="#work" class="btn btn-s">See how it works</a>
631</div>
632<div class="hero-meta">
633<div><b data-count="50000" data-suffix="+">0</b>surveys completed</div>
634<div><b data-count="500" data-suffix="+">0</b>technicians nationwide</div>
635<div><b data-count="48" data-suffix=" hrs">0</b>to site survey</div>
636<div><b data-count="99.99" data-dec="2" data-suffix="%">0</b>data accuracy</div>
637</div>
638</div>
639<div class="holo reveal" aria-hidden="true">
640<canvas id="cloud"></canvas>
641<div class="holo-hud">
642<span class="hud tl">POINT CLOUD <b id="hudPts">0</b></span>
643<span class="hud tr">FACETS <b>4</b></span>
644<span class="hud bl" id="hudPhase">RECONSTRUCTING</span>
645<span class="hud br">PITCH <b id="hudPitch">0.0°</b></span>
646</div>
647</div>
648</div>
649<div class="scrollcue"><span>Scroll</span><i></i></div>
650</section>
651<a href="#top" id="toTop" aria-label="Back to top"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19V5M5 12l7-7 7 7"/></svg></a>
652<section id="problem" data-node="The problem">
653<div class="wrap">
654<div class="reveal">
655<div class="eyebrow">The problem</div>
656<h2>The survey is a rounding error in the budget<br>and it decides the whole project.</h2>
657<p class="lede" style="margin-top:26px">A measurement taken from a driveway. A roof plane estimated off satellite imagery. An electrical panel nobody photographed. None of it looks like a problem until it is one, three weeks later, with a crew already on site.</p>
658</div>
659<div class="cards c3">
660<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><path d="M3 12h4l3 8 4-16 3 8h4"/></svg></div>
661<h3>It surfaces late</h3><p>Bad field data never fails at the survey. It fails at design, at permit, at inspection, or on the roof, where every hour costs ten times more and the customer is watching.</p></div>
662<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><path d="M12 2 2 7l10 5 10-5-10-5Z"/><path d="m2 17 10 5 10-5M2 12l10 5 10-5"/></svg></div>
663<h3>It compounds downstream</h3><p>One wrong azimuth becomes a redesign, a re-permit, a re-schedule and a change order. The original error costs nothing. The chain reaction is the invoice.</p></div>
664<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M2 12s3.6-7 10-7 10 7 10 7-3.6 7-10 7-10-7-10-7Z"/></svg></div>
665<h3>Nobody is measuring it</h3><p>Revisits get logged as scheduling. Redesigns get logged as engineering. The cost is real and continuous, and it almost never appears on a line item anyone owns.</p></div>
666</div>
667</div>
668</section>
669<section id="work" data-node="How it works">
670<div class="wrap">
671<div class="reveal">
672<div class="eyebrow">Where you operate</div>
673<h2>Pick your ground. We'll show you<br>exactly how the work runs.</h2>
674<p class="lede" style="margin-top:26px">The failure mode is different at every scale, and so is the fix. Choose everything that applies; plenty of our customers run two or three of these at once.</p>
675</div>
676<div class="picker reveal" id="picker">
677<button class="vopt" data-v="res" aria-pressed="false">
678<span class="tick"><svg viewBox="0 0 24 24">
678<path d="m5 13 4 4L19 7"/></svg></span>
679<span class="vt">Residential</span><span class="vd">Rooftop PV at volume. Speed and repeatability decide the margin.</span>
680<span class="vs">Surveys · Plan sets · Permits</span></button>
681<button class="vopt" data-v="ci" aria-pressed="false">
682<span class="tick"><svg viewBox="0 0 24 24"><path d="m5 13 4 4L19 7"/></svg></span>
683<span class="vt">Commercial &amp; Industrial</span><span class="vd">Big roofs, real structural risk, and coordination across trades.</span>
684<span class="vs">Assessment · Engineering · Inspection</span></button>
685<button class="vopt" data-v="uti" aria-pressed="false">
686<span class="tick"><svg viewBox="0 0 24 24"><path d="m5 13 4 4L19 7"/></svg></span>
687<span class="vt">Utility-Scale</span><span class="vd">Thousands of acres. You can only manage what you can actually see.</span>
688<span class="vs">LiDAR · Monitoring · Thermal &amp; EL</span></button>
689</div>
690<div class="pickhint">
691<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 16v-4M12 8h.01"/></svg>
692<span id="pickmsg">Select one or more. The sections below adapt, and your calculator gets tuned to match.</span>
693</div>
694<div style="margin-top:44px">
695<div class="vpanel" data-p="res">
696<div class="vpanel-h">
697<div><div class="vh-t">Residential</div>
698<div class="vh-s">First, the layout everyone else would sell you. Then a technician on site, and the same roof gets bigger. Keep scrolling:</div></div>
699<div class="ph-r"><a class="plogin" href="https://app.crewlink.ai/sign-in">Client sign-in
700<svg viewBox="0 0 24 24"><path d="M7 17 17 7M9 7h8v8"/></svg></a>
701<a class="pbeta" href="https://app.solarsurvey.ai/login" data-beta="res"><i></i>Beta access for select clients
702<svg viewBox="0 0 24 24"><path d="M7 17 17 7M9 7h8v8"/></svg></a>
703<span class="badge" data-b="res">Available</span></div>
704</div>
705<div class="scrolly" data-steps="4" data-diagram="res">
706<div class="dgm">
707<div class="dgm-stage">
708<svg viewBox="0 0 460 320" id="resSvg">
709<defs>
710<linearGradient id="roofG" x1="0" y1="0" x2="1" y2="1">
711<stop offset="0%" stop-color="#25201B"/><stop offset="100%" stop-color="#17140F"/></linearGradient>
712<linearGradient id="scanG" x1="0" y1="0" x2="0" y2="1">
713<stop offset="0%" stop-color="#FF6A00" stop-opacity="0"/><stop offset="100%" stop-color="#FF6A00" stop-opacity=".55"/></linearGradient>
714</defs>
715<ellipse cx="230" cy="286" rx="180" ry="16" fill="#0E0D0C"/>
716<path d="M120 286 L120 200 L230 152 L340 200 L340 286 Z" fill="#141210" stroke="#33302B" stroke-width="1.5"/>
717<path id="rp1" d="M112 204 L230 148 L230 214 L170 246 Z" fill="url(#roofG)" stroke="#4A423A" stroke-width="1.5"/>
718<path id="rp2" d="M348 204 L230 148 L230 214 L290 246 Z" fill="url(#roofG)" stroke="#2A4459" stroke-width="1.5"/>
719<g id="resCloud"></g>
720<g id="resKeep" opacity="0"></g>
721<g id="resWire" opacity="0"></g>
722<g id="resObs" opacity="0"></g>
723<g id="resLegend" opacity="0"></g>
724<g id="resPanels"></g>
725<g id="resTech" opacity="0">
726<ellipse cx="386" cy="285.5" rx="13" ry="2.6" fill="rgba(0,0,0,.55)"/>
727<ellipse cx="386" cy="285.5" rx="9" ry="1.8" fill="rgba(255,106,0,.10)"/>
728<path d="M381.6 268 L381.2 281.5 M390.4 268 L390.8 281.5" stroke="#23232B" stroke-width="4.4" stroke-linecap="round"/>
729<path d="M378.6 283.2 h6 a1.4 1.4 0 0 0 1.4-1.4 v-1.6 h-5.4 Z" fill="#3A342C"/>
730<path d="M393.4 283.2 h-6 a1.4 1.4 0 0 1 -1.4-1.4 v-1.6 h5.4 Z" fill="#3A342C"/>
731<path d="M380.2 255.5 Q376.4 259.5 377.4 264.8" stroke="#26262E" stroke-width="3.4" stroke-linecap="round" fill="none"/>
732<circle cx="377.6" cy="266" r="1.9" fill="#DCA87C"/>
733<rect x="378" y="251.8" width="16" height="17.6" rx="3.2" fill="#FFA33D"/>
734<rect x="381.6" y="251.8" width="1.9" height="17.6" fill="#FFE9D2" opacity=".85"/>
735<rect x="388.5" y="251.8" width="1.9" height="17.6" fill="#FFE9D2" opacity=".85"/>
736<path d="M386 252 V269.2" stroke="#C9761B" stroke-width="1"/>
737<path d="M381.8 252 L386 255.6 L390.2 252" fill="#23232B"/>
738<path d="M391.8 255.5 Q396.6 258 395.2 262.6" stroke="#26262E" stroke-width="3.4" stroke-linecap="round" fill="none"/>
739<circle cx="394.9" cy="263.4" r="1.9" fill="#DCA87C"/>
740<g transform="rotate(-14 396 267)">
741<rect x="391.6" y="261.6" width="9" height="12" rx="1.4" fill="#101014" stroke="#4A4A55" stroke-width=".9"/>
742<rect x="392.9" y="262.9" width="6.4" height="9.4" rx=".8" fill="rgba(255,106,0,.16)" stroke="#FF6A00" stroke-width=".55"/>
743<path d="M394 265.2 h4.2 M394 267.6 h4.2 M394 270 h2.6" stroke="#FF8A2B" stroke-width=".7" stroke-linecap="round"/>
744</g>
745<circle cx="386" cy="248.6" r="4" fill="#DCA87C"/>
746<path d="M382.4 247.4 A 3.9 3.9 0 0 1 389.6 247.4 Z" fill="#C89268" opacity=".0"/>
747<path d="M380.9 245.4 A 5.1 5.1 0 0 1 391.1 245.4 Z" fill="#FF6A00"/>
748<path d="M382.2 242.6 A 5.1 5.1 0 0 1 385.4 240.6 L385.4 243.6 Z" fill="#FFC98F" opacity=".55"/>
749<rect x="379" y="244.9" width="14" height="2.3" rx="1.15" fill="#E05B00"/>
750<rect x="384.6" y="240.2" width="2.8" height="1.6" rx=".8" fill="#E05B00"/>
751<text x="386" y="297" fill="#FF8A2B" font-size="7.6" text-anchor="middle" letter-spacing="1">TECHNICIAN</text>
752<text x="386" y="306" fill="#78716A" font-size="6.6" text-anchor="middle" letter-spacing=".7">STRUCTURAL + ELECTRICAL</text>
753</g>
754<g id="resEng" opacity="0">
755<path d="M204.5 250 V228.5 L216 217" stroke="#FFA33D" stroke-width="5.2" stroke-linecap="round" fill="none" opacity=".22"/>
755
756<path d="M204.5 250 V228.5 L216 217" stroke="#FFA33D" stroke-width="2.3" stroke-linecap="round" fill="none"/>
757<path d="M201.8 245 h5.4 M201.8 239 h5.4" stroke="#C9761B" stroke-width="1.1"/>
758<circle cx="204.5" cy="228.8" r="2.6" fill="#17181D" stroke="#FFA33D" stroke-width="1.2"/>
759<circle cx="209.5" cy="223.5" r="1.5" fill="#FFE9D2"/><circle cx="215.5" cy="217.5" r="1.5" fill="#FFE9D2"/>
760<rect x="146" y="244" width="22" height="34" rx="3" fill="#17181D" stroke="#FFA33D" stroke-width="1.5"/>
761<rect x="150" y="250" width="14" height="3" rx="1" fill="#FF6A00"/>
762<rect x="150" y="256" width="14" height="3" rx="1" fill="#FF6A00"/>
763<rect x="150" y="262" width="14" height="3" rx="1" fill="#FF6A00" opacity=".45"/>
764<circle cx="157" cy="272.5" r="1.6" fill="#FFA33D"/>
765<rect x="196" y="250" width="17" height="24" rx="2.2" fill="#14161A" stroke="#A9A49D" stroke-width="1.2"/>
766<rect x="199" y="254" width="11" height="6" rx="1" fill="rgba(255,106,0,.2)" stroke="#FF6A00" stroke-width=".7"/>
767<circle cx="201" cy="267" r="1.2" fill="#FF8A2B"/><circle cx="206" cy="267" r="1.2" fill="#4A4A55"/>
768<rect x="226" y="252" width="13" height="20" rx="1.8" fill="#101014" stroke="#4A4A55" stroke-width="1.1"/>
769<path d="M229 256 h7 M229 260 h7 M229 264 h7" stroke="#78716A" stroke-width="1"/>
770<path d="M168 264 h28 M213 262 h13" stroke="#78716A" stroke-width="1.3"/>
771<text x="157" y="289" fill="#FF8A2B" font-size="6.8" text-anchor="middle" letter-spacing=".6">BATTERY · 13.5 kWh</text>
772<text x="204.5" y="298" fill="#FF8A2B" font-size="6.8" text-anchor="middle" letter-spacing=".6">INVERTER · 7.6 kW</text>
773<text x="232.5" y="283" fill="#78716A" font-size="6.4" text-anchor="middle" letter-spacing=".5">MAIN 200A</text>
774<text x="210" y="245" fill="#A9A49D" font-size="6.4" letter-spacing=".7">CONDUIT</text>
775</g>
776<g id="resDrone" opacity="0">
777<line id="dbeamL" x1="0" y1="0" x2="0" y2="0" stroke="url(#scanG)" stroke-width="1"/>
778<line id="dbeamR" x1="0" y1="0" x2="0" y2="0" stroke="url(#scanG)" stroke-width="1"/>
779<g id="droneBody">
780<line x1="-11" y1="-4" x2="11" y2="-4" stroke="#A9A49D" stroke-width="1.6"/>
781<circle cx="-11" cy="-4" r="3.4" fill="none" stroke="#FF6A00" stroke-width="1.4"/>
782<circle cx="11" cy="-4" r="3.4" fill="none" stroke="#FF6A00" stroke-width="1.4"/>
783<rect x="-5" y="-4" width="10" height="6" rx="2" fill="#1B1E24" stroke="#A9A49D" stroke-width="1.2"/>
784</g>
785</g>
786<text id="resTag" x="230" y="34" text-anchor="middle" fill="#63798B" font-size="11" letter-spacing="2.4">CAPTURE</text>
787</svg>
788</div>
789<div class="dgm-side">
790<div class="scrollhint">Keep <b>scrolling</b>. The roof builds itself</div>
791<div class="sprog" id="resProg"><i></i><i></i><i></i><i></i></div>
792<div class="steps" id="resSteps">
793<button class="step" data-s="0" aria-current="true"><span class="sn">1</span><span><span class="st">The desktop layout</span>
794<span class="sd">How this roof gets designed today: off satellite imagery. Nobody knows exactly what is up there, so the design carries a blanket keep-out and the array shrinks to whatever is definitely safe.</span></span></button>
795<button class="step" data-s="1"><span class="sn">2</span><span><span class="st">On-site survey: a technician, in person</span>
796<span class="sd">Not a desktop estimate, and not a drone alone. A trained technician is physically at the property within 48 hours: on the roof measuring planes, azimuth and pitch, in the attic assessing rafter size, spacing and sheathing, and at the service panel checking bus rating, main breaker and interconnection path. Structural and electrical are assessed on site, by the person standing there.</span></span></button>
797<button class="step" data-s="2"><span class="sn">3</span><span><span class="st">Design on real data</span>
798<span class="sd">The capture becomes a dimensioned 3D model: every plane with a real area, pitch and azimuth, every vent and skylight located to the inch. The blanket keep-out collapses onto the real hardware, the array re-flows around it, and the same roof takes a bigger system.</span></span></button>
799<button class="step" data-s="3"><span class="sn">4</span><span><span class="st">Engineer the full system</span>
800<span class="sd">Structural and electrical engineering off the precise 3D model and what the technician verified on site. The whole system lands on the home: battery and inverter placed at the service entrance, conduit run sized and routed up to the array, attachments landed on real rafters. Stamped and permit-ready.</span></span></button>
801</div>
802<div class="readout" id="resRead">&nbsp;</div>
803<div class="dgm-note">Guessing costs you modules. Measuring finds them. Same roof, same setbacks, more system, because the exclusions are real instead of precautionary.</div>
804</div>
805</div>
806</div>
807<div class="vstats"><div><b>48 hrs</b>to an in-person survey</div><div><b>72 hrs</b>proposal → plan set</div><div><b>99.99%</b>data accuracy</div></div>
808</div>
809<div class="vpanel" data-p="ci">
810<div class="vpanel-h">
811<div><div class="vh-t">Commercial &amp; Industrial</div>
812<div class="vh-s">Same scope as residential at building scale: an in-person technician survey, then design and engineering, plus the inspection work that keeps it producing. Scroll through the whole engagement:</div></div>
813<div class="ph-r"><a class="plogin" href="https://app.crewlink.ai/sign-in">Client sign-in
814<svg viewBox="0 0 24 24">
814<path d="M7 17 17 7M9 7h8v8"/></svg></a>
815<a class="pbeta" href="https://app.solarsurvey.ai/login" data-beta="ci"><i></i>Beta access for select clients
816<svg viewBox="0 0 24 24"><path d="M7 17 17 7M9 7h8v8"/></svg></a>
817<span class="badge" data-b="ci">Available</span></div>
818</div>
819<div class="scrolly" data-steps="5" data-diagram="ci">
820<div class="dgm">
821<div class="dgm-stage">
822<svg viewBox="0 0 460 320" id="ciSvg">
823<rect x="24" y="26" width="412" height="252" rx="6" fill="#0B1520" stroke="#1E2E3C" stroke-width="1.5"/>
824<g id="ciSurvey" opacity="0"></g>
825<g id="ciGrid"></g>
826<g id="ciSetback" opacity="0"></g>
827<g id="ciSweep" opacity="0"><rect id="ciSweepR" x="24" y="26" width="26" height="252" fill="url(#sweepG)"/></g>
828<defs><linearGradient id="sweepG" x1="0" y1="0" x2="1" y2="0">
829<stop offset="0%" stop-color="#FF6A00" stop-opacity="0"/><stop offset="100%" stop-color="#FF6A00" stop-opacity=".28"/></linearGradient></defs>
830<text id="ciCap" x="230" y="302" text-anchor="middle" fill="#FF8A2B" font-size="10.6">Technician on site: roof, structure, electrical room</text>
831<text id="ciTip" x="230" y="302" text-anchor="middle" fill="#A9A49D" font-size="11.5" opacity="0">Hover a module to inspect it</text>
832</svg>
833</div>
834<div class="dgm-side">
835<div class="scrollhint">Keep <b>scrolling</b>. Survey, design, then three inspections</div>
836<div class="sprog" id="ciProg"><i></i><i></i><i></i><i></i><i></i></div>
837<div class="steps" id="ciSteps">
838<button class="step" data-s="0" aria-current="true"><span class="sn">1</span><span><span class="st">On-site survey: a technician, in person</span>
839<span class="sd">Same as residential, at building scale: a technician is physically on the property, not reading it off imagery. On the roof for membrane condition, penetrations and drainage; inside for the structural assessment covering bar joist depth, spacing and span, deck gauge and column lines; and in the electrical room for switchgear, available bus capacity and the interconnection path. Access and lift constraints recorded at the same time, before a single line of design is drawn.</span></span></button>
840<button class="step" data-s="1"><span class="sn">2</span><span><span class="st">Design &amp; engineering</span>
841<span class="sd">Array layout, structural and electrical engineering on the building as measured, not as drawn in a set from 1997: bar joist depth and spacing, deck type, panel capacity.</span></span></button>
842<button class="step" data-s="2"><span class="sn">3</span><span><span class="st">Visual inspection</span>
843<span class="sd">Full-array imagery at module resolution. Soiling, physical damage, vegetation encroachment and mounting failures, located against the as-built.</span></span></button>
844<button class="step" data-s="3"><span class="sn">4</span><span><span class="st">Thermal inspection</span>
845<span class="sd">Radiometric capture across every module. Hot cells, failed bypass diodes, bad connections and dead optimizers show up as temperature deltas long before production data notices.</span></span></button>
846<button class="step" data-s="4"><span class="sn">5</span><span><span class="st">Electroluminescence</span>
847<span class="sd">Current driven through the array so the cells emit infrared. Microcracks and inactive cell regions become visible: damage invisible to both visual and thermal.</span></span></button>
848</div>
849<div class="readout" id="ciRead">&nbsp;</div>
850<div class="dgm-note">One partner from the first site visit to the last inspection, and the survey data the design was built from is the same record the inspections are measured against.</div>
851</div>
852</div>
853</div>
854<div class="vstats"><div><b>In person → stamped</b>survey, design &amp; engineering in-house</div><div><b>Nationwide</b>500+ technicians</div><div><b>One record</b>as-built through inspection</div></div>
855</div>
856<div class="vpanel" data-p="uti">
857<div class="vpanel-h">
858<div><div class="vh-t">Utility-Scale</div>
859<div class="vh-s">One dataset across the whole asset lifecycle. Scroll and the site ages under you:</div></div>
860<div class="ph-r"><a class="plogin" href="https://app.crewlink.ai/sign-in">Client sign-in
861<svg viewBox="0 0 24 24">
861<path d="M7 17 17 7M9 7h8v8"/></svg></a>
862<a class="pbeta" href="https://app.solarsurvey.ai/login" data-beta="uti"><i></i>Beta access for select clients
863<svg viewBox="0 0 24 24"><path d="M7 17 17 7M9 7h8v8"/></svg></a>
864<span class="badge" data-b="uti">Available</span></div>
865</div>
866<div class="scrolly" data-steps="3" data-diagram="uti">
867<div class="dgm">
868<div class="dgm-stage">
869<svg viewBox="0 0 460 320" id="utiSvg">
870<defs>
871<linearGradient id="terG" x1="0" y1="0" x2="0" y2="1">
872<stop offset="0%" stop-color="#17161A"/><stop offset="100%" stop-color="#0B0C0F"/></linearGradient>
873<linearGradient id="utiSweepG" x1="0" y1="0" x2="1" y2="0">
874<stop offset="0%" stop-color="#FF6A00" stop-opacity="0"/><stop offset="60%" stop-color="#FF6A00" stop-opacity=".22"/><stop offset="100%" stop-color="#FF6A00" stop-opacity="0"/></linearGradient>
875</defs>
876<rect x="20" y="40" width="420" height="220" rx="6" fill="url(#terG)" stroke="#1E2E3C" stroke-width="1.5"/>
877<g id="utiContours" opacity="0"></g>
878<g id="utiRows"></g>
879<g id="utiFaults" opacity="0"></g>
880<rect id="utiSweep" x="20" y="40" width="60" height="220" fill="url(#utiSweepG)" opacity="0"/>
881<text id="utiTag" x="230" y="290" text-anchor="middle" fill="#63798B" font-size="11" letter-spacing="2.4">PLAN</text>
882</svg>
883</div>
884<div class="dgm-side">
885<div class="scrollhint">Keep <b>scrolling</b>. The site moves through its life</div>
886<div class="sprog" id="utiProg"><i></i><i></i><i></i></div>
887<div class="modes" id="utiModes">
888<button class="mode" data-m="0" aria-pressed="true">Plan</button>
889<button class="mode" data-m="1" aria-pressed="false">Build</button>
890<button class="mode" data-m="2" aria-pressed="false">Operate</button>
891</div>
892<div>
893<div style="font-weight:600;font-size:1.05rem;margin-bottom:8px" id="utiTitle">LiDAR topography</div>
894<p style="color:var(--tx-2);font-size:.93rem;font-weight:300;line-height:1.6" id="utiDesc">Centimetre-grade terrain capture before earthwork commits you to a number. Grading volumes, drainage paths and racking layout come off measured ground rather than a desktop estimate.</p>
895</div>
896<div class="readout" id="utiRead">SURFACE · <b>1,840</b> acres · contour interval <b>0.5 m</b></div>
897<div class="dgm-note">Plan, build and operate against the same spatial record, so the as-built is a byproduct of the work, not a separate project.</div>
898</div>
899</div>
900</div>
901<div class="vstats"><div><b>Every string</b>not a sample</div><div><b>Plan · Build · Operate</b>one dataset</div><div><b>Next morning</b>results</div></div>
902</div>
903</div>
904</div>
905</section>
906<section id="flywheel" data-node="The flywheel">
907<div class="wrap">
908<div class="reveal">
909<div class="eyebrow">Why we get better and they don't</div>
910<h2>Every survey we run makes<br>the next one sharper.</h2>
911<p class="lede" style="margin-top:26px">A survey company sells you hours. We're running a data flywheel: every site we capture becomes labelled training data, every model improvement makes the next capture faster and more accurate, and that speed wins more surveys, which produces more data. It compounds, and it can't be bought. Only accumulated.</p>
912</div>
913<div class="fly">
914<div class="flystage reveal">
915<svg viewBox="0 0 400 400" id="flysvg">
916<defs><linearGradient id="flyGrad" x1="0" y1="0" x2="1" y2="1">
917<stop offset="0%" stop-color="#FF3D00"/><stop offset="55%" stop-color="#FF6A00"/><stop offset="100%" stop-color="#FFD9A8"/></linearGradient></defs>
918<circle class="fw-ring" cx="200" cy="200" r="140"/>
919<path class="fw-arc" id="flyArc"/>
920<g class="fw-core"><text class="k" x="200" y="196" id="flyK">99.99%</text><text class="l" x="200" y="220">data accuracy</text></g>
921<g id="flyNodes"></g><g id="flyPulses"></g>
922</svg>
923</div>
924<div class="reveal">
925<div class="flyrow"><span class="n">01</span><div><div class="t">Capture</div><div class="d">A technician or a drone captures a site to a fixed spec: the same fields, the same angles, the same photo discipline, every time.</div></div></div>
926<div class="flyrow"><span class="n">02</span><div><div class="t">Label</div><div class="d">Every capture is verified against what was actually built. Right and wrong both become ground truth, which is the part most datasets never get.</div></div></div>
927<div class="flyrow"><span class="n">03</span><div><div class="t">Train</div><div class="d">Computer-vision models learn roof geometry, obstruction detection, thermal signatures and defect classes from real installed outcomes.</div></div></div>
928<div class="flyrow"><span class="n">04</span><div><div class="t">Automate</div><div class="d">What a person measured last year, a model measures this year: faster, cheaper, and without the variance between technicians.</div></div></div>
929<div class="flyrow"><span class="n">05</span><div><div class="t">Compound</div><div class="d">Faster and more accurate wins more surveys. More surveys produce more ground truth. The gap widens every quarter.</div></div></div>
930</div>
931</div>
932</div>
933</section>
934<section id="platform" data-node="The platform">
935<div class="wrap">
936<div class="plat-top">
937<div class="reveal">
938<div class="eyebrow">New · The Solar Survey AI platform</div>
939<h2>Every roof we touch becomes<br><span class="grad">a live, measured model.</span></h2>
940<p class="lede" style="margin-top:26px">Every site we capture now lands in your own client portal as working data: photos sorted and mapped, every roof section measured in 3D, and shade calculated for every hour of the year.</p>
941</div>
942<div class="holo reveal plat-dome" aria-hidden="true">
943<canvas id="dome"></canvas>
944<div class="holo-hud">
945<span class="hud tl">SUN PATH <b>12 MONTHS</b></span>
946<span class="hud tr">SA <b id="domeSA">0%</b></span>
947<span class="hud bl" id="domeMonth">JUNE</span>
948<span class="hud br">OBSTRUCTIONS <b>3</b></span>
949</div>
950</div>
951</div>
952<div class="cards c4">
953<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 9h18M8 14h3M8 17h6"/></svg></div>
954<span class="plive"><i></i>Live</span><h3>Client portal</h3><p>Every delivered site in one place: sorted photo sets, a photo map, the model and the report.</p></div>
955<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><path d="M3 10 12 4l9 6-9 6-9-6Z"/><path d="M3 10v6l9 6 9-6v-6"/></svg></div>
956<span class="plive"><i></i>Live</span><h3>3D roof model</h3><p>Pitch, azimuth, area and edges for every roof section, each with its tolerance.</p></div>
957<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg></div>
958<span class="plive"><i></i>Live</span><h3><a href="/shading-analysis" style="text-decoration:none">Shading analysis</a></h3><p>Solar access, TOF and TSRF per roof section, on typical-year NREL weather.</p></div>
959<div class="card reveal"><div class="ix"><svg viewBox="0 0 24 24"><path d="M3 17l6-6 4 4 8-8"/><path d="M15 7h6v6"/></svg></div>
960<span class="plive soon"><i></i>Next</span><h3>Predict &amp; protect</h3><p>Remote shade reports, P50 / P90 production estimates and performance monitoring.</p></div>
961</div>
962<p class="platform-release-note reveal">Now in the web platform: follow your company's survey pipeline, add company capture checklists and discuss specific measurements or photographs. Explore contextual 3D site maps, where coverage is available, alongside confirmed equipment callouts, visual sun playback and preliminary electricity-use estimates. <a href="/platform#client-workspace">Explore the client workspace &rarr;</a> The initial <a href="/platform#sales-studio">Sales Studio</a> adds pre-survey concepts, a separate roof designer where source coverage is available, illustrative cash/loan comparisons and branded proposal PDFs.</p>
963<div class="reveal" style="display:flex;gap:14px;flex-wrap:wrap;align-items:center;margin-top:36px">
964<a class="btn btn-p" href="/platform">Explore the platform
965<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></a>
966<a class="btn btn-s" href="/shading-analysis">Move the sun yourself</a>
967<a class="pbeta" style="padding:12px 18px;font-size:.92rem;border-radius:12px" href="https://app.solarsurvey.ai/login"><i></i>Beta access for select clients
968<svg viewBox="0 0 24 24"><path d="M7 17 17 7M9 7h8v8"/></svg></a>
969</div>
970</div>
971</section>
972<section id="calc" data-node="The cost">
973<div class="wrap">
974<div class="reveal">
975<div class="eyebrow">The number nobody owns</div>
976<h2>What is bad field data<br>costing you a year?</h2>
977<p class="lede" style="margin-top:26px">Three models, one per vertical, each built on published industry benchmarks: Aurora Solar's change-order study, SurgePV's 2026 margin data, and Raptor Maps' 125 GW underperformance report. Everything is computed in your browser and nothing is sent anywhere until you decide to send it.</p>
978<div class="company-proof benchmark-proof"><p class="company-proof-note">Published benchmark sources</p><div class="company-logos" aria-label="Published industry benchmark sources"><div class="company-logo "><img src="/img/logos/aurora-solar.svg" alt="Aurora Solar" loading="lazy" decoding="async"></div><div class="company-logo "><img src="/img/logos/surgepv.svg" alt="SurgePV" loading="lazy" decoding="async"></div><div class="company-logo "><img src="/img/logos/raptor-maps.png" alt="Raptor Maps" loading="lazy" decoding="async"></div></div></div>
979</div>
980<div class="calcbox reveal">
981<div class="calcgrid">
982<div class="calcin">
983<div class="modes" id="calcTabs" style="margin-bottom:26px">
984<button class="mode" data-cv="res" aria-pressed="true">Residential</button>
985<button class="mode" data-cv="ci" aria-pressed="false">Commercial &amp; Industrial</button>
986<button class="mode" data-cv="uti" aria-pressed="false">Utility-Scale</button>
987</div>
988<div class="field" id="f1"><label for="i1"><span id="l1">Installs completed per month</span> <span class="val" id="v1">40</span></label>
989<input type="range" id="i1" min="1" max="400" value="40" step="1"></div>
990<div class="field" id="f2"><label for="i2"><span id="l2">Share hitting a change order or revisit</span> <span class="val" id="v2">20%</span></label>
991<input type="range" id="i2" min="1" max="60" value="20" step="1"></div>
992<div class="field" id="f3"><label for="i3"><span id="l3">Average contract value</span> <span class="val" id="v3">$23,400</span></label>
993<input type="range" id="i3" min="8000" max="60000" value="23400" step="200"></div>
994<div class="field" id="f4"><label for="i4"><span id="l4">Days of delay when it happens</span> <span class="val" id="v4">10</span></label>
995<input type="range" id="i4" min="1" max="60" value="10" step="1"></div>
996<details class="assump"><summary>Assumptions &amp; sources</summary>
997<span id="assumpBody"></span>
998</details>
999</div>
1000<div class="calcout locked" id="calcout">
1001<div class="lockveil" id="lockveil">
1002<svg viewBox="0 0 24 24" fill="none" stroke="#FF8A2B" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
1003<div class="lv-t">Your result is ready</div>
1004<div class="lv-s">Enter your info below and we'll reveal what bad field data is costing you per year.</div>
1005<a href="#gate" class="btn btn-p btn-sm" id="lvBtn">Reveal my number</a>
1006</div>
1007<div class="hero-fig-l" id="calcTag">Estimated annual cost: Residential</div>
1008<div class="hero-fig" id="total">$0</div>
1009<div class="hero-fig-s" id="totalSub"></div>
1010<div class="bd">
1011<div class="bdrow"><div class="bl"><span class="nm"><i style="background:#D76902"></i><span id="n1">Revisits &amp; truck rolls</span></span><span class="amt" id="b1">$0</span></div><div class="track"><i id="t1" style="background:#D76902;width:0"></i></div></div>
1012<div class="bdrow"><div class="bl"><span class="nm"><i style="background:#3284D0"></i><span id="n2">Change-order rework</span></span><span class="amt" id="b2">$0</span></div><div class="track"><i id="t2" style="background:#3284D0;width:0"></i></div></div>
1013<div class="bdrow"><div class="bl"><span class="nm"><i style="background:#C44085"></i><span id="n3">Delay carrying cost</span></span><span class="amt" id="b3">$0</span></div><div class="track"><i id="t3" style="background:#C44085;width:0"></i></div></div>
1014<div class="bdrow"><div class="bl"><span class="nm"><i style="background:#8459C3"></i><span id="n4">Cancelled jobs</span></span><span class="amt" id="b4">$0</span></div><div class="track"><i id="t4" style="background:#8459C3;width:0"></i></div></div>
1015</div>
1016</div>
1017</div>
1018<div class="gate" id="gate">
1019<div id="gateForm">
1020<div class="gt">See what it’s costing you</div>
1021<div class="gs">Your numbers are set. Enter your info to reveal your estimated annual cost. We'll also send a one-page teardown of the model behind it, what the recoverable portion typically is, and where it usually hides in your operation.</div>
1022<form class="gform" id="leadForm" novalidate>
1023<input type="email" id="lEmail" placeholder="Work email" required autocomplete="email">
1024<input type="text" id="lCo" placeholder="Company" autocomplete="organization">
1025<div aria-hidden="true" style="position:absolute;
1025left:-9999px;width:1px;height:1px;overflow:hidden"><label>Website<input type="text" id="lWeb" name="website" tabindex="-1" autocomplete="off"></label></div>
1026<button type="submit" class="btn btn-p">Reveal my number</button>
1027</form>
1028<div class="gnote"><svg viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
1029<span>No newsletter, no sequence. One email with your numbers in it.</span></div>
1030</div>
1031<div class="gdone" id="gateDone">
1032<div class="dt">On its way.</div>
1033<div class="ds">We've got what we need. Here's what we're working from:</div>
1034<div class="recap" id="recap"></div>
1035</div>
1036</div>
1037</div>
1038<div id="dx">
1039<div class="dx-head">
1040<div class="eyebrow">Your number, read back</div>
1041<h2 class="dx-t" id="dxTitle"></h2>
1042<p class="dx-s" id="dxSub"></p>
1043</div>
1044<div class="dx-grid">
1045<div class="dx-card">
1046<div class="dx-k">Your biggest single driver</div>
1047<div class="dx-v" id="dxDriver"></div>
1048<p id="dxDriverWhy"></p>
1049</div>
1050<div class="dx-card">
1051<div class="dx-k">Where it actually starts</div>
1052<div class="dx-v" id="dxCause"></div>
1053<p id="dxCauseWhy"></p>
1054</div>
1055<div class="dx-card">
1056<div class="dx-k">What we would do about it</div>
1057<div class="dx-v" id="dxFix"></div>
1058<p id="dxFixWhy"></p>
1059</div>
1060</div>
1061<div class="dx-foot">
1062<div>
1063<div class="ft" id="dxRec"></div>
1064<div class="fs" id="dxRecWhy"></div>
1065</div>
1066<a href="#work" class="btn btn-s">See how the work runs</a>
1067<a href="https://calendly.com/carson-solarsurvey/discover-solar-survey-ai" class="btn btn-p" target="_blank" rel="noopener">Bring us one project</a>
1068</div>
1069<div class="dx-note" id="dxNote"></div>
1070</div>
1071</div>
1072</section>
1073<section data-node="Proof" style="padding-top:40px">
1074<div class="wrap">
1075<div class="reveal"><div class="eyebrow">Track record</div><h2>Not a pilot. An operation.</h2></div>
1076<div class="proof reveal">
1077<div><b data-count="50000" data-suffix="+">0</b><span>surveys completed</span></div>
1078<div><b data-count="500" data-suffix="+">0</b><span>technicians deployed</span></div>
1079<div><b data-count="7" data-suffix="+ yrs">0</b><span>in the field</span></div>
1080<div><b>All 50</b><span>states covered</span></div>
1081</div>
1082</div>
1083</section>
1084<section class="finale" data-node="Talk to us">
1085<div class="wrap reveal">
1086<h2>Stop guessing at the ground.</h2>
1087<p class="lede">Bring us one project. We'll survey it, deliver it, and you can compare it against whatever you're doing now.</p>
1088<div class="hero-cta"><a href="#calc" class="btn btn-p">Run the numbers first</a><a href="https://calendly.com/carson-solarsurvey/discover-solar-survey-ai" class="btn btn-s" target="_blank" rel="noopener">Book a demo</a></div>
1089</div>
1090</section>
1091</main></div>
1092<div class="sx-foot" role="contentinfo"><div class="in"><div class="cols"><div class="sx-fbrand"><a class="sx-logo" href="/"><svg viewBox="0 0 32 32" fill="none" aria-hidden="true"><defs><linearGradient id="sxLg" x1="6" y1="6" x2="26" y2="26" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#FF3D00"/><stop offset=".55" stop-color="#FF6A00"/><stop offset="1" stop-color="#FFA33D"/></linearGradient></defs><rect x="7" y="7" width="18" height="18" rx="3" stroke="url(#sxLg)" stroke-width="1.9"/><path d="M7 16h18M16 7v18" stroke="#FF6A00" stroke-opacity=".45" stroke-width="1.2"/><rect x="16.9" y="8.9" width="6.2" height="6.2" rx="1.4" fill="#FFE9D2"/></svg><span class="sx-wm"><span class="w1">Solar Survey <em>AI</em></span><span class="w2">Field data for solar</span></span></a><p>In-person site surveys, engineering and inspection for residential, C&amp;I and utility-scale solar, delivered as live data.</p></div><div><h5>Solutions</h5><ul><li><a href="/residential">Residential</a></li><li><a href="/commercial-industrial">Commercial &amp; Industrial</a></li><li><a href="/utility-scale">Utility-Scale</a></li></ul></div><div><h5>Services</h5><ul><li><a href="/site-survey">Site surveys</a></li><li><a href="/permitting">Permitting &amp; engineering</a></li><li><a href="/inspection">Inspection</a></li><li><a href="/land-survey">Land surveying</a></li><li><a href="/shading-analysis">Shading analysis</a></li><li><a href="/services">All services</a></li></ul></div><div><h5>Platform</h5><ul><li><a href="/platform">Platform overview</a></li><li><a href="/features">Feature guides</a></li><li><a href="https://app.solarsurvey.ai/login">Vanguard Access</a></li><li><a href="https://app.crewlink.ai/sign-in">Scheduling &amp; Dispatch</a></li><li><a href="/#calc">Cost calculator</a></li></ul></div><div><h5>Company</h5><ul><li><a href="/about">About</a></li><li><a href="/compare">Compare</a></li><li><a href="/field-notes">Field Notes</a></li><li><a href="https://calendly.com/carson-solarsurvey/discover-solar-survey-ai">Book a 15-min call</a></li><li><a href="/privacy-policy">Privacy</a></li></ul></div></div><div class="base"><span>&copy; 2026 Solar Survey AI &middot; Chicago, IL</span><span>Residential &middot; Commercial &amp; Industrial &middot; Utility-Scale</span></div></div></div>
1093<script>(function(){
1094"use strict";
1095var RM = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
1096var $=function(s,r){return (r||document).querySelector(s);};
1097var $$=function(s,r){return Array.prototype.slice.call((r||document).querySelectorAll(s));};
1098var NS='http://www.w3.org/2000/svg';
1099function el(n,a){var e=document.createElementNS(NS,n);for(var k in a)e.setAttribute(k,a[k]);return e;}
1100function lerp(a,b,t){return a+(b-a)*t;}
1101function clamp(v,a,b){return Math.min(Math.max(v,a),b);}
1102function mixHex(a,b,t){t=clamp(t,0,1);
1103var pa=[1,3,5].map(function(i){return parseInt(a.substr(i,2),16);});
1104var pb=[1,3,5].map(function(i){return parseInt(b.substr(i,2),16);});
1105return 'rgb('+pa.map(function(v,i){return Math.round(v+(pb[i]-v)*t);}).join(',')+')';}
1106function heat(t){t=clamp(t,0,1);
1107if(t<.30) return mixHex('#0A0709','#7A1200',t/.30);
1108if(t<.58) return mixHex('#7A1200','#FF3D00',(t-.30)/.28);
1109if(t<.78) return mixHex('#FF3D00','#FF6A00',(t-.58)/.20);
1110return mixHex('#FF6A00','#FFF3E4',(t-.78)/.22);}
1111document.addEventListener('DOMContentLoaded',function(){document.body.classList.add('loaded');});
1112setTimeout(function(){document.body.classList.add('h1done');},RM?0:1700);
1113window.addEventListener('load',function(){document.body.classList.add('loaded');});
1114(function(){
1115var w=$('#signin'), btn=$('#signinBtn'); if(!w) return;
1116function close(){ w.classList.remove('open'); btn.setAttribute('aria-expanded','false'); }
1117btn.addEventListener('click',function(e){ e.stopPropagation();
1118var open=w.classList.toggle('open'); btn.setAttribute('aria-expanded',open?'true':'false'); });
1119document.addEventListener('click',function(e){ if(!w.contains(e.target)) close(); });
1120document.addEventListener('keydown',function(e){ if(e.key==='Escape') close(); });
1121})();
1122var io=new IntersectionObserver(function(es){es.forEach(function(e){if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target);}});},{threshold:.12,rootMargin:'0px 0px -8% 0px'});
1123$$('.reveal').forEach(function(el){io.observe(el);});
1124var cio=new IntersectionObserver(function(es){es.forEach(function(e){
1125if(!e.isIntersecting) return; cio.unobserve(e.target);
1126var node=e.target, target=parseFloat(node.getAttribute('data-count')),
1127dec=parseInt(node.getAttribute('data-dec')||'0',10), suf=node.getAttribute('data-suffix')||'';
1128if(RM){node.textContent=target.toLocaleString('en-US',{minimumFractionDigits:dec,maximumFractionDigits:dec})+suf;return;}
1129var t0=null,D=1500;
1130function step(ts){ if(!t0)t0=ts; var p=clamp((ts-t0)/D,0,1); var e2=1-Math.pow(1-p,3);
1131var v=target*e2; node.textContent=v.toLocaleString('en-US',{minimumFractionDigits:dec,maximumFractionDigits:dec})+suf;
1132if(p<1) requestAnimationFrame(step); }
1133requestAnimationFrame(step);
1134});},{threshold:.5});
1135$$('[data-count]').forEach(function(n){cio.observe(n);});
1136(function(){
1137var cv=$('#heroCanvas'); if(!cv||RM) return;
1138var ctx=cv.getContext('2d'), pts=[], W=0,H=0,DPR=Math.min(window.devicePixelRatio||1,2);
1139function size(){ var r=cv.getBoundingClientRect(); W=r.width;H=r.height;
1140cv.width=W*DPR;cv.height=H*DPR;ctx.setTransform(DPR,0,0,DPR,0,0);
1141pts=[]; var n=Math.min(Math.round(W*H/6200),230);
1142for(var i=0;i<n;i++) pts.push({x:Math.random()*W,y:Math.random()*H,z:Math.random(),vy:0.06+Math.random()*0.16});}
1143size(); window.addEventListener('resize',function(){clearTimeout(cv.__t);cv.__t=setTimeout(size,200);});
1144var scan=0;
1145(function loop(){
1146ctx.clearRect(0,0,W,H); scan=(scan+0.0016)%1.4;
1147var sy=(scan-0.2)*H;
1148for(var i=0;i<pts.length;i++){var p=pts[i];
1149p.y-=p.vy; if(p.y<-8){p.y=H+8;p.x=Math.random()*W;}
1150var d=Math.abs(p.y-sy), lit=clamp(1-d/90,0,1);
1151var r=0.6+p.z*1.5+lit*1.6;
1152ctx.beginPath(); ctx.arc(p.x,p.y,r,0,6.2832);
1153ctx.fillStyle= lit>0.05 ? 'rgba('+Math.round(lerp(255,255,lit))+','+Math.round(lerp(61,190,lit))+','+Math.round(lerp(0,110,lit))+','+(0.18+lit*0.7)+')'
1154: 'rgba(140,120,104,'+(0.10+p.z*0.16)+')';
1155ctx.fill();
1156if(lit>0.55&&i%7===0){ ctx.beginPath(); ctx.moveTo(p.x,p.y); ctx.lineTo(p.x,sy);
1157ctx.strokeStyle='rgba(255,106,0,'+(lit*0.20)+')'; ctx.lineWidth=0.6; ctx.stroke(); }
1158}
1159ctx.beginPath(); ctx.moveTo(0,sy); ctx.lineTo(W,sy);
1160var g=ctx.createLinearGradient(0,0,W,0);
1161g.addColorStop(0,'rgba(255,106,0,0)');g.addColorStop(.5,'rgba(255,106,0,.42)');g.addColorStop(1,'rgba(255,106,0,0)');
1162ctx.strokeStyle=g; ctx.lineWidth=1; ctx.stroke();
1163requestAnimationFrame(loop);
1164})();
1165})();
1166var page=$('#page'),svg=$('#spine'),track=$('#spineTrack'),live=$('#spineLive'),nodesG=$('#spineNodes'),orb=$('#orb');
1167var PL=0,H=0,nodeList=[],SW=90,TOP=150;
1168function xAt(y,h){return 40+26*Math.sin((y/Math.max(h,1))*Math.PI*5.2);}
1169function buildSpine(){
1170if(window.innerWidth<1280) return;
1171H=page.scrollHeight;
1172svg.setAttribute('width',SW);svg.setAttribute('height',H);svg.setAttribute('viewBox','0 0 '+SW+' '+H);
1173var d='M '+xAt(TOP,H).toFixed(1)+' '+TOP;
1174for(var y=TOP+18;y<=H;y+=18) d+=' L '+xAt(y,H).toFixed(1)+' '+y;
1175track.setAttribute('d',d);live.setAttribute('d',d);
1176PL=track.getTotalLength();live.style.strokeDasharray=PL;live.style.strokeDashoffset=PL;
1177nodesG.innerHTML='';nodeList=[];
1178var pageTop=page.getBoundingClientRect().top+window.scrollY;
1179$$('[data-node]').forEach(function(sec){
1180var r=sec.getBoundingClientRect(), y=r.top+window.scrollY-pageTop+Math.min(r.height*0.32,250);
1181var c=el('circle',{cx:xAt(y,H).toFixed(1),cy:y,r:6,'class':'spine-node'});
1182nodesG.appendChild(c);nodeList.push({y:y,c:c});
1183});
1184paint();
1185}
1186function paint(){
1187if(window.innerWidth<1280||!PL) return;
1188var max=Math.max(document.documentElement.scrollHeight-window.innerHeight,1);
1189var p=clamp(window.scrollY/max,0,1), len=PL*p;
1190live.style.strokeDashoffset=PL-len;
1191var pt=track.getPointAtLength(len);
1192orb.setAttribute('transform','translate('+pt.x.toFixed(2)+','+pt.y.toFixed(2)+')');
1193orb.firstElementChild.setAttribute('fill', p<.55?mixHex('#FF3D00','#FF6A00',p/.55):mixHex('#FF6A00','#FFD9A8',(p-.55)/.45));
1194nodeList.forEach(function(n){ n.c.classList.toggle('on', pt.y>=n.y-4); });
1195}
1196var SETTERS={res:function(n){window.__resSet&&window.__resSet(n);},
1197ci:function(n){window.__ciSet&&window.__ciSet(n);},
1198uti:function(n){window.__utiSet&&window.__utiSet(n);}};
1199function scrollyGeom(w){
1200var n=+w.getAttribute('data-steps');
1201var top=w.getBoundingClientRect().top+window.scrollY;
1202var span=Math.max(w.offsetHeight-window.innerHeight,1);
1203return {n:n,top:top,span:span};
1204}
1205function driveScrolly(){
1206$$('.scrolly').forEach(function(w){
1207var g=scrollyGeom(w);
1208var raw=clamp((window.scrollY-g.top)/g.span,0,1);
1209var idx=Math.min(Math.floor(raw*g.n),g.n-1);
1210SETTERS[w.getAttribute('data-diagram')](idx);
1211var prog=$('#'+w.getAttribute('data-diagram')+'Prog');
1212if(prog){ var within=(raw*g.n)-idx;
1213Array.prototype.forEach.call(prog.children,function(bar,i){
1214bar.classList.toggle('done', i<idx);
1215bar.classList.toggle('now', i===idx);
1216bar.style.setProperty('--pw', i===idx ? (within*100).toFixed(1)+'%' : '0%');
1217});
1218}
1219});
1220}
1221function scrollToStage(tag,idx,n){
1222var w=document.querySelector('.scrolly[data-diagram="'+tag+'"]');
1223if(!w){ SETTERS[tag](idx); return; }
1224var g=scrollyGeom(w);
1225if(g.span<40){ SETTERS[tag](idx); return; }
1226window.scrollTo({top:g.top+g.span*((idx+0.45)/g.n),behavior:'smooth'});
1227}
1228var ticking=false, hdr=$('#hdr');
1229var toTop=$('#toTop');
1230window.addEventListener('scroll',function(){
1231if(window.scrollY>12)hdr.classList.add('stuck');else hdr.classList.remove('stuck');
1232if(toTop)toTop.classList.toggle('show', window.scrollY>1400);
1233if(!ticking){ticking=true;requestAnimationFrame(function(){paint();driveScrolly();ticking=false;});}
1234},{passive:true});
1235window.addEventListener('resize',function(){clearTimeout(window.__rz);window.__rz=setTimeout(buildSpine,180);});
1236(function(){
1237if(window.innerWidth>980) return;
1238['#resSvg','#ciSvg','#utiSvg'].forEach(function(sel){
1239var svg=$(sel); if(!svg) return;
1240$$('text',svg).forEach(function(t){
1241if(t.__boosted) return; t.__boosted=1;
1242var fs=parseFloat(t.getAttribute('font-size')||'10');
1243t.setAttribute('font-size',(fs*1.5).toFixed(1));
1244var ls=parseFloat(t.getAttribute('letter-spacing')||'0');
1245if(ls) t.setAttribute('letter-spacing',(ls*0.6).toFixed(2));
1246});
1247});
1248var eng=$('#resEng'); if(!eng) return;
1249var moves=[['BATTERY',292,null,null],['INVERTER',309,null,null],
1250['MAIN 200A',250,246,'start'],['CONDUIT',241,null,null]];
1251$$('text',eng).forEach(function(t){
1252moves.forEach(function(m){
1253if((t.textContent||'').indexOf(m[0])!==0) return;
1254t.setAttribute('y',m[1]);
1255if(m[2]!==null) t.setAttribute('x',m[2]);
1256if(m[3]) t.setAttribute('text-anchor',m[3]);
1257});
1258});
1259})();
1260setTimeout(function(){buildSpine();driveScrolly();},60);setTimeout(function(){buildSpine();driveScrolly();},700);
1261window.addEventListener('load',function(){setTimeout(buildSpine,140);});
1262(function(){
1263var svgR=$('#resSvg'); if(!svgR) return;
1264var cloud=$('#resCloud'),wire=$('#resWire'),obs=$('#resObs'),pan=$('#resPanels'),keep=$('#resKeep'),leg=$('#resLegend'),tech=$('#resTech'),eng=$('#resEng'),
1265drone=$('#resDrone'),dbody=$('#droneBody'),bl=$('#dbeamL'),br=$('#dbeamR'),
1266tag=$('#resTag'),read=$('#resRead');
1267var P1=[[112,204],[230,148],[230,214],[170,246]], P2=[[348,204],[230,148],[230,214],[290,246]];
1268function inPoly(x,y,poly){var c=false;for(var i=0,j=poly.length-1;i<poly.length;j=i++){
1269var xi=poly[i][0],yi=poly[i][1],xj=poly[j][0],yj=poly[j][1];
1270if(((yi>y)!==(yj>y))&&(x<(xj-xi)*(y-yi)/(yj-yi)+xi))c=!c;}return c;}
1271var cpts=[];
1272(function seed(){ var tries=0;
1273while(cpts.length<150&&tries<4000){tries++;
1274var x=110+Math.random()*240,y=145+Math.random()*105;
1275if(inPoly(x,y,P1)||inPoly(x,y,P2)) cpts.push({x:x,y:y});} })();
1276cpts.forEach(function(p){ var c=el('circle',{cx:p.x,cy:p.y,r:1.1,fill:'#FF6A00',opacity:0}
1276); cloud.appendChild(c); p.el=c; });
1277[[P1,'S 22° · 178° az · 412 sf',138,170],[P2,'S 22° · 358° az · 398 sf',322,170]].forEach(function(cfg){
1278var pts=cfg[0].map(function(p){return p.join(',');}).join(' ');
1279wire.appendChild(el('polygon',{points:pts,fill:'none',stroke:'#FF6A00','stroke-width':1.6,'stroke-dasharray':'4 3'}));
1280var t=el('text',{x:cfg[2],y:cfg[3],fill:'#FF6A00','font-size':8.5,'text-anchor':'middle','letter-spacing':.4});
1281t.textContent=cfg[1]; wire.appendChild(t);
1282});
1283[[196,196,12,9],[262,205,15,11],[157,222,14,10]].forEach(function(o){
1284obs.appendChild(el('rect',{x:o[0]-1.5,y:o[1]-1.5,width:o[2]+3,height:o[3]+3,rx:2.5,
1285fill:'rgba(255,233,210,.10)',stroke:'#FFE9D2','stroke-width':1.4}));
1286});
1287(function(){
1288var items=[['#3284D0','What the estimate allowed'],['#FF6A00','Unlocked by the survey'],['#FFE9D2','Obstruction, located']];
1289var x=74;
1290items.forEach(function(it){
1291var isO = it[0]==='#FFE9D2';
1292leg.appendChild(el('rect',{x:x,y:298,width:9,height:9,rx:1.5,
1293fill:isO?'rgba(255,233,210,.10)':it[0], stroke:isO?'#FFE9D2':'none','stroke-width':1.2}));
1294var t=el('text',{x:x+14,y:306,fill:'#A9A49D','font-size':8.4,'letter-spacing':.3});
1295t.textContent=it[1]; leg.appendChild(t);
1296x += 24 + it[1].length*4.3;
1297});
1298})();
1299var panels=[], COUNT_DESK=0, COUNT_SURV=0;
1300var OBS=[[196,196,12,9],[262,205,15,11],[157,222,14,10]];
1301function bilerp(q,sx,ty){
1302var A=q[0],B=q[1],C=q[2],D=q[3];
1303return [ (1-sx)*(1-ty)*A[0] + sx*(1-ty)*B[0] + sx*ty*C[0] + (1-sx)*ty*D[0],
1304(1-sx)*(1-ty)*A[1] + sx*(1-ty)*B[1] + sx*ty*C[1] + (1-sx)*ty*D[1] ];
1305}
1306function hitsObstruction(cx,cy,pad){
1307for(var i=0;i<OBS.length;i++){ var o=OBS[i];
1308if(cx>o[0]-pad && cx<o[0]+o[2]+pad && cy>o[1]-pad && cy<o[1]+o[3]+pad) return true; }
1309return false;
1310}
1311function mkPanels(){
1312pan.innerHTML=''; keep.innerHTML=''; panels=[]; COUNT_DESK=0; COUNT_SURV=0;
1313var COLS=6, ROWS=3, G=0.014;
1314var Ms=0.06;
1315var Md=0.18;
1316[P1,P2].forEach(function(q){
1317for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){
1318var s0=Ms+(1-2*Ms)*(c/COLS)+G, s1=Ms+(1-2*Ms)*((c+1)/COLS)-G;
1319var t0=Ms+(1-2*Ms)*(r/ROWS)+G, t1=Ms+(1-2*Ms)*((r+1)/ROWS)-G;
1320var sm=(s0+s1)/2, tm=(t0+t1)/2;
1321var ctr=bilerp(q,sm,tm);
1322var surveyed = !hitsObstruction(ctr[0],ctr[1],5.5);
1323var desktop  = sm>Md && sm<1-Md && tm>Md && tm<1-Md;
1324if(desktop) COUNT_DESK++;
1325if(surveyed) COUNT_SURV++;
1326var pts=[bilerp(q,s0,t0),bilerp(q,s1,t0),bilerp(q,s1,t1),bilerp(q,s0,t1)]
1327.map(function(pt){return pt[0].toFixed(1)+','+pt[1].toFixed(1);}).join(' ');
1328var poly=el('polygon',{points:pts,fill:'#3284D0',stroke:'#08080A','stroke-width':.5,opacity:0});
1329poly.__desk=desktop; poly.__surv=surveyed;
1330pan.appendChild(poly); panels.push(poly);
1331}
1332function pstr(a){return a.map(function(pt){return pt[0].toFixed(1)+' '+pt[1].toFixed(1);}).join(' L ');}
1333var outer=pstr(q.map(function(pt){return [pt[0],pt[1]];}));
1334var inner=pstr([bilerp(q,Md,Md),bilerp(q,1-Md,Md),bilerp(q,1-Md,1-Md),bilerp(q,Md,1-Md)]);
1335keep.appendChild(el('path',{d:'M '+outer+' Z M '+inner+' Z','fill-rule':'evenodd',
1336fill:'rgba(255,61,0,.17)',stroke:'#FF3D00','stroke-width':.9,'stroke-dasharray':'3 3'}));
1337});
1338}
1339mkPanels();
1340var STEP=0, tAnim=0, running=false;
1341var TAGS=['DESKTOP LAYOUT','TECHNICIAN ON SITE','DESIGN ON REAL DATA','ENGINEERED SYSTEM'];
1342var SEQ=['desk','survey','design','engineer'];
1343function reads(){
1344var kwD=(COUNT_DESK*0.4).toFixed(1), kwS=(COUNT_SURV*0.4).toFixed(1);
1345var gain=Math.round((COUNT_SURV/Math.max(COUNT_DESK,1)-1)*100);
1346return ['ESTIMATED · <b>'+COUNT_DESK+'</b> modules · <b>'+kwD+' kW</b> · blanket keep-out',
1347'ON SITE · <b>248</b> images · roof, attic &amp; service panel · <b>structural + electrical</b> assessed',
1348'DESIGNED · <b>'+COUNT_SURV+'</b> modules · <b>'+kwS+' kW</b> · <b>+'+gain+'%</b> same roof, measured',
1349'ENGINEERED · <b>'+kwS+' kW</b> PV · <b>13.5 kWh</b> storage · <b>7.6 kW</b> inverter · stamped <b>structural + electrical</b>'];
1350}
1351function setStep(n){
1352STEP=n; tAnim=0;
1353$$('#resSteps .step').forEach(function(b){b.setAttribute('aria-current', +b.getAttribute('data-s')===n ? 'true':'false');});
1354tag.textContent=TAGS[n]; read.innerHTML=reads()[n];
1355if(RM) renderAll(1);
1356}
1357function renderAll(prog){
1358var S=SEQ[STEP];
1359cpts.forEach(function(p,i){
1360var show = S==='survey' ? clamp(prog*1.6-(i/cpts.length),0,1)
1361: S==='design' ? clamp(1-prog*1.3,0,1)*0.55
1362: S==='engineer' ? 0 : 0;
1363p.el.setAttribute('opacity', show*0.85);
1364});
1365wire.setAttribute('opacity', S==='design' ? clamp(prog*1.4,0,1)*0.85 : 0);
1366leg.setAttribute('opacity', S==='design' ? clamp(prog*1.6,0,1) : 0);
1367keep.setAttribute('opacity', S==='desk' ? clamp(prog*1.5,0,1) : 0);
1368obs.setAttribute('opacity', S==='design' ? clamp(prog*1.5,0,1) : (S==='engineer' ? 0.4 : 0));
1369panels.forEach(function(p,i){
1370var want = S==='desk' ? p.__desk : ((S==='design'||S==='engineer') ? p.__surv : false);
1371var o = want ? (S==='engineer' ? 1 : clamp(prog*2.4-(i/panels.length)*1.2,0,1)) : 0;
1372p.setAttribute('opacity', o*0.92);
1373p.setAttribute('fill', ((S==='design'||S==='engineer') && !p.__desk) ? '#FF6A00' : '#3284D0');
1374});
1375drone.setAttribute('opacity', S==='survey'?1:0);
1376tech.setAttribute('opacity', S==='survey'?clamp(prog*1.8,0,1):0);
1377eng.setAttribute('opacity', S==='engineer'?clamp(prog*1.7,0,1):0);
1378}
1379function tick(ts){
1380if(!running){requestAnimationFrame(tick);return;}
1381tAnim=Math.min(tAnim+0.012,1);
1382if(SEQ[STEP]==='survey'){
1383var a=ts/1400, dx=230+96*Math.cos(a), dy=112+16*Math.sin(a*2);
1384drone.setAttribute('transform','translate('+dx.toFixed(1)+','+dy.toFixed(1)+')');
1385dbody.setAttribute('transform','scale('+(Math.cos(a)>=0?1:-1)+',1)');
1386bl.setAttribute('x1',-8);bl.setAttribute('y1',2);bl.setAttribute('x2',(200-dx)*0.55);bl.setAttribute('y2',(210-dy));
1387br.setAttribute('x1',8);br.setAttribute('y1',2);br.setAttribute('x2',(260-dx)*0.55);br.setAttribute('y2',(210-dy));
1388}
1389renderAll(tAnim);
1390requestAnimationFrame(tick);
1391}
1392window.__resSet=function(n){ if(n!==STEP){ var fwd=n>STEP; setStep(n); if(!fwd){ tAnim=1; renderAll(1); } } };
1393$$('#resSteps .step').forEach(function(b){ b.addEventListener('click',function(){ scrollToStage('res',+b.getAttribute('data-s'),4); }); });
1394new IntersectionObserver(function(es){es.forEach(function(e){ running=e.isIntersecting; });},{threshold:.2}).observe(svgR);
1395setStep(0); if(!RM) requestAnimationFrame(tick); else renderAll(1);
1396})();
1397(function(){
1398var svgC=$('#ciSvg'); if(!svgC) return;
1399var g=$('#ciGrid'),tip=$('#ciTip'),read=$('#ciRead'),
1400surv=$('#ciSurvey'),setb=$('#ciSetback'),cap=$('#ciCap'),
1401sweep=$('#ciSweep'),sweepR=$('#ciSweepR');
1402var COLS=15,ROWS=8,X0=38.8,Y0=46.4,W=24,Hh=25,GAP=1.6;
1403var MARK=[[70,62,'RTU'],[300,74,'RTU'],[150,240,'HATCH'],[372,232,'ELEC ROOM'],[228,58,'DRAIN'],[96,236,'PARAPET']];
1404var cells=[];
1405var seed=1337; function rnd(){seed=(seed*1103515245+12345)&0x7fffffff;return seed/0x7fffffff;}
1406for(var r=0;r<ROWS;r++)for(var c=0;c<COLS;c++){
1407var v=rnd(), t=rnd(), e=rnd();
1408var d={r:r,c:c,
1409soil: v>0.955, damage: v<0.018,
1410hot: t>0.962, warm: t>0.90,
1411crack: e<0.045,
1412base: 0.07+ (rnd()*0.15)};
1413var rect=el('rect',{x:X0+c*(W+GAP),y:Y0+r*(Hh+GAP),width:W,height:Hh,rx:1.5,
1414fill:'#1A1C21',stroke:'#0B0C0F','stroke-width':.6,style:'transition:fill .5s'});
1415(function(){
1416var cx=X0+c*(W+GAP), cy=Y0+r*(Hh+GAP), P=3;
1417d.blocked = MARK.some(function(m){
1418return cx < m[0]+26+P && cx+W > m[0]-P && cy < m[1]+15+P && cy+Hh > m[1]-P;
1419});
1420})();
1421rect.style.cursor='pointer';
1422(function(d,rect){
1423rect.addEventListener('mouseenter',function(){ if(STATE<2) return; tip.textContent=describe(d); tip.setAttribute('fill','#FF6A00'); });
1424rect.addEventListener('mouseleave',function(){ tip.textContent='Hover a module to inspect it'; tip.setAttribute('fill','#9DB0BF'); });
1425})(d,rect);
1426g.appendChild(rect); d.el=rect; cells.push(d);
1427}
1428var overlay=el('g',{}); g.parentNode.appendChild(overlay);
1429MARK.forEach(function(m){
1430var gg=el('g',{});
1431gg.appendChild(el('rect',{x:m[0],y:m[1],width:26,height:15,rx:2.5,fill:'rgba(255,106,0,.14)',stroke:'#FF6A00','stroke-width':1.2}));
1432var t=el('text',{x:m[0]+13,y:(m[1]>150? m[1]-5 : m[1]+26),fill:'#FF8A2B','font-size':7.4,'text-anchor':'middle','letter-spacing':.7});
1433t.textContent=m[2]; gg.appendChild(t); surv.appendChild(gg);
1434});
1435surv.appendChild(el('path',{d:'M34 36 L34 268 M426 36 L426 268',stroke:'#FF6A00','stroke-width':.8,'stroke-dasharray':'2 4',opacity:.6}));
1436setb.appendChild(el('rect',{x:33,y:41,width:394,height:222,rx:3,fill:'none',stroke:'#3284D0','stroke-width':1.2,'stroke-dasharray':'5 4'}));
1437var sl=el('text',{x:36,y:36,fill:'#3284D0','font-size':7.6,'letter-spacing':.7});
1438sl.textContent='ENGINEERED SETBACK · BAR JOIST 5 ft O.C. · 22 GA DECK'; setb.appendChild(sl);
1439var MODE='visual';
1440function describe(d){
1441var id='R'+(d.r+1)+'·C'+(d.c+1);
1442if(MODE==='visual') return d.damage?id+': cracked glass, module replace':(d.soil?id+': heavy soiling, wash scheduled':id+': n
1442ominal');
1443if(MODE==='thermal') return d.hot?id+': hot cell, ΔT 21.4 °C':(d.warm?id+': elevated, ΔT 6.1 °C':id+': nominal, ΔT 0.8 °C');
1444return d.crack?id+': microcracking, 3 cells affected':id+': no cell defects';
1445}
1446var COPY={
1447visual:['Visual inspection','Full-array imagery at module resolution. Catches soiling, physical damage, vegetation encroachment and mounting failures: anything a camera can see, located precisely against the as-built.'],
1448thermal:['Thermal inspection','Radiometric capture across every module. Hot cells, failed bypass diodes, bad connections and dead optimizers show up as temperature deltas long before they show up in production data.'],
1449el:['Electroluminescence','Current is driven through the array and the cells emit infrared. Microcracks, broken fingers and inactive cell regions become visible: damage that is completely invisible to both visual and thermal.']
1450};
1451function paintCells(){
1452overlay.innerHTML='';
1453if(STATE<=0){ cells.forEach(function(d){ d.el.setAttribute('opacity',0); }); return; }
1454if(STATE===1){
1455cells.forEach(function(d){ d.el.setAttribute('opacity', d.blocked?0:1); d.el.setAttribute('fill','#2A5F8C'); });
1456return;
1457}
1458var counts={a:0,b:0};
1459cells.forEach(function(d){
1460if(d.blocked){ d.el.setAttribute('opacity',0); return; }
1461d.el.setAttribute('opacity',1);
1462var fill='#1A1C21';
1463if(MODE==='visual'){
1464var tone=clamp(d.base*4.2,0,1);
1465fill = d.damage ? '#3A2029'
1466: (d.soil ? mixHex('#3A4436','#5A5238',tone)
1467: mixHex('#16273A','#274A6B',tone));
1468var px=+d.el.getAttribute('x'), py=+d.el.getAttribute('y');
1469overlay.appendChild(el('rect',{x:px+1.5,y:py+1.5,width:W-3,height:2.2,rx:1,
1470fill:'rgba(255,255,255,.13)'}));
1471if(d.damage||d.soil){ d.damage?counts.b++:counts.a++;
1472overlay.appendChild(el('rect',{x:px-1,y:py-1,width:W+2,height:Hh+2,rx:2,
1473fill:'none',stroke:d.damage?'#C44085':'#D76902','stroke-width':1.4}));}
1474} else if(MODE==='thermal'){
1475var t = d.hot? 0.99 : (d.warm? 0.72 : d.base);
1476fill = heat(t);
1477if(d.hot){counts.b++;} else if(d.warm){counts.a++;}
1478if(d.hot) overlay.appendChild(el('circle',{cx:+d.el.getAttribute('x')+W/2,cy:+d.el.getAttribute('y')+Hh/2,r:9,
1479fill:'none',stroke:'#FFE9D2','stroke-width':1.2,opacity:.9}));
1480} else {
1481fill = d.crack? '#221A2B' : '#101115';
1482if(d.crack){counts.b++;
1483var x=+d.el.getAttribute('x'),y=+d.el.getAttribute('y');
1484overlay.appendChild(el('path',{d:'M'+(x+4)+' '+(y+Hh-5)+' L'+(x+W*0.45)+' '+(y+Hh*0.42)+' L'+(x+W-5)+' '+(y+5),
1485fill:'none',stroke:'#8459C3','stroke-width':1.3,opacity:.95}));}
1486}
1487d.el.setAttribute('fill',fill);
1488});
1489if(STATE<2) return;
1490if(MODE==='visual') read.innerHTML='DETECTED · <b>'+counts.a+'</b> soiling · <b>'+counts.b+'</b> physical damage';
1491if(MODE==='thermal') read.innerHTML='DETECTED · <b>'+counts.b+'</b> hot cells · <b>'+counts.a+'</b> elevated · max ΔT <b>21.4 °C</b>';
1492if(MODE==='el') read.innerHTML='DETECTED · <b>'+counts.b+'</b> modules microcracked · <b>'+(counts.b*3)+'</b> cells affected';
1493}
1494var ORDER=['visual','thermal','el'], STATE=-1;
1495function setState(i){
1496i=clamp(i,0,4); if(i===STATE) return;
1497var fwd = i > STATE;
1498STATE=i;
1499$$('#ciSteps .step').forEach(function(b){ b.setAttribute('aria-current', +b.getAttribute('data-s')===i?'true':'false'); });
1500surv.setAttribute('opacity', i===0?1:(i===1?0.42:0.20));
1501setb.setAttribute('opacity', i===1?1:0);
1502tip.setAttribute('opacity', i>=2?1:0);
1503cap.setAttribute('opacity', i<=1?1:0);
1504cap.textContent = i===0 ? 'Technician on site: roof, structure, electrical room'
1505: 'Engineered on the building as measured';
1506if(i>=2){ MODE=ORDER[i-2]; paintCells(); if(fwd) runSweep(); }
1507else { paintCells(); if(i===1&&fwd) runSweep(); }
1508if(i===0) read.innerHTML='ON SITE · <b>148,000</b> sf walked · <b>6</b> penetrations · <b>structural + electrical</b> assessed in person';
1509if(i===1){ var fit=cells.filter(function(d){return !d.blocked;}).length;
1510read.innerHTML='ENGINEERED · <b>'+fit+'</b> modules · <b>'+(fit*0.4).toFixed(1)+' kW</b> · routed around <b>'+MARK.length+'</b> penetrations'; }
1511}
1512window.__ciSet=setState;
1513$$('#ciSteps .step').forEach(function(b){
1514b.addEventListener('click',function(){ scrollToStage('ci',+b.getAttribute('data-s'),5); });
1515});
1516function runSweep(){
1517if(RM) return;
1518sweep.setAttribute('opacity',1); var t0=null;
1519function s(ts){ if(!t0)t0=ts; var p=clamp((ts-t0)/900,0,1);
1520sweepR.setAttribute('x', 24+p*412); if(p<1) requestAnimationFrame(s); else sweep.setAttribute('opacity',0);}
1521requestAnimationFrame(s);
1522}
1523setState(0);
1524var seen=false;
1525new IntersectionObserver(function(es){es.forEach(function(e){ if(e.isIntersecting&&!seen){seen=true;runSweep();} });},{threshold:.3}).observe(svgC);
1526})();
1527(function(){
1528var svgU=$('#utiSvg'); if(!svgU) return;
1529var cont=$('#utiContours'),rows=$('#utiRows'),faults=$('#utiFaults'),sweep=$('#utiSweep'),
1530tag=$('#utiTag'),read=$('#utiRead'),title=$('#utiTitle'),desc=$('#utiDesc');
1531for(var i=0;i<7;i++){
1532var y=64+i*28, amp=9+i*1.6, d='M 26 '+y;
1533for(var x=26;x<=434;x+=8){ d+=' L '+x+' '+(y+Math.sin((x/54)+i*0.85)*amp+Math.cos(x/23)*2.5).toFixed(1); }
1534cont.appendChild(el('path',{d:d,fill:'none',stroke:i%3===0?'#FF6A00':'#6E2A05','stroke-width':i%3===0?1.2:0.8,opacity:i%3===0?.85:.55}));
1535}
1536var rowEls=[];
1537for(var r=0;r<7;r++){ for(var c=0;c<9;c++){
1538var x=36+c*44, y=62+r*28;
1539var rect=el('rect',{x:x,y:y,width:36,height:13,rx:2,fill:'#3284D0',opacity:0,style:'transition:fill .5s'});
1540rows.appendChild(rect); rowEls.push({el:rect,r:r,c:c,i:r*9+c});
1541}}
1542[0,11,22,33].map(function(i){return [Math.floor(i/9),i%9];}).forEach(function(f){
1543var x=36+f[1]*44, y=62+f[0]*28;
1544var gg=el('g',{});
1545gg.appendChild(el('rect',{x:x-2,y:y-2,width:40,height:17,rx:3,fill:'none',stroke:'#FFE9D2','stroke-width':1.4}));
1546gg.appendChild(el('circle',{cx:x+18,cy:y+6.5,r:3.2,fill:'#FFE9D2'}));
1547faults.appendChild(gg);
1548});
1549var PHASE=0;
1550var COPY=[
1551['LiDAR topography','Centimetre-grade terrain capture before earthwork commits you to a number. Grading volumes, drainage paths and racking layout come off measured ground rather than a desktop estimate.','SURFACE · <b>1,840</b> acres · contour interval <b>0.5 m</b>','PLAN'],
1552['Construction monitoring','Repeat flights through the build. Installed rows are counted against the plan set, deviations are flagged while they are still cheap, and the as-built record assembles itself as you go.','PROGRESS · <b>47</b> of <b>63</b> rows set · <b>74.6%</b> vs plan','BUILD'],
1553['Post-energization inspection','Visual, thermal and EL scanning across the whole site. Every string, not a sample. Underperformance gets a location and a cause instead of a line on an inverter report.','SCANNED · <b>63</b> strings · <b>4</b> faults located · next morning','OPERATE']
1554];
1555function render(fwd){
1556cont.setAttribute('opacity', PHASE===0?1:0.16);
1557faults.setAttribute('opacity', PHASE===2?1:0);
1558rowEls.forEach(function(o){
1559var show, fill='#3284D0';
1560if(PHASE===0) show=0;
1561else if(PHASE===1) show = o.i < 47 ? 0.95 : 0.16;
1562else { show=0.95; fill = (o.i%11===0)? '#FF6A00' : '#1E4E7A'; }
1563o.el.setAttribute('opacity',show); o.el.setAttribute('fill',fill);
1564});
1565tag.textContent=COPY[PHASE][3];
1566title.textContent=COPY[PHASE][0]; desc.textContent=COPY[PHASE][1]; read.innerHTML=COPY[PHASE][2];
1567$$('#utiModes .mode').forEach(function(b){b.setAttribute('aria-pressed', +b.getAttribute('data-m')===PHASE?'true':'false');});
1568if(PHASE===2&&fwd) runSweep();
1569}
1570function runSweep(){
1571if(RM) return; sweep.setAttribute('opacity',1); var t0=null;
1572function s(ts){ if(!t0)t0=ts; var p=clamp((ts-t0)/1400,0,1);
1573sweep.setAttribute('x', 20+p*380); if(p<1) requestAnimationFrame(s); else sweep.setAttribute('opacity',0);}
1574requestAnimationFrame(s);
1575}
1576window.__utiSet=function(n){ n=clamp(n,0,2); if(n!==PHASE){ var fwd=n>PHASE; PHASE=n; render(fwd); } };
1577$$('#utiModes .mode').forEach(function(b){ b.addEventListener('click',function(){ scrollToStage('uti',+b.getAttribute('data-m'),3); }); });
1578render();
1579})();
1580var sel=new Set();
1581var LABELS={res:'Residential',ci:'Commercial & Industrial',uti:'Utility-Scale'};
1582function syncPanels(){
1583var any=sel.size>0;
1584$$('.vpanel').forEach(function(p){
1585var k=p.getAttribute('data-p'), on=sel.has(k);
1586p.classList.toggle('dim', any&&!on);
1587var b=$('.badge[data-b="'+k+'"]',p);
1588if(b){b.classList.toggle('on',on); b.textContent = on?'Selected':(any?'Not selected':'Available');}
1589});
1590var msg=$('#pickmsg');
1591if(!any) msg.textContent='Select one or more. The sections below adapt, and the cost calculator switches to your vertical.';
1592else{
1593var names=Array.from(sel).map(function(k){return LABELS[k];});
1594msg.textContent=(names.length>1?names.slice(0,-1).join(', ')+' and '+names[names.length-1]:names[0])+' selected. The calculator has its own tab for each, and it\u2019s set to your pick.';
1595}
1596setTimeout(buildSpine,450);
1597}
1598$$('.vopt').forEach(function(b){
1599b.addEventListener('click',function(){
1600var k=b.getAttribute('data-v'), on=b.getAttribute('aria-pressed')==='true';
1601b.setAttribute('aria-pressed',on?'false':'true');
1602if(on)sel.delete(k);else{ sel.add(k); if(window.__calSet) window.__calSet(k); }
1603syncPanels();
1604});
1605});
1606var FN=['Capture','Label','Train','Automate','Compound'];
1607var fnG=$('#flyNodes'),fpG=$('#flyPulses'),flyArc=$('#flyArc'),flyK=$('#flyK'),CX=200,CY=200,RR=140;
1608(function(){
1609flyArc.setAttribute('d','M '+CX+' '+(CY-RR)+' A '+RR+' '+RR+' 0 1 1 '+(CX-0.01)+' '+(CY-RR));
1610var L=flyArc.getTotalLength(); flyArc.style.strokeDasharray=L; flyArc.style.strokeDashoffset=L;
1611FN.forEach(function(name,i){
1612var a=(-90+i*72)*Math.PI/180, x=CX+RR*Math.cos(a), y=CY+RR*Math.sin(a);
1613var g=el('g',{'class':'fw-node'});
1614g.appendChild(el('circle',{cx:x,cy:y,r:9}));
1615var out=1.30,tx=CX+RR*out*Math.cos(a),ty=CY+RR*out*Math.sin(a);
1616var t=el('text',{x:tx,y:ty+4,'text-anchor':Math.abs(Math.cos(a))<0.25?'middle':(Math.cos(a)>0?'start':'end')});
1617t.textContent=name; g.appendChild(t); fnG.appendChild(g);
1618});
1619for(var i=0;i<3;i++) fpG.appendChild(el('circle',{r:4,fill:'#FF6A00',opacity:.9,filter:'url(#glow)'}));
1620})();
1621var flyOn=false,flyT=0,flyProg=0;
1622new IntersectionObserver(function(es){es.forEach(function(e){flyOn=e.isIntersecting;});},{threshold:.25}).observe($('#flysvg'));
1623(function flyTick(){
1624if(flyOn&&!RM){
1625flyT+=0.0045; flyProg=Math.min(flyProg+0.006,1);
1626var L=flyArc.getTotalLength(); flyArc.style.strokeDashoffset=L*(1-flyProg);
1627var ps=fpG.children;
1628for(var i=0;i<ps.length;i++){var t=(flyT+i/ps.length)%1,a=(-90+t*360)*Math.PI/180;
1629ps[i].setAttribute('cx',CX+RR*Math.cos(a)); ps[i].setAttribute('cy',CY+RR*Math.sin(a));}
1630var idx=Math.floor(((flyT%1)*5))%5, gs=fnG.children;
1631for(var j=0;j<gs.length;j++) gs[j].classList.toggle('on', j===idx);
1632flyK.textContent=(94.2+(99.99-94.2)*(1-Math.pow(1-Math.min(flyT/1.6,1),3))).toFixed(2)+'%';
1633}
1634requestAnimationFrame(flyTick);
1635})();
1636if(RM){flyK.textContent='99.99%';flyArc.style.strokeDashoffset=0;}
1637function fmt(n){return '$'+Math.round(n).toLocaleString('en-US');}
1638function fmtC(n){n=Math.round(n);
1639if(n>=1e6)return '$'+(n/1e6).toFixed(n>=1e7?1:2)+'M';
1640if(n>=1e4)return '$'+Math.round(n/1e3)+'K';
1641return '$'+n.toLocaleString('en-US');}
1642var num=function(v){return (+v).toLocaleString('en-US');};
1643var pct=function(v){return v+'%';};
1644var CAL={
1645res:{
1646name:'Residential',
1647fields:[
1648{l:'Installs completed per month',min:1,max:400,val:40,step:1,f:num},
1649{l:'Share hitting a change order or revisit',min:1,max:60,val:20,step:1,f:pct},
1650{l:'Average contract value',min:8000,max:60000,val:23400,step:200,f:fmt},
1651{l:'Days of delay when it happens',min:1,max:60,val:10,step:1,f:num}
1652],
1653rows:['Revisits & truck rolls','Change-order rework','Delay carrying cost','Cancelled installs'],
1654assump:'A revisit is costed as a truck roll plus a half-day of technician time ($495). Rework is costed at the industry weighted-average change order of $583 (Aurora Solar installer study). Delay carries gross margin at 25% (SurgePV: top-quartile residential runs 25\u201335%) at a 12% annual cost of capital. Cancellation assumes 4% of affected 
1654installs are lost. EagleView reports contractors losing sales after change orders; we set it conservatively. Defaults: the 2026 median install, 7.2 kW at $3.25/W \u2248 $23,400 (SurgePV); Aurora Solar found change orders touch 10\u201330% of projects at roughly half of installers.',
1655compute:function(v){
1656var aff=v[0]*12*v[1]/100;
1657return {aff:Math.round(aff),
1658rows:[aff*495, aff*583, aff*v[2]*0.25*0.12*(v[3]/365), aff*0.04*v[2]*0.25],
1659sub:Math.round(aff).toLocaleString('en-US')+' installs a year hit an avoidable field-data problem. This is the bill.'};
1660}
1661},
1662ci:{
1663name:'Commercial & Industrial',
1664fields:[
1665{l:'Projects completed per year',min:1,max:120,val:14,step:1,f:num},
1666{l:'Share hitting rework or a structural surprise',min:1,max:60,val:22,step:1,f:pct},
1667{l:'Average project value',min:60000,max:3000000,val:350000,step:10000,f:fmt},
1668{l:'Days of delay when it happens',min:1,max:90,val:21,step:1,f:num}
1669],
1670rows:['Remobilization & lift time','Redesign & re-engineering','Delay carrying cost','Projects lost outright'],
1671assump:'A revisit at building scale is costed as crew remobilization plus lift access ($850). Rework is 12 engineering hours at $115/hr, because a structural surprise on a C&I roof reopens the stamped set, not just the layout. Delay carries gross margin at 22% (SurgePV: top-quartile C&I runs 18\u201328%) at a 12% annual cost of capital. Attrition assumes 3% of affected projects die. Default project: ~200 kW at $1.75/W \u2248 $350K, inside the DOE\u2019s 2024 commercial benchmark range ($1.34\u20131.51/W at 3 MW scale; smaller rooftops run higher).',
1672compute:function(v){
1673var aff=v[0]*v[1]/100;
1674return {aff:Math.round(aff),
1675rows:[aff*850, aff*12*115, aff*v[2]*0.22*0.12*(v[3]/365), aff*0.03*v[2]*0.22],
1676sub:Math.round(aff).toLocaleString('en-US')+' projects a year hit an avoidable field-data problem. This is the bill.'};
1677}
1678},
1679uti:{
1680name:'Utility-Scale',
1681fields:[
1682{l:'MW under management',min:0,max:1000,val:150,step:1,f:num,
1683map:function(t){var mw=5*Math.pow(10000,t/1000);
1684var r= mw<100?5 : mw<1000?25 : mw<10000?250 : 1000;
1685return Math.max(5,Math.round(mw/r)*r);},
1686unmap:function(mw){return Math.round(1000*Math.log(mw/5)/Math.log(10000));}},
1687{l:'Average power loss vs expected',min:1,max:10,val:4.5,step:0.1,f:function(v){return (+v).toFixed(1)+'%';}},
1688{l:'Average energy price ($/MWh)',min:20,max:120,val:45,step:1,f:fmt}
1689],
1690rows:['Inverter faults','String outages','Combiner faults','Tracker & other'],
1691assump:'Annual production is modeled at a 25% capacity factor across 8,760 hours. The loss composition follows Raptor Maps\u2019 global report across 125 GW in 41 countries: inverter faults 1.91%, string outages 0.90%, combiner faults 0.81%, tracker and other 0.85%, totalling a 4.47% fleet-average power loss, worth $4,696 per MW-year at prevailing prices. Raptor Maps classifies these losses as preventable: findable by aerial inspection, recoverable by O&M. Your slider scales that composition to your own fleet.',
1692compute:function(v){
1693var total=v[0]*8760*0.25*v[2]*(v[1]/100);
1694var mix=[1.91,0.90,0.81,0.85], K=4.47;
1695return {aff:null,
1696rows:mix.map(function(m){return total*(m/K);}),
1697sub:'At '+(+v[1]).toFixed(1)+'% of expected production across '+num(v[0])+' MW. The fleet average is 4.47%, and Raptor Maps calls all of it preventable.'};
1698}
1699}};
1700var V='res', LAST={};
1701function setVertical(k){
1702if(!CAL[k]) return;
1703V=k; var C=CAL[k];
1704$$('#calcTabs .mode').forEach(function(b){b.setAttribute('aria-pressed', b.getAttribute('data-cv')===k?'true':'false');});
1705$('#calcTag').textContent='Estimated annual cost: '+C.name;
1706for(var i=0;i<4;i++){
1707var w=$('#f'+(i+1)), inp=$('#i'+(i+1));
1708if(i<C.fields.length){
1709var f=C.fields[i];
1710w.style.display='';
1711$('#l'+(i+1)).textContent=f.l;
1712inp.min=f.min; inp.max=f.max; inp.step=f.step;
1713inp.value = f.unmap ? f.unmap(f.val) : f.val;
1714} else w.style.display='none';
1715$('#n'+(i+1)).textContent=C.rows[i]||'';
1716}
1717$('#assumpBody').textContent=C.assump;
1718compute();
1719}
1720window.__calSet=setVertical;
1721function compute(){
1722var C=CAL[V], vals=[];
1723for(var i=0;i<C.fields.length;i++){
1724var f=C.fields[i], raw=+$('#i'+(i+1)).value;
1725var v=f.map?f.map(raw):raw; vals.push(v);
1726$('#v'+(i+1)).textContent=f.f(v);
1727}
1728var r=C.compute(vals);
1729var total=r.rows.reduce(function(a,b){return a+b;},0), mx=Math.max.apply(null,r.rows.concat([1]));
1730var tEl=$('#total'), nv=fmtC(total);
1731if(tEl.textContent!==nv && !RM){ tEl.classList.remove('bump'); void tEl.offsetWidth; tEl.classList.add('bump'); }
1732tEl.textContent=nv;
1733$('#totalSub').textContent=r.sub;
1734r.rows.forEach(function(v,i){ $('#b'+(i+1)).textContent=fmtC(v); $('#t'+(i+1)).style.width=(v/mx*100).toFixed(1)+'%'; });
1735LAST={calcVertical:C.name, inputs:{}, breakdown:{}, totalAnnual:Math.round(total)};
1736C.fields.forEach(function(f,i){ LAST.inputs[f.l]=vals[i]; });
1737C.rows.forEach(function(n,i){ LAST.breakdown[n]=Math.round(r.rows[i]); });
1738if(r.aff!==null) LAST.affectedPerYear=r.aff;
1739}
1740['i1','i2','i3','i4'].forEach(function(id){$('#'+id).addEventListener('input',function(){compute();
1741if($('#dx').classList.contains('on')) renderDiagnosis();});});
1742$$('#calcTabs .mode').forEach(function(b){ b.addEventListener('click',function(){ setVertical(b.getAttribute('data-cv')); }); });
1743setVertical('res'); syncPanels();
1744var DX={
1745res:[
1746['Revisits and truck rolls',
1747'Every revisit is a truck, a technician and half a day you have already paid for, spent re-measuring something that should have been captured once.',
1748'A survey nobody stood on the roof for',
1749'Revisits cluster around the same three gaps: a roof plane estimated off imagery, an obstruction nobody photographed, and a service panel nobody opened.',
1750'One in-person capture to a fixed spec',
1751'A trained technician at the property within 48 hours. Planes, pitch and azimuth measured, attic and rafters assessed, service panel opened and rated. The second trip stops being scheduled because the first one was complete.'],
1752['Change-order rework',
1753'A change order does not only cost its own value. It reopens the design, the permit and the customer conversation at the worst possible moment.',
1754'A design built on an estimate, not a measurement',
1755'Aurora Solar found change orders touch 10 to 30% of projects at roughly half of installers. The trigger is almost always a dimension that was assumed rather than recorded.',
1756'Design off a dimensioned 3D model',
1757'Every plane with a real area, pitch and azimuth, every vent and skylight located to the inch. The design starts from what is on the roof, so there is nothing left to discover on install day.'],
1758['Delay carrying cost',
1759'Delay is the quiet one. It never appears on an invoice. It just holds your margin and your crew capacity for a few extra weeks on every affected job.',
1760'Rework that surfaces after the schedule is set',
1761'The days come from re-drawing, re-permitting and re-scheduling, and none of that starts until the crew has already found the problem.',
1762'Move the discovery to day one',
1763'Survey to plan set in 72 hours, off data that was measured rather than assumed. Problems surface before the schedule exists instead of after it.'],
1764['Cancelled installs',
1765'The most expensive outcome on the list. You carry the full acquisition and design cost and book none of the revenue.',
1766'Trust lost at the change-order conversation',
1767'Customers rarely cancel over the money itself. They cancel because being told the price moved after signing reads as not knowing what you were doing.',
1768'Quote off measured reality',
1769'When the survey is complete before the proposal is signed, the number you quote is the number you install. There is no second conversation to lose.']],
1770ci:[
1771['Remobilisation and lift time',
1772'Getting a crew and lift access back onto a commercial roof is a fixed cost you pay in full, however small the thing you came back for.',
1773'Access and roof conditions recorded too late',
1774'Membrane condition, penetrations, drainage and lift constraints decide the whole mobilisation plan, and they are often captured after it has been booked.',
1775'One technician, everything in one visit',
1776'Roof, structure and electrical room walked together, with access and lift constraints recorded before a line of design is drawn.'],
1777['Redesign and re-engineering',
1778'A structural surprise on a C&I roof does not just move panels. It reopens the stamped set, which is engineering hours plus a fresh review cycle.',
1779'A building drawn from a set from 1997',
1780'Bar joist depth and spacing, deck gauge and column lines are routinely taken from record drawings that no longer describe the building.',
1781'Engineer on the building as measured',
1782'Structural assessment on site: joist depth, spacing and span, deck gauge, column lines, switchgear and available bus capacity. The stamped set gets built once, on what is actually there.'],
1783['Delay carrying cost',
1784'At C&I project values, three weeks of held margin is a serious number on its own, before you count the crew you could not deploy somewhere else.',
1785'Discovery after the schedule commits',
1786'The delay is not the fix itself. It is the re-engineering, the re-review and the remobilisation queue that follow it.',
1787'Front-load the unknowns',
1788'In-person survey, then design and engineering in-house off that same record, so the surprises land while the schedule is still a draft.'],
1789['Projects lost outright',
1790'A C&I project that dies takes months of pursuit cost with it, and the relationship usually does not come back.',
1791'Confidence lost mid-project',
1792'Attrition at this scale is rarely about one number. It is a sponsor who has stopped believing the delivery estimate.',
1793'One partner, one record',
1794'The survey data the design was built from is the same record the inspections are measured against, so the story stays consistent from first visit to final walkthrough.']],
1795uti:[
1796['Inverter faults',
1797'The largest single slice of fleet underperformance, and the one most likely to sit undetected, because a production dip looks a lot like weather.',
1798'Sampling instead of scanning',
1799'Raptor Maps puts inverter faults at 1.91% of a 4.47% fleet-average loss across 125 GW in 41 countries. A partial inspection finds a proportional fraction of it.',
1800'Every string, not a sample',
1801'Full-site aerial thermal and EL capture, with each fault given a location and a cause instead of a line on an inverter report.'],
1802['String outages',
1803'String-level losses are invisible at the meter until they aggregate, and by then you have lost a season of production.',
1804'No spatial record to compare against',
1805'Without an as-built you can measure against, an outage is a number. With one, it is a position on a map with a work order attached.',
1806'One dataset across plan, build and operate',
1807'The as-built assembles itself as a byproduct of construction monitoring, so every later inspection has something to be measured against.'],
1808['Combiner faults',
1809'Combiner faults are cheap to fix and expensive to miss, because each one quietly removes a block of production.',
1810'Detection cadence too slow',
1811'These are found by looking, not by waiting for the data to show it. Your inspection interval decides how long each one bleeds.',
1812'Next-morning results',
1813'Visual, thermal and EL scanning across the whole site, processed overnight, so the O&M queue is populated before the next billing period.'],
1814['Tracker and balance of system',
1815'The long tail, and the tail is where most fleets stop looking.',
1816'Nobody owns the residual',
1817'Inverter and string issues get attention because they are legible. The remainder gets absorbed into an availability number and forgotten.',
1818'Inspect the whole asset, not the obvious parts',
1819'Raptor Maps classifies this entire loss profile as preventable: findable by aerial inspection and recoverable by O&M. The only real constraint is whether anyone is looking.']]
1820};
1821function renderDiagnosis(){
1822var C=CAL[V], rows=LAST.breakdown||{}, names=C.rows;
1823var top=0,topV=-1,i;
1824for(i=0;i<names.length;i++){ var v=rows[names[i]]||0; if(v>topV){topV=v;top=i;} }
1825var d=DX[V][top]; if(!d) return;
1826var total=LAST.totalAnnual||0, share=total?Math.round(topV/total*100):0;
1827$('#dxTitle').innerHTML = share>=45
1828? 'Most of that <b>'+fmtC(total)+'</b> is coming from one place.'
1829: 'The biggest slice of that <b>'+fmtC(total)+'</b> has a single cause.';
1830$('#dxSub').textContent=d[0]+' is '+share+'% of your total, at '+fmtC(topV)+' a year. '+
1831'Here is what sits underneath it and what we would change.';
1832$('#dxDriver').textContent=d[0]+' · '+fmtC(topV)+'/yr';
1833$('#dxDriverWhy').textContent=d[1];
1834$('#dxCause').textContent=d[2];
1835$('#dxCauseWhy').textContent=d[3];
1836$('#dxFix').textContent=d[4];
1837$('#dxFixWhy').textContent=d[5];
1838if(V==='uti'){
1839$('#dxRec').textContent='Raptor Maps classifies all of this as preventable: '+fmtC(total)+' a year.';
1840$('#dxRecWhy').textContent='Findable by aerial inspection, recoverable by O&M. The question is inspection coverage, not whether the loss is real.';
1841} else {
1842$('#dxRec').textContent='Recoverable on this driver alone: '+fmtC(topV*0.5)+' to '+fmtC(topV*0.7)+' a year.';
1843$('#dxRecWhy').textContent='That is 50 to 70% of your largest line, which is the share these costs typically trace back to field data that was assumed rather than measured.';
1844}
1845$('#dxNote').textContent='Calculated from the numbers you entered, using the published benchmarks listed above. '+
1846'It is an estimate to size the problem, not a quote. Bring us one project and we will show you the real number on your own site.';
1847$('#dx').classList.add('on');
1848setTimeout(buildSpine,400);
1849}
1850var LEAD_T0=Date.now();
1851$('#leadForm').addEventListener('submit',function(e){
1852e.preventDefault();
1853var em=$('#lEmail');
1854if(!em.value||!/^[^@\s]+@[^@\s]+\.[^@\s]{2,}$/.test(em.value)){em.focus();em.style.borderColor='#C44085';return;}
1855var payload=Object.assign({},LAST,{
1856email:em.value.trim(), company:$('#lCo').value.trim(),
1857verticals:Array.from(sel).map(function(k){return LABELS[k];}),
1858source:'cost-of-bad-data-calculator',
1859utm:window.SSAI_ATTR ? window.SSAI_ATTR.current().utm : {},
1860attribution:window.SSAI_ATTR ? window.SSAI_ATTR.current() : null,
1861submittedAt:new Date().toISOString(),
1862formRenderedAt:LEAD_T0, website:$('#lWeb').value
1863});
1864if(window.console) console.log('LEAD PAYLOAD',payload);
1865try{fetch('/api/lead',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)}).catch(function(){});}catch(_){}
1866var recap=$('#recap'); recap.innerHTML='';
1867[payload.company||payload.email,
1868payload.verticals.length?payload.verticals.join(' + '):'Vertical not specified',
1869payload.calcVertical+' model',
1870fmtC(payload.totalAnnual)+' / yr at stake'
1871].forEach(function(c){var s=document.createElement('span');s.className='chip';s.textContent=c;recap.appendChild(s);});
1872$('#gateForm').style.display='none';
1873$('#gateDone').classList.add('on');
1874var co=$('#calcout'); co.classList.remove('locked');
1875var tEl=$('#total');
1876if(!RM){ tEl.classList.remove('bump'); void tEl.offsetWidth; tEl.classList.add('bump'); }
1877renderDiagnosis();
1878setTimeout(function(){ var dx=$('#dx');
1879if(dx) dx.scrollIntoView({behavior:RM?'auto':'smooth',block:'start'}); },900);
1880setTimeout(buildSpine,300);
1881});
1882var lv=$('#lvBtn');
1883if(lv){ lv.addEventListener('click',function(){ setTimeout(function(){ $('#lEmail').focus(); },650); }); }
1884})();</script>
vendor: 1 bytes, line 1884
1884
1885<script>window.SSFX_LITE=1;</script>
vendor: 1 bytes, line 1885
1885
1886<script src="/fx.js"></script>
vendor: 1 bytes, line 1886
1886
1887<script src="/platform.js"></script>
vendor: 1 bytes, line 1887
1887
1888<script src="/shading.js"></script>
vendor: 1 bytes, line 1888
1888
1889<script src="/work.js"></script>
vendor: 1 bytes, line 1889
1889
1890<script src="/book-form.js" defer></script>
vendor: 1 bytes, line 1890
1890
1891<script src="/skin.js"></script>
vendor: 1 bytes, line 1891
1891
1892<script src="/ai-referrals.js"></script>
vendor: 1 bytes, line 1892
1892
1893<script src="/track.js"></script>
1893
1894</body>
1895</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.