PageSourceSearch

https://alignwave.ai/

html alignwave.ai collected 2026-10-09 22:58:02 UTC 144,037 bytes, 2,886 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en" class="theme-wi">
3
4<head>
5    <!-- Google tag (gtag.js) -->
vendor: 68 bytes, lines 5-6
5
6    <script async src="https://www.googletagmanager.com/gtag/js?id=
6G-D3W0C32KLH
vendor: 16 bytes, lines 6-7
6"></script>
7    
7<script>
8        
vendor: 176 bytes, lines 8-15
8window.dataLayer = window.dataLayer || [];
9
10        function gtag() {
11            dataLayer.push(arguments);
12        }
13        gtag("js", new Date());
14
15        gtag("config", "
15G-D3W0C32KLH
vendor: 8 bytes, lines 15-16
15");
16    
16</script>
16
17
18    <!-- JSON-LD Schema Markup -->
19    
19<script type="application/ld+json">
20        {
21            "@context": "https://schema.org",
22            "@type": "Organization",
23            "name": "AlignWave",
24            "alternateName": ["Align Wave"],
25            "url": "https://alignwave.ai/",
26            "logo": "https://alignwave.ai/assets/images/logo.svg",
27            "description": "Workforce Intelligence from AlignWave: AI-driven insights to hire the best, reduce turnover, and improve retention—from driver hiring to frontline teams.",
28            "sameAs": [
29                "https://www.linkedin.com/company/axamai",
30                "https://twitter.com/axamai",
31                "https://github.com/axamai"
32            ],
33            "contactPoint": {
34                "@type": "ContactPoint",
35                "contactType": "customer service",
36                "url": "https://alignwave.ai/app/request"
37            }
38        }
39    </script>
39
40    
40<script type="application/ld+json">
41        {
42            "@context": "https://schema.org",
43            "@type": "WebSite",
44            "url": "https://alignwave.ai/",
45            "name": "AlignWave",
46            "description": "Workforce Intelligence – AI for better decisions with your existing and new talent"
47        }
48    </script>
48
49    
49<script type="application/ld+json">
50        {
51            "@context": "https://schema.org",
52            "@type": "FAQPage",
53            "mainEntity": [{
54                    "@type": "Question",
55                    "name": "What is AlignWave?",
56                    "acceptedAnswer": {
57                        "@type": "Answer",
58                        "text": "AlignWave is a workforce intelligence platform that helps companies discover the patterns in their own data — and use that intelligence to hire, retain, and develop the people most likely to succeed in their specific environment."
59                    }
60                },
61                {
62                    "@type": "Question",
63                    "name": "What makes AlignWave different from other hiring assessments?",
64                    "acceptedAnswer": {
65                        "@type": "Answer",
66                        "text": "AlignWave builds a company-specific alignment profile from your own workforce — not a generalized industry benchmark. Every candidate is measured against the people who actually succeed at your organization, under your operating conditions."
67                    }
68                },
69                {
70                    "@type": "Question",
71                    "name": "Who is AlignWave built for?",
72                    "acceptedAnswer": {
73                        "@type": "Answer",
74                        "text": "AlignWave is built for mid-market employers starting with logistics and trucking companies — organizations with measurable workforce costs and the operational data needed to build company-specific intelligence."
75                    }
76                }
77            ]
78        }
79    </script>
79
80
81    <!-- Required Meta Tags -->
82    <meta charset="UTF-8" />
83    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
84    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
85    <meta name="robots" content="index, follow" />
86    <meta name="author" content="AlignWave" />
87    <meta name="format-detection" content="telephone=no" />
88
89    <!-- Title -->
90    <title>
91        Workforce Intelligence | AlignWave – AI for better decisions with your talent
92    </title>
93
94    <!-- SEO Meta Description -->
95    <meta name="description" content="Workforce Intelligence from AlignWave: use AI-driven insights to hire the best, reduce turnover, and improve retention—from driver hiring to frontline teams." />
96    <meta property="og:description" content="Workforce Intelligence from AlignWave: use AI-driven insights to hire the best, reduce turnover, and improve retention." />
97    <link rel="canonical" href="https://alignwave.ai/" />
98    <meta name="keywords" content="workforce intelligence, AI hiring, predictive hiring, behavioral fit, retention, driver hiring, talent analytics, alignwave" />
99
100    <!-- Open Graph / Facebook -->
101    <meta property="og:type" content="website" />
102    <meta property="og:url" content="https://alignwave.ai/" />
103    <meta property="og:title" content="Workforce Intelligence | AlignWave" />
104    <meta property="og:description" content="Workforce Intelligence from alignwave.ai: use AI-driven insights to hire the best, reduce turnover, and improve retention." />
105    <meta property="og:image" content="https://alignwave.ai/assets/images/screens/screen-1.png" />
106
107    <!-- Twitter -->
108    <meta name="twitter:card" content="summary_large_image" />
109    <meta name="twitter:url" content="https://alignwave.ai/" />
110    <meta name="twitter:title" content="Workforce Intelligence | AlignWave" />
111    <meta name="twitter:description" content="Workforce Intelligence from AlignWave: AI-driven insights to hire the best, reduce turnover, and improve retention." />
112    <meta name="twitter:image" content="https://alignwave.ai/assets/images/screens/screen-1.png" />
113
114    <!-- Favicon -->
115    <link rel="icon" href="/assets/images/icon.png?v=2" type="image/png" />
116
117    <!-- Font: Plus Jakarta Sans (Workforce Intelligence theme) -->
118    <link rel="preconnect" href="https://fonts.googleapis.com" />
119    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
120    <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
121    <!-- Three.js import map — resolves bare specifiers for the 3D graph -->
122    
122<script type="importmap">
123        {
124      "imports": {
125        "three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
126        "three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/"
127      }
128    }
129  </script>
129
130    <!-- Stylesheets -->
131    <link rel="stylesheet" href="/assets/css/plugins.css" />
132    <link rel="stylesheet" href="/assets/css/style.css" />
133    <link rel="stylesheet" href="/assets/css/theme-redesign.css" />
134    <!-- Font Awesome -->
135    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" />
136</head>
137
138<body class="theme-wi">
139    <header class="site-header" id="site-header">
140        <div class="header-inner">
141            <a href="/" class="logo">
142                <img src="/assets/images/logo.png" alt="AlignWave" />
143            </a>
144            <nav class="nav-links" aria-label="Main navigation">
145                <a href="/#assessment">How it works</a>
146                <a href="/#report-preview">The Result</a>
147                <a href="/team">Team</a>
148                <a href="/app">Login</a>
149                <a href="/app/register" class="btn-cta">Get Started</a>
150            </nav>
151            <button type="button" class="header-hamburger" aria-label="Open menu" aria-expanded="false" aria-controls="mobile-menu" id="header-hamburger-btn">
152                <span class="hamburger-line"></span>
153                <span class="hamburger-line"></span>
154                <span class="hamburger-line"></span>
155            </button>
156        </div>
157        <div class="mobile-menu-overlay" id="mobile-menu-overlay" aria-hidden="true"></div>
158        <div class="mobile-menu" id="mobile-menu" aria-label="Mobile navigation" aria-hidden="true">
159            <a href="/#assessment" class="mobile-menu-link">How it works</a>
160            <a href="/#report-preview" class="mobile-menu-link">The Result</a>
161            <a href="/team" class="mobile-menu-link">Team</a>
162            <a href="/app" class="mobile-menu-link">Login</a>
163            <a href="/app/register" class="mobile-menu-link mobile-menu-cta">Get Started</a>
164        </div>
165    </header>
166    
166<script>
167        (function() {
168            var header = document.getElementById('site-header');
169            var btn = document.getElementById('header-hamburger-btn');
170            var overlay = document.getElementById('mobile-menu-overlay');
171            var menu = document.getElementById('mobile-menu');
172            if (!header || !btn || !overlay || !menu) return;
173
174            function openMenu() {
175                header.classList.add('menu-open');
176                btn.setAttribute('aria-expanded', 'true');
177                btn.setAttribute('aria-label', 'Close menu');
178                overlay.setAttribute('aria-hidden', 'false');
179                menu.setAttribute('aria-hidden', 'false');
180                document.body.style.overflow = 'hidden';
181            }
182
183            function closeMenu() {
184                header.classList.remove('menu-open');
185                btn.setAttribute('aria-expanded', 'false');
186                btn.setAttribute('aria-label', 'Open menu');
187                overlay.setAttribute('aria-hidden', 'true');
188                menu.setAttribute('aria-hidden', 'true');
189                document.body.style.overflow = '';
190            }
191
192            function toggleMenu() {
193                if (header.classList.contains('menu-open')) closeMenu();
194                else openMenu();
195            }
196            btn.addEventListener('click', toggleMenu);
197            overlay.addEventListener('click', closeMenu);
198            menu.querySelectorAll('.mobile-menu-link').forEach(function(link) {
199                link.addEventListener('click', closeMenu);
200            });
201        })();
202    </script>
202
203
204
205    <main>
206        <section class="hero">
207            <!--
208  <div class="hero-video" aria-hidden="true">
209    <video autoplay muted loop playsinline poster="/assets/video/background.jpg">
210      <source src="/assets/video/background.webm" type="video/webm" />
211    </video>
212  </div>
213  -->
214            <canvas id="hero-wave-canvas" class="hero-canvas" aria-hidden="true"></canvas>
215            <div class="hero-bg" aria-hidden="true"></div>
216            <div class="hero-inner">
217                <h1 class="hero-badge">Workforce Intelligence</h1>
218                <h2>People and companies have patterns &mdash; let's align them.</h2>
219                <h3 class="subline">
220                    AlignWave is a workforce intelligence platform that connects the signals that emerge from your people and your organization&mdash;uncovering
221                    patterns that improve how you hire, market, train, and manage quality.
222                </h3>
223                <a href="#assessment" class="btn-cta hero-scroll-cta" id="hero-scroll-btn">
224                    What Is Workforce Intelligence? &darr;
225                </a>
226                
226<script>
227                    (function() {
228                        var btn = document.getElementById('hero-scroll-btn');
229                        if (!btn) return;
230                        btn.addEventListener('click', function(e) {
231                            e.preventDefault();
232                            var target = document.getElementById('assessment');
233                            if (target) target.scrollIntoView({
234                                behavior: 'smooth'
235                            });
236                        });
237                    })();
238                </script>
238
239            </div>
240            
240<script>
241                (function() {
242                    var canvas = document.getElementById('hero-wave-canvas');
243                    if (!canvas) return;
244                    var ctx = canvas.getContext('2d');
245                    var W = 0,
246                        H = 0;
247
248                    function resize() {
249                        W = canvas.width = canvas.offsetWidth;
250                        H = canvas.height = canvas.offsetHeight;
251                    }
252                    window.addEventListener('resize', resize);
253                    resize();
254
255                    // === World / grid ===
256                    var COLS = 64;
257                    var ROWS = 48;
258                    var GRID_W = 4200;
259                    var Z_NEAR = 230;
260                    var Z_FAR = 2700;
261                    var Z_RANGE = Z_FAR - Z_NEAR;
262
263                    // Very slow scroll — matches original video's gentle drift
264                    var SCROLL_SPEED = 35;
265                    var FOG_START = 480;
266                    var FOG_END = 2200;
267
268                    // Camera just above water; horizon near vertical center
269                    var FOV = 300;
270                    var CAM_Y = 22;
271                    var HORIZON = 0.47;
272
273                    // Gentle, slow-rolling waves — 4 superimposed components for variety
274                    var AMP = 130;
275                    var AMP_RANGE = AMP * 1.6;
276
277                    function waveH(wx, wz, t) {
278                        return AMP * Math.sin(wx * 0.0090 + wz * 0.013 + t * 0.062) +
279                            AMP * 0.38 * Math.sin(-wx * 0.016 + wz * 0.008 + t * 0.042) +
280                            AMP * 0.22 * Math.sin(wx * 0.006 - wz * 0.018 + t * 0.027) +
281                            AMP * 0.12 * Math.sin(wx * 0.022 + wz * 0.005 + t * 0.019);
282                    }
283
284                    // === Particle grid — z scrolls toward viewer each frame and wraps ===
285                    var particles = [];
286                    for (var r = 0; r < ROWS; r++) {
287                        for (var c = 0; c < COLS; c++) {
288                            particles.push({
289                                wx: (c / (COLS - 1) - 0.5) * GRID_W,
290                                wz: Z_NEAR + (r / (ROWS - 1)) * Z_RANGE,
291                                row: r,
292                                col: c
293                            });
294                        }
295                    }
296
297                    // Row/col lookup for drawing connections
298                    var pIdx = {};
299                    for (var i = 0; i < particles.length; i++) {
300                        pIdx[particles[i].row + '_' + particles[i].col] = i;
301                    }
302
303                    function projPt(wx, wy, wz) {
304                        var sc = FOV / wz;
305                        return {
306                            x: wx * sc + W * 0.5,
307                            y: (CAM_Y - wy) * sc + H * HORIZON,
308                            sc: sc
309                        };
310                    }
311
312                    function getFog(wz) {
313                        if (wz <= FOG_START) return 1;
314                        if (wz >= FOG_END) return 0;
315                        return (FOG_END - wz) / (FOG_END - FOG_START);
316                    }
317
318                    // === Slice events ===
319                    // Each slice = thin z-band covering 30-65% of x, showing the wave cross-section in blue.
320                    // 4 depth zones each run their own independent timer — guarantees constant spread,
321                    // no gaps and no clustering.
322                    var SLICE_THICK = 65;
323                    var slices = [];
324
325                    var ZONES = [{
326                            zMin: 270,
327                            zMax: 540
328                        }, // very near — large, prominent
329                        {
330                            zMin: 540,
331                            zMax: 900
332                        }, // near-mid
333                        {
334                            zMin: 900,
335                            zMax: 1320
336                        }, // mid
337                        {
338                            zMin: 1320,
339                            zMax: 1720
340                        }, // mid-far
341                    ];
342                    // Independent timer per zone (ms). Stagger so they don't all fire at once.
343                    var zTimers = [1600, 400, 2800, 1100];
344                    var zIntervals = [3200, 3600, 3000, 3800]; // initial intervals (randomised after first fire)
345
346                    function spawnSliceInZone(zone) {
347                        var coverage = 0.30 + Math.random() * 0.35;
348                        var halfW = coverage * GRID_W * 0.5;
349                        var cx = (Math.random() - 0.5) * GRID_W * 0.52;
350                        var wz = zone.zMin + Math.random() * (zone.zMax - zone.zMin);
351                        slices.push({
352                            wz: wz,
353                            x0: cx - halfW,
354                            x1: cx + halfW,
355                            life: 0,
356                            maxLife: 2200 + Math.random() * 1600, // 2.2-3.8s
357                            alpha: 0
358                        });
359                    }
360
361                    // Pre-seed one slice per zone with staggered lifetimes so we're never empty at start
362                    for (var i = 0; i < ZONES.length; i++) {
363                        spawnSliceInZone(ZONES[i]);
364                        slices[i].life = i * 900; // stagger: 0s, 0.9s, 1.8s, 2.7s into their lives
365                    }
366
367                    // === Draw loop ===
368                    var lastT = 0;
369                    var animId;
370
371                    function draw(ts) {
372                        var t = ts * 0.001;
373                        var dt = Math.min(t - lastT, 0.05);
374                        lastT = t;
375                        ctx.clearRect(0, 0, W, H);
376
377                        // Scroll particles toward viewer; wrap at the near clipping plane
378                        for (var i = 0; i < particles.length; i++) {
379                            particles[i].wz -= SCROLL_SPEED * dt;
380                            if (particles[i].wz < Z_NEAR) particles[i].wz += Z_RANGE;
381                        }
382
383                        // Tick each zone's independent timer; spawn when due
384                        for (var z = 0; z < ZONES.length; z++) {
385                            zTimers[z] += dt * 1000;
386                            if (zTimers[z] >= zIntervals[z]) {
387                                spawnSliceInZone(ZONES[z]);
388                                zTimers[z] = 0;
389                                zIntervals[z] = 2800 + Math.random() * 2000; // 2.8-4.8s between repeats
390                            }
391                        }
392
393                        // Update slice lifetimes and alpha; remove expired ones
394                        for (var i = slices.length - 1; i >= 0; i--) {
395                            var sl = slices[i];
396                            sl.wz -= SCROLL_SPEED * dt;
397                            sl.life += dt * 1000;
398                            var fade = 480;
399                            sl.alpha = sl.life < fade ? sl.life / fade :
400                                sl.life > sl.maxLife - fade ? Math.max(0, (sl.maxLife - sl.life) / fade) :
401                                1;
402                            if (sl.life >= sl.maxLife || sl.wz < Z_NEAR - 50) slices.splice(i, 1);
403                        }
404
405                        // Compute per-particle state (wave height, projection, fog, slice membership)
406                        var st = new Array(particles.length);
407                        for (var i = 0; i < particles.length; i++) {
408                            var p = particles[i];
409                            var fv = getFog(p.wz);
410                            var h = fv > 0.005 ? waveH(p.wx, p.wz, t) : 0;
411                            var pr = projPt(p.wx, h, p.wz);
412                            var nh = Math.max(0, Math.min(1, (h + AMP_RANGE) / (AMP_RANGE * 2)));
413
414                            // Find the strongest slice this particle belongs to
415                            var ss = 0;
416                            for (var j = 0; j < slices.length; j++) {
417                                var sl = slices[j];
418                                var dz = Math.abs(p.wz - sl.wz);
419                                if (dz > SLICE_THICK || p.wx < sl.x0 || p.wx > sl.x1) continue;
420                                var str = (1 - dz / SLICE_THICK) * sl.alpha;
421                                if (str > ss) ss = str;
422                            }
423                            st[i] = {
424                                pr: pr,
425                                fv: fv,
426                                nh: nh,
427                                ss: ss
428                            };
429                        }
430
431                        // --- Smooth spline through each highlighted row in each slice ---
432                        // Collects all lit points in a row, then draws one quadratic Bezier curve
433                        // through them — no jagged segments, just a flowing wave-profile line.
434                        ctx.lineWidth = 0.9;
435                        for (var j = 0; j < slices.length; j++) {
436                            var sl = slices[j];
437                            if (sl.alpha < 0.02) continue;
438
439                            for (var r = 0; r < ROWS; r++) {
440                                // All particles in a row share the same wz (they scroll in lockstep)
441                                var rowWz = particles[r * COLS].wz;
442                                var dz = Math.abs(rowWz - sl.wz);
443                                if (dz > SLICE_THICK) continue;
444
445                                var slStr = (1 - dz / SLICE_THICK) * sl.alpha;
446                                var rowFog = getFog(rowWz);
447                                if (rowFog < 0.05) continue;
448
449                                // Collect projected positions for particles within the slice's x-range
450                                var pts = [];
451                                for (var c = 0; c < COLS; c++) {
452                                    var idx = r * COLS + c;
453                                    var p = particles[idx];
454                                    if (p.wx < sl.x0 || p.wx > sl.x1) continue;
455                                    pts.push({
456                                        x: st[idx].pr.x,
457                                        y: st[idx].pr.y,
458                                        nh: st[idx].nh
459                                    });
460                                }
461                                if (pts.length < 2) continue;
462
463                                // Derive line color from average wave height across the row segment
464                                var avgNh = 0;
465                                for (var k = 0; k < pts.length; k++) avgNh += pts[k].nh;
466                                avgNh /= pts.length;
467                                var rr = Math.round(147 - avgNh * 118);
468                                var gg = Math.round(197 - avgNh * 119);
469                                var bb = Math.round(253 - avgNh * 37);
470                                var la = slStr * rowFog * 0.62;
471
472                                // Catmull-Rom spline converted to cubic Bezier segments.
473                                // Unlike quadratic Bezier, this passes exactly through every point.
474                                ctx.beginPath();
475                                ctx.moveTo(pts[0].x, pts[0].y);
476                                if (pts.length === 2) {
477                                    ctx.lineTo(pts[1].x, pts[1].y);
478                                } else {
479                                    for (var k = 0; k < pts.length - 1; k++) {
480                                        var p0 = pts[k > 0 ? k - 1 : 0];
481                                        var p1 = pts[k];
482                                        var p2 = pts[k + 1];
483                                        var p3 = pts[k + 2 < pts.length ? k + 2 : pts.length - 1];
484                                        ctx.bezierCurveTo(
485                                            p1.x + (p2.x - p0.x) / 6, p1.y + (p2.y - p0.y) / 6,
486                                            p2.x - (p3.x - p1.x) / 6, p2.y - (p3.y - p1.y) / 6,
487                                            p2.x, p2.y
488                                        );
489                                    }
490                                }
491                                ctx.strokeStyle = 'rgba(' + rr + ',' + gg + ',' + bb + ',' + la.toFixed(3) + ')';
492                                ctx.stroke();
493                            }
494                        }
495
496                        // --- Draw particles (far-to-near via row order: row 0 = Z_NEAR) ---
497                        // Iterate rows from ROWS-1 (far) to 0 (near) for painter's order
498                        var rowOrder = [];
499                        for (var r = ROWS - 1; r >= 0; r--) rowOrder.push(r);
500
501                        for (var ri = 0; ri < rowOrder.length; ri++) {
502                            var r = rowOrder[ri];
503                            for (var c = 0; c < COLS; c++) {
504                                var idx = r * COLS + c;
505                                var s = st[idx];
506                                var pr = s.pr;
507                                if (s.fv < 0.005) continue;
508                                if (pr.x < -60 || pr.x > W + 60 || pr.y < -90 || pr.y > H + 90) continue;
509
510                                var radius = Math.max(0.20, Math.min(1.8, (0.32 + s.nh * 0.50) * pr.sc * 4.2));
511
512                                if (s.ss > 0.01) {
513                                    // Blue cross-section: #93c5fd (light) at trough → #1d4ed8 (darker blue) at crest
514                                    var bA = s.fv * s.ss * (0.45 + s.nh * 0.55);
515                                    var rr = Math.round(147 - s.nh * 118); // 147→29
516                                    var gg = Math.round(197 - s.nh * 119); // 197→78
517                                    var bb = Math.round(253 - s.nh * 37); // 253→216
518                                    if (s.ss > 0.45 && s.nh > 0.50) {
519                                        ctx.shadowColor = 'rgba(' + rr + ',' + gg + ',' + bb + ',0.35)';
520                                        ctx.shadowBlur = radius * 1.8;
521                                    } else {
522                                        ctx.shadowBlur = 0;
523                                    }
524                                    ctx.beginPath();
525                                    ctx.arc(pr.x, pr.y, radius * (1 + s.ss * 0.30), 0, 6.2832);
526                                    ctx.fillStyle = 'rgba(' + rr + ',' + gg + ',' + bb + ',' + bA.toFixed(3) + ')';
527                                    ctx.fill();
528                                } else {
529                                    // White base — barely visible, depth-driven opacity
530                                    var wA = s.fv * (0.07 + s.nh * 0.20);
531                                    ctx.shadowBlur = 0;
532                                    ctx.beginPath();
533                                    ctx.arc(pr.x, pr.y, radius, 0, 6.2832);
534                                    ctx.fillStyle = 'rgba(255,255,255,' + Math.min(0.42, wA).toFixed(3) + ')';
535                                    ctx.fill();
536                                }
537                            }
538                        }
539
540                        ctx.shadowBlur = 0;
541                        animId = requestAnimationFrame(draw);
542                    }
543
544                    animId = requestAnimationFrame(draw);
545
546                    document.addEventListener('visibilitychange', function() {
547                        if (document.hidden) cancelAnimationFrame(animId);
548                        else animId = requestAnimationFrame(draw);
549                    });
550                })();
551            </script>
551
552        </section>
553
554        <section class="section assessment-section" id="assessment">
555            <div class="section-inner">
556                <div class="assessment-split">
557                    <div class="assessment-copy" data-aos="fade-right" data-aos-duration="700">
558                        <div class="section-label">What Is Workforce Intelligence?</div>
559                        <h2 class="section-title">Questions that uncover patterns.</h2>
560                        <p class="section-desc">
561                            Workforce intelligence starts with your own people's data, not generic industry benchmarks&mdash;and it starts with the assessment. Every question is purpose-built to reveal two things: behavioral fit and functional experience. In under 10 minutes, candidates surface the instincts, working habits, and role-specific judgment your top performers share&mdash;things a r&eacute;sum&eacute; will never show you. What comes next is the insight: a full report, scored against your own best performers.
562                        </p>
563                        <ul class="assessment-bullet-list">
564                            <li>
565                                <span class="bullet-dot"></span>
566                                Quick, focused questions built around your actual role
567                            </li>
568                            <li>
569                                <span class="bullet-dot"></span>
570                                Measures real behaviors: judgment, safety instincts, adaptability under pressure
571                            </li>
572                            <li>
573                                <span class="bullet-dot"></span>
574                                Mobile-friendly—candidates complete it from any device, anywhere
575                            </li>
576                            <li>
577                                <span class="bullet-dot"></span>
578                                No specialized training needed for your team to run it
579                            </li>
580                        </ul>
581                    </div>
582                    <div class="assessment-card" data-aos="fade-left" data-aos-duration="700" data-aos-delay="100">
583
584                        <!-- Meanwhile overlay -->
585                        <div class="acard-meanwhile">
586                            <div class="acard-meanwhile-inner">
587                                <div class="acard-meanwhile-spinner"></div>
588                                <p>Meanwhile, on your hiring team&rsquo;s screen&hellip;</p>
589                            </div>
590                        </div>
591
592                        <!-- State 1: Candidate Assessment (18 of 18) -->
593                        <div class="acard-state acard-state--candidate is-active">
594                            <div class="acard-header">
595                                <span class="acard-progress-text">Question 18 of 18</span>
596                                <div class="acard-progress-bar">
597                                    <div class="acard-progress-fill" style="width:100%"></div>
598                                </div>
599                            </div>
600                            <p class="acard-question">When you receive an unexpected change to your schedule, how do you typically respond?</p>
601                            <ul class="acard-options">
602                                <li class="acard-option">
603                                    <span class="acard-dot"></span>
604                                    <span>I review the new plan carefully before making any moves</span>
605                                </li>
606                                <li class="acard-option selected">
607                                    <span class="acard-dot"></span>
608                                    <span>I adapt quickly and handle it as the situation unfolds</span>
609                                </li>
610                                <li class="acard-option">
611                                    <span class="acard-dot"></span>
612                                    <span>I confirm with my supervisor to understand the new expectations</span>
613                                </li>
614                                <li class="acard-option">
615                                    <span class="acard-dot"></span>
616                                    <span>I finish my current task first, then address the change</span>
617                                </li>
618                            </ul>
619                            <button class="acard-next btn-cta acard-submit" type="button">Submit Assessment &rarr;</button>
620                            <div class="acard-hint" aria-hidden="true">
621                                <svg class="acard-hint-arrow" viewBox="0 0 46 46" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
622                                    <path d="M 40 40 C 32 28, 22 20, 8 8" />
623                                    <polyline points="8,20 8,8 20,8" />
624                                </svg>
625                                <span class="acard-hint-text">Try it out</span>
626                            </div>
627                        </div>
628
629                        <!-- State 2: Admin Inbox -->
630                        <div class="acard-state acard-state--admin">
631                            <div class="acard-admin-header">
632                                <div class="acard-admin-title-row">
633                                    <span class="acard-admin-title">Submissions</span>
634                                    <span class="acard-admin-badge">&#9679; 1 New</span>
635                                </div>
636                                <div class="acard-admin-role">OTR Driver — Southeast Region</div>
637                            </div>
638                            <div class="acard-admin-list">
639                                <div class="acard-admin-row acard-admin-row--read">
640                                    <div class="acard-admin-avatar">MT</div>
641                                    <div class="acard-admin-info">
642                                        <span class="acard-admin-name">Marcus T.</span>
643                                        <span class="acard-admin-meta">2 days ago &middot; Reviewed</span>
644                                    </div>
645                                </div>
646                                <div class="acard-admin-row acard-admin-row--read">
647                                    <div class="acard-admin-avatar">JL</div>
648                                    <div class="acard-admin-info">
649                                        <span class="acard-admin-name">Jordan L.</span>
650                                        <span class="acard-admin-meta">5 days ago &middot; Reviewed</span>
651                                    </div>
652                                </div>
653                                <div class="acard-admin-row acard-admin-row--new">
654                                    <div class="acard-admin-avatar">AR</div>
655                                    <div class="acard-admin-info">
656                                        <span class="acard-admin-name">Alex R.</span>
657                                        <span class="acard-admin-meta acard-admin-meta--time">Just now</span>
658                                    </div>
659                                    <span class="acard-new-badge">NEW</span>
660                                    <button class="acard-view-report" type="button">View Report &rarr;</button>
661                                </div>
662                            </div>
663                            <div class="acard-hint acard-admin-hint" aria-hidden="true">
664                                <span class="acard-hint-text">click here</span>
665                                <svg class="acard-hint-arrow" viewB
665ox="0 0 46 46" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
666                                    <path d="M 6 40 C 14 28, 24 20, 38 8" />
667                                    <polyline points="38,20 38,8 26,8" />
668                                </svg>
669                            </div>
670                        </div>
671
672                        <!-- State 3: Report -->
673                        <div class="acard-state acard-state--report">
674                            <div class="acard-report-top">
675                                <div class="acard-report-avatar">AR</div>
676                                <div class="acard-report-meta">
677                                    <span class="acard-report-name">Alex R.</span>
678                                    <span class="acard-report-role">OTR Driver &mdash; Southeast Region &middot; Just now</span>
679                                </div>
680                            </div>
681                            <div class="acard-retention">
682                                <div class="acard-retention-stat">
683                                    <span class="acard-retention-pct">89%</span>
684                                    <span class="acard-retention-lbl">90-Day Retention</span>
685                                </div>
686                                <div class="acard-retention-divider"></div>
687                                <div class="acard-retention-stat">
688                                    <span class="acard-retention-pct">74%</span>
689                                    <span class="acard-retention-lbl">1-Year Retention</span>
690                                </div>
691                            </div>
692                            <div class="acard-chips">
693                                <span class="acard-chip">Reliable</span>
694                                <span class="acard-chip">Safety-Aware</span>
695                                <span class="acard-chip">Steady Under Pressure</span>
696                            </div>
697                            <div class="acard-bars">
698                                <div class="acard-bar-row">
699                                    <span class="acard-bar-label">Reliability</span>
700                                    <div class="acard-bar-track">
701                                        <div class="acard-bar-fill acard-bar-fill--70"></div>
702                                    </div>
703                                    <span class="acard-bar-pct">70%</span>
704                                </div>
705                                <div class="acard-bar-row">
706                                    <span class="acard-bar-label">Composure</span>
707                                    <div class="acard-bar-track">
708                                        <div class="acard-bar-fill acard-bar-fill--92"></div>
709                                    </div>
710                                    <span class="acard-bar-pct">92%</span>
711                                </div>
712                                <div class="acard-bar-row">
713                                    <span class="acard-bar-label">Adaptability</span>
714                                    <div class="acard-bar-track">
715                                        <div class="acard-bar-fill acard-bar-fill--88"></div>
716                                    </div>
717                                    <span class="acard-bar-pct">88%</span>
718                                </div>
719                                <div class="acard-bars-fade">
720                                    <div class="acard-bar-row">
721                                        <span class="acard-bar-label">Safety</span>
722                                        <div class="acard-bar-track">
723                                            <div class="acard-bar-fill acard-bar-fill--81"></div>
724                                        </div>
725                                        <span class="acard-bar-pct">81%</span>
726                                    </div>
727                                    <div class="acard-bar-row">
728                                        <span class="acard-bar-label">Judgment</span>
729                                        <div class="acard-bar-track">
730                                            <div class="acard-bar-fill acard-bar-fill--76"></div>
731                                        </div>
732                                        <span class="acard-bar-pct">76%</span>
733                                    </div>
734                                </div>
735                            </div>
736                            <div class="acard-chat">
737                                <div class="acard-chat-header">
738                                    <svg class="acard-chat-icon" viewBox="0 0 16 16" fill="none" aria-hidden="true">
739                                        <path d="M8 1l1.5 3 3.5.5-2.5 2.5.5 3.5L8 9l-3 1.5.5-3.5L3 4.5 6.5 4 8 1z" fill="#2563eb" />
740                                    </svg>
741                                    <span class="acard-chat-title">AI Insights</span>
742                                </div>
743                                <div class="acard-chat-messages">
744                                    <div class="acard-msg acard-msg--ai">
745                                        Composure and adaptability are strong retention signals. Reliability at 70% is trainable &mdash; three of your top drivers showed the same profile at hire.
746                                    </div>
747                                    <div class="acard-msg acard-msg--user">
748                                        Are there similar drivers already on the team?
749                                    </div>
750                                </div>
751                            </div>
752                            <div class="acard-cta-sep"></div>
753                            <a href="/app/register" class="acard-next btn-cta acard-demo-cta">See this in your dashboard &rarr;</a>
754                            <div class="acard-hint acard-report-hint" aria-hidden="true">
755                                <svg class="acard-hint-arrow" viewB
755ox="0 0 46 46" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
756                                    <path d="M 40 40 C 32 28, 22 20, 8 8" />
757                                    <polyline points="8,20 8,8 20,8" />
758                                </svg>
759                                <span class="acard-hint-text">Give it a try</span>
760                            </div>
761                            <button class="acard-replay" type="button">&#8635; Replay</button>
762                        </div>
763
764                    </div>
765                </div>
766            </div>
767        </section>
768
769        <section class="section report-preview-section" id="report-preview">
770            <div class="section-inner">
771                <div class="section-label">The Result</div>
772                <h2 class="section-title">Every report is workforce intelligence in action.</h2>
773                <p class="section-desc">
774                    The assessment is the first step. What comes back is the real value: retention odds, behavioral fit, and culture alignment&mdash;scored against the people already succeeding on your team, not generic industry benchmarks. This is the intelligence behind every hiring decision.
775                </p>
776                <div class="rpreview-grid">
777
778                    <!-- ROW 1: Progress bar outputs -->
779
780                    <div class="rpreview-card rpreview-card--featured" data-aos="fade-up" data-aos-duration="600" data-aos-delay="0">
781                        <div class="rpreview-card-header">
782                            <div class="rpreview-icon">
783                                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
784                                    <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
785                                    <polyline points="9 12 11 14 15 10" />
786                                </svg>
787                            </div>
788                            <h3>Retention Prediction</h3>
789                        </div>
790                        <p>AI-modeled probabilities built from similar employees in your own workforce. Know who&rsquo;s built to stay before day one.</p>
791                        <div class="rpreview-example">
792                            <div class="rpex-bar-row">
793                                <span class="rpex-bar-label">90-Day</span>
794                                <div class="rpex-bar-track">
795                                    <div class="rpex-bar-fill rpex-bar-fill--blue" style="width:89%"></div>
796                                </div>
797                                <span class="rpex-bar-value">89%</span>
798                            </div>
799                            <div class="rpex-bar-row">
800                                <span class="rpex-bar-label">1-Year</span>
801                                <div class="rpex-bar-track">
802                                    <div class="rpex-bar-fill" style="width:74%"></div>
803                                </div>
804                                <span class="rpex-bar-value">74%</span>
805                            </div>
806                            <div class="rpex-footnote">Est. tenure &nbsp;&middot;&nbsp; ~34 months</div>
807                        </div>
808                    </div>
809
810                    <div class="rpreview-card rpreview-card--featured" data-aos="fade-up" data-aos-duration="600" data-aos-delay="80">
811                        <div class="rpreview-card-header">
812                            <div class="rpreview-icon">
813                                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
814                                    <line x1="18" y1="20" x2="18" y2="10" />
815                                    <line x1="12" y1="20" x2="12" y2="4" />
816                                    <line x1="6" y1="20" x2="6" y2="14" />
817                                    <line x1="2" y1="20" x2="22" y2="20" />
818                                </svg>
819                            </div>
820                            <h3>Behavioral Profile</h3>
821                        </div>
822                        <p>8 dimensions scored across personal and professional context&mdash;from leadership drive to adaptability under pressure.</p>
823                        <div class="rpreview-example">
824                            <div class="rpex-dim-row">
825                                <span class="rpex-dim-label">Consistency &amp; Reliability</span>
826                                <div class="rpex-bar-track">
827                                    <div class="rpex-bar-fill rpex-bar-fill--dim-high" style="width:82%"></div>
828                                </div>
829                                <span class="rpex-dim-score">8.2</span>
830                            </div>
831                            <div class="rpex-dim-row">
832                                <span class="rpex-dim-label">Adaptability &amp; Change</span>
833                                <div class="rpex-bar-track">
834                                    <div class="rpex-bar-fill rpex-bar-fill--dim-low" style="width:69%"></div>
835                                </div>
836                                <span class="rpex-dim-score">6.9</span>
837                            </div>
838                            <div class="rpex-footnote rpex-footnote--top">+ 6 more dimensions scored</div>
839                        </div>
840                    </div>
841
842                    <div class="rpreview-card rpreview-card--featured" data-aos="fade-up" data-aos-duration="600" data-aos-delay="160">
843                        <div class="rpreview-card-header">
844                            <div class="rpreview-icon">
845                                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
846                                    <circle cx="12" cy="12" r="10" />
847                                    <circle cx="12" cy="12" r="6" />
848                                    <circle cx="12" cy="12" r="2" />
849                                </svg>
850                            </div>
851                            <h3>Role &amp; Culture Fit</h3>
852                        </div>
853                        <p>Two separate scores comparing the candidate against your actual workforce&mdash;not generic benchmarks.</p>
854                        <div class="rpreview-example">
855                            <div class="rpex-bar-row">
856                                <span class="rpex-bar-label">Role Fit</span>
857                                <div class="rpex-bar-track">
858                                    <div class="rpex-bar-fill rpex-bar-fill--blue" style="width:84%"></div>
859                                </div>
860                                <span class="rpex-bar-value">84%</span>
861                            </div>
862                            <div class="rpex-bar-row">
863                                <span class="rpex-bar-label">Culture Fit</span>
864                                <div class="rpex-bar-track">
865                                    <div class="rpex-bar-fill" style="width:78%"></div>
866                                </div>
867                                <span class="rpex-bar-value">78%</span>
868                            </div>
869                            <div class="rpex-footnote">Compared against 24 active employees</div>
870                        </div>
871                    </div>
872
873                    <!-- ROW 2 -->
874
875                    <div class="rpreview-card" data-aos="fade-up" data-aos-duration="600" data-aos-delay="0">
876                        <div class="rpreview-card-header">
877                            <div class="rpreview-icon">
878                                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
879                                    <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
880                                    <circle cx="12" cy="7" r="4" />
881                                </svg>
882                            </div>
883                            <h3>Social Preference</h3>
884                        </div>
885                        <p>How the candidate shows up at work, makes decisions, and relates to others&mdash;mapped to a profile type that predicts cultural alignment.</p>
886                        <div class="rpreview-example">
887                            <div class="rpex-archetype-row">
888                                <span class="rpex-archetype-tag rpex-archetype-tag--primary">Primary</span>
889                                <span class="rpex-archetype-name">Leader</span>
890                            </div>
891                            <p class="rpex-archetype-desc">Driven, accountable, and self-directed. Takes ownership and sets the pace for those around them.</p>
892                            <div class="rpreview-chips">
893                                <span class="rpreview-chip">Adaptable</span>
894                                <span class="rpreview-chip">Dedicated</span>
895                                <span class="rpreview-chip">Self-Starter</span>
896                                <span class="rpreview-chip">Accountable</span>
897                                <span class="rpreview-chip">Resilient</span>
898                            </div>
899                        </div>
900                    </div>
901
902                    <div class="rpreview-card" data-aos="fade-up" data-aos-duration="600" data-aos-delay="80">
903                        <div class="rpreview-card-header">
904                            <div class="rpreview-icon">
905                                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
906                                    <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
907                                    <polyline points="14 2 14 8 20 8" />
908                                    <line x1="16" y1="13" x2="8" y2="13" />
909                                    <line x1="16" y1="17" x2="8" y2="17" />
910                                </svg>
911                            </div>
912                            <h3>Summary</h3>
913                        </div>
914                        <p>A written narrative your team can act on: strengths, development areas, and where personal and professional patterns diverge.</p>
915                        <div class="rpreview-example rpreview-example--quote">
916                            <p class="rpex-quote">&ldquo;Strong leadership instincts and steady composure align well with your top-retaining drivers. Low early-exit risk across the board. Communication is a development area&mdash;set clear expectations early and this candidate should onboard smoothly.&rdquo;</p>
917                        </div>
918                    </div>
919
920                    <div class="rpreview-card" data-aos="fade-up" data-aos-duration="600" data-aos-delay="160">
921                        <div class="rpreview-card-header">
922                            <div class="rpreview-icon">
923                                <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
924                                    <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
925                                </svg>
926                            </div>
927                            <h3>AI Chat</h3>
928                        </div>
929                        <p>The system surfaces what it finds&mdash;then you ask. Explore patterns, flag training gaps, or query your workforce.</p>
930                        <div class="rpreview-example rpex-chat">
931                            <div class="rpex-bubble-row">
932                                <div class="rpex-avatar"><img src="/assets/images/icon.png" alt="" /></div>
933                                <div class="rpex-bubble rpex-bubble--system">Reliability is above 90%. Communication is slightly below your top drivers.</div>
934                            </div>
935                            <div class="rpex-bubble rpex-bubble--user">Can they be coached on that?</div>
936                            <div class="rpex-bubble-row">
937                                <div class="rpex-avatar"><img src="/assets/images/icon.png" alt="" /></div>
938                                <div class="rpex-bubble rpex-bubble--system">Yes&mdash;it&rsquo;s worked well with others. Set clear expectations early.</div>
939                            </div>
940                        </div>
941                    </div>
942
943                </div>
944            </div>
945        </section>
946
947        <section class="section turnover-section" id="turnover">
948            <div class="section-inner">
949                <div class="section-label">The Problem</div>
950                <h2 class="section-title">Driver turnover is your most expensive recurring cost.</h2>
951                <p class="section-desc">
952                    The logistics industry sees roughly 100% driver turnover every year. Most companies accept it as unavoidable. It isn&rsquo;t.
953                </p>
954
955                <div class="turnover-stat-row" data-aos="fade-up" data-aos-duration="600">
956                    <div class="turnover-stat">
957                        <span class="turnover-stat-value">92%</span>
958                        <span class="turnover-stat-label">Annual driver turnover<br>logistics industry average</span>
959                    </div>
960                    <div class="turnover-stat">
961                        <span class="turnover-stat-value">$8k+</span>
962                        <span class="turnover-stat-label">Average replacement cost<br>per driver hired</span>
963                    </div>
964                    <div class="turnover-stat">
965                        <span class="turnover-stat-value">3&ndash;4 wks</span>
966                        <span class="turnover-stat-label">Before a new hire<br>reaches full productivity</span>
967                    </div>
968                    <div class="turnover-stat">
969                        <span class="turnover-stat-value">40%+</span>
970                        <span class="turnover-stat-label">Of drivers who leave<br>do so within 90 days</span>
971                    </div>
972                </div>
973
974            </div>
975        </section>
976
977        <section class="section" id="features">
978            <div class="section-inner">
979                <div class="section-label">The Solution</div>
980                <div class="turnover-solve">
981                    <div class="turnover-solve-copy" data-aos="fade-right" data-aos-duration="600">
982                        <div class="turnover-copy-body">
983                            <h2 class="section-title">What you get with Workforce Intelligence</h2>
984                            <p>
985                                Turnover isn&rsquo;t random. The behavior, work habits, and quality that predict how long someone stays show up before day one. AlignWave surfaces them in a 10-minute assessment.
986                            </p>
987                            <p>
988                                Every report is measured against your own workforce, not an industry average. The more you use it, the better it understands what works for your company&mdash;putting things in your favor on every hire you make.
989                            </p>
990                        </div>
991                        <a href="/app/register" class="btn-ghost-dark">Start my first assessment &rarr;</a>
992                    </div>
993                    <div class="turnover-solve-features" data-aos="fade-left" data-aos-duration="600" data-aos-delay="100">
994                        <div class="turnover-solve-feature">
995                            <div class="tsf-dot"></div>
996                            <div class="turnover-solve-feature-text">
997                                <strong>90-day &amp; 1-year retention</strong>
998                                <span>See how likely someone is to stay, before they&rsquo;re ever on the job&mdash;not after turnover already cost you.</span>
999                            </div>
1000                        </div>
1001                        <div class="turnover-solve-feature">
1002                            <div class="tsf-dot"></div>
1003                            <div class="turnover-solve-feature-text">
1004                                <strong>Matched against your workforce</strong>
1005                                <span>Candidates are compared to the people already succeeding on your team&mdash;including how long they&rsquo;ve stayed&mdash;not a generic industry number.</span>
1006                            </div>
1007                        </div>
1008                        <div class="turnover-solve-feature">
1009                            <div class="tsf-dot"></div>
1010                            <div class="turnover-solve-feature-text">
1011                                <strong>Behavior, not just experience</strong>
1012                                <span>Years behind the wheel don&rsquo;t predict longevity. The habits and instincts that do&mdash;that&rsquo;s what we measure.</span>
1013                            </div>
1014                        </div>
1015                    </div>
1016                </div>
1017            </div>
1018        </section>
1019
1020        <section class="section" id="workforce-network">
1021            <div class="section-inner">
1022                <div class="network-split">
1023                    <div class="network-copy" data-aos="fade-right" data-aos-duration="700">
1024                        <div class="section-label">Graph3D</div>
1025                        <h2 class="section-title">
1026                            Your company has a story to tell
1027                        </h2>
1028                        <p class="section-desc">
1029                            Top performers glow at the center of their groups, connections show
1030                            who works well together, and work groups form naturally around
1031                            shared strengths. New candidates fit in among
1032                            them, and every connection says something about where the team is
1033                            headed. Some roles pull in the same direction&mdash;others stand
1034                            apart, and either one is worth understanding about how the company
1035                            actually works.
1036                        </p>
1037                        <ul class="network-bullets">
1038                            <li>
1039                                <span class="bullet-dot"></span>
1040                                Top performers naturally cluster together, revealing what success
1041                                looks like in each role
1042                            </li>
1043                            <li>
1044                                <span class="bullet-dot"></span>
1045                                New candidates are scored and placed against your actual
1046                                workforce—not generic benchmarks
1047                            </li>
1048                            <li>
1049                                <span class="bullet-dot"></span>
1050                                Identify those who need more development and build a stronger, more
1051                                cohesive team over time
1052                            </li>
1053                        </ul>
1054                    </div>
1055                    <div class="network-canvas-outer" data-aos="fade-left" data-aos-duration="700" data-aos-delay="100">
1056                        <div id="graph-tooltip" class="graph-tooltip">
1057                            <span class="gtt-name" id="gtt-name"></span>
1058                            <span class="gtt-role" id="gtt-role"></span>
1059                            <div class="gtt-sep"></div>
1060                            <div class="gtt-score-row">
1061                                <span class="gtt-score-label">Fit Score</span>
1062                                <div class="gtt-score-bar">
1063                                    <div class="gtt-score-fill" id="gtt-fill"></div>
1064                                </div>
1065                                <span class="gtt-score-val" id="gtt-val"></span>
1066                            </div>
1067                            <div class="gtt-meta-row">
1068                                <span class="gtt-tenure" id="gtt-tenure"></span>
1069                                <span class="gtt-dollar" id="gtt-dollar"></span>
1070                            </div>
1071                            <div class="gtt-conns" id="gtt-conns"></div>
1072                        </div>
1073                        <div class="network-canvas-wrap" id="graph-wrap">
1074                            <div class="graph-live-badge">
1075                                <span class="graph-live-dot"></span>Live
1076                            </div>
1077                            <div class="graph-hint-bar" id="graph-hint-bar">
1078                                Drag to rotate &nbsp;&bull;&nbsp; Scroll to zoom &nbsp;&bull;&nbsp;
1079                                Click node to inspect
1080                            </div>
1081                            <div class="graph-legend">
1082                                <span class="gleg gleg-all active">All</span>
1083                                <span class="gleg gleg-yellow">Top performers</span>
1084                                <span class="gleg gleg-blue">Strong matches</span>
1085                            </div>
1086                        </div>
1087                    </div>
1088                </div>
1089            </div>
1090        </section>
1091
1092        
1092<script type="module">
1093            import * as THREE from "three";
1094            import {
1095                OrbitControls
1096            } from "three/addons/controls/OrbitControls.js";
1097
1098            (function() {
1099                const wrap = document.getElementById("graph-wrap");
1100                if (!wrap) return;
1101
1102                function r(s) {
1103                    const x = Math.sin(s + 1.7) * 43758.5453;
1104                    return x - Math.floor(x);
1105                }
1106
1107                // ── Node dataset — 5 employees + 5 candidates per cluster (30 total, 50/50) ─
1108                const NODES = [
1109                    // ── Cluster 0: Top performers ──────────────────────────────────────────
1110                    {
1111                        name: "M. Torres",
1112                        role: "Senior Driver",
1113                        score: 91,
1114                        ci: 0,
1115                        tenure: 7.2,
1116                        dollar: 5,
1117                        isCandidate: false
1118                    },
1119                    {
1120                        name: "S. Kaur",
1121                        role: "Fleet Lead",
1122                        score: 88,
1123                        ci: 0,
1124                        tenure: 5.8,
1125                        dollar: 5,
1126                        isCandidate: false
1127                    },
1128                    {
1129                        name: "J. Rivera",
1130                        role: "Route Specialist",
1131                        score: 85,
1132                        ci: 0,
1133                        tenure: 4.1,
1134                        dollar: 4,
1135                        isCandidate: false
1136                    },
1137                    {
1138                        name: "P. Monroe",
1139                        role: "Senior Driver",
1140                        score: 82,
1141                        ci: 0,
1142                        tenure: 3.5,
1143                        dollar: 4,
1144                        isCandidate: false
1145                    },
1146                    {
1147                        name: "D. Walsh",
1148                        role: "Fleet Supervisor",
1149                        score: 79,
1150                        ci: 0,
1151                        tenure: 6.0,
1152                        dollar: 4,
1153                        isCandidate: false
1154                    },
1155                    {
1156                        name: "R. Chen",
1157                        role: "Candidate",
1158                        score: 90,
1159                        ci: 0,
1160                        tenure: 0,
1161                        dollar: 5,
1162                        isCandidate: true
1163                    },
1164                    {
1165                        name: "A. Davis",
1166                        role: "Candidate",
1167                        score: 86,
1168                        ci: 0,
1169                        tenure: 0,
1170                        dollar: 4,
1171                        isCandidate: true
1172                    },
1173                    {
1174                        name: "M. Park",
1175                        role: "Candidate",
1176                        score: 83,
1177                        ci: 0,
1178                        tenure: 0,
1179                        dollar: 4,
1180                        isCandidate: true
1181                    },
1182                    {
1183                        name: "T. Singh",
1184                        role: "Candidate",
1185                        score: 80,
1186                        ci: 0,
1187                        tenure: 0,
1188                        dollar: 4,
1189                        isCandidate: true
1190                    },
1191                    {
1192                        name: "W. Lee",
1193                        role: "Candidate",
1194                        score: 77,
1195                        ci: 0,
1196                        tenure: 0,
1197                        dollar: 4,
1198                        isCandidate: true
1199                    },
1200                    // ── Cluster 1: Strong matches ──────────────────────────────────────────
1201                    {
1202                        name: "C. Burns",
1203                        role: "Driver",
1204                        score: 72,
1205                        ci: 1,
1206                        tenure: 2.8,
1207                        dollar: 3,
1208                        isCandidate: false
1209                    },
1210                    {
1211                        name: "D. Lim",
1212                        role: "Driver",
1213                        score: 68,
1214                        ci: 1,
1215                        tenure: 1.5,
1216                        dollar: 3,
1217                        isCandidate: false
1218                    },
1219                    {
1220                        name: "N. Abbas",
1221                        role: "Fleet Coordinator",
1222                        score: 65,
1223                        ci: 1,
1224                        tenure: 2.1,
1225                        dollar: 3,
1226                        isCandidate: false
1227                    },
1228                    {
1229                        name: "E. Reyes",
1230                        role: "Driver",
1231                        score: 62,
1232                        ci: 1,
1233                        tenure: 1.8,
1234                        dollar: 2,
1235                        isCandidate: false
1236                    },
1237                    {
1238                        name: "F. Mills",
1239                        role: "Driver Trainee",
1240                        score: 59,
1241                        ci: 1,
1242                        tenure: 0.9,
1243                        dollar: 2,
1244                        isCandidate: false
1245                    },
1246                    {
1247                        name: "K. Martinez",
1248                        role: "Candidate",
1249                        score: 71,
1250                        ci: 1,
1251                        tenure: 0,
1252                        dollar: 3,
1253                        isCandidate: true
1254                    },
1255                    {
1256                        name: "B. Nguyen",
1257                        role: "Candidate",
1258                        score: 67,
1259                        ci: 1,
1260                        tenure: 0,
1261                        dollar: 3,
1262                        isCandidate: true
1263                    },
1264                    {
1265                        name: "T. Osei",
1266                        role: "Candidate",
1267                        score: 64,
1268                        ci: 1,
1269                        tenure: 0,
1270                        dollar: 2,
1271                        isCandidate: true
1272                    },
1273                    {
1274                        name: "U. Patel",
1275                        role: "Candidate",
1276                        score: 61,
1277                        ci: 1,
1278                        tenure: 0,
1279                        dollar: 2,
1280                        isCandidate: true
1281                    },
1282                    {
1283                        name: "V. Kim",
1284                        role: "Candidate",
1285                        score: 57,
1286                        ci: 1,
1287                        tenure: 0,
1288                        dollar: 2,
1289                        isCandidate: true
1290                    },
1291                    // ── Cluster 2: Developing ─────────────────────────────────────────────
1292                    {
1293                        name: "G. Abbott",
1294                        role: "Driver Trainee",
1295                        score: 44,
1296                        ci: 2,
1297                        tenure: 1.1,
1298                        dollar: 2,
1299                        isCandidate: false
1300                    },
1301                    {
1302                        name: "H. Santos",
1303                        role: "Driver Trainee",
1304                        score: 40,
1305                        ci: 2,
1306                        tenure: 0.8,
1307                        dollar: 1,
1308                        isCandidate: false
1309                    },
1310                    {
1311                        name: "I. Petrov",
1312                        role: "Driver",
1313                        score: 37,
1314                        ci: 2,
1315                        tenure: 0.5,
1316                        dollar: 1,
1317                        isCandidate: false
1318                    },
1319                    {
1320                        name: "J. Costa",
1321                        role: "Driver Trainee",
1322                        score: 33,
1323                        ci: 2,
1324                        tenure: 0.4,
1325                        dollar: 1,
1326                        isCandidate: false
1327                    },
1328                    {
1329                        name: "K. Bell",
1330                        role: "Trainee",
1331                        score: 30,
1332                        ci: 2,
1333                        tenure: 0.3,
1334                        dollar: 1,
1335                        isCandidate: false
1336                    },
1337                    {
1338                        name: "L. Grant",
1339                        role: "Candidate",
1340                        score: 43,
1341                        ci: 2,
1342                        tenure: 0,
1343                        dollar: 1,
1344                        isCandidate: true
1345                    },
1346                    {
1347                        name: "M. Stone",
1348                        role: "Candidate",
1349                        score: 39,
1350                        ci: 2,
1351                        tenure: 0,
1352                        dollar: 1,
1353                        isCandidate: true
1354                    },
1355                    {
1356                        name: "N. Fox",
1357                        role: "Candidate",
1358                        score: 36,
1359                        ci: 2,
1360                        tenure: 0,
1361                        dollar: 1,
1362                        isCandidate: true
1363                    },
1364                    {
1365                        name: "O. Webb",
1366                        role: "Candidate",
1367                        score: 32,
1368                        ci: 2,
1369                        tenure: 0,
1370                        dollar: 1,
1371                        isCandidate: true
1372                    },
1373                    {
1374                        name: "Q. Hart",
1375                        role: "Candidate",
1376                        score: 28,
1377                        ci: 2,
1378                        tenure: 0,
1379                        dollar: 1,
1380                        isCandidate: true
1381                    },
1382                ];
1383
1384                const CLUSTERS = [{
1385                        col: 0x7a8fa5,
1386                        cx: 0.80,
1387                        cy: 0.28,
1388                        cz: 0.05,
1389                        spread: 0.52
1390                    },
1391                    {
1392                        col: 0x60a5fa,
1393                        cx: -0.68,
1394                        cy: -0.12,
1395                        cz: 0.18,
1396                        spread: 0.54
1397                    },
1398                    {
1399                        col: 0xb8c8d2,
1400                        cx: 0.05,
1401                        cy: -0.58,
1402                        cz: -0.28,
1403                        spread: 0.58
1404                    },
1405                ];
1406
1407                const C_ACTIVE = 0xe6f85e;
1408                const C_NEIGHBOR = 0x0062fb;
1409                const C_DIM = 0xeaeef2;
1410
1411                function lightenHex(hex, amt) {
1412                    const rv = (hex >> 16) & 0xff,
1413                        gv = (hex >> 8) & 0xff,
1414                        bv = hex & 0xff;
1415                    const m = (c) => Math.round(c + (255 - c) * amt);
1416                    return (m(rv) << 16) | (m(gv) << 8) | m(bv);
1417                }
1418
1419                function scaleHex(hex, amt) {
1420                    const rv = (hex >> 16) & 0xff,
1421                        gv = (hex >> 8) & 0xff,
1422                        bv = hex & 0xff;
1423                    const m = (c) => Math.round(c * amt);
1424                    return (m(rv) << 16) | (m(gv) << 8) | m(bv);
1425                }
1426                const nodeBaseHex = NODES.map((nd) =>
1427                    nd.isCandidate ? lightenHex(CLUSTERS[nd.ci].col, 0.34) : CLUSTERS[nd.ci].col,
1428                );
1429
1430                // ── Positions ─────────────────────────────────────────────────────────────
1431                let seed = 0;
1432                const pos = NODES.map((n) => {
1433                    const cl = CLUSTERS[n.ci];
1434                    const phi = Math.acos(1 - 2 * r(seed++));
1435                    const th = r(seed++) * Math.PI * 2;
1436                    const rad = r(seed++) * cl.spread;
1437                    return new THREE.Vector3(
1438                        cl.cx + rad * Math.sin(phi) * Math.cos(th),
1439                        cl.cy + rad * Math.sin(phi) * Math.sin(th),
1440                        cl.cz + rad * Math.cos(phi),
1441                    );
1442                });
1443
1444                // ── Edges + adjacency ─────────────────────────────────────────────────────
1445                const edges = [];
1446                const adj = NODES.map(() => []);
1447                for (let i = 0; i < NODES.length; i++) {
1448                    for (let j = i + 1; j < NODES.length; j++) {
1449                        const same = NODES[i].ci === NODES[j].ci;
1450                        if (r(i * 37 + j * 11 + 7) < (same ? 0.35 : 0.03)) {
1451                            edges.push([i, j]);
1452                            adj[i].push(j);
1453                            adj[j].push(i);
1454                        }
1455                    }
1456                }
1457
1458                // ── Scene ─────────────────────────────────────────────────────────────────
1459                const scene = new THREE.Scene();
1460                scene.background = new THREE.Color(0xffffff);
1461                scene.fog = new THREE.FogExp2(0xffffff, 0.022);
1462
1463                const W = wrap.clientWidth || 500;
1464                const H = wrap.clientHeight || 500;
1465                const camera = new THREE.PerspectiveCamera(55, W / H, 0.05, 60);
1466                camera.position.set(0, 0, 2.8);
1467
1468                // ── Renderer — same pipeline as the app ───────────────────────────────────
1469                const renderer = new THREE.WebGLRenderer({
1470                    antialias: true
1471                });
1472                renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
1473                renderer.setSize(W, H);
1474                renderer.setClearColor(0xffffff, 1);
1475                renderer.outputColorSpace = THREE.SRGBColorSpace;
1476                renderer.toneMapping = THREE.ACESFilmicToneMapping;
1477                renderer.toneMappingExposure = 1.0;
1478                wrap.insertBefore(renderer.domElement, wrap.firstChild);
1479
1480                // ── Orbit controls ────────────────────────────────────────────────────────
1481                const controls = new OrbitControls(camera, renderer.domElement);
1482                controls.enableDamping = true;
1483                controls.dampingFactor = 0.065;
1484                controls.enablePan = false;
1485                controls.minDistance = 1.5;
1486                controls.maxDistance = 10;
1487                controls.autoRotate = false;
1488                controls.autoRotateSpeed = 3.0;
1489                controls.zoomToCursor = true;
1490
1491                const hintBar = document.getElementById("graph-hint-bar");
1492                let userIdleTimer = null;
1493
1494                controls.addEventListener("start", () => {
1495                    controls.autoRotate = false;
1496                    if (hintBar) hintBar.classList.add("faded");
1497                });
1498                controls.addEventListener("end", () => {});
1499                setTimeout(() => {
1500                    if (hintBar) hintBar.classList.add("faded");
1501                }, 5000);
1502
1503                // Start rotating when section scrolls into view
1504                const networkSection = document.getElementById("workforce-network");
1505                if (networkSection && "IntersectionObserver" in window) {
1506                    const obs = new IntersectionObserver((entries) => {
1507                        if (entries[0].isIntersecting) {
1508                            controls.autoRotate = true;
1509                            obs.disconnect();
1510                        }
1511                    }, {
1512                        threshold: 0.2
1513                    });
1514                    obs.observe(networkSection);
1515                } else {
1516                    controls.autoRotate = true;
1517                }
1518
1519                // ── Lighting — matches app ─────────────────────────────────────────────────
1520                scene.add(new THREE.AmbientLight(0xffffff, 1.05));
1521                const keyLight = new THREE.DirectionalLight(0xffffff, 0.85);
1522                keyLight.position.set(4, 6, 5);
1523                scene.add(keyLight);
1524                const fillLight = new THREE.DirectionalLight(0xffffff, 0.35);
1525                fillLight.position.set(-3, -1, -4);
1526                scene.add(fillLight);
1527
1528                // ── Nodes — shiny spheres with MeshPhongMaterial ──────────────────────────
1529                function nRad(nd) {
1530                    const base = 0.022 + (nd.score / 100) * 0.016;
1531                    return nd.isCandidate ? base * 0.88 : base;
1532                }
1533
1534                const meshes = [];
1535                NODES.forEach((nd, i) => {
1536                    const m = new THREE.Mesh(
1537                        new THREE.SphereGeometry(nRad(nd), 24, 16),
1538                        new THREE.MeshPhongMaterial({
1539                            color: nodeBaseHex[i],
1540                            shininess: 180,
1541                            specular: 0xeeeeee,
1542                            emissive: 0x000000,
1543                        }),
1544                    );
1545                    m.position.copy(pos[i]);
1546                    m.userData.idx = i;
1547                    scene.add(m);
1548                    meshes.push(m);
1549                });
1550
1551                // ── Cylinder edge helpers ─────────────────────────────────────────────────
1552                const EDGE_RADIUS = 0.005;
1553                const _cylDummy = new THREE.Object3D();
1554                const _cylUp = new THREE.Vector3(0, 1, 0);
1555                const _cylQ = new THREE.Quaternion();
1556                const _cylDir = new THREE.Vector3();
1557
1558                function setCylinderMatrix(from, to) {
1559                    const dx = to.x - from.x,
1560                        dy = to.y - from.y,
1561                        dz = to.z - from.z;
1562                    const len = Math.sqrt(dx * dx + dy * dy + dz * dz);
1563                    if (len < 0.0001) {
1564                        _cylDummy.scale.set(0, 0, 0);
1565                        _cylDummy.updateMatrix();
1566                        return;
1567                    }
1568                    _cylDummy.position.set((from.x + to.x) * 0.5, (from.y + to.y) * 0.5, (from.z + to.z) * 0.5);
1569                    _cylDir.set(dx / len, dy / len, dz / len);
1570                    _cylQ.setFromUnitVectors(_cylUp, _cylDir);
1571                    _cylDummy.quaternion.copy(_cylQ);
1572                    _cylDummy.scale.set(1, len, 1);
1573                    _cylDummy.updateMatrix();
1574                }
1575
1576                // ── Base edge mesh — all connections as gray cylinders ────────────────────
1577                const edgeGeo = new THREE.CylinderGeometry(EDGE_RADIUS, EDGE_RADIUS, 1, 6);
1578                const edgeMat = new THREE.MeshPhongMaterial({
1579                    shininess: 60,
1580                    specular: 0x888888
1581                });
1582                const edgeIM = new THREE.InstancedMesh(edgeGeo, edgeMat, edges.length);
1583                edgeIM.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
1584                edges.forEach(([i, j], k) => {
1585                    setCylinderMatrix(pos[i], pos[j]);
1586                    edgeIM.setMatrixAt(k, _cylDummy.matrix);
1587                    edgeIM.setColorAt(k, new THREE.Color(0xd1d5db));
1588                });
1589                edgeIM.instanceMatrix.needsUpdate = true;
1590                edgeIM.instanceColor.needsUpdate = true;
1591                scene.add(edgeIM);
1592
1593                // ── Selection edge mesh — only connected edges, built on demand ───────────
1594                let selEdgeMesh = null;
1595
1596                function clearSelEdgeMesh() {
1597                    if (selEdgeMesh) {
1598                        scene.remove(selEdgeMesh);
1599                        selEdgeMesh.geometry.dispose();
1600                        selEdgeMesh.material.dispose();
1601                        selEdgeMesh = null;
1602                    }
1603                }
1604
1605                function buildSelEdgeMesh(idx) {
1606                    clearSelEdgeMesh();
1607                    const connEdges = edges.filter(([i, j]) => i === idx || j === idx);
1608                    if (!connEdges.length) return;
1609                    const geo = new THREE.CylinderGeometry(EDGE_RADIUS, EDGE_RADIUS, 1, 6);
1610                    selEdgeMesh = new THREE.InstancedMesh(
1611                        geo,
1612                        new THREE.MeshPhongMaterial({
1613                            color: C_NEIGHBOR,
1614                            emissive: scaleHex(C_NEIGHBOR, 0.6),
1615                            shininess: 60,
1616                            specular: 0x888888,
1617                        }),
1618                        connEdges.length,
1619                    );
1620                    connEdges.forEach(([i, j], k) => {
1621                        setCylinderMatrix(pos[i], pos[j]);
1622                        selEdgeMesh.setMatrixAt(k, _cylDummy.matrix);
1623                    });
1624                    selEdgeMesh.instanceMatrix.needsUpdate = true;
1625                    scene.add(selEdgeMesh);
1626                }
1627
1628                // ── Color helpers ─────────────────────────────────────────────────────────
1629                const cActive = new THREE.Color(C_ACTIVE);
1630                const cNeighbor = new THREE.Color(C_NEIGHBOR);
1631                const cDim = new THREE.Color(C_DIM);
1632                const cActiveEmissive = new THREE.Color(scaleHex(C_ACTIVE, 0.65));
1633                const cNeighborEmissive = new THREE.Color(scaleHex(C_NEIGHBOR, 0.6));
1634                const cNoEmissive = new THREE.Color(0x000000);
1635
1636                let selectedIdx = -1;
1637                let actCI = -1;
1638
1639                function getNodeColor(i) {
1640                    if (actCI >= 0) return NODES[i].ci === actCI ? cActive.clone() : cDim.clone();
1641                    if (selectedIdx < 0) return new THREE.Color(nodeBaseHex[i]);
1642                    if (i === selectedIdx) return cActive.clone();
1643                    if (adj[selectedIdx].includes(i)) return cNeighbor.clone();
1644                    return cDim.clone();
1645                }
1646
1647                // Emissive glow keeps the highlight color true to the brand hex regardless
1648                // of light angle/tone mapping — only active/neighbor nodes glow.
1649                function getNodeEmissive(i) {
1650                    if (actCI >= 0) return NODES[i].ci === actCI ? cActiveEmissive : cNoEmissive;
1651                    if (selectedIdx < 0) return cNoEmissive;
1652                    if (i === selectedIdx) return cActiveEmissive;
1653                    if (adj[selectedIdx].includes(i)) return cNeighborEmissive;
1654                    return cNoEmissive;
1655                }
1656
1657                function refreshColors() {
1658                    meshes.forEach((m, i) => {
1659                        m.material.color.copy(getNodeColor(i));
1660                        m.material.emissive.copy(getNodeEmissive(i));
1661                    });
1662                    if (selectedIdx >= 0) {
1663                        edgeIM.visible = false;
1664                        buildSelEdgeMesh(selectedIdx);
1665                    } else {
1666                        clearSelEdgeMesh();
1667                        edgeIM.visible = false;
1668                    }
1669                }
1670
1671                // ── Screen-space hit detection ────────────────────────────────────────────
1672                const HIT_PX = 18;
1673                const _v = new THREE.Vector3();
1674
1675                function findNodeAtScreen(cx, cy) {
1676                    camera.updateMatrixWorld();
1677                    const rect = wrap.getBoundingClientRect();
1678                    const mx = cx - rect.left,
1679                        my = cy - rect.top;
1680                    const limSq = HIT_PX * HIT_PX;
1681                    let bestIdx = -1,
1682                        bestSq = limSq;
1683                    for (let i = 0; i < NODES.length; i++) {
1684                        _v.copy(pos[i]).project(camera);
1685                        if (_v.z > 1) continue;
1686                        const sx = (_v.x + 1) * 0.5 * rect.width;
1687                        const sy = (-_v.y + 1) * 0.5 * rect.height;
1688                        const d2 = (mx - sx) ** 2 + (my - sy) ** 2;
1689                        if (d2 < bestSq) {
1690                            bestSq = d2;
1691                            bestIdx = i;
1692                        }
1693                    }
1694                    return bestIdx;
1695                }
1696
1697                // ── Info card ─────────────────────────────────────────────────────────────
1698                const tip = document.getElementById("graph-tooltip");
1699                const ttName = document.getElementById("gtt-name");
1700                const ttRole = document.getElementById("gtt-role");
1701                const ttFill = document.getElementById("gtt-fill");
1702                const ttVal = document.getElementById("gtt-val");
1703                const ttTen = document.getElementById("gtt-tenure");
1704                const ttDol = document.getElementById("gtt-dollar");
1705                const ttConns = document.getElementById("gtt-conns");
1706
1707                function scoreColor(s) {
1708                    if (s >= 85) return "#3b82f6";
1709                    if (s >= 65) return "#93c5fd";
1710                    return "#b8c8d2";
1711                }
1712
1713                function tenureStr(t) {
1714                    if (t === 0) return "New candidate";
1715                    if (t < 1) return Math.round(t * 12) + " mo. tenure";
1716                    return t.toFixed(1) + " yrs tenure";
1717                }
1718
1719                function dollarHTML(rank) {
1720                    let s = "";
1721                    for (let i = 1; i <= 5; i++)
1722                        s += `<span class="${i <= rank ? "gtt-d-on" : "gtt-d-off"}">$</span>`;
1723                    return s;
1724                }
1725
1726                function showCard(idx) {
1727                    if (!tip) return;
1728                    const nd = NODES[idx];
1729                    ttName.textContent = nd.name;
1730                    ttRole.textContent = nd.isCandidate ? "New Candidate" : nd.role;
1731                    ttRole.style.color = nd.isCandidate ? "#917200" : "";
1732                    ttFill.style.width = nd.score + "%";
1733                    ttFill.style.background = scoreColor(nd.score);
1734                    ttVal.textContent = nd.score;
1735                    ttTen.textContent = tenureStr(nd.tenure);
1736                    ttDol.innerHTML = dollarHTML(nd.dollar);
1737                    const cc = adj[idx].length;
1738                    ttConns.textContent = cc + " connection" + (cc !== 1 ? "s" : "");
1739                    tip.style.opacity = "1";
1740                }
1741
1742                function hideCard() {
1743                    if (tip) tip.style.opacity = "0";
1744                }
1745
1746                // ── Pointer interaction ───────────────────────────────────────────────────
1747                let pdX = 0,
1748                    pdY = 0,
1749                    hovIdx = -1;
1750
1751                wrap.addEventListener("pointermove", (e) => {
1752                    const idx = findNodeAtScreen(e.clientX, e.clientY);
1753                    if (idx !== hovIdx) {
1754                        hovIdx = idx;
1755                        if (idx >= 0) {
1756                            showCard(idx);
1757                            wrap.style.cursor = "pointer";
1758                        } else {
1759                            if (selectedIdx >= 0) showCard(selectedIdx);
1760                            else hideCard();
1761                            wrap.style.cursor = "";
1762                        }
1763                    }
1764                });
1765
1766                wrap.addEventListener("pointerleave", () => {
1767                    hovIdx = -1;
1768                    if (selectedIdx >= 0) showCard(selectedIdx);
1769                    else hideCard();
1770                    wrap.style.cursor = "";
1771                });
1772
1773                wrap.addEventListener("pointerdown", (e) => {
1774                    pdX = e.clientX;
1775                    pdY = e.clientY;
1776                });
1777
1778                wrap.addEventListener("click", (e) => {
1779                    if ((e.clientX - pdX) ** 2 + (e.clientY - pdY) ** 2 > 25) return;
1780                    if (e.target.closest(".graph-legend")) return;
1781                    const idx = hovIdx >= 0 ? hovIdx : findNodeAtScreen(e.clientX, e.clientY);
1782                    if (idx >= 0) {
1783                        stopAutoPlay();
1784                        scheduleResume();
1785                        actCI = -1;
1786                        syncLegActive();
1787                        selectedIdx = selectedIdx === idx ? -1 : idx;
1788                        refreshColors();
1789                        if (selectedIdx >= 0) showCard(selectedIdx);
1790                        else hideCard();
1791                    } else {
1792                        selectedIdx = -1;
1793                        if (actCI < 0) refreshColors();
1794                        hideCard();
1795                    }
1796                });
1797
1798                const legEls = wrap.querySelectorAll(".gleg");
1799                const CHIP_CI = [-1, 0, 1];
1800
1801                function syncLegActive() {
1802                    legEls.forEach((l, k) => l.classList.toggle("active", CHIP_CI[k] === actCI));
1803                }
1804
1805                legEls.forEach((el, i) => {
1806                    el.addEventListener("click", () => {
1807                        stopAutoPlay();
1808                        scheduleResume();
1809                        selectedIdx = -1;
1810                        hovIdx = -1;
1811                        const chipCI = CHIP_CI[i];
1812                        actCI = chipCI === -1 || actCI === chipCI ? -1 : chipCI;
1813                        syncLegActive();
1814                        refreshColors();
1815                        hideCard();
1816                    });
1817                });
1818
1819                // ── Auto-play ─────────────────────────────────────────────────────────────
1820                let autoPlayTimer = null,
1821                    autoPlayIdx = -1,
1822                    autoPlayActive = false;
1823
1824                function autoPlayNext() {
1825                    if (!autoPlayActive) return;
1826                    const jump = Math.round(NODES.length / 3);
1827                    autoPlayIdx = (autoPlayIdx + 1) % NODES.length;
1828                    const nodeIdx = Math.floor(autoPlayIdx / 3) + (autoPlayIdx % 3) * jump;
1829                    actCI = -1;
1830                    syncLegActive();
1831                    selectedIdx = nodeIdx;
1832                    refreshColors();
1833                    showCard(nodeIdx);
1834                    autoPlayTimer = setTimeout(autoPlayNext, 2000);
1835                }
1836
1837                function startAutoPlay() {
1838                    autoPlayActive = true;
1839                    autoPlayIdx = -1;
1840                    clearTimeout(autoPlayTimer);
1841                    autoPlayNext();
1842                }
1843
1844                function stopAutoPlay() {
1845                    autoPlayActive = false;
1846                    clearTimeout(autoPlayTimer);
1847                    autoPlayTimer = null;
1848                }
1849
1850                function scheduleResume() {
1851                    clearTimeout(userIdleTimer);
1852                    userIdleTimer = setTimeout(() => {
1853                        controls.autoRotate = true;
1854                        startAutoPlay();
1855                    }, 60000);
1856                }
1857
1858                // ── Animation loop ────────────────────────────────────────────────────────
1859                function animate() {
1860                    requestAnimationFrame(animate);
1861                    controls.update();
1862                    renderer.render(scene, camera);
1863                }
1864                animate();
1865                startAutoPlay();
1866
1867                // ── Camera position by canvas size ────────────────────────────────────────
1868                function applyCameraForSize(w) {
1869                    if (w < 600) {
1870                        camera.position.set(0, 0, 2.1);
1871                        controls.target.set(0, -0.1, 0);
1872                    } else {
1873                        camera.position.set(0, 0, 2.8);
1874                        controls.target.set(0, 0, 0);
1875                    }
1876                    controls.update();
1877                }
1878                applyCameraForSize(W);
1879
1880                // ── Resize ────────────────────────────────────────────────────────────────
1881                new ResizeObserver(() => {
1882                    const w = wrap.clientWidth,
1883                        h = wrap.clientHeight;
1884                    if (w > 0 && h > 0) {
1885                        renderer.setSize(w, h);
1886                        camera.aspect = w / h;
1887                        camera.updateProjectionMatrix();
1888                        applyCameraForSize(w);
1889                    }
1890                }).observe(wrap);
1891            })();
1892        </script>
1892
1893
1894        <section class="section" id="geotrend">
1895            <div class="section-inner">
1896
1897                <!-- Text above map -->
1898                <div class="geotrend-header" data-aos="fade-up" data-aos-duration="700">
1899                    <div class="section-label">GeoTrend</div>
1900                    <h2 class="section-title">
1901                        Learn trends and groups by region
1902                    </h2>
1903                    <p class="section-desc">
1904                        The same relationships show up differently depending on where people are. This map traces those patterns by location: how people in one region relate to each other, where certain roles or strengths cluster, and how that compares from one area to the next.
1905                    </p>
1906                </div>
1907
1908                <!-- Cards above map -->
1909                <div class="geotrend-footer" data-aos="fade-up" data-aos-duration="700" data-aos-delay="150">
1910                    <div class="geotrend-cards">
1911                        <div class="geotrend-card">
1912                            <div class="geotrend-card-header">
1913                                <div class="geotrend-card-icon">
1914                                    <i class="fa-solid fa-route"></i>
1915                                </div>
1916                                <h4 class="geotrend-card-title">Corridor Intelligence</h4>
1917                            </div>
1918                            <p class="geotrend-card-desc">Identify high-retention corridors and source more candidates from proven regions.</p>
1919                        </div>
1920                        <div class="geotrend-card">
1921                            <div class="geotrend-card-header">
1922                                <div class="geotrend-card-icon">
1923                                    <i class="fa-solid fa-bell"></i>
1924                                </div>
1925                                <h4 class="geotrend-card-title">Early Warning</h4>
1926                            </div>
1927                            <p class="geotrend-card-desc">Spot early-exit clusters by region before they become a costly pattern.</p>
1928                        </div>
1929                        <div class="geotrend-card">
1930                            <div class="geotrend-card-header">
1931                                <div class="geotrend-card-icon">
1932                                    <i class="fa-solid fa-ranking-star"></i>
1933                                </div>
1934                                <h4 class="geotrend-card-title">Geographic Scoring</h4>
1935                            </div>
1936                            <p class="geotrend-card-desc">Score new candidates based on the geographic profile of your top performers.</p>
1937                        </div>
1938                    </div>
1939                </div>
1940
1941                <!-- Full-width map -->
1942                <div class="map-canvas-outer" data-aos="fade-up" data-aos-duration="700" data-aos-delay="100">
1943                    <div class="network-canvas-wrap map-canvas-wrap map-wide" id="map-wrap">
1944                        <div class="graph-live-badge map-live-badge">
1945                            <span class="graph-live-dot"></span>Live
1946                        </div>
1947
1948                        <div class="map-legend-bar">
1949                            <span class="mleg mleg-all active">All</span>
1950                            <span class="mleg mleg-top">Top performers</span>
1951                            <span class="mleg mleg-strong">Strong matches</span>
1952                        </div>
1953
1954                        <div id="geo-map"></div>
1955                    </div>
1956
1957                    <div id="map-info-card" class="graph-tooltip">
1958                        <span class="gtt-name" id="mic-name"></span>
1959                        <span class="gtt-role" id="mic-type"></span>
1960                        <div class="mic-location" id="mic-location"></div>
1961                        <div class="gtt-sep"></div>
1962                        <div class="gtt-score-row">
1963                            <span class="gtt-score-label">Retention</span>
1964                            <div class="gtt-score-bar">
1965                                <div class="gtt-score-fill" id="mic-fill"></div>
1966                            </div>
1967                            <span class="gtt-score-val" id="mic-val"></span>
1968                        </div>
1969                        <div class="gtt-meta-row">
1970                            <span class="gtt-tenure" id="mic-tenure"></span>
1971                            <span class="mic-status" id="mic-status"></span>
1972                        </div>
1973                    </div>
1974                </div>
1975
1976            </div>
1977        </section>
1978
1979        
1979<script>
1980            (function() {
1981                var PINS = [
1982                    // Active employees
1983                    {
1984                        name: 'J. Martinez',
1985                        city: 'Dallas',
1986                        state: 'TX',
1987                        lat: 32.7767,
1988                        lng: -96.7970,
1989                        score: 94,
1990                        isCandidate: false,
1991                        tenure: 3.2
1992                    },
1993                    {
1994                        name: 'S. Johnson',
1995                        city: 'Chicago',
1996                        state: 'IL',
1997                        lat: 41.8781,
1998                        lng: -87.6298,
1999                        score: 87,
2000                        isCandidate: false,
2001                        tenure: 2.1
2002                    },
2003                    {
2004                        name: 'L. Chen',
2005                        city: 'Denver',
2006                        state: 'CO',
2007                        lat: 39.7392,
2008                        lng: -104.9903,
2009                        score: 92,
2010                        isCandidate: false,
2011                        tenure: 4.5
2012                    },
2013                    {
2014                        name: 'J. Wilson',
2015                        city: 'Columbus',
2016                        state: 'OH',
2017                        lat: 39.9612,
2018                        lng: -82.9988,
2019                        score: 91,
2020                        isCandidate: false,
2021                        tenure: 5.1
2022                    },
2023                    {
2024                        name: 'C. Thomas',
2025                        city: 'Kansas City',
2026                        state: 'MO',
2027                        lat: 39.0997,
2028                        lng: -94.5786,
2029                        score: 83,
2030                        isCandidate: false,
2031                        tenure: 3.7
2032                    },
2033                    {
2034                        name: 'T. Thompson',
2035                        city: 'Houston',
2036                        state: 'TX',
2037                        lat: 29.7604,
2038                        lng: -95.3698,
2039                        score: 89,
2040                        isCandidate: false,
2041                        tenure: 2.8
2042                    },
2043                    {
2044                        name: 'R. Nelson',
2045                        city: 'Minneapolis',
2046                        state: 'MN',
2047                        lat: 44.9778,
2048                        lng: -93.2650,
2049                        score: 88,
2050                        isCandidate: false,
2051                        tenure: 1.9
2052                    },
2053                    {
2054                        name: 'B. Harris',
2055                        city: 'Louisville',
2056                        state: 'KY',
2057                        lat: 38.2527,
2058                        lng: -85.7585,
2059                        score: 86,
2060                        isCandidate: false,
2061                        tenure: 3.3
2062                    },
2063                    {
2064                        name: 'D. Clark',
2065                        city: 'St. Louis',
2066                        state: 'MO',
2067                        lat: 38.6270,
2068                        lng: -90.1994,
2069                        score: 84,
2070                        isCandidate: false,
2071                        tenure: 2.6
2072                    },
2073                    {
2074                        name: 'M. White',
2075                        city: 'Detroit',
2076                        state: 'MI',
2077                        lat: 42.3314,
2078                        lng: -83.0458,
2079                        score: 82,
2080                        isCandidate: false,
2081                        tenure: 4.1
2082                    },
2083                    {
2084                        name: 'K. Lewis',
2085                        city: 'Pittsburgh',
2086                        state: 'PA',
2087                        lat: 40.4406,
2088                        lng: -79.9959,
2089                        score: 79,
2090                        isCandidate: false,
2091                        tenure: 1.4
2092                    },
2093                    {
2094                        name: 'P. Walker',
2095                        city: 'Oklahoma City',
2096                        state: 'OK',
2097                        lat: 35.4676,
2098                        lng: -97.5164,
2099                        score: 77,
2100                        isCandidate: false,
2101                        tenure: 2.9
2102                    },
2103                    {
2104                        name: 'A. Hill',
2105                        city: 'Cincinnati',
2106                        state: 'OH',
2107                        lat: 39.1031,
2108                        lng: -84.5120,
2109                        score: 75,
2110                        isCandidate: false,
2111                        tenure: 1.7
2112                    },
2113                    {
2114                        name: 'G. Hernandez',
2115                        city: 'San Antonio',
2116                        state: 'TX',
2117                        lat: 29.4241,
2118                        lng: -98.4936,
2119                        score: 93,
2120                        isCandidate: false,
2121                        tenure: 4.8
2122                    },
2123                    // Candidates
2124                    {
2125                        name: 'M. Williams',
2126                        city: 'Atlanta',
2127                        state: 'GA',
2128                        lat: 33.7490,
2129                        lng: -84.3880,
2130                        score: 76,
2131                        isCandidate: true,
2132                        tenure: 0
2133                    },
2134                    {
2135                        name: 'R. Davis',
2136                        city: 'Nashville',
2137                        state: 'TN',
2138                        lat: 36.1627,
2139                        lng: -86.7816,
2140                        score: 68,
2141                        isCandidate: true,
2142                        tenure: 0
2143                    },
2144                    {
2145                        name: 'M. Garcia',
2146                        city: 'Phoenix',
2147                        state: 'AZ',
2148                        lat: 33.4484,
2149                        lng: -112.0740,
2150                        score: 85,
2151                        isCandidate: true,
2152                        tenure: 0
2153                    },
2154                    {
2155                        name: 'A. Brown',
2156                        city: 'Memphis',
2157                        state: 'TN',
2158                        lat: 35.1495,
2159                        lng: -90.0490,
2160                        score: 72,
2161                        isCandidate: true,
2162                        tenure: 0
2163                    },
2164                    {
2165                        name: 'K. Taylor',
2166                        city: 'Indianapolis',
2167                        state: 'IN',
2168                        lat: 39.7684,
2169                        lng: -86.1581,
2170                        score: 63,
2171                        isCandidate: true,
2172                        tenure: 0
2173                    },
2174                    {
2175                        name: 'D. Anderson',
2176                        city: 'Charlotte',
2177                        state: 'NC',
2178                        lat: 35.2271,
2179                        lng: -80.8431,
2180                        score: 58,
2181                        isCandidate: true,
2182                        tenure: 0
2183                    },
2184                    {
2185                        name: 'J. Scott',
2186                        city: 'Salt Lake City',
2187                        state: 'UT',
2188                        lat: 40.7608,
2189                        lng: -111.8910,
2190                        score: 81,
2191                        isCandidate: true,
2192                        tenure: 0
2193                    },
2194                    {
2195                        name: 'N. Young',
2196                        city: 'Omaha',
2197                        state: 'NE',
2198                        lat: 41.2565,
2199                        lng: -95.9345,
2200                        score: 74,
2201                        isCandidate: true,
2202                        tenure: 0
2203                    },
2204                    {
2205                        name: 'C. Moore',
2206                        city: 'Jackson',
2207                        state: 'MS',
2208                        lat: 32.2988,
2209                        lng: -90.1848,
2210                        score: 61,
2211                        isCandidate: true,
2212                        tenure: 0
2213                    },
2214                    {
2215                        name: 'L. Wright',
2216                        city: 'Cleveland',
2217                        state: 'OH',
2218                        lat: 41.4993,
2219                        lng: -81.6944,
2220                        score: 78,
2221                        isCandidate: true,
2222                        tenure: 0
2223                    },
2224                    {
2225                        name: 'H. King',
2226                        city: 'Des Moines',
2227                        state: 'IA',
2228                        lat: 41.5868,
2229                        lng: -93.6250,
2230                        score: 70,
2231                        isCandidate: true,
2232                        tenure: 0
2233                    },
2234                    {
2235                        name: 'F. Lee',
2236                        city: 'Little Rock',
2237                        state: 'AR',
2238                        lat: 34.7465,
2239                        lng: -92.2896,
2240                        score: 66,
2241                        isCandidate: true,
2242                        tenure: 0
2243                    },
2244                    {
2245                        name: 'X. Martinez',
2246                        city: 'Albuquerque',
2247                        state: 'NM',
2248                        lat: 35.0844,
2249                        lng: -106.6504,
2250                        score: 73,
2251                        isCandidate: true,
2252                        tenure: 0
2253                    },
2254                ];
2255
2256                var CONNECTIONS = [
2257                    [5, 13, 1, 11], // 0  Dallas      → Houston, San Antonio, Chicago, OKC
2258                    [0, 3, 6, 7], // 1  Chicago     → Dallas, Columbus, Minneapolis, Louisville
2259                    [4, 20, 26, 11], // 2  Denver      → Kansas City, Salt Lake, Albuquerque, OKC
2260                    [1, 9, 12, 23], // 3  Columbus    → Chicago, Detroit, Cincinnati, Cleveland
2261                    [8, 2, 11, 21], // 4  Kansas City → St. Louis, Denver, OKC, Omaha
2262                    [0, 13, 22, 17], // 5  Houston     → Dallas, San Antonio, Jackson, Memphis
2263                    [1, 21, 24, 4], // 6  Minneapolis → Chicago, Omaha, Des Moines, Kansas City
2264                    [1, 12, 15, 8], // 7  Louisville  → Chicago, Cincinnati, Nashville, St. Louis
2265                    [4, 7, 25, 17], // 8  St. Louis   → Kansas City, Louisville, Little Rock, Memphis
2266                    [1, 3, 23, 10], // 9  Detroit     → Chicago, Columbus, Cleveland, Pittsburgh
2267                    [3, 9, 23, 19], // 10 Pittsburgh  → Columbus, Detroit, Cleveland, Charlotte
2268                    [0, 4, 2, 25], // 11 OKC         → Dallas, Kansas City, Denver, Little Rock
2269                    [7, 3, 15, 18], // 12 Cincinnati  → Louisville, Columbus, Nashville, Indianapolis
2270                    [0, 5, 11, 2], // 13 San Antonio → Dallas, Houston, OKC, Denver
2271                    [15, 19, 17, 12], // 14 Atlanta     → Nashville, Charlotte, Memphis, Cincinnati
2272                    [7, 14, 17, 12], // 15 Nashville   → Louisville, Atlanta, Memphis, Cincinnati
2273                    [2, 26, 20, 11], // 16 Phoenix     → Denver, Albuquerque, Salt Lake, OKC
2274                    [8, 14, 15, 22], // 17 Memphis     → St. Louis, Atlanta, Nashville, Jackson
2275                    [1, 7, 3, 12], // 18 Indianapolis→ Chicago, Louisville, Columbus, Cincinnati
2276                    [14, 10, 15, 23], // 19 Charlotte   → Atlanta, Pittsburgh, Nashville, Cleveland
2277                    [2, 16, 26, 21], // 20 Salt Lake   → Denver, Phoenix, Albuquerque, Omaha
2278                    [4, 6, 24, 20], // 21 Omaha       → Kansas City, Minneapolis, Des Moines, Salt Lake
2279                    [17, 5, 14, 25], // 22 Jackson     → Memphis, Houston, Atlanta, Little Rock
2280                    [3, 9, 10, 18], // 23 Cleveland   → Columbus, Detroit, Pittsburgh, Indianapolis
2281                    [4, 6, 21, 8], // 24 Des Moines  → Kansas City, Minneapolis, Omaha, St. Louis
2282                    [8, 17, 22, 11], // 25 Little Rock → St. Louis, Memphis, Jackson, OKC
2283                    [16, 2, 11, 20], // 26 Albuquerque → Phoenix, Denver, OKC, Salt Lake
2284                ];
2285
2286                var MAP_STYLES = [{
2287                        elementType: 'geometry',
2288                        stylers: [{
2289                            color: '#212121'
2290                        }]
2291                    },
2292                    {
2293                        elementType: 'labels.icon',
2294                        stylers: [{
2295                            visibility: 'off'
2296                        }]
2297                    },
2298                    {
2299                        elementType: 'labels.text.fill',
2300                        stylers: [{
2301                            color: '#757575'
2302                        }]
2303                    },
2304                    {
2305                        elementType: 'labels.text.stroke',
2306                        stylers: [{
2307                            color: '#212121'
2308                        }]
2309                    },
2310                    {
2311                        featureType: 'administrative',
2312                        elementType: 'geometry',
2313                        stylers: [{
2314                            visibility: 'off'
2315                        }]
2316                    },
2317                    {
2318                        featureType: 'administrative.country',
2319                        elementType: 'labels.text.fill',
2320                        stylers: [{
2321                            color: '#9e9e9e'
2322                        }]
2323                    },
2324                    {
2325                        featureType: 'administrative.locality',
2326                        elementType: 'labels.text.fill',
2327                        stylers: [{
2328                            color: '#bdbdbd'
2329                        }]
2330                    },
2331                    {
2332                        featureType: 'administrative.land_parcel',
2333                        stylers: [{
2334                            visibility: 'off'
2335                        }]
2336                    },
2337                    {
2338                        featureType: 'poi',
2339                        elementType: 'labels.text.fill',
2340                        stylers: [{
2341                            color: '#757575'
2342                        }]
2343                    },
2344                    {
2345                        featureType: 'poi.park',
2346                        elementType: 'geometry',
2347                        stylers: [{
2348                            color: '#181818'
2349                        }]
2350                    },
2351                    {
2352                        featureType: 'poi.park',
2353                        elementType: 'labels.text.fill',
2354                        stylers: [{
2355                            color: '#616161'
2356                        }]
2357                    },
2358                    {
2359                        featureType: 'poi.park',
2360                        elementType: 'labels.text.stroke',
2361                        stylers: [{
2362                            color: '#1b1b1b'
2363                        }]
2364                    },
2365                    {
2366                        featureType: 'road',
2367                        elementType: 'geometry.fill',
2368                        stylers: [{
2369                            color: '#2c2c2c'
2370                        }]
2371                    },
2372                    {
2373                        featureType: 'road',
2374                        elementType: 'labels.text.fill',
2375                        stylers: [{
2376                            color: '#8a8a8a'
2377                        }]
2378                    },
2379                    {
2380                        featureType: 'road.arterial',
2381                        elementType: 'geometry',
2382                        stylers: [{
2383                            color: '#373737'
2384                        }]
2385                    },
2386                    {
2387                        featureType: 'road.highway',
2388                        elementType: 'geometry',
2389                        stylers: [{
2390                            color: '#3c3c3c'
2391                        }]
2392                    },
2393                    {
2394                        featureType: 'road.highway.controlled_access',
2395                        elementType: 'geometry',
2396                        stylers: [{
2397                            color: '#4e4e4e'
2398                        }]
2399                    },
2400                    {
2401                        featureType: 'road.local',
2402                        elementType: 'labels.text.fill',
2403                        stylers: [{
2404                            color: '#616161'
2405                        }]
2406                    },
2407                    {
2408                        featureType: 'transit',
2409                        elementType: 'labels.text.fill',
2410                        stylers: [{
2411                            color: '#757575'
2412                        }]
2413                    },
2414                    {
2415                        featureType: 'water',
2416                        elementType: 'geometry',
2417                        stylers: [{
2418                            color: '#000000'
2419                        }]
2420                    },
2421                    {
2422                        featureType: 'water',
2423                        elementType: 'labels.text.fill',
2424                        stylers: [{
2425                            color: '#3d3d3d'
2426                        }]
2427                    },
2428                ];
2429
2430                // Graph3D color palette
2431                var C_DEFAULT = '#7a8fa5'; // Graph3D cluster grey (base node color)
2432                var C_SELECTED = '#e5e65c'; // Graph3D C_ACTIVE
2433                var C_NEIGHBOR = '#93c5fd'; // Graph3D C_NEIGHBOR
2434                var C_EDGE = '#93c5fd';
2435
2436                function pinColor(score) {
2437                    if (score >= 85) return '#3b82f6';
2438                    if (score >= 65) return '#93c5fd';
2439                    return '#b8c8d2';
2440                }
2441
2442                // viewBox 0 0 90 90 — tip at y=90, head center at y=27, matches the app's pin SVG exactly
2443                function makePinUrl(color) {
2444                    var svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 90 90">' +
2445                        '<path fill="' + color + '" stroke="rgba(255,255,255,0.55)" stroke-width="2.5" d="M45,0C29.897,0,17.611,12.287,17.611,27.389C17.611,46.616,43.853,66.607,45,90c1.147-23.393,27.389-43.384,27.389-62.611C72.389,12.287,60.103,0,45,0z"/>' +
2446                        '<circle fill="rgba(0,0,0,0.5)" cx="45" cy="27" r="12"/>' +
2447                        '</svg>';
2448                    return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg);
2449                }
2450
2451                function tenureStr(t) {
2452                    if (t === 0) return 'New candidate';
2453                    if (t < 1) return Math.round(t * 12) + ' mo. tenure';
2454                    return t.toFixed(1) + ' yrs tenure';
2455                }
2456
2457                var card = document.getElementById('map-info-card');
2458                var micName = document.getElementById('mic-name');
2459                var micType = document.getElementById('mic-type');
2460                var micLoc = document.getElementById('mic-location');
2461                var micFill = document.getElementById('mic-fill');
2462                var micVal = document.getElementById('mic-val');
2463                var micTen = document.getElementById('mic-tenure');
2464                var micStat = document.getElementById('mic-status');
2465
2466                var gmap, markers = [],
2467                    lines = [],
2468                    selectedIdx = -1;
2469                var activeGroup = -1;
2470                var autoActive = false;
2471                var autoTimer = null;
2472                var idleTimer = null;
2473                var autoIdx = 0;
2474
2475                // Maps chip index → group: -1=All, 0=Top performers, 1=Strong matches
2476                var CHIP_GROUPS = [-1, 0, 1];
2477                var chipEls = document.querySelectorAll('.map-legend-bar .mleg');
2478
2479                // Score → group (mirrors graph3D clusters)
2480                function pinGroup(score) {
2481                    if (score >= 80) return 0; // Top performers
2482                    if (score >= 60) return 1; // Strong matches
2483                    return 2; // Developing — in map, no chip
2484                }
2485
2486                function syncChipActive() {
2487                    chipEls.forEach(function(el, k) {
2488                        el.classList.toggle('active', CHIP_GROUPS[k] === activeGroup);
2489                    });
2490                }
2491
2492                function getFilteredPins() {
2493                    if (activeGroup < 0) return PINS.map(function(_, i) {
2494                        return i;
2495                    });
2496                    return PINS.reduce(function(acc, p, i) {
2497                        if (pinGroup(p.score) === activeGroup) acc.push(i);
2498                        return acc;
2499                    }, []);
2500                }
2501
2502                // Highlight all pins in the active group immediately (called on chip click)
2503                function applyGroupHighlight() {
2504                    PINS.forEach(function(p, i) {
2505                        var inGroup = activeGroup < 0 || pinGroup(p.score) === activeGroup;
2506                        setMarkerIcon(i, activeGroup >= 0 && inGroup ? C_SELECTED : C_DEFAULT, 18);
2507                        markers[i].setOpacity(inGroup ? 1 : 0.08);
2508                        markers[i].setZIndex(inGroup ? 2 : 1);
2509                    });
2510                }
2511
2512                function stopAutoPlay() {
2513                    autoActive = false;
2514                    clearTimeout(autoTimer);
2515                    autoTimer = null;
2516                }
2517
2518                function startAutoPlay() {
2519                    activeGroup = -1;
2520                    syncChipActive();
2521                    autoActive = true;
2522                    clearTimeout(autoTimer);
2523                    autoNext();
2524                }
2525
2526                function scheduleResume() {
2527                    clearTimeout(idleTimer);
2528                    idleTimer = setTimeout(startAutoPlay, 60000);
2529                }
2530
2531                chipEls.forEach(function(el, k) {
2532                    el.addEventListener('click', function() {
2533                        stopAutoPlay();
2534                        scheduleResume();
2535                        var chipGroup = CHIP_GROUPS[k];
2536                        activeGroup = (chipGroup < 0 || activeGroup === chipGroup) ? -1 : chipGroup;
2537                        syncChipActive();
2538                        if (card) card.style.opacity = '0';
2539                        selectedIdx = -1;
2540                        if (gmap) applyGroupHighlight();
2541                    });
2542                });
2543
2544                function setMarkerIcon(markerIdx, color, size) {
2545                    markers[markerIdx].setIcon({
2546                        url: makePinUrl(color),
2547                        scaledSize: new google.maps.Size(size, size),
2548                        anchor: new google.maps.Point(size / 2, size),
2549                    });
2550                }
2551
2552                function selectPin(idx) {
2553                    var connSet = {};
2554                    CONNECTIONS[idx].forEach(function(ci) {
2555                        connSet[ci] = true;
2556                    });
2557
2558                    // Clear old lines
2559                    lines.forEach(function(l) {
2560                        l.setMap(null);
2561                    });
2562                    lines = [];
2563
2564                    // Reset all markers: selected → yellow, connected → blue, others → grey dimmed
2565                    PINS.forEach(function(p, i) {
2566                        var isConn = connSet[i];
2567                        var isSel = (i === idx);
2568                        var inGroup = activeGroup < 0 || pinGroup(p.score) === activeGroup;
2569                        var baseOp = inGroup ? (activeGroup >= 0 ? 0.55 : 0.3) : 0.08;
2570                        markers[i].setOpacity(isSel || isConn ? 1 : baseOp);
2571                        markers[i].setZIndex(isSel ? 10 : isConn ? 5 : 1);
2572                        if (!isSel) setMarkerIcon(i, isConn ? C_NEIGHBOR : (inGroup && activeGroup >= 0 ? C_SELECTED : C_DEFAULT), 18);
2573                    });
2574
2575                    selectedIdx = idx;
2576                    var pin = PINS[idx];
2577
2578                    // Selected pin: Graph3D yellow, larger
2579                    setMarkerIcon(idx, C_SELECTED, 24);
2580
2581                    // Connection lines: Graph3D blue
2582                    CONNECTIONS[idx].forEach(function(connIdx) {
2583                        var conn = PINS[connIdx];
2584                        lines.push(new google.maps.Polyline({
2585                            path: [{
2586                                lat: pin.lat,
2587                                lng: pin.lng
2588                            }, {
2589                                lat: conn.lat,
2590                                lng: conn.lng
2591                            }],
2592                            geodesic: true,
2593                            strokeColor: C_EDGE,
2594                            strokeOpacity: 0.8,
2595                            strokeWeight: 2,
2596                            map: gmap,
2597                        }));
2598                    });
2599
2600                    // Update info card with fade
2601                    if (card) {
2602                        card.style.transition = 'opacity 0.15s';
2603                        card.style.opacity = '0';
2604                        setTimeout(function() {
2605                            var c = pinColor(pin.score);
2606                            micName.textContent = pin.name;
2607                            micType.textContent = pin.isCandidate ? 'New Candidate' : 'Active Employee';
2608                            micType.style.color = pin.isCandidate ? '#93c5fd' : '';
2609                            micLoc.textContent = pin.city + ', ' + pin.state;
2610                            micFill.style.width = pin.score + '%';
2611                            micFill.style.background = c;
2612                            micVal.textContent = pin.score;
2613                            micTen.textContent = tenureStr(pin.tenure);
2614                            micStat.textContent = pin.isCandidate ? 'Candidate' : 'Active';
2615                            micStat.style.color = pin.isCandidate ? '#93c5fd' : 'rgba(255,255,255,0.55)';
2616                            card.style.opacity = '1';
2617                        }, 150);
2618                    }
2619                }
2620
2621                function autoNext() {
2622                    if (!autoActive) return;
2623                    var pins = getFilteredPins();
2624                    if (pins.length) {
2625                        selectPin(pins[autoIdx % pins.length]);
2626                        autoIdx++;
2627                    }
2628                    autoTimer = setTimeout(autoNext, 3000);
2629                }
2630
2631                window._geoTrendInit = function() {
2632                    var container = document.getElementById('geo-map');
2633                    if (!container) return;
2634
2635                    var initialZoom = container.clientWidth < 600 ? 3 : 5;
2636                    gmap = new google.maps.Map(container, {
2637                        center: {
2638                            lat: 38.5,
2639                            lng: -97.0
2640                        },
2641                        zoom: initialZoom,
2642                        gestureHandling: 'none',
2643                        zoomControl: false,
2644                        mapTypeControl: false,
2645                        streetViewControl: false,
2646                        fullscreenControl: false,
2647                        clickableIcons: false,
2648                        styles: MAP_STYLES,
2649                    });
2650
2651                    PINS.forEach(function(pin, i) {
2652                        var marker = new google.maps.Marker({
2653                            position: {
2654                                lat: pin.lat,
2655                                lng: pin.lng
2656                            },
2657                            map: gmap,
2658                            icon: {
2659                                url: makePinUrl(C_DEFAULT),
2660                                scaledSize: new google.maps.Size(18, 18),
2661                                anchor: new google.maps.Point(9, 18),
2662                            },
2663                            clickable: true,
2664                            title: pin.name,
2665                        });
2666                        google.maps.event.addListener(marker, 'click', (function(idx) {
2667                            return function() {
2668                                stopAutoPlay();
2669                                scheduleResume();
2670                                selectPin(idx);
2671                            };
2672                        })(i));
2673                        markers.push(marker);
2674                    });
2675
2676                    setTimeout(function() {
2677                        google.maps.event.trigger(gmap, 'resize');
2678                        startAutoPlay();
2679                    }, 150);
2680                };
2681
2682                var wrap = document.getElementById('map-wrap');
2683                var started = false;
2684                if (!wrap) return;
2685
2686                function start() {
2687                    if (started) return;
2688                    started = true;
2689
2690                    if (window.google && window.google.maps) {
2691                        window._geoTrendInit();
2692                        return;
2693                    }
2694
2695                    var script = document.createElement('script');
2696                    script.src = 'https://maps.googleapis.com/maps/api/js?key=AIzaSyC6PEbXEh7ON2LGWfLmnqlU4QHxCJvpQJQ&callback=_geoTrendInit';
2697                    script.async = true;
2698                    script.defer = true;
2699                    document.head.appendChild(script);
2700                }
2701
2702                if ('IntersectionObserver' in window) {
2703                    var obs = new IntersectionObserver(function(entries) {
2704                        if (entries[0].isIntersecting) {
2705                            start();
2706                            obs.disconnect();
2707                        }
2708                    }, {
2709                        threshold: 0.2
2710                    });
2711                    obs.observe(wrap);
2712                } else {
2713                    start();
2714                }
2715            })();
2716        </script>
2716
2717
2718        <section class="users-strip" id="who-uses-it">
2719            <span class="closing-users-label">Who Uses It</span>
2720
2721            <!-- Scrolling marquee row -->
2722            <div class="closing-marquee-outer">
2723                <div class="closing-marquee-track closing-marquee-track--single">
2724                    <span class="closing-chip">Fleet Operators</span>
2725                    <span class="closing-chip">Staffing Agencies</span>
2726                    <span class="closing-chip">Logistics Companies</span>
2727                    <span class="closing-chip">Transportation Firms</span>
2728                    <span class="closing-chip">HR Teams</span>
2729                    <span class="closing-chip">Warehousing Operations</span>
2730                    <span class="closing-chip">Driver Management</span>
2731                    <span class="closing-chip">Frontline Hiring</span>
2732                    <span class="closing-chip">Third-Party Logistics</span>
2733                    <span class="closing-chip">Private Fleets</span>
2734                    <span class="closing-chip">Workforce Planning</span>
2735                    <span class="closing-chip">Operations Leaders</span>
2736                    <span class="closing-chip">Regional Carriers</span>
2737                    <span class="closing-chip">Delivery Networks</span>
2738                    <span class="closing-chip">Talent Acquisition</span>
2739                    <span class="closing-chip">Human Resources</span>
2740                    <span class="closing-chip">Fleet Operators</span>
2741                    <span class="closing-chip">Staffing Agencies</span>
2742                    <span class="closing-chip">Logistics Companies</span>
2743                    <span class="closing-chip">Transportation Firms</span>
2744                    <span class="closing-chip">HR Teams</span>
2745                    <span class="closing-chip">Warehousing Operations</span>
2746                    <span class="closing-chip">Driver Management</span>
2747                    <span class="closing-chip">Frontline Hiring</span>
2748                    <span class="closing-chip">Third-Party Logistics</span>
2749                    <span class="closing-chip">Private Fleets</span>
2750                    <span class="closing-chip">Workforce Planning</span>
2751                    <span class="closing-chip">Operations Leaders</span>
2752                    <span class="closing-chip">Regional Carriers</span>
2753                    <span class="closing-chip">Delivery Networks</span>
2754                    <span class="closing-chip">Talent Acquisition</span>
2755                    <span class="closing-chip">Human Resources</span>
2756                    <span class="closing-chip">Fleet Operators</span>
2757                    <span class="closing-chip">Staffing Agencies</span>
2758                    <span class="closing-chip">Logistics Companies</span>
2759                    <span class="closing-chip">Transportation Firms</span>
2760                    <span class="closing-chip">HR Teams</span>
2761                    <span class="closing-chip">Warehousing Operations</span>
2762                    <span class="closing-chip">Driver Management</span>
2763                    <span class="closing-chip">Frontline Hiring</span>
2764                    <span class="closing-chip">Third-Party Logistics</span>
2765                    <span class="closing-chip">Private Fleets</span>
2766                    <span class="closing-chip">Workforce Planning</span>
2767                    <span class="closing-chip">Operations Leaders</span>
2768                    <span class="closing-chip">Regional Carriers</span>
2769                    <span class="closing-chip">Delivery Networks</span>
2770                    <span class="closing-chip">Talent Acquisition</span>
2771                    <span class="closing-chip">Human Resources</span>
2772                </div>
2773            </div>
2774        </section>
2775
2776        <section class="section wi-section" id="workforce-intelligence">
2777            <div class="section-inner">
2778                <div class="section-label">Continuous Learning</div>
2779                <h2 class="section-title">Every hire makes the system smarter.</h2>
2780                <p class="section-desc">
2781                    AlignWave doesn't use generic benchmarks. It's a system built on your roles, your people, and your outcomes&mdash;and the more you use it, the more it reflects what's actually working for your company.
2782                </p>
2783                <div class="wi-tiles">
2784                    <div class="wi-tile" data-aos="fade-up" data-aos-duration="600">
2785                        <div class="wi-tile-header">
2786                            <div class="wi-tile-icon"><i class="fa-solid fa-chart-line" aria-hidden="true"></i></div>
2787                            <span class="wi-tile-num">01</span>
2788                        </div>
2789                        <h3>Analyze your best performers</h3>
2790                        <p>Your top employees define the benchmark. We identify the behavioral patterns that explain their success so you can find more people like them&mdash;for every role you hire into.</p>
2791                    </div>
2792                    <div class="wi-tile" data-aos="fade-up" data-aos-duration="600" data-aos-delay="100">
2793                        <div class="wi-tile-header">
2794                            <div class="wi-tile-icon"><i class="fa-solid fa-magnifying-glass" aria-hidden="true"></i></div>
2795                            <span class="wi-tile-num">02</span>
2796                        </div>
2797                        <h3>Detect what drives retention</h3>
2798                        <p>As your team grows, patterns emerge across tenure, location, and team structure. The system surfaces the behavioral traits that keep people on the job&mdash;before you see the turnover.</p>
2799                    </div>
2800                    <div class="wi-tile" data-aos="fade-up" data-aos-duration="600" data-aos-delay="200">
2801                        <div class="wi-tile-header">
2802                            <div class="wi-tile-icon"><i class="fa-solid fa-arrow-trend-up" aria-hidden="true"></i></div>
2803                            <span class="wi-tile-num">03</span>
2804                        </div>
2805                        <h3>Get sharper with every hire</h3>
2806                        <p>Each new hire&rsquo;s outcome feeds back in. The longer you use AlignWave, the more it reflects what&rsquo;s actually working for your company&mdash;not an industry average.</p>
2807                    </div>
2808                </div>
2809            </div>
2810        </section>
2811
2812        <section class="section closing-section" id="closing">
2813
2814            <!-- Who benefits + CTA -->
2815            <div class="section-inner">
2816                <div class="closing-split">
2817                    <div class="closing-benefits-cards">
2818                        <div class="closing-benefit-card">
2819                            <div class="closing-benefit-header">
2820                                <div class="closing-benefit-icon"><i class="fa-solid fa-user-tie" aria-hidden="true"></i></div>
2821                                <h4>HR Teams</h4>
2822                            </div>
2823                            <p>Hiring is the obvious part. HR also sees which campaigns are working, and knows exactly which drivers to talk to when it&rsquo;s time to review practices or dig into an issue.</p>
2824                        </div>
2825                        <div class="closing-benefit-card">
2826                            <div class="closing-benefit-header">
2827                                <div class="closing-benefit-icon"><i class="fa-solid fa-bullhorn" aria-hidden="true"></i></div>
2828                                <h4>Marketing</h4>
2829                            </div>
2830                            <p>See which profiles perform best in which roles, and point recruiting campaigns at the people most likely to succeed in them.</p>
2831                        </div>
2832                        <div class="closing-benefit-card">
2833                            <div class="closing-benefit-header">
2834                                <div class="closing-benefit-icon"><i class="fa-solid fa-graduation-cap" aria-hidden="true"></i></div>
2835                                <h4>Training</h4>
2836                            </div>
2837                            <p>Know who you&rsquo;re training before they start, and shape the process around a role or a specific group instead of a generic course.</p>
2838                        </div>
2839                        <div class="closing-benefit-card">
2840                            <div class="closing-benefit-header">
2841                                <div class="closing-benefit-icon"><i class="fa-solid fa-truck" aria-hidden="true"></i></div>
2842                                <h4>Drivers</h4>
2843                            </div>
2844                            <p>Onboarding and support are built around who they are from day one&mdash;not a one-size-fits-all process.</p>
2845                        </div>
2846                    </div>
2847                    <div class="closing-cta">
2848                        <div class="section-label">Who it&rsquo;s built for</div>
2849                        <h2 class="closing-headline">Who Benefits.</h2>
2850                        <p class="closing-desc">Stop relying on instinct and tenure alone. AlignWave gives your hiring team the behavioral data to hire with confidence&mdash;and build a workforce that actually lasts.</p>
2851                        <p class="closing-desc">Every team that touches a driver works from the same picture. Recruiters know where to look, trainers know who they&rsquo;re teaching, and managers know who to support before small issues turn into turnover.</p>
2852                        <div class="closing-actions">
2853                            <a href="/app/register" class="btn-ghost-dark">Let&rsquo;s put your company on track to scale &rarr;</a>
2854                        </div>
2855                    </div>
2856                </div>
2857            </div>
2858
2859        </section>
2860
2861    </main>
2862
2863    <footer class="site-footer">
2864        <div class="footer-inner">
2865            <span>© <span id="current-year">2025</span> AlignWave</span>
2866            <div>
2867                <a href="#">Privacy</a>
2868                <a href="#">Terms</a>
2869                <a href="/app/request">Contact</a>
2870            </div>
2871        </div>
2872    </footer>
2873    
2873<script>
2874        (function() {
2875            var y = document.getElementById("current-year");
2876            if (y) y.textContent = new Date().getFullYear();
2877        })();
2878    </script>
2878
2879
2880    <!-- JS -->
2881    
2881<script src="/assets/js/plugins.js"></script>
2881
2882    
2882<script src="/assets/js/main.js"></script>
2882
2883
2884</body>
2885
2886</html>

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