PageSourceSearch

https://cadient.ai/resources/case-studies/how-cadients-smarttextin…nal-grocery-store-reduce-hiring-time-by-41/

html cadient.ai collected 2026-10-01 12:01:37 UTC 141,766 bytes, 2,321 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en" class="scroll-smooth">
3
4
5
6<head>
7    <meta charset="UTF-8">
8    <meta name="viewport" content="width=device-width, initial-scale=1.0">
9    <title>How Cadient’s SmartTexting™ helped a large regional grocery store reduce hiring time by 41%</title>
10    <meta name="description" content="Find out how SmartTexting helped a grocery chain reduce hiring time by 41% and improve candidate engagement. Explore now!">
11    <link rel="canonical" href="https://cadient.ai/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/">
12    <meta property="og:type" content="article">
13    <meta property="og:title" content="How Cadient’s SmartTexting™ helped a large regional grocery store reduce hiring time by 41%">
14    <meta property="og:description" content="Find out how SmartTexting helped a grocery chain reduce hiring time by 41% and improve candidate engagement. Explore now!">
15    <meta property="og:url" content="https://cadient.ai/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/">
16    <meta name="twitter:card" content="summary_large_image">
17    <link rel="icon" type="image/png" href="/static/images/favicon_cadientai.png">
18    <link rel="stylesheet" href="/static/css/tailwind.css?v=6b6fbc49b0">
19    <link rel="preconnect" href="https://fonts.googleapis.com">
20    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
21    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" media="print" onload="this.media='all'">
22    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap" rel="stylesheet" media="print" onload="this.media='all'">
23    <style>
24        body { font-family: 'Inter', sans-serif; }
25        .ss-orange { color: #fe4a23; }
26        .ss-bg-orange { background-color: #fe4a23; }
27        .ss-bg-orange:hover { background-color: #d93d1a; }
28        .ss-cream { background-color: #fcfcfa; }
29        .ss-box { background-color: #f2f2f2; }
30        .ss-dark { background-color: #141414; }
31        .ss-dark:hover { background-color: #2a2a2a; }
32    </style>
33    
34
35
36    
37
38
39<script>
40    window.dataLayer = window.dataLayer || [];
41    function gtag() { dataLayer.push(arguments); }
42    (function () {
43        // Single source: app.py parses GTM_CONTAINER_IDS once into the GTM_IDS
44        // Jinja global, which _gtm_noscript.html reads too — see the note there.
45        // Fails closed (no containers) if it is somehow undefined.
46        var GTM_IDS = ["GTM-WWP5NR36", "GTM-PDRS4M23"];
47        // Configured, not hardcoded, and each vendor is skipped below when its
48        // ID is blank. ANALYTICS_ENABLED stays the master switch for the whole
49        // stack; these decide WHICH vendors that switch turns on.
50        //
51        // That distinction is what makes a non-prod environment testable. These
52        // two used to be literals, so the only way to exercise any vendor on dev
53        // was ANALYTICS_ENABLED=true, which also pointed dev traffic at the
54        // PRODUCTION GA4 property and Clarity project — quietly polluting the
55        // baseline the SEO programme is measured against. Now dev can enable the
56        // stack with these blank and run a single vendor in isolation.
57        //
58        // The defaults are the live prod IDs, so an environment that sets
59        // neither behaves exactly as before.
60        var GA_ID = "G-1T86EMJJXX";
61        var CLARITY_ID = "vfab1wc1af";
62        var started = false;
63
64        // ── Embedded (iframe) awareness ────────────────────────────────────────
65        // Set by pages that are normally rendered inside an iframe on one of our
66        // own pages — the scheduler at /lp/schedule. Such a page is a WIDGET, not
67        // a destination: the visitor never navigated to /lp/schedule, so a
68        // page_view against that URL is a phantom hit that inflates its views and
69        // distorts engagement for the property (LEAD_AUDIT.md F19).
70        //
71        // The old fix was to load NOTHING when embedded. That killed the phantom
72        // page_view but also blinded us on the same page's direct loads — and
73        // /lp/schedule has 19 real hrefs pointing at it, plus cross-origin
74        // WordPress embeds where the parent may carry no tags of ours at all. So
75        // it traded a known over-count for an unknown blind spot on the surface
76        // that books demos.
77        //
78        // What is actually unwanted is the automatic page_view, not the stack. So
79        // load everything either way and decide the page_view at RUNTIME, which
80        // is also the only place the answer is knowable: whether we are framed is
81        // not a fact the server has.
82        //
83        // IMPORTANT — this only covers the page_view that OUR gtag config sends.
84        // If a GTM container holds a GA4 tag firing on All Pages, that tag sends
85        // its own and no template change can stop it. `embedded` is pushed to the
86        // dataLayer BEFORE gtm.js loads so the container can carry a trigger
87        // exception on it; until someone adds that exception in the GTM UI, the
88        // container's own page_view still fires here. See LEAD_AUDIT.md F19 and
89        // the unresolved two-container question above.
90        var embedded = false && (function () {
91            try { return window.self !== window.top; } catch (e) { return true; }
92        })();
93
94        function loadAnalytics() {
95            if (started) return; started = true;
96            // Google Tag Manager
97            dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
98            if (embedded) { dataLayer.push({ embedded: true }); }
99            GTM_IDS.forEach(function (id) {
100                var s = document.createElement('script');
101                s.async = true;
102                s.src = 'https://www.googletagmanager.com/gtm.js?id=' + id;
103                document.head.appendChild(s);
104            });
105            // Google Analytics (gtag.js). Skipped entirely without an ID —
106            // loading gtag.js with an empty id still opens a connection to
107            // Google and registers a property-less client, so the guard has to
108            // be here rather than relying on GA to ignore the config call.
109            if (GA_ID) {
110                var g = document.createElement('script');
111                g.async = true;
112                g.src = 'https://www.googletagmanager.com/gtag/js?i
112d=' + GA_ID;
113                document.head.appendChild(g);
114                gtag('js', new Date());
115                // send_page_view:false keeps gtag() live for event hits (a booking is
116                // still measurable) while omitting the phantom navigation hit.
117                gtag('config', GA_ID, { send_page_view: !embedded });
118            }
119            // Microsoft Clarity
120            if (CLARITY_ID) {
121                (function (c, l, a, r, i, t, y) {
122                    c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments) };
123                    t = l.createElement(r); t.async = 1; t.src = 'https://www.clarity.ms/tag/' + i;
124                    y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y);
125                })(window, document, 'clarity', 'script', CLARITY_ID);
126            }
127        }
128        var evts = ['scroll', 'mousemove', 'touchstart', 'keydown', 'click'];
129        function onFirst() {
130            evts.forEach(function (e) { window.removeEventListener(e, onFirst); });
131            loadAnalytics();
132        }
133        evts.forEach(function (e) { window.addEventListener(e, onFirst, { passive: true }); });
134        window.addEventListener('load', function () {
135            if ('requestIdleCallback' in window) { requestIdleCallback(loadAnalytics, { timeout: 4000 }); }
136            else { setTimeout(loadAnalytics, 3000); }
137        });
138    })();
139</script>
139
140
141
142
143<script>
144    (function () {
145        // Queue stub first and synchronously: it is the whole point of the
146        // vendor's snippet shape. Anything measured before the SDK lands is
147        // held here and replayed, so deferring the network load cannot lose an
148        // event.
149        window.oaiq = window.oaiq || function () {
150            (window.oaiq.q = window.oaiq.q || []).push(arguments);
151        };
152
153        var PIXEL_ID = "QnvACeEzLpeAcz9ok3djRz";
154
155        // Same question the page_view asks: are we the scheduler running
156        // inside one of our own pages? If so the parent owns measurement.
157        var embedded = (function () {
158            try { return window.self !== window.top; } catch (e) { return true; }
159        })();
160        if (embedded) return;
161
162        oaiq('init', { pixelId: PIXEL_ID });
163
164        var loaded = false;
165        function loadPixel() {
166            if (loaded) return; loaded = true;
167            var s = document.createElement('script');
168            s.async = true;
169            s.src = 'https://bzrcdn.openai.com/sdk/oaiq.min.js';
170            document.head.appendChild(s);
171        }
172        var evts = ['scroll', 'mousemove', 'touchstart', 'keydown', 'click'];
173        function onFirst() {
174            evts.forEach(function (e) { window.removeEventListener(e, onFirst); });
175            loadPixel();
176        }
177        evts.forEach(function (e) { window.addEventListener(e, onFirst, { passive: true }); });
178        window.addEventListener('load', function () {
179            if ('requestIdleCallback' in window) { requestIdleCallback(loadPixel, { timeout: 4000 }); }
180            else { setTimeout(loadPixel, 3000); }
181        });
182
183        // Our event names -> OpenAI's taxonomy. Both are `customer_action`;
184        // neither carries an amount, because a booked demo has no transaction
185        // value we can state honestly. Adding an invented one would flow
186        // straight into their bidding.
187        var EVENTS = {
188            demo_booking_success: 'appointment_scheduled',
189            playbook_request_success: 'lead_created'
190        };
191
192        // Fired at most once per event name per page: the two channels below
193        // can both be live on one page, and a booking must not be reported
194        // twice because it arrived by two routes.
195        var sent = {};
196
197        function measureConversion(name, detail) {
198            try {
199                var mapped = EVENTS[name];
200                if (!mapped || sent[mapped]) return;
201                sent[mapped] = true;
202                var options = {};
203                // event_id is what lets a future Conversions API call dedupe
204                // against this one. The playbook flows have a real lead id to
205                // key on; the iframe route has none on the parent, so it goes
206                // without and cannot be deduped server-side until the slot is
207                // threaded back. Sharing one id across the two event NAMES is
208                // fine — the dedup key is (pixel, event name, event_id).
209                if (detail && detail.eventId) options.event_id = String(detail.eventId);
210                oaiq('measure', mapped, { type: 'customer_action' }, options);
211            } catch (e) { /* measurement must never break the page */ }
212        }
213        window.cadientMeasureConversion = measureConversion;
214
215        // Route 1: the scheduler iframe announcing a confirmed booking. Same
216        // message the ad consultants' own listener consumes. Origin-checked —
217        // the frame posts to '*' because it is embedded cross-origin by the
218        // WordPress estate, so the check belongs here, on the receiving end.
219        window.addEventListener('message', function (e) {
220            if (e.origin !== window.location.origin &&
221                e.origin !== 'https://cadient.ai' &&
222                e.origin !== 'https://dev.cadient.ai') return;
223            if (!e.data || e.data.event !== 'demo_booking_success') return;
224            measureConversion('demo_booking_success', e.data);
225        });
226    })();
227</script>
227
228
229
230<!-- Warmly — visitor de-anonymisation, i.e. analytics, so it rides the same
231     gate as everything above it including suppress_analytics. -->
232
233
234<script id="warmly-script-loader" src="https://opps-widget.getwarmly.com/warmly.js?clientId=5a34a1084b3b8a03e3901742e03c280b" defer></script>
234
235
236
237    
238
239<!-- Chat widget — loaded only after the first user interaction, so it stays off
240     the initial load (no PageSpeed/Speed-Index hit during the lab test, which
241     doesn't interact). Once loaded, the widget's own open-delay behaviour is
242     unchanged. -->
243<script>
244    (function () {
245        // ── Suppress the widget's auto-open on mobile ──────────────────────────
246        // On a phone the panel covers the whole screen, so auto-opening hides the
247        // page the visitor came to read. The bubble stays fully functional; only
248        // the unprompted open is blocked. Desktop behaviour is untouched.
249        //
250        // This used to pre-seed sessionStorage['cadient-chat-widget-dismissed'],
251        // which was a GUESS at widget.js's internals and verifiably did nothing —
252        // tested against the live script with that key set, and the panel still
253        // auto-opened. widget.js is third-party, exposes no config for this, and
254        // contains no matchMedia/innerWidth at all, so it has no concept of
255        // mobile.
256        //
257        // So we gate on the one thing that is observable and stable: the widget
258        // opens by adding class "open" to #cadient-chat-widget-frame. When that
259        // happens unprompted we close it again by clicking the widget's OWN
260        // bubble, not by stripping the class.
261        //
262        // That distinction matters and cost a bug: removing the class directly
263        // hides the panel but leaves the widget's internal isOpen === true, so the
264        // visitor's first real tap on the bubble only toggles that stale flag back
265        // to false and appears to do nothing — they'd have to tap twice. Driving
266        // the widget's own toggle keeps its state and the DOM in step, so the
267        // first tap opens the panel properly (verified both ways against the live
268        // script).
269        //
270        // The pointerdown listener is on capture, so `userAsked` is always set
271        // before the widget's click handler runs — no race. Our own .click()
272        // dispatches no pointerdown, so it can't trip that flag. After the first
273        // genuine tap we stop interfering entirely. autoCloses is capped at 3 as a
274        // loop guard in case a future widget build reopens itself.
275        try {
276            if (window.matchMedia('(max-width: 767.98px)').matches) {
277                var userAsked = false, autoCloses = 0;
278                document.addEventListener('pointerdown', function (e) {
279                    if (e.target && e.target.closest &&
280                        e.target.closest('#cadient-chat-widget-bubble')) userAsked = true;
281                }, true);
282                new MutationObserver(function () {
283                    if (userAsked || autoCloses >= 3) return;
284                    var f = document.getElementById('cadient-chat-widget-frame');
285                    var b = document.getElementById('cadient-chat-widget-bubble');
286                    if (f && b && f.classList.contains('open')) { autoCloses++; b.click(); }
287                }).observe(document.documentElement,
288                    { subtree: true, childList: true, attributeFilter: ['class'] });
289            }
290        } catch (e) { /* very old browser — fall through, widget just behaves as before */ }
291
292        // ── Give the widget's bubble a landmark ────────────────────────────────
293        // widget.js appends #cadient-chat-widget-bubble straight to <body>, so
294        // its text/icon content sits outside every landmark (main/nav/etc.) —
295        // an axe "region" violation. We don't control widget.js, so once the
296        // bubble shows up we label it as its own landmark region instead of
297        // waiting on a third-party fix.
298        function labelChatBubbleLandmark() {
299            var b = document.getElementById('cadient-chat-widget-bubble');
300            if (b && !b.hasAttribute('role')) {
301                b.setAttribute('role', 'region');
302                b.setAttribute('aria-label', 'Chat with us');
303            }
304            return !!b;
305        }
306        var chatBubbleObserver = new MutationObserver(function () {
307            if (labelChatBubbleLandmark()) chatBubbleObserver.disconnect();
308        });
309        // documentElement, NOT document.body. This include is pulled into <head>
310        // (see smart_automation/base.html and every other layout that uses it),
311        // and <body> does not exist yet at that point — so this line threw
312        // "parameter 1 is not of type 'Node'" on every page load of every page in
313        // the product, and the label this observer exists to apply was never
314        // applied. The uncaught exception was the visible symptom; the silent
315        // accessibility regression was the actual cost.
316        //
317        // The observer twenty lines above already got this right. subtree:true
318        // means watching the root covers the widget's append to <body> exactly as
319        // watching <body> would, and it works before <body> is parsed.
320        chatBubbleObserver.observe(document.documentElement, { childList: true, subtree: true });
321        // The bubble can also already be in the DOM if this include is ever moved
322        // below the widget script: a MutationObserver only reports changes AFTER
323        // it starts, so an element that is already there never fires it. Cheap to
324        // check once, and it removes the ordering dependency entirely.
325        if (labelChatBubbleLandmark()) chatBubbleObserver.disconnect();
326
327        var loaded = false;
328        function loadWidget() {
329            if (loaded) return; loaded = true;
330            var s = document.createElement('script');
331            s.src = "https://consultant-api-dev.cadient.ai/static/widget.js?key=cadient_test_key_abc123";
332            s.defer = true;
333            // loadWidget only ever runs from a user-interaction handler, so <body>
334            // is parsed by now even though this include sits in <head>.
335            document.body.appendChild(s);
336        }
337        // Deliberate-engagement signals only — no 'mousemove' (fires near-instantly
338        // on desktop) and no 'click' (redundant with 'pointerdown').
339        var evts = ['scroll', 'pointerdown', 'touchstart', 'keydown'];
340        function onFirst() {
341            evts.forEach(function (e) { window.removeEventListener(e, onFirst); });
342            loadWidget();
343        }
344        evts.forEach(function (e) { window.addEventListener(e, onFirst, { passive: true, once: true }); });
345    })();
346</script>
346
347
348</head>
349
350<body class="bg-white text-slate-900 flex flex-col min-h-screen">
vendor: 75 bytes, lines 350-354
350
351    
352
353
354<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
354GTM-WWP5NR36
vendor: 151 bytes, lines 354-357
354"
355height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
356
357<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
357GTM-PDRS4M23
vendor: 86 bytes, lines 357-361
357"
358height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
359
360
361
362<style>
363/* Mobile menu: keep the scroll inside the panel.
364   The panel is max-h + overflow-y-auto, so on a phone its link list scrolls.
365   Without this, reaching the end of that list hands the remaining gesture to
366   the document — you keep swiping, the page moves behind the open menu, and
367   closing it reveals you are somewhere else entirely. overscroll-behavior
368   contains the chain at the panel's boundary.
369   Applied here rather than in the 28 per-page toggle scripts, none of which
370   need to change. */
371#mobile-menu { overscroll-behavior: contain; }
372
373/* Mobile menu geometry.
374   ------------------------------------------------------------------------
375   Earlier versions capped the panel with calc(100vh - Npx), then
376   calc(100svh - Npx). Both derive the bottom edge from numbers that are wrong on
377   a phone: N is a guess at the header height (not one value — 80px on the blog
378   header, 90px on /smart-refer, 80px on /smart-shield) and 100vh is the LARGE
379   viewport, 60-120px taller than what is visible with the browser toolbar shown.
380   Any error there puts the panel's bottom edge outside the visible area, and the
381   tail then cannot be scrolled to: scrolling moves content to the panel's own
382   bottom edge and that edge is itself off-screen. The panel reports
383   scrollTop === scrollHeight - clientHeight while its last child is invisible.
384
385   The script sets an exact height from the panel's measured position and
386   visualViewport.height, so nothing here has to be right.
387
388   Note bottom:0 is deliberately NOT used. It reads as the obvious fix, but
389   _product_nav.html's <nav> carries backdrop-blur, and backdrop-filter makes an
390   element a containing block for fixed descendants — bottom:0 there resolves
391   against the nav rather than the viewport and collapses the panel to ~33px.
392   Measuring getBoundingClientRect() sidesteps that: it is in viewport
393   coordinates whatever the containing block is.
394
395   The max-height below is the no-JS floor only. */
396#mobile-menu {
397    position: fixed;
398    top: 80px;
399    left: 0;
400    right: 0;
401    max-height: calc(100vh - 100px);
402    max-height: calc(100svh - 100px);
403    overflow-y: auto;
404    z-index: 50;
405    /* 5rem, not 1rem. This is scroll slack, and it is load-bearing.
406       The JS sizes the panel to end at the reported viewport bottom
407       (visualViewport.height, or innerHeight). Nothing guarantees that number
408       equals the area actually on screen: DevTools reports a 932px device while
409       displaying ~884px of it, and mobile browsers can over-report the same way
410       with a toolbar overlaying content. When they disagree, the bottom band of
411       the panel is unreachable — the panel is already at scrollTop === maxScroll,
412       so scrolling cannot recover it, and the last child sat only 17px above the
413       box bottom (= the old 1rem padding).
414
415       Bottom padding on an overflow container IS included in scrollHeight, so at
416       the scroll end the last child now stops ~65px above the box bottom instead
417       of 17px, without the panel needing to know the true visible height.
418
419       65px is chosen against a measured 48px discrepancy (a 932px-reported device
420       displaying ~884px), leaving ~17px of margin. Don't trim this much further:
421       below ~50px it stops covering that case and the last CTA becomes unreachable
422       again, which is the bug this exists to prevent.
423
424       env() adds the iPhone home-indicator inset on top, which no viewport unit
425       accounts for. */
426    padding-bottom: calc(4rem + env(safe-area-inset-bottom, 0px));
427}
428
429    /* Solutions mega-menu — matches page design language (brand orange accent) */
430    /* Visibility lives here (not in any single page) so the dropdown behaves
431       correctly on EVERY page that includes this header, not just the homepage. */
432    .solutions-menu { display: none; }
433    /* Invisible hover bridge across the few px between the trigger's bottom edge
434       and the panel's top, so moving the pointer down does not close the menu. */
435    .solutions-menu::before { content: ''; position: absolute; left: 0; right: 0; top: -12px; height: 12px; }
436    .solutions-trigger:hover .solutions-menu,
437    .solutions-menu:hover { display: block; }
438    .sol-link { color:#374151; transition: background-color .15s ease, color .15s ease; }
439    .sol-link:hover { background-color:#fff7f5; color:#fe4a23; }
440    .sol-new { font-size:9px; font-weight:700; letter-spacing:.04em; color:#fff; background:#fe4a23; padding:2px 6px; border-radius:4px; line-height:1; }
441    /* Solutions mega-menu promo card — one product called out beside the three
442       Recruit/Hire/Retain columns, e.g. Cadient Talent's MCP while it's new.
443       Peach/orange to match the brand accent, not the neutral list styling
444       above, so it reads as a callout rather than a fourth column of links. */
445    .mcp-promo-card {
446        display: flex; flex-direction: column; height: 100%;
447        background: #FFF3EE; border: 1px solid #FFD9C2; border-radius: 14px;
448        padding: 16px; text-decoration: none; transition: border-color .15s ease, background-color .15s ease;
449    }
450    .mcp-promo-card:hover { background: #FFEBE1; border-color: #fe4a23; }
451    .mcp-promo-top { display: flex; align-items: center; justify-content: space-between; }
452    .mcp-promo-title { margin: 10px 0 4px; font-weight: 700; font-size: 14.5px; color: #111827; }
453    .mcp-promo-desc { margin: 0; font-size: 12.5px; color: #6b7280; line-height: 1.45; flex: 1; }
454    .mcp-promo-link { margin-top: 10px; font-size: 12.5px; font-weight: 600; color: #fe4a23; }
455    /* Mobile variant of the same promo. The sheet is one narrow column, so this
456       lays out horizontally (mark beside the copy) instead of the desktop card's
457       vertical stack, and sits at the top of the Solutions group. */
458    .mcp-promo-m {
459        display: flex; align-items: flex-start; gap: 12px;
460        margin: 8px 16px 4px; padding: 14px;
461        background: #FFF3EE; border: 1px solid #FFD9C2; border-radius: 12px;
462        text-decoration: none;
463    }
464    .mcp-promo-m > img { flex: none; width: 34px; height: 34px; }
465    .mcp-promo-m-head { display: flex; align-items: center; gap: 8px; }
466    .mcp-promo-m-title { margin: 0; font-weight: 700; font-size: 14.5px; color: #111827; }
467    .mcp-promo-m-desc { margin: 4px 0 0; font-size: 12.5px; color: #6b7280; line-height: 1.45; }
468    .mcp-promo-m-link { display: inline-block; margin-top: 6px; font-size: 12.5px; font-weight: 600; color: #fe4a23; }
469    .solutions-trigger .fa-chevron-down { transition: transform .2s ease; }
470    .solutions-trigger:hover .fa-chevron-down { transform: rotate(180deg); }
471
472    /* Media dropdown — "Cadient in the Press" card, same white/orange
473       hover pattern as the Solutions mega-menu above. */
474    .media-menu { display: none; }
475    .media-trigger:hover .media-menu,
476    .media-menu:hover { display: block; }
477    .media-trigger .fa-chevron-down { transition: transform .2s ease; }
478    .media-trigger:hover .fa-chevron-down { transform: rotate(180deg); }
479    .media-item:not(:last-child) { border-bottom: 1px solid #f3f4f6; }
480    .media-item:hover { background-color:#fff7f5; }
481</style><div id="site-sticky-top" style="position:sticky; top:0; z-index:50;">
482<style>
483    /* position:relative, not sticky: blog/_header.html wraps this banner and
484       the <header> that follows it in ONE shared position:sticky container,
485       so the pair sticks together as a unit. (An earlier attempt made this
486       element sticky on its own, alongside the header's own independent
487       sticky — two sticky siblings are supposed to stack, but body's Tailwind
488       flex layout on several pages made the header render pinned under the
489       banner instead of below it.) Keep this position:relative; the sticky
490       behavior lives one level up now. */
491    #promo-banner { position:relative; background:linear-gradient(90deg,#fb923c,#fe4a23); color:#fff; font-size:14px; }
492    /* The whole banner is the link, with no separate CTA pill. The icon used to
493       be a flex sibling of the message paragraph; when the row didn't fit
494       and wrapped, the icon (a tiny flex item) got shoved onto its own
495       line above the text instead of sitting beside the first word, adding a
496       3rd line of near-empty height on top of the message's own 2 lines.
497       Putting the icon INSIDE the paragraph as inline content means the
498       whole thing wraps as ordinary text, so it costs no more height than
499       the message text itself needs. */
500    #promo-banner .promo-link {
501        display:block; color:inherit; text-decoration:none;
502        max-width:80rem; margin:0 auto; padding:10px 40px 10px 16px;
503        text-align:center;
504    }
505    #promo-banner .promo-link:hover .promo-msg,
506    #promo-banner .promo-link:focus-visible .promo-msg { text-decoration:underline; }
507    #promo-banner .promo-msg { margin:0; font-weight:500; font-size:13px; line-height:1.35; }
508    #promo-banner .promo-msg i { font-size:11px; }
509    #promo-close {
510        position:absolute; right:12px; top:50%; transform:translateY(-50%);
511        background:transparent; border:0; color:rgba(255,255,255,.8); cursor:pointer; padding:4px;
512        line-height:1; z-index:1;
513    }
514    #promo-close:hover { color:#fff; }
515    @media (min-width: 640px) {
516        #promo-banner .promo-link { text-align:left; padding-left:24px; padding-right:48px; }
517        #promo-banner .promo-msg { font-size:14px; }
518        #promo-close { right:24px; }
519    }
520</style>
521<div id="promo-banner">
522    <a class="promo-link" href="/mcp">
523        <p class="promo-msg"><i class="fa-solid fa-wand-magic-sparkles" aria-hidden="true"></i> Get the first look at <strong>Cadient Talent MCP</strong> - your ATS, now connected to AI <i class="fa-solid fa-arrow-right" aria-hidden="true"></i></p>
524    </a>
525    <button type="button" id="promo-close" aria-label="Dismiss announcement">
526        <i class="fa-solid fa-xmark" aria-hidden="true"></i>
527    </button>
528</div>
529<script>
530(function () {
531    /* Bump this version to re-show the bar to people who dismissed the last one. */
532    var DISMISS_KEY = 'cadient_promo_banner_dismissed_v2';
533    var banner = document.getElementById('promo-banner');
534    if (!banner) return;
535    try {
536        if (localStorage.getItem(DISMISS_KEY)) { banner.remove(); return; }
537    } catch (e) {}
538    var closeBtn = document.getElementById('promo-close');
539    if (closeBtn) {
540        closeBtn.addEventListener('click', function () {
541            banner.remove();
542            try { localStorage.setItem(DISMISS_KEY, '1'); } catch (e) {}
543        });
544    }
545})();
546</script>
546
547
548<!-- HEADER -->
549<header class="bg-white border-b border-slate-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative">
550        <div class="flex justify-between items-center h-20">
551            <!-- Logo -->
552            <div class="flex-shrink-0 flex items-center">
553                <a href="/"><img src="/static/img/logo.webp" width="300" height="93" class="w-32"
554                        alt="Cadient"></a>
555            </div>
556
557            <!-- Desktop Nav -->
558            <nav class="hidden md:flex flex-1 items-center justify-center space-x-10 lg:space-x-12">
559                <div class="solutions-trigger">
560                    <a href="/solutions"
561                        class="flex items-center gap-1 text-slate-600 hover:text-cadient-blue font-medium transition-colors py-6">
562                        Solutions <i class="fa-solid fa-chevron-down text-xs mt-0.5"></i>
563                    </a>
564                    <div
565                        class="solutions-menu absolute top-full left-1/2 -translate-x-1/2 bg-white rounded-2xl border border-gray-100 z-50 p-7"
566                        style="width: 940px; max-width: calc(100vw - 32px); box-shadow: 0 20px 50px -12px rgba(17,24,39,0.18);">
567                        <div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) 240px; gap: 32px;">
568                            <!-- Recruit -->
569                            <div>
570                                <p class="text-xs font-semibold uppercase tracking-widest text-gray-400 mb-3">Recruit</p>
571                                <div class="space-y-0.5">
572                                    <a href="/smart-automation" class="sol-link flex items-center gap-2 px-2.5 py-2 rounded-lg text-sm">SmartAutomation <span class="sol-new">*New</span></a>
573                                    <a href="/smart-refer/" class="sol-link flex items-center gap-2 px-2.5 py-2 rounded-lg text-sm">SmartRefer <span class="sol-new">NEW</span></a>
574                                    <a href="/smart-source" class="sol-link flex items-center gap-2 px-2.5 py-2 rounded-lg text-sm">SmartSource <span class="sol-new">NEW</span></a>
575                                    <a href="/smart-registry" class="sol-link flex items-center gap-2 px-2.5 py-2 rounded-lg text-sm">SmartRegistry <span class="sol-new">NEW</span></a>
576                                    <a href="/smartsuite/smartmatch/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartMatch</a>
577                                    <a href="/smartsuite/smartscreen/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartScreen</a>
578                                    <a href="/smartsuite/smarttexting/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartTexting</a>
579                                </div>
580                            </div>
581                            <!-- Hire -->
582                            <div>
583                                <p class="text-xs font-semibold uppercase tracking-widest text-gray-400 mb-3">Hire</p>
584                                <div class="space-y-0.5">
585                                    <a href="/smart-shield" class="sol-link flex items-center gap-2 px-2.5 py-2 rounded-lg text-sm">SmartShield <span class="sol-new">NEW</span></a>
586                                    <a href="/smartsuite/smarthire/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartHire</a>
587                                    <a href="/smartsuite/smartinterview/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartInterview</a>
588                                    <a href="/smartsuite/smarttexting/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartTexting</a>
589                                    <a href="/smartsuite/smartoffer/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartOffer</a>
590                                </div>
591                            </div>
592                            <!-- Retain -->
593                            <div>
594                                <p class="text-xs font-semibold uppercase tracking-widest text-gray-400 mb-3">Retain</p>
595                                <div class="space-y-0.5">
596                                    <a href="/smart-communicate/" class="sol-link flex items-center gap-2 px-2.5 py-2 rounded-lg text-sm">SmartCommunicate <span class="sol-new">NEW</span></a>
597                                    <a href="/smartsuite/smarttenure/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartTenure</a>
598                                    <a href="/smartsuite/smartfeedback/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartFeedback</a>
599                                    <a href="/smartsuite/smarttexting/" class="sol-link block px-2.5 py-2 rounded-lg text-sm">SmartTexting</a>
600                                </div>
601                            </div>
602                            <!-- MCP promo -->
603                            <a href="/mcp" class="mcp-promo-card">
604                                <div class="mcp-promo-top">
605                                    <img src="/static/img/logo-mark.webp" alt="" width="28" height="28">
606                                    <span class="sol-new">NEW</span>
607                                </div>
608                                <p class="mcp-promo-title">Cadient Talent&rsquo;s MCP</p>
609                                <p class="mcp-promo-desc">Ask your AI assistant about your hiring data.</p>
610                                <span class="mcp-promo-link">Learn more &rarr;</span>
611                            </a>
612                        </div>
613                        <div class="mt-5 pt-4 border-t border-gray-100">
614                            <a href="/lp/schedule" rel="nofollow"
615                                class="trigger-contact-sales flex items-center justify-center gap-2 text-sm font-medium hover:underline" style="color:#fe4a23;">Not
616                                sure where to start? <strong>Book Consult →</strong></a>
617                        </div>
618                    </div>
619                </div>
620                <a href="/why-cadient"
621                    class="text-slate-600 hover:text-cadient-blue font-medium transition-colors">Why Cadient</a>
622                <a href="/industries/"
623                    class="text-slate-600 hover:text-cadient-blue font-medium transition-colors">Industries</a>
624                <a href="/playbooks/"
625                    class="text-slate-600 hover:text-cadient-blue font-medium transition-colors">Playbooks</a>
626                <a href="/article" class="text-slate-600 hover:text-cadient-blue font-medium transition-colors">Blog</a>
627                <div class="media-trigger relative">
628                    <a href="/press/article"
629                        class="flex items-center gap-1 text-slate-600 hover:text-cadient-blue font-medium transition-colors py-6">
630                        Media <i class="fa-solid fa-chevron-down text-xs mt-0.5"></i>
631                    </a>
632                    <div class="media-menu absolute top-full right-0 bg-white rounded-2xl border border-gray-100 z-50 p-5"
633                        style="width: 360px; box-shadow: 0 20px 50px -12px rgba(17,24,39,0.18);">
634                        <p class="text-[10px] font-bold uppercase tracking-widest text-gray-400 mb-2">Cadient in the Press</p>
635                        
636                        <a href="/article/usatoday-ai-resume-human-touch"
637                            class="media-item block px-2.5 py-3 -mx-2.5 rounded-lg">
638                            <div class="flex items-center gap-2 mb-1">
639                                <span class="text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">USA TODAY</span>
640                                <span class="text-[10px] text-gray-400">Sep 18, 2026</span>
641                            </div>
642                            <p class="text-sm font-semibold text-gray-900 leading-snug">
643                                Bot vs. bot. Why your AI-written résumé needs a human touch
644                            </p>
645                        </a>
646                        
647                        <a href="/article/authority-magazine-bill-mastin-sticky-tech-products"
648                            class="media-item block px-2.5 py-3 -mx-2.5 rounded-lg">
649                            <div class="flex items-center gap-2 mb-1">
650                                <span class="text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">Authority Magazine</span>
651                                <span class="text-[10px] text-gray-400">Aug 6, 2026</span>
652                            </div>
653                            <p class="text-sm font-semibold text-gray-900 leading-snug">
654                                Bill Mastin of Cadient on 5 tricks for creating “sticky” tech products
655                            </p>
656                        </a>
657                        
658                        <a href="/article/forbes-entry-level-jobs-college-graduates"
659                            class="media-item block px-2.5 py-3 -mx-2.5 rounded-lg">
660                            <div class="flex items-center gap-2 mb-1">
661                                <span class="text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">Forbes</span>
662                                <span class="text-[10px] text-gray-400">Aug 2, 2026</span>
663                            </div>
664                            <p class="text-sm font-semibold text-gray-900 leading-snug">
665                                What the changes in entry-level jobs mean for new college graduates
666                            </p>
667                        </a>
668                        
669                        <a href="/article/hr-techseries-hiring-infrastructure-analysis"
670                            class="media-item block px-2.5 py-3 -mx-2.5 rounded-lg">
671                            <div class="flex items-center gap-2 mb-1">
672                                <span class="text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">HR TechSeries</span>
673                                <span class="text-[10px] text-gray-400">Jul 6, 2026</span>
674                            </div>
675                            <p class="text-sm font-semibold text-gray-900 leading-snug">
676                                Analysis of 1,386 employers reveals hiring infrastructure is holding companies back
677                            </p>
678                        </a>
679                        
680                        <a href="/article/hrdive-candidates-overuse-ai"
681                            class="media-item block px-2.5 py-3 -mx-2.5 rounded-lg">
682                            <div class="flex items-center gap-2 mb-1">
683                                <span class="text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">HR Dive</span>
684                                <span class="text-[10px] text-gray-400">Jul 1, 2026</span>
685                            </div>
686                            <p class="text-sm font-semibold text-gray-900 leading-snug">
687                                What happens when candidates overuse AI — and what recruiters can do
688                            </p>
689                        </a>
690                        
691                        <a href="/article/issuewire-ai-resume-verification-crisis"
692                            class="media-item block px-2.5 py-3 -mx-2.5 rounded-lg">
693                            <div class="flex items-center gap-2 mb-1">
694                                <span class="text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">IssueWire</span>
695                                <span class="text-[10px] text-gray-400">Jun 3, 2026</span>
696                            </div>
697                            <p class="text-sm font-semibold text-gray-900 leading-snug">
698                                Cadient report: rise of AI-generated resumes has precipitated a new hiring verification crisis
699                            </p>
700                        </a>
701                        
702                        <div class="mt-2 pt-3 border-t border-gray-100">
703                            <a href="/press/article"
704                                class="text-xs font-bold" style="color:#fe4a23;">All media coverage &rarr;</a>
705                        </div>
706                    </div>
707                </div>
708            </nav>
709
710            <!-- CTA -->
711            <div class="hidden md:flex items-center gap-4">
712                
713                
714                <a href="/auth/login?next=/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/?" rel="nofollow"
715                    class="text-slate-600 hover:text-cadient-blue font-medium transition-colors">Log In</a>
716                <a href="/lp/schedule?source=header&amp;page=/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/" rel="nofollow"
717                    onclick="if(window.openPlaybookScheduler){event.preventDefault();window.openPlaybookScheduler({inline:true,role:'expert'});}"
718                    class="inline-flex items-center text-white px-6 py-2.5 rounded-full font-semibold transition-colors shadow-md"
719                    style="background-color:#fe4a23; box-shadow:0 4px 14px -4px rgba(254,74,35,.5);"
720                    onmouseover="this.style.backgroundColor='#d93d1a'"
721                    onmouseout="this.style.backgroundColor='#fe4a23'">Book session</a>
722                
723            </div>
724
725            <!-- Mobile: optional CTA + menu button -->
726            <div class="md:hidden flex items-center gap-3">
727                <a href="/lp/schedule?source=header&amp;page=/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/" rel="nofollow"
728                    onclick="if(window.openPlaybookScheduler){event.preventDefault();window.openPlaybookScheduler({inline:true,role:'expert'});}"
729                    class="inline-flex items-center whitespace-nowrap rounded-lg border px-3 py-1.5 text-xs font-semibold transition-colors"
730                    style="border-color:#fe4a23; color:#fe4a23;">Book session</a>
731                
732                <button id="mobile-menu-btn" aria-label="Open menu" aria-expanded="false" class="text-slate-600 hover:text-slate-900 focus:outline-none">
733                    <i class="fa-solid fa-bars text-2xl" aria-hidden="true"></i>
734                </button>
735            </div>
736        </div>
737    </div>
738
739    <!-- Mobile Menu -->
740    <div id="mobile-menu"
741        class="hidden md:hidden bg-white border-b border-slate-100 p-4 absolute w-full z-50 max-h-[calc(100vh-80px)] overflow-y-auto">
742        <div class="py-2 border-b border-slate-100">
743            <a href="/solutions"
744                class="flex items-center justify-between px-4 py-2 text-sm font-semibold text-slate-500 uppercase hover:bg-slate-50 rounded-md">Solutions
745                <span aria-hidden="true" class="font-normal">&rarr;</span></a><a href="/mcp" class="mcp-promo-m">
746                <img src="/static/img/logo-mark.webp" alt="" width="34" height="34">
747                <span>
748                    <span class="mcp-promo-m-head">
749                        <span class="sol-new">NEW</span>
750                        <p class="mcp-promo-m-title">Cadient Talent&rsquo;s MCP</p>
751                    </span>
752                    <p class="mcp-promo-m-desc">Connect your ATS to AI.</p>
753                    <span class="mcp-promo-m-link">Learn more &rarr;</span>
754                </span>
755            </a>
756
757            <p class="px-4 pt-2 pb-1 text-[11px] font-bold uppercase tracking-wider text-slate-400">Recruit</p>
758            <div class="pl-4 space-y-1">
759                <a href="/smart-automation" class="flex items-center justify-between px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartAutomation <span class="text-[10px] font-bold text-white px-2 py-0.5 rounded-full" style="background:#fe4a23;">*New</span></a>
760                <a href="/smart-refer/" class="flex items-center justify-between px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartRefer <span class="text-[10px] font-bold text-white px-2 py-0.5 rounded-full" style="background:#fe4a23;">NEW</span></a>
761                <a href="/smart-source" class="flex items-center justify-between px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartSource <span class="text-[10px] font-bold text-white px-2 py-0.5 rounded-full" style="background:#fe4a23;">NEW</span></a>
762                <a href="/smart-registry" class="flex items-center justify-between px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartRegistry <span class="text-[10px] font-bold text-white px-2 py-0.5 rounded-full" style="background:#fe4a23;">NEW</span></a>
763                <a href="/smartsuite/smartmatch/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartMatch</a>
764                <a href="/smartsuite/smartscreen/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartScreen</a>
765                <a href="/smartsuite/smarttexting/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartTexting</a>
766            </div>
767
768            <p class="px-4 pt-2 pb-1 text-[11px] font-bold uppercase tracking-wider text-slate-400">Hire</p>
769            <div class="pl-4 space-y-1">
770                <a href="/smart-shield" class="flex items-center justify-between px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartShield <span class="text-[10px] font-bold text-white px-2 py-0.5 rounded-full" style="background:#fe4a23;">NEW</span></a>
771                <a href="/smartsuite/smarthire/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartHire</a>
772                <a href="/smartsuite/smartinterview/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartInterview</a>
773                <a href="/smartsuite/smarttexting/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartTexting</a>
774                <a href="/smartsuite/smartoffer/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartOffer</a>
775            </div>
776
777            <p class="px-4 pt-2 pb-1 text-[11px] font-bold uppercase tracking-wider text-slate-400">Retain</p>
778            <div class="pl-4 space-y-1">
779                <a href="/smart-communicate/" class="flex items-center justify-between px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartCommunicate <span class="text-[10px] font-bold text-white px-2 py-0.5 rounded-full" style="background:#fe4a23;">NEW</span></a>
780                <a href="/smartsuite/smarttenure/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartTenure</a>
781                <a href="/smartsuite/smartfeedback/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartFeedback</a>
782                <a href="/smartsuite/smarttexting/" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-50 hover:text-cadient-blue rounded-md">SmartTexting</a>
783            </div>
784        </div>
785        <a href="/why-cadient"
786            class="block px-4 py-3 text-slate-600 font-medium hover:bg-slate-50 hover:text-cadient-blue">Why Cadient</a>
787        <a href="/industries/"
788            class="block px-4 py-3 text-slate-600 font-medium hover:bg-slate-50 hover:text-cadient-blue">Industries</a>
789        <a href="/playbooks/"
790            class="block px-4 py-3 text-slate-600 font-medium hover:bg-slate-50 hover:text-cadient-blue">Playbooks</a>
791        <a href="/article"
792            class="block px-4 py-3 text-slate-600 font-medium hover:bg-slate-50 hover:text-cadient-blue">Blog</a>
793        <div class="py-2 border-t border-slate-100"><a href="/press/article"
794                class="flex items-center justify-between px-4 py-2 text-sm font-semibold text-slate-500 uppercase hover:bg-slate-50 rounded-md">Media
795                <span aria-hidden="true" class="font-normal">&rarr;</span></a>
796            <div class="pl-4 space-y-1">
797                
798                <a href="/article/usatoday-ai-resume-human-touch" class="block px-4 py-2 rounded-md hover:bg-slate-50">
799                    <span class="block text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">USA TODAY &middot; Sep 18, 2026</span>
800                    <span class="block text-sm text-slate-700 leading-snug">Bot vs. bot. Why your AI-written résumé needs a human touch</span>
801                </a>
802                
803                <a href="/article/authority-magazine-bill-mastin-sticky-tech-products" class="block px-4 py-2 rounded-md hover:bg-slate-50">
804                    <span class="block text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">Authority Magazine &middot; Aug 6, 2026</span>
805                    <span class="block text-sm text-slate-700 leading-snug">Bill Mastin of Cadient on 5 tricks for creating “sticky” tech products</span>
806                </a>
807                
808                <a href="/article/forbes-entry-level-jobs-college-graduates" class="block px-4 py-2 rounded-md hover:bg-slate-50">
809                    <span class="block text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">Forbes &middot; Aug 2, 2026</span>
810                    <span class="block text-sm text-slate-700 leading-snug">What the changes in entry-level jobs mean for new college graduates</span>
811                </a>
812                
813                <a href="/article/hr-techseries-hiring-infrastructure-analysis" class="block px-4 py-2 rounded-md hover:bg-slate-50">
814                    <span class="block text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">HR TechSeries &middot; Jul 6, 2026</span>
815                    <span class="block text-sm text-slate-700 leading-snug">Analysis of 1,386 employers reveals hiring infrastructure is holding companies back</span>
816                </a>
817                
818                <a href="/article/hrdive-candidates-overuse-ai" class="block px-4 py-2 rounded-md hover:bg-slate-50">
819                    <span class="block text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">HR Dive &middot; Jul 1, 2026</span>
820                    <span class="block text-sm text-slate-700 leading-snug">What happens when candidates overuse AI — and what recruiters can do</span>
821                </a>
822                
823                <a href="/article/issuewire-ai-resume-verification-crisis" class="block px-4 py-2 rounded-md hover:bg-slate-50">
824                    <span class="block text-[10px] font-bold uppercase tracking-wide" style="color:#fe4a23;">IssueWire &middot; Jun 3, 2026</span>
825                    <span class="block text-sm text-slate-700 leading-snug">Cadient report: rise of AI-generated resumes has precipitated a new hiring verification crisis</span>
826                </a>
827                
828                <a href="/press/article" class="block px-4 py-2 text-sm font-bold" style="color:#fe4a23;">All media coverage &rarr;</a>
829            </div>
830        </div>
831        <div class="mt-4 pt-4 border-t border-slate-100 px-4 flex flex-col gap-3">
832            
833            <a href="/lp/schedule?source=header&amp;page=/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/" rel="nofollow"
834                onclick="if(window.openPlaybookScheduler){event.preventDefault();window.openPlaybookScheduler({inline:true,role:'expert'});}"
835                class="block px-4 py-3 text-center text-white font-semibold rounded-lg shadow-sm"
836                style="background-color:#fe4a23;">Book session</a>
837            
838            <a href="/auth/login?next=/resources/case-studies/how-cadients-smarttexting-helped-a-large-regional-grocery-store-reduce-hiring-time-by-41/?" rel="nofollow"
839                class="block px-4 py-3 text-center text-slate-600 font-medium border border-slate-200 rounded-lg hover:bg-slate-50">Log
840                In</a>
841            
842            
843        </div>
844    </div>
845</header>
846</div>
847
848<script>
849/* Mobile menu: exact height, measured in viewport coordinates.
850   ------------------------------------------------------------------------
851   Two inputs, both asked for rather than assumed:
852
853     getBoundingClientRect().top  where the panel actually starts. Read AFTER
854                                  setting `top`, because `top` resolves against
855                                  the containing block and _product_nav.html's
856                                  <nav> has backdrop-blur, which makes it one —
857                                  there, top:91px lands at viewport y=101. The
858                                  rect is always viewport-relative, so measuring
859                                  after the write self-corrects for that.
860     visualViewport.height        the genuinely visible height, excluding browser
861                                  chrome and any on-screen keyboard. This is the
862                                  number vh/svh/dvh only approximate, and the
863                                  reason the panel used to run off the bottom of
864                                  the screen with the toolbar showing.
865
866   height, not max-height: the panel then ends exactly at the visible bottom, so
867   every child is reachable by scrolling.
868
869   Re-runs on visualViewport resize, so showing/hiding the URL bar while the menu
870   is open re-fits it instead of clipping it. */
871(function () {
872    var MIN = 160;
873    var writing = false;
874
875    function panel() { return document.getElementById('mobile-menu'); }
876
877    function isOpen() {
878        var m = panel();
879        /* Not offsetParent: that is null for position:fixed elements, which the
880           panel is — the guard silently bailed every time on blog/_header.html and
881           only appeared to work on _product_nav.html, where the nav's
882           backdrop-filter containing block makes it non-null. Check display. */
883        return !!m && getComputedStyle(m).display !== 'none';
884    }
885
886    function place() {
887        var m = panel();
888        if (!m || !isOpen()) return;
889        var bar = m.parentElement;
890        if (!bar) return;
891        writing = true;
892
893        /* Intent: start just below the header bar. */
894        var barBottom = Math.round(bar.getBoundingClientRect().bottom);
895        var wantTop = Math.max(0, barBottom) + 'px';
896        if (m.style.top !== wantTop) m.style.top = wantTop;
897
898        /* Reality: where that actually put it. getBoundingClientRect is relative to
899           the LAYOUT viewport, and a fixed element is positioned against that too —
900           but visualViewport.height measures the VISUAL viewport. On a phone those
901           two diverge (offsetTop > 0) once the browser shifts the visible area
902           within the layout viewport, so the two numbers have to be brought into
903           the same coordinate space before subtracting. Mixing them made the panel
904           come out short by offsetTop. */
905        var vv = window.visualViewport;
906        var visible = vv ? vv.height : window.innerHeight;
907        var offset = vv ? vv.offsetTop : 0;
908        var topInVisual = m.getBoundingClientRect().top - offset;
909
910        var wantH = Math.max(MIN, Math.round(visible - topInVisual)) + 'px';
911        if (m.style.height !== wantH) m.style.height = wantH;   /* only on change: this
912                                                                   runs every frame */
913        if (m.style.maxHeight !== 'none') m.style.maxHeight = 'none';
914        writing = false;
915    }
916
917    /* Re-measure every frame for as long as the panel is open.
918       ----------------------------------------------------------------
919       This replaced an event-driven approach (visualViewport resize +
920       orientationchange + window resize) that was correct whenever it ran but
921       depended on being *told* the viewport changed. On a phone the browser
922       toolbar sliding in shrinks visualViewport.height, and the last resize event
923       can arrive before the transition settles — so a height computed one frame
924       too early stays in place and the tail of the panel ends up below the visible
925       area. Nothing re-runs, and it looks intermittent because it depends on
926       toolbar state and scroll position at the moment the menu was opened.
927
928       A frame loop cannot be out of date by more than one frame, whatever the
929       browser does or doesn't report. It only runs while the menu is open — a
930       short, idle, user-initiated state — and the writes above are guarded 
930so a
931       steady state performs no style mutations at all. */
932    var raf = null;
933    function loop() {
934        if (!isOpen()) { raf = null; return; }
935        place();
936        raf = requestAnimationFrame(loop);
937    }
938    function startLoop() {
939        place();                       /* immediately, so nothing paints mis-sized */
940        if (raf === null) raf = requestAnimationFrame(loop);
941    }
942
943    var m = panel();
944    if (m) {
945        /* ~28 per-page scripts open this panel, all via classList.toggle('hidden').
946           Observing the attribute catches every one without editing any of them. */
947        new MutationObserver(function () {
948            if (!writing) startLoop();
949        }).observe(m, { attributes: true, attributeFilter: ['class'] });
950    }
951
952    var btn = document.getElementById('mobile-menu-btn');
953    if (btn) btn.addEventListener('click', startLoop);
954
955    /* The frame loop is the real mechanism; these only matter when frames are not
956       being served. rAF is paused in a hidden tab, so a device rotated or resized
957       while backgrounded would otherwise restore mis-sized. */
958    document.addEventListener('visibilitychange', startLoop);
959    window.addEventListener('orientationchange', startLoop);
960    window.addEventListener('resize', startLoop);
961    startLoop();
962})();
963</script>
963<style>
964    /* ── Utilities this partial owns outright — DO NOT move back to classes ──
965       Tailwind's scanner does not follow Jinja includes, and this partial is not
966       listed in tailwind.config.js's `content` array, so every utility it is the
967       only user of gets purged out of static/css/tailwind.css. The classes are
968       still on the markup below (they resolve on the Play-CDN pages, which
969       generate at runtime); these rules are what makes them true on the purged
970       bundle — /solutions, home, blog, playbooks, why-cadient, smartsuite,
971       resources, industries, comparisons, mcp, roi-calculator.
972
973       The one that mattered: `z-[110]` was missing, so the overlay computed to
974       z-index:auto. #site-sticky-top (blog/_header.html, z-index:50) painted
975       over it, and so did the page's own `position:relative` cards — this
976       partial is included from the header, i.e. BEFORE <main> in the DOM, so
977       two z-index:auto positioned elements resolve on source order and the
978       cards win. The open modal rendered underneath the page.
979
980       Declared here rather than fixed in the config on purpose: it holds on
981       both delivery paths and cannot regress the next time a partial is added
982       without being scanned. */
983    #pb-scheduler-modal{z-index:110}                                     /* z-[110] */
984    #pb-scheduler-modal [data-role="sched-panel"]{max-height:92vh}       /* max-h-[92vh] */
985    #pb-scheduler-modal [data-role="sched-skip"]{
986        border-bottom-left-radius:1rem;border-bottom-right-radius:1rem}  /* rounded-b-2xl */
987    #pb-scheduler-modal #pb-scheduler-frame{border:0}                    /* border-0 */
988    #pb-scheduler-modal #pb-scheduler-close:hover{color:#374151}         /* hover:text-gray-700 */
989    @media (min-width:768px){
990        #pb-scheduler-modal [data-role="sched-form"],
991        #pb-scheduler-modal [data-role="sched-thanks"],
992        #pb-scheduler-modal [data-role="sched-skip"]{
993            padding-left:1.5rem;padding-right:1.5rem}                    /* md:px-6 */
994    }
995
996    /* ── Panel transition ───────────────────────────────────────────────
997       The modal had no transition at all — a straight display:flex/none. Both
998       presentations now animate off .is-open, which the JS toggles a reflow after
999       display is set. */
1000    #pb-scheduler-modal [data-role="sched-panel"]{
1001        transition:transform .30s cubic-bezier(.32,.72,0,1),opacity .22s ease}
1002    #pb-scheduler-modal{opacity:0;transition:opacity .22s ease}
1003    #pb-scheduler-modal.is-open{opacity:1}
1004
1005    /* Desktop: a slight rise, nothing more. */
1006    #pb-scheduler-modal [data-role="sched-panel"]{transform:translateY(10px)}
1007    #pb-scheduler-modal.is-open [data-role="sched-panel"]{transform:none}
1008
1009    /* Inline SVG, not Font Awesome: this partial is included by blog pages
1010       (which load FA) AND by the product dashboards (which use Lucide), so a
1011       hardcoded FA class rendered as an empty box in half the app. 1em keeps
1012       them sized by the surrounding font-size, exactly like the icon font. */
1013    .pbs-ico{width:1em;height:1em;display:inline-block;vertical-align:-0.125em;flex-shrink:0}
1014    .pbs-ico-sm{width:.75em;height:.75em}
1015    .pbs-ico-lg{width:1.125em;height:1.125em}
1016
1017    /* Own the body's scrollbar. The panel body carries Tailwind's
1018       .overflow-y-auto, and smart-refer/dashboard.html styles THAT CLASS
1019       globally for its dark sidebar — a #0f172a track with a periwinkle-edged
1020       thumb — so the modal rendered a near-black scrollbar inside a white
1021       dialog on that page and a default grey one everywhere else. Keyed on the
1022       id + attribute so it outranks the page's class selector wherever this
1023       partial is embedded. */
1024    #pb-scheduler-modal [data-role="sched-body"]{
1025        scrollbar-width:thin;scrollbar-color:#d4d4d8 transparent}
1026    #pb-scheduler-modal [data-role="sched-body"]::-webkit-scrollbar{width:8px}
1027    #pb-scheduler-modal [data-role="sched-body"]::-webkit-scrollbar-track{
1028        background:transparent}
1029    #pb-scheduler-modal [data-role="sched-body"]::-webkit-scrollbar-thumb{
1030        background:#d4d4d8;border-radius:9999px;
1031        border:2px solid transparent;background-clip:content-box}
1032    #pb-scheduler-modal [data-role="sched-body"]::-webkit-scrollbar-thumb:hover{
1033        background:#a1a1aa;background-clip:content-box}
1034
1035    /* Grip is mobile-only; on desktop the sheet metaphor doesn't apply. */
1036    .pbs-grip{display:none}
1037
1038    /* ── Step 2 (borrowed form) ─────────────────────────────────────────
1039       Two things kept step 2 reading as a different popup rather than the
1040       same one:
1041       1. HEIGHT. Hiding the 560px iframe collapsed the panel to the form's own
1042          height — a ~290px drop, and a different aspect ratio. showFormPane
1043          pins this pane (and the header) to the height step 1 measured, so the
1044          panel does not change size at all between steps. Content is anchored
1045          to the TOP of that space: centring it left the form floating in the
1046          middle of a tall box, which looked worse than the jump.
1047       2. WIDTH. The panel is 672px; a form stretched across all of it reads as
1048          a clumsier, unrelated dialog. The column keeps the fields at the same
1049          measure they had in the standalone card. */
1050    /* Centred, not top-anchored: with the session block in place the column is
1051       ~420px of the ~600px pane, so the remaining space splits into two modest
1052       margins and reads as breathing room. (Centring was wrong when the form
1053       was alone — 340px of content left it floating.) */
1054    /* Biased above centre. True centring measured even, but read bottom-heavy:
1055       the header block above adds visual weight the maths doesn't account for,
1056       so the optical centre sits higher than the geometric one. The extra
1057       bottom padding shrinks the box the content centres within, lifting it. */
1058    #pb-scheduler-modal [data-role="sched-form"]{
1059        padding-top:1.5rem;padding-bottom:4.5rem;
1060        display:flex;flex-direction:column;align-items:center;justify-content:center}
1061    .pbs-formcol{width:100%;max-width:28rem}
1062
1063    /* Asset card. Deliberately NOT shaped like the panel header above it: that
1064       is a round accent medallion + bold heading + grey subline on white, and
1065       repeating the pattern here made the step look like the heading had been
1066       pasted in twice. This is a tinted, bordered card with a portrait document
1067       tile, a micro eyebrow and body-weight text — a thing being described,
1068       rather than a second title. */
1069    .pbs-asset{width:100%;margin-bottom:1.75rem;padding:1rem 1.125rem;
1070        background:#fffaf8;border:1px solid #ffe0d6;border-radius:.875rem}
1071    .pbs-asset-main{display:flex;align-items:flex-start;gap:.875rem}
1072    .pbs-doc{width:2.75rem;height:3.5rem;flex-shrink:0;border-radius:.375rem;
1073        display:flex;align-items:center;justify-content:center;
1074        background:#ffffff;border:1px solid #ffd5c8;color:#fe4a23;font-size:1.125rem;
1075        box-shadow:0 1px 2px rgba(17,17,17,.05)}
1076    .pbs-asset-txt{min-width:0;flex:1}
1077    .pbs-eyebrow{margin:0 0 .25rem;font-size:.6875rem;font-weight:700;
1078        letter-spacing:.08em;text-transform:uppercase;color:#c2410c}
1079    .pbs-asset-title{margin:0;font-size:.9375rem;font-weight:600;color:#111827;
1080        line-height:1.35}
1081    .pbs-asset-blurb{margin:.25rem 0 0;font-size:.8125rem;line-height:1.5;color:#6b7280}
1082    .pbs-asset-slot{display:flex;align-items:center;gap:.5rem;
1083        margin:.875rem 0 0;padding-top:.75rem;border-top:1px solid #ffe0d6;
1084        font-size:.8125rem;font-weight:600;color:#374151}
1085    .pbs-asset-slot svg{color:#fe4a23}
1086    .pbs-change{margin-left:auto;font-size:.75rem;font-weight:400;color:#9ca3af;
1087        text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}
1088    .pbs-change:hover{color:#4b5563}
1089
1090    /* Scale the borrowed form up for the roomier panel. Scoped to this modal,
1091       so the standalone card (used when the panel can't host the form) keeps
1092       its original compact proportions. */
1093    /* The form's own legend and helper text ("Send it to your work email" /
1094       "We'll send the playbook straight to you") are redundant in here: the
1095       panel header already asks the question and the fields are labelled. They
1096       stay in the standalone card, which has no header to lean on. */
1097    #pb-scheduler-modal .pbf-legend,
1098    #pb-scheduler-modal .pbf-help{display:none}
1099    #pb-scheduler-modal .pbf-field{margin-bottom:.875rem;padding:0 1.125rem;
1100        border-radius:.875rem}
1101    #pb-scheduler-modal .pbf-field input{padding:1.0625rem 0;font-size:.9375rem}
1102    #pb-scheduler-modal .pbf-ico{font-size:1rem}
1103    #pb-scheduler-modal .pbf-submit{margin-top:.75rem;padding:1.0625rem 1.25rem;
1104        font-size:.9375rem;border-radius:.875rem}
1105    #pb-scheduler-modal .pbf-privacy{margin-top:1.25rem;font-size:.8125rem}
1106
1107    @media (max-width: 767.98px){
1108        /* ── Bottom sheet ──────────────────────────────────────────────
1109           Anchored to the bottom edge, full width, square at the bottom. */
1110        #pb-scheduler-modal{align-items:flex-end;padding:0}
1111        #pb-scheduler-modal [data-role="sched-panel"]{
1112            max-width:none;width:100%;
1113            border-radius:1.25rem 1.25rem 0 0;
1114            transform:translateY(100%);
1115            /* EXPLICIT height, not max-height. The body is flex:1 containing an
1116               iframe at height:100%, and a percentage needs a resolved parent
1117               height to compute against — with max-height alone the iframe
1118               collapses to its min-height and the time slots vanish below the
1119               fold. That was the "slots are not visible" bug on the sheet this
1120               replaces.
1121               vh first as the fallback, then dvh, which tracks the mobile URL bar
1122               collapsing where vh does not. */
1123            height:calc(92vh - 40px);
1124            height:calc(92dvh - 40px);
1125            max-height:none;
1126            /* Keeps the last row of slots clear of the iOS home indicator. */
1127            padding-bottom:env(safe-area-inset-bottom,0px)}
1128        #pb-scheduler-modal.is-open [data-role="sched-panel"]{transform:translateY(0)}
1129
1130        /* The sheet's bottom corners are square, so the pinned skip footer must
1131           be too — it is the panel's last child and would otherwise round
1132           against a straight edge. */
1133        #pb-scheduler-modal [data-role="sched-skip"]{border-radius:0}
1134
1135        /* Follows the finger during a drag — instant, no easing. */
1136        #pb-scheduler-modal [data-role="sched-panel"].is-dragging{transition:none}
1137
1138        .pbs-grip{display:flex;align-items:center;justify-content:center;
1139            padding:10px 0 2px;flex-shrink:0;cursor:grab;touch-action:none}
1140        .pbs-grip span{display:block;width:40px;height:4px;border-radius:999px;
1141            background:#D7D3CE}
1142        .pbs-grip:active{cursor:grabbing}
1143        /* The header's own top padding is redundant under the grip. */
1144        #pb-scheduler-modal [data-role="sched-panel"] > div:nth-of-type(2){
1145            padding-top:.5rem !important}
1146    }
1147
1148    @media (prefers-reduced-motion: reduce){
1149        #pb-scheduler-modal,
1150        #pb-scheduler-modal [data-role="sched-panel"]{transition:none}
1151        #pb-scheduler-modal [data-role="sched-panel"]{transform:none}
1152    }
1153</style>
1154    <!-- ══════════════════════════════════════════════════════
1155     Scheduler modal — opened automatically after a successful download
1156     capture (mirrors the playbook category pages). Embeds /lp/schedule
1157     and forwards source/topic/email through to /lp/capture.
1158     ══════════════════════════════════════════════════════ -->
1159    <div id="pb-scheduler-modal" class="fixed inset-0 z-[110] items-center justify-center px-4 py-6"
1160        style="display:none; background-color:rgba(17,17,17,0.6);">
1161        <div data-role="sched-panel"
1162            class="bg-white rounded-2xl shadow-2xl max-w-2xl w-full relative flex flex-col max-h-[92vh]"><div data-role="sched-grip" class="pbs-grip" aria-hidden="true"><span></span></div>
1163            <div class="px-6 pb-3 border-b border-gray-100 flex-shrink-0" style="padding-top:1.75rem;">
1164                <div class="flex items-start gap-3">
1165                    <span data-role="sched-headicon"
1166                        class="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 mt-0.5"
1167                        style="background-color:#fff2ef;">
1168                        <svg class="pbs-ico" style="color:#fe4a23;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/><path d="m9 16 2 2 4-4"/></svg>
1169                    </span>
1170                    <div data-role="sched-headtext" class="flex-1 min-w-0"><h2 data-role="sched-head" class="text-lg md:text-xl font-bold text-gray-900 leading-snug">
1171                            Want a quick walkthrough?
1172                        </h2>
1173                        <p data-role="sched-sub" class="text-sm text-gray-500 mt-1 leading-relaxed">
1174                            <span data-role="sched-topic">Your playbook</span> is on its way to your inbox. Grab 30
1175                            minutes with our team to see how it applies to your hiring — no pressure.
1176                        </p>
1177                    </div>
1178                    <button type="button" id="pb-scheduler-close" aria-label="Close"
1179                        class="flex-shrink-0 w-9 h-9 flex items-center justify-center rounded-full text-gray-400 hover:text-gray-700 transition"
1180                        style="margin-top:-0.25rem; margin-right:-0.5rem;">
1181                        <svg class="pbs-ico pbs-ico-lg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 6 6 18M6 6l12 12"/></svg>
1182                    </button>
1183                </div>
1184            </div>
1185            <div class="flex-1 overflow-y-auto" data-role="sched-body">
1186                <iframe id="pb-scheduler-frame" title="Schedule a session" loading="lazy" class="w-full block border-0"
1187                    style="height:560px;" src="about:blank"></iframe><div data-role="sched-form" class="px-5 md:px-6 pb-6" style="display:none;">
1188                    
1189                    <div class="pbs-formcol">
1190                        
1191                        <div data-role="sched-summary" class="pbs-asset">
1192                            <div class="pbs-asset-main">
1193                                
1194                                <span class="pbs-doc" aria-hidden="true">
1195                                    <svg class="pbs-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M12 12v5"/><path d="m9.5 14.5 2.5 2.5 2.5-2.5"/></svg>
1196                                </span>
1197                                <div class="pbs-asset-txt">
1198                                    <p class="pbs-eyebrow">You'll receive</p>
1199                                    <p class="pbs-asset-title" data-role="sched-assettitle">Your playbook</p>
1200                                    <p class="pbs-asset-blurb" data-role="sched-assetblurb"></p>
1201                                </div>
1202                            </div>
1203                            <p class="pbs-asset-slot" data-role="sched-slotrow" style="display:none;">
1204                                <svg class="pbs-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/><path d="m9 16 2 2 4-4"/></svg>
1205                                <span data-role="sched-slottext"></span>
1206                                <button type="button" data-role="sched-slotchange" class="pbs-change">change</button>
1207                            </p>
1208                        </div>
1209                        <div data-role="sched-formhost"></div>
1210                    </div>
1211                </div><div data-role="sched-thanks" class="px-5 md:px-6 py-2" style="display:none;"></div>
1212            </div><div data-role="sched-skip"
1213                class="px-5 md:px-6 py-4 border-t border-gray-100 bg-white text-center flex-shrink-0 rounded-b-2xl"
1214                style="display:none;">
1215                <button type="button" data-role="sched-skip-btn"
1216                    class="inline-flex items-center gap-1.5 text-sm font-semibold transition"
1217                    style="color:#fe4a23;"
1218                    onmouseover="this.style.color='#d93d1a';"
1219                    onmouseout="this.style.color='#fe4a23';">
1220                    <span data-role="sched-skip-label">Just email me the playbook</span>
1221                    <svg class="pbs-ico pbs-ico-sm" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
1222                </button>
1223            </div>
1224        </div>
1225    </div>
1226
1227    
1227<script>
1228        (function () {
1229            var modal = document.getElementById('pb-scheduler-modal');
1230            if (!modal) return;
1231            var frame = document.getElementById('pb-scheduler-frame');
1232            var topicEl = modal.querySelector('[data-role="sched-topic"]');
1233
1234            window.openPlaybookScheduler = function (ctx) {
1235                ctx = ctx || {};
1236                // Re-opening while a form is still borrowed (the chip's "change",
1237                // or a second entry point): give it back first, or the restore
1238                // target would become this panel's own host and the node would
1239                // be stranded here when the modal closes.
1240                restoreFormPane();
1241                resetThanks();
1242                // Carry context through the scheduler to /lp/capture.
1243                //
1244                // Seeded from the PARENT page's query string, not empty. This
1245                // used to be `new URLSearchParams()`, so a visitor who landed
1246                // on ?utm_source=google&gclid=... and opened this modal sent
1247                // an iframe URL with no attribution on it at all. The comment
1248                // inside lp-schedule.html says "Preserve existing query params
1249                // (UTMs)" — but inside an iframe `window.location.search` is
1250                // the iframe's own URL, i.e. whatever we build here. So the
1251                // UTMs were dropped at this boundary and /lp/capture rendered
1252                // five blank hidden inputs (LEAD_AUDIT.md F6).
1253                //
1254                // Whitelisted rather than copied wholesale: the parent URL can
1255                // carry things that must not end up in an iframe src (?email=,
1256                // tokens, one-time codes).
1257                var CARRY = ['utm_source', 'utm_medium', 'utm_campaign',
1258                             'utm_content', 'utm_term',
1259                             'gclid', 'gbraid', 'wbraid', 'fbclid',
1260                             'msclkid', 'li_fat_id', 'ttclid'];
1261                var parentParams = new URLSearchParams(window.location.search);
1262                var params = new URLSearchParams();
1263                CARRY.forEach(function (k) {
1264                    var v = parentParams.get(k);
1265                    if (v) params.set(k, v);
1266                });
1267                // The page the visitor is converting FROM. Distinct from the
1268                // first-touch landing URL the server reads out of the cookie —
1269                // this is the conversion page, and /lp/capture forwards it to
1270                // Zoho as Referral_Link.
1271                params.set('referral_page', window.location.href);
1272                // ctx.source lets a caller label its own lead source; the two
1273                // playbook values stay the default so existing callers are unchanged.
1274                params.set('source', ctx.source || (ctx.inline ? 'playbook_inline' : 'playbook'));
1275                if (ctx.page) params.set('topic', ctx.page);
1276                if (ctx.email) params.set('email', ctx.email);
1277                // Scheduler-first: we don't know who this is yet, so the iframe
1278                // must hand the slot back instead of trying to book it against a
1279                // lead that doesn't exist. See lp-schedule.html.
1280                slotHandler = (typeof ctx.onSlot === 'function') ? ctx.onSlot : null;
1281                if (slotHandler) params.set('flow', 'slot_first');
1282                // Optional: host the caller's form in this same panel for step 2
1283                // instead of handing back to a second modal.
1284                // Adopted for the scheduler-first flow (ctx.onSlot) and for
1285                // re-opening a finished one (ctx.startAtForm), which has no
1286                // calendar step and therefore no onSlot.
1287                formPane = ((slotHandler || ctx.startAtForm) &&
1288                            ctx.formPane && ctx.formPane.nodeType === 1)
1289                    ? ctx.formPane : null;
1290                formHead = ctx.formHead || 'Where should we send it?';
1291                formSub = ctx.formSub || '';
1292                // What they're getting, named per playbook. textContent, so
1293                // editor-supplied category copy can never inject markup.
1294                assetTitle = ctx.assetTitle || ctx.offer || 'Your playbook';
1295                assetBlurb = ctx.assetBlurb || '';
1296                if (frame) { resetFrameHeight(); frame.src = '/lp/schedule?' + params.toString(); }
1297
1298                // The skip is meaningful when something waits behind this modal
1299                // (a scheduler-first form) or when the caller says the whole
1300                // prompt is optional — the onboarding greeting has no second
1301                // step, it just needs a way out that isn't the X.
1302                dismissHandler = (typeof ctx.onDismiss === 'function') ? ctx.onDismiss : null;
1303                if (skipWrap) {
1304                    skipWrap.style.display = (slotHandler || ctx.dismissible) ? '' : 'none';
1305                }
1306                // "playbook" is wrong on the hub, where the asset is whatever the
1307                // widget is offering — let the caller name it. textContent, so a
1308                // caller's own copy can never inject markup.
1309                if (skipLabel) skipLabel.textContent = ctx.skipLabel || 'Just email me the playbook';
1310
1311                // Header + subhead adapt to the entry point. The download flow keeps
1312                // its "on its way to your inbox" confirmation; a cold mid-article
1313                // click (ctx.inline) gets a neutral "talk to an expert" framing.
1314                var headEl = modal.querySelector('[data-role="sched-head"]');
1315                var subEl = modal.querySelector('[data-role="sched-sub"]');
1316                if (ctx.head || ctx.sub) {
1317                    // Caller-supplied copy, for entry points that need their own
1318                    // confirmation — e.g. the Solutions Lab brief, which opens this
1319                    // modal to say the brief was emailed and then show slots.
1320                    // textContent, not innerHTML: this text can originate from a
1321                    // page's own JS and must never be able to inject markup.
1322                    if (ctx.head && headEl) headEl.textContent = ctx.head;
1323                    if (ctx.sub && subEl) subEl.textContent = ctx.sub;
1324                } else if (ctx.inline) {
1325                    if (headEl) headEl.textContent = 'Talk to a Cadient ' + (ctx.role || 'expert');
1326                    if (subEl) subEl.textContent =
1327                        'Book 30 minutes to talk through ' + (ctx.topic || 'your hiring') +
1328                        ' — no pitch, just answers.';
1329                } else {
1330                    if (headEl) headEl.textContent = 'Want a quick walkthrough?';
1331                    if (subEl) subEl.innerHTML =
1332                        '<span data-role="sched-topic">Your playbook</span> is on its way to ' +
1333                        'your inbox. Grab 30 minutes with our team to see how it applies to ' +
1334                        'your hiring — no pressure.';
1335                    var t = modal.querySelector('[data-role="sched-topic"]');
1336                    if (t) t.textContent = ctx.offer
1337                        ? ('Your playbook, “' + ctx.offer + ',”')
1338                        : 'Your playbook';
1339                }
1340                // A cold open (sidebar card, header CTA) is not part of the
1341                // download flow, so closing must not fire its callbacks —
1342                // otherwise dismissing this would surface a "check your inbox"
1343                // card for a download the visitor never requested.
1344                coldOpen = !!ctx.inline;
1345                // Per-open callback, from the copy this partial absorbed. Takes
1346                // precedence over the window.__pb* globals below: it lets the
1347                // originating widget decide its own end state, which the globals
1348                // can't do when more than one widget is on the page.
1349                currentResolve = (typeof ctx.onResolve === 'function') ? ctx.onResolve : null;
1350                // Every entry point ends on the confirmation now, so this defaults
1351                // rather than being opt-in. A caller overrides it only to add flow
1352                // context — the playbook forms append products=1 and an article
1353                // slug, which is what makes the two product cards appear; without
1354                // that the confirmation renders without the products section.
1355                //
1356                // Relative paths only, so a caller can't point this at another
1357                // origin and have its markup injected into the modal.
1358                thankYouUrl = (typeof ctx.thankYou === 'string' &&
1359                               ctx.thankYou.charAt(0) === '/' &&
1360                               ctx.thankYou.charAt(1) !== '/')
1361                    ? ctx.thankYou
1362                    : '/playbooks/thank-you';
1363                booked = false;
1364                handoffDone = false;
1365                openModalUI();
1366
1367                // ctx.startAtForm: re-opening a flow that already finished, so
1368                // there is nothing to book — jump straight to the form pane and
1369                // never load the calendar. Keeps the confirmation in THIS panel
1370                // at its usual width, instead of a second, smaller dialog.
1371                if (ctx.startAtForm && formPane) {
1372                    if (frame) { resetFrameHeight(); frame.src = 'about:blank'; }
1373                    handoffDone = true;
1374                    slotHandler = null;
1375                    showFormPane(ctx.slot || null, true);
1376                }
1377                };
1378
1379                var booked = false;
1380                var coldOpen = false;
1381                var currentResolve = null;
1382                var thankYouUrl = null;
1383                var thanksShown = false;
1384                // Scheduler-first state. slotHandler is set per open by ctx.onSlot;
1385                // handoffDone marks that we've already passed control back to the
1386                // caller (via a slot pick or the skip), so the close that follows
1387                // must not also fire the dismissal callbacks — the flow is
1388                // continuing in the caller's form, not ending here.
1389                var slotHandler = null;
1390                var handoffDone = false;
1391                var skipWrap = modal.querySelector('[data-role="sched-skip"]');
1392                var skipBtn = modal.querySelector('[data-role="sched-skip-btn"]');
1393                var skipLabel = modal.querySelector('[data-role="sched-skip-label"]');
1394                // In-panel step 2. formPane is the caller's node while we're
1395                // borrowing it; formHome remembers exactly where to put it back
1396                // (parent + the sibling it sat before), so restoring can't
1397                // reorder the page.
1398                var formWrap = modal.querySelector('[data-role="sched-form"]');
1399                var formHost = modal.querySelector('[data-role="sched-formhost"]');
1400                var assetTitleEl = modal.querySelector('[data-role="sched-assettitle"]');
1401                var assetBlurbEl = modal.querySelector('[data-role="sched-assetblurb"]');
1402                var slotRow = modal.querySelector('[data-role="sched-slotrow"]');
1403                var slotText = modal.querySelector('[data-role="sched-slottext"]');
1404                var slotChange = modal.querySelector('[data-role="sched-slotchange"]');
1405                var formPane = null, formHome = null, formHead = '', formSub = '';
1406                var assetTitle = '', assetBlurb = '';
1407                // Set by ctx.onDismiss. Fires once, however the visitor leaves —
1408                // skip link, X, Escape or backdrop — so a one-time prompt is
1409                // never shown twice because they closed it the "wrong" way.
1410                var dismissHandler = null;
1411                var thanksEl = modal.querySelector('[data-role="sched-thanks"]');
1412                // The header BAR stays while the confirmation shows — it carries
1413                // the close button, and hiding the whole block left the visitor with
1414                // no X at all. Only the icon and the heading text go, since the
1415                // confirmation has its own heading.
1416                var headBlock = modal.querySelector('[data-role="sched-head"]')
1417                    ? modal.querySelector('[data-role="sched-head"]').closest('.border-b')
1418                    : null;
1419                var headIcon = modal.querySelector('[data-role="sched-headicon"]');
1420                var headText = modal.querySelector('[data-role="sched-headtext"]');function showFormPane(slot, noPin) {
1421                    if (!formPane || !formHost) return false;
1422                    // Lock step 2 to the exact height step 1 occupied, measured
1423                    // rather than hardcoded — the calendar's height varies with
1424                    // the slot grid, and the CSS min-height alone still left the
1425                    // panel shrinking by however tall the skip block was. Desktop
1426                    // only: the mobile sheet is already a fixed height, and
1427                    // forcing this there would push the submit button off-screen.
1428                    // Hold the panel at exactly the size step 1 occupied. Measured,
1429                    // not hardcoded: the calendar's height varies with the slot
1430                    // grid. Both halves matter — the body (which loses the 560px
1431                    // iframe and the skip block) and the header (whose two-line
1432                    // subhead becomes one line), or the panel still shifts.
1433                    // noPin: no calendar step preceded this open, so there is no
1434                    // height to match — let the pane size to its content.
1435                    var bodyEl = modal.querySelector('[data-role="sched-body"]');
1436                    if (!noPin && bodyEl && formWrap && window.innerWidth >= 768) {
1437                        // The skip footer lives OUTSIDE the body and is hidden a
1438                        // few lines below, so its height has to be folded into
1439                        // the pin or the panel shrinks by exactly that much when
1440                        // step 2 appears.
1441                        var footerH = (skipWrap && skipWrap.offsetParent !== null)
1442                            ? skipWrap.offsetHeight : 0;
1443                        formWrap.style.minHeight = (bodyEl.offsetHeight + footerH) + 'px';
1444                        if (headBlock) headBlock.style.minHeight = headBlock.offsetHeight + 'px';
1445                    }
1446                    formHome = { parent: formPane.parentNode, before: formPane.nextSibling };
1447                    formHost.appendChild(formPane);
1448                    if (frame) frame.style.display = 'none';
1449                    if (skipWrap) skipWrap.style.display = 'none';
1450                    if (assetTitleEl) assetTitleEl.textContent = assetTitle;
1451                    if (assetBlurbEl) {
1452                        assetBlurbEl.textContent = assetBlurb;
1453                        // No category description configured — drop the line
1454                        // rather than leaving an empty gap under the title.
1455                        assetBlurbEl.style.display = assetBlurb ? '' : 'none';
1456                    }
1457
1458                    // The summary block itself is always shown; only the booked
1459                    // slot line is conditional.
1460                    if (slotRow) {
1461                        if (slot && slotText) {
1462                            slotText.textContent = slot;
1463                            slotRow.style.display = '';
1464                        } else {
1465                            slotRow.style.display = 'none';
1466                        }
1467                    }
1468                    var headEl = modal.querySelector('[data-role="sched-head"]');
1469                    var subEl = modal.querySelector('[data-role="sched-sub"]');
1470                    if (headEl) headEl.textContent = formHead;
1471                    if (subEl) subEl.textContent = formSub;
1472                    formWrap.style.display = '';
1473                    var body = modal.querySelector('[data-role="sched-body"]');
1474                    if (body) body.scrollTop = 0;
1475
1476                    // Focus the first empty field so the visitor can just type.
1477                    var firstInput = formPane.querySelector('input[type="email"], input[type="text"]:not([tabindex="-1"])');
1478                    if (firstInput) setTimeout(function () { firstInput.focus(); }, 120);
1479                    return true;
1480                }function restoreFormPane() {
1481                    if (formPane && formHome && formHome.parent) {
1482                        formHome.parent.insertBefore(formPane, formHome.before);
1483                    }
1484                    formPane = null;
1485                    formHome = null;
1486                    if (formWrap) { formWrap.style.display = 'none'; formWrap.style.minHeight = ''; }
1487                    if (headBlock) headBlock.style.minHeight = '';
1488                    if (slotRow) slotRow.style.display = 'none';
1489                    // Drop any leftover animation state, or the next open would
1490                    // start pinned to the previous step's height.
1491                    if (panel) panel.style.height = '';
1492                }
1493
1494                // "change" on the chip: back to the calendar, same panel.
1495                if (slotChange) slotChange.addEventListener('click', function () {
1496                    if (typeof window.__pbReopenScheduler === 'function') window.__pbReopenScheduler();
1497                });
1498
1499                function handoff(slot, tz) {
1500                    if (handoffDone || !slotHandler) return;
1501                    handoffDone = true;
1502                    var fn = slotHandler;
1503                    slotHandler = null;
1504                    // With a form pane the panel STAYS OPEN and swaps its body;
1505                    // without one we fall back to the original behaviour of
1506                    // closing and letting the caller open its own surface.
1507                    if (!showFormPane(slot)) closeModalUI();
1508                    fn(slot || null, tz || null);
1509                }
1510
1511                function fireDismiss() {
1512                    if (!dismissHandler) return false;
1513                    var fn = dismissHandler;
1514                    dismissHandler = null;   // once only
1515                    try { fn(); } catch (e) { /* caller's problem, not ours */ }
1516                    return true;
1517                }
1518
1519                if (skipBtn) skipBtn.addEventListener('click', function () {
1520                    // A dismissible prompt with no form behind it: record the
1521                    // dismissal and close, rather than handing off to a step
1522                    // that doesn't exist.
1523                    if (!slotHandler && dismissHandler) { fireDismiss(); closeScheduler(); return; }
1524                    handoff(null, null);
1525                });
1526
1527                function closeScheduler() {
1528                    // However this prompt is being left — skip, X, Escape,
1529                    // backdrop — it counts as shown.
1530                    fireDismiss();
1531                    // Step 2 is showing in this panel: close for real and give the
1532                    // borrowed form back to the page. No end-of-flow callbacks —
1533                    // the visitor abandoned, they didn't finish.
1534                    if (handoffDone) {
1535                        closeModalUI();
1536                        setTimeout(function () { restoreFormPane(); resetThanks(); }, 320);
1537                        return;
1538                    }
1539                    // Scheduler-first WITHOUT an in-panel form: dismissing is
1540                    // treated as the skip, so the visitor still reaches the
1541                    // caller's own form rather than losing the download.
1542                    // With a form pane we don't do that — the panel would appear
1543                    // to ignore the X. The skip link is the route to step 2.
1544                    if (slotHandler && !formPane) { handoff(null, null); return; }
1545                    if (slotHandler) { slotHandler = null; formPane = null; }
1546
1547                    closeModalUI();
1548
1549                    var resolve = currentResolve;
1550                    var didBook = booked;
1551                    currentResolve = null;
1552                    booked = false;
1553                    // Cleared here too, so a later cold open can't inherit a
1554                    // thank-you target from a dismissed download flow.
1555                    thankYouUrl = null;
1556                    resetThanks();
1557
1558                    // An explicit per-open callback is honoured whether or not this
1559                    // was a cold open. A caller that passed onResolve has asked to be
1560                    // told the outcome; coldOpen exists to suppress the GLOBAL __pb*
1561                    // download fallbacks below, which is what it was added for.
1562                    //
1563                    // Without this the Solutions Lab could never learn that a booking
1564                    // happened — it opens inline — so its done state went on offering
1565                    // "Impatient? Grab a slot on our calendar" to someone who had just
1566                    // grabbed one.
1567                    if (resolve) {
1568                        coldOpen = false;
1569                        resolve(didBook);
1570                        return;
1571                    }
1572                    if (coldOpen) {
1573                        // Not part of a download flow and no explicit callback, so the
1574                        // globals must not fire — otherwise dismissing this would
1575                        // surface a "check your inbox" card for a download the visitor
1576                        // never requested.
1577                        coldOpen = false;
1578                        return;
1579                    }
1580                    if (didBook) {
1581                        // A booking completed inside the iframe and its confirmation
1582                        // already showed — close the underlying download modal too, so
1583                        // we don't re-surface the "check your inbox" card. The flow
1584                        // ends on the booking confirmation (single success message).
1585                        if (window.__pbCloseDownloadModal) window.__pbCloseDownloadModal();
1586                    } else {
1587                        // Visitor dismissed the walkthrough without booking — now show
1588                        // the inline download confirmation underneath.
1589                        if (window.__pbShowDownloadSuccess) window.__pbShowDownloadSuccess();
1590                    }
1591                }
1592
1593                // ── Show / hide ───────────────────────────────────────────
1594                // Two steps, because the panel now animates: display first so the
1595                // element has a box, then .is-in on the next paint to trigger the
1596                // transition. The reflow between them is a synchronous
1597                // `void offsetWidth`, NOT requestAnimationFrame — rAF is paused in
1598                // hidden and throttled tabs, which would leave the sheet stuck at
1599                // opacity 0 with no error. That exact bug cost a debugging session
1600                // on the consult sheet this replaces.
1601                var panel = modal.querySelector('[data-role="sched-panel"]') ||
1602                            modal.firstElementChild;
1603                var closeT = null;
1604
1605                
1606                
1607                var IFRAME_BASE_H = 560;
1608                var frameRO = null;
1609
1610                function fitFrameToContent() {
1611                    if (!frame) return;
1612                    var doc;
1613                    try { doc = frame.contentDocument; } catch (e) { return; }
1614                    if (!doc || !doc.body) return;
1615                    var h = Math.max(doc.documentElement ? doc.documentElement.scrollHeight : 0,
1616                                     doc.body.scrollHeight, 0);
1617                    if (h > 0) frame.style.height = h + 'px';
1618                }
1619
1620                function watchFrameHeight() {
1621                    var doc, win;
1622                    try { doc = frame && frame.contentDocument; win = frame && frame.contentWindow; }
1623                    catch (e) { return; }
1624                    if (!doc || !doc.body) return;
1625                    fitFrameToContent();
1626                    if (frameRO) { frameRO.disconnect(); frameRO = null; }
1627
1628                    // The grid grows and shrinks as the visitor changes date or
1629                    // the morning/evening tab, so one measurement is never
1630                    // enough. Note the constructor: it must be the IFRAME's
1631                    // ResizeObserver, not ours. Observations belong to the
1632                    // document that created the observer, so a parent-window
1633                    // observer watching a node inside the child silently never
1634                    // fires — the box stayed at its 560px placeholder while the
1635                    // content underneath grew to 605.
1636                    if (win && typeof win.ResizeObserver === 'function') {
1637                        frameRO = new win.ResizeObserver(fitFrameToContent);
1638                        frameRO.observe(doc.body);
1639                    }
1640
1641                    // Safety net for the first render: the slot grid arrives
1642                    // from a fetch after load, so the measurement above can land
1643                    // before there is anything to measure. Cheap, bounded, and
1644                    // stops as soon as the height settles.
1645                    var polls = 0, lastH = -1;
1646                    (function settle() {
1647                        if (polls++ > 12) return;                    // ~3s
1648                        fitFrameToContent();
1649                        var h = frame ? parseInt(frame.style.height, 10) : 0;
1650                        if (h === lastH && polls > 3) return;        // stable
1651                        lastH = h;
1652                        setTimeout(settle, 250);
1653                    })();
1654                }
1655
1656                if (frame) frame.addEventListener('load', watchFrameHeight);
1657
1658                // Back to the placeholder height before a new document loads, so
1659                // a tall previous render doesn't leave a gap under a short one.
1660                function resetFrameHeight() {
1661                    if (frameRO) { frameRO.disconnect(); frameRO = null; }
1662                    if (frame) frame.style.height = IFRAME_BASE_H + 'px';
1663                }
1664
1665                function ensureIconFont() {
1666                    if (document.getElementById('pbs-iconfont')) return;
1667                    if (document.querySelector('link[href*="font-awesome"],link[href*="fontawesome"]')) return;
1668                    var l = document.createElement('link');
1669                    l.id = 'pbs-iconfont';
1670                    l.rel = 'stylesheet';
1671                    l.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css';
1672                    document.head.appendChild(l);
1673                }
1674
1675                function openModalUI() {
1676                    // Fetched early so the stylesheet has arrived by the time a
1677                    // booking completes and the confirmation renders.
1678                    ensureIconFont();
1679                    clearTimeout(closeT);
1680                    modal.style.display = 'flex';
1681                    document.body.style.overflow = 'hidden';
1682                    void modal.offsetWidth;
1683                    modal.classList.add('is-open');
1684                }
1685
1686                function closeModalUI() {
1687                    modal.classList.remove('is-open');
1688                    // Keep it displayed until the slide-out finishes. The timeout is a
1689                    // fallback rather than the mechanism: transitionend does not fire
1690                    // if the property never animates (reduced-motion, or a browser
1691                    // that ignores the transition), which would leave the modal
1692                    // permanently covering the page.
1693                    clearTimeout(closeT);
1694                    closeT = setTimeout(function () {
1695                        modal.style.display = 'none';
1696                        document.body.style.overflow = '';
1697                        if (panel) panel.style.transform = '';   // clear any drag offset
1698                    }, 300);
1699                }
1700
1701                function isOpen() { return modal.classList.contains('is-open'); }
1702
1703                var closeBtn = document.getElementById('pb-scheduler-close');
1704                if (closeBtn) closeBtn.addEventListener('click', closeScheduler);
1705                modal.addEventListener('click', function (e) {
1706                    if (e.target === modal) closeScheduler();
1707                });
1708                // Keyed off the class, not style.display: display is now 'flex' for
1709                // the whole slide-out too, so the old test would have fired a second
1710                // close mid-animation.
1711                document.addEventListener('keydown', function (e) {
1712                    if (e.key === 'Escape' && isOpen()) closeScheduler();
1713                });
1714                // After a successful booking the embedded confirmation posts
1715                // 'closeModal' — mark it booked and close the flow cleanly.
1716                //
1717                // It also posts a structured 'cadient:booked' immediately, carrying
1718                // the slot. We act on that ONLY when the caller asked for a
1719                // thank-you redirect: reacting to it otherwise would close the
1720                // modal at once and cut short the in-frame confirmation that every
1721                // other entry point currently ends on.
1722                window.addEventListener('message', function (e) {
1723                    if (e.data === 'closeModal') {
1724                        // Once our own confirmation is up, this 3s-delayed message
1725                        // from the iframe must not tear the modal down underneath
1726                        // it — the visitor is reading the thank-you.
1727                        if (thanksShown) return;
1728                        booked = true; closeScheduler(); return;
1729                    }
1730
1731                    if (!e.data || e.origin !== window.location.origin) return;
1732
1733                    // Scheduler-first: the iframe hands the picked slot back
1734                    // instead of booking it, because there's no lead yet. The
1735                    // caller holds it and commits the booking with its form.
1736                    if (e.data.type === 'cadient:slot') {
1737                        handoff(e.data.slot, e.data.timeZone);
1738                        return;
1739                    }
1740
1741                    // Booked: stamp the prompt now rather than waiting for the
1742                    // close, so a tab crash mid-confirmation can't re-greet
1743                    // someone who has a meeting on the calendar.
1744                    if (e.data.type === 'cadient:booked') fireDismiss();
1745
1746                    // Structured payload. Same-origin only — the bare string above
1747                    // stays permissive for the cross-origin WordPress embed, but
1748                    // this one drives a fetch and a DOM swap.
1749                    if (e.data.type !== 'cadient:booked') return;
1750                    if (!thankYouUrl || thanksShown) return;
1751
1752                    booked = true;
1753                    showThanks(e.data.slot, e.data.timeZone);
1754                });
1755
1756                // ── Post-booking confirmation, in place of the scheduler ──
1757                // Fetched rather than pre-rendered: the fragment depends on the
1758                // booked slot, which only exists once the booking is made, and on
1759                // the article's two featured products, which are resolved
1760                // server-side. One template serves this and the standalone page.
1761                function showThanks(slot, tz) {
1762                    var url = thankYouUrl;
1763                    if (slot) url += (url.indexOf('?') === -1 ? '?' : '&') +
1764                        'slot=' + encodeURIComponent(slot);
1765                    if (tz) url += '&tz=' + encodeURIComponent(tz);
1766
1767                    fetch(url, { credentials: 'same-origin' })
1768                        .then(function (r) { if (!r.ok) throw new Error(r.status); return r.text(); })
1769                        .then(function (html) {
1770                            if (!thanksEl) throw new Error('no host');
1771                            thanksEl.innerHTML = html;
1772                            thanksShown = true;
1773                            // style.display, NOT the hidden attribute: the iframe
1774                            // carries Tailwind's `block`, and .block{display:block}
1775                            // outranks the UA's [hidden]{display:none}. Setting
1776                            // hidden left it laid out as a 560px blank gap above
1777                            // the confirmation.
1778                            if (frame) frame.style.display = 'none';
1779                            // The modal's own heading duplicates the
1780                            // confirmation's, so it goes — but the bar holding the
1781                            // close button stays.
1782                            if (headIcon) headIcon.style.display = 'none';
1783                            if (headText) headText.style.display = 'none';
1784                            if (headBlock) {
1785                                headBlock.style.borderBottomColor = 'transparent';
1786                                headBlock.style.paddingBottom = '0';
1787                            }
1788                            // The skip footer offered "Maybe later" on a booking
1789                            // that had just succeeded — it read as though the
1790                            // booking had not taken, and clicking it only closed
1791                            // the modal. There is nothing left to skip here.
1792                            if (skipWrap) skipWrap.style.display = 'none';
1793                            // A CTA pointing at the page we are already on should
1794                            // close the modal, not reload it. Bound here because
1795                            // innerHTML never runs 
1795<script> in the fragment.
1796                            var tyCta = thanksEl.querySelector('[data-role="ty-cta"]');
1797                            if (tyCta) {
1798                                tyCta.addEventListener('click', function (ev) {
1799                                    var href = tyCta.getAttribute('href') || '';
1800                                    if (href && href.split('?')[0] === window.location.pathname) {
1801                                        ev.preventDefault();
1802                                        closeScheduler();
1803                                    }
1804                                });
1805                            }
1806                            thanksEl.style.display = '';
1807                            var body = modal.querySelector('[data-role="sched-body"]');
1808                            if (body) body.scrollTop = 0;
1809                        })
1810                        .catch(function () {
1811                            // Fetch failed and the meeting is already booked, so
1812                            // fall back to the iframe's own confirmation rather than
1813                            // leaving a blank panel. It closes itself after 3s.
1814                            thanksShown = false;
1815                        });
1816                }
1817
1818                // Put the modal back the way it was, so the next open shows the
1819                // scheduler and not a stale confirmation.
1820                
1821                window.pbSchedulerShowFragment = function (html) {
1822                    if (!thanksEl) return false;
1823                    // Re-opening a finished flow: the panel is closed, so open it
1824                    // here rather than letting the caller fall back to its own
1825                    // (much smaller) card — that size flip is exactly what the
1826                    // single-panel work was for.
1827                    if (!isOpen()) {
1828                        if (frame) { resetFrameHeight(); frame.src = 'about:blank'; }
1829                        handoffDone = true;
1830                        slotHandler = null;
1831                        if (skipWrap) skipWrap.style.display = 'none';
1832                        openModalUI();
1833                    }
1834                    if (formWrap) formWrap.style.display = 'none';
1835                    if (frame) frame.style.display = 'none';
1836                    if (headIcon) headIcon.style.display = 'none';
1837                    if (headText) headText.style.display = 'none';
1838                    if (headBlock) {
1839                        headBlock.style.borderBottomColor = 'transparent';
1840                        headBlock.style.paddingBottom = '0';
1841                    }
1842                    thanksEl.innerHTML = html;
1843                    thanksEl.style.display = '';
1844                    thanksShown = true;
1845                    var body = modal.querySelector('[data-role="sched-body"]');
1846                    if (body) body.scrollTop = 0;
1847                    return true;
1848                };
1849
1850                // Whether the panel is currently hosting a caller's form.
1851                window.pbSchedulerHasFormPane = function () { return !!formPane; };
1852
1853                function resetThanks() {
1854                    thanksShown = false;
1855                    if (thanksEl) { thanksEl.style.display = 'none'; thanksEl.innerHTML = ''; }
1856                    if (formWrap) formWrap.style.display = 'none';
1857                    if (frame) frame.style.display = '';
1858                    if (headIcon) headIcon.style.display = '';
1859                    if (headText) headText.style.display = '';
1860                    if (headBlock) {
1861                        headBlock.style.borderBottomColor = '';
1862                        headBlock.style.paddingBottom = '';
1863                    }
1864                }
1865
1866                // ── Drag to dismiss ───────────────────────────────────────
1867                // Handle only, and that is a constraint rather than a choice: the
1868                // panel body is a same-origin iframe, and pointer events inside a
1869                // nested document never reach this one, so a drag begun over the
1870                // scheduler cannot be tracked. Hence the visible grip.
1871                //
1872                // Pointer Events, not touchstart/touchmove: one code path covers
1873                // touch, pen and mouse, and setPointerCapture keeps delivering moves
1874                // if the finger slides outside the handle — which it will, because
1875                // the gesture is downward and the handle is 14px tall.
1876                var grip = modal.querySelector('[data-role="sched-grip"]');
1877                if (grip && panel && window.PointerEvent) {
1878                    var startY = 0, dy = 0, dragging = false;
1879                    var DISMISS_PX = 90;     // past this, let go and it closes
1880
1881                    grip.addEventListener('pointerdown', function (e) {
1882                        dragging = true; startY = e.clientY; dy = 0;
1883                        panel.classList.add('is-dragging');
1884                        grip.setPointerCapture(e.pointerId);
1885                    });
1886
1887                    grip.addEventListener('pointermove', function (e) {
1888                        if (!dragging) return;
1889                        dy = e.clientY - startY;
1890                        // Downward only. Allowing an upward drag would lift the sheet
1891                        // off the bottom edge and expose the backdrop beneath it.
1892                        if (dy < 0) dy = 0;
1893                        panel.style.transform = 'translateY(' + dy + 'px)';
1894                    });
1895
1896                    function endDrag() {
1897                        if (!dragging) return;
1898                        dragging = false;
1899                        panel.classList.remove('is-dragging');
1900                        if (dy > DISMISS_PX) {
1901                            // Hand back to the normal close path so the end-state
1902                            // callbacks still run — a dismissed sheet has to behave
1903                            // exactly like pressing the × or Escape.
1904                            panel.style.transform = '';
1905                            closeScheduler();
1906                        } else {
1907                            panel.style.transform = '';   // springs back via the transition
1908                        }
1909                        dy = 0;
1910                    }
1911                    grip.addEventListener('pointerup', endDrag);
1912                    grip.addEventListener('pointercancel', endDrag);
1913                }
1914            }) ();
1915    </script>
1915
1916
1917<script>
1918    const menuBtn = document.getElementById('mobile-menu-btn');
1919    const mobileMenu = document.getElementById('mobile-menu');
1920    if (menuBtn && mobileMenu) {
1921        menuBtn.addEventListener('click', () => mobileMenu.classList.toggle('hidden'));
1922    }
1923</script>
1923
1924
1925<main id="main-content">
1926
1927    <!-- ══════════ HERO ══════════ -->
1928    <section class="px-6 py-16 md:py-20">
1929        <div class="max-w-4xl mx-auto text-center">
1930            <p class="text-sm font-semibold uppercase tracking-widest ss-orange mb-5">Case Study</p>
1931            <h1 class="text-4xl md:text-5xl lg:text-6xl font-extrabold leading-[1.1] tracking-tight">
1932                How Cadient’s SmartTexting™<br>helped a large regional grocery store reduce hiring time by 41%
1933            </h1>
1934        </div>
1935    </section>
1936
1937    
1938    <!-- ══════════ STATS ══════════ -->
1939    <section class="ss-cream px-6 py-12">
1940        <div class="max-w-5xl mx-auto grid sm:grid-cols-3 gap-8 text-center">
1941            
1942            <div>
1943                
1944                <div class="text-4xl md:text-5xl font-extrabold ss-orange">55%</div>
1945                
1946                <p class="text-slate-600 font-medium mt-2">Increase in Opt-in Automation Efficiency</p>
1947            </div>
1948            
1949            <div>
1950                
1951                <div class="text-4xl md:text-5xl font-extrabold ss-orange">64%</div>
1952                
1953                <p class="text-slate-600 font-medium mt-2">Increase in response rates</p>
1954            </div>
1955            
1956            <div>
1957                
1958                <div class="text-4xl md:text-5xl font-extrabold ss-orange">41%</div>
1959                
1960                <p class="text-slate-600 font-medium mt-2">Faster Hiring Cycle</p>
1961            </div>
1962            
1963        </div>
1964    </section>
1965    
1966
1967    
1968    <!-- ══════════ ABOUT THE CLIENT ══════════ -->
1969    <section class="px-6 py-16 md:py-20">
1970        <div class="max-w-3xl mx-auto">
1971            <h2 class="text-2xl md:text-3xl font-extrabold tracking-tight mb-6">About the Client</h2>
1972            
1973            <p class="text-lg text-slate-600 leading-relaxed mb-4">For the longest time, a leading regional grocery store struggled with keeping its applicant pool engaged throughout the hiring journey.</p>
1974            
1975            <p class="text-lg text-slate-600 leading-relaxed mb-4">This became a major barrier to hiring quality candidates, which negatively impacted overall business growth.</p>
1976            
1977            <p class="text-lg text-slate-600 leading-relaxed mb-4">When Cadient stepped in, its texting solutions transformed the client’s applicant pool, delivering multi-pronged benefits through improved communication and engagement.</p>
1978            
1979            
1980        </div>
1981    </section>
1982    
1983
1984    
1985    <!-- ══════════ CHALLENGES ══════════ -->
1986    <section class="ss-cream px-6 py-16 md:py-20">
1987        <div class="max-w-3xl mx-auto">
1988            <h2 class="text-2xl md:text-3xl font-extrabold tracking-tight mb-4">Challenges</h2>
1989            <p class="text-lg text-slate-600 leading-relaxed mb-6">Before using Cadient’s texting solutions, the client faced several HR obstacles:</p>
1990            <ul class="space-y-3">
1991                
1992                <li class="flex items-start gap-3">
1993                    <i class="fa-solid fa-circle-xmark text-slate-400 mt-1 flex-shrink-0"></i>
1994                    <span class="text-slate-700">Difficulty reaching applicants and getting timely responses</span>
1995                </li>
1996                
1997                <li class="flex items-start gap-3">
1998                    <i class="fa-solid fa-circle-xmark text-slate-400 mt-1 flex-shrink-0"></i>
1999                    <span class="text-slate-700">Trouble scheduling interviews</span>
2000                </li>
2001                
2002                <li class="flex items-start gap-3">
2003                    <i class="fa-solid fa-circle-xmark text-slate-400 mt-1 flex-shrink-0"></i>
2004                    <span class="text-slate-700">Low attendance at job fairs and events</span>
2005                </li>
2006                
2007                <li class="flex items-start gap-3">
2008                    <i class="fa-solid fa-circle-xmark text-slate-400 mt-1 flex-shrink-0"></i>
2009                    <span class="text-slate-700">6% average email response rate—too low to sustain interest</span>
2010                </li>
2011                
2012                <li class="flex items-start gap-3">
2013                    <i class="fa-solid fa-circle-xmark text-slate-400 mt-1 flex-shrink-0"></i>
2014                    <span class="text-slate-700">Candidates primarily applied via mobile, but traditional methods lacked mobile responsiveness</span>
2015                </li>
2016                
2017                <li class="flex items-start gap-3">
2018                    <i class="fa-solid fa-circle-xmark text-slate-400 mt-1 flex-shrink-0"></i>
2019                    <span class="text-slate-700">Even small negative experiences often deterred top candidates</span>
2020                </li>
2021                
2022            </ul>
2023            <p class="text-lg text-slate-600 leading-relaxed mt-6">The client was operating in a high-volume hourly hiring market, where fast, effective communication was critical.</p>
2024        </div>
2025    </section>
2026    
2027
2028    
2029    <!-- ══════════ SOLUTION ══════════ -->
2030    <section class="px-6 py-16 md:py-20">
2031        <div class="max-w-3xl mx-auto">
2032            <h2 class="text-2xl md:text-3xl font-extrabold tracking-tight mb-6">The Solution</h2>
2033            <p class="text-lg text-slate-600 leading-relaxed mb-6">The client upgraded its hiring process by integrating Cadient’s texting tools into its Applicant Tracking System (ATS). Compared to legacy methods, this approach delivered:</p>
2034            
2035            <h3 class="font-bold text-lg mb-3">Key Solutions Implemented:</h3>
2036            <ul class="space-y-3 mb-6">
2037                
2038                <li class="flex items-start gap-3">
2039                    <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2040                    <span class="text-slate-700">Cadient Texting: Real-time, one-on-one communication</span>
2041                </li>
2042                
2043                <li class="flex items-start gap-3">
2044                    <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2045                    <span class="text-slate-700">Text Apply: Easy application via text, building a candidate pipeline</span>
2046                </li>
2047                
2048                <li class="flex items-start gap-3">
2049                    <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2050                    <span class="text-slate-700">Text Notify: Automated messaging for status updates and scheduling</span>
2051                </li>
2052                
2053            </ul>
2054            
2055            
2056            
2057            <blockquote class="border-l-4 ss-border-orange border-orange-500 pl-6 my-8 text-xl italic text-slate-700 leading-relaxed">
2058                “We were impressed by Cadient’s focus on our unique data and their ability to reduce turnover through intelligent automation.”
2059            </blockquote>
2060            
2061            
2062            <div class="ss-box rounded-2xl p-6 md:p-8 mt-8">
2063                <h3 class="font-bold text-lg mb-3">Automation &amp; Intelligence:</h3>
2064                <ul class="space-y-2">
2065                    
2066                    <li class="flex items-start gap-3">
2067                        <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2068                        <span class="text-slate-700">Cadient’s SmartTenure, powered by Decision Point, uses machine learning to identify high-tenure candidates</span>
2069                    </li>
2070                    
2071                    <li class="flex items-start gap-3">
2072                        <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2073                        <span class="text-slate-700">Text Notify automates messages to candidates’ preferred devices</span>
2074                    </li>
2075                    
2076                    <li class="flex items-start gap-3">
2077                        <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2078                        <span class="text-slate-700">
2078Cadient’s end-to-end Talent Acquisition system includes texting, selection tools, and real-time analytics</span>
2079                    </li>
2080                    
2081                </ul>
2082                
2083            </div>
2084            
2085        </div>
2086    </section>
2087    
2088
2089    
2090    <!-- ══════════ OUTCOME ══════════ -->
2091    <section class="ss-cream px-6 py-16 md:py-20">
2092        <div class="max-w-3xl mx-auto">
2093            <h2 class="text-2xl md:text-3xl font-extrabold tracking-tight mb-4">The Outcome</h2>
2094            <p class="text-lg text-slate-600 leading-relaxed mb-6">Cadient’s texting solutions brought about a transformative shift in the client’s recruitment process. By replacing outdated, low-response methods with real-time, text-based interactions, the company unlocked immediate improvements in speed, communication, and applicant satisfaction.</p>
2095            
2096            <div class="mb-6">
2097                <h3 class="font-bold text-lg mb-3">Key Outcomes Achieved:</h3>
2098                <ul class="space-y-2">
2099                    
2100                    <li class="flex items-start gap-3">
2101                        <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2102                        <span class="text-slate-700">41% faster hiring cycle</span>
2103                    </li>
2104                    
2105                    <li class="flex items-start gap-3">
2106                        <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2107                        <span class="text-slate-700">64% average text response rate – most within 90 seconds</span>
2108                    </li>
2109                    
2110                    <li class="flex items-start gap-3">
2111                        <i class="fa-solid fa-circle-check ss-orange mt-1 flex-shrink-0"></i>
2112                        <span class="text-slate-700">55% increase in opt-in automation efficiency</span>
2113                    </li>
2114                    
2115                </ul>
2116            </div>
2117            
2118            <p class="text-lg text-slate-600 leading-relaxed">Overall, the company now operates with a more agile, data-driven hiring process, ensuring that top candidates remain engaged and are brought onboard faster—ultimately supporting better customer service and business performance.</p>
2119        </div>
2120    </section>
2121    
2122
2123    <!-- ══════════ CLOSING CTA ══════════ -->
2124    <section class="px-6 py-16 md:py-20">
2125        <div class="max-w-5xl mx-auto">
2126            <div class="ss-box rounded-[40px] p-8 md:p-12 text-center">
2127                <h2 class="text-3xl md:text-4xl font-extrabold tracking-tight mb-8 max-w-2xl mx-auto">See what Cadient can do for your hiring team.</h2>
2128                <a href="#" class="trigger-contact-sales inline-flex items-center gap-3 ss-dark text-white font-semibold px-8 py-4 rounded-xl transition-colors">
2129                    Talk to an Expert <i class="fa-solid fa-arrow-up-right ss-orange text-sm"></i>
2130                </a>
2131            </div>
2132        </div>
2133    </section>
2134
2135</main>
2136
2137<!-- FOOTER -->
2138<style>.text-cadient-blue{color:#0077b6}.hover\:text-cadient-blue:hover{color:#0077b6}footer summary::-webkit-details-marker{display:none}footer summary{list-style:none}.ftr-more-chev{transition:transform .2s ease}footer details[open] .ftr-more-chev{transform:rotate(90deg)}</style>
2139<footer class="bg-slate-900 text-slate-300 py-16">
2140    <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
2141        <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-y-12 gap-x-16">
2142
2143            <!-- Brand — page-specific when a product context is set, generic Cadient brand otherwise -->
2144            <div class="space-y-4">
2145                
2146                <a href="/"><img src="/static/img/logo.webp" width="300" height="93"
2147                        class="w-32 brightness-0 invert opacity-90 hover:opacity-100 transition-opacity"
2148                        alt="Cadient"></a>
2149                <p class="text-sm text-slate-400 leading-relaxed">Transforming the way businesses hire with precision,
2150                    speed, and intelligence.</p>
2151                
2152                <div class="flex space-x-4 pt-2">
2153                    <a href="https://www.linkedin.com/company/cadient-talent/" aria-label="Cadient on LinkedIn"
2154                        class="text-slate-400 hover:text-white transition-colors"><svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M20.45 20.45h-3.56v-5.57c0-1.33-.02-3.04-1.85-3.04-1.85 0-2.14 1.45-2.14 2.94v5.67H9.34V9h3.42v1.56h.05c.48-.9 1.64-1.85 3.37-1.85 3.6 0 4.27 2.37 4.27 5.45v6.29zM5.34 7.43a2.07 2.07 0 1 1 0-4.14 2.07 2.07 0 0 1 0 4.14zM7.12 20.45H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.73v20.54C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.73V1.73C24 .77 23.2 0 22.22 0z"/></svg></a>
2155                    <a href="https://x.com/CadientTalent" aria-label="Cadient on X (Twitter)" class="text-slate-400 hover:text-white transition-colors"><svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg></a>
2156                    <a href="https://www.facebook.com/cadienttalent/" aria-label="Cadient on Facebook"
2157                        class="text-slate-400 hover:text-white transition-colors"><svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path d="M24 12.07C24 5.4 18.63 0 12 0S0 5.4 0 12.07c0 6.02 4.39 11.01 10.13 11.93v-8.44H7.08v-3.49h3.05V9.41c0-3.02 1.79-4.69 4.53-4.69 1.31 0 2.68.24 2.68.24v2.97h-1.51c-1.49 0-1.96.93-1.96 1.89v2.25h3.33l-.53 3.49h-2.8v8.44C19.61 23.08 24 18.09 24 12.07z"/></svg></a>
2158                </div>
2159            </div>
2160
2161            <!-- Solutions — mirrors the header mega-menu (Recruit / Hire / Retain) -->
2162            <div class="lg:col-span-3">
2163                <h2 class="text-white font-semibold mb-4 text-sm uppercase tracking-wider">Solutions</h2>
2164                <div class="grid grid-cols-2 sm:grid-cols-3 gap-x-8 gap-y-8">
2165                    <!-- Recruit -->
2166                    <div>
2167                        <p class="text-[11px] font-semibold uppercase tracking-widest text-slate-500 mb-3">Recruit</p>
2168                        <ul class="space-y-2.5 text-[13px] text-slate-400">
2169                            <li><a href="/smart-automation" class="hover:text-cadient-blue transition-colors">SmartAutomation</a></li>
2170                            <li><a href="/smart-refer/" class="hover:text-cadient-blue transition-colors">SmartRefer</a></li>
2171                            <li><a href="/smart-source" class="hover:text-cadient-blue transition-colors">SmartSource</a></li>
2172                            <li><a href="/smart-registry" class="hover:text-cadient-blue transition-colors">SmartRegistry</a></li>
2173                            <li><a href="/smartsuite/smartmatch/" class="hover:text-cadient-blue transition-colors">SmartMatch</a></li>
2174                            <li><a href="/smartsuite/smartscreen/" class="hover:text-cadient-blue transition-colors">SmartScreen</a></li>
2175                            <li><a href="/smartsuite/smarttexting/" class="hover:text-cadient-blue transition-colors">SmartTexting</a></li>
2176                        </ul>
2177                    </div>
2178                    <!-- Hire -->
2179                    <div>
2180                        <p class="text-[11px] font-semibold uppercase tracking-widest text-slate-500 mb-3">Hire</p>
2181                        <ul class="space-y-2.5 text-[13px] text-slate-400">
2182                            <li><a href="/smart-shield" class="hover:text-cadient-blue transition-colors">SmartShield</a></li>
2183                            <li><a href="/smartsuite/smarthire/" class="hover:text-cadient-blue transition-colors">SmartHire</a></li>
2184                            <li><a href="/smartsuite/smartinterview/" class="hover:text-cadient-blue transition-colors">SmartInterview</a></li>
2185                            <li><a href="/smartsuite/smarttexting/" class="hover:text-cadient-blue transition-colors">SmartTexting</a></li>
2186                            <li><a href="/smartsuite/smartoffer/" class="hover:text-cadient-blue transition-colors">SmartOffer</a></li>
2187                        </ul>
2188                    </div>
2189                    <!-- Retain -->
2190                    <div>
2191                        <p class="text-[11px] font-semibold uppercase tracking-widest text-slate-500 mb-3">Retain</p>
2192                        <ul class="space-y-2.5 text-[13px] text-slate-400">
2193                            <li><a href="/smart-communicate/" class="hover:text-cadient-blue transition-colors">SmartCommunicate</a></li>
2194                            <li><a href="/smartsuite/smarttenure/" class="hover:text-cadient-blue transition-colors">SmartTenure</a></li>
2195                            <li><a href="/smartsuite/smartfeedback/" class="hover:text-cadient-blue transition-colors">SmartFeedback</a></li>
2196                            <li><a href="/smartsuite/smarttexting/" class="hover:text-cadient-blue transition-colors">SmartTexting</a></li>
2197                        </ul>
2198                    </div>
2199                </div>
2200            </div>
2201
2202            <!-- Company -->
2203            <div>
2204                <h2 class="text-white font-semibold mb-4 text-sm uppercase tracking-wider">Company</h2>
2205                <ul class="space-y-3 text-sm">
2206                    <li><a href="/about"
2207                            class="hover:text-cadient-blue transition-colors">About Us</a></li>
2208                    <li><a href="/lp/schedule" rel="nofollow"
2209                            class="trigger-contact-sales hover:text-cadient-blue transition-colors">Contact</a></li>
2210                    <li><a href="/article" class="hover:text-cadient-blue transition-colors">Blog</a></li>
2211                </ul>
2212            </div>
2213
2214        </div>
2215
2216        <div
2217            class="border-t border-slate-800 mt-12 pt-8 flex flex-col md:flex-row justify-between items-center text-sm text-slate-500">
2218            <p>&copy; 2026 Cadient Talent. All rights reserved.</p>
2219            <a href="/privacy-policy" class="hover:text-white mt-4 md:mt-0">Privacy Policy</a>
2220        </div>
2221    </div>
2222</footer>
2223
2224<!-- Contact Sales Modal Partial -->
2225<style>
2226    /* Explicit sizing so the modal renders correctly regardless of whether the
2227       host page ships full Tailwind (smart-refer/CDN) or purged Tailwind (blog).
2228       Without this, the card has no width below the sm: breakpoint and collapses
2229       to the iframe's 300px intrinsic width, cutting off the scheduler. */
2230    #contactSalesModal .csm-card {
2231        width: 100%;
2232        max-width: 42rem;
2233    }
2234
2235    #contactSalesModal .csm-frame {
2236        display: block;
2237        width: 100%;
2238        height: 650px;
2239    }
2240
2241    @media (max-width: 640px) {
2242        #contactSalesModal .csm-wrap {
2243            padding: 0.5rem;
2244        }
2245
2246        /* Fill the phone viewport instead of shrinking to the iframe default. */
2247        #contactSalesModal .csm-card {
2248            max-width: 100%;
2249        }
2250    }
2251</style>
2252<div id="contactSalesModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
2253    <!-- Backdrop -->
2254    <div class="fixed inset-0 bg-slate-900/50 backdrop-blur-sm transition-opacity" id="modalBackdrop"></div>
2255
2256    <!-- Modal Content -->
2257    <div class="csm-wrap flex min-h-full items-center justify-center p-4 text-center sm:p-0">
2258        <div
2259            class="csm-card relative transform overflow-hidden rounded-2xl bg-white text-left shadow-2xl transition-all sm:my-8">
2260            <div class="bg-white">
2261                <div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
2262                    <h3 class="text-xl font-bold text-slate-900">Schedule a Demo</h3>
2263                    <button type="button" class="text-slate-400 hover:text-slate-600 transition-colors"
2264                        id="closeModalBtn" aria-label="Close">
2265                        <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
2266                    </button>
2267                </div>
2268
2269                <div class="p-0">
2270                    <iframe id="schedulerIframe" src="" title="Schedule a demo with Cadient" class="csm-frame border-none"></iframe>
2271                </div>
2272            </div>
2273        </div>
2274    </div>
2275</div>
2276
2277<script>
2278    (function () {
2279        const modal = document.getElementById('contactSalesModal');
2280        const closeBtn = document.getElementById('closeModalBtn');
2281        const backdrop = document.getElementById('modalBackdrop');
2282        const iframe = document.getElementById('schedulerIframe');
2283
2284        function openContactSalesModal() {
2285            modal.classList.remove('hidden');
2286            document.body.style.overflow = 'hidden';
2287            iframe.src = '/lp/schedule';
2288        }
2289
2290        function closeContactSalesModal() {
2291            modal.classList.add('hidden');
2292            document.body.style.overflow = '';
2293            iframe.src = '';
2294        }
2295
2296        // Event listeners
2297        if (closeBtn) {
2298            closeBtn.addEventListener('click', closeContactSalesModal);
2299        }
2300
2301        if (backdrop) {
2302            backdrop.addEventListener('click', closeContactSalesModal);
2303        }
2304
2305        // Expose globally for other scripts to call
2306        window.openContactSalesModal = openContactSalesModal;
2307        window.closeContactSalesModal = closeContactSalesModal;
2308
2309        // Support for elements with trigger class
2310        document.addEventListener('click', function (e) {
2311            if (e.target.matches('.trigger-contact-sales') || e.target.closest('.trigger-contact-sales')) {
2312                e.preventDefault();
2313                openContactSalesModal();
2314            }
2315        });
2316    })();
2317</script>
2317
2318
2319</body>
2320
2321</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.