PageSourceSearch

https://bizwrap.ai/assets/index-Dv_39A1J.js

js bizwrap.ai collected 2026-10-05 15:02:25 UTC 313,072 bytes, 2,635 lines download raw bytes

1(function(){const a=document.createElement("link").relList;if(a&&a.supports&&a.supports("modulepreload"))return;for(const o of document.querySelectorAll('link[rel="modulepreload"]'))n(o);new MutationObserver(o=>{for(const s of o)if(s.type==="childList")for(const i of s.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&n(i)}).observe(document,{childList:!0,subtree:!0});function t(o){const s={};return o.integrity&&(s.integrity=o.integrity),o.referrerPolicy&&(s.referrerPolicy=o.referrerPolicy),o.crossOrigin==="use-credentials"?s.credentials="include":o.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function n(o){if(o.ep)return;o.ep=!0;const s=t(o);fetch(o.href,s)}})();function ta(){return`
2    <nav class="navbar" id="navbar">
3      <div class="navbar__inner">
4        <a class="navbar__logo" data-navigate="home" id="nav-logo">
5          <img class="navbar__logo-img" src="/assets/Logo Simple 2.png" alt="BizWrap.ai" />
6        </a>
7
8        <div class="navbar__links" id="nav-links">
9          <a class="navbar__link" data-navigate="home" id="nav-home">Home</a>
10          <a class="navbar__link" data-navigate="contact" id="nav-contact">Contact</a>
11          <!-- AI Workshop event entry — delete this line to take the event down -->
12          <a class="btn btn--primary navbar__cta" data-navigate="workshop" id="nav-cta">AI Workshop</a>
13        </div>
14
15        <div class="navbar__mobile-toggle" id="mobile-toggle">
16          <span></span><span></span><span></span>
17        </div>
18      </div>
19    </nav>
20  `}function aa(){const e=document.getElementById("navbar"),a=document.getElementById("mobile-toggle"),t=document.getElementById("nav-links"),n=()=>e.classList.toggle("scrolled",window.scrollY>16);window.addEventListener("scroll",n),n(),a?.addEventListener("click",()=>{a.classList.toggle("open"),t.classList.toggle("open")}),t?.querySelectorAll(".navbar__link, .navbar__cta").forEach(o=>{o.addEventListener("click",()=>{a.classList.remove("open"),t.classList.remove("open")})}),Bt()}function Bt(){const e=window.location.hash.replace("#/","")||"home";document.querySelectorAll(".navbar__link").forEach(a=>{a.classList.toggle("active",a.dataset.navigate===e)})}function sa(){return`
21    <footer class="footer" id="footer">
22      <div class="footer__inner">
23        <div class="footer__top">
24          <div class="footer__brand">
25            <div class="footer__brand-logo">
26              <img class="footer__logo-icon" src="/assets/Logo Simple 2.png" alt="BizWrap.ai logo" style="width: auto; height: 48px;" />
27            </div>
28            <p>We build the intelligent operating systems home-service businesses run on — the studio behind Crystal.</p>
29          </div>
30
31          <div class="footer__col">
32            <h4 class="footer__heading">Navigate</h4>
33            <a class="footer__link" data-navigate="home">Home</a>
34            <a class="footer__link" data-navigate="home">Platform</a>
35            <a class="footer__link" data-navigate="contact">Contact</a>
36            <a class="footer__link" data-navigate="chess">Chess</a>
37            <a class="footer__link" data-navigate="admin-login">Admin sign in</a>
38          </div>
39
40          <div class="footer__col">
41            <h4 class="footer__heading">Get in touch</h4>
42            <a class="footer__link" href="mailto:[email protected]">[email protected]</a>
43            <a class="footer__link" data-navigate="contact">Book a Workshop</a>
44          </div>
45        </div>
46
47        <div class="footer__bottom">
48          <span class="footer__copyright">&copy; ${new Date().getFullYear()} BizWrap.ai — Giftwrap your business.</span>
49          <div class="footer__socials">
50            <a class="footer__social-link" href="#" aria-label="Facebook">
51              <svg viewBox="0 0 24 24" fill="currentColor"><path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/></svg>
52            </a>
53            <a class="footer__social-link" href="#" aria-label="LinkedIn">
54              <svg viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
55            </a>
56            <a class="footer__social-link" href="#" aria-label="YouTube">
57              <svg viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
58            </a>
59            <a class="footer__social-link" href="https://www.skool.com" target="_blank" rel="noopener" aria-label="Skool">
60              <svg viewBox="0 0 24 24" fill="currentColor"><path d="M11.64 2.6a1 1 0 0 1 .72 0l9.5 3.62a1 1 0 0 1 0 1.87l-9.5 3.63a1 1 0 0 1-.72 0l-6.45-2.46v3.66a1.2 1.2 0 0 1-.35.85l-.9.9-.9-.9a1.2 1.2 0 0 1-.35-.85V8.7l-.5-.19a1 1 0 0 1 0-1.87l9.45-3.61zM6 12.86l5.28 2.02a2 2 0 0 0 1.44 0L18 12.86v2.99c0 .66-.33 1.28-.9 1.62-1.27.78-3.2 1.58-5.1 1.58s-3.83-.8-5.1-1.58a1.9 1.9 0 0 1-.9-1.62v-2.99z"/></svg>
61            </a>
62          </div>
63        </div>
64      </div>
65    </footer>
66  `}const Pt=window.matchMedia("(prefers-reduced-motion: reduce)").matches;function vt(){const e=document.querySelectorAll(".reveal:not(.revealed)");if(Pt){e.forEach(t=>t.classList.add("revealed"));return}const a=new IntersectionObserver(t=>{t.forEach(n=>
66{n.isIntersecting&&(n.target.classList.add("revealed"),a.unobserve(n.target))})},{threshold:.12,rootMargin:"0px 0px -40px 0px"});e.forEach(t=>a.observe(t))}function Fe(e,a,t="",n=1600){const o=document.getElementById(e);if(!o)return;if(Pt){o.textContent=a+t;return}const s=new IntersectionObserver(i=>{i.forEach(r=>{if(!r.isIntersecting)return;const d=performance.now();function l(c){const p=Math.min((c-d)/n,1),u=1-Math.pow(1-p,3);o.textContent=Math.floor(u*a)+t,p<1?requestAnimationFrame(l):o.textContent=a+t}requestAnimationFrame(l),s.unobserve(r.target)})},{threshold:.5});s.observe(o)}const V={os:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/></svg>',agent:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="8" width="16" height="12" rx="3"/><path d="M12 8V4"/><circle cx="12" cy="3" r="1.4"/><path d="M9 13h.01M15 13h.01"/><path d="M8 20v1M16 20v1"/></svg>',spatial:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>',gauge:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 14a3 3 0 1 0-3-3"/><path d="M12 14l4-4"/><path d="M3 12a9 9 0 1 1 18 0"/></svg>',box:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.29 7 12 12 20.71 7"/><line x1="12" y1="22" x2="12" y2="12"/></svg>',pen:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 19l7-7 3 3-7 7-3-3z"/><path d="M18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5z"/><path d="M2 2l7.586 7.586"/><circle cx="11" cy="11" r="2"/></svg>',chart:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 15l4-4 3 3 5-6"/></svg>',arrow:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"/><polyline points="12 5 19 12 12 19"/></svg>',chev:'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 6 15 12 9 18"/></svg>'},Ke=[{label:"Inventory"},{label:"Sales & quotes"},{label:"Design studio"},{label:"Property mapping"},{label:"Crew ops"},{label:"Financials"}],At=["var(--purple)","var(--cyan)","var(--amber)"],lt=["var(--purple)","var(--cyan)","var(--amber-ink)"],G=400,q=380,Ce=196,Ie=48,wt=290,na=["#684ac5","#01c7cc","#d97706"],$e=Ke.map((e,a)=>{const t=-Math.PI/2+a*2*Math.PI/Ke.length,n=G+wt*Math.cos(t),o=q+wt*Math.sin(t),s=G-n,i=q-o,r=Ce/2,d=Ie/2;let l=1/0;Math.abs(s)>.001&&(l=Math.min(l,r/Math.abs(s))),Math.abs(i)>.001&&(l=Math.min(l,d/Math.abs(i)));const c=n+s*l,p=o+i*l,u=t+Math.PI/2,g=40,y=(c+G)/2+g*Math.cos(u),m=(p+q)/2+g*Math.sin(u);return{ox:c,oy:p,mx:y,my:m,tx:n,ty:o,angle:t}});G-86,q-86,G+86,q+86,`${$e.map((e,a)=>`<path id="fp${a}" d="M${e.ox.toFixed(1)},${e.oy.toFixed(1)} Q${e.mx.toFixed(1)},${e.my.toFixed(1)} ${G},${q}" fill="none" stroke="#dcdfe6" stroke-width="1.6"/>`).join(`
67    `)}${$e.map((e,a)=>{const t=Ke[a],n=a%3,o=e.tx-Ce/2,s=e.ty-Ie/2,i=o+14,r=e.ty-13,d=o+52,l=e.ty+5;return`
68      <g filter="url(#cardGlow${n})">
69        <rect x="${o.toFixed(1)}" y="${s.toFixed(1)}" width="${Ce}" height="${Ie}" rx="13" fill="#ffffff" stroke="${na[n]}" stroke-width="1.5" stroke-opacity="0.6"/>
70        <rect x="${i.toFixed(1)}" y="${r.toFixed(1)}" width="26" height="26" rx="7" style="fill:${At[a%3]}"/>
71        <text x="${d.toFixed(1)}" y="${l.toFixed(1)}" font-family="Outfit, sans-serif" font-weight="600" font-size="15" fill="#1c1f24">${t.label}</text>
72      </g>`}).join("")}${$e.map((n,o)=>{const s=lt[o%3],i=o*.42;let r="";for(let d=16;d>=1;d--){const l=d/16,c=(5*Math.pow(1-l,1.4)).toFixed(1),p=(.7*Math.pow(1-l,1.2)).toFixed(2),u=(i+d*.035).toFixed(3),g=d<=4?" filter:url(#tailHaze);":"";r+=`
73      <circle r="${c}" style="fill:${s}; opacity:${p};${g}">
74        <animateMotion dur="${2.6}s" begin="${u}
74s" repeatCount="indefinite">
75          <mpath href="#fp${o}"/>
76        </animateMotion>
77      </circle>`}return r+=`
78      <circle r="7" style="fill:${s}; opacity:0.85; filter:url(#cometHead)">
79        <animateMotion dur="${2.6}s" begin="${i.toFixed(2)}s" repeatCount="indefinite">
80          <mpath href="#fp${o}"/>
81        </animateMotion>
82      </circle>`,r+=`
83      <circle r="3" style="fill:#fff">
84        <animateMotion dur="${2.6}s" begin="${i.toFixed(2)}s" repeatCount="indefinite">
85          <mpath href="#fp${o}"/>
86        </animateMotion>
87      </circle>`,r}).join("")}${G}${q}${G}${q}${G}${q}${G}${q}${G}${q}${G}${q}${G}${q}${G}${q}${G}${q}${G}${q}${G}`,q-6,`${G}`,q+19;const Ge=72,oa=G-Ge,ia=q-Ge,ra=`
88  <svg class="arch arch--radial" viewBox="0 0 800 760" role="img"
89       aria-label="Six business systems — inventory, sales, design, mapping, crews, finance — drafted as a blueprint, stamped into cards, then streaming into one central AI Core.">
90    <defs>
91      <pattern id="archGrid" width="34" height="34" patternUnits="userSpaceOnUse">
92        <path d="M34 0H0V34" fill="none" stroke="#684ac5" stroke-width="1" stroke-opacity="0.12"/>
93      </pattern>
94      <linearGradient id="archHubGrad" x1="${oa}" y1="${ia}" x2="${G+Ge}" y2="${q+Ge}" gradientUnits="userSpaceOnUse">
95        <stop offset="0" stop-color="#684ac5"/>
96        <stop offset="0.5" stop-color="#01c7cc"/>
97        <stop offset="1" stop-color="#fecb05"/>
98      </linearGradient>
99      <radialGradient id="archHubGlow" cx="50%" cy="50%" r="50%">
100        <stop offset="0" stop-color="#684ac5" stop-opacity="0.30"/>
101        <stop offset="1" stop-color="#684ac5" stop-opacity="0"/>
102      </radialGradient>
103      <filter id="archCometHead" x="-120%" y="-120%" width="340%" height="340%">
104        <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b1"/>
105        <feGaussianBlur in="SourceGraphic" stdDeviation="2.5" result="b2"/>
106        <feMerge><feMergeNode in="b1"/><feMergeNode in="b2"/><feMergeNode in="SourceGraphic"/></feMerge>
107      </filter>
108      <filter id="archTailHaze" x="-60%" y="-60%" width="220%" height="220%">
109        <feGaussianBlur in="SourceGraphic" stdDeviation="3"/>
110      </filter>
111    </defs>
112
113    <rect class="arch-grid" x="0" y="0" width="800" height="760" fill="url(#archGrid)"/>
114
115    <!-- Connectors drawn out from the center (double as comet motion paths) -->
116    ${$e.map((e,a)=>`<path id="archfp${a}" class="arch-wire" d="M${e.ox.toFixed(1)},${e.oy.toFixed(1)} Q${e.mx.toFixed(1)},${e.my.toFixed(1)} ${G},${q}" pathLength="1" style="--wd:${(.3+a*.16).toFixed(2)}s"/>`).join("")}
117
118    <!-- Converging comet flow (revealed once the system is built) -->
119    <g class="arch-flow">
120      ${$e.map((n,o)=>{const s=lt[o%3],i=o*.42;let r="";for(let d=16;d>=1;d--){const l=d/16,c=(5*Math.pow(1-l,1.4)).toFixed(1),p=(.7*Math.pow(1-l,1.2)).toFixed(2),u=(i+d*.035).toFixed(3),g=d<=4?" filter:url(#archTailHaze);":"";r+=`<circle r="${c}" style="fill:${s}; opacity:${p};${g}"><animateMotion dur="${2.6}s" begin="${u}s" repeatCount="indefinite"><mpath href="#archfp${o}"/></animateMotion></circle>`}return r+=`<circle r="7" style="fill:${s}; opacity:0.85; filter:url(#archCometHead)"><animateMotion dur="${2.6}s" begin="${i.toFixed(2)}s" repeatCount="indefinite"><mpath href="#archfp${o}"/></animateMotion></circle>`,r+=`<circle r="3" style="fill:#fff"><animateMotion dur="${2.6}s" begin="${i.toFixed(2)}s" repeatCount="indefinite"><mpath href="#archfp${o}"/></animateMotion></circle>`,r}).join("")}
121    </g>
122
123    <!-- AI Core hub: glow + spinning flywheel, pulsing ring, gradient disc -->
124    <g class="arch-core" style="--c:var(--purple)">
125      <g class="arch-fly">
126        <circle cx="${G}" cy="${q}" r="118" fill="url(#archHubGlow)"/>
127        <circle cx="${G}" cy="${q}" r="82" fill="none" stroke="#e7e9ee" stroke-width="14" opacity="0.4"/>
128        <g class="arch-fly-spin">
129          <circle cx="${G}" cy="${q}" r="82" fill="none" stroke="#684ac5" stroke-width="14" stroke-dasharray="215 300" opacity="0.9"/>
130          <circle cx="${G}" cy="${q}" r="82" fill="none" stroke="#01c7cc" stroke-width="14" stroke-dasharray="170 345" stroke-dashoffset="-215" opacity="0.9"/>
131          <circle cx="${G}" cy="${q}" r="82" fill="none" stroke="#fecb05" stroke-width="14" stroke-dasharray="130 385" stroke-dashoffset="-385" opacity="0.9"/>
132        </g>
133      </g>
134      <circle class="arch-core__pulse" cx="${G}" cy="${q}
134" r="86" fill="none" stroke="url(#archHubGrad)" stroke-width="2"/>
135      <circle class="arch-core__fill" cx="${G}" cy="${q}" r="${Ge}" fill="url(#archHubGrad)"/>
136      <text class="arch-core__label" x="${G}" y="${q-6}" text-anchor="middle" font-weight="800" font-size="23" fill="#fff">Core</text>
137      <text class="arch-core__sub" x="${G}" y="${q+18}" text-anchor="middle" font-weight="600" font-size="11.5" fill="rgba(255,255,255,0.92)">recursive flywheel</text>
138    </g>
139
140    <!-- Modules: blueprint outline → stamped into solid cards (radially staggered) -->
141    ${$e.map((e,a)=>{const t=a%3,n=e.tx-Ce/2,o=e.ty-Ie/2,s=n+14,i=e.ty-13,r=n+52,d=e.ty+5,l=.45+a*.16,c=(l+.25).toFixed(2),p=(l+.5).toFixed(2);return`
142    <g class="arch-mod" style="--c:${lt[t]}; --d:${l.toFixed(2)}s; --ld:${c}s; --md:${p}s">
143      <rect class="arch-mod__fill" x="${n.toFixed(1)}" y="${o.toFixed(1)}" width="${Ce}" height="${Ie}" rx="13" fill="#fff"/>
144      <rect class="arch-mod__line" x="${n.toFixed(1)}" y="${o.toFixed(1)}" width="${Ce}" height="${Ie}" rx="13" pathLength="1"/>
145      <rect class="arch-mod__accent" x="${s.toFixed(1)}" y="${i.toFixed(1)}" width="26" height="26" rx="7" fill="${At[t]}"/>
146      <text class="arch-mod__label" x="${r.toFixed(1)}" y="${d.toFixed(1)}">${Ke[a].label}</text>
147    </g>`}).join("")}
148  </svg>`,je={VBW:920,VBH:545},We=[{label:"LLM · decide",cx:300,cy:135,side:"top",col:"--purple",legL:210,legR:390,d:1.5,td:0},{label:"Agent · verify",cx:470,cy:455,side:"bottom",col:"--cyan",legL:390,legR:550,d:1.8,td:1},{label:"LLM · route",cx:640,cy:135,side:"top",col:"--amber",legL:550,legR:730,d:2.1,td:2}],la=[70,210,390,550,730],da=[300,470,640],ca="M70,290 H800 C882,290 882,508 800,508 H140 C58,508 58,290 70,290 Z",pa="M800,290 C882,290 882,508 800,508 H140 C58,508 58,290 70,290",dt=e=>e.side==="top"?e.cy+31:e.cy-31,ua=e=>`M${e.cx},290 L${e.cx-48},${dt(e)} L${e.cx+48},${dt(e)} L${e.cx},290`,ha=`
149  <svg class="rl" viewBox="0 0 ${je.VBW} ${je.VBH}" role="img"
150       aria-label="A deterministic routing rail with LLM agents that decide and verify off the harness, executing back onto the rail and recursing through a flywheel, while humans handle oversight on top.">
151    <defs>
152      <pattern id="rlGrid" width="34" height="34" patternUnits="userSpaceOnUse">
153        <path d="M34 0H0V34" fill="none" stroke="#684ac5" stroke-width="1" stroke-opacity="0.12"/>
154      </pattern>
155      <linearGradient id="rlRailGrad" x1="70" y1="0" x2="760" y2="0" gradientUnits="userSpaceOnUse">
156        <stop offset="0" stop-color="#684ac5"/><stop offset="0.5" stop-color="#01c7cc"/><stop offset="1" stop-color="#fecb05"/>
157      </linearGradient>
158      <linearGradient id="rlDiscGrad" x1="777" y1="257" x2="843" y2="323" gradientUnits="userSpaceOnUse">
159        <stop offset="0" stop-color="#684ac5"/><stop offset="0.55" stop-color="#01c7cc"/><stop offset="1" stop-color="#fecb05"/>
160      </linearGradient>
161      <marker id="rlArrow" markerWidth="9" markerHeight="9" refX="4" refY="4" orient="auto">
162        <path d="M1,1 L7,4 L1,7" fill="none" stroke="#f2705a" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
163      </marker>
164      <!-- Soft glow halo behind the flywheel hub -->
165      <radialGradient id="rlHubGlow" cx="50%" cy="50%" r="50%">
166        <stop offset="0" stop-color="#01c7cc" stop-opacity="0.30"/>
167        <stop offset="1" stop-color="#01c7cc" stop-opacity="0"/>
168      </radialGradient>
169      <!-- Comet bloom for traveling nodes (borrowed from the convergence funnel) -->
170      <filter id="rlGlowHead" x="-160%" y="-160%" width="420%" height="420%">
171        <feGaussianBlur in="SourceGraphic" stdDeviation="6" result="b1"/>
172        <feGaussianBlur in="SourceGraphic" stdDeviation="2.5" result="b2"/>
173        <feMerge><feMergeNode in="b1"/><feMergeNode in="b2"/><feMergeNode in="SourceGraphic"/></feMerge>
174      </filter>
175      <filter id="rlGlow" x="-150%" y="-150%" width="400%" height="400%">
176        <feGaussianBlur in="SourceGraphic" stdDeviation="3" result="b"/>
177        <feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge>
178      </filter>
179      <filter id="rlHaze" x="-80%" y="-80%" width="260%" height="260%">
180        <feGaussianBlur in="SourceGraphic" stdDeviation="2.4"/>
181      </filter>
182    </defs>
183
184    <rect class="rl-grid" x="0" y="0" width="${je.VBW}" height="${je.VBH}" fill="url(#rlGrid)"/>
185
186    <!-- Human oversight lane -->
187    <g class="rl-lane-g">
188      <rect class="rl-lane" x="70" y="32" width="780" height="54" rx="16"/>
189      <g class="rl-human" transform="translate(90,47) scale(0.92)" fill="none" stroke="#0a0a0b" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
190        <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
191        <circle cx="12" cy="7" r="4"/>
192      </g>
193      <text class="rl-lane__title" x="118" y="64" font-weight="700" font-size="15" fill="#0a0a0b">Human oversight</text>
194      <text x="838" y="64" text-anchor="end" font-weight="500" font-size="13.5" fill="#5b6472">Dashboards &middot; Exceptions &middot; Monitoring &middot; Problem-solving</text>
195    </g>
196    <!-- Exceptions escalate to the humans -->
197    <path class="rl-esc" d="M640,104 L640,90" marker-end="url(#rlArrow)"/>
198
199    <!-- Deterministic rail (drawn first — "laying the track") -->
200    <path class="rl-rail" d="M70,290 H760" pathLength="1"/>
201    <!-- Recursive feedback / flywheel return -->
202    <path class="rl-return" d="${pa}"/>
203    <text class="rl-rlabel" x="440" y="500" text-anchor="middle" font-weight="500" font-size="12.5" fill="#5b6472">state feeds back — the loop compounds</text>
204
205    <!-- Invisible motion paths -->
206    <path id="rlLoop" d="${ca}" fill="none" stroke="none"/>
207    ${We.map((e,a)=>`<path id="rlBranch${a}" d="${ua(e)}" fill="none" stroke="none"/>`).join("")}
208
209    <!-- Dashed legs converge on the red verify gate (the model taps the rail there) -->
210    ${We.map(e=>{const a=dt(e);return`
211    <path class="rl-leg" d="M${e.cx-48},${a}
211 L${e.cx},290" style="--d:${(e.d+.05).toFixed(2)}s"/>
212    <path class="rl-leg" d="M${e.cx+48},${a} L${e.cx},290" style="--d:${(e.d+.12).toFixed(2)}s"/>`}).join("")}
213
214    <!-- Routing waypoints + verify gates -->
215    ${la.map((e,a)=>`<circle class="rl-ring" cx="${e}" cy="290" r="8" style="--d:${(.9+a*.1).toFixed(2)}s"/>`).join("")}
216    ${da.map((e,a)=>`<circle class="rl-gate" cx="${e}" cy="290" r="7" style="--d:${(1.25+a*.12).toFixed(2)}s"/>`).join("")}
217
218    <!-- Recursive flywheel -->
219    <g class="rl-fly-g">
220      <circle cx="810" cy="290" r="54" fill="url(#rlHubGlow)"/>
221      <circle cx="810" cy="290" r="33" fill="none" stroke="#e7e9ee" stroke-width="8"/>
222      <g class="rl-fly-spin">
223        <circle cx="810" cy="290" r="33" fill="none" stroke="#684ac5" stroke-width="8" stroke-dasharray="55 152"/>
224        <circle cx="810" cy="290" r="33" fill="none" stroke="#01c7cc" stroke-width="8" stroke-dasharray="45 162" stroke-dashoffset="-70"/>
225        <circle cx="810" cy="290" r="33" fill="none" stroke="#fecb05" stroke-width="8" stroke-dasharray="38 169" stroke-dashoffset="-130"/>
226      </g>
227      <circle cx="810" cy="290" r="22" fill="url(#rlDiscGrad)"/>
228      <text x="810" y="352" text-anchor="middle" font-weight="600" font-size="12" fill="#5b6472">recursive flywheel</text>
229    </g>
230
231    <!-- LLM / agent boxes -->
232    ${We.map(e=>`
233    <g class="rl-box" style="--d:${e.d}s">
234      <rect class="rl-box__bg" x="${e.cx-77}" y="${e.cy-31}" width="154" height="62" rx="14"/>
235      <rect class="rl-box__dot" x="${e.cx-60}" y="${e.cy-9}" width="18" height="18" rx="5" fill="var(${e.col})"/>
236      <text class="rl-box__label" x="${e.cx-34}" y="${e.cy+5}">${e.label}</text>
237    </g>`).join("")}
238
239    <!-- Decision tokens: glowing nodes that ride the dashed legs up from each red gate -->
240    ${We.map((e,a)=>`
241    <g class="rl-decide">
242      <circle r="5" style="fill:var(${e.col}); opacity:0.85; filter:url(#rlGlow)">
243        <animateMotion dur="3.2s" begin="${e.td}s" repeatCount="indefinite" calcMode="linear"><mpath href="#rlBranch${a}"/></animateMotion>
244      </circle>
245      <circle r="2.2" fill="#fff">
246        <animateMotion dur="3.2s" begin="${e.td}s" repeatCount="indefinite" calcMode="linear"><mpath href="#rlBranch${a}"/></animateMotion>
247      </circle>
248    </g>`).join("")}
249
250    <!-- Main token: a glowing comet streaking along the deterministic rail + flywheel loop -->
251    <g class="rl-token">
252      ${(()=>{const n="#01c7cc";let o="";for(let s=12;s>=1;s--){const i=s/12,r=(5.4*Math.pow(1-i,1.3)).toFixed(1),d=(.7*Math.pow(1-i,1.1)).toFixed(2),l=(s*.02).toFixed(3),c=s<=4?" filter:url(#rlHaze);":"";o+=`
253      <circle r="${r}" style="fill:${n}; opacity:${d};${c}">
254        <animateMotion dur="${7.5}s" begin="${l}s" repeatCount="indefinite" calcMode="linear"><mpath href="#rlLoop"/></animateMotion>
255      </circle>`}return o+=`
256      <circle r="7" style="fill:${n}; opacity:0.9; filter:url(#rlGlowHead)">
257        <animateMotion dur="${7.5}s" begin="0s" repeatCount="indefinite" calcMode="linear"><mpath href="#rlLoop"/></animateMotion>
258      </circle>
259      <circle r="3" style="fill:#fff">
260        <animateMotion dur="${7.5}s" begin="0s" repeatCount="indefinite" calcMode="linear"><mpath href="#rlLoop"/></animateMotion>
261      </circle>`,o})()}
262    </g>
263
264    <!-- Deterministic routing logic label -->
265    <g class="rl-pill-g">
266      <rect class="rl-pill" x="20" y="232" width="232" height="36" rx="18"/>
267      <text x="136" y="255" text-anchor="middle" font-weight="700" font-size="13.5" fill="#0a0a0b">Deterministic routing logic</text>
268    </g>
269  </svg>`,Gt=[{id:"ltv",label:"LTV",sub:"Lifetime value",value:4820,step:[6,55]},{id:"aov",label:"AOV",sub:"Avg order value",value:1240,step:[3,24]},{id:"cac",label:"CAC",sub:"Acquisition cost",value:312,step:[1,8],goodDown:!0,floor:150},{id:"mrr",label:"MRR",sub:"Monthly revenue",value:86400,step:[60,520]},{id:"cvr",label:"Conv.",sub:"Lead → close",value:34.2,step:[.1,.5],pct:!0,dec:1,cap:64},{id:"ret",label:"Retention",sub:"Net 12-mo",value:96.1,step:[.05,.25],pct:!0,dec:1,cap:99.4}];function ct(e,a=e.value){return e.pct?a.toFixed(e.dec)+"%":"$"+Math.round(a).toLocaleStr
269ing("en-US")}function Et(){return`
270    <!-- HERO -->
271    <section class="hero hero--home" id="home-hero">
272      <div class="hero__grid hero__grid--center">
273        <div class="hero__copy">
274          <div class="brandmark reveal">
275            <img class="brandmark__photo" src="/assets/BizWrap.png" alt="BizWrap.ai — Giftwrap your business." style="display:none"
276                 onload="this.style.display='block'; this.nextElementSibling.style.display='none';" />
277            <div class="brandmark__lockup">
278              <img class="brandmark__icon" src="/assets/logo-mark.svg" alt="" />
279              <div class="brandmark__words">
280                <div class="brandmark__word"><span class="b">Biz</span><span class="w">Wrap</span><span class="ai">.ai</span></div>
281                <div class="brandmark__tag">Giftwrap your business.</div>
282              </div>
283            </div>
284          </div>
285          <h1 class="hero__title reveal reveal-delay-1">
286            The operating system your <span class="grad-text">business thrives</span> on.
287          </h1>
288          <p class="hero__subtitle reveal reveal-delay-2">
289            BizWrap builds intelligent operating systems and AI agents that wrap every part of your operation — inventory, mapping, design, sales, crews, finance — into one system with a single AI core. We're the studio behind <strong>Crystal</strong>.
290          </p>
291          <div class="hero__actions reveal reveal-delay-3">
292            <a class="btn btn--primary btn--lg btn--icon-right" href="#home-capabilities" id="hero-cta-platform">
293              Explore the Platform ${V.arrow}
294            </a>
295            <a class="btn btn--ghost btn--lg" data-navigate="contact" id="hero-cta-contact">Book a Workshop</a>
296          </div>
297        </div>
298      </div>
299    </section>
300
301    <!-- REAL-TIME SENSING -->
302    <section class="section" id="home-live" style="padding-top: 10rem;">
303      <div class="container">
304        <div class="section__head reveal">
305          <span class="eyebrow eyebrow--center">Real-time sensing</span>
306          <h2>Your business, <span class="grad-text">sensed live</span></h2>
307          <p>The core doesn't just store numbers — it watches them. Every metric updates the moment reality does.</p>
308        </div>
309        <div class="live-grid reveal">
310          ${Gt.map(e=>`
311            <div class="lm">
312              <div class="lm__top">
313                <span class="lm__label">${e.label}</span>
314                <span class="lm__live"><i></i> live</span>
315              </div>
316              <div class="lm__value" id="lm-${e.id}">${ct(e)}</div>
317              <div class="lm__foot">
318                <span class="lm__sub">${e.sub}</span>
319                <span class="lm__delta is-up" id="lm-${e.id}-d">▲ 0.0%</span>
320              </div>
321            </div>`).join("")}
322        </div>
323
324        <!-- ARCHITECT GRAPHIC — blueprint → built (below KPI cards) -->
325        <p class="arch-caption reveal" style="margin-top: var(--space-12);">First, we architect &amp; build it — from blueprint to built.</p>
326        <div class="arch-stage reveal">
327          ${ra}
328        </div>
329
330        <!-- CORE RECURSIVE FLYWHEEL — not just consulting -->
331        <div class="section__head reveal" style="margin-top: 10rem;">
332          <span class="eyebrow eyebrow--center">Not just consulting</span>
333          <h2>Deterministic routing, <span class="grad-text">recursive self-healing flywheel</span></h2>
334          <p>Strategy is where most firms stop. We keep going — architecting and building the AI-native applications and operating systems your business runs on, then laying the deterministic routing logic that makes them run themselves. LLM agents decide, verify their output off the harness, and execute — and it compounds into a recursive, self-healing flywheel. Humans evolve workflows: dashboards, exception handling, monitoring, and problem-solving.</p>
335        </div>
336
337        <p class="arch-caption arch-caption--gap reveal">Then it runs itself — deterministic routing into a recursive, self-healing flywheel.</p>
338        <div class="arch-stage reveal">
339          ${ha}
340        </div>
341      </div>
342    </section>
343
344    <!-- PROCESS — how we engage -->
345    <section class="section" id="home-process" style="padding-top: 5rem;">
346      <div class="container">
347        <div class="section__head reveal">
348          <span class="eyebrow eyebrow--center">How we engage</span>
349          <h2>From <span class="grad-text">friction</span> to flow</h2>
350          <p>We don't hand you a tool and walk away. We map your business, architect the system, and stay to make it smarter.</p>
351        </div>
352        <div class="flow">
353          <div class="flow__step reveal reveal-delay-1">
354            <div class="flow__num">1</div>
355            <h4>Map</h4>
356            <p>We learn your operation end to end and chart every discipline, handoff, and source of truth.</p>
357          </div>
358          <div class="flow__step reveal reveal-delay-2">
359            <div class="flow__num">2</div>
360            <h4>Architect</h4>
361            <p>We design the unified system and its AI core, prototyping and iterating against real workflows.</p>
362          </div>
363          <div class="flow__step reveal reveal-delay-3">
364            <div class="flow__num">3</div>
365            <h4>Deploy &amp; evolve</h4>
366            <p>We launch on every device, secured by role — then keep tuning the intelligence season after season.</p>
367          </div>
368        </div>
369      </div>
370    </section>
371
372    <!-- AGENT LOOP — how the intelligence works -->
373    <section class="section" id="home-loop" style="padding-top: 5rem;">
374      <div class="container">
375        <div class="section__head reveal">
376          <span class="eyebrow eyebrow--center">How the intelligence works</span>
377          <h2>Agents that <span class="grad-text">close the loop</span></h2>
378          <p>Our systems don't wait to be asked. They perceive the state of your business, reason over it, take action, and get sharper every cycle.</p>
379        </div>
380        <div class="loop">
381          <div class="loop__step reveal reveal-delay-1" data-step="01">
382            <h4>Perceive</h4>
383            <p>Live data from inventory, jobs, crews, and finance streams into one model of the business.</p>
384            <span class="loop__arrow">${V.chev}</span>
385          </div>
386          <div class="loop__step reveal reveal-delay-2" data-step="02">
387            <h4>Reason</h4>
388            <p>The AI core cross-references every domain to surface risks, bottlenecks, and the next best move.</p>
389            <span class="loop__arrow">${V.chev}</span>
390          </div>
391          <div class="loop__step reveal reveal-delay-3" data-step="03">
392            <h4>Act</h4>
393            <p>It drafts quotes, sequences routes, builds pick lists, and triggers workflows — with you in command.</p>
394            <span class="loop__arrow">${V.chev}</span>
395          </div>
396          <div class="loop__step reveal reveal-delay-4" data-step="04">
397            <h4>Learn</h4>
398            <p>Every season, install, and decision feeds back in. The system you run on keeps getting smarter.</p>
399          </div>
400        </div>
401      </div>
402    </section>
403
404    <!-- WHAT WE BUILD -->
405    <section class="section" id="home-capabilities" style="padding-top: 5rem;">
406      <div class="container">
407        <div class="section__head reveal">
408          <span class="eyebrow eyebrow--center">What we build</span>
409          <h2 class="build-title">
410            <span class="bt-line"><span style="color: var(--purple)">Ecosystem</span> of <span style="color: var(--cyan)">applications</span></span>
411            <span class="bt-arrow" aria-hidden="true">
412              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="4" x2="12" y2="17"/><polyline points="6 11 12 17 18 11"/></svg>
413            </span>
414            <span class="bt-line" style="color: var(--amber)">operating system</span>
415          </h2>
416          <p>Most software records what happened. We build intelligence that tells you what to do next — and then does it.</p>
417        </div>
418        <div class="cap-grid">
419          <div class="cap-card reveal reveal-delay-1">
420            <div class="cap-card__icon">${V.os}</div>
421            <h3 class="cap-card__title">Business Operating Systems</h3>
422            <p class="cap-card__desc">
422One living interface that collapses inventory, logistics, sales, design, and finance into a single source of truth. Every module speaks the same language and draws from the same data.</p>
423          </div>
424          <div class="cap-card reveal reveal-delay-2">
425            <div class="cap-card__icon">${V.agent}</div>
426            <h3 class="cap-card__title">AI Agents &amp; Copilots</h3>
427            <p class="cap-card__desc">A conversational core woven into every screen. Ask in plain language or by voice — it reasons across the whole dataset, orchestrates the answer, and acts on it.</p>
428          </div>
429          <div class="cap-card reveal reveal-delay-3">
430            <div class="cap-card__icon">${V.spatial}</div>
431            <h3 class="cap-card__title">Field &amp; Spatial Systems</h3>
432            <p class="cap-card__desc">GPS-anchored property mapping, CAD-grade design boards, QR-driven pick lists, and crew orchestration — the machinery that turns a signed contract into a finished install.</p>
433          </div>
434        </div>
435      </div>
436    </section>
437
438
439
440    <!-- CRYSTAL CASE STUDY -->
441    <section class="section section--alt" id="home-crystal">
442      <div class="container">
443        <div class="section__head reveal">
444          <span class="eyebrow eyebrow--center">Proof of concept</span>
445          <h2><span class="grad-text">Flagship</span> build</h2>
446          <p>We didn't just imagine this. We built an entire operating system for a real, brutally complex business — and it runs the whole show.</p>
447        </div>
448
449        <div class="case reveal">
450          <div class="case__story">
451            <div class="case__story-copy">
452              <h3 class="case__title grad-text">Crystal</h3>
453              <p class="case__lede">A full operating system for Murphy Christmas Lighting — tens of thousands of assets, fleets of crews, hundreds of mapped estates, and an entire year of revenue compressed into a few decisive weeks. Where competitors juggle a dozen disconnected tools, Crystal is one living organism.</p>
454              <a class="btn btn--primary btn--icon-right" data-navigate="contact" id="crystal-cta">Book a demo ${V.arrow}</a>
455            </div>
456
457            <div class="case__visual">
458              <div class="appwin" aria-hidden="true">
459                <div class="appwin__bar"><i></i><i></i><i></i><span class="appwin__url">crystal · live</span></div>
460                <div class="appwin__body">
461                  <aside class="appwin__side">
462                    <span class="appwin__logo"></span>
463                    <span class="appwin__navi"></span><span class="appwin__navi"></span><span class="appwin__navi"></span><span class="appwin__navi"></span><span class="appwin__navi"></span>
464                  </aside>
465                  <div class="appwin__main">
466                    <div class="appwin__kpis">
467                      <span class="appwin__kpi"></span><span class="appwin__kpi"></span><span class="appwin__kpi"></span>
468                    </div>
469                    <div class="appwin__chart">
470                      <span style="height:42%"></span><span style="height:68%"></span><span style="height:54%"></span><span style="height:88%"></span><span style="height:60%"></span><span style="height:96%"></span><span style="height:74%"></span><span style="height:50%"></span>
471                    </div>
472                    <div class="appwin__tiles">
473                      <span class="appwin__tile"></span><span class="appwin__tile"></span>
474                    </div>
475                  </div>
476                </div>
477              </div>
478            </div>
479          </div>
480
481          <div class="domain-grid">
482            <div class="domain">
483              <div class="domain__top">
484                <div class="domain__icon">${V.gauge}</div>
485                <div>
486                  <div class="domain__name">I-Sight</div>
487                  <div class="domain__role">The command center</div>
488                </div>
489              </div>
490              <p class="domain__desc">Live dashboards, scheduling, operational playbooks, and company knowledge — a real-time read of the entire enterprise the moment you log in.</p>
491              <div class="domain__tags"><span class="chip">Dashboards</span><span class="chip">Scheduling</span><span class="chip">Playbooks</span></div>
492            </div>
493
494            <div class="domain">
495              <div class="domain__top">
496                <div class="domain__icon">${V.box}</div>
497                <div>
498                  <div class="domain__name">Giftwrap</div>
499                  <div class="domain__role">The logistics engine</div>
500                </div>
501              </div>
502              <p class="domain__desc">Spatial warehouse mapping, QR pick lists, kit assembly, audits, low-stock intelligence, a sales pipeline, vendor management, and live asset valuation.</p>
503              <div class="domain__tags"><span class="chip">Warehouse</span><span class="chip">QR pick lists</span><span class="chip">Pipeline</span></div>
504            </div>
505
506            <div class="domain">
507              <div class="domain__top">
508                <div class="domain__icon">${V.pen}</div>
509                <div>
510                  <div class="domain__name">Studio</div>
511                  <div class="domain__role">The creative forge</div>
512                </div>
513              </div>
514              <p class="domain__desc">A CAD-grade Design Board that renders lighting concepts onto a property, GPS-anchored project mapping of every job site, and a built-in marketing studio.</p>
515              <div class="domain__tags"><span class="chip">Design Board</span><span class="chip">Project mapping</span><span class="chip">Marketing</span></div>
516            </div>
517
518            <div class="domain">
519              <div class="domain__top">
520                <div class="domain__icon">${V.chart}</div>
521                <div>
522                  <div class="domain__name">Executive</div>
523                  <div class="domain__role">The boardroom</div>
524                </div>
525              </div>
526              <p class="domain__desc">Financials, reporting, and intake automation that translate ground-level activity into the numbers that steer the company.</p>
527              <div class="domain__tags"><span class="chip">Financials</span><span class="chip">Reporting</span><span class="chip">Intake</span></div>
528            </div>
529          </div>
530
531          <div class="intel">
532            <div class="intel__orb"></div>
533            <div>
534              <h4>Crystal Intelligence</h4>
535              <p>At the center sits Crystal herself — a conversational AI core woven into every screen. Ask a question in plain language or by voice and she reasons across inventory, projects, vendors, and finances. This is the difference between software that records what happened and intelligence that tells you what to do next.</p>
536            </div>
537          </div>
538        </div>
539      </div>
540    </section>
541
542    <!-- MULTIMODALITY -->
543    <section class="section" id="home-multimodality" style="padding-bottom: 0;">
544      <div class="container">
545        <div class="section__head reveal">
546          <span class="eyebrow eyebrow--center">Capabilities</span>
547          <h2 class="grad-text">Multimodality</h2>
548          <p>Each capability is powerful alone. Together — drawing from the same data and the same intelligence — they become an operating system.</p>
549        </div>
550      </div>
551    </section>
552
553    <!-- METRICS -->
554    <section class="section" id="home-metrics" style="padding-top: var(--space-8);">
555      <div class="container">
556        <div class="metrics reveal">
557          <div class="metric"><div class="metric__num" id="m-assets">0</div><div class="metric__label">Assets orchestrated</div></div>
558          <div class="metric"><div class="metric__num" id="m-props">0</div><div class="metric__label">Properties mapped</div></div>
559          <div class="metric"><div class="metric__num" id="m-domains">0</div><div class="metric__label">Operating domains</div></div>
560          <div class="metric"><div class="metric__num" id="m-truth">0</div><div class="metric__label">Source of truth</div></div>
561        </div>
562      </div>
563    </section>
564
565
566    <!-- MEET THE FOUNDER -->
567    <section class="section" id="home-founder" style="padding-top: 5rem;">
568      <div class="container">
569        <div class="section__head reveal">
570          <span class="eyebrow eyebrow--center">About</span>
571          <h2>Meet the <span class="grad-text">Founder</span></h2>
572        </div>
573        <div class="founder reveal">
574          <div class="founder__photo-wrap">
575            <img class="founder__photo" src="/assets/founder.jpg" alt="Joey Murphy — Founder of BizWrap.ai" />
576          </div>
577          <div class="founder__bio">
578            <h3 class="founder__name">Joey Murphy</h3>
579            <p>Joey Murphy is a business operator, technologist, and founder of BizWrap.ai. He built and scaled Murphy Christmas Lighting into one of the most operationally s
579ophisticated home-service companies in the industry — managing tens of thousands of assets, hundreds of mapped properties, and full seasonal logistics.</p>
580            <p>When off-the-shelf software couldn't keep up, he started building his own. That side project became Crystal — a full operating system with an AI core that now runs every corner of the business, from warehouse to rooftop to boardroom.</p>
581            <p>Today, through BizWrap.ai, Joey is turning that hard-won playbook into intelligent systems for other operators who refuse to outgrow their tools.</p>
582            <a class="btn btn--primary btn--icon-right" data-navigate="contact" id="founder-cta">
583              Start a conversation ${V.arrow}
584            </a>
585          </div>
586        </div>
587      </div>
588    </section>
589
590    <!-- COMPANY INTRO VIDEO -->
591    <section class="section" id="home-video">
592      <div class="container">
593        <div class="intro-video reveal">
594          <video class="intro-video__player" id="intro-video"
595                 playsinline preload="auto">
596            <source src="/assets/Company_intro_video_technology_202605252356.mp4" type="video/mp4" />
597          </video>
598          <button class="intro-video__mute" id="intro-video-mute" aria-label="Toggle audio">
599            <svg class="ico-sound" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg>
600            <svg class="ico-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/></svg>
601          </button>
602        </div>
603      </div>
604    </section>
605
606    <!-- CTA -->
607    <section class="section" id="home-cta">
608      <div class="container">
609        <div class="cta reveal">
610          <span class="eyebrow eyebrow--center">Let's build it</span>
611          <h2>Turn your company into a<br>technology company.</h2>
612          <p>Tell us how your business runs today. We'll show you the system it could run on tomorrow.</p>
613          <div class="hero__actions">
614            <a class="btn btn--primary btn--lg btn--icon-right" data-navigate="contact" id="home-cta-button">
615              Start a conversation ${V.arrow}
616            </a>
617          </div>
618        </div>
619      </div>
620    </section>
621  `}function _t(){ma(),vt(),Fe("m-assets",40,"k+"),Fe("m-props",300,"+"),Fe("m-domains",4,""),Fe("m-truth",1,""),va();const e=n=>{const o=document.querySelector(n);if(!o)return;if(window.matchMedia("(prefers-reduced-motion: reduce)").matches){o.classList.add("is-built");return}new IntersectionObserver(([i],r)=>{i.isIntersecting&&(o.classList.add("is-building"),r.disconnect())},{threshold:.25}).observe(o)};e(".arch"),e(".rl");const a=document.getElementById("intro-video");if(a){const n=new IntersectionObserver(([o])=>{o.isIntersecting&&(a.play().catch(()=>{a.muted=!0,a.play()}),n.disconnect())},{threshold:.4});n.observe(a)}const t=document.getElementById("intro-video-mute");if(a&&t){const n=()=>t.classList.toggle("is-muted",a.muted);n(),t.addEventListener("click",()=>{a.muted=!a.muted,n()})}a&&a.addEventListener("click",()=>{(a.paused||a.ended)&&(a.currentTime=0,a.play())}),document.querySelectorAll('a[href^="#home-"]').forEach(n=>{n.addEventListener("click",o=>{o.preventDefault();const s=document.querySelector(n.getAttribute("href"));s&&s.scrollIntoView({behavior:"smooth",block:"start"})})})}function ma(){const e=document.getElementById("page-content");e&&e.querySelectorAll(".section").forEach((a,t)=>{const n=t%2===0?"reveal--from-left":"reveal--from-right";a.querySelectorAll(".reveal").forEach(o=>{o.classList.contains("section__head")?o.classList.add("jm-head"):o.classList.add(n)})})}function va(){window.matchMedia("(prefers-reduced-motion: reduce)").matches||Gt.forEach(e=>{const a=document.getElementById("lm-"+e.id),t=document.getElementById("lm-"+e.id+"-d");if(!a)return;const n=()=>{document.body.contains(a)&&(fa(e,a,t),setTimeout(n,1300+Math.random()*2400))};setTimeout(n,500+Math.random()*1800)})}function fa(e,a,t){const[n,o]=e.step,s=n+Math.random()*(o-n),i=Math.random()<(e.goodDown?.25:.8);let r=e.value+(i?s:-s);e.pct&&(r=Math.min(e.cap,Math.max(0,r))),e.floor&&(r=Math.max(e.floor,r));const d=e.value,l=d?Math.abs((r-d)/d*100):0;if(ga(a,d,r,e),e.value=r,t){const c=e.goodDown?r<d:r>d;t.textContent=(r>=d?"▲ ":"▼ ")+l.toFixed(1)+"%",t.className="lm__delta "+(c?"is-up":"is-down")}a.classList.remove("flash"),a.offsetWidth,a.classList.add("flash")}function ga(e,a,t,n,o=650){const s=performance.now();function i(r){const d=Math.min((r-s)/o,1),l=1-Math.pow(1-d,3);e.textContent=ct(n,a+(t-a)*l),d<1?requestAnimationFrame(i):e.textContent=ct(n,t)}requestAnimationFrame(i)}const ya=!["localhost","127.0.0.1"].includes(window.location.hostname),O=ya?"/api":"http://localhost:5000/api",ba=[{question:"What exactly does BizWrap build?",answer:"Intelligent operating systems and AI agents for home-service businesses. Instead of stitching together a dozen tools, we build one unified system — with a conversational AI core — that runs your inventory, mapping, design, sales, crews, and finance."},{question:"Do I have to be a Christmas-lighting company?",answer:"Not at all. Crystal happens to power a large-scale holiday-lighting operation, but the same architecture fits any field-heavy, seasonal, or multi-crew home-service business — landscaping, pools, restoration, HVAC, and more."},{question:"How long does it take to build a system?",answer:"We start with a focused mapping phase, then ship a working core in weeks and grow it module by module. You see value early instead of waiting for one giant launch."},{question:"Do I need a technical team to run it?",answer:"No. The whole point of an AI core is that you talk to your business in plain language. We handle the engineering, the deployment, and the ongoing tuning."},{question:"What does it cost?",answer:"Every system is scoped to the operation it runs. We offer fixed-scope builds and ongoing partnerships. Reach out and we will put real numbers to your real workflows."}];function wa(){return`
622    <!-- HERO -->
623    <section class="hero hero--page hero--compact" id="contact-hero">
624      <div class="hero__grid hero__grid--center">
625        <div class="hero__copy">
626          <h1 class="hero__title reveal">Book a <span class="grad-text">demo</span>.</h1>
627          <p class="hero__subtitle reveal reveal-delay-1">
628            Tell us how your business runs today and we'll show you the system it could run on. We reply within 24 hours.
629          </p>
630        </div>
631      </div>
632    </section>
633
634    <!-- CONTACT CONTENT -->
635    <section class="section" id="contact-content" style="padding-top:var(--space-4); padding-bottom:var(--space-16);">
636      <div class="container">
637        <div class="section__head reveal" style="margin-bottom:var(--space-10);">
638          <span class="eyebrow eyebrow--center">Get in touch</span>
639          <h2 style="margin-top:var(--space-4);">Let's <span class="grad-text">talk</span></h2>
640        </div>
641        <div class="contact-layout">
642
643          <!-- FORM -->
644          <div class="form-card reveal" id="form-card-container">
645            <h3 style="margin-bottom: var(--space-6);">Send us a message</h3>
646            <form id="contact-form">
647              <div class="form-group">
648                <label class="form-label" for="contact-name">Full name</label>
649                <input class="form-input" type="text" id="contact-name" name="name" placeholder="Joey Murphy" required>
650              </div>
651
652              <div class="form-group">
653                <label class="form-label" for="contact-email">Email address</label>
654                <input class="form-input" type="email" id="contact-email" name="email" placeholder="[email protected]" required>
655              </div>
656
657              <div class="form-group">
658                <label class="form-label" for="contact-company">Company</label>
659                <input class="form-input" type="text" id="contact-company" name="company" placeholder="Your business">
660              </div>
661
662              <div class="form-group">
663                <label class="form-label" for="contact-service">What are you looking to build?</label>
664                <select class="form-select" id="contact-service" name="service">
665                  <option value="" disabled selected>Select an option...</option>
666                  <option value="os">Full operating system</option>
667                  <option value="agent">AI agent / copilot</option>
668                  <option value="spatial">Property mapping &amp; field tools</option>
669                  <option value="design">Design studio</option>
670                  <option value="logistics">Warehouse &amp; logistics</option>
671                  <option value="financials">Financials &amp; reporting</option>
672                  <option value="other">Other / not sure yet</option>
673                </select>
674              </div>
675
676              <div class="form-group">
677                <label class="form-label" for="contact-message">Tell us about your operation</label>
678                <textarea class="form-textarea" id="contact-message" name="message" placeholder="How does your business run today? Where does it hurt?" required></textarea>
679              </div>
680
681              <button class="btn btn--primary btn--lg" type="submit" id="contact-submit" style="width: 100%;">
682                <span id="submit-text">Send message</span>
683                <span id="submit-loading" style="display:none;">Sending...</span>
684              </button>
685
686              <div id="form-status" class="form-status"></div>
687            </form>
688          </div>
689
690          <!-- INFO + FAQ -->
691          <div class="reveal reveal-delay-2">
692            <h3 style="margin-bottom: var(--space-6);">Contact</h3>
693
694            <div class="contact-info-card">
695              <div class="contact-info-card__icon">
696                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
697              </div>
698              <div>
699                <div class="contact-info-card__label">Email</div>
700                <a class="contact-info-card__value" href="mailto:[email protected]">[email protected]</a>
701              </div>
702            </div>
703
704            <div class="contact-info-card">
705              <div class="contact-info-card__icon">
706                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>
707              </div>
708              <div>
709                <div class="contact-info-card__label">Based in</div>
710                <div class="contact-info-card__value">United States</div>
711              </div>
712            </div>
713
714            <div class="contact-info-card">
715              <div class="contact-info-card__icon">
716                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
717              </div>
718              <div>
719                <div class="contact-info-card__label">Response time</div>
720                <div class="contact-info-card__value">Within 24 hours</div>
721              </div>
722            </div>
723
724            <h3 style="margin-top: var(--space-10); margin-bottom: var(--space-4);">
724Frequently asked</h3>
725
726            <div id="faq-list">
727              ${ba.map((e,a)=>`
728                <div class="faq-item" id="faq-${a}">
729                  <button class="faq-question" data-faq="${a}">
730                    <span>${e.question}</span>
731                    <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
732                  </button>
733                  <div class="faq-answer">
734                    <p>${e.answer}</p>
735                  </div>
736                </div>
737              `).join("")}
738            </div>
739          </div>
740        </div>
741      </div>
742    </section>
743  `}function Ea(){vt(),_a(),xa()}function _a(){document.querySelectorAll(".faq-question").forEach(e=>{e.addEventListener("click",()=>{const a=e.closest(".faq-item"),t=a.classList.contains("open");document.querySelectorAll(".faq-item").forEach(n=>n.classList.remove("open")),t||a.classList.add("open")})})}function xt(e,a){const t=document.getElementById("form-card-container");if(!t)return;const n=a?`#/questionnaire?id=${a}`:"#/questionnaire";t.innerHTML=`
744    <div class="booking-success-container" style="text-align: center; padding: var(--space-4) 0; animation: rl-rise 0.5s cubic-bezier(0.22, 1, 0.36, 1) forwards;">
745      <div class="success-icon" style="font-size: 3.2rem; color: #10b981; margin-bottom: var(--space-4);">✓</div>
746      <h3 style="margin-bottom: var(--space-3); font-size: 1.8rem; font-weight: 800; background: var(--grad-text); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; font-family: var(--font-heading);">Intake Submitted!</h3>
747      <p style="color: var(--text-dim); margin-bottom: var(--space-6); font-size: var(--text-base); max-width: 550px; margin-left: auto; margin-right: auto; line-height: var(--leading-normal);">
748        Thanks, ${e||"there"}! Your contact details are saved. Let's get you ready for your discovery call:
749      </p>
750
751      <!-- Step Guidance Banner -->
752      <div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); max-width: 650px; margin: 0 auto var(--space-8) auto; text-align: left;">
753        <div style="background: var(--bg-alt); padding: var(--space-4); border-radius: var(--radius-md); border-left: 3px solid var(--purple);">
754          <strong style="color: var(--purple); font-size: var(--text-sm); text-transform: uppercase;">Step 1: Book Your Call</strong>
755          <p style="font-size: 13px; color: var(--muted); margin-top: 4px;">Choose a date and time on the scheduler below that works for you.</p>
756        </div>
757        <div style="background: var(--bg-alt); padding: var(--space-4); border-radius: var(--radius-md); border-left: 3px solid var(--cyan); display: flex; flex-direction: column; justify-content: space-between;">
758          <div>
759            <strong style="color: var(--cyan-dark); font-size: var(--text-sm); text-transform: uppercase;">Step 2: Tell Us About Your Business</strong>
760            <p style="font-size: 13px; color: var(--muted); margin-top: 4px;">Fill out the 20-question profile form so we can prepare before we chat.</p>
761          </div>
762          <a href="${n}" class="btn btn--primary" style="margin-top: var(--space-3); width: 100%; text-align: center; padding: 8px 12px; font-size: 13px; background: var(--grad-brand); border: none; text-decoration: none; color: #fff; font-weight: 600; display: inline-block; border-radius: var(--radius-md); cursor: pointer;">
763            📋 Fill Business Profile
764          </a>
765        </div>
766      </div>
767      
768      <div class="calendar-embed-wrapper" style="border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--border); box-shadow: var(--shadow-sm); background: #fff; margin-bottom: var(--space-6);">
769        <iframe src="https://calendar.google.com/calendar/appointments/schedules/AcZssZ3SXITjcIytE6aYcvq7OZ9y5Hqhymv3imkt9O4RHuMKlGL7NFsBy4UD99T6ytcuTDy5A3Ebbgcg?gv=true" style="border: 0" width="100%" height="650" frameborder="0"></iframe>
770      </div>
771
772      <div style="margin-top: var(--space-4);">
773        <p style="font-size: var(--text-sm); color: var(--muted); margin-bottom: var(--space-2);">
774          Trouble viewing the scheduler?
775        </p>
776        <div style="display: flex; gap: var(--space-3); justify-content: center; align-items: center;">
777          <a href="https://calendar.app.google/mT6GLLudB4xZryZt7" target="_blank" rel="noopener noreferrer" class="btn btn--secondary btn--md" style="text-decoration: none; display: inline-block;">
778            Open Calendar in New Tab
779          </a>
780          <a href="${n}" class="btn btn--secondary btn--md" style="text-decoration: none; display: inline-block;">
781            Go directly to Business Profile
782          </a>
783        </div>
784      </div>
785    </div>
786  `}function xa(){const e=document.getElementById("contact-form");e&&e.addEventListener("submit",async a=>{a.preventDefault();const t=document.getElementById("submit-text"),n=document.getElementById("submit-loading"),o=document.getElementById("contact-submit"),s=document.getElementById("form-status");t.style.display="none",n.style.display="inline",o.disabled=!0,s.className="form-status";const i=new FormData(e),r=Object.fromEntries(i.entries());try{const d={email:r.email,name:r.name,company:r.company,service:r.service,message:r.message},l=await fetch(`${O}/intakes/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(d)});if(l.ok){const c=await l.json();xt(r.name,c.clientId)}else throw new Error("Backend failed")}catch{try{if((await fetch("https://formsubmit.co/ajax/[email protected]",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(r)})).ok)xt(r.name,null);else throw new Error("Both failed")}catch{s.textContent="Something went wrong. Please email us directly at [email protected]",s.className="form-status form-status--error",t.style.display="inline",n.style.display="none",o.disabled=!1}}})}const le={dateLabel:"July 30–31, 2026",dayLabel:"Thursday & Friday · 2 full days",location:"Dallas–Fort Worth, TX",seats:30,deadlineLabel:"June 30",serviceTag:"AI Workshop · July 30–31, 2026"},ka=[{icon:"🧠",title:"Operating-system thinking",desc:"The framework behind Crystal — how to run your entire business from one place instead of a dozen tools."},{icon:"🗄️",title:"Databases & a single source of truth",desc:"Ground your whole business in one database so your data lives in one place — not scattered across apps and spreadsheets."},{icon:"🤖",title:"AI agents that do the work",desc:"Build agents that handle follow-up, quoting, and scheduling — not just chatbots that answer questions."},{icon:"📈",title:"Scaling vertically with AI",desc:"Stack AI across your operation to grow output and revenue without growing headcount."},{icon:"🔮",title:"Hands-on with Crystal",desc:"Test the Crystal operating system live and walk out with i
786deas to inspire your own build."},{icon:"🤝",title:"Connections with other entrepreneurs",desc:"Two days in a room of operators wrestling with the same growth problems you are."},{icon:"🌐",title:"A community after the course",desc:"Leave with relationships and an ongoing community of like-minded people looking to hyperscale together."}];function Ma(){const e=le,a=t=>`<span style="display:inline-flex; align-items:center; gap:8px; font-size:13px; font-weight:600; color:var(--ink); background:var(--bg-alt); border:1px solid var(--border); padding:8px 14px; border-radius:12px;">${t}</span>`;return`
787    <style>
788      @keyframes wkPulse {
789        0%   { box-shadow: 0 0 0 0 rgba(253,224,71,0.7); }
790        70%  { box-shadow: 0 0 0 10px rgba(253,224,71,0); }
791        100% { box-shadow: 0 0 0 0 rgba(253,224,71,0); }
792      }
793      @media (max-width: 760px) {
794        #workshop .workshop-card { grid-template-columns: 1fr !important; }
795        #workshop .workshop-card__pitch,
796        #workshop .workshop-card__form { padding: var(--space-8) var(--space-5) !important; }
797      }
798    </style>
799
800    <!-- HERO -->
801    <section class="hero hero--page hero--compact" id="workshop-hero">
802      <div class="hero__grid hero__grid--center">
803        <div class="hero__copy">
804          <span class="eyebrow eyebrow--center reveal">Live ${e.dayLabel.includes("2")?"2-Day":"Multi-Day"} Event · Limited Seats</span>
805          <h1 class="hero__title reveal reveal-delay-1" style="margin-top:var(--space-4);">The BizWrap <span class="grad-text">AI Workshop</span></h1>
806          <p class="hero__subtitle reveal reveal-delay-2">
807            Two hands-on days learning to put AI to work <em>inside</em> your business — ${e.dateLabel}. Seats are limited, so this starts as a waitlist: once enough people commit, we lock in the venue and you're first in line for a ticket.
808          </p>
809          <div class="reveal reveal-delay-2" style="display:flex; flex-wrap:wrap; gap:10px; justify-content:center; margin-top:var(--space-6);">
810            ${a("📅 "+e.dateLabel)}
811            ${a("🗓️ "+e.dayLabel)}
812            ${a("📍 "+e.location)}
813          </div>
814        </div>
815      </div>
816    </section>
817
818    <!-- CARD: pitch + signup form -->
819    <section class="section" id="workshop" style="padding-top:var(--space-4); padding-bottom:var(--space-16);">
820      <div class="container">
821        <div class="workshop-card reveal" style="
822          display:grid; grid-template-columns:1.05fr 0.95fr; gap:0;
823          border-radius:var(--radius-xl, 26px); overflow:hidden;
824          border:1px solid var(--border); box-shadow:var(--shadow-lg, 0 24px 60px rgba(16,16,40,0.18));">
825
826          <!-- LEFT: value + scarcity (dark gradient) -->
827          <div class="workshop-card__pitch" style="
828            position:relative; padding:var(--space-10) var(--space-8);
829            background:linear-gradient(150deg, #15102e 0%, #1d1340 45%, #221a4d 100%); color:#fff; overflow:hidden;">
830            <div aria-hidden="true" style="position:absolute; top:-90px; right:-90px; width:280px; height:280px; border-radius:50%; background:radial-gradient(circle, rgba(124,58,237,0.45), transparent 70%); filter:blur(8px);"></div>
831            <div aria-hidden="true" style="position:absolute; bottom:-110px; left:-70px; width:260px; height:260px; border-radius:50%; background:radial-gradient(circle, rgba(1,199,204,0.30), transparent 70%);"></div>
832
833            <div style="position:relative;">
834              <h2 style="margin:0 0 var(--space-5); font-size:clamp(1.5rem, 3vw, 1.9rem); line-height:1.15; color:#fff;">What you'll walk away with</h2>
835
836              <div style="display:grid; gap:var(--space-4); margin-bottom:var(--space-6);">
837                ${ka.map(t=>`
838                  <div style="display:flex; gap:14px; align-items:flex-start;">
839                    <span style="font-size:1.4rem; line-height:1;">${t.icon}</span>
840                    <div>
841                      <div style="font-weight:700; color:#fff; font-size:15px; margin-bottom:2px;">${t.title}</div>
842                      <div style="font-size:13.5px; color:rgba(255,255,255,0.74); line-height:1.5;">${t.desc}</div>
843                    </div>
844                  </div>
845                `).join("")}
846              </div>
847
848              <!-- scarcity bar -->
849              <div style="background:rgba(0,0,0,0.22); border:1px solid rgba(255,255,255,0.12); border-radius:var(--radius-lg, 18px); padding:var(--space-4) var(--space-5);">
850                <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:10px;">
851                  <span style="font-size:12px; font-weight:800; letter-spacing:0.05em; text-transform:uppercase; color:#fff; display:inline-flex; align-items:center; gap:8px;">
852                    <span style="width:8px; height:8px; border-radius:50%; background:#fde047; box-shadow:0 0 0 0 rgba(253,224,71,0.7); animation:wkPulse 2s infinite;"></span>
853                    🪑 Only ${e.seats} seats
854                  </span>
855                  <span style="font-size:11px; font-weight:800; letter-spacing:0.05em; text-transform:uppercase; color:#0f1020; background:#fde047; padding:4px 10px; border-radius:999px;">Filling fast</span>
856                </div>
857                <div style="height:8px; border-radius:999px; background:rgba(255,255,255,0.14); overflow:hidden;">
858                  <div style="height:100%; width:72%; border-radius:999px; background:linear-gradient(90deg, #7c3aed, #01c7cc);"></div>
859                </div>
860                <p style="margin:10px 0 0; font-size:12.5px; color:rgba(255,255,255,0.72); line-height:1.5;">
861                  Seats are first-come, first-seated. Waitlist closes <strong style="color:#fff;">${e.deadlineLabel}</strong> — confirmed signups get the time, exact location, and their ticket by then.
862                </p>
863              </div>
864            </div>
865          </div>
866
867          <!-- RIGHT: the form (white) -->
868          <div class="workshop-card__form" style="padding:var(--space-10) var(--space-8); background:#fff;">
869            <h3 style="margin-bottom:6px;">Save my seat</h3>
870            <p style="color:var(--muted); font-size:var(--text-sm); margin-bottom:var(--space-6);">
871              No payment now — just claim your spot on the list. We'll email your ticket &amp; the details by ${e.deadlineLabel}.
872            </p>
873
874            <form id="workshop-form">
875              <div class="form-group">
876                <label class="form-label" for="ws-name">Full name</label>
877                <input class="form-input" type="text" id="ws-name" name="name" placeholder="Joey Murphy" required>
878              </div>
879
880              <div class="form-group">
881                <label class="form-label" for="ws-email">Email address</label>
882                <input class="form-input" type="email" id="ws-email" name="email" placeholder="[email protected]" required>
883              </div>
884
885              <div style="display:grid; grid-template-columns:1fr 1fr; gap:var(--space-4);">
886                <div class="form-group">
887                  <label class="form-label" for="ws-phone">Mobile (for your ticket)</label>
888                  <input class="form-input" type="tel" id="ws-phone" name="phone" placeholder="(555) 123-4567">
889                </div>
890                <div class="form-group">
891                  <label class="form-label" for="ws-seats">How many seats?</label>
892                  <select class="form-select" id="ws-seats" name="seats">
893                    <option value="1" selected>Just me (1)</option>
894                    <option value="2">2 seats</option>
895                    <option value="3">3 seats</option>
896                    <option value="4+">4+ (bringing my team)</option>
897                  </select>
898                </div>
899              </div>
900
901              <div class="form-group">
902                <label class="form-label" for="ws-company">Company <span style="color:var(--muted-light); font-weight:500;">(optional)</span></label>
903                <input class="form-input" type="text" id="ws-company" name="company" placeholder="Your business">
904              </div>
905
906              <div class="form-group">
907                <label class="form-label" for="ws-goal">What do you want to walk away with? <span style="color:var(--muted-light); font-weight:500;">(optional)</span></label>
908                <textarea class="form-textarea" id="ws-goal" name="goal" style="min-height:90px;" placeholder="e.g. AI agents for my sales follow-up, or just to understand what's possible"></textarea>
909              </div>
910
911              <button class="btn btn--primary btn--lg" type="submit" id="ws-submit" style="width:100%;">
912                <span id="ws-submit-text">🎟️ Claim my seat on the waitlist</span>
913                <span id="ws-submit-loading" style="display:none;">Reserving your spot…</span>
914              </button>
915
916              <div id="ws-form-status" class="form-status"></div>
917              <p style="text-align:center; color:var(--muted-light); font-size:12px; margin-top:var(--space-4); line-height:1.5;">
918                Signing up is free and non-binding — it just puts you on the list. If we hit enough committed attendees, the event is on and you'll be first to get a ticket.
919              </p>
920            </form>
921          </div>
922        </div>
923      </div>
924    </section>
925  `}function Ta(){vt(),Sa()}function Sa(){const e=document.getElementById("workshop-form");e&&e.addEventListener("submit",async a=>{a.preventDefault();const t=document.getElementById("ws-submit-text"),n=document.getElementById("ws-submit-loading"),o=document.getElementById("ws-submit"),s=document.getElementById("ws-form-status");t.style.display="none",n.style.display="inline",o.disabled=!0,s.className="form-status";const i=Object.fromEntries(new FormData(e).entries()),r=i.seats||"1",d=(i.goal||"").trim(),l=`🎟️ AI WORKSHOP WAITLIST — ${le.dateLabel}
926Seats requested: ${r}
927Goal: ${d||"—"}
928
929(Joined via the AI Workshop page waitlist. Send time, exact location & ticket by ${le.deadlineLabel} if confirmed.)`,c={email:i.email,name:i.name,company:i.company,phone:i.phone,service:le.serviceTag,source:"workshop-waitlist",message:l};try{if(!(await fetch(`${O}/intakes/submit`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)})).ok)throw new Error("Backend failed");kt(i.name)}catch{try{if(!(await fetch("https://formsubmit.co/ajax/[email protected]",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({_subject:`AI Workshop waitlist: ${i.name||"New signup"}`,...c})})).ok)throw new Error("Both failed");kt(i.name)}catch{s.textContent="Something went wrong. Please email us directly at [email protected]",s.className="form-status form-status--error",t.style.display="inline",n.style.display="none",o.disabled=!1}}})}function kt(e){const a=document.querySelector(".workshop-card__form");a&&(a.innerHTML=`
930    <div style="text-align:center; padding:var(--space-4) 0; animation:rl-rise 0.5s cubic-bezier(0.22,1,0.36,1) forwards;">
931      <div style="font-size:3rem; line-height:1; margin-bottom:var(--space-4);">🎟️</div>
932      <h3 style="margin-bottom:var(--space-3); font-size:1.7rem; font-weight:800; background:var(--grad-text); -webkit-background-clip:text; -webkit-text-fill-color:transparent; background-clip:text; font-family:var(--font-heading);">
933        You're on the list${e?", "+e.split(" ")[0]:""}!
934      </h3>
935      <p style="color:var(--muted); font-size:var(--text-base); line-height:1.6; max-width:42ch; margin:0 auto var(--space-6);">
936        Your seat for the <strong>${le.dateLabel}</strong> AI Workshop is reserved on the waitlist. The moment we confirm enough attendees, we'll email you the time, the exact ${le.location} location, and your ticket — by <strong>${le.deadlineLabel}</strong>.
937      </p>
938
939      <div style="display:grid; gap:10px; text-align:left; max-width:420px; margin:0 auto var(--space-6);">
940        <div style="display:flex; gap:12px; align-items:flex-start; background:var(--bg-alt); border:1px solid var(--border); border-radius:var(--radius-md); padding:var(--space-3) var(--space-4);">
941          <span style="font-size:1.1rem;">✅</span>
942          <span style="font-size:13px; color:var(--ink); line-height:1.5;"><strong>Spot held.</strong> Seats are limited to ${le.seats} — you're in line.</span>
943        </div>
944        <div style="display:flex; gap:12px; align-items:flex-start; background:var(--bg-alt); border:1px solid var(--border); border-radius:var(--radius-md); padding:var(--space-3) var(--space-4);">
945          <span style="font-size:1.1rem;">📨</span>
946          <span style="font-size:13px; color:var(--ink); line-height:1.5;"><strong>Watch your inbox.</strong> Details &amp; your ticket arrive by ${le.deadlineLabel}
946.</span>
947        </div>
948        <div style="display:flex; gap:12px; align-items:flex-start; background:var(--bg-alt); border:1px solid var(--border); border-radius:var(--radius-md); padding:var(--space-3) var(--space-4);">
949          <span style="font-size:1.1rem;">📣</span>
950          <span style="font-size:13px; color:var(--ink); line-height:1.5;"><strong>Know someone who'd love this?</strong> Send them here — more committed seats means it's locked in sooner.</span>
951        </div>
952      </div>
953
954      <a href="#/contact" class="btn btn--secondary btn--md" style="text-decoration:none; display:inline-block;">
955        Want a full system too? Book a demo →
956      </a>
957    </div>
958  `)}function _e(e){return e?e[0]:null}function ze(e){return e?e[1]:null}function tt(e){return e==="white"?"black":"white"}function ft(e){return e==="white"?"w":"b"}function de(e,a){return e>=0&&e<8&&a>=0&&a<8}function qt(e){return e.map(a=>[...a])}function Ha(e){return{board:qt(e.board),turn:e.turn,moveHistory:[...e.moveHistory],captured:{white:[...e.captured.white],black:[...e.captured.black]},castlingRights:{...e.castlingRights},enPassantTarget:e.enPassantTarget?{...e.enPassantTarget}:null,halfMoveClock:e.halfMoveClock,fullMoveNumber:e.fullMoveNumber,gameOver:e.gameOver,result:e.result}}function Qe(){return{board:[["bR","bN","bB","bQ","bK","bB","bN","bR"],["bP","bP","bP","bP","bP","bP","bP","bP"],[null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null],[null,null,null,null,null,null,null,null],["wP","wP","wP","wP","wP","wP","wP","wP"],["wR","wN","wB","wQ","wK","wB","wN","wR"]],turn:"white",moveHistory:[],captured:{white:[],black:[]},castlingRights:{wK:!0,wQ:!0,bK:!0,bQ:!0},enPassantTarget:null,halfMoveClock:0,fullMoveNumber:1,gameOver:!1,result:null}}function we(e,a,t,n){const o=ft(n),s=[[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]];for(const[c,p]of s){const u=a+c,g=t+p;if(de(u,g)&&e[u][g]===o+"N")return!0}const i=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];for(const[c,p]of i){const u=a+c,g=t+p;if(de(u,g)&&e[u][g]===o+"K")return!0}const r=n==="white"?1:-1;for(const c of[-1,1]){const p=a+r,u=t+c;if(de(p,u)&&e[p][u]===o+"P")return!0}const d=[[-1,0],[1,0],[0,-1],[0,1]];for(const[c,p]of d){let u=a+c,g=t+p;for(;de(u,g);){const y=e[u][g];if(y){if(y===o+"R"||y===o+"Q")return!0;break}u+=c,g+=p}}const l=[[-1,-1],[-1,1],[1,-1],[1,1]];for(const[c,p]of l){let u=a+c,g=t+p;for(;de(u,g);){const y=e[u][g];if(y){if(y===o+"B"||y===o+"Q")return!0;break}u+=c,g+=p}}return!1}function zt(e,a){const t=ft(a)+"K";for(let n=0;n<8;n++)for(let o=0;o<8;o++)if(e[n][o]===t)return{row:n,col:o};return null}function ve(e,a){const t=zt(e.board,a);return t?we(e.board,t.row,t.col,tt(a)):!1}function Dt(e,a){for(let t=0;t<8;t++)for(let n=0;n<8;n++)if(e.board[t][n]&&_e(e.board[t][n])===ft(a)&&Ne(e,t,n).length>0)return!0;return!1}function pt(e,a){return ve(e,a)&&!Dt(e,a)}function Ra(e,a){return!ve(e,a)&&!Dt(e,a)}function Ca(e,a,t){const n=e.board[a][t];if(!n)return[];const o=_e(n),s=ze(n),i=[];function r(l,c){if(!de(l,c))return!1;const p=e.board[l][c];return p&&_e(p)===o?!1:(i.push({row:l,col:c}),!p)}function d(l){for(const[c,p]of l){let u=a+c,g=t+p;for(;de(u,g)&&r(u,g);)u+=c,g+=p}}switch(s){case"P":{const l=o==="w"?-1:1,c=o==="w"?6:1;de(a+l,t)&&!e.board[a+l][t]&&(i.push({row:a+l,col:t}),a===c&&!e.board[a+2*l][t]&&i.push({row:a+2*l,col:t}));for(const p of[-1,1]){const u=a+l,g=t+p;if(de(u,g)){const y=e.board[u][g];y&&_e(y)!==o&&i.push({row:u,col:g}),e.enPassantTarget&&e.enPassantTarget.row===u&&e.enPassantTarget.col===g&&i.push({row:u,col:g})}}break}case"N":{const l=[[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]];for(const[c,p]of l)r(a+c,t+p);break}case"B":d([[-1,-1],[-1,1],[1,-1],[1,1]]);break;case"R":d([[-1,0],[1,0],[0,-1],[0,1]]);break;case"Q":d([[-1,-1],[-1,1],[1,-1],[1,1],[-1,0],[1,0],[0,-1],[0,1]]);break;case"K":{const l=[[-1,-1],[-1,0],[-1,1],[0,-1],[0,1],[1,-1],[1,0],[1,1]];for(const[u,g]of l)r(a+u,t+g);const c=o==="w"?"black":"white",p=o==="w"?7:0;a===p&&t===4&&!we(e.board,a,t,c)&&(e.castlingRights[o+"K"]&&!e.board[p][5]&&!e.board[p][6]&&e.board[p][7]===o+"R"&&!we(e.board,p,5,c)&&!we(e.board,p,6,c)&&i.push({row:p,col:6}),e.castlingRights[o+"Q"]&&!e.board[p][3]&&!e.board[p][2]&&!e.board[p][1]&&e.board[p][0]===o+"R"&&!we(e.board,p,3,c)&&!we(e.board,p,2,c)&&i.push({row:p,col:2}));break}}return i}function Ne(e,a,t){const n=e.board[a][t];
958if(!n)return[];const o=_e(n),s=o==="w"?"white":"black";return s!==e.turn?[]:Ca(e,a,t).filter(r=>{const d=qt(e.board);if(d[r.row][r.col]=d[a][t],d[a][t]=null,ze(n)==="P"&&e.enPassantTarget&&r.row===e.enPassantTarget.row&&r.col===e.enPassantTarget.col){const c=o==="w"?r.row+1:r.row-1;d[c][r.col]=null}if(ze(n)==="K"&&Math.abs(r.col-t)===2){const c=o==="w"?7:0;r.col===6?(d[c][5]=d[c][7],d[c][7]=null):r.col===2&&(d[c][3]=d[c][0],d[c][0]=null)}const l=zt(d,s);return l?!we(d,l.row,l.col,tt(s)):!1})}function Ue(e){return"abcdefgh"[e]}function st(e){return String(8-e)}function Ia(e,a,t,n,o,s,i){const r=e.board[a][t],d=ze(r),c=_e(r)==="w"?"white":"black",p=tt(c);if(d==="K"&&Math.abs(o-t)===2){const y=o===6?"O-O":"O-O-O";let m="";return pt(i,p)?m="#":ve(i,p)&&(m="+"),y+m}let u="";const g=e.board[n][o]!==null||d==="P"&&o!==t;if(d!=="P"){u+=d;const y=[];for(let m=0;m<8;m++)for(let v=0;v<8;v++)m===a&&v===t||e.board[m][v]===r&&Ne(e,m,v).some(b=>b.row===n&&b.col===o)&&y.push({row:m,col:v});if(y.length>0){const m=y.some(T=>T.col===t),v=y.some(T=>T.row===a);m?v?u+=Ue(t)+st(a):u+=st(a):u+=Ue(t)}}else g&&(u+=Ue(t));return g&&(u+="x"),u+=Ue(o)+st(n),s&&(u+="="+s),pt(i,p)?u+="#":ve(i,p)&&(u+="+"),u}function at(e,a,t,n,o,s){const i=Ha(e),r=i.board[a][t];if(!r)return e;const d=_e(r),l=ze(r),c=d==="w"?"white":"black",p=i.board[n][o];if(p&&i.captured[c].push(p),l==="P"&&i.enPassantTarget&&n===i.enPassantTarget.row&&o===i.enPassantTarget.col){const y=d==="w"?n+1:n-1,m=i.board[y][o];m&&i.captured[c].push(m),i.board[y][o]=null}if(l==="K"&&Math.abs(o-t)===2){const y=d==="w"?7:0;o===6?(i.board[y][5]=i.board[y][7],i.board[y][7]=null):o===2&&(i.board[y][3]=i.board[y][0],i.board[y][0]=null)}if(i.board[n][o]=r,i.board[a][t]=null,l==="P"&&n===(d==="w"?0:7)){const m=s||"Q";i.board[n][o]=d+m}if(l==="P"&&Math.abs(n-a)===2){const y=(a+n)/2;i.enPassantTarget={row:y,col:t}}else i.enPassantTarget=null;l==="K"&&(i.castlingRights[d+"K"]=!1,i.castlingRights[d+"Q"]=!1),l==="R"&&(a===7&&t===0&&(i.castlingRights.wQ=!1),a===7&&t===7&&(i.castlingRights.wK=!1),a===0&&t===0&&(i.castlingRights.bQ=!1),a===0&&t===7&&(i.castlingRights.bK=!1)),n===7&&o===0&&(i.castlingRights.wQ=!1),n===7&&o===7&&(i.castlingRights.wK=!1),n===0&&o===0&&(i.castlingRights.bQ=!1),n===0&&o===7&&(i.castlingRights.bK=!1),l==="P"||p?i.halfMoveClock=0:i.halfMoveClock++;const u=Ia(e,a,t,n,o,l==="P"&&(n===0||n===7)?s||"Q":null,i);i.moveHistory.push(u),c==="black"&&i.fullMoveNumber++,i.turn=tt(c);const g=i.turn;return pt(i,g)?(i.gameOver=!0,i.result={type:"checkmate",winner:c}):Ra(i,g)?(i.gameOver=!0,i.result={type:"stalemate",winner:null}):i.halfMoveClock>=100&&(i.gameOver=!0,i.result={type:"draw",reason:"50-move rule",winner:null}),i}const $a={P:100,N:320,B:330,R:500,Q:900,K:2e4},La=[0,0,0,0,0,0,0,0,50,50,50,50,50,50,50,50,10,10,20,30,30,20,10,10,5,5,10,25,25,10,5,5,0,0,0,20,20,0,0,0,5,-5,-10,0,0,-10,-5,5,5,10,10,-20,-20,10,10,5,0,0,0,0,0,0,0,0],Ba=[-50,-40,-30,-30,-30,-30,-40,-50,-40,-20,0,0,0,0,-20,-40,-30,0,10,15,15,10,0,-30,-30,5,15,20,20,15,5,-30,-30,0,15,20,20,15,0,-30,-30,5,10,15,15,10,5,-30,-40,-20,0,5,5,0,-20,-40,-50,-40,-30,-30,-30,-30,-40,-50],Pa=[-20,-10,-10,-10,-10,-10,-10,-20,-10,0,0,0,0,0,0,-10,-10,0,10,10,10,10,0,-10,-10,5,5,10,10,5,5,-10,-10,0,5,10,10,5,0,-10,-10,5,5,5,5,5,5,-10,-10,5,0,0,0,0,5,-10,-20,-10,-10,-10,-10,-10,-10,-20],Aa=[0,0,0,0,0,0,0,0,5,10,10,10,10,10,10,5,-5,0,0,0,0,0,0,-5,-5,0,0,0,0,0,0,-5,-5,0,0,0,0,0,0,-5,-5,0,0,0,0,0,0,-5,-5,0,0,0,0,0,0,-5,0,0,0,5,5,0,0,0],Ga=[-20,-10,-10,-5,-5,-10,-10,-20,-10,0,0,0,0,0,0,-10,-10,0,5,5,5,5,0,-10,-5,0,5,5,5,5,0,-5,0,0,5,5,5,5,0,-5,-10,5,5,5,5,5,0,-10,-10,0,5,0,0,0,0,-10,-20,-10,-10,-5,-5,-10,-10,-20],qa=[-30,-40,-40,-50,-50,-40,-40,-30,-30,-40,-40,-50,-50,-40,-40,-30,-30,-40,-40,-50,-50,-40,-40,-30,-30,-40,-40,-50,-50,-40,-40,-30,-20,-30,-30,-40,-40,-30,-30,-20,-10,-20,-20,-20,-20,-20,-20,-10,20,20,0,0,0,0,20,20,20,30,10,0,0,10,30,20],za={P:La,N:Ba,B:Pa,R:Aa,Q:Ga,K:qa};function Da(e){let a=0;for(let t=0;t<8;t++)for(let n=0;n<8;n++){const o=e.board[t][n];if(!o)continue;const s=o[1],i=o[0]==="w",r=$a[s]||0,d=za[s],l=i?t*8+n:(7-t)*8+n,c=d?d[l]:0;i?a+=r+c:a-=r+c}return ve(e,e.turn)&&(a+=e.turn==="white"?-50:50),a}function Nt(e,a,t,n,o){if(a===0||e.gameOver)return{score:Da(e),move:null};const s=o?"w":"b";let i=null,r=o?-1/0:1/0;const d=[];for(let l=0;l<8;l++)for(let c=0;c<8;c++){const p=e.board[l][c];if(p&&p[0]===s){const u=Ne(e,l,c);for(const g of u)d.push({from:{row:l,col:c},to:g})}}if(d.length===0)return ve(e,o?"white":"black")?{score:o?-99999+(3-a):99999-(3-a),move:null}:{score:0,move:null};d.sort((l,c)=>{const p=e.board[l.to.row][l.to.col]?1:0;return(e.board[c.to.row][c.to.col]?1:0)-p});for(const l of d){const c=at(e,l.from.row,l.from.col,l.to.row,l.to.col),p=Nt(c,a-1,t,n,!o);if(o?(p.score>r&&(r=p.score,i=l),t=Math.max(t,r)):(p.score<r&&(r=p.score,i=l),n=Math.min(n,r)),n<=t)break}return{score:r,move:i}}function Na(e,a=3){if(e.gameOver)return null;const t=e.turn==="white",n=Nt(e,a,-1/0,1/0,t);if(!n.move)return null;const o=e.board[n.move.from.row][n.move.from.col];let s;if(o&&o[1]==="P"){const i=o[0]==="w"?0:7;n.move.to.row===i&&(s="Q")}return{from:n.move.from,to:n.move.to,promotion:s}}
958let Y=null,$=null,A=null,te="",X="",ke="",U=null,se=[],K="lobby",Le=[],j=!1,ue="medium",ae=!1,Xe=!1,Be=null,Pe=null;const He={wK:"♔",wQ:"♕",wR:"♖",wB:"♗",wN:"♘",wP:"♙",bK:"♚",bQ:"♛",bR:"♜",bB:"♝",bN:"♞",bP:"♟"},Mt={P:1,N:3,B:3,R:5,Q:9,K:0};let N=null,Ze=!1;function Oa(){return new Promise((e,a)=>{if(window.THREE){Ze=!0,e();return}const t=document.createElement("script");t.src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js",t.onload=()=>{Ze=!0,e()},t.onerror=a,document.head.appendChild(t)})}class Fa{constructor(a){this.container=a;const t=a.clientWidth||800,n=window.innerHeight-100,o=Math.max(400,Math.min(t*.88,n));a.style.height=o+"px",this.scene=new THREE.Scene,this.camera=new THREE.PerspectiveCamera(50,t/o,.1,100),this.camera.position.set(0,10,9),this.camera.lookAt(0,0,.5),this.renderer=new THREE.WebGLRenderer({antialias:!0,alpha:!0}),this.renderer.setSize(t,o),this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,2)),this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=THREE.PCFSoftShadowMap,this.renderer.toneMapping=THREE.ACESFilmicToneMapping,this.renderer.toneMappingExposure=1.3,this.renderer.domElement.style.borderRadius="16px",this.renderer.domElement.style.cursor="pointer",a.appendChild(this.renderer.domElement),this.pieces={},this.squareMeshes=[],this.hlMeshes=[],this.dragging=null,this.dragStartPos=null,this.dragStartSq=null,this.dragPlane=new THREE.Plane(new THREE.Vector3(0,1,0),0),this.raycaster=new THREE.Raycaster,this.mouse=new THREE.Vector2,this.animQueue=[],this.theme="classic",this._orbitTarget=new THREE.Vector3(0,0,.5);const s=new THREE.Vector3().subVectors(this.camera.position,this._orbitTarget);this._orbitRadius=s.length(),this._orbitTheta=Math.atan2(s.x,s.z),this._orbitPhi=Math.acos(Math.max(-1,Math.min(1,s.y/this._orbitRadius))),this._orbiting=!1;let i=null,r=null;try{i=this._makeBumpMap(.12,"marble"),r=this._makeBumpMap(.15,"crystal")}catch{}this.wMat=new THREE.MeshStandardMaterial({color:16116438,roughness:.22,metalness:.08,...i?{bumpMap:i,bumpScale:.015}:{},envMapIntensity:.6}),this.bMat=new THREE.MeshStandardMaterial({color:7233428,roughness:.2,metalness:.14,...r?{bumpMap:r,bumpScale:.012}:{},envMapIntensity:.8}),this._lights(),this._board(),this._edgeMesh=this.scene.children.find(d=>d.geometry?.parameters?.width===8.8),this._edgeMesh&&(this._edgeMesh.userData.type="edge"),this._slabMesh=this.scene.children.find(d=>d.geometry?.parameters?.width===8.6),this._slabMesh&&(this._slabMesh.userData.type="edge"),this._events(),this._loop(),this._ro=new ResizeObserver(()=>this._resize()),this._ro.observe(a)}_makeBumpMap(a,t){const o=document.createElement("canvas");o.width=256,o.height=256;const s=o.getContext("2d");s.fillStyle="#808080",s.fillRect(0,0,256,256);const i=s.getImageData(0,0,256,256),r=i.data;for(let l=0;l<r.length;l+=4){const c=l/4%256,p=Math.floor(l/4/256);let u=128;if(t==="marble"){const g=Math.sin(c*.05+p*.03+Math.sin(p*.08)*4)*.5+.5,y=(Math.random()-.5)*30;u=128+g*a*200+y}else{const g=Math.sin(c*.12)*Math.cos(p*.12)*.5+.5,y=(Math.random()-.5)*25;u=128+g*a*180+y}r[l]=r[l+1]=r[l+2]=Math.max(0,Math.min(255,u))}s.putImageData(i,0,0);const d=new THREE.CanvasTexture(o);return d.wrapS=d.wrapT=THREE.RepeatWrapping,d.repeat.set(2,2),d}_lights(){this.scene.add(new THREE.AmbientLight(16777215,.45));const a=new THREE.DirectionalLight(16777215,1);a.position.set(-4,10,5),a.castShadow=!0,a.shadow.mapSize.set(2048,2048),a.shadow.camera.near=1,a.shadow.camera.far=25,a.shadow.camera.left=-6,a.shadow.camera.right=6,a.shadow.camera.top=6,a.shadow.camera.bottom=-6,a.shadow.bias=-8e-4,this.scene.add(a);const t=new THREE.DirectionalLight(8961023,.3);t.position.set(5,6,-3),this.scene.add(t);const n=new THREE.DirectionalLight(16768460,.2);n.position.set(0,4,-8),this.scene.add(n)}_board(){const a=new THREE.Mesh(new THREE.BoxGeometry(8.6,.35,8.6),new THREE.MeshStandardMaterial({color:1468008,roughness:.25,metalness:.15}));a.position.y=-.175,a.receiveShadow=!0,a.castShadow=!0,this.scene.add(a);const t=new THREE.Mesh(new THREE.BoxGeometry(8.8,.38,8.8),new THREE.MeshStandardMaterial({color:936524,roughness:.4,metalness:.1}));t.position.y=-.19,this.scene.add(t);const n=new THREE.MeshStandardMaterial({color:16054264,roughness:.5,metalness:.02}),o=new THREE.MeshStandardMaterial({color:3126712,roughness:.35,metalness:.05});for(let s=0;s<8;s++)for(let i=0;i<8;i++){const r=(s+i)%2===1,d=new THREE.Mesh(new THREE.BoxGeometry(1,.06,1),(r?n:o).clone());d.position.set(i-3.5,.001,s-3.5),d.receiveShadow=!0,d.userData={type:"sq",row:s,col:i,origColor:d.material.color.getHex()},this.squareMeshes.push(d),this.scene.add(d)}}_prof(a){const t=(n,o)=>new THREE.Vector2(n,o);switch(a){case"P":return[t(0,0),t(.32,0),t(.32,.02),t(.3,.04),t(.28,.06),t(.26,.08),t(.18,.12),t(.16,.14),t(.18,.16),t(.18,.18),t(.16,.2),t(.12,.24),t(.1,.3),t(.09,.34),t(.1,.37),t(.12,.39),t(.12,.41),t(.1,.43),t(.09,.46),t(.08,.5),t(.14,.52),t(.16,.55),t(.16,.58),t(.14,.6),t(.12,.63),t(.14,.66),t(.16,.7),t(.17,.74),t(.17,.78),t(.15,.82),t(.12,.86),t(.08,.9),t(.04,.93),t(0,.95)];case"R":return[t(0,0),t(.34,0),t(.34,.02),t(.32,.04),t(.3,.06),t(.28,.08),t(.2,.12),t(.18,.14),t(.2,.16),t(.2,.18),t(.18,.2),t(.16,.22),t(.15,.26),t(.14,.3),t(.14,.5),t(.15,.54),t(.16,.56),t(.16,.6),t(.15,.62),t(.18,.64),t(.2,.66),t(.2,.68),t(.18,.7),t(.22,.72),t(.22,.74),t(0,.74)];case"N":return[t(0,0),t(.32,0),t(.32,.02),t(.3,.04),t(.28,.06),t(.26,.08),t(.18,.12),t(.16,.14),t(.18,.16),t(.18,.18),t(.16,.2),t(.12,.24),t(.1,.28),t(.09,.32),t(.1,.35),t(.12,.37),t(.12,.39),t(.1,.41),t(.08,.44),t(.07,.48),t(.06,.5),t(0,.5)];case"B":return[t(0,0),t(.32,0),t(.32,.02),t(.3,.04),t(.28,.06),t(.26,.08),t(.18,.12),t(.16,.14),t(.18,.16),t(.18,.18),t(.16,.2),t(.12,.24),t(.1,.28),t(.09,.32),t(.1,.35),t(.12,.37),t(.12,.39),t(.1,.41),t(.08,.44),t(.07,.48),t(.06,.52),t(.1,.55),t(.13,.58),t(.14,.6),t(.14,.64),t(.13,.68),t(.15,.7),t(.16,.74),t(.16,.78),t(.14,.82),t(.12,.86),t(.08,.9),t(.04,.94),t(.02,.97),t(0,.99)];case"Q":return[t(0,0),t(.34,0),t(.34,.02),t(.32,.04),t(.3,.06),t(.28,.08),t(.2,.12),t(.18,.14),t(.2,.16),t(.2,.18),t(.18,.2),t(.14,.24),t(.12,.28),t(.11,.32),t(.1,.38),t(.09,.44),t(.09,.5),t(.1,.54),t(.12,.56),t(.12,.58),t(.1,.6),t(.09,.63),t(.1,.66),t(.12,.68),t(.14,.72),t(.16,.76),t(.18,.8),t(.19,.84),t(.18,.88),t(.2,.9),t(.22,.94),t(.18,.92),t(.14,.96),t(.1,.94),t(.06,.98),t(.04,.96),t(.03,1),t(0,1.02)];case"K":return[t(0,0),t(.36,0),t(.36,.02),t(.34,.04),t(.32,.06),t(.3,.08),t(.22,.12),t(.2,.14),t(.22,.16),t(.22,.18),t(.2,.2),t(.16,.24),t(.14,.28),t(.13,.32),t(.12,.38),t(.11,.44),t(.11,.5),t(.12,.54),t(.14,.56),t(.14,.58),t(.12,.6),t(.11,.63),t(.12,.66),t(.14,.68),t(.16,.72),t(.18,.76),t(.2,.8),t(.21,.84),t(.2,.88),t(.18,.92),t(.16,.94),t(.1,.97),t(.06,.99),t(0,1)]}}
958_addGrooves(a,t,n,o){n.forEach(s=>{const i=new THREE.Mesh(new THREE.TorusGeometry(o,.008,8,32),t);i.rotation.x=Math.PI/2,i.position.y=s,i.castShadow=!0,a.add(i)})}_makeKnightHead(a){const t=new THREE.Group,n=new THREE.Shape;n.moveTo(-.1,0),n.bezierCurveTo(-.12,.15,-.08,.3,-.04,.4),n.bezierCurveTo(-.02,.44,.02,.46,.04,.46),n.lineTo(.1,.44),n.bezierCurveTo(.12,.4,.14,.3,.12,.15),n.bezierCurveTo(.12,.06,.1,0,.1,0),n.lineTo(-.1,0);const o=new THREE.ExtrudeGeometry(n,{depth:.22,bevelEnabled:!0,bevelThickness:.04,bevelSize:.035,bevelOffset:0,bevelSegments:10}),s=new THREE.Mesh(o,a);s.position.set(-.04,.48,-.11),s.rotation.set(0,0,.06),s.castShadow=!0,s.receiveShadow=!0,t.add(s);const i=new THREE.SphereGeometry(.13,24,18),r=new THREE.Mesh(i,a);r.scale.set(1,.8,.75),r.position.set(-.02,.9,0),r.castShadow=!0,t.add(r);const d=new THREE.SphereGeometry(.06,14,10),l=new THREE.Mesh(d,a);l.position.set(-.06,.86,-.08),l.scale.set(1,.8,.6),l.castShadow=!0,t.add(l);const c=l.clone();c.position.set(-.06,.86,.08),t.add(c);const p=new THREE.CapsuleGeometry(.03,.1,6,10),u=new THREE.Mesh(p,a);u.position.set(-.04,.96,0),u.rotation.set(Math.PI/2,0,0),u.castShadow=!0,t.add(u);const g=new THREE.CapsuleGeometry(.055,.18,10,14),y=new THREE.Mesh(g,a);y.position.set(-.18,.78,0),y.rotation.set(0,0,Math.PI*.4),y.castShadow=!0,t.add(y);const m=new THREE.SphereGeometry(.05,12,10),v=new THREE.Mesh(m,a);v.position.set(-.28,.72,0),v.scale.set(1,.65,.85),v.castShadow=!0,t.add(v);const T=new THREE.SphereGeometry(.022,10,8);[-.035,.035].forEach(w=>{const f=new THREE.Mesh(T,a);f.position.set(-.31,.71,w),f.scale.set(.8,.6,1),f.castShadow=!0,t.add(f)});const b=new THREE.MeshStandardMaterial({color:2236962,roughness:.9}),R=new THREE.BoxGeometry(.12,.005,.06),M=new THREE.Mesh(R,b);M.position.set(-.22,.7,0),M.rotation.set(0,0,.35),t.add(M);const k=new THREE.CapsuleGeometry(.04,.14,8,12),_=new THREE.Mesh(k,a);_.position.set(-.14,.74,0),_.rotation.set(0,0,Math.PI*.38),_.castShadow=!0,t.add(_);const I=new THREE.SphereGeometry(.07,12,10),E=new THREE.Mesh(I,a);E.position.set(-.08,.62,0),E.scale.set(.8,1.2,.7),E.castShadow=!0,t.add(E);const h=new THREE.ConeGeometry(.028,.09,8),S=new THREE.Mesh(h,a);S.position.set(.04,1.01,-.045),S.rotation.set(.12,0,-.3),S.castShadow=!0,t.add(S);const x=S.clone();x.position.set(.04,1.01,.045),x.rotation.set(-.12,0,-.3),t.add(x);const C=new THREE.MeshStandardMaterial({color:1118481,roughness:.3,metalness:.5}),H=new THREE.SphereGeometry(.02,12,10);[-.09,.09].forEach(w=>{const f=new THREE.SphereGeometry(.028,10,8),L=new THREE.Mesh(f,a);L.position.set(-.06,.92,w),L.scale.set(.6,.7,.5),t.add(L);const P=new THREE.Mesh(H,C);P.position.set(-.07,.92,w),t.add(P)});for(let w=0;w<8;w++){const f=w/7,L=.032-f*.006,P=new THREE.SphereGeometry(L,10,8),Z=new THREE.Mesh(P,a),fe=.54+f*.42,ge=.06+Math.sin(f*Math.PI*.6)*.025-f*.02;Z.position.set(ge,fe,0),Z.scale.set(.45,1.6,.8),Z.castShadow=!0,t.add(Z)}return t}_makeRookBattlements(a,t){const n=new THREE.Group,o=new THREE.CylinderGeometry(.21,.21,.05,28,1,!0),s=new THREE.Mesh(o,a);s.position.y=t+.025,s.castShadow=!0,n.add(s);const i=new THREE.CylinderGeometry(.21,.21,.02,28),r=new THREE.Mesh(i,a);r.position.y=t,r.castShadow=!0,n.add(r);for(let d=0;d<6;d++){const l=d/6*Math.PI*2,c=new THREE.BoxGeometry(.1,.13,.1),p=new THREE.Mesh(c,a);p.position.set(Math.sin(l)*.19,t+.05+.065,Math.cos(l)*.19),p.rotation.y=-l,p.castShadow=!0,n.add(p)}return n}_makePiece(a){if(this.theme==="alien")return this._makeAlienPiece(a);if(this.theme==="christmas")return this._makeChristmasPiece(a);if(this.theme==="halloween")return this._makeHalloweenPiece(a);const t=a[0]==="w",n=a[1],o=(t?this.wMat:this.bMat).clone();if(n==="N"){const r=new THREE.Mesh(new THREE.LatheGeometry(this._prof("N"),64),o);r.castShadow=!0,r.receiveShadow=!0,this._addGrooves(r,o,[.15,.19,.37,.4],.11);const d=this._makeKnightHead(o);return r.add(d),r.scale.set(1.15,1.15,1.15),r}if(n==="R"){const r=new THREE.Mesh(new THREE.LatheGeometry(this._prof("R"),64),o);r.castShadow=!0,r.receiveShadow=!0,this._addGrooves(r,o,[.15,.19,.37,.55,.65,.69],.15);const d=this._makeRookBattlements(o,.74);return r.add(d),r.scale.set(1.15,1.15,1.15),r}const s=new THREE.Mesh(new THREE.LatheGeometry(this._prof(n),64),o);if(s.castShadow=!0,s.receiveShadow=!0,n==="P"&&this._addGrooves(s,o,[.15,.19,.38,.42],.11),n==="B"&&this._addGrooves(s,o,[.15,.19,.36,.4,.56],.11),n==="Q"&&this._addGrooves(s,o,[.15,.19,.36,.55,.59],.13),n==="K"&&this._addGrooves(s,o,[.15,.19,.36,.55,.59],.14),n==="K"){const r=o.clone(),d=new THREE.Mesh(new THREE.BoxGeometry(.05,.28,.05),r),l=new THREE.Mesh(new THREE.BoxGeometry(.18,.05,.05),r);d.position.y=1+.14,l.position.y=1+.08,d.castShadow=!0,l.castShadow=!0;const c=new THREE.Mesh(new THREE.SphereGeometry(.04,12,12),r);c.position.y=1.02,c.castShadow=!0,s.add(d),s.add(l),s.add(c)}if(n==="Q"){const r=new THREE.Mesh(new THREE.SphereGeometry(.05,16,16),o);r.position.y=1.06,r.castShadow=!0;const d=new THREE.Mesh(new THREE.BoxGeometry(.02,.1,.02),o),l=new THREE.Mesh(new THREE.BoxGeometry(.07,.02,.02),o);d.position.y=1.14,l.position.y=1.12,d.castShadow=!0,l.castShadow=!0,s.add(r),s.add(d),s.add(l)}if(n==="B"){const r=new THREE.Mesh(new THREE.SphereGeometry(.035,14,14),o);r.position.y=1.01,r.castShadow=!0,s.add(r);const d=new THREE.MeshStandardMaterial({color:t?13154464:1315882,roughness:.5,metalness:.05}),l=new THREE.Mesh(new THREE.BoxGeometry(.005,.16,.2),d);l.position.set(0,.84,0),l.rotation.z=Math.PI*.18,s.add(l)}const i=Math.random()*100;return n==="K"?s.userData._animFn=r=>{s.scale.setScalar(1.15+Math.sin(r*1.5+i)*.005)}:n==="Q"?s.userData._animFn=r=>{s.rotation.y+=Math.sin(r*1.2+i)*3e-4}:n==="N"&&(s.userData._animFn=r=>{s.position.y=Math.sin(r*2+i)*.004}),s.scale.set(1.15,1.15,1.15),s}sync(a,t){for(const o in this.pieces)this.scene.remove(this.pieces[o]);this.pieces={};const n=t==="black";for(let o=0;o<8;o++)for(let s=0;s<8;s++){const i=a.board[o][s];if(i)try{const r=this._makePiece(i),d=n?7-o:o,l=n?7-s:s;r.position.set(l-3.5,0,d-3.5);const c=i[0]==="w";r.rotation.y=c!==n?-Math.PI/2:Math.PI/2,Object.assign(r.userData,{piece:i,row:o,col:s}),this.pieces[`${o}-${s}`]=r,this.scene.add(r)}catch(r){console.error("Piece creation error:",i,r)}}}
958clearHL(){this.hlMeshes.forEach(a=>this.scene.remove(a)),this.hlMeshes=[],this.squareMeshes.forEach(a=>{a.material.color.setHex(a.userData.origColor),a.material.emissive&&a.material.emissive.setHex(0)})}setTheme(a){this.theme=a;const t={classic:{w:16116438,b:7233428,wR:.22,bR:.2,wM:.08,bM:.14},alien:{w:61047,b:12272895,wR:.12,bR:.12,wM:.25,bM:.25},christmas:{w:16775406,b:13378099,wR:.28,bR:.22,wM:.05,bM:.08},halloween:{w:16746513,b:2759230,wR:.25,bR:.18,wM:.1,bM:.2}},n=t[a]||t.classic;this.wMat.color.setHex(n.w),this.wMat.roughness=n.wR,this.wMat.metalness=n.wM,this.bMat.color.setHex(n.b),this.bMat.roughness=n.bR,this.bMat.metalness=n.bM,$&&this.sync($,A),N&&N.applyHighlights()}_makeAlienPiece(a){const t=a[0]==="w",n=a[1],o=(t?this.wMat:this.bMat).clone();o.emissive=new THREE.Color(t?13073:2228292),o.emissiveIntensity=.15;const s=new THREE.Group,i=new THREE.MeshStandardMaterial({color:4521864,emissive:65348,emissiveIntensity:.6,roughness:.3}),r=new THREE.MeshStandardMaterial({color:1118481,roughness:.2,metalness:.8}),d=new THREE.MeshStandardMaterial({color:13426107,roughness:.35,metalness:.1}),l=new THREE.MeshStandardMaterial({color:65484,emissive:65450,emissiveIntensity:1,roughness:.1,transparent:!0,opacity:.8}),c=new THREE.Mesh(new THREE.CylinderGeometry(.22,.28,.06,24),o);c.castShadow=!0,s.add(c);const p=new THREE.Mesh(new THREE.TorusGeometry(.25,.012,8,24),i);p.rotation.x=Math.PI/2,p.position.y=.03,s.add(p);const u=new THREE.Mesh(new THREE.TorusGeometry(.18,.008,8,20),i);u.rotation.x=Math.PI/2,u.position.y=.035,s.add(u);const g={parts:[]};if(n==="P"){const y=new THREE.Mesh(new THREE.SphereGeometry(.11,18,14),o);y.position.y=.14,y.scale.set(1.2,.5,1),y.castShadow=!0,s.add(y);for(let _=0;_<4;_++){const I=new THREE.Mesh(new THREE.ConeGeometry(.012,.06,5),o);I.position.set(.03-_*.02,.19,0),I.castShadow=!0,s.add(I)}const m=[];for(let _=-1;_<=1;_+=2)for(let I=0;I<4;I++){const E=(I-1.5)*.35,h=new THREE.Mesh(new THREE.CylinderGeometry(.008,.012,.12,6),o);h.position.set(-.04+I*.03,.12,_*.1),h.rotation.set(0,0,_*.8+E*.3),h.castShadow=!0,s.add(h),m.push({mesh:h,baseRot:_*.8+E*.3,side:_,idx:I});const S=new THREE.Mesh(new THREE.CylinderGeometry(.005,.008,.08,6),o);S.position.set(-.04+I*.03,.06,_*.18),S.rotation.set(0,0,_*1.2),S.castShadow=!0,s.add(S);const x=new THREE.Mesh(new THREE.ConeGeometry(.006,.02,4),i);x.position.set(-.04+I*.03,.02,_*.22),x.rotation.x=_*.5,s.add(x)}const v=new THREE.Mesh(new THREE.CylinderGeometry(.015,.008,.14,8),o);v.position.set(.14,.16,0),v.rotation.z=-.6,v.castShadow=!0,s.add(v);const T=new THREE.Mesh(new THREE.CylinderGeometry(.008,.004,.1,6),o);T.position.set(.2,.22,0),T.rotation.z=-1,T.castShadow=!0,s.add(T);const b=new THREE.Mesh(new THREE.ConeGeometry(.006,.04,4),i);b.position.set(.23,.28,0),b.rotation.z=-1.2,s.add(b);const R=new THREE.Mesh(new THREE.SphereGeometry(.03,10,8),i);R.position.set(-.12,.15,0),s.add(R);const M=new THREE.Mesh(new THREE.SphereGeometry(.015,8,6),r);M.position.set(-.14,.15,0),s.add(M);const k=Math.random()*100;s.userData._animFn=_=>{m.forEach(I=>{I.mesh.rotation.z=I.baseRot+Math.sin(_*4+I.idx*1.2+k)*.12}),v.rotation.z=-.6+Math.sin(_*2.5+k)*.15,T.rotation.z=-1+Math.sin(_*3+k)*.12,b.rotation.z=-1.2+Math.sin(_*3.5+k)*.1,R.material.emissiveIntensity=.4+Math.sin(_*5+k)*.3,p.rotation.z=_*.5}}else if(n==="R"){const y=new THREE.Mesh(new THREE.CylinderGeometry(.06,.14,.18,16),o);y.position.y=.12,y.castShadow=!0,s.add(y);const m=new THREE.Mesh(new THREE.CylinderGeometry(.32,.32,.04,32),o);m.position.y=.24,m.castShadow=!0,s.add(m);const v=new THREE.Mesh(new THREE.TorusGeometry(.32,.018,8,32),o);v.rotation.x=Math.PI/2,v.position.y=.24,v.castShadow=!0,s.add(v);const T=new THREE.Mesh(new THREE.CylinderGeometry(.2,.22,.04,24),o);T.position.y=.32,T.castShadow=!0,s.add(T);const b=new THREE.Mesh(new THREE.TorusGeometry(.21,.015,8,24),o);b.rotation.x=Math.PI/2,b.position.y=.32,b.castShadow=!0,s.add(b);const R=i.clone();R.transparent=!0,R.opacity=.6;const M=new THREE.Mesh(new THREE.SphereGeometry(.12,20,12,0,Math.PI*2,0,Math.PI/2),R);M.position.y=.34,M.castShadow=!0,s.add(M);const k=new THREE.Mesh(new THREE.CylinderGeometry(.005,.005,.12,6),o);k.position.y=.46,k.castShadow=!0,s.add(k);const _=new THREE.Mesh(new THREE.SphereGeometry(.018,10,8),l);_.position.y=.52,s.add(_);const I=new THREE.Mesh(new THREE.BoxGeometry(.08,.03,.04),r);I.position.set(-.3,.24,0),s.add(I);const E=[];for(let H=0;H<10;H++){const w=H/10*Math.PI*2,f=new THREE.Mesh(new THREE.SphereGeometry(.012,8,6),i.clone());
958f.position.set(Math.sin(w)*.3,.24,Math.cos(w)*.3),s.add(f),E.push(f)}for(let H=0;H<4;H++){const w=H/4*Math.PI*2+Math.PI/4,f=new THREE.Mesh(new THREE.ConeGeometry(.03,.04,8),o);f.position.set(Math.sin(w)*.18,.18,Math.cos(w)*.18),f.rotation.x=Math.PI,f.castShadow=!0,s.add(f)}const h=[];for(let H=0;H<12;H++){const w=new THREE.Mesh(new THREE.SphereGeometry(.012,8,6),l.clone());w.position.y=.2,s.add(w),h.push(w)}const S=l.clone();S.opacity=.15;const x=new THREE.Mesh(new THREE.ConeGeometry(.14,.16,16),S);x.position.y=.1,x.rotation.x=Math.PI,s.add(x);const C=Math.random()*100;s.userData._animFn=H=>{m.position.y=.24+Math.sin(H*2+C)*.015,T.position.y=.32+Math.sin(H*2+C)*.015,M.position.y=.34+Math.sin(H*2+C)*.015,h.forEach((w,f)=>{const L=H*2.5+f/12*Math.PI*2,P=.16+Math.sin(H*5+f*1.5)*.04;w.position.set(Math.sin(L)*P,.18+Math.sin(H*6+f)*.02,Math.cos(L)*P),w.scale.setScalar(.5+Math.sin(H*8+f*2)*.5),w.material.opacity=.4+Math.sin(H*7+f)*.4}),M.material.emissiveIntensity=.4+Math.sin(H*3+C)*.3,M.material.opacity=.4+Math.sin(H*2.5+C)*.2,_.scale.setScalar(.8+Math.sin(H*6)*.4),E.forEach((w,f)=>{w.material.emissiveIntensity=.3+Math.sin(H*4+f*.8)*.5}),p.rotation.z=H*.8,u.rotation.z=-H*1.2,x.material.opacity=.08+Math.sin(H*3)*.08}}else if(n==="N"){const y=new THREE.Mesh(new THREE.SphereGeometry(.18,24,20),o);y.position.y=.52,y.scale.set(.85,1.3,.8),y.castShadow=!0,s.add(y);for(let M=0;M<6;M++){const k=M/6*Math.PI*2,_=new THREE.Mesh(new THREE.CylinderGeometry(.008,.008,.28,6),o);_.position.set(Math.sin(k)*.12,.52,Math.cos(k)*.12),_.castShadow=!0,s.add(_)}for(let M=0;M<8;M++){const k=M/8*Math.PI*2,_=new THREE.Mesh(new THREE.SphereGeometry(.012,6,6),i.clone());_.position.set(Math.sin(k)*.13,.48+M%3*.06,Math.cos(k)*.13),s.add(_),g.parts.push({mesh:_,type:"pulse",idx:M})}const m=new THREE.Mesh(new THREE.CylinderGeometry(.1,.06,.12,14),o);m.position.y=.32,m.castShadow=!0,s.add(m),[-.1,.1].forEach(M=>{const k=new THREE.Mesh(new THREE.SphereGeometry(.06,12,10),o);k.position.set(-.08,.5,M),k.scale.set(.7,.8,1),k.castShadow=!0,s.add(k);const _=new THREE.Mesh(new THREE.SphereGeometry(.045,12,10),i);_.position.set(-.1,.5,M),s.add(_);const I=new THREE.Mesh(new THREE.SphereGeometry(.02,8,8),r);I.position.set(-.13,.5,M),s.add(I);const E=new THREE.Mesh(new THREE.TorusGeometry(.04,.006,6,12,Math.PI),o);E.position.set(-.1,.53,M),E.rotation.set(0,M>0?-.3:.3,0),s.add(E)});const v=new THREE.Mesh(new THREE.ConeGeometry(.025,.04,4),d);v.position.set(-.14,.34,0),v.rotation.z=Math.PI/2,s.add(v);const T=new THREE.Mesh(new THREE.ConeGeometry(.02,.03,4),d);T.position.set(-.14,.3,0),T.rotation.z=-Math.PI/2,s.add(T);const b=[];for(let M=0;M<8;M++){const k=M/8*Math.PI*2,_=[],I=5;for(let E=0;E<I;E++){const h=E/I,x=.032*(1-(E+1)/I*.85),C=.032*(1-h*.85),H=.07-E*.005,w=new THREE.Mesh(new THREE.CylinderGeometry(x,C,H,8),o),f=.08+h*.22,L=.28-h*.22;w.position.set(Math.sin(k)*f,L,Math.cos(k)*f);const P=.3+h*.9;if(w.rotation.set(Math.cos(k)*P,0,-Math.sin(k)*P),w.castShadow=!0,s.add(w),_.push({mesh:w,baseRotX:Math.cos(k)*P,baseRotZ:-Math.sin(k)*P,basePosY:L}),E<I-1){const Z=.007*(1-h*.6),fe=f+.015,ge=new THREE.Mesh(new THREE.TorusGeometry(Z,Z*.4,4,6),i);ge.position.set(Math.sin(k)*fe,L-.01,Math.cos(k)*fe),ge.rotation.x=Math.PI/2,s.add(ge)}}b.push({segs:_,angle:k,idx:M})}const R=Math.random()*100;s.userData._animFn=M=>{b.forEach(_=>{const I=M*1.6+_.idx*.8+R;_.segs.forEach((E,h)=>{const S=h/(_.segs.length-1),x=.04+S*.14,C=1+S*.5;E.mesh.rotation.x=E.baseRotX+Math.sin(I*C+h*.6)*x,E.mesh.rotation.z=E.baseRotZ+Math.cos(I*.8*C+h*.5)*x*.7,h>=3&&(E.mesh.position.y=E.basePosY+Math.sin(I+h)*.01)})});const k=1+Math.sin(M*2.5+R)*.04;y.scale.set(.85*k,1.3,.8*k),g.parts.forEach(_=>{_.type==="pulse"&&(_.mesh.material.emissiveIntensity=.3+Math.sin(M*4+_.idx*1.1+R)*.4,_.mesh.scale.setScalar(.8+Math.sin(M*5+_.idx)*.3))}),p.rotation.z=M*.5,u.rotation.z=-M*.8}}else if(n==="B"){[-.06,.06].forEach(w=>{const f=new THREE.Mesh(new THREE.CylinderGeometry(.025,.035,.14,8),o);f.position.set(.02,.1,w),f.rotation.z=.15,f.castShadow=!0,s.add(f);const L=new THREE.Mesh(new THREE.CylinderGeometry(.018,.025,.1,8),o);L.position.set(.02,.04,w*1.1),L.castShadow=!0,s.add(L)});const y=new THREE.Mesh(new THREE.CylinderGeometry(.06,.09,.3,12),o);y.position.y=.26,y.castShadow=!0,s.add(y);const m=[];for(let w=0;w<5;w++){const f=new THREE.Mesh(new THREE.TorusGeometry(.065+w*.002,.005,6,12,Math.PI),o);f.position.set(.02,.16+w*.05,0),f.rotation.set(0,Math.PI/2,0),f.castShadow=!0,s.add(f),m.push(f)}const v=new THREE.Mesh(new THREE.CylinderGeometry(.03,.05,.1,10),o);v.position.y=.44,v.castShadow=!0,s.add(v);const T=new THREE.Mesh(new THREE.SphereGeometry(.1,18,14),o);T.position.set(-.02,.54,0),T.scale.set(1,.7,.75),T.castShadow=!0,s.add(T);const b=new THREE.Mesh(new THREE.CylinderGeometry(.04,.08,.22,14),o);b.position.set(.1,.58,0),b.rotation.z=.6,b.castShadow=!0,s.add(b);const R=new THREE.Mesh(new THREE.SphereGeometry(.04,10,10),o);R.position.set(.2,.64,0),R.castShadow=!0,s.add(R);const M=new THREE.Mesh(new THREE.BoxGeometry(.04,.02,.06),r);M.position.set(-.1,.48,0),s.add(M);const k=new THREE.Mesh(new THREE.CylinderGeometry(.008,.012,.05,6),r);k.position.set(-.12,.47,0),k.rotation.z=Math.PI/2,s.add(k);const _=new THREE.Mesh(new THREE.ConeGeometry(.005,.015,4),d);_.position.set(-.1,.46,-.02),s.add(_);const I=_.clone();I.position.z=.02,s.add(I);const E=_.clone();E.position.set(-.1,.5,-.02),E.rotation.x=Math.PI,s.add(E);const h=_.clone();h.position.set(-.1,.5,.02),h.rotation.x=Math.PI,s.add(h);const S=[];for(let w=0;w<6;w++){const f=new THREE.Mesh(new THREE.ConeGeometry(.008,.05+w*.008,4),o);f.position.set(.04+w*.025,.38+w*.035,0),f.rotation.z=.6,f.castShadow=!0,s.add(f),S.push(f)}const x=new THREE.Mesh(new THREE.CylinderGeometry(.018,.008,.2,8),o);x.position.set(.15,.16,0),x.rotation.z=-.8,x.castShadow=!0,s.add(x);const C=new THREE.Mesh(new THREE.ConeGeometry(.012,.06,4),i);C.position.set(.24,.1,0),C.rotation.z=-1.2,s.add(C);const H=Math.random()*100;s.userData._animFn=w=>{m.forEach((L,P)=>{const Z=1+Math.sin(w*2.2+P*.4+H)*.06;L.scale.set(Z,1,Z)});const f=Math.sin(w*1.5+H);k.position.x=-.12-(f>.7?(f-.7)*.15:0),x.rotation.x=Math.sin(w*2+H)*.15,C.rotation.x=Math.sin(w*2.5+H)*.12,S.forEac
958h((L,P)=>{L.scale.y=1+Math.sin(w*3+P*.7+H)*.15}),p.rotation.z=w*.4}}else if(n==="Q"){const y=new THREE.Mesh(new THREE.CylinderGeometry(.1,.16,.36,16),o);y.position.y=.24,y.castShadow=!0,s.add(y),[-.06,0,.06].forEach(E=>{const h=new THREE.Mesh(new THREE.BoxGeometry(.08,.1,.04),o);h.position.set(-.08,.28,E),h.castShadow=!0,s.add(h)});for(let E=0;E<4;E++){const h=new THREE.Mesh(new THREE.TorusGeometry(.11+E*.005,.006,6,16,Math.PI),o);h.position.set(.02,.14+E*.06,0),h.rotation.y=Math.PI/2,s.add(h)}const m=new THREE.Mesh(new THREE.CylinderGeometry(.05,.08,.1,12),o);m.position.y=.46,m.castShadow=!0,s.add(m);const v=new THREE.Mesh(new THREE.SphereGeometry(.12,20,16),o);v.position.y=.56,v.scale.set(.9,.85,.8),v.castShadow=!0,s.add(v);const T=[];for(let E=0;E<7;E++){const h=(E-3)/3,S=.26-Math.abs(h)*.06,x=new THREE.Mesh(new THREE.ConeGeometry(.018,S,6),o);if(x.position.set(.08,.74+Math.abs(h)*.02,h*.05),x.rotation.z=.4+Math.abs(h)*.15,x.rotation.x=h*.3,x.castShadow=!0,s.add(x),T.push({mesh:x,baseH:S,idx:E}),E<6){const C=new THREE.Mesh(new THREE.PlaneGeometry(.015,S*.6),o.clone());C.position.set(.08,.72,(h+1/6)*.05),C.rotation.z=.5,C.material.transparent=!0,C.material.opacity=.4,C.material.side=THREE.DoubleSide,s.add(C)}}const b=[];
958[-.05,.05].forEach(E=>{const h=new THREE.Mesh(new THREE.SphereGeometry(.04,12,10),i.clone());h.position.set(-.09,.58,E),s.add(h),b.push(h);const S=new THREE.Mesh(new THREE.SphereGeometry(.018,8,6),r);S.position.set(-.12,.58,E),s.add(S)}),[-.08,.08].forEach(E=>{const h=new THREE.Mesh(new THREE.SphereGeometry(.02,8,6),i.clone());h.position.set(-.07,.54,E),s.add(h),b.push(h)});const R=[];[-.03,.03].forEach(E=>{const h=new THREE.Mesh(new THREE.CylinderGeometry(.006,.015,.08,6),o);h.position.set(-.14,.5,E),h.rotation.z=.6,h.rotation.x=E>0?-.2:.2,h.castShadow=!0,s.add(h),R.push({mesh:h,baseRotX:E>0?-.2:.2,side:E})});const M=o.clone();M.transparent=!0,M.opacity=.5;const k=new THREE.Mesh(new THREE.SphereGeometry(.1,14,12),M);k.position.set(.1,.18,0),k.scale.set(.8,1.2,1),s.add(k);const _=[];for(let E=0;E<4;E++){const h=new THREE.Mesh(new THREE.SphereGeometry(.015,8,8),l.clone());s.add(h),_.push(h)}const I=Math.random()*100;s.userData._animFn=E=>{T.forEach(h=>{h.mesh.scale.y=1+Math.sin(E*3+h.idx*.6+I)*.12}),b.forEach((h,S)=>{h.material.emissiveIntensity=.4+Math.sin(E*4+S*1.5+I)*.4}),R.forEach(h=>{h.mesh.rotation.x=h.baseRotX+Math.sin(E*3.5+I)*.15}),_.forEach((h,S)=>{const x=E*2+S/4*Math.PI*2;h.position.set(Math.sin(x)*.22,.5+Math.sin(E*3+S)*.06,Math.cos(x)*.22),h.scale.setScalar(.5+Math.sin(E*5+S)*.5),h.material.opacity=.4+Math.sin(E*6+S)*.4}),k.scale.y=1.2+Math.sin(E*1.5+I)*.06,p.rotation.z=E*.6}}else if(n==="K"){const y=new THREE.Mesh(new THREE.CylinderGeometry(.12,.18,.2,16),o);y.position.y=.13,y.castShadow=!0,s.add(y);const m=new THREE.Mesh(new THREE.CylinderGeometry(.08,.12,.22,14),o);m.position.y=.34,m.castShadow=!0,s.add(m);const v=new THREE.Mesh(new THREE.SphereGeometry(.22,24,20),o);v.position.y=.62,v.scale.set(.9,1.15,.85),v.castShadow=!0,s.add(v);for(let h=0;h<8;h++){const S=h/8*Math.PI*2,x=new THREE.Mesh(new THREE.TorusGeometry(.16,.008,6,20,Math.PI*.8),o);x.position.set(0,.58+h%3*.06,0),x.rotation.set(S,S*.5,0),x.castShadow=!0,s.add(x)}const T=new THREE.Mesh(new THREE.TorusGeometry(.18,.015,8,24),i);T.rotation.x=Math.PI/2,T.position.y=.86,s.add(T);const b=new THREE.Mesh(new THREE.TorusGeometry(.14,.01,6,20),i);b.rotation.x=Math.PI/2,b.position.y=.9,s.add(b);const R=[];for(let h=0;h<3;h++){const S=h/3*Math.PI*2,x=new THREE.Mesh(new THREE.ConeGeometry(.012,.14,6),i.clone());x.position.set(Math.sin(S)*.14,.92,Math.cos(S)*.14),x.castShadow=!0,s.add(x),R.push(x)}const M=[];[-.07,-.03,.03,.07].forEach(h=>{const S=new THREE.Mesh(new THREE.SphereGeometry(.03,10,8),i.clone());S.position.set(-.14,.6,h),s.add(S),M.push(S);const x=new THREE.Mesh(new THREE.SphereGeometry(.014,8,6),r);x.position.set(-.16,.6,h),s.add(x)});const k=[];for(let h=0;h<6;h++){const S=h/6*Math.PI*2,x=new THREE.Mesh(new THREE.CylinderGeometry(.008,.018,.18,6),o);x.position.set(Math.sin(S)*.1,.3,Math.cos(S)*.1),x.rotation.set(Math.cos(S)*.4,0,-Math.sin(S)*.4),x.castShadow=!0,s.add(x),k.push({mesh:x,baseRotX:Math.cos(S)*.4,baseRotZ:-Math.sin(S)*.4,angle:S})}const _=[];for(let h=0;h<8;h++){const S=new THREE.Mesh(new THREE.SphereGeometry(.008,6,6),l.clone());s.add(S),_.push(S)}const I=new THREE.Mesh(new THREE.TorusGeometry(.1,.012,8,20),i);I.rotation.x=Math.PI/2,I.position.y=.44,s.add(I);const E=Math.random()*100;s.userData._animFn=h=>{T.rotation.z=h*1.5,b.rotation.z=-h*2;const S=1+Math.sin(h*2+E)*.04;v.scale.set(.9*S,1.15*S,.85*S),R.forEach((x,C)=>{x.material.emissiveIntensity=.4+Math.sin(h*4+C*2+E)*.4,x.scale.y=1+Math.sin(h*3+C+E)*.15}),M.forEach((x,C)=>{x.material.emissiveIntensity=.3+Math.sin(h*5+C*.8+E)*.5}),k.forEach(x=>{x.mesh.rotation.x=x.baseRotX+Math.sin(h*1.8+x.angle+E)*.15,x.mesh.rotation.z=x.baseRotZ+Math.cos(h*1.5+x.angle+E)*.1}),_.forEach((x,C)=>{const H=h*2.2+C/8*Math.PI*2,w=.2+Math.sin(h*3+C)*.05,f=.62+Math.sin(h*2.5+C*1.3)*.1;x.position.set(Math.sin(H)*w,f,Math.cos(H)*w),x.scale.setScalar(.5+Math.sin(h*6+C)*.5),x.material.opacity=.3+Math.sin(h*4+C)*.4}),p.rotation.z=h*.5,u.rotation.z=-h*.7}}return s.scale.set(1.15,1.15,1.15),s}_makeChristmasPiece(a){const t=a[0]==="w",n=a[1],o=(t?this.wMat:this.bMat).clone(),s=new THREE.Group,i=new THREE.MeshStandardMaterial({color:16766720,roughness:.25,metalness:.6}),r=new THREE.MeshStandardMaterial({color:1732142,roughness:.35,metalness:.05}),d=new THREE.MeshStandardMaterial({color:16720418,roughness:.3,metalness:.2,emissive:3342336,emissiveIntensity:.2}),l=new THREE.MeshStandardMaterial({color:16777215,roughness:.5}),c=new THREE.Mesh(new THREE.CylinderGeometry(.24,.28,.06,24),o);if(c.castShadow=!0,s.add(c),n==="P"){const p=new THREE.Mesh(new THREE.BoxGeometry(.26,.26,.26),o);p.position.y=.19,p.castShadow=!0,s.add(p);const u=new THREE.Mesh(new THREE.BoxGeometry(.28,.04,.04),i);u.position.y=.19,u.castShadow=!0,s.add(u);const g=new THREE.Mesh(new THREE.BoxGeometry(.04,.04,.28),i);g.position.y=.19,g.castShadow=!0,s.add(g);const y=new THREE.Mesh(new THREE.BoxGeometry(.04,.28,.04),i);
958y.position.y=.19,y.castShadow=!0,s.add(y);const m=new THREE.Mesh(new THREE.SphereGeometry(.05,10,8),i);m.position.set(.05,.36,0),m.scale.set(1.4,.6,.8),s.add(m);const v=m.clone();v.position.set(-.05,.36,0),s.add(v);const T=new THREE.Mesh(new THREE.SphereGeometry(.03,8,8),i);T.position.y=.36,s.add(T);const b=Math.random()*100;s.userData._animFn=R=>{m.scale.x=1.4+Math.sin(R*3+b)*.15,v.scale.x=1.4+Math.sin(R*3+b+1)*.15,p.rotation.y=Math.sin(R*1.5+b)*.03}}else if(n==="R"){const p=new THREE.Mesh(new THREE.BoxGeometry(.28,.52,.28),o);p.position.y=.32,p.castShadow=!0,s.add(p);const u=new THREE.Mesh(new THREE.BoxGeometry(.32,.06,.32),o);u.position.y=.61,u.castShadow=!0,s.add(u);for(let m=0;m<4;m++){const v=new THREE.Mesh(new THREE.BoxGeometry(.3,.005,.005),new THREE.MeshStandardMaterial({color:3355443,roughness:.8}));v.position.set(0,.12+m*.12,.142),v.castShadow=!0,s.add(v);const T=v.clone();T.position.z=-.142,s.add(T)}const g=new THREE.Mesh(new THREE.BoxGeometry(.34,.04,.34),l);g.position.y=.66,g.castShadow=!0,s.add(g);const y=Math.random()*100;s.userData._animFn=m=>{g.position.y=.66+Math.sin(m*2+y)*.005}}else if(n==="N"){const p=new THREE.Mesh(new THREE.CylinderGeometry(.06,.1,.28,12),o);p.position.y=.2,p.castShadow=!0,s.add(p);const u=new THREE.Mesh(new THREE.SphereGeometry(.12,16,14),o);u.position.set(-.04,.44,0),u.scale.set(1.1,.9,.85),u.castShadow=!0,s.add(u),[-.08,.08].forEach(v=>{const T=new THREE.Mesh(new THREE.CylinderGeometry(.012,.012,.18,6),o);T.position.set(.02,.58,v),T.rotation.z=v>0?-.3:.3,T.castShadow=!0,s.add(T);const b=new THREE.Mesh(new THREE.CylinderGeometry(.008,.008,.1,6),o);b.position.set(v>0?.08:-.08,.66,v*1.2),b.rotation.z=v>0?-.8:.8,b.castShadow=!0,s.add(b);const R=new THREE.Mesh(new THREE.CylinderGeometry(.008,.008,.08,6),o);R.position.set(v>0?.04:-.04,.62,v*1.1),R.rotation.z=v>0?.4:-.4,R.castShadow=!0,s.add(R)});const g=new THREE.Mesh(new THREE.SphereGeometry(.035,10,10),d);g.position.set(-.15,.42,0),s.add(g);const y=new THREE.MeshStandardMaterial({color:1118481,roughness:.3});[-.04,.04].forEach(v=>{const T=new THREE.Mesh(new THREE.SphereGeometry(.02,8,8),y);T.position.set(-.12,.47,v),s.add(T)});const m=Math.random()*100;
958s.userData._animFn=v=>{g.material.emissiveIntensity=.1+Math.sin(v*4+m)*.3,g.scale.setScalar(.8+Math.sin(v*5+m)*.3),u.rotation.x=Math.sin(v*2+m)*.04,u.position.y=.44+Math.sin(v*2.5+m)*.008}}else if(n==="B"){const p=new THREE.Mesh(new THREE.CylinderGeometry(.04,.06,.12,8),o);p.position.y=.12,p.castShadow=!0,s.add(p),[[.28,.24,.22],[.22,.2,.36],[.16,.16,.5]].forEach(([v,T,b])=>{const R=new THREE.Mesh(new THREE.ConeGeometry(v,T,12),r);R.position.y=b,R.castShadow=!0,s.add(R)});const g=new THREE.Mesh(new THREE.OctahedronGeometry(.04,0),i);g.position.y=.72,g.rotation.z=Math.PI/4,g.castShadow=!0,s.add(g);const y=[16720418,2245887,16768256];for(let v=0;v<5;v++){const T=v/5*Math.PI*2,b=v<2?0:v<4?1:2,R=[.18,.14,.1][b],M=[.22,.36,.5][b],k=new THREE.Mesh(new THREE.SphereGeometry(.02,8,8),new THREE.MeshStandardMaterial({color:y[v%3],roughness:.2,metalness:.4}));k.position.set(Math.sin(T)*R,M,Math.cos(T)*R),s.add(k)}const m=Math.random()*100;s.userData._animFn=v=>{g.rotation.y=v*2,g.scale.setScalar(.8+Math.sin(v*4+m)*.3)}}else if(n==="Q"){const p=new THREE.Mesh(new THREE.ConeGeometry(.14,.4,12),o);p.position.y=.26,p.castShadow=!0,s.add(p);const u=new THREE.Mesh(new THREE.SphereGeometry(.1,16,14),o);u.position.y=.54,u.castShadow=!0,s.add(u),[-.01,.01].forEach(v=>{const T=new THREE.Mesh(new THREE.SphereGeometry(.12,12,10),l);T.position.set(.1,.38,v>0?.12:-.12),T.scale.set(.3,.8,1),T.castShadow=!0,s.add(T)});const g=new THREE.Mesh(new THREE.TorusGeometry(.08,.012,8,20),i);g.rotation.x=Math.PI/2,g.position.y=.68,s.add(g);const y=new THREE.MeshStandardMaterial({color:2245802,roughness:.3});[-.03,.03].forEach(v=>{const T=new THREE.Mesh(new THREE.SphereGeometry(.015,8,8),y);T.position.set(-.08,.56,v),s.add(T)});const m=Math.random()*100;
958s.userData._animFn=v=>{g.rotation.z=Math.sin(v*2+m)*.1,g.position.y=.68+Math.sin(v*3+m)*.01,p.position.y=.26+Math.sin(v*2+m)*.008,u.position.y=.54+Math.sin(v*2+m)*.008}}else if(n==="K"){const p=new THREE.Mesh(new THREE.SphereGeometry(.18,18,16),o);p.position.y=.26,p.scale.set(1,1.2,.9),p.castShadow=!0,s.add(p);const u=new THREE.Mesh(new THREE.TorusGeometry(.16,.02,8,20),new THREE.MeshStandardMaterial({color:1118481,roughness:.5}));u.rotation.x=Math.PI/2,u.position.y=.22,s.add(u);const g=new THREE.Mesh(new THREE.BoxGeometry(.06,.05,.04),i);g.position.set(-.16,.22,0),s.add(g);const y=new THREE.Mesh(new THREE.SphereGeometry(.11,16,14),new THREE.MeshStandardMaterial({color:16768181,roughness:.5}));y.position.y=.48,y.castShadow=!0,s.add(y);const m=new THREE.Mesh(new THREE.ConeGeometry(.1,.22,12),d);m.position.set(.03,.64,0),m.rotation.z=.2,m.castShadow=!0,s.add(m);const v=new THREE.Mesh(new THREE.TorusGeometry(.11,.02,8,20),l);v.rotation.x=Math.PI/2,v.position.y=.56,s.add(v);const T=new THREE.Mesh(new THREE.SphereGeometry(.03,10,8),l);T.position.set(.08,.76,0),s.add(T);const b=new THREE.Mesh(new THREE.SphereGeometry(.1,14,12),l);b.position.set(-.06,.4,0),b.scale.set(.8,1.2,.9),b.castShadow=!0,s.add(b);const R=new THREE.MeshStandardMaterial({color:2237064,roughness:.3});[-.03,.03].forEach(_=>{const I=new THREE.Mesh(new THREE.SphereGeometry(.015,8,8),R);I.position.set(-.09,.5,_),s.add(I)});const M=new THREE.Mesh(new THREE.SphereGeometry(.025,8,8),d);M.position.set(-.11,.46,0),s.add(M);const k=Math.random()*100;s.userData._animFn=_=>{T.position.y=.76+Math.sin(_*3+k)*.015,T.position.x=.08+Math.sin(_*2+k)*.01,p.scale.x=1+Math.sin(_*2.5+k)*.02,b.scale.y=1.2+Math.sin(_*2+k)*.03,M.material.emissiveIntensity=.1+Math.sin(_*4+k)*.15}}return s.scale.set(1.15,1.15,1.15),s}_makeHalloweenPiece(a){const t=a[0]==="w",n=a[1],o=(t?this.wMat:this.bMat).clone(),s=new THREE.Group,i=new THREE.MeshStandardMaterial({color:15260872,roughness:.45,metalness:.05}),r=new THREE.MeshStandardMaterial({color:8978346,emissive:2293572,emissiveIntensity:.4,roughness:.3}),d=new THREE.MeshStandardMaterial({color:1710638,roughness:.3,metalness:.15}),l=new THREE.MeshStandardMaterial({color:11141120,roughness:.3,metalness:.2,emissive:3342336,emissiveIntensity:.2}),c=new THREE.MeshStandardMaterial({color:6044190,roughness:.6,metalness:0}),p=new THREE.MeshStandardMaterial({color:8423568,roughness:.65,metalness:.05}),u=new THREE.MeshStandardMaterial({color:6963248,roughness:.55,metalness:.05}),g=new THREE.MeshStandardMaterial({color:5914664,roughness:.75,metalness:0}),y=new THREE.MeshStandardMaterial({color:1714718,roughness:.4,metalness:.1}),m=new THREE.MeshStandardMaterial({color:2755098,roughness:.35,metalness:.15}),v=new THREE.Mesh(new THREE.CylinderGeometry(.22,.28,.06,8),o);v.castShadow=!0,s.add(v);const T=new THREE.Mesh(new THREE.TorusGeometry(.25,.01,6,8),o);if(T.rotation.x=Math.PI/2,T.position.y=.03,s.add(T),n==="P"){const b=new THREE.Mesh(new THREE.BoxGeometry(.06,.38,.22),p);b.position.y=.25,b.castShadow=!0,s.add(b);const R=new THREE.Mesh(new THREE.CylinderGeometry(.11,.11,.06,16,1,!1,0,Math.PI),p);R.position.y=.44,R.rotation.x=-Math.PI/2,R.castShadow=!0,s.add(R);const M=new THREE.Mesh(new THREE.BoxGeometry(.008,.14,.03),o);M.position.set(-.035,.3,0),s.add(M);const k=new THREE.Mesh(new THREE.BoxGeometry(.008,.03,.1),o);k.position.set(-.035,.34,0),s.add(k);const _=new THREE.Mesh(new THREE.BoxGeometry(.07,.1,.005),d);_.position.set(0,.2,.06),_.rotation.z=.3,s.add(_);const I=new THREE.Mesh(new THREE.BoxGeometry(.07,.07,.005),d);I.position.set(0,.15,-.04),I.rotation.z=-.4,s.add(I);const E=new THREE.Mesh(new THREE.SphereGeometry(.14,10,8),c);E.position.y=.04,E.scale.set(.8,.3,1),E.castShadow=!0,s.add(E);const h=[];for(let x=0;x<3;x++){const C=new THREE.Mesh(new THREE.SphereGeometry(.015,6,6),r.clone());C.material.transparent=!0,C.material.opacity=.4,s.add(C),h.push(C)}const S=Math.random()*100;s.userData._animFn=x=>{h.forEach((C,H)=>{const w=x*1.5+H*2.1+S;C.position.set(Math.sin(w)*.12,.35+Math.sin(x*2+H)*.08,Math.cos(w)*.12),C.material.opacity=.2+Math.sin(x*3+H)*.2,C.scale.setScalar(.5+Math.sin(x*4+H)*.4)}),T.rotation.z=x*.3}}else if(n==="R"){const b=new THREE.Mesh(new THREE.CylinderGeometry(.14,.18,.48,8),u);b.position.y=.3,b.castShadow=!0,s.add(b);for(let S=0;S<6;S++){const x=S/6*Math.PI*2,C=new THREE.Mesh(new THREE.BoxGeometry(.05,.08,.05),u);C.position.set(Math.sin(x)*.14,.58,Math.cos(x)*.14),C.castShadow=!0,s.add(C)}const R=[];
958[0,Math.PI/2,Math.PI,Math.PI*1.5].forEach((S,x)=>{const C=new THREE.Mesh(new THREE.BoxGeometry(.04,.08,.03),r.clone());C.position.set(Math.sin(S)*.16,.36-x%2*.1,Math.cos(S)*.16),s.add(C),R.push(C)});const M=new THREE.Mesh(new THREE.ConeGeometry(.18,.2,8),o);M.position.y=.64,M.castShadow=!0,s.add(M);const k=new THREE.Mesh(new THREE.CylinderGeometry(.004,.004,.1,4),d);k.position.y=.78,s.add(k);const _=new THREE.Mesh(new THREE.SphereGeometry(.015,6,6),d);_.position.set(-.18,.46,.05),s.add(_);const I=new THREE.Mesh(new THREE.PlaneGeometry(.04,.015),d);I.position.set(-.2,.46,.05),s.add(I);const E=new THREE.Mesh(new THREE.PlaneGeometry(.04,.015),d);E.position.set(-.16,.46,.05),s.add(E);const h=Math.random()*100;s.userData._animFn=S=>{R.forEach((x,C)=>{x.material.emissiveIntensity=.2+Math.sin(S*3+C*1.7+h)*.3}),I.rotation.z=.3+Math.sin(S*8+h)*.4,E.rotation.z=-.3-Math.sin(S*8+h)*.4,k.rotation.y=S*2,T.rotation.z=S*.2}}else if(n==="N"){const b=new THREE.Mesh(new THREE.CylinderGeometry(.08,.12,.28,12),g);b.position.y=.2,b.castShadow=!0,s.add(b);const R=new THREE.Mesh(new THREE.SphereGeometry(.12,14,10),g);R.position.y=.36,R.scale.set(1.2,.5,.9),R.castShadow=!0,s.add(R);const M=new THREE.Mesh(new THREE.CylinderGeometry(.05,.07,.08,10),g);M.position.y=.42,M.castShadow=!0,s.add(M);const k=new THREE.Mesh(new THREE.SphereGeometry(.1,16,14),g);k.position.y=.52,k.scale.set(.9,1,.85),k.castShadow=!0,s.add(k);const _=new THREE.Mesh(new THREE.CylinderGeometry(.03,.05,.1,10),g);_.position.set(-.1,.48,0),_.rotation.z=Math.PI/2,_.castShadow=!0,s.add(_);const I=new THREE.Mesh(new THREE.SphereGeometry(.025,8,8),d);I.position.set(-.15,.49,0),s.add(I);const E=new THREE.Mesh(new THREE.CylinderGeometry(.025,.04,.08,8),g);E.position.set(-.1,.44,0),E.rotation.z=Math.PI/2,E.castShadow=!0,s.add(E),[-.02,.02].forEach(w=>{const f=new THREE.Mesh(new THREE.ConeGeometry(.006,.03,4),i);f.position.set(-.12,.42,w),f.rotation.x=Math.PI,s.add(f);const L=new THREE.Mesh(new THREE.ConeGeometry(.006,.025,4),i);L.position.set(-.13,.46,w),s.add(L)});const h=new THREE.MeshStandardMaterial({color:16763904,emissive:16746496,emissiveIntensity:.8,roughness:.2}),S=[];[-.04,.04].forEach(w=>{const f=new THREE.Mesh(new THREE.SphereGeometry(.02,10,8),h.clone());f.position.set(-.08,.54,w),s.add(f),S.push(f)});const x=[];[-.06,.06].forEach(w=>{const f=new THREE.Mesh(new THREE.ConeGeometry(.025,.08,4),g);f.position.set(.02,.62,w),f.rotation.z=w>0?-.2:.2,f.castShadow=!0,s.add(f),x.push({mesh:f,baseRotZ:w>0?-.2:.2})});for(let w=0;w<6;w++){const f=w/6*Math.PI*2,L=new THREE.Mesh(new THREE.ConeGeometry(.008,.05,4),g);L.position.set(Math.sin(f)*.1,.38,Math.cos(f)*.1),L.rotation.set(Math.cos(f)*.4,0,Math.sin(f)*.4),s.add(L)}[-.1,.1].forEach(w=>{const f=new THREE.Mesh(new THREE.CylinderGeometry(.018,.025,.14,6),g);f.position.set(-.02,.28,w),f.rotation.z=w>0?.5:-.5,f.castShadow=!0,s.add(f);for(let L=-1;L<=1;L++){const P=new THREE.Mesh(new THREE.ConeGeometry(.004,.025,4),i);P.position.set(-.06,.2,w+L*.012),P.rotation.z=w>0?.8:-.8,s.add(P)}});const C=new THREE.Mesh(new THREE.TorusGeometry(.08,.012,6,16),o);C.rotation.x=Math.PI/2,C.position.y=.4,s.add(C);const H=Math.random()*100;s.userData._animFn=w=>{const f=1+Math.sin(w*2.5+H)*.03;
958b.scale.set(f,1,f),R.scale.set(1.2*f,.5,.9*f),S.forEach((L,P)=>{L.material.emissiveIntensity=.5+Math.sin(w*4+P+H)*.4}),x.forEach((L,P)=>{L.mesh.rotation.z=L.baseRotZ+Math.sin(w*5+P*3+H)*.08}),E.rotation.x=Math.sin(w*3+H)*.05,T.rotation.z=w*.3}}else if(n==="B"){const b=new THREE.Mesh(new THREE.ConeGeometry(.16,.36,12),y);b.position.y=.24,b.castShadow=!0,s.add(b);const R=new THREE.Mesh(new THREE.TorusGeometry(.16,.012,6,16),y);R.rotation.x=Math.PI/2,R.position.y=.08,s.add(R);const M=new THREE.Mesh(new THREE.CylinderGeometry(.05,.08,.14,10),y);M.position.y=.4,M.castShadow=!0,s.add(M);const k=new THREE.Mesh(new THREE.SphereGeometry(.08,14,12),new THREE.MeshStandardMaterial({color:9485168,roughness:.5}));k.position.y=.52,k.castShadow=!0,s.add(k);const _=new THREE.Mesh(new THREE.CylinderGeometry(.14,.14,.015,16),d);_.position.y=.58,_.castShadow=!0,s.add(_);const I=new THREE.Mesh(new THREE.ConeGeometry(.08,.24,10),d);I.position.y=.72,I.rotation.z=.08,I.castShadow=!0,s.add(I);const E=new THREE.Mesh(new THREE.BoxGeometry(.04,.03,.02),o);E.position.set(-.08,.62,0),s.add(E);const h=new THREE.Mesh(new THREE.ConeGeometry(.012,.04,4),new THREE.MeshStandardMaterial({color:8432224,roughness:.5}));h.position.set(-.08,.5,0),h.rotation.z=Math.PI/2,s.add(h);const S=[];[-.025,.025].forEach(w=>{const f=new THREE.Mesh(new THREE.SphereGeometry(.012,8,6),r.clone());f.position.set(-.06,.53,w),s.add(f),S.push(f)});const x=new THREE.Mesh(new THREE.CylinderGeometry(.008,.008,.4,6),c);x.position.set(.1,.26,.06),x.rotation.z=.15,x.castShadow=!0,s.add(x);const C=new THREE.Mesh(new THREE.ConeGeometry(.04,.1,6),c);C.position.set(.12,.04,.06),C.castShadow=!0,s.add(C);const H=Math.random()*100;s.userData._animFn=w=>{I.rotation.z=.08+Math.sin(w*2+H)*.06,S.forEach((f,L)=>{f.material.emissiveIntensity=.2+Math.sin(w*5+L*2+H)*.3}),x.rotation.x=Math.sin(w*1.5+H)*.05,b.rotation.y=Math.sin(w*1.2+H)*.03,T.rotation.z=w*.25}}else if(n==="Q"){const b=new THREE.Mesh(new THREE.ConeGeometry(.16,.38,14),m);b.position.y=.25,b.castShadow=!0,s.add(b);const R=new THREE.Mesh(new THREE.CylinderGeometry(.055,.08,.18,12),m);R.position.y=.42,R.castShadow=!0,s.add(R);const M=[];[-.01,.01].forEach(C=>{const H=new THREE.Mesh(new THREE.PlaneGeometry(.24,.36),l.clone());H.position.set(.06,.3,C>0?.08:-.08),H.rotation.y=C>0?-.4:.4,H.material.side=THREE.DoubleSide,H.castShadow=!0,s.add(H),M.push({mesh:H,baseRotY:C>0?-.4:.4})}),[-.06,.06].forEach(C=>{const H=new THREE.Mesh(new THREE.BoxGeometry(.04,.12,.01),l);H.position.set(.04,.48,C),H.rotation.y=C>0?-.3:.3,s.add(H)});const k=new THREE.Mesh(new THREE.SphereGeometry(.08,14,12),new THREE.MeshStandardMaterial({color:15260896,roughness:.4}));k.position.y=.56,k.castShadow=!0,s.add(k);const _=new THREE.MeshStandardMaterial({color:16711680,emissive:11141120,emissiveIntensity:.6,roughness:.2}),I=[];[-.03,.03].forEach(C=>{const H=new THREE.Mesh(new THREE.SphereGeometry(.015,8,8),_.clone());H.position.set(-.06,.58,C),s.add(H),I.push(H)}),[-.015,.015].forEach(C=>{const H=new THREE.Mesh(new THREE.ConeGeometry(.004,.025,4),i);H.position.set(-.06,.5,C),s.add(H)});const E=new THREE.Mesh(new THREE.SphereGeometry(.09,12,10),d);E.position.set(.02,.58,0),E.scale.set(.9,1,.9),E.castShadow=!0,s.add(E);const h=new THREE.Mesh(new THREE.TorusGeometry(.06,.008,6,12,Math.PI),o);h.position.set(-.02,.64,0),h.rotation.x=.3,s.add(h);const S=new THREE.Mesh(new THREE.OctahedronGeometry(.015,0),r);S.position.set(-.04,.66,0),s.add(S);const x=Math.random()*100;s.userData._animFn=C=>{M.forEach((H,w)=>{H.mesh.rotation.y=H.baseRotY+Math.sin(C*2+w*Math.PI+x)*.08}),I.forEach((H,w)=>{H.material.emissiveIntensity=.3+Math.sin(C*4+w+x)*.4}),S.rotation.y=C*3,S.scale.setScalar(.8+Math.sin(C*5+x)*.3),b.rotation.y=Math.sin(C*1.3+x)*.02,T.rotation.z=C*.3}}else if(n==="K"){const b=new THREE.Mesh(new THREE.ConeGeometry(.18,.52,14),d);b.position.y=.32,b.castShadow=!0,s.add(b);const R=[];for(let w=0;w<4;w++){const f=w/4*Math.PI*2,L=new THREE.Mesh(new THREE.BoxGeometry(.01,.4,.04),d);L.position.set(Math.sin(f)*.1,.28,Math.cos(f)*.1),L.rotation.y=f,s.add(L),R.push(L)}const M=new THREE.Mesh(new THREE.SphereGeometry(.12,16,12,0,Math.PI*2,0,Math.PI*.65),d);
958M.position.y=.62,M.castShadow=!0,s.add(M);const k=new THREE.Mesh(new THREE.SphereGeometry(.08,12,10),new THREE.MeshStandardMaterial({color:328976,roughness:.9}));k.position.set(-.03,.6,0),k.scale.set(.7,.8,.7),s.add(k);const _=new THREE.MeshStandardMaterial({color:16729088,emissive:16720384,emissiveIntensity:.9,roughness:.1}),I=[];[-.025,.025].forEach(w=>{const f=new THREE.Mesh(new THREE.SphereGeometry(.012,8,8),_.clone());f.position.set(-.08,.61,w),s.add(f),I.push(f)});const E=new THREE.Mesh(new THREE.CylinderGeometry(.01,.01,.7,6),c);E.position.set(.14,.38,.08),E.rotation.z=.12,E.castShadow=!0,s.add(E);const h=new THREE.Mesh(new THREE.TorusGeometry(.12,.008,4,12,Math.PI*.6),new THREE.MeshStandardMaterial({color:12303308,roughness:.15,metalness:.8}));h.position.set(.1,.72,.08),h.rotation.set(Math.PI/2,.3,.5),h.castShadow=!0,s.add(h);const S=new THREE.Mesh(new THREE.TorusGeometry(.11,.003,3,10,Math.PI*.55),new THREE.MeshStandardMaterial({color:15658751,roughness:.1,metalness:.9,emissive:4474111,emissiveIntensity:.2}));S.position.set(.1,.72,.08),S.rotation.set(Math.PI/2,.3,.5),s.add(S);const x=new THREE.Mesh(new THREE.SphereGeometry(.02,8,8),i);x.position.set(.08,.52,.06),s.add(x),[-.008,0,.008].forEach(w=>{const f=new THREE.Mesh(new THREE.CylinderGeometry(.003,.003,.025,4),i);f.position.set(.06,.5,.06+w),f.rotation.z=.5,s.add(f)});const C=[];for(let w=0;w<4;w++){const f=new THREE.Mesh(new THREE.SphereGeometry(.01,6,6),r.clone());f.material.transparent=!0,f.material.opacity=.5,s.add(f),C.push(f)}const H=Math.random()*100;s.userData._animFn=w=>{R.forEach((f,L)=>{f.rotation.z=Math.sin(w*1.5+L*1.5+H)*.05}),b.rotation.y=Math.sin(w*1.2+H)*.03,I.forEach((f,L)=>{f.material.emissiveIntensity=.5+Math.sin(w*6+L*2+H)*.4}),S.material.emissiveIntensity=.1+Math.sin(w*3+H)*.2,C.forEach((f,L)=>{const P=w*1.8+L/4*Math.PI*2;f.position.set(Math.sin(P)*.2,.4+Math.sin(w*2.5+L)*.12,Math.cos(P)*.2),f.material.opacity=.2+Math.sin(w*4+L)*.2,f.scale.setScalar(.4+Math.sin(w*5+L)*.4)}),T.rotation.z=w*.25}}return s.scale.set(1.15,1.15,1.15),s}hlSquare(a,t,n,o){const s=o==="black",i=s?7-a:a,r=s?7-t:t,d=this.squareMeshes.find(l=>l.userData.row===i&&l.userData.col===r);if(d)switch(n){case"selected":d.material.color.setHex(16698117),d.material.emissive=new THREE.Color(3352576);break;case"legal":{const l=new THREE.Mesh(new THREE.CylinderGeometry(.1,.1,.08,16),new THREE.MeshStandardMaterial({color:16757760,emissive:6703104,roughness:.3}));l.position.set(r-3.5,.05,i-3.5),this.scene.add(l),this.hlMeshes.push(l);break}case"capture":{const l=new THREE.Mesh(new THREE.TorusGeometry(.38,.04,8,24),new THREE.MeshStandardMaterial({color:16746496,emissive:6693376,roughness:.3}));l.rotation.x=-Math.PI/2,l.position.set(r-3.5,.06,i-3.5),this.scene.add(l),this.hlMeshes.push(l);break}case"check":d.material.color.setHex(13378082),d.material.emissive=new THREE.Color(4456448);break;case"last-from":d.material.color.setHex(15257696);break;case"last-to":d.material.color.setHex(13940800);break}}applyHighlights(){if(this.clearHL(),U&&this.hlSquare(U.row,U.col,"selected",A),se.forEach(a=>{const t=$?.board[a.row][a.col];this.hlSquare(a.row,a.col,t?"capture":"legal",A)}),$){const a=$.turn;if(ve($,a))for(let t=0;t<8;t++)for(let n=0;n<8;n++){const o=$.board[t][n];o&&o[1]==="K"&&(a==="white"&&o[0]==="w"||a==="black"&&o[0]==="b")&&this.hlSquare(t,n,"check",A)}}Be&&this.hlSquare(Be.row,Be.col,"last-from",A),Pe&&this.hlSquare(Pe.row,Pe.col,"last-to",A)}animateMove3D(a,t,n,o){const s=A==="black",i=`${a.row}-${a.col}`;let r=this.pieces[i];if(!r){o();return}const d=s?7-t.row:t.row,c=(s?7-t.col:t.col)-3.5,p=d-3.5,u=r.position.clone(),g=performance.now(),v=350+Math.sqrt((c-u.x)**2+(p-u.z)**2)*60,T=n?n[1]:"P",b=this.theme||"classic",R=r.rotation.y,M=r.rotation.x,k=r.rotation.z;r.userData._isMoving=!0;const _=[];if(b==="alien"&&(T==="R"||T==="Q"||T==="K")||b==="halloween"&&(T==="B"||T==="K")){const h=new THREE.MeshStandardMaterial({color:b==="alien"?65484:8978346,emissive:b==="alien"?65416:4521830,emissiveIntensity:.8,roughness:.2,transparent:!0,opacity:.6});for(let S=0;S<5;S++){const x=new THREE.Mesh(new THREE.SphereGeometry(.012,6,6),h.clone());this.scene.add(x),_.push(x)}}const E=()=>{const h=Math.min(1,(performance.now()-g)/v),S=h<.5?4*h*h*h:1-Math.pow(-2*h+2,3)/2,x=u.x+(c-u.x)*S,C=u.z+(p-u.z)*S,H=Math.sin(h*Math.PI),w=performance.now()*.001;let f=0;if(b==="alien")switch(T){case"R":f=H*.55,r.rotation.z=k+Math.sin(h*Math.PI*2)*.04;break;case"P":f=Math.abs(Math.sin(h*Math.PI*7))*.025,r.rotation.z=k+Math.sin(h*Math.PI*12)*.08;break;case"N":f=H*.2+Math.sin(h*Math.PI*3)*.05;break;case"B":f=H*.06,r.rotation.x=M+Math.sin(h*Math.PI)*-.2;break;case"Q":f=H*.3;break;case"K":f=H*.35;break;default:f=H*.15}else if(b==="halloween")switch(T){case"P":h<.25?f=-h*4*.25:h<.75?f=-.25:f=-(1-h)*4*.25;break;case"R":f=H*.04,r.rotation.z=k+Math.sin(w*40)*.008;break;case"N":f=Math.abs(Math.sin(h*Math.PI*4))*.18,r.rotation.x=M+Math.sin(h*Math.PI*4)*-.12;break;case"B":f=H*.55,r.rotation.z=k+Math.sin(h*Math.PI)*.1;break;case"Q":f=H*.28;break;case"K":f=.04+H*.08;break;default:f=H*.15}else if(b==="christmas")switch(T){case"P":f=H*.1,r.rotation.z=k+Math.sin(h*Math.PI*6)*.05;break;case"R":f=H*.05;break;case"N":f=Math.abs(Math.sin(h*Math.PI*5))*.12;break;case"B":f=H*.15;break;case"Q":f=H*.4;break;case"K":f=H*.08,r.rotation.z=k+Math.sin(h*Math.PI*8)*.06;break;default:f=H*.12}else f=H*.12;r.position.set(x,f,C),r.rotation.y=R,_.length>0&&_.forEach((L,P)=>{const Z=(P+1)*.06,fe=Math.max(0,h-Z),ge=u.x+(c-u.x)*fe,ea=u.z+(p-u.z)*fe;L.position.set(ge,f*.5+Math.sin(w*8+P)*.02,ea),L.material.opacity=Math.max(0,.5-h*.6),L.scale.setScalar(.3+(1-h)*.7)}),h<1?requestAnimationFrame(E):(r.position.set(c,0,p),r.rotation.y=R,r.rotation.x=M,r.rotation.z=k,r.scale.set(1.15,1.15,1.15),delete r.userData._isMoving,_.forEach(L=>this.scene.remove(L)),o())};requestAnimationFrame(E)}_applyCameraOrbit(){const a=this._orbitRadius,t=this._orbitPhi,n=this._orbitTheta;this.camera.position.set(this._orbitTarget.x+a*Math.sin(t)*Math.sin(n),this._orbitTarget.y+a*Math.cos(t),this._orbitTarget.z+a*Math.sin(t)*Math.cos(n)),this.camera.lookAt(this._orbitTarget)}_events(){const a=this.renderer.domElement;let t={x:0,y:0};const n=l=>{const c=a.getBoundingClientRect(),p=l.touches?l.touches[0]:l;return{x:(p.clientX-c.left)/c.width*2-1,y:-((p.clientY-c.top)/c.height)*2+1}},o=l=>{const c=l.touches?l.touches[0]:l;return{cx:c.clientX,cy:c.clientY}},s=l=>{const{cx:c,cy:p}=o(l);if(t={x:c,y:p},l.button===2){l.preventDefault(),this._orbiting=!0,a.style.cursor="grabbing";return}const u=n(l);if(this.mouse.set(u.x,u.y),this.raycaster.setFromCamera(this.mouse,this.camera),$&&!$.gameOver&&!Xe&&$.turn===A&&!(j&&ae)){const y=Object.values(this.pieces),m=this.raycaster.intersectObjects(y,!0);if(m.length>0){let v=m[0].object;for(;v.parent&&v.parent!==this.scene;)v=v.parent;if(v.userData.piece){const T=A==="white"?"w":"b";if(v.userData.piece[0]!==T){St(v.userData.row,v.userData.col);return}this.dragging=v,this.dragStartPos=v.position.clone(),this.dragStartSq={row:v.userData.row,col:v.userData.col},U={row:v.userData.row,col:v.userData.col},se=Ne($,v.userData.row,v.userData.col),this.applyHighlights(),l.preventDefault();return}}}const g=this.raycaster.intersectObjects(this.squareMeshes);if(g.length>0&&$&&!$.gameOver){const y=g[0].object.userData,m=A==="black";St(m?7-y.row:y.row,m?7-y.col:y.col);return}this._orbiting=!0,a.style.cursor="grabbing",l.preventDefault()},i=l=>{const{cx:c,cy:p}=o(l),u=c-t.x,g=p-t.y;if(t={x:c,y:p},this.dragging){l.preventDefault();const y=n(l);this.mouse.set(y.x,y.y),this.raycaster.setFromCamera(this.mouse,this.camera);const m=new THREE.Vector3;this.raycaster.ray.intersectPlane(this.dragPlane,m)&&(this.dragging.position.x=m.x,this.dragging.position.z=m.z,this.dragging.position.y=.4,this.dragging.scale.setScalar(1.35));return}this._orbiting&&(l.preventDefault(),this._orbitTheta-=u*.008,this._orbitPhi-=g*.008,this._orbitPhi=Math.max(.25,Math.min(1.35,this._orbitPhi)),this._applyCameraOrbit())},r=l=>{if(this._orbiting&&(this._orbiting=!1,a.style.cursor="pointer"),this.dragging){l.preventDefault();const c=this.dragging,p=Math.round(c.position.x+3.5),u=Math.round(c.position.z+3.5),g=A==="black",y=g?7-u:u,m=g?7-p:p;if(se.some(T=>T.row===y&&T.col===m)&&this.dragStartSq){const T=this.dragStartSq,b=$.board[T.row][T.col],R=A==="white"?"w":"b";b&&b[1]==="P"&&(R==="w"&&y===0||R==="b"&&y===7)?(c.position.copy(this.dragStartPos),c.position.y=0,c.scale.setScalar(1.15),Ft(T,{row:y,col:m})):(c.position.y=0,c.scale.setScalar(1.15),yt(T,{row:y,col:m}))}else c.position.copy(this.dragStartPos),c.position.y=0,c.scale.setScalar(1.15);this.dragging.userData._dragParticles&&(this.dragging.userData._dragParticles.forEac
958h(T=>this.dragging.remove(T)),delete this.dragging.userData._dragParticles),this.dragging=null,this.dragStartPos=null,this.dragStartSq=null}},d=l=>{l.preventDefault(),this._orbitRadius+=l.deltaY*.01,this._orbitRadius=Math.max(6,Math.min(22,this._orbitRadius)),this._applyCameraOrbit()};a.addEventListener("contextmenu",l=>l.preventDefault()),a.addEventListener("mousedown",s),document.addEventListener("mousemove",i),document.addEventListener("mouseup",r),a.addEventListener("touchstart",s,{passive:!1}),document.addEventListener("touchmove",i,{passive:!1}),document.addEventListener("touchend",r,{passive:!1}),a.addEventListener("wheel",d,{passive:!1})}_loop(){requestAnimationFrame(()=>this._loop());const a=Date.now()*.001;for(const t in this.pieces){const n=this.pieces[t];n===this.dragging||n.userData._isMoving||n.userData._animFn&&n.userData._animFn(a,n)}if(this.dragging){if(this.dragging.position.y=.4+Math.sin(a*4)*.02,!this.dragging.userData._dragParticles){const t=[],n=new THREE.MeshStandardMaterial({color:16763972,emissive:16746496,emissiveIntensity:.8,roughness:.2});for(let o=0;o<6;o++){const s=new THREE.Mesh(new THREE.SphereGeometry(.015,6,6),n);s.layers.disableAll(),s.layers.enable(0),this.dragging.add(s),t.push(s)}this.dragging.userData._dragParticles=t}this.dragging.userData._dragParticles.forEach((t,n)=>{const o=a*3+n/6*Math.PI*2,s=.25+Math.sin(a*5+n)*.04;t.position.set(Math.sin(o)*s,-.1+Math.sin(a*6+n*2)*.08,Math.cos(o)*s),t.scale.setScalar(.6+Math.sin(a*8+n)*.4)})}this.renderer.render(this.scene,this.camera)}_resize(){const a=this.container.clientWidth||800,t=window.innerHeight-100,n=Math.max(400,Math.min(a*.88,t));this.container.style.height=n+"px",this.camera.aspect=a/n,this.camera.updateProjectionMatrix(),this.renderer.setSize(a,n)}dispose(){this._ro?.disconnect(),this.renderer.dispose(),this.renderer.domElement.remove()}}function ja(){return`
959    <section class="chess-page section" id="chess-page">
960      <div class="container">
961        <div id="chess-root"></div>
962      </div>
963    </section>
964
965    <style>
966      /* ============================================
967         CHESS PAGE — BizWrap premium light theme
968         ============================================ */
969
970      .chess-page {
971        min-height: calc(100vh - var(--nav-height));
972        display: flex;
973        align-items: flex-start;
974        justify-content: center;
975        padding-top: calc(var(--nav-height) + var(--space-4));
976        padding-bottom: var(--space-2);
977        background: var(--bg);
978      }
979
980      /* ---- Lobby ---- */
981      .chess-lobby {
982        max-width: 480px;
983        margin: 0 auto;
984        padding: var(--space-10) var(--space-8);
985        background: var(--white);
986        border: 1px solid var(--border);
987        border-radius: var(--radius-xl);
988        box-shadow: var(--shadow-lg);
989        position: relative;
990        overflow: hidden;
991        text-align: center;
992      }
993      .chess-lobby::before {
994        content: '';
995        position: absolute;
996        inset: 0;
997        background: linear-gradient(135deg, rgba(104, 74, 197, 0.03) 0%, rgba(1, 199, 204, 0.03) 100%);
998        pointer-events: none;
999      }
1000      .chess-lobby__title {
1001        font-family: var(--font-heading);
1002        font-size: var(--text-4xl);
1003        font-weight: var(--weight-extrabold);
1004        margin-bottom: var(--space-2);
1005        color: var(--ink);
1006        position: relative;
1007      }
1008      .chess-lobby__sub {
1009        color: var(--muted);
1010        font-size: var(--text-sm);
1011        margin-bottom: var(--space-8);
1012        position: relative;
1013      }
1014
1015      .chess-lobby__input {
1016        width: 100%;
1017        padding: var(--space-3) var(--space-4);
1018        background: var(--bg-alt);
1019        border: 1px solid var(--border);
1020        border-radius: var(--radius-md);
1021        font-size: var(--text-base);
1022        font-family: var(--font-body);
1023        color: var(--ink);
1024        margin-bottom: var(--space-5);
1025        transition: all var(--transition-fast);
1026        position: relative;
1027      }
1028      .chess-lobby__input:focus {
1029        outline: none;
1030        border-color: var(--purple);
1031        background: var(--white);
1032        box-shadow: 0 0 0 4px rgba(104, 74, 197, 0.12);
1033      }
1034      .chess-lobby__input::placeholder { color: var(--muted-light); }
1035
1036      .chess-lobby__actions {
1037        display: flex;
1038        gap: var(--space-3);
1039        margin-bottom: var(--space-6);
1040        position: relative;
1041      }
1042      .chess-lobby__actions .btn { flex: 1; }
1043
1044      .chess-lobby__pin-display {
1045        padding: var(--space-5);
1046        background: var(--bg-tint);
1047        border: 1px solid rgba(104, 74, 197, 0.15);
1048        border-radius: var(--radius-lg);
1049        margin-top: var(--space-5);
1050        position: relative;
1051      }
1052      .chess-lobby__pin-label {
1053        font-family: 'Courier New', monospace;
1054        font-size: var(--text-xs);
1055        text-transform: uppercase;
1056        letter-spacing: 0.2em;
1057        color: var(--purple);
1058        margin-bottom: var(--space-2);
1059        font-weight: var(--weight-semibold);
1060      }
1061      .chess-lobby__pin-code {
1062        font-family: 'Courier New', monospace;
1063        font-size: var(--text-5xl);
1064        font-weight: var(--weight-black);
1065        letter-spacing: 0.25em;
1066        background: var(--grad-text);
1067        -webkit-background-clip: text;
1068        background-clip: text;
1069        -webkit-text-fill-color: transparent;
1070      }
1071      .chess-lobby__pin-status {
1072        font-size: var(--text-sm);
1073        color: var(--muted);
1074        margin-top: var(--space-3);
1075        display: flex;
1076        align-items: center;
1077        justify-content: center;
1078        gap: var(--space-2);
1079      }
1080      .chess-lobby__pin-status .chess-dot {
1081        width: 8px; height: 8px;
1082        border-radius: 50%;
1083        background: var(--purple);
1084        box-shadow: 0 0 8px rgba(104, 74, 197, 0.5);
1085        animation: chess-pulse 2s ease-in-out infinite;
1086      }
1087
1088      .chess-lobby__join-section {
1089        margin-top: var(--space-5);
1090        position: relative;
1091      }
1092      .chess-lobby__join-row {
1093        display: flex;
1094        gap: var(--space-3);
1095      }
1096      .chess-lobby__join-row .chess-lobby__input {
1097        flex: 1;
1098        margin-bottom: 0;
1099        text-align: center;
1100        font-family: 'Courier New', monospace;
1101        font-size: var(--text-xl);
1102        letter-spacing: 0.2em;
1103      }
1104
1105      .chess-lobby__error {
1106        color: var(--danger);
1107        font-size: var(--text-sm);
1108        margin-top: var(--space-3);
1109        min-height: 1.4em;
1110        position: relative;
1111      }
1112
1113      /* ---- VS Computer section ---- */
1114      .chess-lobby__vs-computer {
1115        margin-bottom: var(--space-5);
1116        position: relative;
1117      }
1118      .chess-lobby__ai-btn {
1119        width: 100%;
1120        font-size: var(--text-base);
1121        padding: var(--space-3) var(--space-5);
1122        display: flex;
1123        align-items: center;
1124        justify-content: center;
1125      }
1126      .chess-lobby__difficulty {
1127        display: flex;
1128        gap: var(--space-2);
1129        margin-top: var(--space-3);
1130        justify-content: center;
1131      }
1132      .chess-diff-btn {
1133        padding: 6px 16px;
1134        border-radius: var(--radius-full);
1135        border: 1px solid var(--border-strong);
1136        background: transparent;
1137        color: var(--muted);
1138        font-size: var(--text-xs);
1139        font-family: 'Courier New', monospace;
1140        letter-spacing: 0.1em;
1141        text-transform: uppercase;
1142        cursor: pointer;
1143        transition: all var(--transition-fast);
1144      }
1145      .chess-diff-btn:hover {
1146        border-color: var(--purple);
1147        color: var(--purple);
1148        background: var(--bg-tint);
1149      }
1150      .chess-diff-btn--active {
1151        background: var(--bg-tint);
1152        border-color: var(--purple);
1153        color: var(--purple);
1154        font-weight: var(--weight-semibold);
1155        box-shadow: 0 2px 8px rgba(104, 74, 197, 0.15);
1156      }
1157
1158      .chess-lobby__divider {
1159        display: flex;
1160        align-items: center;
1161        gap: var(--space-3);
1162        margin-bottom: var(--space-4);
1163        color: var(--muted-light);
1164        font-size: var(--text-xs);
1165        text-transform: uppercase;
1166        letter-spacing: 0.15em;
1167        position: relative;
1168      }
1169      .chess-lobby__divider::before,
1170      .chess-lobby__divider::after {
1171        content: '';
1172        flex: 1;
1173        height: 1px;
1174        background: var(--border);
1175      }
1176
1177      /* Thinking status pulse */
1178      .chess-status--thinking {
1179        background: var(--bg-tint);
1180        animation: chess-thinking-pulse 1.2s ease-in-out infinite;
1181      }
1182      @keyframes chess-thinking-pulse {
1183        0%, 100% { opacity: 1; }
1184        50% { opacity: 0.6; }
1185      }
1186
1187      /* ---- Game layout ---- */
1188      .chess-game {
1189        display: flex;
1190        flex-direction: column;
1191        align-items: center;
1192        gap: 0;
1193        max-width: 900px;
1194        margin: 0 auto;
1195        width: 100%;
1196      }
1197
1198      /* ---- 3D Board Container ---- */
1199      .chess-3d-wrap {
1200        position: relative;
1201        width: 100%;
1202        max-width: 900px;
1203        border-radius: var(--radius-lg);
1204        overflow: visible;
1205        background: transparent;
1206      }
1207      .chess-3d-wrap canvas {
1208        display: block;
1209        width: 100% !important;
1210        height: auto !important;
1211      }
1212
1213      /* ---- Top Bar (replaces sidebar) ---- */
1214      .chess-topbar {
1215        width: 100%;
1216        max-width: 900px;
1217        background: var(--white);
1218        border: 1px solid var(--border);
1219        border-radius: var(--radius-lg);
1220        box-shadow: var(--shadow-md);
1221        margin-bottom: var(--space-3);
1222        overflow: hidden;
1223      }
1224      .chess-topbar__row {
1225        display: flex;
1226        align-items: center;
1227        gap: var(--space-4);
1228        padding: var(--space-2) var(--space-4);
1229        flex-wrap: wrap;
1230      }
1231      .chess-topbar__status {
1232        font-size: var(--text-sm);
1233        font-weight: var(--weight-semibold);
1234        color: var(--ink);
1235        background: var(--bg-tint);
1236        padding: var(--space-2) var(--space-5);
1237        text-align: center;
1238        border-bottom: 1px solid var(--border);
1239      }
1240      .chess-topbar__status.chess-status--check {
1241        background: rgba(220, 38, 38, 0.08);
1242        color: var(--danger);
1243      }
1244      .chess-topbar__status.chess-status--thinking {
1245        animation: chess-thinking-pulse 1.5s ease-in-out infinite;
1246      }
1247      .chess-topbar__pin {
1248        font-family: 'Courier New', monospace;
1249        font-size: var(--text-xs);
1250        color: var(--muted-light);
1251        letter-spacing: 0.12em;
1252        white-space: nowrap;
1253      }
1254      .chess-topbar__pin span {
1255        color: var(--purple);
1256        font-weight: var(--weight-semibold);
1257      }
1258
1259      /* Player chips */
1260      .chess-topbar__players {
1261        display: flex;
1262        gap: var(--space-3);
1263        align-items: center;
1264      }
1265      .chess-player {
1266        display: flex;
1267        align-items: center;
1268        gap: var(--space-2);
1269      }
1270      .chess-player__dot {
1271        width: 8px; height: 8px;
1272        border-radius: 50%;
1273        background: var(--muted-light);
1274        flex-shrink: 0;
1275      }
1276      .chess-player__dot--active {
1277        background: var(--purple);
1278        box-shadow: 0 0 6px rgba(104, 74, 197, 0.4);
1279        animation: chess-pulse 2s ease-in-out infinite;
1280      }
1281      .chess-player__name {
1282        font-weight: var(--weight-semibold);
1283        color: var(--ink);
1284        font-size: var(--text-xs);
1285        white-space: nowrap;
1286      }
1287      .chess-player__color {
1288        font-family: 'Courier New', monospace;
1289        font-size: 10px;
1290        color: var(--muted-light);
1291      }
1292      @keyframes chess-pulse {
1293        0%, 100% { opacity: 1; transform: scale(1); }
1294        50% { opacity: 0.5; transform: scale(0.75); }
1295      }
1296
1297      /* Captured pieces — compact inline */
1298      .chess-topbar__captured {
1299        display: flex;
1300        align-items: center;
1301        gap: var(--space-3);
1302        flex: 1;
1303        min-width: 0;
1304      }
1305      .chess-captured-group {
1306        display: flex;
1307        align-items: center;
1308        gap: 1px;
1309      }
1310      .chess-captured-group__label {
1311        font-family: 'Courier New', monospace;
1312        font-size: 9px;
1313        color: var(--muted-light);
1314        text-transform: uppercase;
1315        letter-spacing: 0.08em;
1316        margin-right: 4px;
1317        white-space: nowrap;
1318      }
1319      .chess-captured__piece {
1320        font-size: 0.95rem;
1321        line-height: 1;
1322        filter: drop-shadow(0 1px 1px rgba(0,0,0,0.15));
1323        opacity: 0.75;
1324      }
1325
1326      /* Move history — horizontal scroll strip */
1327      .chess-topbar__moves {
1328        display: flex;
1329        align-items: center;
1330        gap: var(--space-2);
1331        padding: var(--space-1) var(--space-4);
1332        border-top: 1px solid var(--border);
1333        overflow-x: auto;
1334        scrollbar-width: thin;
1335        -ms-overflow-style: none;
1336      }
1337      .chess-topbar__moves::-webkit-scrollbar { height: 3px; }
1338      .chess-topbar__moves::-webkit-scrollbar-thumb { background: var(--purple); border-radius: 2px; }
1339      .chess-topbar__moves-label {
1340        font-family: 'Courier New', monospace;
1341        font-size: 9px;
1342        color: var(--muted-light);
1343        text-transform: uppercase;
1344        letter-spacing: 0.1em;
1345        white-space: nowrap;
1346        flex-shrink: 0;
1347      }
1348      .chess-move-chip {
1349        font-family: 'Courier New', monospace;
1350        font-size: var(--text-xs);
1351        color: var(--ink-soft);
1352        background: var(--bg-alt);
1353        padding: 2px 6px;
1354        border-radius: 4px;
1355        white-space: nowrap;
1356        flex-shrink: 0;
1357      }
1358      .chess-move-chip:last-child {
1359        background: var(--bg-tint);
1360        color: var(--purple);
1361        font-weight: var(--weight-semibold);
1362      }
1363      .chess-move-chip__num {
1364        color: var(--muted-light);
1365        margin-right: 2px;
1366      }
1367
1368      /* Actions inline */
1369      .chess-topbar__actions {
1370        display: flex;
1371        gap: var(--space-2);
1372        flex-shrink: 0;
1373      }
1374      .chess-topbar__actions .btn {
1375        font-size: 11px;
1376        padding: var(--space-1) var(--space-3);
1377      }
1378      .chess-theme-select {
1379        padding: 4px 10px;
1380        border-radius: var(--radius-md);
1381        border: 1px solid var(--border);
1382        background: var(--bg-tint);
1383        font-size: 11px;
1384        font-family: inherit;
1385        cursor: pointer;
1386        color: var(--ink);
1387        outline: none;
1388        transition: border-color 0.2s;
1389      }
1390      .chess-theme-select:focus {
1391        border-color: var(--purple);
1392      }
1393
1394      /* ---- Responsive ---- */
1395      @media (max-width: 640px) {
1396        .chess-topbar__row {
1397          gap: var(--space-2);
1398          padding: var(--space-2) var(--space-3);
1399        }
1400        .chess-topbar__captured { display: none; }
1401      }
1402      @media (max-width: 500px) {
1403        .chess-lobby {
1404          padding: var(--space-6) var(--space-5);
1405        }
1406      }
1407      /* ---- Promotion Modal ---- */
1408      .chess-promo-overlay {
1409        position: fixed;
1410        inset: 0;
1411        background: rgba(10, 10, 11, 0.45);
1412        backdrop-filter: blur(6px);
1413        z-index: 2000;
1414        display: flex;
1415        align-items: center;
1416        justify-content: center;
1417      }
1418      .chess-promo-modal {
1419        background: var(--white);
1420        border: 1px solid var(--border);
1421        border-radius: var(--radius-lg);
1422        padding: var(--space-6);
1423        text-align: center;
1424        box-shadow: var(--shadow-xl);
1425      }
1426      .chess-promo-modal h3 {
1427        font-size: var(--text-lg);
1428        font-family: var(--font-heading);
1429        font-weight: var(--weight-bold);
1430        color: var(--ink);
1431        margin-bottom: var(--space-4);
1432      }
1433      .chess-promo-options {
1434        display: flex;
1435        gap: var(--space-3);
1436        justify-content: center;
1437      }
1438      .chess-promo-btn {
1439        width: 64px; height: 64px;
1440        font-size: 2.5rem;
1441        display: flex;
1442        align-items: center;
1443        justify-content: center;
1444        background: var(--bg-alt);
1445        border: 1px solid var(--border);
1446        border-radius: var(--radius-md);
1447        cursor: pointer;
1448        transition: all var(--transition-fast);
1449        line-height: 1;
1450      }
1451      .chess-promo-btn:hover {
1452        border-color: var(--purple);
1453        box-shadow: var(--shadow-purple);
1454        transform: translateY(-2px);
1455        background: var(--bg-tint);
1456      }
1457
1458      /* ---- Game Over ---- */
1459      .chess-gameover {
1460        max-width: 520px;
1461        margin: 0 auto;
1462        padding: var(--space-10) var(--space-8);
1463        background: var(--white);
1464        border: 1px solid var(--border);
1465        border-radius: var(--radius-xl);
1466        box-shadow: var(--shadow-lg);
1467        text-align: center;
1468        position: relative;
1469        overflow: hidden;
1470      }
1471      .chess-gameover::before {
1472        content: '';
1473        position: absolute;
1474        inset: 0;
1475        background: linear-gradient(135deg, rgba(104, 74, 197, 0.03) 0%, rgba(1, 199, 204, 0.03) 100%);
1476        pointer-events: none;
1477      }
1478      .chess-gameover__result {
1479        font-family: 'Courier New', monospace;
1480        font-size: var(--text-xs);
1481        text-transform: uppercase;
1482        letter-spacing: 0.2em;
1483        color: var(--purple);
1484        margin-bottom: var(--space-2);
1485        font-weight: var(--weight-semibold);
1486        position: relative;
1487      }
1488      .chess-gameover__title {
1489        font-family: var(--font-heading);
1490        font-size: var(--text-4xl);
1491        font-weight: var(--weight-extrabold);
1492        color: var(--ink);
1493        margin-bottom: var(--space-6);
1494        position: relative;
1495      }
1496      .chess-gameover__leaderboard {
1497        margin-top: var(--space-6);
1498        text-align: left;
1499        position: relative;
1500      }
1501      .chess-gameover__lb-title {
1502        font-family: 'Courier New', monospace;
1503        font-size: var(--text-xs);
1504        text-transform: uppercase;
1505        letter-spacing: 0.15em;
1506        color: var(--purple);
1507        margin-bottom: var(--space-3);
1508        display: flex;
1509        align-items: center;
1510        gap: var(--space-2);
1511        font-weight: var(--weight-semibold);
1512      }
1513      .chess-gameover__lb-title::before {
1514        content: '';
1515        width: 16px; height: 2px;
1516        background: var(--purple);
1517        border-radius: 1px;
1518      }
1519      .chess-gameover__lb-row {
1520        display: grid;
1521        grid-template-columns: 30px 1fr auto;
1522        gap: var(--space-2);
1523        padding: var(--space-2) var(--space-3);
1524        border-bottom: 1px solid var(--border);
1525        font-size: var(--text-sm);
1526        align-items: center;
1527      }
1528      .chess-gameover__lb-row:last-child { border-bottom: none; }
1529      .chess-gameover__lb-rank {
1530        font-family: 'Courier New', monospace;
1531        font-size: var(--text-xs);
1532        color: var(--muted-light);
1533      }
1534      .chess-gameover__lb-name { color: var(--ink); }
1535      .chess-gameover__lb-wins {
1536        font-family: 'Courier New', monospace;
1537        font-size: var(--text-xs);
1538        color: var(--purple);
1539        font-weight: var(--weight-semibold);
1540      }
1541      .chess-gameover__actions {
1542        margin-top: var(--space-6);
1543        display: flex;
1544        gap: var(--space-3);
1545        justify-content: center;
1546        position: relative;
1547      }
1548    </style>
1549  `}function Wa(){return`
1550    <div class="chess-lobby">
1551      <h2 class="chess-lobby__title"><span class="grad-text">Chess Arena</span></h2>
1552      <p class="chess-lobby__sub">Challenge a friend or test your skill against BizWrap AI</p>
1553
1554      <input
1555        type="text"
1556        class="chess-lobby__input"
1557        id="chess-name-input"
1558        placeholder="Enter your name"
1559        maxlength="20"
1560        autocomplete="off"
1561      />
1562
1563      <!-- Single Player -->
1564      <div class="chess-lobby__vs-computer">
1565        <button class="btn btn--primary chess-lobby__ai-btn" id="chess-vs-computer-btn">
1566          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="vertical-align:-3px;margin-right:6px"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/><circle cx="8" cy="10" r="1"/><circle cx="16" cy="10" r="1"/><path d="M9 14c.6.9 1.6 1 3 1s2.4-.1 3-1"/></svg>
1567          Play vs Computer
1568        </button>
1569        <div class="chess-lobby__difficulty" id="chess-difficulty">
1570          <button class="chess-diff-btn ${ue==="easy"?"chess-diff-btn--active":""}" data-diff="easy">Easy</button>
1571          <button class="chess-diff-btn ${ue==="medium"?"chess-diff-btn--active":""}" data-diff="medium">Medium</button>
1572          <button class="chess-diff-btn ${ue==="hard"?"chess-diff-btn--active":""}" data-diff="hard">Hard</button>
1573        </div>
1574      </div>
1575
1576      <div class="chess-lobby__divider">
1577        <span>or play online</span>
1578      </div>
1579
1580      <div class="chess-lobby__actions">
1581        <button class="btn btn--ghost" id="chess-create-btn">Create Game</button>
1582        <button class="btn btn--ghost" id="chess-show-join-btn">Join Game</button>
1583      </div>
1584
1585      <div id="chess-pin-area"></div>
1586      <div id="chess-join-area"></div>
1587      <div class="chess-lobby__error" id="chess-lobby-error"></div>
1588    </div>
1589  `}function Ua(e){return`
1590    <div class="chess-lobby__pin-display">
1591      <div class="chess-lobby__pin-label">Game PIN</div>
1592      <div class="chess-lobby__pin-code">${e}</div>
1593      <div class="chess-lobby__pin-status">
1594        <span class="chess-dot"></span>
1595        Waiting for opponent…
1596      </div>
1597    </div>
1598  `}function Ya(){return`
1599    <div class="chess-lobby__join-section">
1600      <div class="chess-lobby__join-row">
1601        <input
1602          type="text"
1603          class="chess-lobby__input"
1604          id="chess-pin-input"
1605          placeholder="PIN"
1606          maxlength="4"
1607          autocomplete="off"
1608        />
1609        <button class="btn btn--primary" id="chess-join-btn">Join</button>
1610      </div>
1611    </div>
1612  `}function Va(){if(!$)return"";const e=$.turn,a=e===A,t=Tt($.captured.white),n=Tt($.captured.black);let o=a?"Your turn":`${X}'s turn`;j&&ae&&(o="BizWrap AI is thinking…");let s="chess-status";ve($,e)&&(o=a?"You are in check!":`${X} is in check!`,s+=" chess-status--check"),j&&ae&&(s+=" chess-status--thinking");let i="";for(let r=0;r<$.moveHistory.length;r+=2){const d=Math.floor(r/2)+1,l=$.moveHistory[r]||"",c=$.moveHistory[r+1]||"";i+=`<span class="chess-move-chip"><span class="chess-move-chip__num">${d}.</span>${l}</span>`,c&&(i+=`<span class="chess-move-chip"><span class="chess-move-chip__num">${d}…</span>${c}</span>`)}return`
1613    <div class="chess-game">
1614      <div class="chess-topbar">
1615        <div class="chess-topbar__status ${s}" id="chess-status">${o}</div>
1616        <div class="chess-topbar__row">
1617          <div class="chess-topbar__pin">${j?"VS <span>BIZWRAP AI</span> · "+ue.toUpperCase():"GAME <span>#"+ke+"</span>"}</div>
1618          <div class="chess-topbar__players">
1619            <div class="chess-player">
1620              <span class="chess-player__dot ${e==="white"?"chess-player__dot--active":""}"></span>
1621              <span class="chess-player__name">${A==="white"?te:X}</span>
1622              <span class="chess-player__color">W</span>
1623            </div>
1624            <div class="chess-player">
1625              <span class="chess-player__dot ${e==="black"?"chess-player__dot--active":""}"></span>
1626              <span class="chess-player__name">${A==="black"?te:X}</span>
1627              <span class="chess-player__color">B</span>
1628            </div>
1629          </div>
1630          <div class="chess-topbar__captured">
1631            <div class="chess-captured-group">
1632              <span class="chess-captured-group__label">W:</span>
1633              ${t.map(r=>`<span class="chess-captured__piece">${He[r]}</span>`).join("")}
1634            </div>
1635            <div class="chess-captured-group">
1636              <span class="chess-captured-group__label">B:</span>
1637              ${n.map(r=>`<span class="chess-captured__piece">${He[r]}</span>`).join("")}
1638            </div>
1639          </div>
1640          <div class="chess-topbar__actions">
1641            <select class="chess-theme-select" id="chess-theme-select">
1642              <option value="classic"${N?.theme==="classic"?" selected":""}>♟ Classic</option>
1643              <option value="alien"${N?.theme==="alien"?" selected":""}>👽 Alien</option>
1644              <option value="christmas"${N?.theme==="christmas"?" selected":""}>🎄 Christmas</option>
1645              <option value="halloween"${N?.theme==="halloween"?" selected":""}>🎃 Halloween</option>
1646            </select>
1647            <button class="btn btn--ghost" id="chess-resign-btn">Resign</button>
1648            <button class="btn btn--ghost" id="chess-draw-btn">Draw</button>
1649          </div>
1650        </div>
1651        ${$.moveHistory.length>0?`
1652          <div class="chess-topbar__moves" id="chess-moves">
1653            <span class="chess-topbar__moves-label">MOVES</span>
1654            ${i}
1655          </div>
1656        `:""}
1657      </div>
1658
1659      <div class="chess-3d-wrap" id="chess-3d-container"></div>
1660    </div>
1661  `}function Ja(){const e=$?.result;if(!e)return"";let a="",t="";switch(e.type){case"checkmate":a="Checkmate",e.winner===A?t='<span class="grad-text">You Win!</span>':j?t=`<span class="grad-text">You've Been Gift Wrapped!</span>`:t=`<span class="grad-text">${X} Wins</span>`;break;case"stalemate":a="Stalemate",t='<span class="grad-text">Draw</span>';break;case"resignation":a="Resignation",e.winner===A?t='<span class="grad-text">You Win!</span>':j?t=`<span class="grad-text">You've Been Gift Wrapped!</span>`:t=`<span class="grad-text">${X} Wins</span>`;break;case"draw":a=e.reason||"Draw",t='<span class="grad-text">Draw</span>';break;default:a="Game Over",t='<span class="grad-text">Game Over</span>'}let n="";return Le.length>0&&(n=Le.slice(0,10).map((o,s)=>`
1662      <div class="chess-gameover__lb-row">
1663        <span class="chess-gameover__lb-rank">#${s+1}</span>
1664        <span class="chess-gameover__lb-name">${Ka(o.name)}</span>
1665        <span class="chess-gameover__lb-wins">${o.wins} W</span>
1666      </div>
1667    `).join("")),`
1668    <div class="chess-gameover">
1669      <div class="chess-gameover__result">${a}</div>
1670      <h2 class="chess-gameover__title">${t}</h2>
1671
1672      ${e.winner===A?`
1673        <div style="margin-bottom:var(--space-4)">
1674          <input
1675            type="text"
1676            class="chess-lobby__input"
1677            id="chess-lb-name"
1678            placeholder="Name for leaderboard"
1679            value="${Qa(te)}"
1680            maxlength="20"
1681          />
1682          <button class="btn btn--primary" id="chess-lb-submit">Submit to Leaderboard</button>
1683        </div>
1684      `:""}
1685
1686      ${Le.length>0?`
1687        <div class="chess-gameover__leaderboard">
1688          <div class="chess-gameover__lb-title">Top Players</div>
1689          ${n}
1690        </div>
1691      `:""}
1692
1693      <div class="chess-gameover__actions">
1694        <button class="btn btn--primary" id="chess-play-again">Play Again</button>
1695      </div>
1696    </div>
1697  `}function Ka(e){const a=document.createElement("div");return a.textContent=e,a.innerHTML}function Qa(e){return e.replace(/"/g,"&quot;").replace(/'/g,"&#39;")}function Tt(e){return[...e].sort((a,t)=>(Mt[a[1]]||0)-(Mt[t[1]]||0))}function F(){const e=document.getElementById("chess-root");if(!e)return;const a=N?.renderer?.domElement;switch(a?.parentNode&&a.parentNode.removeChild(a),K){case"lobby":N&&(N.dispose(),N=null),e.innerHTML=Wa(),as();break;case"game":{e.innerHTML=Va();const t=document.getElementById("chess-3d-container");t&&Ze&&(N?(t.appendChild(N.renderer.domElement),N.container=t,N._resize()):N=new Fa(t),$&&(N.sync($,A),N.applyHighlights())),ns();break}case"gameover":N&&(N.dispose(),N=null),e.innerHTML=Ja(),os();break}}function gt(e,a,t,n){N?N.animateMove3D(e,a,t,n):n()}function ce(e){const a=document.getElementById("chess-lobby-error");a&&(a.textContent=e)}function Ot(){const e=window.location.protocol==="https:"?"wss:":"ws:",a=window.location.hostname||"localhost",n=`${e}//${a}:5000/chess`;Y=new WebSocket(n),Y.onopen=()=>{console.log("[Chess] WebSocket connected")},Y.onmessage=o=>{let s;try{s=JSON.parse(o.data)}catch{return}Xa(s)},Y.onclose=()=>{console.log("[Chess] WebSocket closed"),Y=null},Y.onerror=o=>{console.error("[Chess] WebSocket error",o),ce("Cannot connect to server. Make sure the backend is running on port 5000.")}}function Me(e){Y&&Y.readyState===WebSocket.OPEN&&Y.send(JSON.stringify(e))}function Xa(e){switch(e.type){case"created":ke=e.pin,A=e.color||"white";const a=document.getElementById("chess-pin-area");a&&(a.innerHTML=Ua(e.pin));break;case"joined":A=e.color||"black",X=e.opponentName||"Opponent",$=Qe(),K="game",F();break;case"opponent-joined":X=e.opponentName||"Opponent",$=Qe(),K="game",F();break;case"move":{const{from:t,to:n,promotion:o}=e;if($&&!$.gameOver){const s=$.board[t.row][t.col];gt(t,n,s,()=>{$=at($,t.row,t.col,n.row,n.col,o),U=null,se=[],Be={row:t.row,col:t.col},Pe={row:n.row,col:n.col},$.gameOver&&(K="gameover",Ee()),F();const i=document.getElementById("chess-moves");i&&(i.scrollTop=i.scrollHeight)})}break}case"opponent-resigned":$&&($.gameOver=!0,$.result={type:"resignation",winner:A},K="gameover",Ee(),F());break;case"draw-offered":confirm(`${X} offers a draw. Accept?`)?(Me({type:"draw-accept"}),$&&($.gameOver=!0,$.result={type:"draw",reason:"agreement",winner:null},K="gameover",Ee(),F())):Me({type:"draw-decline"});break;case"draw-accepted":$&&($.gameOver=!0,$.result={type:"draw",reason:"agreement",winner:null},K="gameover",Ee(),F());break;case"error":ce(e.message||"An error occurred");break}}async function Ee(){try{const e=await fetch("/api/chess/leaderboard");e.ok&&(Le=await e.json())}catch{Le=[]}}async function Za(e){try{await fetch("/api/chess/leaderboard",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:e})}),await Ee(),F()}catch{}}let Ye=null;function Ft(e,a){Ye={from:e,to:a};const t=A==="white"?"w":"b",n=[{type:"Q",symbol:He[t+"Q"]},{type:"R",symbol:He[t+"R"]},{type:"B",symbol:He[t+"B"]},{type:"N",symbol:He[t+"N"]}],o=document.createElement("div");
1697o.className="chess-promo-overlay",o.id="chess-promo-overlay",o.innerHTML=`
1698    <div class="chess-promo-modal">
1699      <h3>Promote Pawn</h3>
1700      <div class="chess-promo-options">
1701        ${n.map(s=>`
1702          <button class="chess-promo-btn" data-promo="${s.type}">${s.symbol}</button>
1703        `).join("")}
1704      </div>
1705    </div>
1706  `,document.body.appendChild(o),o.addEventListener("click",s=>{const i=s.target.closest("[data-promo]");if(i){const r=i.dataset.promo;yt(Ye.from,Ye.to,r),Ye=null,o.remove()}})}function St(e,a){if(!$||$.gameOver||$.turn!==A||j&&ae||Xe)return;const t=$.board[e][a],n=A==="white"?"w":"b";if(t&&t[0]===n){U={row:e,col:a},se=Ne($,e,a),F();return}if(U)if(se.some(s=>s.row===e&&s.col===a)){const s=$.board[U.row][U.col];s&&s[1]==="P"&&(n==="w"&&e===0||n==="b"&&e===7)?Ft(U,{row:e,col:a}):yt(U,{row:e,col:a})}else U=null,se=[],F()}function yt(e,a,t){if(!$)return;const n=$.board[e.row][e.col];Xe=!0,gt(e,a,n,()=>{$=at($,e.row,e.col,a.row,a.col,t),U=null,se=[],Be={row:e.row,col:e.col},Pe={row:a.row,col:a.col},Xe=!1,j||Me({type:"move",from:{row:e.row,col:e.col},to:{row:a.row,col:a.col},promotion:t||void 0}),$.gameOver&&(K="gameover",j||Ee()),F();const o=document.getElementById("chess-moves");o&&(o.scrollTop=o.scrollHeight),j&&!$.gameOver&&$.turn!==A&&ts()})}function es(){switch(ue){case"easy":return 1;case"hard":return 4;default:return 3}}function ts(){ae=!0,F(),setTimeout(()=>{if(!$||$.gameOver||$.turn===A){ae=!1;return}const a=Na($,es());if(a){const t=$.board[a.from.row][a.from.col];gt(a.from,a.to,t,()=>{$=at($,a.from.row,a.from.col,a.to.row,a.to.col,a.promotion),Be={row:a.from.row,col:a.from.col},Pe={row:a.to.row,col:a.to.col},ae=!1,$.gameOver&&(K="gameover"),F();const n=document.getElementById("chess-moves");n&&(n.scrollTop=n.scrollHeight)})}else ae=!1,F()},ue==="easy"?350:ue==="hard"?800:500)}function as(){const e=document.getElementById("chess-name-input"),a=document.getElementById("chess-create-btn"),t=document.getElementById("chess-show-join-btn"),n=document.getElementById("chess-vs-computer-btn"),o=document.querySelectorAll(".chess-diff-btn");o.forEach(s=>{s.addEventListener("click",()=>{ue=s.dataset.diff,o.forEach(i=>i.classList.remove("chess-diff-btn--active")),s.classList.add("chess-diff-btn--active")})}),n?.addEventListener("click",()=>{te=e?.value.trim()||"Player",ce(""),j=!0,X="BizWrap AI",A="white",ke="AI",$=Qe(),K="game",F()}),a?.addEventListener("click",()=>{if(te=e?.value.trim()||"",!te){ce("Please enter your name");return}ce(""),j=!1,Ot();const s=setInterval(()=>{Y&&Y.readyState===WebSocket.OPEN&&(clearInterval(s),Me({type:"create",playerName:te}))},100)}),t?.addEventListener("click",()=>{if(te=e?.value.trim()||"",!te){ce("Please enter your name");return}ce(""),j=!1;const s=document.getElementById("chess-join-area");s&&(s.innerHTML=Ya(),ss())})}function ss(){const e=document.getElementById("chess-join-btn"),a=document.getElementById("chess-pin-input");e?.addEventListener("click",()=>{const t=a?.value.trim()||"";if(!t||t.length<4){ce("Please enter a valid 4-digit PIN");return}ce(""),ke=t,Ot();const n=setInterval(()=>{Y&&Y.readyState===WebSocket.OPEN&&(clearInterval(n),Me({type:"join",pin:t,playerName:te}))},100)}),a?.addEventListener("keydown",t=>{t.key==="Enter"&&e?.click()})}function ns(){document.getElementById("chess-resign-btn")?.addEventListener("click",()=>{confirm("Are you sure you want to resign?")&&(j||Me({type:"resign"}),$&&($.gameOver=!0,$.result={type:"resignation",winner:A==="white"?"black":"white"},K="gameover",j||Ee(),F()))});const a=document.getElementById("chess-draw-btn");a?.addEventListener("click",()=>{if(j){a.textContent="Declined!",a.disabled=!0,a.style.opacity="0.5";return}Me({type:"draw-offer"}),a.textContent="Draw Offered",a.disabled=!0,a.style.opacity="0.5"}),document.getElementById("chess-theme-select")?.addEventListener("change",n=>{N&&N.setTheme(n.target.value)})}function os(){document.getElementById("chess-play-again")?.addEventListener("click",()=>{if(j){$=Qe(),A="white",X="BizWrap AI",ke="AI",U=null,se=[],ae=!1,K="game",F();return}
1706$=null,A=null,X="",ke="",U=null,se=[],j=!1,ae=!1,Y&&(Y.close(),Y=null),K="lobby",F()});const a=document.getElementById("chess-lb-submit");a?.addEventListener("click",()=>{const n=document.getElementById("chess-lb-name")?.value.trim()||te;n&&(Za(n),a.textContent="Submitted!",a.disabled=!0,a.style.opacity="0.5")})}function is(){K="lobby",$=null,A=null,te="",X="",ke="",U=null,se=[],Le=[],j=!1,ae=!1,ue="medium",Ze||Oa().then(()=>F()).catch(e=>console.warn("Three.js load failed:",e)),F()}function rs(){return`
1707    <section class="hero hero--page hero--compact">
1708      <div class="hero__grid hero__grid--center">
1709        <div class="hero__copy">
1710          <h1 class="hero__title"><span class="grad-text">Business</span> Discovery</h1>
1711          <p class="hero__subtitle">
1712            Please tell us about your operation so we can prepare for our discovery meeting.
1713          </p>
1714        </div>
1715      </div>
1716    </section>
1717
1718    <section class="section" style="padding-top: var(--space-4); padding-bottom: var(--space-16);">
1719      <div class="container container--narrow">
1720        <div class="form-card" style="max-width: 720px; margin: 0 auto;">
1721          
1722          <!-- PROGRESS BAR -->
1723          <div class="q-progress-container" style="margin-bottom: var(--space-8);">
1724            <div style="display: flex; justify-content: space-between; font-size: var(--text-xs); font-weight: var(--weight-bold); text-transform: uppercase; color: var(--muted); margin-bottom: var(--space-2);">
1725              <span id="q-step-indicator">Step 1 of 4: Business Profile</span>
1726              <span id="q-percent-indicator">25% Complete</span>
1727            </div>
1728            <div style="height: 6px; background: var(--border); border-radius: var(--radius-full); overflow: hidden;">
1729              <div id="q-progress-bar" style="width: 25%; height: 100%; background: var(--grad-brand); transition: width 0.3s ease;"></div>
1730            </div>
1731          </div>
1732
1733          <form id="questionnaire-form">
1734            <!-- STEP 1: General Business Profile -->
1735            <div class="q-step" id="q-step-1">
1736              <h3 style="margin-bottom: var(--space-6); font-family: var(--font-heading);">1. General Business Profile</h3>
1737              
1738              <!-- Conditional Email Input (only if clientId is missing) -->
1739              <div class="form-group" id="q-email-group" style="display: none;">
1740                <label class="form-label" for="q-email">Email Address <span style="color:var(--danger)">*</span></label>
1741                <input class="form-input" type="email" id="q-email" placeholder="[email protected]" />
1742                <span class="form-help" style="font-size:11px; color:var(--muted)">Enter the same email address you used in the contact form so we can link your responses.</span>
1743              </div>
1744
1745              <!-- COMPANY BASICS — these power a live demo of your OS, branded as YOUR company -->
1746              <div style="background: var(--surface-2, rgba(255,255,255,0.03)); border:1px solid var(--border); border-radius: var(--radius-lg, 14px); padding: var(--space-5); margin-bottom: var(--space-6);">
1747                <div style="font-size:var(--text-xs); font-weight:var(--weight-bold); text-transform:uppercase; letter-spacing:0.06em; color:var(--brand, #d9b25a); margin-bottom: var(--space-2);">Company Basics</div>
1748                <p style="font-size:var(--text-xs); color:var(--muted); margin-bottom: var(--space-4); line-height:1.5;">
1749                  We use these to build a working demo of your operating system — branded as <em>your</em> company — to walk through together on our discovery call.
1750                </p>
1751
1752                <div class="form-group">
1753                  <label class="form-label" for="q-company-name">Company name <span style="color:var(--danger)">*</span></label>
1754                  <input class="form-input" type="text" id="q-company-name" placeholder="e.g. Acme Holiday Lighting" required />
1755                </div>
1756
1757                <div class="form-group">
1758                  <label class="form-label" for="q-slogan">Company slogan <span style="color:var(--danger)">*</span></label>
1759                  <input class="form-input" type="text" id="q-slogan" placeholder="e.g. Light Up The Night" required />
1760                  <span class="form-help" style="font-size:11px; color:var(--muted)">Your tagline — shown front-and-center on your demo dashboard.</span>
1761                </div>
1762
1763                <div class="form-group">
1764                  <label class="form-label" for="q-owner-name">Your first name <span style="color:var(--danger)">*</span></label>
1765                  <input class="form-input" type="text" id="q-owner-name" placeholder="e.g. Sam" required />
1766                </div>
1767
1768                <div class="form-group">
1769                  <label class="form-label" for="q-owner-email">Best email for you (the owner) <span style="color:var(--danger)">*</span></label>
1770                  <input class="form-input" type="email" id="q-owner-email" placeholder="[email protected]" required />
1771                  <span class="form-help" id="q-owner-email-hint" style="display:none; font-size:11px; color:var(--muted)">Use the same email you used to contact us, so we can match your responses.</span>
1772                </div>
1773
1774                <div class="form-group">
1775                  <label class="form-label" for="q-website">Company website <span style="color:var(--danger)">*</span></label>
1776                  <input class="form-input" type="text" id="q-website" placeholder="e.g. acmelights.com" required />
1777                </div>
1778
1779                <div class="form-group">
1780                  <label class="form-label" for="q-city">City &amp; state you serve <span style="color:var(--danger)">*</span></label>
1781                  <input class="form-input" type="text" id="q-city" placeholder="e.g. Austin, TX" required />
1782                </div>
1783
1784                <div class="form-group">
1785                  <label class="form-label" for="q-brand-color">Brand color (optional)</label>
1786                  <input type="color" id="q-brand-color" value="#d9b25a" style="height:44px; padding:4px; width:80px; border:1px solid var(--border); border-radius: var(--radius-md, 8px); background:transparent; cursor:pointer; display:block;" />
1787                  <span class="form-help" style="font-size:11px; color:var(--muted)">Your main brand color — used to theme your demo.</span>
1788                </div>
1789
1790                <div class="form-group" style="margin-bottom:0;">
1791                  <label class="form-label" for="q-logo-file">Your logo (optional)</label>
1792                  <input class="form-input" type="file" id="q-logo-file" accept="image/*" />
1793                  <span class="form-help" style="font-size:11px; color:var(--muted)">Upload your logo, or paste a link to it below — we can always add it later.</span>
1794                  <div id="q-logo-preview" style="margin-top: var(--space-3); display:none;">
1795                    <img id="q-logo-img" alt="Logo preview" style="max-height:64px; max-width:220px; border-radius:8px; background:#fff; padding:6px;" />
1796                  </div>
1797                  <input class="form-input" type="text" id="q-logo-url" placeholder="…or paste a logo URL (https://…)" style="margin-top: var(--space-3);" />
1798                </div>
1799              </div>
1800
1801              <div class="form-group">
1802                <label class="form-label" for="q-years">How many years have you been in business? <span style="color:var(--danger)">*</span></label>
1803                <input class="form-input" type="text" id="q-years" placeholder="e.g. 5 years" required />
1804              </div>
1805
1806              <div class="form-group">
1807                <label class="form-label" for="q-niche">What is your primary industry/niche? <span style="color:var(--danger)">*</span></label>
1808                <input class="form-input" type="text" id="q-niche" placeholder="e.g. Christmas Lighting & Landscaping" required />
1809              </div>
1810
1811              <div class="form-group">
1812                <label class="form-label" for="q-revenue">
1812What is your current annual revenue range? <span style="color:var(--danger)">*</span></label>
1813                <select class="form-select" id="q-revenue" required>
1814                  <option value="" disabled selected>Select revenue range...</option>
1815                  <option value="under-250k">Under $250k</option>
1816                  <option value="250k-1m">$250k - $1M</option>
1817                  <option value="1m-3m">$1M - $3M</option>
1818                  <option value="3m-10m">$3M - $10M</option>
1819                  <option value="10m-plus">$10M+</option>
1820                </select>
1821              </div>
1822
1823              <div class="form-group">
1824                <label class="form-label" for="q-revenue-goal">What is your target revenue goal for the next 12-24 months? <span style="color:var(--danger)">*</span></label>
1825                <input class="form-input" type="text" id="q-revenue-goal" placeholder="e.g. Grow to $2M" required />
1826              </div>
1827
1828              <div class="form-group">
1829                <label class="form-label" for="q-office-staff">How many full-time office staff members do you employ? <span style="color:var(--danger)">*</span></label>
1830                <input class="form-input" type="number" id="q-office-staff" min="0" placeholder="e.g. 3" required />
1831              </div>
1832
1833              <div class="form-group">
1834                <label class="form-label" for="q-field-crews">How many field crews or technicians do you run in peak season? <span style="color:var(--danger)">*</span></label>
1835                <input class="form-input" type="number" id="q-field-crews" min="0" placeholder="e.g. 8" required />
1836              </div>
1837            </div>
1838
1839            <!-- STEP 2: Current Tech Stack & Software -->
1840            <div class="q-step" id="q-step-2" style="display: none;">
1841              <h3 style="margin-bottom: var(--space-6); font-family: var(--font-heading);">2. Current Tech Stack & Software</h3>
1842
1843              <div class="form-group">
1844                <label class="form-label" for="q-crm">What CRM or field service software do you currently use? <span style="color:var(--danger)">*</span></label>
1845                <input class="form-input" type="text" id="q-crm" placeholder="e.g. Jobber, ServiceTitan, spreadsheets" required />
1846              </div>
1847
1848              <div class="form-group">
1849                <label class="form-label" for="q-design-tool">What software do you use for sales / design / estimates? <span style="color:var(--danger)">*</span></label>
1850                <input class="form-input" type="text" id="q-design-tool" placeholder="e.g. Photoshop, Canva, none" required />
1851              </div>
1852
1853              <div class="form-group">
1854                <label class="form-label" for="q-accounting">What accounting / financial tool do you use? <span style="color:var(--danger)">*</span></label>
1855                <input class="form-input" type="text" id="q-accounting" placeholder="e.g. QuickBooks Online, Xero" required />
1856              </div>
1857
1858              <div class="form-group">
1859                <label class="form-label" for="q-tech-frustrations">What is your biggest frustration with your current software stack? <span style="color:var(--danger)">*</span></label>
1860                <textarea class="form-textarea" id="q-tech-frustrations" placeholder="e.g. Systems don't talk to each other, lots of manual entries, double booking..." required></textarea>
1861              </div>
1862            </div>
1863
1864            <!-- STEP 3: Operations & Friction Points -->
1865            <div class="q-step" id="q-step-3" style="display: none;">
1866              <h3 style="margin-bottom: var(--space-6); font-family: var(--font-heading);">3. Operations & Friction Points</h3>
1867
1868              <div class="form-group">
1869                <label class="form-label" for="q-bottleneck">What is the single biggest bottleneck in your operations today? <span style="color:var(--danger)">*</span></label>
1870                <textarea class="form-textarea" id="q-bottleneck" placeholder="Describe where things slow down..." required></textarea>
1871              </div>
1872
1873              <div class="form-group">
1874                <label class="form-label" for="q-time-waste">How much time per week does your office team waste copy/pasting data between tools? <span style="color:var(--danger)">*</span></label>
1875                <select class="form-select" id="q-time-waste" required>
1876                  <option value="" disabled selected>Select estimated time waste...</option>
1877                  <option value="0-2">0 - 2 hours / week</option>
1878                  <option value="2-5">2 - 5 hours / week</option>
1879                  <option value="5-10">5 - 10 hours / week</option>
1880                  <option value="10-20">10 - 20 hours / week</option>
1881                  <option value="20-plus">20+ hours / week</option>
1882                </select>
1883              </div>
1884
1885              <div class="form-group">
1886                <label class="form-label" for="q-crew-tracking">How do you currently track crew progress and job status in the field? <span style="color:var(--danger)">*</span></label>
1887                <input class="form-input" type="text" id="q-crew-tracking" placeholder="e.g. Calls, texts, paper sheets, Jobber app" required />
1888              </div>
1889
1890              <div class="form-group">
1891                <label class="form-label" for="q-lead-slip">What percentage of your leads do you feel slip through the cracks? <span style="color:var(--danger)">*</span></label>
1892                <select class="form-select" id="q-lead-slip" required>
1893                  <option value="" disabled selected>Select estimated percentage...</option>
1894                  <option value="under-10">Under 10%</option>
1895                  <option value="10-25">10% - 25%</option>
1896                  <option value="25-50">25% - 50%</option>
1897                  <option value="over-50">Over 50%</option>
1898                </select>
1899              </div>
1900
1901              <div class="form-group">
1902                <label class="form-label" for="q-comms">How do you handle client communication (SMS, Email, Call) & is it centralized? <span style="color:var(--danger)">*</span></label>
1903                <input class="form-input" type="text" id="q-comms" placeholder="e.g. Separate personal phones, Gmail, not centralized" required />
1904              </div>
1905            </div>
1906
1907            <!-- STEP 4: AI & Custom OS Goals -->
1908            <div class="q-step" id="q-step-4" style="display: none;">
1909              <h3 style="margin-bottom: var(--space-6); font-family: var(--font-heading);">
19094. AI & Custom OS Goals</h3>
1910
1911              <div class="form-group">
1912                <label class="form-label" for="q-os-goal">What is the main outcome you want to achieve with a custom AI Operating System? <span style="color:var(--danger)">*</span></label>
1913                <textarea class="form-textarea" id="q-os-goal" placeholder="e.g. Automate scheduling, centralize communication, visual design board..." required></textarea>
1914              </div>
1915
1916              <div class="form-group">
1917                <label class="form-label" for="q-agent-use">If you could build one "AI Agent" to take over one repetitive daily task, what would it do? <span style="color:var(--danger)">*</span></label>
1918                <textarea class="form-textarea" id="q-agent-use" placeholder="e.g. Draft proposals, route crews, answer basic client queries..." required></textarea>
1919              </div>
1920
1921              <div class="form-group">
1922                <label class="form-label" for="q-tech-comfort">How comfortable is your team with adopting new technology? (1 = Low, 10 = High) <span style="color:var(--danger)">*</span></label>
1923                <input class="form-input" type="number" id="q-tech-comfort" min="1" max="10" placeholder="e.g. 7" required />
1924              </div>
1925
1926              <div class="form-group">
1927                <label class="form-label" for="q-launch-timeline">What is your desired timeline for launching the custom operating system? <span style="color:var(--danger)">*</span></label>
1928                <select class="form-select" id="q-launch-timeline" required>
1929                  <option value="" disabled selected>Select desired timeline...</option>
1930                  <option value="immediate">As soon as possible (< 1 month)</option>
1931                  <option value="1-3-months">1 - 3 months</option>
1932                  <option value="3-6-months">3 - 6 months</option>
1933                  <option value="flexible">Flexible / planning for next season</option>
1934                </select>
1935              </div>
1936
1937              <div class="form-group">
1938                <label class="form-label" for="q-additional">Any additional details or specific workflows you'd like us to review? (Optional)</label>
1939                <textarea class="form-textarea" id="q-additional" placeholder="Any specific requirements..."></textarea>
1940              </div>
1941            </div>
1942
1943            <!-- BUTTONS -->
1944            <div style="display: flex; justify-content: space-between; margin-top: var(--space-8); padding-top: var(--space-5); border-top: 1px solid var(--border);">
1945              <button class="btn btn--secondary" type="button" id="q-prev-btn" style="visibility: hidden;">
1946                ← Back
1947              </button>
1948              <button class="btn btn--primary" type="button" id="q-next-btn" style="background: var(--grad-brand);">
1949                Next Step →
1950              </button>
1951              <button class="btn btn--primary" type="submit" id="q-submit-btn" style="display: none; background: var(--grad-brand);">
1952                Submit Profile ✓
1953              </button>
1954            </div>
1955
1956            <div id="q-status" class="form-status" style="margin-top: var(--space-4);"></div>
1957          </form>
1958
1959        </div>
1960      </div>
1961    </section>
1962  `}function ls(){const e=window.location.hash,t=new URLSearchParams(e.includes("?")?e.split("?")[1]:"").get("id")||"";if(!t){const b=document.getElementById("q-owner-email-hint");b&&(b.style.display="block")}let n="";const o=document.getElementById("q-logo-file");o&&o.addEventListener("change",()=>{const b=o.files&&o.files[0];if(!b){n="";return}const R=new FileReader;R.onload=()=>{const M=new Image;M.onload=()=>{let{width:_,height:I}=M;if(_>512||I>512){const C=Math.min(512/_,512/I);_=Math.round(_*C),I=Math.round(I*C)}const E=document.createElement("canvas");E.width=_,E.height=I,E.getContext("2d").drawImage(M,0,0,_,I);let h=E.toDataURL("image/png");h.length>45e4&&(h=E.toDataURL("image/jpeg",.82)),n=h.length>7e5?"":h;const S=document.getElementById("q-logo-preview"),x=document.getElementById("q-logo-img");x&&(x.src=n||M.src),S&&(S.style.display=n?"block":"none")},M.src=R.result},R.readAsDataURL(b)});let s=1;const i=4,r=[{el:document.getElementById("q-step-1"),label:"Step 1 of 4: Business Profile",progress:25},{el:document.getElementById("q-step-2"),label:"Step 2 of 4: Tech Stack & Software",progress:50},{el:document.getElementById("q-step-3"),label:"Step 3 of 4: Operations & Friction",progress:75},{el:document.getElementById("q-step-4"),label:"Step 4 of 4: AI & OS Goals",progress:100}],d=document.getElementById("q-next-btn"),l=document.getElementById("q-prev-btn"),c=document.getElementById("q-submit-btn"),p=document.getElementById("q-step-indicator"),u=document.getElementById("q-percent-indicator"),g=document.getElementById("q-progress-bar"),y=document.getElementById("questionnaire-form"),m=document.getElementById("q-status");function v(){r.forEach((R,M)=>{M+1===s?(R.el.style.display="block",p&&(p.textContent=R.label),u&&(u.textContent=`${R.progress}% Complete`),g&&(g.style.width=`${R.progress}%`)):R.el.style.display="none"}),s===1?l&&(l.style.visibility="hidden"):l&&(l.style.visibility="visible"),s===i?(d&&(d.style.display="none"),c&&(c.style.display="inline-block")):(d&&(d.style.display="inline-block"),c&&(c.style.display="none"));const b=document.querySelector(".form-card");b&&b.scrollIntoView({behavior:"smooth",block:"start"})}function T(){const R=r[s-1].el.querySelectorAll("input[required], select[required], textarea[required]");let M=!0;return R.forEach(k=>{k.classList.remove("form-input--error"),k.value.trim()||(M=!1,k.classList.add("form-input--error"))}),M?m&&(m.textContent="",m.className="form-status"):m&&(m.textContent="Please fill out all required fields marked in red.",m.className="form-status form-status--error"),M}d&&d.addEventListener("click",()=>{T()&&s<i&&(s++,v())}),l&&l.addEventListener("click",()=>{s>1&&(s--,v())}),y&&y.addEventListener("submit",async b=>{if(b.preventDefault(),!T())return;c&&(c.disabled=!0),c&&(c.textContent="Submitting...");const R={questionnaire:{yearsInBusiness:document.getElementById("q-years")?.value||"",industryNiche:document.getElementById("q-niche")?.value||"",revenueRange:document.getElementById("q-revenue")?.value||"",revenueGoal:document.getElementById("q-revenue-goal")?.value||"",officeStaffCount:parseInt(document.getElementById("q-office-staff")?.value||"0",10),fieldCrewCount:parseInt(document.getElementById("q-field-crews")?.value||"0",10),currentCrm:document.getElementById("q-crm")?.value||"",designTool:document.getElementById("q-design-tool")?.value||"",accountingTool:document.getElementById("q-accounting")?.value||"",techFrustrations:document.getElementById("q-tech-frustrations")?.value||"",biggestBottleneck:document.getElementById("q-bottleneck")?.value||"",timeWasteCopyPaste:document.getElementById("q-time-waste")?.value||"",crewTrackingMode:document.getElementById("q-crew-tracking")?.value||"",leadSlipPercentage:document.getElementById("q-lead-slip")?.value||"",clientCommsCentralized:document.getElementById("q-comms")?.value||"",osGoalOutcome:document.getElementById("q-os-goal")?.value||"",desiredAgentTask:document.getElementById("q-agent-use")?.value||"",techAdoptionComfort:parseInt(document.getElementById("q-tech-comfort")?.value||"1",10),launchTimelineGoal:document.getElementById("q-launch-timeline")?.value||"",additionalWorkflowNotes:document.getElementById("q-additional")?.value||"",companyProfile:{companyName:document.getElementById("q-company-name")?.value.trim()||"",slogan:document.getElementById("q-slogan")?.value.trim()||"",ownerName:document.getElementById("q-owner-name")?.value.trim()||"",ownerEmail:document.getElementById("q-owner-email")?.value.trim()||"",website:document.getElementById("q-website")?.value.trim()||"",cityState:document.getElementById("q-city")?.value.trim()||"",brandColor:document.getElementById("q-brand-color")?.value||"",logoUrl:document.getElementById("q-logo-url")?.value.trim()||"",logoDataUrl:n||""},submittedAt:new Date().toISOString()}};t||(R.email=document.getElementById("q-owner-email")?.value.trim()||"");try{const k=await fetch(`${O}/intakes/questionnaire/${t||"unknown"}`,{method:"PATCH",headers:{"Content-Type":"application/json"},body:JSON.stringify(R)});if(k.ok){const _=document.querySelector(".form-card");_&&(_.innerHTML=`
1963              <div style="text-align: center; padding: var(--space-8) 0; animation: rl-rise 0.5s ease forwards;">
1964                <div style="font-size: 4rem; color: #10b981; margin-bottom: var(--space-4);">🚀</div>
1965                <h3 style="margin-bottom: var(--space-4); font-size: 1.8rem; font-weight: 800; font-family: var(--font-heading); background: var(--grad-text); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">
1965Profile Completed!</h3>
1966                <p style="color: var(--text-dim); margin-bottom: var(--space-8); font-size: var(--text-base); max-width: 500px; margin-left: auto; margin-right: auto; line-height: var(--leading-normal);">
1967                  Thank you! We've saved your business profile responses. Our team will review this in detail to prepare for your discovery workshop call.
1968                </p>
1969                <button class="btn btn--primary" onclick="window.location.hash='#/home'" style="background: var(--grad-brand);">
1970                  Return Home
1971                </button>
1972              </div>
1973            `)}else{const _=await k.json();throw new Error(_.error||"Server error")}}catch(M){c&&(c.disabled=!1,c.textContent="Submit Profile ✓"),m&&(m.textContent=`Error submitting form: ${M.message}. Please try again.`,m.className="form-status form-status--error")}})}const ds="modulepreload",cs=function(e){return"/"+e},Ht={},Rt=function(a,t,n){let o=Promise.resolve();if(t&&t.length>0){let i=function(l){return Promise.all(l.map(c=>Promise.resolve(c).then(p=>({status:"fulfilled",value:p}),p=>({status:"rejected",reason:p}))))};document.getElementsByTagName("link");const r=document.querySelector("meta[property=csp-nonce]"),d=r?.nonce||r?.getAttribute("nonce");o=i(t.map(l=>{if(l=cs(l),l in Ht)return;Ht[l]=!0;const c=l.endsWith(".css"),p=c?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${p}`))return;const u=document.createElement("link");if(u.rel=c?"stylesheet":ds,c||(u.as="script"),u.crossOrigin="",u.href=l,d&&u.setAttribute("nonce",d),document.head.appendChild(u),c)return new Promise((g,y)=>{u.addEventListener("load",g),u.addEventListener("error",()=>y(new Error(`Unable to preload CSS for ${l}`)))})}))}function s(i){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=i,window.dispatchEvent(r),!r.defaultPrevented)throw i}return o.then(i=>{for(const r of i||[])r.status==="rejected"&&s(r.reason);return a().catch(s)})},ps={projectId:"bizwrap-ai-prod",appId:"1:480877542373:web:2923d2fc004a86a6344d0c",storageBucket:"bizwrap-ai-prod.firebasestorage.app",apiKey:"AIzaSyATeaBa8FH_jsWv46pcRVO6bq6VIjd3ghI",authDomain:"bizwrap-ai-prod.firebaseapp.com",messagingSenderId:"480877542373",measurementId:"G-S9093ZJGTF"};let nt=null;function et(){return nt||(nt=(async()=>{const{initializeApp:e}=await Rt(async()=>{const{initializeApp:l}=await import("https://www.gstatic.com/firebasejs/11.8.1/firebase-app.js");return{initializeApp:l}},[]),{getAuth:a,GoogleAuthProvider:t,signInWithPopup:n,signInWithEmailAndPassword:o,signOut:s,onAuthStateChanged:i}=await Rt(async()=>{const{getAuth:l,GoogleAuthProvider:c,signInWithPopup:p,signInWithEmailAndPassword:u,signOut:g,onAuthStateChanged:y}=await import("https://www.gstatic.com/firebasejs/11.8.1/firebase-auth.js");return{getAuth:l,GoogleAuthProvider:c,signInWithPopup:p,signInWithEmailAndPassword:u,signOut:g,onAuthStateChanged:y}},[]),r=e(ps),d=a(r);return i(d,async l=>{if(l)try{localStorage.setItem("bizwrap_token",await l.getIdToken())}catch{}Ve&&(Ve(),Ve=null)}),setInterval(async()=>{const l=d.currentUser;if(l)try{localStorage.setItem("bizwrap_token",await l.getIdToken())}catch{}},1800*1e3),{auth:d,GoogleAuthProvider:t,signInWithPopup:n,signInWithEmailAndPassword:o,signOut:s,onAuthStateChanged:i}})()),nt}let Ve=null;const us=new Promise(e=>{Ve=e});async function jt(e=!1){try{const a=await et();await Promise.race([us,new Promise(o=>setTimeout(o,4e3))]);const t=a.auth.currentUser;if(!t)return localStorage.getItem("bizwrap_token");const n=await t.getIdToken(e);return localStorage.setItem("bizwrap_token",n),n}catch(a){return console.warn("Failed to get valid token from Firebase:",a),localStorage.getItem("bizwrap_token")}}function hs(){return`
1974    <section class="section" style="min-height:80vh;display:flex;align-items:center;justify-content:center;padding:var(--space-12) var(--space-6);">
1975      <div class="form-card" style="max-width:440px;width:100%;">
1976
1977        <!-- Logo -->
1978        <div style="text-align:center;margin-bottom:var(--space-8);">
1979          <img src="/assets/bizwrap-logo-lockup.svg" alt="BizWrap.ai" style="height:48px;margin:0 auto;" />
1980          <p style="color:var(--text-dim);font-size:var(--text-sm);margin-top:var(--space-3);">
1981            Sign in to manage your BizWrap dashboard
1982          </p>
1983        </div>
1984
1985        <!-- Google Sign-In -->
1986        <button id="admin-google-btn" type="button" style="
1987          width:100%;
1988          display:inline-flex;
1989          align-items:center;
1990          justify-content:center;
1991          gap:var(--space-3);
1992          padding:var(--space-3) var(--space-6);
1993          background:#fff;
1994          color:var(--ink);
1995          font-family:var(--font-body);
1996          font-size:var(--text-sm);
1997          font-weight:var(--weight-semibold);
1998          border:1.5px solid var(--border-strong);
1999          border-radius:var(--radius-full);
2000          cursor:pointer;
2001          transition:all var(--transition-base);
2002        ">
2003          <svg width="18" height="18" viewBox="0 0 48 48">
2004            <path fill="#EA4335" d="M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5z"/>
2005            <path fill="#4285F4" d="M46.98 24.55c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65z"/>
2006            <path fill="#FBBC05" d="M10.53 28.59a14.5 14.5 0 0 1 0-9.18l-7.98-6.19a24.0 24.0 0 0 0 0 21.56l7.98-6.19z"/>
2007            <path fill="#34A853" d="M24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48z"/>
2008            <path fill="none" d="M0 0h48v48H0z"/>
2009          </svg>
2010          Sign in with Google
2011        </button>
2012
2013        <!-- Divider -->
2014        <div style="display:flex;align-items:center;gap:var(--space-4);margin:var(--space-6) 0;">
2015          <span style="flex:1;height:1px;background:var(--border-strong);"></span>
2016          <span style="font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--muted-light);text-transform:uppercase;letter-spacing:0.1em;">or</span>
2017          <span style="flex:1;height:1px;background:var(--border-strong);"></span>
2018        </div>
2019
2020        <!-- Email / Password -->
2021        <div class="form-group">
2022          <label class="form-label" for="admin-email">Email address</label>
2023          <input class="form-input" type="email" id="admin-email" placeholder="[email protected]" autocomplete="email" />
2024        </div>
2025
2026        <div class="form-group">
2027          <label class="form-label" for="admin-pass">Password</label>
2028          <input class="form-input" type="password" id="admin-pass" placeholder="••••••••" autocomplete="current-password" />
2029        </div>
2030
2031        <button id="admin-login-btn" class="btn btn--primary" type="button" style="width:100%;">
2032          Sign in
2033        </button>
2034
2035        <!-- Status -->
2036        <div id="admin-login-status" class="form-status" style="margin-top:var(--space-4);"></div>
2037      </div>
2038    </section>
2039  `}function ms(){const e=document.getElementById("admin-google-btn"),a=document.getElementById("admin-login-btn"),t=document.getElementById("admin-login-status");e?.addEventListener("click",async()=>{if(o(),!!["localhost","127.0.0.1"].includes(window.location.hostname)){console.log("[AUTH] Local bypass triggered via Google Sign-In"),localStorage.setItem("bizwrap_token","mock-local-token"),localStorage.setItem("bizwrap_user","local-admin"),window.location.hash="#/admin";return}try{const{auth:i,GoogleAuthProvider:r,signInWithPopup:d,signOut:l}=await et(),c=new r,u=(await d(i,c)).user;if(u.email&&u.email.endsWith("@murphychristmaslighting.com")){const g=await u.getIdToken();localStorage.setItem("bizwrap_token",g),localStorage.setItem("bizwrap_user",u.displayName||u.email.split("@")[0]),window.location.hash="#/admin"}else await l(i),n("Access restricted to Murphy Christmas Lighting team members.")}catch(i){n(i.message)}}),a?.addEventListener("click",async()=>{o();const s=document.getElementById("admin-email")?.value?.trim(),i=document.getElementById("admin-pass")?.value;if(!s||!i){n("Please enter your email and password.");return}if(!!["localhost","127.0.0.1"].includes(window.location.hostname)&&(s==="admin@local"||s.endsWith("@murphychristmaslighting.com"))){console.log("[AUTH] Local bypass triggered via Email/Password"),localStorage.setItem("bizwrap_token","mock-local-token"),localStorage.setItem("bizwrap_user",s.split("@")[0]),window.location.hash="#/admin";return}try{const{auth:d,signInWithEmailAndPassword:l}=await et(),c=await l(d,s,i),p=await c.user.getIdToken();localStorage.setItem("bizwrap_token",p),localStorage.setItem("bizwrap_user",c.user.displayName||s.split("@")[0]),window.location.hash="#/admin"}catch(d){n(d.message)}}),document.getElementById("admin-pass")?.addEventListener("keydown",s=>{s.key==="Enter"&&a?.click()});function n(s){t&&(t.textContent=s,t.className="form-status form-status--error")}function o(){t&&(t.textContent="",t.className="form-status")}}var ot=null,re=!1;const it=[{key:"new",label:"New Lead",icon:"📥"},{key:"called",label:"Contacted",icon:"📞"},{key:"interested",label:"Interested",icon:"💡"},{key:"discovery",label:"Discovery",icon:"📅"},{key:"onboarding",label:"Onboarding",icon:"🚀"},{key:"converted",label:"Converted",icon:"⭐"}];function vs(e){const a=[];if(a.push("completed"),e.called)a.push("completed");else return e.contactAttempted||e.contactFailed,a.push("in-progress"),he(a),a;if(e.interested==="yes")a.push("completed");else return e.interested==="no"?(a.push("declined"),he(a),a):(a.push("in-progress"),he(a),a);if(e.discoveryCall==="completed")a.push("completed");else return e.discoveryCall==="scheduled",a.push("in-progress"),he(a),a;if(e.onboardingStatus==="completed")a.push("completed");else return e.onboardingStatus==="scheduled"||e.onboarding,a.push("in-progress"),he(a),a;return e.status==="converted"?a.push("completed"):a.push("in-progress"),a}function he(e){for(;e.length<6;)e.push("locked")}function fs(e){for(var a="",t=1;t<=5;t++){var n=t<=e?"star-rating__star star-rating__star--filled":"star-rating__star";a+='<span class="'+n+'" data-star="'+t+'">★</span>'}return a}function bt(e){return e.status==="converted"?{label:"Converted",cls:"stage-6"}:(e.onboardingStatus==="completed"||e.onboardingStatus==="scheduled"||e.onboarding)&&e.discoveryCall==="completed"?{label:"Onboarding",cls:"stage-5"}:e.discoveryCall==="completed"&&e.interested==="yes"?{label:"Discovery",cls:"stage-4"}:e.discoveryCall==="scheduled"&&e.interested==="yes"?{label:"Discovery",cls:"stage-4"}:e.interested==="yes"&&e.called?{label:"Interested",cls:"stage-3"}:e.interested==="no"?{label:"Not Interested",cls:"stage-dead"}:e.contactFailed?{label:"Can't Reach",cls:"stage-dead"}:e.called?{label:"Contacted",cls:"stage-2"}:e.contactAttempted?{label:"Not Reached",cls:"stage-1-warn"}:{label:"New Lead",cls:"stage-1"}}var z={};function gs(){return`
2040    <section class="section" style="padding-top:calc(var(--nav-height) + var(--space-8));">
2041      <div class="container admin-dash">
2042        <div class="admin-header">
2043          <h1 class="admin-header__title">Admin Portal</h1>
2044        </div>
2045        <div class="admin-nav">
2046          <span class="admin-nav__link admin-nav__link--active" onclick="window.location.hash='#/admin'">Pipeline</span>
2047          <span class="admin-nav__link" onclick="window.location.hash='#/admin-forms'">Forms Builder</span>
2048          <span class="admin-nav__link" onclick="window.location.hash='#/admin-email'">Email Template</span>
2049        </div>
2050        <div class="admin-kpis" id="admin-kpis">
2051          <div class="admin-kpi"><div class="admin-kpi__value" id="kpi-total">—</div><div class="admin-kpi__label">Total Leads</div></div>
2052          <div class="admin-kpi"><div class="admin-kpi__value" id="kpi-called">—</div><div class="admin-kpi__label">Called</div></div>
2053          <div class="admin-kpi"><div class="admin-kpi__value" id="kpi-interested">—</div><div class="admin-kpi__label">Interested</div></div>
2054          <div class="admin-kpi"><div class="admin-kpi__value" id="kpi-discovery">—</div><div class="admin-kpi__label">Discovery Scheduled</div></div>
2055          <div class="admin-kpi"><div class="admin-kpi__value" id="kpi-onboarding">—</div><div class="admin-kpi__label">Onboarding</div></div>
2056        </div>
2057        <!-- Schedule Section -->
2058        <div id="pipeline-schedule" class="pipeline-schedule"></div>
2059
2060        <div class="admin-table-wrap" style="border:none;background:transparent;">
2061          <div class="pipeline-section-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--space-5);">
2062            <h3 class="pipeline-section-title" id="pipeline-title">Client Pipeline <span class="pipeline-section-count" id="admin-table-count"></span></h3>
2063            <button class="pipeline-archive-toggle" id="pipeline-archive-toggle" style="background: var(--bg-alt); border: 1px solid var(--border-strong); padding: 8px 16px; border-radius: var(--radius-md); font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--muted); cursor: pointer; display: flex; align-items: center; gap: 8px; transition: var(--transition-fast); font-family: var(--font-body);">
2064              <span>🗄️</span> Show Archived Leads
2065            </button>
2066          </div>
2067          <div id="admin-table-body">
2068            <div class="admin-empty"><div class="admin-empty__icon">📋</div><div class="admin-empty__text">Loading pipeline…</div></div>
2069          </div>
2070        </div>
2071      </div>
2072    </section>
2073  `}function ys(){re=!1,(async()=>{const e=await jt();if(!e){window.location.hash="#/admin-login";return}xe(e)})()}async function xe(e){try{let y=function(){document.getElementById("kanban-modal").style.display="none",document.body.style.overflow="",xe(e)};var r=y;fetch(O+"/calendar/sync-from-google",{method:"POST",headers:{Authorization:"Bearer "+e}}).catch(m=>console.warn("Background calendar sync failed:",m));const d=await fetch(O+"/clients",{headers:{Authorization:"Bearer "+e}});if(!d.ok){if(d.status===401||d.status===403){const m=await jt(!0);if(m&&m!==e)return xe(m);localStorage.removeItem("bizwrap_token"),localStorage.removeItem("bizwrap_user"),window.location.hash="#/admin-login";return}throw new Error(`Server returned status ${d.status}`)}const c=(await d.json()).filter(function(m){return re?m.status==="archived":m.status!=="archived"});document.getElementById("kpi-total").textContent=c.length,document.getElementById("kpi-called").textContent=c.filter(function(m){return m.called}).length,document.getElementById("kpi-interested").textContent=c.filter(function(m){return m.interested==="yes"}).length,document.getElementById("kpi-discovery").textContent=c.filter(function(m){return m.discoveryCall==="scheduled"||m.discoveryCall==="completed"}).length,document.getElementById("kpi-onboarding").textContent=c.filter(function(m){return m.onboarding}).length,document.getElementById("admin-table-count").innerHTML='<span class="pipeline-count-bracket">[</span><span class="pipeline-count-number">'+c.length+'</span><span class="pipeline-count-bracket">]</span>';const p=document.getElementById("pipeline-title"),u=document.getElementById("pipeline-archive-toggle");re?(p&&(p.innerHTML='Archived Leads <span class="pipeline-section-count" id="admin-table-count"></span>'),u&&(u.innerHTML="<span>📋</span> Show Active Pipeline")):(p&&(p.innerHTML='Client Pipeline <span class="pipeline-section-count" id="admin-table-count"></span>'),u&&(u.innerHTML="<span>🗄️</span> Show Archived Leads")),u&&(u.onclick=function(){re=!re,xe(e)});const g=document.getElementById("pipeline-schedule");if(re?g&&(g.style.display="none"):g&&(g.style.display="block",c.length>0?(ks(c),Ms(c)):g.innerHTML=""),c.length===0){var a=re?"No archived leads. You can archive leads from their details card.":"No leads yet. Submissions from your contact form will appear here.";document.getElementById("admin-table-body").innerHTML='<div class="admin-empty"><div class="admin-empty__icon">📭</div><div class="admin-empty__text">'+a+"</div></div>";return}var t={newLead:{label:"New Lead",color:"#7c3aed",bg:"var(--bg-alt)",icon:"📥",clients:[]},contacted:{label:"Contacted",color:"#06b6d4",bg:"var(--bg-alt)",icon:"📞",clients:[]},interested:{label:"Interested",color:"#0d9488",bg:"var(--bg-alt)",icon:"💡",clients:[]},discovery:{label:"Discovery",color:"#eab308",bg:"var(--bg-alt)",icon:"📅",clients:[]}
2073,onboarding:{label:"Onboarding",color:"#f97316",bg:"var(--bg-alt)",icon:"🚀",clients:[]},converted:{label:"Converted",color:"#16a34a",bg:"var(--bg-alt)",icon:"⭐",clients:[]}};c.forEach(function(m){var v=bt(m);v.label==="Converted"?t.converted.clients.push(m):v.label==="Onboarding"?t.onboarding.clients.push(m):v.label==="Discovery"?t.discovery.clients.push(m):v.label==="Interested"?t.interested.clients.push(m):v.label==="Contacted"||v.label==="Not Reached"||v.label==="Not Interested"||v.label==="Can't Reach"?t.contacted.clients.push(m):t.newLead.clients.push(m)});var n=re?"kanban-board kanban-board--archived":"kanban-board",o='<div class="'+n+'">',s=["newLead","contacted","interested","discovery","onboarding","converted"];s.forEach(function(m){var v=t[m],T=m==="newLead"||m==="contacted"||m==="interested";o+='<div class="kanban-column" style="background:'+v.bg+"; border-color:"+v.color+'20;"><div class="kanban-column__header" style="border-top: 3px solid '+v.color+';"><span class="kanban-column__icon">'+v.icon+'</span><span class="kanban-column__title">'+v.label+'</span><span class="kanban-column__count" style="background:'+v.color+';">'+v.clients.length+'</span></div><div class="kanban-column__body">',v.clients.length===0?o+='<div class="kanban-empty">—</div>':v.clients.forEach(function(b){var R=b.businessName||b.name||"Unknown",M="";if(T&&b.createdAt){var k=Math.floor((Date.now()-new Date(b.createdAt).getTime())/6e4);k<60?M=k+"m ago":k<1440?M=Math.floor(k/60)+"h ago":M=Math.floor(k/1440)+"d ago"}for(var _="",I=b.interestRating||0,E=1;E<=5;E++)_+='<span class="kanban-star'+(E<=I?" kanban-star--filled":"")+'">★</span>';var h=re?"kanban-card kanban-card--archived":"kanban-card",S=b.source==="workshop-waitlist"||/workshop/i.test(b.service||""),x=S?'<span style="display:inline-block;font-size:9px;font-weight:800;letter-spacing:0.04em;text-transform:uppercase;color:#fff;background:linear-gradient(90deg,#7c3aed,#01c7cc);padding:2px 7px;border-radius:999px;margin-left:6px;vertical-align:middle;">🎟️ Training</span>':"";o+='<div class="'+h+'" data-client-id="'+b.id+'"><div class="kanban-card__row"><div class="kanban-card__info"><span class="kanban-card__name">'+R+x+'</span><div class="kanban-card__stars">'+_+"</div></div>"+(M?'<span class="kanban-card__age">'+M+"</span>":"")+'<span class="kanban-card__arrow">▸</span></div></div>'}),o+="</div></div>"}),o+="</div>",o+='<div class="kanban-modal" id="kanban-modal" style="display:none;"><div class="kanban-modal__backdrop"></div><div class="kanban-modal__content"><button class="kanban-modal__close">&times;</button><div class="kanban-modal__body" id="kanban-modal-body"></div></div></div>',document.getElementById("admin-table-body").innerHTML=o;var i={};c.forEach(function(m){i[m.id]=m}),document.querySelectorAll(".kanban-card__row").forEach(function(m){m.addEventListener("click",function(){var v=m.closest(".kanban-card"),T=v.dataset.clientId,b=i[T];if(b){var R=document.getElementById("kanban-modal"),M=document.getElementById("kanban-modal-body");M.innerHTML=Ut(b);var k=M.querySelector(".pipeline-card");k&&k.classList.remove("pipeline-card--collapsed"),R.style.display="flex",document.body.style.overflow="hidden",ut(e)}})}),document.querySelector(".kanban-modal__backdrop").addEventListener("click",y),document.querySelector(".kanban-modal__close").addEventListener("click",y),document.addEventListener("keydown",function(m){m.key==="Escape"&&y()}),ut(e)}catch{document.getElementById("admin-table-body").innerHTML='<div class="admin-empty"><div class="admin-empty__icon">⚠️</div><div class="admin-empty__text">Failed to load pipeline. Is the server running?</div></div>'}}function be(e){var a=[];if(e.createdAt&&a.push({icon:"📥",label:"Lead submitted",by:e.source||"form",at:e.createdAt}),e.calledAt&&a.push({icon:"📞",label:e.called?"Reached":e.contactFailed?"Can't reach":"Not reached",by:e.calledBy,at:e.calledAt}),e.contactLog&&e.contactLog.length&&e.contactLog.forEach(function(n,o){a.push({icon:"📝",label:n.note,by:n.by,at:n.at,field:"contactLog",entry:n})}
2073),e.interestedAt&&a.push({icon:"💡",label:"Interest: "+(e.interested||"?")+(e.interestRating?" ("+e.interestRating+"★)":""),by:e.interestedBy,at:e.interestedAt}),e.discoveryAt&&a.push({icon:"📅",label:"Discovery: "+(e.discoveryCall||"—"),by:e.discoveryBy,at:e.discoveryAt}),e.discoveryLog&&e.discoveryLog.length&&e.discoveryLog.forEach(function(n){a.push({icon:"📋",label:n.note,by:n.by,at:n.at,field:"discoveryLog",entry:n})}),e.onboardingAt&&a.push({icon:"🚀",label:"Onboarding: "+(e.onboardingStatus||"started"),by:e.onboardingBy,at:e.onboardingAt}),e.onboardingLog&&e.onboardingLog.length&&e.onboardingLog.forEach(function(n){a.push({icon:"📋",label:n.note,by:n.by,at:n.at,field:"onboardingLog",entry:n})}),e.quickNotes&&e.quickNotes.length&&e.quickNotes.forEach(function(n){a.push({icon:"📝",label:n.note,by:n.by,at:n.at,field:"quickNotes",entry:n})}),e.status==="converted"&&a.push({icon:"⭐",label:"Converted!",by:"",at:e.updatedAt||e.onboardingAt||""}),a.sort(function(n,o){return new Date(o.at||0)-new Date(n.at||0)}),a.length===0)return'<div class="timeline-empty">No activity yet</div>';var t="";return a.forEach(function(n){var o=new Date(n.at),s=isNaN(o)?"":o.toLocaleDateString("en-US",{month:"short",day:"numeric"})+", "+o.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"}),i=n.field?'<span class="timeline-event__edit" data-field="'+n.field+`" data-entry='`+JSON.stringify(n.entry).replace(/'/g,"&#39;")+"'>✏️</span>":"";t+='<div class="timeline-event"><div class="timeline-event__dot"></div><div class="timeline-event__content"><div class="timeline-event__header"><span class="timeline-event__icon">'+n.icon+'</span><span class="timeline-event__label">'+n.label+"</span>"+i+'</div><div class="timeline-event__meta">'+(n.by?"👤 "+n.by+" · ":"")+s+"</div></div></div>"}),t}const J=e=>String(e??"").replace(/[&<>"']/g,a=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[a]),me=e=>'"'+String(e??"").replace(/"/g,"")+'"',bs=e=>String(e||"").toLowerCase().replace(/[^a-z0-9]+/g,"").slice(0,24)||"tenant",Wt=e=>String(e||"").trim().replace(/^https?:\/\//i,"").replace(/\/.*$/,""),ws="c:\\Users\\murph\\OneDrive\\Desktop\\Thrive\\Crystal";function Es(e,a){const t=["node scripts/make-tenant.mjs","--name "+me(e.companyName)];e.slogan&&t.push("--slogan "+me(e.slogan)),e.ownerName&&t.push("--owner "+me(e.ownerName)),e.ownerEmail&&t.push("--email "+me(e.ownerEmail));const n=Wt(e.website);return n&&t.push("--domain "+me(n)),e.cityState&&t.push("--city "+me(e.cityState)),e.brandColor&&t.push("--color "+me(e.brandColor)),a&&t.push("--logo "+me(a)),t.push("--launch"),t.join(" ")}typeof window<"u"&&(window.cdbCopy=function(e){try{navigator.clipboard.writeText(e.getAttribute("data-cmd")),e.textContent="Copied ✓",setTimeout(function(){e.textContent="Copy command"},1500)}catch{}},window.cdbLauncher=function(e){var a=e.getAttribute("data-cmd"),t=e.getAttribute("data-path"),n=e.getAttribute("data-slug"),o=`@echo off\r
2074title Crystal Demo - `+n+`\r
2075cd /d "`+t+`"\r
2076`+a+`\r
2077pause\r
2078`,s=new Blob([o],{type:"application/octet-stream"}),i=document.createElement("a");i.href=URL.createObjectURL(s),i.download="demo-"+n+".bat",document.body.appendChild(i),i.click(),i.remove(),setTimeout(function(){URL.revokeObjectURL(i.href)},2e3)});function _s(e){const a=e&&e.questionnaire&&e.questionnaire.companyProfile;if(!a||!a.companyName)return"";const t=location.hostname==="localhost"||location.hostname==="127.0.0.1"?"http://localhost:5000":location.origin,n=a.logoUrl||(a.logoDataUrl?t+"/api/intakes/"+e.id+"/logo":""),o=bs(a.companyName),s=Es(a,n),i=a.logoDataUrl||a.logoUrl,r="font-size:10px;font-weight:700;text-transform:uppercase;color:var(--muted-light);letter-spacing:0.05em;";var d='<div style="border:1px solid var(--brand,#7c3aed);border-radius:var(--radius-md,10px);padding:var(--space-4);margin-bottom:var(--space-4);background:var(--bg-alt);">';return d+='<div style="font-weight:800;font-size:14px;margin-bottom:var(--space-3);">🎁 Crystal Demo Builder</div>',d+='<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);font-size:13px;margin-bottom:var(--space-3);">',d+='<div><div style="'+r+'">Company</div><div style="font-weight:600;">'+J(a.companyName)+"</div></div>",d+='<div><div style="'+r+'">Owner</div><div style="font-weight:600;">'+(J(a.ownerName)||"—")+"</div></div>",d+='<div><div style="'+r+'">Owner email</div><div style="font-weight:600;">'+(J(a.ownerEmail)||"—")+"</div></div>",d+='<div><div style="'+r+'">Website</div><div style="font-weight:600;">'+(J(Wt(a.website))||"—")+"</div></div>",d+='<div><div style="'+r+'">City</div><div style="font-weight:600;">'+(J(a.cityState)||"—")+"</div></div>",d+='<div><div style="'+r+'">Brand color</div><div style="font-weight:600;"><span style="display:inline-block;width:12px;height:12px;border-radius:3px;vertical-align:middle;margin-right:5px;background:'+(J(a.brandColor)||"#7c3aed")+';"></span>'+(J(a.brandColor)||"—")+"</div></div>",d+="</div>",i&&(d+='<div style="margin-bottom:var(--space-3);"><div style="'+r+'">Logo</div><img src="'+J(i)+'" alt="logo" style="max-height:54px;
2078max-width:200px;background:#fff;border-radius:8px;padding:6px;margin-top:4px;"/></div>'),d+='<div style="'+r+'margin-bottom:6px;">Build &amp; open the demo</div>',d+='<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:8px;">',d+='<button type="button" data-cmd="'+J(s)+'" data-path="'+J(ws)+'" data-slug="'+J(o)+'" onclick="cdbLauncher(this)" style="background:var(--brand,#7c3aed);color:#fff;border:none;border-radius:8px;padding:9px 16px;font-size:13px;font-weight:700;cursor:pointer;">⬇ Download launcher (.bat)</button>',d+='<button type="button" data-cmd="'+J(s)+'" onclick="cdbCopy(this)" style="background:transparent;color:var(--ink);border:1px solid var(--border);border-radius:8px;padding:9px 14px;font-size:12px;font-weight:600;cursor:pointer;">Copy command</button>',d+="</div>",d+='<div style="font-size:12px;color:var(--muted);line-height:1.5;">Click <strong>Download launcher</strong>, then double-click the downloaded <code>demo-'+J(o)+".bat</code> — it builds the demo for "+J(a.companyName)+" (logo and all) and opens it. No typing, no terminal.</div>",d+="</div>",d}function xs(e){const a=e.questionnaire;if(!a)return`<div style="text-align: center; padding: var(--space-8) var(--space-4); color: var(--muted);"><div style="font-size: 2.5rem; margin-bottom: var(--space-3);">📋</div><h4 style="font-weight: 700; color: var(--ink); margin-bottom: 4px;">No business profile submitted yet</h4><p style="font-size: 13px; max-width: 280px; margin: 0 auto; color: var(--muted-light);">The lead hasn't completed the 20-question discovery questionnaire yet.</p></div>`;const t=[{label:"Years in Business",val:a.yearsInBusiness},{label:"Industry / Niche",val:a.industryNiche},{label:"Annual Revenue Range",val:a.revenueRange?a.revenueRange.replace("-"," - ").toUpperCase():"—"},{label:"12-24 Month Revenue Goal",val:a.revenueGoal},{label:"Full-time Office Staff",val:a.officeStaffCount},{label:"Peak Season Crews / Techs",val:a.fieldCrewCount},{label:"Current CRM / Field Software",val:a.currentCrm},{label:"Sales / Design Tools",val:a.designTool},{label:"Accounting / Financials",val:a.accountingTool},{label:"Biggest CRM/Software Frustration",val:a.techFrustrations,fullWidth:!0},{label:"Biggest Operational Bottleneck",val:a.biggestBottleneck,fullWidth:!0},{label:"Estimated Weekly Office Copy-Paste Time",val:a.timeWasteCopyPaste?a.timeWasteCopyPaste.replace("-"," - ")+" hours":"—"},{label:"Field Crew / Job Progress Tracking",val:a.crewTrackingMode},{label:"Estimated Leads Lost / Slip Percentage",val:a.leadSlipPercentage?a.leadSlipPercentage.replace("-"," - ")+"%":"—"},{label:"Client Comms Setup (Centralized?)",val:a.clientCommsCentralized},{label:"Main Outcome Wanted from Custom OS",val:a.osGoalOutcome,fullWidth:!0},{label:"Desired AI Agent Role / Tasks",val:a.desiredAgentTask,fullWidth:!0},{label:"Team Tech Adoption Comfort (1-10)",val:a.techAdoptionComfort?a.techAdoptionComfort+" / 10":"—"},{label:"Target Launch Timeline",val:a.launchTimelineGoal},{label:"Additional Workflow Notes",val:a.additionalWorkflowNotes,fullWidth:!0}];var n=_s(e);return n+='<div style="display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); font-size: 13px;">',t.forEach(function(o){const s=o.fullWidth?"grid-column: span 2;":"",i=o.val||"—";n+='<div style="background: var(--bg-alt); padding: var(--space-3) var(--space-4); border-radius: var(--radius-sm); border: 1px solid var(--border); '+s+'"><div style="font-size: 10px; font-weight: 700; text-transform: uppercase; color: var(--muted-light); letter-spacing: 0.05em; margin-bottom: var(--space-1);">'+o.label+'</div><div style="font-weight: 600; color: var(--ink); line-height: 1.4; white-space: pre-wrap;">'+i+"</div></div>"}),n+="</div>",n}function Ut(e){z[e.id]=Object.assign({},e);var a=vs(e),t=e.createdAt?new Date(e.createdAt).toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"}):"—",n=e.nextCallScheduled||"";n&&n.includes("T")&&(n=n.slice(0,16));var o="";if(e.nextCallScheduled){var s=new Date(e.nextCallScheduled);o="Call on "+s.toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric",year:"numeric"})+" at "+s.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})}for(var i="",r=0;r<it.length;r++){var C=it[r],d=a[r],l=C.icon;if(i+='<div class="pipeline-progress__step"><div class="pipeline-progress__dot pipeline-progress__dot--'+d+'">'+l+'</div><span class="pipeline-progress__label pipeline-progress__label--'+d+'">'+C.label+"</span></div>",r<it.length-1){var c=d==="completed"?"done":"pending",p=d==="in-progress"?" pipeline-progress__arrow--next":"";i+='<div class="pipeline-progress__arrow pipeline-progress__arrow--'+c+p+'">→</div>'}}var u=e.discoveryCall==="not-scheduled"||!e.discoveryCall?" active":"",g=e.discoveryCall==="scheduled"?" active":"",y=e.discoveryCall==="completed"?" active":"",m=e.interested!=="yes"?" disabled":"";e.discoveryCall==="scheduled"&&""+e.id+n+e.id+(o?'<span class="pipeline-date__preview">'+o+"</span>":"");var v="";e.discoveryCall==="completed"&&(v='<div class="pipeline-step__expand"><div class="contact-memo discovery-notes" data-id="'+e.id+'" data-log-field="discoveryLog"><div class="contact-memo__custom"><textarea class="contact-memo__text" placeholder="Add notes about the discovery call…"></textarea><button class="contact-memo__save contact-memo__save--discovery" data-id="'+e.id+'">Save Note</button></div>'+(Array.isArray(e.discoveryLog)&&e.discoveryLog.length>0?'<div class="contact-log">'+e.discoveryLog.map(function(f){var L=new Date(f.at),P=isNaN(L)?"":L.toLocaleDateString("en-US",{month:"short",day:"numeric"})+", "+L.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"});return'<div class="contact-log__entry"><span class="contact-log__meta">
2078👤 '+(f.by||"Unknown")+" · "+P+'</span><span class="contact-log__text">'+f.note+"</span></div>"}).reverse().join("")+"</div>":"")+"</div></div>");var T=e.onboardingStatus==="not-scheduled"||!e.onboardingStatus?" active":"",b=e.onboardingStatus==="scheduled"?" active":"",R=e.onboardingStatus==="completed"?" active":"",M=e.discoveryCall!=="completed"?" disabled":"",k=e.onboardingScheduled||"";if(k&&k.includes("T")&&(k=k.slice(0,16)),e.onboardingScheduled){var _=new Date(e.onboardingScheduled);""+_.toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric",year:"numeric"})+_.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"})}var I=e.interested==="unknown"?" active":"",E=e.interested==="yes"?" active":"",h=e.interested==="no"?" active":"",S=e.called?"":" disabled",x=e.interestRating||0,C=bt(e),H=C.label,w=C.cls;return e.message&&(e.message.length>120?e.message.substring(0,120)+"":e.message),'<div class="pipeline-card pipeline-card--collapsed" data-id="'+e.id+'"><div class="pipeline-card__header pipeline-card__toggle"><div class="pipeline-card__lead"><h4 class="pipeline-card__name">'+(e.name||"—")+'</h4><span class="pipeline-card__company">'+(e.company||"—")+'</span></div><div class="pipeline-card__header-right"><span class="pipeline-card__status pipeline-card__status--'+w+'">'+H+"</span>"+(x>0?'<span class="pipeline-card__stars-mini">'+"★".repeat(x)+"☆".repeat(5-x)+"</span>":"")+'<span class="pipeline-card__chevron">▾</span></div></div><div class="pipeline-card__info-bar"><div class="info-bar__item"><span class="info-bar__icon">📧</span>'+(e.email?'<a href="https://mail.google.com/mail/?view=cm&to='+encodeURIComponent(e.email)+"&su="+encodeURIComponent("BizWrap - "+(e.businessName||e.name||""))+'" target="_blank" class="pipeline-card__email-link">'+e.email+"</a>":'<span class="info-bar__empty">—</span>')+'</div><div class="info-bar__item"><span class="info-bar__icon">📱</span>'+(e.phone?'<a href="tel:'+e.phone+'" class="pipeline-card__phone-link">'+e.phone+"</a>":'<span class="info-bar__empty">—</span>')+'</div><div class="info-bar__item"><span class="info-bar__icon">🏷️</span><span>'+(e.service||"—")+'</span></div><div class="info-bar__item"><span class="info-bar__icon">📅</span><span>'+t+"</span></div>"+(e.source?'<div class="info-bar__item"><span class="info-bar__icon">📍</span><span class="admin-source">'+e.source+"</span></div>":"")+'</div><div class="pipeline-progress">'+i+'</div><div class="pipeline-card__body"><div class="pipeline-card__split"><div class="pipeline-card__left">'+(e.message?'<div class="pipeline-card__message" style="margin-bottom: var(--space-4); background: #fffbeb; border: 1px solid #fde047; padding: var(--space-4); border-radius: var(--radius-md);"><div style="display: flex; align-items: center; gap: 6px; font-weight: 700; color: #854d0e; font-size: 13px; margin-bottom: var(--space-1);"><span>💬</span> Lead Message</div><div style="font-size: 13px; color: var(--ink); line-height: 1.5; white-space: pre-wrap;">'+e.message+"</div></div>":"")+'<div class="pipeline-card__stages-glass"><div class="pipeline-step"><div class="pipeline-step__row" style="display: flex; justify-content: space-between; align-items: center;"><div class="pipeline-step__info" style="display: flex; align-items: center; gap: 8px;"><span class="pipeline-step__icon">📞</span><span class="pipeline-step__title" style="font-weight: 700; color: var(--ink);">Initial Contact</span></div><div class="segmented-control contact-result" data-id="'+e.id+'"><button class="segmented-btn segmented-btn--red'+(e.contactFailed?" active":"")+'" data-val="cant-reach">Failed</button><button class="segmented-btn segmented-btn--warn'+(e.contactAttempted&&!e.called&&!e.contactFailed?" active":"")+'" data-val="not-reached">Missed</button><button class="segmented-btn segmented-btn--green'+(e.called?" active":"")+'" data-val="reached">Reached</button></div></div><div class="pipeline-step__expand"><div class="contact-memo" data-id="'+e.id+'"><div class="contact-memo__custom"><textarea class="contact-memo__text" placeholder="Add a note about initial contact…"></textarea><button class="contact-memo__save" data-id="'+e.id+'">Save Note</button></div></div></div>'+(e.called&&Array.isArray(e.contactLog)&&e.contactLog.length>0?'<div class="contact-log contact-log--history">'+e.contactLog.map(function(f){var L=new Date(f.at),P=isNaN(L)?"":L.toLocaleDateString("en-US",{month:"short",day:"numeric"})+", "+L.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"});return'<div class="contact-log__entry"><span class="contact-log__meta">
2078👤 '+(f.by||"Unknown")+" · "+P+'</span><span class="contact-log__text">'+f.note+"</span></div>"}).reverse().join("")+"</div>":"")+'</div><div class="pipeline-step '+(e.called?"":"pipeline-step--locked")+'"><div class="pipeline-step__row" style="display: flex; justify-content: space-between; align-items: center;"><div class="pipeline-step__info" style="display: flex; align-items: center; gap: 8px;"><span class="pipeline-step__icon">💡</span><span class="pipeline-step__title" style="font-weight: 700; color: var(--ink);">Interest Level</span>'+(e.called?'<div class="star-rating" data-field="interestRating" data-id="'+e.id+'" style="margin-left: 8px;">'+fs(x)+"</div>":"")+'</div><div class="segmented-control admin-tri" data-field="interested" data-id="'+e.id+'"><button class="segmented-btn segmented-btn--gray'+I+'" data-val="unknown"'+S+'>?</button><button class="segmented-btn segmented-btn--red'+h+'" data-val="no"'+S+'>No</button><button class="segmented-btn segmented-btn--green'+E+'" data-val="yes"'+S+'>Yes</button></div></div></div><div class="pipeline-step '+(e.interested!=="yes"?"pipeline-step--locked":"")+'"><div class="pipeline-step__row" style="display: flex; justify-content: space-between; align-items: center;"><div class="pipeline-step__info" style="display: flex; align-items: center; gap: var(--space-3);"><div style="display: flex; align-items: center; gap: 8px;"><span class="pipeline-step__icon">📅</span><span class="pipeline-step__title" style="font-weight: 700; color: var(--ink);">Discovery Call</span></div><select class="admin-select admin-select--assignee" data-field="discoveryAssignee" data-id="'+e.id+'"'+(e.interested!=="yes"?" disabled":"")+'><option value=""'+(e.discoveryAssignee?"":" selected")+'>Assign to…</option><option value="Joey"'+(e.discoveryAssignee==="Joey"?" selected":"")+'>Joey</option><option value="Chelsea"'+(e.discoveryAssignee==="Chelsea"?" selected":"")+'>Chelsea</option><option value="Gracie"'+(e.discoveryAssignee==="Gracie"?" selected":"")+'>Gracie</option></select></div><div class="segmented-control admin-tri" data-field="discoveryCall" data-id="'+e.id+'"><button class="segmented-btn segmented-btn--gray'+u+'" data-val="not-scheduled"'+m+'>None</button><button class="segmented-btn segmented-btn--warn'+g+'" data-val="scheduled"'+m+'>Sched</button><button class="segmented-btn segmented-btn--green'+y+'" data-val="completed"'+m+">Done</button></div></div>"+(e.discoveryCall==="scheduled"?'<div class="pipeline-step__date-row"><input type="datetime-local" class="pipeline-date__input pipeline-date__input--inline" data-field="nextCallScheduled" data-id="'+e.id+'" value="'+n+'" /><button class="pipeline-date__save pipeline-date__save--inline'+(e.nextCallScheduled?" pipeline-date__save--saved":"")+'" data-field="nextCallScheduled" data-id="'+e.id+'">'+(e.nextCallScheduled?"✓ Saved":"Save")+'</button><button class="pipeline-date__crystal" data-id="'+e.id+'" data-title="Discovery Call" title="Sync to Crystal Calendar">'+(e.crystalEventId?"✅":"📅")+'</button></div><div class="pipeline-step__meet-row"><span style="font-size: 1.1rem;" title="Google Meet Link">🎥</span><div class="pipeline-meet__view" style="display: '+(e.discoveryMeetLink?"flex":"none")+';"><a href="'+(e.discoveryMeetLink||"#")+'" target="_blank" class="pipeline-meet__link">Join Google Meet</a><button class="pipeline-meet__edit" data-id="'+e.id+'" title="Edit Google Meet Link">✏️</button></div><div class="pipeline-meet__edit-container" style="display: '+(e.discoveryMeetLink?"none":"flex")+';"><input type="text" class="pipeline-meet__input" data-field="discoveryMeetLink" data-id="'+e.id+'" placeholder="Paste Google Meet Link..." value="'+(e.discoveryMeetLink||"")+'" /><button class="pipeline-meet__save" data-field="discoveryMeetLink" data-id="'+e.id+'">Save Link</button></div></div>':"")+v+'</div><div class="pipeline-step '+(e.discoveryCall!=="completed"?"pipeline-step--locked":"")+'"><div class="pipeline-step__row" style="display: flex; justify-content: space-between; align-items: center;"><div class="pipeline-step__info" style="display: flex; align-items: center; gap: var(--space-3);"><div style="display: flex; align-items: center; gap: 8px;"><span class="pipeline-step__icon">🚀</span><span class="pipeline-step__title" style="font-weight: 700; color: var(--ink);">Onboarding</span></div><select class="admin-select admin-select--assignee" data-field="onboardingAssignee" data-id="'+e.id+'"'+(e.discoveryCall!=="completed"?" disabled":"")+'><option value=""'+(e.onboardingAssignee?"":" selected")+'>Assign to…</option><option value="Joey"'+(e.onboardingAssignee==="Joey"?" selected":"")+'>Joey</option><option value="Chelsea"'+(e.onboardingAssignee==="Chelsea"?" selected":"")+'>Chelsea</option><option value="Gracie"'+(e.onboardingAssignee==="Gracie"?" selected":"")+'>Gracie</option></select></div><div class="segmented-control admin-tri" data-field="onboardingStatus" data-id="'+e.id+'"><button class="segmented-btn segmented-btn--gray'+T+'" data-val="not-scheduled"'+M+'>None</button><button class="segmented-btn segmented-btn--warn'+b+'" data-val="scheduled"'+M+'>Sched</button><button class="segmented-btn segmented-btn--green'+R+'" data-val="completed"'+M+">Done</button></div></div>"+(e.onboardingStatus==="scheduled"?'<div class="pipeline-step__date-row"><input type="datetime-local" class="pipeline-date__input pipeline-date__input--inline" data-field="onboardingScheduled" data-id="'+e.id+'" value="'+k+'" /><button class="pipeline-date__save pipeline-date__save--inline'+(e.onboardingScheduled?" pipeline-date__save--saved":"")+'" data-field="onboardingScheduled" data-id="'+e.id+'">'+(e.onboardingScheduled?"✓ Saved":"Save")+'</button><button class="pipeline-date__crystal" data-id="'+e.id+'" data-title="Onboarding" title="Sync to Crystal Calendar">'+(e.crystalEventId?"✅":"📅")+"</button></div>":"")+'<div class="pipeline-step__expand"><div class="contact-memo" data-id="'+e.id+'" data-log-field="onboardingLog"><div class="contact-memo__custom"><textarea class="contact-memo__text" placeholder="Add a note about onboarding…"></textarea><button class="contact-memo__save" data-id="'+e.id+'">Save Note</button></div></div></div></div><div class="pipeline-step '+(e.onboardingStatus!=="completed"?"pipeline-step--locked":"")+'"><div class="pipeline-step__row" style="display: flex; justify-content: space-between; align-items: center;"><div class="pipeline-step__info" style="display: flex; align-items: center; gap: 8px;"><span class="pipeline-step__icon">⭐</span><span class="pipeline-step__title" style="font-weight: 700; color: var(--ink);">Converted</span></div><label class="admin-toggle '+(e.status==="converted"?"admin-toggle--on":"")+'" data-field="status" data-id="'+e.id+'" data-val-on="converted" data-val-off="new"><span class="admin-toggle__track"></span></label></div>'+(e.status==="converted"?'<div class="pipeline-step__expand"><div class="build-checklist" data-id="'+e.id+'"><div class="build-checklist__item"><label class="build-checklist__label"><input type="checkbox" class="build-checklist__check" data-field="buildGrounding"'+(e.buildGrounding?" checked":"")+' /> Grounding Data</label></div><div class="build-checklist__item"><label class="build-checklist__label"><input type="checkbox" class="build-checklist__check" data-field="buildFlows"'+(e.buildFlows?" checked":"")+' /> Application Flows</label></div><div class="build-checklist__item"><label class="build-checklist__label"><input type="checkbox" class="build-checklist__check" data-field="buildAgents"'+(e.buildAgents?" checked":"")+' /> Building Agents</label></div><div class="build-checklist__item"><label class="build-checklist__label"><input type="checkbox" class="build-checklist__check" data-field="buildInterface"'+(e.buildInterface?" checked":"")+' /> Building Interface</label></div><div class="build-checklist__item"><label class="build-checklist__label"><input type="checkbox" class="build-checklist__check" data-field="buildComplete"'+(e.buildComplete?" checked":"")+" /> Initial Builds Complete</label></div></div></div>":"")+'</div></div><div class="pipeline-card__notes"><div class="pipeline-card__notes-header"><span class="pipeline-step__icon">📝</span><span class="pipeline-step__title">Quick Note</span></div><div class="pipeline-card__notes-input"><textarea class="pipeline-note__text" data-id="'+e.id+'" placeholder="Add a note about this lead…"></textarea><button class="pipeline-note__save" data-id="'+e.id+'">Save</button></div></div><div class="pipeline-card__actions">'+(e.status==="archived"?'<button class="pipeline-action-btn pipeline-action-btn--archive" data-id="'+e.id+'" data-action="restore"><span>📤</span> Restore Lead</button>':'<button class="pipeline-action-btn pipeline-action-btn--archive" data-id="'+e.id+'" data-action="archive"><span>🗄️</span> Archive Lead</button>')+'<button class="pipeline-action-btn pipeline-action-btn--delete" data-id="'+e.id+'"><span>🗑️</span> Delete Lead</button></div></div><div class="pipeline-card__right"><div class="timeline-panel"><div class="timeline-panel__tabs"><button class="timeline-tab timeline-tab--active" data-tab="timeline" data-id="'+e.id+'"><span>🕐</span> Timeline</button><button class="timeline-tab" data-tab="profile" data-id="'+e.id+'"><span>📋</span> Business Profile'+(e.questionnaire?'<span style="background: var(--success); color: #fff; font-size: 9px; padding: 2px 8px; border-radius: var(--radius-full); font-weight: var(--weight-extrabold); letter-spacing: 0.05em; display: inline-block; margin-left: 2px;">DONE</span>':"")+'</button></div><div class="timeline-scroll-container tab-content--timeline" id="tab-timeline-'+e.id+'" style="flex: 1; overflow-y: auto; padding: var(--space-4) var(--space-5);"><div class="timeline-panel__body">'+be(e)+'</div></div><div class="profile-scroll-container tab-content--profile" id="tab-profile-'+e.id+'" style="display: none; flex: 1; overflow-y: auto; padding: var(--space-5);">'+xs(e)+"</div></div></div></div></div></div>"}function oe(e,a){var t=e.id;z[t]=Object.assign({},e);var n=document.getElementById("kanban-modal");if(n&&n.style.display==="flex"){var o=document.getElementById("kanban-modal-body");if(o){o.innerHTML=Ut(e);var s=o.querySelector(".pipeline-card");s&&s.classList.remove("pipeline-card--collapsed"),ut(a)}}}function ut(e){var a=localStorage.getItem("bizwrap_user")||"Unknown";document.querySelectorAll(".pipeline-card__toggle").forEach(function(n){n.addEventListener("click",function(o){if
2078(!(o.target.closest("button")||o.target.closest("select")||o.target.closest("input"))){var s=n.closest(".pipeline-card");s&&s.classList.toggle("pipeline-card--collapsed")}})}),document.querySelectorAll(".pipeline-note__save").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id,s=n.closest(".pipeline-card__notes-input").querySelector(".pipeline-note__text");if(!(!s||!s.value.trim())){var i=s.value.trim(),r={note:i,by:a,at:new Date().toISOString()};if(await W(o,{quickNotesEntry:r},e),s.value="",z[o]){z[o].quickNotes||(z[o].quickNotes=[]),z[o].quickNotes.push(r);var d=n.closest(".pipeline-card");if(d){var l=d.querySelector(".timeline-panel__body");l&&(l.innerHTML=be(z[o]))}}n.textContent="✓ Saved!",n.classList.add("pipeline-note__save--saved"),setTimeout(function(){n.textContent="Save",n.classList.remove("pipeline-note__save--saved")},2e3)}})}),document.querySelectorAll(".timeline-panel__body").forEach(function(n){n.addEventListener("click",function(o){var s=o.target.closest(".timeline-event__edit");if(s){o.stopPropagation(),document.querySelectorAll(".timeline-edit-pop").forEach(function(u){u.remove()});var i=s.closest(".timeline-event"),r=s.dataset.field,d=JSON.parse(s.dataset.entry),l=s.closest(".pipeline-card"),c=l?l.dataset.id:null;if(c){var p=document.createElement("div");p.className="timeline-edit-pop",p.innerHTML='<button class="timeline-edit-pop__btn timeline-edit-pop__btn--edit">✏️ Edit</button><button class="timeline-edit-pop__btn timeline-edit-pop__btn--delete">🗑️ Delete</button>',i.appendChild(p),p.querySelector(".timeline-edit-pop__btn--delete").addEventListener("click",async function(){await W(c,{deleteLogEntry:{field:r,entry:d}},e),z[c]&&z[c][r]&&(z[c][r]=z[c][r].filter(function(g){return g.at!==d.at||g.note!==d.note}));var u=l.querySelector(".timeline-panel__body");u&&(u.innerHTML=be(z[c]))}),p.querySelector(".timeline-edit-pop__btn--edit").addEventListener("click",function(){p.remove();var u=i.querySelector(".timeline-event__label"),g=u.textContent;u.innerHTML='<input class="timeline-edit-input" value="'+g.replace(/"/g,"&quot;")+'" /><button class="timeline-edit-save">Save</button>';var y=u.querySelector(".timeline-edit-input");y.focus(),y.select(),u.querySelector(".timeline-edit-save").addEventListener("click",async function(){var m=y.value.trim();if(!m||m===g){u.textContent=g;return}var v={note:m,by:d.by,at:d.at};await W(c,{deleteLogEntry:{field:r,entry:d}},e);var T=r+"Entry";await W(c,{[T]:v},e),z[c]&&z[c][r]&&(z[c][r]=z[c][r].map(function(R){return R.at===d.at&&R.note===d.note?v:R}));var b=l.querySelector(".timeline-panel__body");b&&(b.innerHTML=be(z[c]))})}),setTimeout(function(){document.addEventListener("click",function u(g){p.contains(g.target)||(p.remove(),document.removeEventListener("click",u))})},0)}}})}),document.querySelectorAll(".contact-result").forEach(function(n){var o=n.dataset.id;n.querySelectorAll(".segmented-btn").forEach(function(s){s.addEventListener("click",async function(){var i=s.dataset.val,r=new Date().toISOString(),d={calledBy:a,calledAt:r};i==="reached"?(d.called=!0,d.contactAttempted=!1,d.contactFailed=!1):i==="not-reached"?(d.called=!1,d.contactAttempted=!0,d.contactFailed=!1):i==="cant-reach"&&(d.called=!1,d.contactAttempted=!1,d.contactFailed=!0),n.querySelectorAll(".segmented-btn").forEach(function(g){g.classList.remove("active")}),s.classList.add("active");var c=n.closest(".pipeline-card");if(c&&z[o]){Object.assign(z[o],d),z[o].calledBy=a,z[o].calledAt=r;var l=c.querySelector(".timeline-panel__body");l&&(l.innerHTML=be(z[o]))}var c=n.closest(".pipeline-card");if(c){var p=c.querySelector(".pipeline-card__status");p&&(p.className="pipeline-card__status",i==="reached"?(p.textContent="Contacted",p.classList.add("pipeline-card__status--stage-2")):i==="not-reached"?(p.textContent="Not Reached",p.classList.add("pipeline-card__status--stage-1-warn")):i==="cant-reach"&&(p.textContent="Can't Reach",p.classList.add("pipeline-card__status--stage-dead")))}var u=await W(o,d,e);u&&oe(u,e)})})});async function t(n,o,s){var i=new Date,r={note:s,by:a,at:i.toISOString()},d=i.toLocaleDateString("en-US",{month:"short",day:"numeric"})+", "+i.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"}),l=n.querySelector(".contact-log");
2078l||(l=document.createElement("div"),l.className="contact-log",n.appendChild(l));var c=document.createElement("div");c.className="contact-log__entry contact-log__entry--new",c.innerHTML='<span class="contact-log__meta">👤 '+a+" · "+d+'</span><span class="contact-log__text">'+s+"</span>",l.insertBefore(c,l.firstChild);var p=n.querySelector(".contact-memo__save");p&&(p.textContent="✓ Saved!",p.classList.add("contact-memo__save--saved"),setTimeout(function(){p.textContent="Save Note",p.classList.remove("contact-memo__save--saved")},2e3));var u=n.dataset.logField||"contactLog",g=u+"Entry",y=await W(o,{[g]:r},e);if(z[o]){z[o][u]||(z[o][u]=[]),z[o][u].push(r);var m=n.closest(".pipeline-card");if(m){var v=m.querySelector(".timeline-panel__body");v&&(v.innerHTML=be(z[o]))}}return y}document.querySelectorAll(".contact-memo").forEach(function(n){var o=n.dataset.id;n.querySelectorAll(".contact-memo__preset").forEach(function(s){s.addEventListener("click",async function(){var i=s.textContent;n.querySelectorAll(".contact-memo__preset").forEach(function(d){d.classList.remove("contact-memo__preset--active")}),s.classList.add("contact-memo__preset--active");var r=n.querySelector(".contact-memo__text");r&&(r.value=""),await t(n,o,i)})})}),document.querySelectorAll(".contact-memo__save").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id,s=n.closest(".contact-memo"),i=s?s.querySelector(".contact-memo__text"):null;if(!(!i||!i.value.trim())){var r=i.value.trim();i.value="",s&&s.querySelectorAll(".contact-memo__preset").forEach(function(d){d.classList.remove("contact-memo__preset--active")}),await t(s,o,r)}})}),document.querySelectorAll(".pipeline-card .admin-tri").forEach(function(n){var o=n.dataset.field,s=n.dataset.id;n.querySelectorAll(".segmented-btn").forEach(function(i){i.addEventListener("click",async function(){if(!i.disabled){var r=i.dataset.val,d=new Date().toISOString();n.querySelectorAll(".segmented-btn").forEach(function(m){m.classList.remove("active")}),i.classList.add("active");var l=n.closest(".pipeline-card"),c=n.dataset.id;if(l&&c&&z[c]){var p=l.querySelector(".timeline-panel__body");p&&(p.innerHTML=be(z[c]))}var u={[o]:r};if(o==="interested")u.interestedBy=a,u.interestedAt=d;else if(o==="discoveryCall"){if(u.discoveryBy=a,u.discoveryAt=d,r==="completed"){var l=n.closest(".pipeline-card");if(l){var g=l.querySelectorAll(".pipeline-step");g.length>0&&g[g.length-1].classList.remove("pipeline-step--locked")}}}else o==="onboardingStatus"&&(u.onboardingBy=a,u.onboardingAt=d,u.onboarding=r==="scheduled"||r==="completed");var y=await W(s,u,e);y&&oe(y,e)}})})}),document.querySelectorAll(".star-rating").forEach(function(n){n.querySelectorAll(".star-rating__star").forEach(function(o){o.addEventListener("click",async function(){var s=n.dataset.id,i=n.dataset.field,r=parseInt(o.dataset.star);n.querySelectorAll(".star-rating__star").forEach(function(l,c){c<r?l.classList.add("star-rating__star--filled"):l.classList.remove("star-rating__star--filled")});var d=await W(s,{[i]:r},e);d&&oe(d,e)})})}),document.querySelectorAll(".pipeline-card .admin-select").forEach(function(n){n.addEventListener("change",async function(){if(!n.disabled){var o=n.dataset.field,s=n.dataset.id,i=await W(s,{[o]:n.value},e);i&&oe(i,e)}})}),document.querySelectorAll('.pipeline-card .admin-toggle[data-field="status"]').forEach(function(n){n.addEventListener("click",async function(){var o=n.closest(".pipeline-step");if(!(o&&o.classList.contains("pipeline-step--locked"))){var s=n.dataset.id,i=n.classList.contains("admin-toggle--on");n.classList.toggle("admin-toggle--on");var r=i?"new":"converted",d=await W(s,{status:r},e);d&&oe(d,e)}})}),document.querySelectorAll(".build-checklist__check").forEach(function(n){n.addEventListener("change",async function(){var o=n.closest(".build-checklist"),s=o?o.dataset.id:null;if(s){var i=n.dataset.field,r=await W(s,{[i]:n.checked},e);r&&oe(r,e)}})}),document.querySelectorAll(".pipeline-date__save").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id,s=n.dataset.field,i=n.closest(".pipeline-date__row"),r=i?i.querySelector(".pipeline-date__input"):n.previousElementSibling;if(!(!r||!r.value)){n.disabled=!0,n.textContent="Saving…";var d=await W(o,{[s]:r.value},e);d&&oe(d,e)}})}),document.querySelectorAll(".pipeline-meet__save").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id,s=n.dataset.f
2078ield,i=n.closest(".pipeline-step__meet-row"),r=i?i.querySelector(".pipeline-meet__input"):null;if(r){n.disabled=!0,n.textContent="Saving…";var d=await W(o,{[s]:r.value.trim()},e);d&&oe(d,e)}})}),document.querySelectorAll(".pipeline-meet__input").forEach(function(n){n.addEventListener("keydown",async function(o){if(o.key==="Enter"){var s=n.dataset.id,i=n.dataset.field,r=n.closest(".pipeline-step__meet-row"),d=r?r.querySelector(".pipeline-meet__save"):null;d&&(d.disabled=!0);var l=await W(s,{[i]:n.value.trim()},e);l&&oe(l,e)}})}),document.querySelectorAll(".pipeline-meet__edit").forEach(function(n){n.addEventListener("click",function(){var o=n.closest(".pipeline-step__meet-row"),s=o?o.querySelector(".pipeline-meet__view"):null,i=o?o.querySelector(".pipeline-meet__edit-container"):null;if(s&&i){s.style.display="none",i.style.display="flex";var r=i.querySelector(".pipeline-meet__input");r&&(r.focus(),r.select())}})}),document.querySelectorAll(".pipeline-date__input").forEach(function(n){n.addEventListener("input",function(){var o=n.closest(".pipeline-step__date-row, .pipeline-step__date-inline, .pipeline-date__row"),s=o?o.querySelector(".pipeline-date__save"):null;s&&(s.textContent="Save",s.classList.remove("pipeline-date__save--saved"),s.disabled=!1)})}),document.querySelectorAll(".pipeline-meet__input").forEach(function(n){n.addEventListener("input",function(){var o=n.closest(".pipeline-step__meet-row"),s=o?o.querySelector(".pipeline-meet__save"):null;s&&(s.textContent="Save Link",s.classList.remove("pipeline-date__save--saved"),s.style.background="var(--purple)",s.disabled=!1)})}),document.querySelectorAll(".pipeline-date__crystal").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id,s=n.dataset.title||"Event",i=n.closest(".pipeline-step__row"),r=i?i.querySelector(".pipeline-date__input"):null;if(!r||!r.value){alert("Please set a date/time first, then sync to Crystal.");return}var d=new Date(r.value),l=d.getFullYear()+"-"+String(d.getMonth()+1).padStart(2,"0")+"-"+String(d.getDate()).padStart(2,"0"),c=d.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"});n.disabled=!0,n.textContent="⏳";try{var p=await fetch(O+"/calendar/sync",{method:"POST",headers:{Authorization:"Bearer "+e,"Content-Type":"application/json"},body:JSON.stringify({clientId:o,title:s,date:l,time:c})});if(p.ok)n.textContent="✅",n.title="Synced to Crystal!";else{var u=await p.json();n.textContent="❌",n.title="Sync failed: "+(u.error||"Unknown error"),setTimeout(function(){n.textContent="📅",n.disabled=!1},3e3)}}catch(g){n.textContent="❌",n.title="Sync error: "+g.message,setTimeout(function(){n.textContent="📅",n.disabled=!1},3e3)}})}),document.querySelectorAll(".pipeline-action-btn--archive").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id,s=n.dataset.action||"archive";if(s==="restore"){if(!confirm("Are you sure you want to restore this lead to the active pipeline?"))return;n.disabled=!0,n.textContent="Restoring…";var i=await W(o,{status:"new"},e);if(i){var r=document.querySelector(".kanban-modal__close");r?r.click():xe(e)}else n.disabled=!1,n.innerHTML="<span>📤</span> Restore Lead",alert("Failed to restore lead. Please try again.")}else{if(!confirm("Are you sure you want to archive this lead? It will be hidden from the active pipeline."))return;n.disabled=!0,n.textContent="Archiving…";var i=await W(o,{status:"archived"},e);if(i){var r=document.querySelector(".kanban-modal__close");r?r.click():xe(e)}else n.disabled=!1,n.innerHTML="<span>🗄️</span> Archive Lead",alert("Failed to archive lead. Please try again.")}})}),document.querySelectorAll(".pipeline-action-btn--delete").forEach(function(n){n.addEventListener("click",async function(){var o=n.dataset.id;if(confirm("WARNING: Are you sure you want to permanently delete this lead? This action cannot be undone.")){n.disabled=!0,n.textContent="Deleting…";try{var s=await fetch(O+"/clients/"+o,{method:"DELETE",headers:{Authorization:"Bearer "+e}});if(s.ok){var i=document.querySelector(".kanban-modal__close");i?i.click():xe(e)}else throw new Error("Delete failed")}catch{n.disabled=!1,n.innerHTML="<span>🗑️</span> Delete Lead",alert("Failed to delete lead. Please try again.")}}})}),document.querySelectorAll(".timeline-tab").forEach(function(n){n.addEventListener("click",function(){var o=n.dataset.tab,s=n.dataset.id,i=n.closest(".pipeline-card");if(i){i.querySelectorAll(".timeline-tab").forEach(function(l){l.classList.remove("timeline-tab--active")}),n.classList.add("timeline-tab--active");var r=document.getElementById("tab-timeline-"+s),d=document.getElementById("tab-profile-"+s);o==="timeline"?(r&&(r.style.display="block"),d&&(d.style.display="none")):(r&&(r.style.display="none"),d&&(d.style.display="block"))}})})}function Yt(e){if(e<=0){var a=Math.abs(e),t=Math.floor(a/36e5),n=Math.floor(a%36e5/6e4);return{text:"OVERDUE by "+t+"h "+n+"m",urgent:"overdue"}}var o=Math.floor(e/36e5),s=Math.floor(e%36e5/6e4),i=o+"h "+s+"m remaining",r="normal";
2078return o<2?r="critical":o<6?r="warning":o<12&&(r="caution"),{text:i,urgent:r}}function ks(e){var a=document.getElementById("pipeline-schedule");if(a){var t=Date.now(),n=1440*60*1e3,o=new Set,s=e.filter(function(l){return!l.called&&!l.contactFailed}).map(function(l){var c=new Date(l.createdAt).getTime(),p=c+n,u=p-t,g=Math.max(0,Math.min(100,u/n*100)),y=Yt(u);return o.add(l.id),{client:l,remaining:u,pct:g,countdown:y,created:c}}).sort(function(l,c){return l.remaining-c.remaining}),i=e.filter(function(l){return l.discoveryCall==="scheduled"&&l.nextCallScheduled&&!o.has(l.id)}).map(function(l){var c=new Date(l.nextCallScheduled);return o.add(l.id),{client:l,date:c,isPast:c.getTime()<t}}).sort(function(l,c){return l.date-c.date}),r=e.filter(function(l){return l.onboarding&&l.status!=="converted"&&!o.has(l.id)});if(s.length===0&&i.length===0&&r.length===0){a.innerHTML="";return}var d='<div class="schedule-wrap"><div class="schedule-header"><h3>📋 Upcoming Schedule</h3><span class="schedule-count">'+(s.length+i.length+r.length)+' items</span></div><div class="schedule-grid">';
2078s.forEach(function(l){var c=l.client,p=l.countdown.urgent,u=new Date(l.created).toLocaleString("en-US",{month:"short",day:"numeric",hour:"numeric",minute:"2-digit"});d+='<div class="schedule-item schedule-item--contact schedule-item--'+p+'"><div class="schedule-item__left"><span class="schedule-item__badge schedule-badge--'+p+'">📞 Needs Contact</span><h4 class="schedule-item__name">'+(c.name||"—")+'</h4><span class="schedule-item__detail">'+(c.company||"—")+" • "+(c.service||"—")+'</span><span class="schedule-item__submitted">Submitted: '+u+'</span></div><div class="schedule-item__right"><div class="schedule-timer schedule-timer--'+p+'"><span class="schedule-timer__text" data-deadline="'+(l.created+n)+'">'+l.countdown.text+'</span></div><div class="schedule-bar"><div class="schedule-bar__fill schedule-bar__fill--'+p+'" style="width:'+l.pct+'%"></div></div></div></div>'}),i.forEach(function(l){var c=l.client,p=l.date.toLocaleDateString("en-US",{weekday:"short",month:"short",day:"numeric"}),u=l.date.toLocaleTimeString("en-US",{hour:"numeric",minute:"2-digit"});l.isPast,d+='<div class="schedule-item schedule-item--discovery"><div class="schedule-item__left"><span class="schedule-item__badge schedule-badge--discovery">📅 Discovery Call</span><h4 class="schedule-item__name">'+(c.name||"—")+'</h4><span class="schedule-item__detail">'+(c.company||"—")+'</span></div><div class="schedule-item__right"><div class="schedule-datetime"><span class="schedule-datetime__date">'+p+'</span><span class="schedule-datetime__time">'+u+"</span></div></div></div>"}),r.forEach(function(l){var c=bt(l);d+='<div class="schedule-item schedule-item--onboarding"><div class="schedule-item__left"><span class="schedule-item__badge schedule-badge--onboarding">🚀 Onboarding</span><h4 class="schedule-item__name">'+(l.name||"—")+'</h4><span class="schedule-item__detail">'+(l.company||"—")+'</span></div><div class="schedule-item__right"><span class="pipeline-card__status pipeline-card__status--'+c.cls+'">'+c.label+"</span></div></div>"}),d+="</div></div>",a.innerHTML=d}}function Ms(e){ot&&clearInterval(ot),ot=setInterval(function(){var a=Date.now();document.querySelectorAll(".schedule-timer__text").forEach(function(t){var n=parseInt(t.dataset.deadline,10),o=n-a,s=Yt(o);t.textContent=s.text;var i=t.closest(".schedule-timer");i&&(i.className="schedule-timer schedule-timer--"+s.urgent);var r=t.closest(".schedule-item");r&&(r.className=r.className.replace(/schedule-item--(normal|caution|warning|critical|overdue)/g,""),r.classList.add("schedule-item--"+s.urgent));var d=r?r.querySelector(".schedule-bar__fill"):null;if(d){var l=Math.max(0,Math.min(100,o/864e5*100));d.style.width=l+"%",d.className="schedule-bar__fill schedule-bar__fill--"+s.urgent}})},6e4)}async function W(e,a,t){try{const n=await fetch(O+"/clients/"+e,{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:"Bearer "+t},body:JSON.stringify(a)});if(n.ok)return await n.json()}catch(n){console.error("patchClient error:",n)}return null}function Ts(){return`
2079    <section class="section">
2080      <div class="container">
2081        <div class="section__head">
2082          <h2>Clients</h2>
2083          <p>List of intake submissions and clients</p>
2084        </div>
2085        <div class="glass" style="padding:12px;overflow:auto;">
2086          <table id="clients-table" style="width:100%;border-collapse:collapse;">
2087            <thead>
2088              <tr><th>Name</th><th>Email</th><th>Phone</th><th>Company</th><th>Status</th><th>Updated</th><th>Actions</th></tr>
2089            </thead>
2090            <tbody id="clients-tbody"></tbody>
2091          </table>
2092        </div>
2093      </div>
2094    </section>
2095  `}function Ss(e){const a=new Date(e.updatedAt||e.createdAt).toLocaleString();return`
2096    <tr data-id="${e.id}">
2097      <td>${e.name||"-"}</td>
2098      <td>${e.email||"-"}</td>
2099      <td>${e.phone||"-"}</td>
2100      <td>${e.company||"-"}</td>
2101      <td>
2102        <select class="client-status-select">
2103          <option value="new" ${e.status==="new"?"selected":""}>new</option>
2104          <option value="contacted" ${e.status==="contacted"?"selected":""}>contacted</option>
2105          <option value="interested" ${e.status==="interested"?"selected":""}>interested</option>
2106          <option value="in-program" ${e.status==="in-program"?"selected":""}>in-program</option>
2107          <option value="converted" ${e.status==="converted"?"selected":""}>converted</option>
2108          <option value="closed" ${e.status==="closed"?"selected":""}>closed</option>
2109        </select>
2110      </td>
2111      <td>${a}</td>
2112      <td>
2113        <button class="btn btn--small btn-view">View</button>
2114        <button class="btn btn--small btn-note">Note</button>
2115      </td>
2116    </tr>
2117  `}function Hs(){const e=localStorage.getItem("bizwrap_token");if(!e){window.location.hash="#/admin-login";return}(async function(){const t=await fetch(`${O}/clients`,{headers:{Authorization:`Bearer ${e}`}});if(!t.ok){if(t.status===401||t.status===403){localStorage.removeItem("bizwrap_token"),localStorage.removeItem("bizwrap_user"),window.location.hash="#/admin-login";return}throw new Error(`Server returned status ${t.status}`)}const n=await t.json(),o=document.getElementById("clients-tbody");o.innerHTML=n.map(Ss).join(""),o.querySelectorAll(".btn-view").forEach(s=>{s.addEventListener("click",i=>{const r=i.target.closest("tr").dataset.id;window.location.hash=`#/admin/clients/${r}`})}),o.querySelectorAll(".client-status-select").forEach(s=>{s.addEventListener("change",async i=>{const d=i.target.closest("tr").dataset.id,l=i.target.value;try{await fetch(`${O}/clients/${d}`,{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify({status:l})})}catch(c){console.error(c)}})}),o.querySelectorAll(".btn-note").forEach(s=>{s.addEventListener("click",async i=>{const r=i.target.closest("tr").dataset.id,d=prompt("Quick note");if(d)try{await fetch(`${O}/clients/${r}/timeline`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`},body:JSON.stringify({event:"note",note:d})}),alert("Note added")}catch(l){alert("Error"),console.error(l)}})})})()}const ee=e=>String(e??"").replace(/[&<>"']/g,a=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"})[a]),ye=e=>`"${String(e??"").replace(/"/g,"")}"`,Vt=e=>String(e||"").toLowerCase().replace(/[^a-z0-9]+/g,"").slice(0,24)||"tenant",Jt=e=>String(e||"").trim().replace(/^https?:\/\//i,"").replace(/\/.*$/,"");function Rs(e){const a=["node scripts/make-tenant.mjs","--name "+ye(e.companyName)];e.ownerName&&a.push("--owner "+ye(e.ownerName)),e.ownerEmail&&a.push("--email "+ye(e.ownerEmail));const t=Jt(e.website);return t&&a.push("--domain "+ye(t)),e.cityState&&a.push("--city "+ye(e.cityState)),e.brandColor&&a.push("--color "+ye(e.brandColor)),e.logoUrl?a.push("--logo "+ye(e.logoUrl)):e.logoDataUrl&&a.push('--logo "PATH-TO-DOWNLOADED-LOGO"'),a.join(" ")}function Cs(e){const a=e.questionnaire&&e.questionnaire.companyProfile,t="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;";if(!a||!a.companyName)return e.questionnaire?`<div class="admin-detail__info-card" style="margin-top:var(--space-4);">
2118        <h3 style="font-size:var(--text-base);font-weight:var(--weight-bold);margin-bottom:var(--space-3);">🎁 Crystal Demo Builder</h3>
2119        <p style="color:var(--muted);font-size:var(--text-sm);line-height:1.5;">This lead finished the discovery questionnaire before the Company Basics section existed, so there's no company profile to auto-build a demo from. Grab their company name, owner, website and city on the call, then run <code>make-demo.bat</code> in your Crystal folder.</p>
2120      </div>`:"";const n=Vt(a.companyName),o=Rs(a),s=!!a.logoDataUrl,i=a.logoDataUrl||a.logoUrl;return`
2121    <div class="admin-detail__info-card" id="crystal-demo-builder" style="margin-top:var(--space-4);border:1px solid var(--brand, #d9b25a);">
2122      <h3 style="font-size:var(--text-base);font-weight:var(--weight-bold);margin-bottom:var(--space-4);">🎁 Crystal Demo Builder</h3>
2123      <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);font-size:var(--text-sm);margin-bottom:var(--space-4);">
2124        <div><strong style="${t}">Company</strong><br/>${ee(a.companyName)}</div>
2125        <div><strong style="${t}">Owner</strong><br/>${ee(a.ownerName)||"—"}</div>
2126        <div><strong style="${t}">Owner email</strong><br/>${ee(a.ownerEmail)||"—"}</div>
2127        <div><strong style="${t}">Website</strong><br/>${ee(Jt(a.website))||"—"}</div>
2128        <div><strong style="${t}">City</strong><br/>${ee(a.cityState)||"—"}</div>
2129        <div><strong style="${t}">Brand color</strong><br/><span style="display:inline-block;width:13px;height:13px;border-radius:3px;vertical-align:middle;margin-right:6px;background:${ee(a.brandColor)||"#d9b25a"};"></span>${ee(a.brandColor)||"—"}</div>
2130      </div>
2131      ${i?`<div style="margin-bottom:var(--space-4);"><strong style="${t}">Logo</strong><br/><img src="${ee(i)}" alt="logo" style="max-height:56px;
2131max-width:200px;background:#fff;border-radius:8px;padding:6px;margin-top:6px;"/></div>`:""}
2132
2133      <strong style="${t}">1 · Build the tenant</strong>
2134      <p style="color:var(--muted);font-size:var(--text-xs);margin:4px 0 6px;line-height:1.5;">Run this in your <code>Crystal</code> folder.${s?" Download the logo (below) first, then replace <code>PATH-TO-DOWNLOADED-LOGO</code> with the saved file (or just drag the file onto the command).":""}</p>
2135      <pre id="demo-cmd" style="background:var(--code-bg,#0d1117);color:#e6edf3;border:1px solid var(--border);border-radius:8px;padding:12px 14px;font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12px;line-height:1.55;white-space:pre-wrap;word-break:break-word;overflow-x:auto;margin:0;">${ee(o)}</pre>
2136      <div style="display:flex;gap:var(--space-2);flex-wrap:wrap;margin-top:var(--space-2);">
2137        <button class="btn btn--primary" id="demo-copy-cmd" style="background:var(--grad-brand);">Copy command</button>
2138        ${s?'<button class="btn btn--secondary" id="demo-dl-logo">Download logo</button>':""}
2139      </div>
2140
2141      <p style="color:var(--muted);font-size:var(--text-xs);margin-top:var(--space-4);line-height:1.5;"><strong style="${t}">2 · Open it on the call</strong><br/><code>demo.bat ${ee(n)}</code> — launches Crystal branded as ${ee(a.companyName)} on your machine. The city was geocoded automatically; fine-tune coords in <code>web-app/src/tenants/${ee(n)}.js</code> if needed.</p>
2142    </div>`}function Is(){return`
2143    <section class="section" style="padding-top:calc(var(--nav-height) + var(--space-8));">
2144      <div class="container admin-detail" id="admin-detail">
2145        <div class="admin-empty"><div class="admin-empty__icon">⏳</div><div class="admin-empty__text">Loading client…</div></div>
2146      </div>
2147    </section>
2148  `}function $s(){const e=localStorage.getItem("bizwrap_token");if(!e){window.location.hash="#/admin-login";return}const t=window.location.hash.split("/clients/")[1];if(!t){window.location.hash="#/admin";return}Kt(t,e)}async function Kt(e,a){try{const t=await fetch(`${O}/clients/${e}`,{headers:{Authorization:`Bearer ${a}`}});if(!t.ok)throw new Error("Not found");const n=await t.json();Ls(n,a)}catch{document.getElementById("admin-detail").innerHTML=`
2149      <div class="admin-empty"><div class="admin-empty__icon">⚠️</div><div class="admin-empty__text">Client not found.</div></div>
2150      <div style="text-align:center;margin-top:var(--space-4);"><button class="btn btn--ghost" onclick="window.location.hash='#/admin'">← Back to Pipeline</button></div>`}}function Ls(e,a){const t=e,n=document.getElementById("admin-detail");n.innerHTML=`
2151    <div style="margin-bottom:var(--space-4);">
2152      <button class="admin-action" onclick="window.location.hash='#/admin'" style="font-size:var(--text-sm);">← Back to Pipeline</button>
2153    </div>
2154
2155    <div class="admin-detail__header">
2156      <div class="admin-detail__info-card">
2157        <h2 style="font-size:var(--text-2xl);font-weight:var(--weight-black);margin-bottom:var(--space-4);">${t.name||"Unknown"}</h2>
2158        <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);font-size:var(--text-sm);">
2159          <div><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Email</strong><br/>${t.email||"—"}</div>
2160          <div><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Phone</strong><br/>${t.phone||"—"}</div>
2161          <div><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Company</strong><br/>${t.company||"—"}</div>
2162          <div><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Service</strong><br/>${t.service||"—"}</div>
2163          <div><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Source</strong><br/><span class="admin-source">${t.source||"manual"}</span></div>
2164          <div><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Submitted</strong><br/>${new Date(t.createdAt).toLocaleDateString()}</div>
2165        </div>
2166        ${t.message?`<div style="margin-top:var(--space-5);padding-top:var(--space-4);border-top:1px solid var(--border);"><strong style="color:var(--muted);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:0.06em;">Original Message</strong><p style="font-size:var(--text-sm);color:var(--ink);margin-top:var(--space-2);">${t.message}</p></div>`:""}
2167      </div>
2168
2169      <div class="admin-detail__status-card">
2170        <h3 style="font-size:var(--text-base);font-weight:var(--weight-bold);margin-bottom:var(--space-4);">Status Tracking</h3>
2171        <div class="admin-detail__status-row">
2172          <span class="admin-detail__status-label">Pipeline</span>
2173          <select class="admin-select" id="detail-status" data-field="status">
2174            ${["new","contacted","interested","in-program","converted","closed"].map(s=>`<option value="${s}" ${t.status===s?"selected":""}>${s}</option>`).join("")}
2175          </select>
2176        </div>
2177        <div class="admin-detail__status-row">
2178          <span class="admin-detail__status-label">Called</span>
2179          <label class="admin-toggle ${t.called?"admin-toggle--on":""}" id="detail-called" data-field="called">
2180            <span class="admin-toggle__track"></span>
2181          </label>
2182        </div>
2183        <div class="admin-detail__status-row">
2184          <span class="admin-detail__status-label">Interested</span>
2185          <div class="admin-tri" id="detail-interested" data-field="interested">
2186            <button class="admin-tri__opt ${t.interested==="unknown"?"admin-tri__opt--active-unknown":""}" data-val="unknown">?</button>
2187            <button class="admin-tri__opt ${t.interested==="yes"?"admin-tri__opt--active-yes":""}" data-val="yes">Yes</button>
2188            <button class="admin-tri__opt ${t.interested==="no"?"admin-tri__opt--active-no":""}" data-val="no">No</button>
2189          </div>
2190        </div>
2191        <div class="admin-detail__status-row">
2192          <span class="admin-detail__status-label">Discovery Call</span>
2193          <select class="admin-select" id="detail-discovery" data-field="discoveryCall">
2194            ${["not-scheduled","scheduled","completed"].map(s=>`<option value="${s}" ${t.discoveryCall===s?"selected":""}>${s.replace("-"," ")}</option>`).join("")}
2195          </select>
2196        </div>
2197        <div class="admin-detail__status-row">
2198          <span class="admin-detail__status-label">Onboarding</span>
2199          <label class="admin-toggle ${t.onboarding?"admin-toggle--on":""}" id="detail-onboarding" data-field="onboarding">
2200            <span class="admin-toggle__track"></span>
2201          </label>
2202        </div>
2203      </div>
2204    </div>
2205
2206    ${Cs(t)}
2207
2208    <!-- Timeline -->
2209    <div class="admin-detail__info-card admin-timeline">
2210      <h3 style="font-size:var(--text-base);font-weight:var(--weight-bold);margin-bottom:var(--space-4);">Timeline</h3>
2211      <div style="display:flex;gap:var(--space-3);margin-bottom:var(--space-5);">
2212        <input class="form-input" id="detail-event" placeholder="Event (e.g. Follow-up call)" style="flex:1;" />
2213        <input class="form-input" id="detail-note" placeholder="Note (optional)" style="flex:1;" />
2214        <button class="btn btn--primary" id="detail-add-event">Add</button>
2215      </div>
2216      <div id="detail-timeline">
2217        ${(t.timeline||[]).reverse().map(s=>`
2218          <div class="admin-timeline__item">
2219            <div class="admin-timeline__dot"></div>
2220            <div class="admin-timeline__content">
2221              <div class="admin-timeline__event">${s.event||"Note"}</div>
2222              ${s.note?`<div class="admin-timeline__note">${s.note}</div>`:""}
2223              <div class="admin-timeline__date">${new Date(s.date).toLocaleString()}</div>
2224            </div>
2225          </div>
2226        `).join("")||'<div style="color:var(--muted);font-size:var(--text-sm);">No events yet.</div>'}
2227      </div>
2228    </div>
2229  `;const o=t.id;document.querySelectorAll(".admin-toggle").forEach(s=>{s.addEventListener("click",async()=>{const i=s.dataset.field,r=s.classList.contains("admin-toggle--on");s.classList.toggle("admin-toggle--on"),await rt(o,{[i]:!r},a)})}),document.querySelectorAll(".admin-select").forEach(s=>{s.addEventListener("change",async()=>
2229{await rt(o,{[s.dataset.field]:s.value},a)})}),document.querySelectorAll(".admin-tri").forEach(s=>{s.querySelectorAll(".admin-tri__opt").forEach(i=>{i.addEventListener("click",async()=>{s.querySelectorAll(".admin-tri__opt").forEach(d=>d.className="admin-tri__opt");const r=i.dataset.val;r==="yes"?i.classList.add("admin-tri__opt--active-yes"):r==="no"?i.classList.add("admin-tri__opt--active-no"):i.classList.add("admin-tri__opt--active-unknown"),await rt(o,{interested:r},a)})})}),document.getElementById("demo-copy-cmd")?.addEventListener("click",async s=>{const i=document.getElementById("demo-cmd")?.textContent||"";try{await navigator.clipboard.writeText(i);const r=s.currentTarget,d=r.textContent;r.textContent="Copied ✓",setTimeout(()=>{r.textContent=d},1500)}catch{}}),document.getElementById("demo-dl-logo")?.addEventListener("click",()=>{const s=t.questionnaire&&t.questionnaire.companyProfile;if(!s||!s.logoDataUrl)return;const i=s.logoDataUrl.match(/^data:image\/(png|jpeg|jpg|webp|svg\+xml);/),r=i?i[1]==="jpeg"?"jpg":i[1]==="svg+xml"?"svg":i[1]:"png",d=document.createElement("a");d.href=s.logoDataUrl,d.download=`${Vt(s.companyName)}-logo.${r}`,document.body.appendChild(d),d.click(),d.remove()}),document.getElementById("detail-add-event")?.addEventListener("click",async()=>{const s=document.getElementById("detail-event")?.value,i=document.getElementById("detail-note")?.value;if(s)try{const d=await(await fetch(`${O}/clients/${o}/timeline`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${a}`},body:JSON.stringify({event:s,note:i})})).json();Kt(o,a)}catch{}})}async function rt(e,a,t){await fetch(`${O}/clients/${e}`,{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify(a)})}function Bs(){return`
2230    <section class="section" style="padding-top:calc(var(--nav-height) + var(--space-8));">
2231      <div class="container admin-dash">
2232        <div class="admin-header">
2233          <h1 class="admin-header__title">Admin Portal</h1>
2234        </div>
2235
2236        <!-- Admin Sub-Nav -->
2237        <div class="admin-nav">
2238          <span class="admin-nav__link" onclick="window.location.hash='#/admin'">Pipeline</span>
2239          <span class="admin-nav__link admin-nav__link--active" onclick="window.location.hash='#/admin-forms'">Forms Builder</span>
2240          <span class="admin-nav__link" onclick="window.location.hash='#/admin-email'">Email Template</span>
2241        </div>
2242
2243        <!-- Create new form -->
2244        <div class="admin-table-wrap" style="margin-bottom:var(--space-6);">
2245          <div class="admin-table-header">
2246            <h3>Create New Form</h3>
2247          </div>
2248          <div style="padding:var(--space-5) var(--space-6);display:flex;gap:var(--space-3);">
2249            <input class="form-input" id="new-form-name" placeholder="Form name (e.g. Client Intake)" style="flex:1;margin:0;" />
2250            <input class="form-input" id="new-form-desc" placeholder="Description (optional)" style="flex:1;margin:0;" />
2251            <button class="btn btn--primary" id="create-form-btn" style="white-space:nowrap;">+ Create</button>
2252          </div>
2253        </div>
2254
2255        <!-- Forms list -->
2256        <div id="forms-list-area">
2257          <div class="admin-empty"><div class="admin-empty__icon">📝</div><div class="admin-empty__text">Loading forms…</div></div>
2258        </div>
2259
2260        <!-- Form editor (hidden until a form is selected) -->
2261        <div id="form-editor-area" style="display:none;">
2262          <div class="admin-table-wrap">
2263            <div class="admin-table-header">
2264              <div>
2265                <h3 id="editor-form-name" style="margin:0;"></h3>
2266                <span id="editor-form-desc" style="font-size:var(--text-xs);color:var(--muted);"></span>
2267              </div>
2268              <div style="display:flex;gap:var(--space-2);">
2269                <button class="admin-action" id="editor-close-btn">✕ Close</button>
2270                <button class="btn btn--primary" id="editor-save-btn" style="font-size:var(--text-sm);padding:var(--space-2) var(--space-5);">Save Form</button>
2271              </div>
2272            </div>
2273
2274            <!-- Fields list -->
2275            <div style="padding:var(--space-5) var(--space-6);">
2276              <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--space-4);">
2277                <span style="font-size:var(--text-sm);font-weight:var(--weight-bold);color:var(--ink);">Form Fields</span>
2278                <button class="admin-action" id="add-field-btn" style="color:var(--purple);border-color:var(--purple);">+ Add Field</button>
2279              </div>
2280              <div id="fields-container"></div>
2281              <div id="fields-empty" class="admin-empty" style="padding:var(--space-8) var(--space-4);">
2282                <div class="admin-empty__icon">📋</div>
2283                <div class="admin-empty__text">No fields yet. Click "+ Add Field" to start building your form.</div>
2284              </div>
2285            </div>
2286
2287            <!-- Live preview -->
2288            <div style="border-top:1px solid var(--border);padding:var(--space-5) var(--space-6);">
2289              <span style="font-size:var(--text-sm);font-weight:var(--weight-bold);color:var(--ink);display:block;margin-bottom:var(--space-4);">Live Preview</span>
2290              <div id="form-preview" style="background:var(--bg-alt);border-radius:var(--radius-md);padding:var(--space-6);"></div>
2291            </div>
2292          </div>
2293        </div>
2294      </div>
2295    </section>
2296
2297    <style>
2298      .fb-field {
2299        background: var(--bg-alt);
2300        border: 1px solid var(--border);
2301        border-radius: var(--radius-md);
2302        padding: var(--space-4);
2303        margin-bottom: var(--space-3);
2304        transition: box-shadow 0.15s ease, opacity 0.15s ease, transform 0.15s ease;
2305        position: relative;
2306      }
2307      .fb-field:hover { box-shadow: var(--shadow-sm); }
2308      /* Drag states */
2309      .fb-field--dragging {
2310        opacity: 0.4;
2311        transform: scale(0.98);
2312        box-shadow: none;
2313      }
2314      .fb-field--drag-over {
2315        /* don't highlight, the indicator line handles this */
2316      }
2317      .fb-drop-indicator {
2318        height: 4px;
2319        background: var(--purple);
2320        border-radius: 2px;
2321        margin: -2px 0;
2322        box-shadow: 0 0 8px rgba(104, 74, 197, 0.4);
2323        transition: opacity 0.15s ease;
2324        position: relative;
2325        z-index: 10;
2326      }
2327      .fb-drop-indicator::before {
2328        content: '';
2329        position: absolute;
2330        left: -4px; top: -4px;
2331        width: 12px; height: 12px;
2332        border-radius: 50%;
2333        background: var(--purple);
2334      }
2335      .fb-drop-indicator::after {
2336        content: '';
2337        position: absolute;
2338        right: -4px; top: -4px;
2339        width: 12px; height: 12px;
2340        border-radius: 50%;
2341        background: var(--purple);
2342      }
2343      .fb-field__header {
2344        display: flex;
2345        align-items: center;
2346        gap: var(--space-3);
2347        margin-bottom: var(--space-3);
2348      }
2349      .fb-field__grip {
2350        color: var(--muted-light);
2351        cursor: grab;
2352        font-size: 16px;
2353        line-height: 1;
2354        user-select: none;
2355        padding: 4px 2px;
2356        border-radius: 4px;
2357        transition: color 0.15s ease, background 0.15s ease;
2358      }
2359      .fb-field__grip:hover { color: var(--purple); background: var(--bg-tint); }
2360      .fb-field__grip:active { cursor: grabbing; color: var(--purple); }
2361      .fb-field__num {
2362        width: 24px; height: 24px;
2363        border-radius: 50%;
2364        background: var(--purple);
2365        color: #fff;
2366        font-size: 0.65rem;
2367        font-weight: var(--weight-bold);
2368        display: flex;
2369        align-items: center;
2370        justify-content: center;
2371        flex-shrink: 0;
2372      }
2373      .fb-field__body {
2374        display: grid;
2375        grid-template-columns: 1fr 140px 140px auto;
2376        gap: var(--space-3);
2377        align-items: start;
2378      }
2379      .fb-field__actions {
2380        display: flex;
2381        gap: var(--space-2);
2382        align-items: center;
2383        margin-left: auto;
2384      }
2385      .fb-field__remove {
2386        width: 28px; height: 28px;
2387        display: flex;
2388        align-items: center;
2389        justify-content: center;
2390        border: 1px solid var(--border);
2391        border-radius: var(--radius-full);
2392        background: #fff;
2393        color: var(--muted);
2394        cursor: pointer;
2395        font-size: 0.8rem;
2396        transition: all var(--transition-fast);
2397      }
2398      .fb-field__remove:hover { border-color: #dc2626;
2398 color: #dc2626; background: #fef2f2; }
2399      .fb-field__move {
2400        width: 28px; height: 28px;
2401        display: flex;
2402        align-items: center;
2403        justify-content: center;
2404        border: 1px solid var(--border);
2405        border-radius: var(--radius-full);
2406        background: #fff;
2407        color: var(--muted);
2408        cursor: pointer;
2409        font-size: 0.75rem;
2410        transition: all var(--transition-fast);
2411      }
2412      .fb-field__move:hover { border-color: var(--purple); color: var(--purple); }
2413      .fb-mini-input {
2414        padding: 5px 10px;
2415        font-size: var(--text-sm);
2416        border: 1px solid var(--border);
2417        border-radius: var(--radius-sm);
2418        background: #fff;
2419        color: var(--ink);
2420        width: 100%;
2421      }
2422      .fb-mini-input:focus { border-color: var(--purple); outline: none; }
2423      .fb-mini-select {
2424        padding: 5px 8px;
2425        font-size: var(--text-sm);
2426        border: 1px solid var(--border);
2427        border-radius: var(--radius-sm);
2428        background: #fff;
2429        color: var(--ink);
2430        width: 100%;
2431        cursor: pointer;
2432      }
2433      .fb-mini-select:focus { border-color: var(--purple); outline: none; }
2434      .fb-options-area {
2435        grid-column: 1 / -1;
2436        margin-top: var(--space-2);
2437      }
2438      .fb-required-toggle {
2439        display: flex;
2440        align-items: center;
2441        gap: 4px;
2442        cursor: pointer;
2443        user-select: none;
2444        font-size: var(--text-xs);
2445        color: var(--muted);
2446        white-space: nowrap;
2447      }
2448      .fb-form-card {
2449        display: flex;
2450        justify-content: space-between;
2451        align-items: center;
2452        padding: var(--space-4) var(--space-5);
2453        border-bottom: 1px solid var(--border);
2454      }
2455      .fb-form-card:last-child { border-bottom: none; }
2456      .fb-form-card:hover { background: var(--bg-alt); }
2457      @media (max-width: 700px) {
2458        .fb-field__body { grid-template-columns: 1fr; }
2459      }
2460    </style>
2461  `}let pe=[],Q=null,D=[],Te="";function Ps(){if(Te=localStorage.getItem("bizwrap_token"),!Te){window.location.hash="#/admin-login";return}document.getElementById("create-form-btn")?.addEventListener("click",qs),document.getElementById("add-field-btn")?.addEventListener("click",Os),document.getElementById("editor-save-btn")?.addEventListener("click",zs),document.getElementById("editor-close-btn")?.addEventListener("click",Xt),Oe()}const As=[{id:"name",label:"Full name",type:"text",required:!0,placeholder:"Joey Murphy",options:[]},{id:"email",label:"Email address",type:"email",required:!0,placeholder:"[email protected]",options:[]},{id:"company",label:"Company",type:"text",required:!1,placeholder:"Your business",options:[]},{id:"service",label:"What are you looking to build?",type:"select",required:!1,placeholder:"Select an option...",options:["Full operating system","AI agent / copilot","Property mapping & field tools","Design studio","Warehouse & logistics","Financials & reporting","Other / not sure yet"]},{id:"message",label:"Tell us about your operation",type:"textarea",required:!0,placeholder:"How does your business run today? Where does it hurt?",options:[]}];async function Oe(){try{const e=await fetch(`${O}/forms`,{headers:{Authorization:`Bearer ${Te}`}});if(!e.ok){if(e.status===401||e.status===403){localStorage.removeItem("bizwrap_token"),localStorage.removeItem("bizwrap_user"),window.location.hash="#/admin-login";return}throw new Error(`Server returned status ${e.status}`)}if(pe=await e.json(),pe.length===0){await Gs();return}Ns(),pe.length>0&&!Q&&Qt(pe[0].id)}catch{document.getElementById("forms-list-area").innerHTML=`
2462      <div class="admin-empty"><div class="admin-empty__icon">⚠️</div><div class="admin-empty__text">Failed to load forms. Is the server running?</div></div>`}}async function Gs(){try{await fetch(`${O}/forms`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${Te}`},body:JSON.stringify({name:"Contact Form",description:'Main website intake form — "Book a Demo" / "Send us a message"',fields:As})}),await Oe()}catch{}}async function qs(){const e=document.getElementById("new-form-name"),a=document.getElementById("new-form-desc"),t=e?.value?.trim();if(!t){e?.focus();return}await fetch(`${O}/forms`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${Te}`},body:JSON.stringify({name:t,description:a?.value?.trim()||"",fields:[]})}),e.value="",a.value="",await Oe()}async function zs(){if(!Q)return;Ws(),await fetch(`${O}/forms/${Q.id}`,{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:`Bearer ${Te}`},body:JSON.stringify({name:Q.name,description:Q.description,fields:D})});const e=document.getElementById("editor-save-btn");if(e){const a=e.textContent;e.textContent="✓ Saved!",e.style.background="#16a34a",setTimeout(()=>{e.textContent=a,e.style.background=""},1500)}await Oe()}async function Ds(e){confirm("Delete this form permanently?")&&(await fetch(`${O}/forms/${e}`,{method:"DELETE",headers:{Authorization:`Bearer ${Te}`}}),Q?.id===e&&Xt(),await Oe())}function Ns(){const e=document.getElementById("forms-list-area");if(pe.length===0){e.innerHTML=`
2463      <div class="admin-empty"><div class="admin-empty__icon">📄</div><div class="admin-empty__text">No forms yet. Create one above.</div></div>`;return}e.innerHTML=`
2464    <div class="admin-table-wrap">
2465      <div class="admin-table-header">
2466        <h3>Your Forms</h3>
2467        <span class="admin-table-count">${pe.length} form${pe.length!==1?"s":""}</span>
2468      </div>
2469      ${pe.map(a=>`
2470        <div class="fb-form-card" data-form-id="${a.id}">
2471          <div>
2472            <strong style="font-size:var(--text-sm);">${a.name||a.title||"Untitled"}</strong>
2473            ${a.description?`<div style="font-size:var(--text-xs);color:var(--muted);margin-top:2px;">${a.description}</div>`:""}
2474            <div style="font-size:var(--text-xs);color:var(--muted-light);margin-top:2px;">${(a.fields||[]).length} field${(a.fields||[]).length!==1?"s":""} · ${a.active!==!1?'<span style="color:#16a34a;">Active</span>':'<span style="color:var(--muted);">Inactive</span>'}</div>
2475          </div>
2476          <div style="display:flex;gap:var(--space-2);">
2477            <button class="admin-action fb-edit-btn" data-id="${a.id}" style="color:var(--purple);border-color:var(--purple);">Edit</button>
2478            <button class="admin-action fb-delete-btn" data-id="${a.id}">Delete</button>
2479          </div>
2480        </div>
2481      `).join("")}
2482    </div>`,e.querySelectorAll(".fb-edit-btn").forEach(a=>{a.addEventListener("click",()=>Qt(a.dataset.id))}),e.querySelectorAll(".fb-delete-btn").forEach(a=>{a.addEventListener("click",()=>Ds(a.dataset.id))})}function Qt(e){Q=pe.find(a=>a.id===e),Q&&(D=(Q.fields||[]).map((a,t)=>({id:a.id||`field_${Date.now()}_${t}`,label:a.label||"",type:a.type||"text",required:a.required||!1,placeholder:a.placeholder||"",options:a.options||[]})),document.getElementById("editor-form-name").textContent=Q.name||Q.title||"Untitled",document.getElementById("editor-form-desc").textContent=Q.description||"",document.getElementById("form-editor-area").style.display="block",Se(),ne())}function Xt(){Q=null,D=[],document.getElementById("form-editor-area").style.display="none"}function Os(){D.push({id:`field_${Date.now()}`,label:"",type:"text",required:!1,placeholder:"",options:[]}),Se(),ne(),setTimeout(()=>{const e=document.querySelectorAll(".fb-label-input");e[e.length-1]?.focus()},50)}function Fs(e){D.splice(e,1),Se(),ne()}function Ct(e,a){const t=e+a;t<0||t>=D.length||([D[e],D[t]]=[D[t],D[e]],Se(),ne())}function Se(){const e=document.getElementById("fields-container"),a=document.getElementById("fields-empty");if(D.length===0){e.innerHTML="",a.style.display="";return}a.style.display="none",e.innerHTML=D.map((t,n)=>`
2483    <div class="fb-field" data-index="${n}" draggable="true">
2484      <div class="fb-field__header">
2485        <span class="fb-field__grip" title="Drag to reorder">â ¿</span>
2486        <span class="fb-field__num">${n+1}</span>
2487        <div class="fb-field__body">
2488          <input class="fb-mini-input fb-label-input" data-index="${n}" data-prop="label" value="${qe(t.label)}" placeholder="Field label" />
2489          <select class="fb-mini-select fb-type-select" data-index="${n}" data-prop="type">
2490            <option value="text" ${t.type==="text"?"selected":""}>Text</option>
2491            <option value="email" ${t.type==="email"?"selected":""}>Email</option>
2492            <option value="tel" ${t.type==="tel"?"selected":""}>Phone</option>
2493            <option value="textarea" ${t.type==="textarea"?"selected":""}>Textarea</option>
2494            <option value="select" ${t.type==="select"?"selected":""}>Dropdown</option>
2495            <option value="checkbox" ${t.type==="checkbox"?"selected":""}>Checkbox</option>
2496          </select>
2497          <input class="fb-mini-input" data-index="${n}" data-prop="placeholder" value="${qe(t.placeholder)}" placeholder="Placeholder" />
2498          <div style="display:flex;align-items:center;gap:var(--space-2);">
2499            <label class="fb-required-toggle">
2500              <input type="checkbox" class="fb-req-check" data-index="${n}" ${t.required?"checked":""} style="accent-color:var(--purple);" />
2501              Req
2502            </label>
2503          </div>
2504        </div>
2505        <div class="fb-field__actions">
2506          <button class="fb-field__move fb-move-up" data-index="${n}" title="Move up" ${n===0?'disabled style="opacity:0.3;pointer-events:none;"':""}>▲</button>
2507          <button class="fb-field__move fb-move-down" data-index="${n}" title="Move down" ${n===D.length-1?'disabled style="opacity:0.3;pointer-events:none;"':""}>▼</button>
2508          <button class="fb-field__remove fb-remove" data-index="${n}" title="Remove field">✕</button>
2509        </div>
2510      </div>
2511      ${t.type==="select"?`
2512        <div class="fb-options-area">
2513          <label style="font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--muted);display:block;margin-bottom:var(--space-1);">Options (comma-separated)</label>
2514          <input class="fb-mini-input fb-options-input" data-index="${n}" value="${qe((t.options||[]).join(", "))}" placeholder="Option 1, Option 2, Option 3" />
2515        </div>`:""}
2516    </div>
2517  `).join(""),e.querySelectorAll('.fb-label-input, .fb-mini-input[data-prop="placeholder"]').forEach(t=>{t.addEventListener("input",()=>{const n=parseInt(t.dataset.index),o=t.dataset.prop;D[n][o]=t.value,ne()})}),e.querySelectorAll(".fb-type-select").forEach(t=>{t.addEventListener("change",()=>{const n=parseInt(t.dataset.index);D[n].type=t.value,t.value!=="select"&&(D[n].options=[]),Se(),ne()})}),e.querySelectorAll(".fb-req-check").forEach(t=>{t.addEventListener("change",()=>{const n=parseInt(t.dataset.index);D[n].required=t.checked,ne()})}),e.querySelectorAll(".fb-options-input").forEach(t=>{t.addEventListener("input",()=>{const n=parseInt(t.dataset.index);D[n].options=t.value.split(",").map(o=>
2517o.trim()).filter(Boolean),ne()})}),e.querySelectorAll(".fb-move-up").forEach(t=>{t.addEventListener("click",()=>Ct(parseInt(t.dataset.index),-1))}),e.querySelectorAll(".fb-move-down").forEach(t=>{t.addEventListener("click",()=>Ct(parseInt(t.dataset.index),1))}),e.querySelectorAll(".fb-remove").forEach(t=>{t.addEventListener("click",()=>Fs(parseInt(t.dataset.index)))}),js(e)}function js(e){let a=null;function t(){return Array.from(e.querySelectorAll(".fb-field"))}function n(){e.querySelectorAll(".fb-drop-indicator").forEach(s=>s.remove()),t().forEach(s=>{s.classList.remove("fb-field--drag-over")})}function o(s,i){const r=i.getBoundingClientRect(),d=r.top+r.height/2;return s.clientY<d?"before":"after"}t().forEach(s=>{s.addEventListener("mousedown",i=>{i.target.closest(".fb-field__grip")?s.setAttribute("draggable","true"):s.setAttribute("draggable","false")}),s.addEventListener("dragstart",i=>{a=parseInt(s.dataset.index),s.classList.add("fb-field--dragging"),i.dataTransfer.effectAllowed="move",i.dataTransfer.setData("text/plain",String(a))}),s.addEventListener("dragover",i=>{if(i.preventDefault(),i.dataTransfer.dropEffect="move",parseInt(s.dataset.index)===a)return;n();const d=o(i,s),l=document.createElement("div");l.className="fb-drop-indicator",d==="before"?s.parentNode.insertBefore(l,s):s.parentNode.insertBefore(l,s.nextSibling)}),s.addEventListener("dragleave",i=>{s.contains(i.relatedTarget)||n()}),s.addEventListener("drop",i=>{i.preventDefault(),i.stopPropagation(),n();const r=a,d=parseInt(s.dataset.index);if(r===null||r===d)return;const l=o(i,s);let c=d;l==="after"&&(c+=1),r<c&&(c-=1);const[p]=D.splice(r,1);D.splice(c,0,p),a=null,Se(),ne()}),s.addEventListener("dragend",()=>{s.classList.remove("fb-field--dragging"),n(),a=null})}),e.addEventListener("dragover",s=>{s.preventDefault()}),e.addEventListener("drop",s=>{if(s.target===e||s.target.closest(".fb-field")===null){s.preventDefault(),n();const i=a;if(i===null)return;const[r]=D.splice(i,1);D.push(r),a=null,Se(),ne()}})}function ne(){const e=document.getElementById("form-preview");if(e){if(D.length===0){e.innerHTML='<div style="text-align:center;color:var(--muted);font-size:var(--text-sm);padding:var(--space-6);">Add fields to see a live preview</div>';return}e.innerHTML=D.map(a=>{const t=a.required?' <span style="color:#dc2626;">*</span>':"",n=`<label style="display:block;font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--ink);margin-bottom:var(--space-1);">${a.label||"Untitled"}${t}</label>`;switch(a.type){case"textarea":return`<div style="margin-bottom:var(--space-4);">${n}<textarea class="form-input" placeholder="${qe(a.placeholder)}" style="min-height:80px;" disabled></textarea></div>`;case"select":return`<div style="margin-bottom:var(--space-4);">${n}<select class="form-input" disabled><option>${a.placeholder||"Select..."}</option>${(a.options||[]).map(o=>`<option>${o}</option>`).join("")}</select></div>`;case"checkbox":return`<div style="margin-bottom:var(--space-4);"><label style="display:flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--ink);cursor:pointer;"><input type="checkbox" disabled style="accent-color:var(--purple);" /> ${a.label||"Untitled"}${t}</label></div>`;default:return`<div style="margin-bottom:var(--space-4);">${n}<input class="form-input" type="${a.type}" placeholder="${qe(a.placeholder)}" disabled /></div>`}}).join("")}}function Ws(){D.forEach((e,a)=>{e.id||(e.id=`field_${Date.now()}_${a}`)})}function qe(e){return(e||"").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}let B=null,De="",It=null;function Us(){return`
2518    <section class="section" style="padding-top:calc(var(--nav-height) + var(--space-8));">
2519      <div class="container admin-dash">
2520        <div class="admin-header">
2521          <h1 class="admin-header__title">Admin Portal</h1>
2522        </div>
2523
2524        <!-- Admin Sub-Nav -->
2525        <div class="admin-nav">
2526          <span class="admin-nav__link" onclick="window.location.hash='#/admin'">Pipeline</span>
2527          <span class="admin-nav__link" onclick="window.location.hash='#/admin-forms'">Forms Builder</span>
2528          <span class="admin-nav__link admin-nav__link--active" onclick="window.location.hash='#/admin-email'">Email Template</span>
2529        </div>
2530
2531        <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-6);align-items:start;">
2532          <!-- Editor Panel -->
2533          <div class="admin-table-wrap">
2534            <div class="admin-table-header">
2535              <h3>Edit Template</h3>
2536              <button class="btn btn--primary" id="et-save-btn" style="font-size:var(--text-sm);padding:var(--space-2) var(--space-5);">Save Template</button>
2537            </div>
2538            <div style="padding:var(--space-5) var(--space-6);" id="et-editor">
2539              <div class="admin-empty"><div class="admin-empty__icon">✉️</div><div class="admin-empty__text">Loading template…</div></div>
2540            </div>
2541          </div>
2542
2543          <!-- Live Preview Panel -->
2544          <div class="admin-table-wrap" style="position:sticky;top:calc(var(--nav-height) + var(--space-4));">
2545            <div class="admin-table-header">
2546              <h3>Live Preview</h3>
2547              <span style="font-size:var(--text-xs);color:var(--muted);">Updates as you type</span>
2548            </div>
2549            <div style="padding:var(--space-4);background:var(--bg-alt);" id="et-preview">
2550              <div class="admin-empty" style="padding:var(--space-8);"><div class="admin-empty__text">Loading preview…</div></div>
2551            </div>
2552          </div>
2553        </div>
2554
2555        <!-- Template Variables Reference -->
2556        <div class="admin-table-wrap" style="margin-top:var(--space-6);">
2557          <div class="admin-table-header">
2558            <h3>Template Variables</h3>
2559          </div>
2560          <div style="padding:var(--space-4) var(--space-6);font-size:var(--text-sm);color:var(--muted);">
2561            <p style="margin-bottom:var(--space-2);">Use these placeholders in your subject line and body. They'll be replaced with the submitter's data:</p>
2562            <div style="display:flex;flex-wrap:wrap;gap:var(--space-2);">
2563              <code style="background:var(--bg-tint);padding:2px 8px;border-radius:var(--radius-sm);color:var(--purple);font-size:var(--text-xs);font-weight:600;">{{name}}</code>
2564              <code style="background:var(--bg-tint);padding:2px 8px;border-radius:var(--radius-sm);color:var(--purple);font-size:var(--text-xs);font-weight:600;">{{email}}</code>
2565              <code style="background:var(--bg-tint);padding:2px 8px;border-radius:var(--radius-sm);color:var(--purple);font-size:var(--text-xs);font-weight:600;">{{company}}</code>
2566              <code style="background:var(--bg-tint);padding:2px 8px;border-radius:var(--radius-sm);color:var(--purple);font-size:var(--text-xs);font-weight:600;">{{service}}</code>
2567              <code style="background:var(--bg-tint);padding:2px 8px;border-radius:var(--radius-sm);color:var(--purple);font-size:var(--text-xs);font-weight:600;">{{message}}</code>
2568            </div>
2569          </div>
2570        </div>
2571      </div>
2572    </section>
2573  `}function Ys(){if(De=localStorage.getItem("bizwrap_token"),!De){window.location.hash="#/admin-login";return}document.getElementById("et-save-btn")?.addEventListener("click",Ks),Vs()}async function Vs(){try{const e=await fetch(`${O}/email-template`,{headers:{Authorization:`Bearer ${De}`}});if(!e.ok){if(e.status===401||e.status===403){localStorage.removeItem("bizwrap_token"),localStorage.removeItem("bizwrap_user"),window.location.hash="#/admin-login";return}throw new Error(`Server returned status ${e.status}`)}B=await e.json(),Js(),Zt()}catch{document.getElementById("et-editor").innerHTML='<div class="admin-empty"><div class="admin-empty__icon">⚠️</div><div class="admin-empty__text">Failed to load template. Is the server running?</div></div>'}}function Js(){if(!B)return;const e=document.getElementById("et-editor");e.innerHTML=`
2574    <div style="margin-bottom:var(--space-5);">
2575      <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Subject Line</label>
2576      <input class="form-input et-field" data-field="subject" value="${ie(B.subject)}" style="margin:0;" />
2577    </div>
2578
2579    <div style="margin-bottom:var(--space-5);">
2580      <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Preheader Text</label>
2581      <input class="form-input et-field" data-field="preheader" value="${ie(B.preheader)}" placeholder="Preview text shown in inbox" style="margin:0;" />
2582    </div>
2583
2584    <div style="margin-bottom:var(--space-5);">
2585      <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Email Heading</label>
2586      <input class="form-input et-field" data-field="heading" value="${ie(B.heading)}" style="margin:0;" />
2587    </div>
2588
2589    <div style="margin-bottom:var(--space-5);">
2590      <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Body</label>
2591      <textarea class="form-input et-field" data-field="bodyHtml" rows="8" style="margin:0;font-size:var(--text-sm);line-height:1.6;">${ie(Qs(B.bodyHtml))}</textarea>
2592    </div>
2593
2594    <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4);margin-bottom:var(--space-5);">
2595      <div>
2596        <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Button Text</label>
2597        <input class="form-input et-field" data-field="ctaText" value="${ie(B.ctaText)}" style="margin:0;" />
2598      </div>
2599      <div>
2600        <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Button URL</label>
2601        <input class="form-input et-field" data-field="ctaUrl" value="${ie(B.ctaUrl)}" placeholder="https://" style="margin:0;" />
2602      </div>
2603    </div>
2604
2605    <div style="margin-bottom:var(--space-5);">
2606      <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Footer Text</label>
2607      <input class="form-input et-field" data-field="footerText" value="${ie(B.footerText)}" style="margin:0;" />
2608    </div>
2609
2610    <div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4);">
2611      <div>
2612        <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Brand Color</label>
2613        <div style="display:flex;gap:var(--space-2);align-items:center;">
2614          <input type="color" class="et-field" data-field="brandColor" value="${B.brandColor||"#684ac5"}" style="width:40px;height:34px;border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;padding:2px;" />
2615          <input class="form-input et-field" data-field="brandColor" value="${ie(B.brandColor)}" style="margin:0;flex:1;font-family:'Courier New',monospace;" />
2616        </div>
2617      </div>
2618      <div>
2619        <label style="font-size:var(--text-xs);font-weight:var(--weight-bold);text-transform:uppercase;letter-spacing:0.06em;color:var(--muted);display:block;margin-bottom:var(--space-1);">Accent Color</label>
2620        <div style="display:flex;gap:var(--space-2);align-items:center;">
2621          <input type="color" class="et-field" data-field="accentColor" value="${B.accentColor||"#01c7cc"}" style="width:40px;height:34px;border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;padding:2px;" />
2622          <input class="form-input et-field" data-field="accentColor" value="${ie(B.accentColor)}" style="margin:0;flex:1;font-family:'Courier New',monospace;" />
2623        </div>
2624      </div>
2625    </div>
2626  `,e.querySelectorAll(".et-field").forEach(a=>{const t=()=>
2626{const n=a.dataset.field,o=a.type==="checkbox"?a.checked:a.value;n==="bodyHtml"?B[n]=o.split(`
2627`).filter(s=>s.trim()).map(s=>"<p>"+s+"</p>").join(""):B[n]=o,(n==="brandColor"||n==="accentColor")&&e.querySelectorAll(`.et-field[data-field="${n}"]`).forEach(s=>{s!==a&&(s.value=o)}),clearTimeout(It),It=setTimeout(Zt,300)};a.addEventListener("input",t),a.addEventListener("change",t)})}async function Zt(){if(!B)return;const e=document.getElementById("et-preview");try{const a=await fetch(`${O}/email-template/preview`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${De}`},body:JSON.stringify({template:{subject:B.subject,preheader:B.preheader,heading:B.heading,bodyHtml:B.bodyHtml,ctaText:B.ctaText,ctaUrl:B.ctaUrl,footerText:B.footerText,brandColor:B.brandColor,accentColor:B.accentColor,logoUrl:B.logoUrl}})}),{html:t}=await a.json();e.innerHTML='<iframe id="et-preview-frame" style="width:100%;border:none;border-radius:var(--radius-md);background:#fff;min-height:500px;" sandbox="allow-same-origin"></iframe>';const n=document.getElementById("et-preview-frame"),o=n.contentDocument||n.contentWindow.document;o.open(),o.write(t),o.close(),setTimeout(()=>{n.style.height=o.body.scrollHeight+20+"px"},100)}catch{e.innerHTML='<div class="admin-empty" style="padding:var(--space-6);"><div class="admin-empty__text">Preview unavailable — is the server running?</div></div>'}}async function Ks(){if(!B?.id)return;const e=document.getElementById("et-save-btn");try{if(await fetch(`${O}/email-template/${B.id}`,{method:"PATCH",headers:{"Content-Type":"application/json",Authorization:`Bearer ${De}`},body:JSON.stringify({subject:B.subject,preheader:B.preheader,heading:B.heading,bodyHtml:B.bodyHtml,ctaText:B.ctaText,ctaUrl:B.ctaUrl,footerText:B.footerText,brandColor:B.brandColor,accentColor:B.accentColor,logoUrl:B.logoUrl})}),e){const a=e.textContent;e.textContent="✓ Saved!",e.style.background="#16a34a",setTimeout(()=>{e.textContent=a,e.style.background=""},1500)}}catch{e&&(e.textContent="✕ Error",e.style.background="#dc2626",setTimeout(()=>{e.textContent="Save Template",e.style.background=""},2e3))}}function ie(e){return(e||"").replace(/&/g,"&amp;").replace(/"/g,"&quot;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function Qs(e){return e?e.replace(/<p>/gi,"").replace(/<\/p>/gi,`
2628`).replace(/<br\s*\/?>/gi,`
2629`).trim():""}function Xs(){return hs()}function Zs(){ms()}function en(){return gs()}function tn(){ys()}function an(){return Ts()}function sn(){Hs()}function nn(){return Is()}function on(){$s()}function rn(){return Bs()}function ln(){Ps()}function dn(){return Us()}function cn(){Ys()}const ht={home:{render:Et,init:_t},services:{render:Et,init:_t},contact:{render:wa,init:Ea},workshop:{render:Ma,init:Ta},chess:{render:ja,init:is},questionnaire:{render:rs,init:ls},"admin-login":{render:Xs,init:Zs},admin:{render:en,init:tn},"admin-clients":{render:an,init:sn},"admin-client":{render:nn,init:on},"admin-forms":{render:rn,init:ln},"admin-email":{render:dn,init:cn}};let Re=null,Ae=null,Je=!1,mt=null;function $t(){const e=window.location.hash.replace("#/","");if(!e)return"home";if(e.startsWith("admin/clients/"))return"admin-client";if(ht[e])return e;const a=e.split("/")[0];return ht[a]?a:"home"}async function Lt(e){if(e===Re)return;const a=ht[e];if(!a)return;if((e.startsWith("admin")||e==="admin-login")&&!Je&&mt&&await mt,e.startsWith("admin")&&e!=="admin-login"){const d=!!["localhost","127.0.0.1"].includes(window.location.hostname)&&localStorage.getItem("bizwrap_token")==="mock-local-token";if(!Ae&&!d){window.location.hash="#/admin-login";return}try{if(Ae){const l=await Ae.getIdToken();localStorage.setItem("bizwrap_token",l)}}catch(l){console.warn("Failed to refresh token during navigation:",l)}}const n=!!["localhost","127.0.0.1"].includes(window.location.hostname)&&localStorage.getItem("bizwrap_token")==="mock-local-token";if(e==="admin-login"&&(Ae||n)){window.location.hash="#/admin";return}Re=e;const o=document.getElementById("app"),s=document.getElementById("page-content");s?(s.style.animation="none",s.offsetHeight,s.innerHTML=a.render(),s.style.animation="pageIn 0.4s ease forwards"):(o.innerHTML=`
2630      ${ta()}
2631      <main id="page-content">
2632        ${a.render()}
2633      </main>
2634      ${sa()}
2635    `,aa()),window.scrollTo({top:0,behavior:"instant"}),a.init(),Bt();const i={home:"BizWrap AI — Gift Wrap Your Business",services:"Platform — BizWrap AI",contact:"Contact — BizWrap AI",workshop:"AI Workshop — BizWrap AI",chess:"Chess Arena — BizWrap AI"};document.title=i[e]||i.home}function pn(){mt=new Promise(e=>{et().then(({auth:a,onAuthStateChanged:t})=>{t(a,async n=>{if(Ae=n,n)try{const o=await n.getIdToken(!0);localStorage.setItem("bizwrap_token",o),localStorage.setItem("bizwrap_user",n.displayName||n.email.split("@")[0])}catch(o){console.warn("Token refresh failed:",o)}if(Je||(Je=!0,e()),!n&&Re&&Re.startsWith("admin")&&Re!=="admin-login"){const o=!["localhost","127.0.0.1"].includes(window.location.hostname),s=localStorage.getItem("bizwrap_token");(o||s!=="mock-local-token")&&(window.location.hash="#/admin-login")}n&&Re==="admin-login"&&(window.location.hash="#/admin")})}).catch(a=>{console.warn("Firebase auth unavailable:",a),Je=!0,e()})}),window.addEventListener("hashchange",()=>{Lt($t())}),document.addEventListener("click",e=>{const a=e.target.closest("[data-navigate]");if(a){e.preventDefault();const t=a.dataset.navigate;window.location.hash=`#/${t}`}}),window.location.hash?Lt($t()):window.location.hash="#/home"}document.addEventListener("DOMContentLoaded",()=>{pn()});

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.