PageSourceSearch

https://cybercompany.ai/cybercompany-pages.js

js cybercompany.ai collected 2026-10-05 12:11:25 UTC 43,097 bytes, 788 lines download raw bytes

1(() => {
2  const root = document.documentElement;
3  const body = document.body;
4  const sharedHeader = document.querySelector('[data-header]');
5  if (sharedHeader) {
6    sharedHeader.querySelector('.brand img')?.classList.add('brand-logo');
7    const solutionsItem = sharedHeader.querySelector('.desktop-nav > .nav-item:first-child');
8    const solutionsTrigger = solutionsItem?.querySelector('button');
9    solutionsItem?.classList.add('nav-item-mega');
10    solutionsTrigger?.classList.add('nav-link-button');
11    const oldSolutionChevron = solutionsTrigger?.querySelector(':scope > span[aria-hidden="true"]');
12    if (oldSolutionChevron) {
13      oldSolutionChevron.outerHTML = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="m4 6 4 4 4-4"/></svg>';
14    }
15    sharedHeader.querySelectorAll('.mega-grid').forEach(grid => grid.classList.add('mega-menu-grid'));
16    sharedHeader.querySelectorAll('.mega-menu-glow').forEach(glow => glow.remove());
17    const aboutTrigger = sharedHeader.querySelector('.nav-item-about > button');
18    aboutTrigger?.classList.add('nav-link-button');
19    const oldAboutChevron = aboutTrigger?.querySelector(':scope > span[aria-hidden="true"]');
20    if (oldAboutChevron) {
21      oldAboutChevron.outerHTML = '<svg viewBox="0 0 16 16" aria-hidden="true"><path d="m4 6 4 4 4-4"/></svg>';
22    }
23    const headerRobot = sharedHeader.querySelector('.mega-feature-robot');
24    const headerRobotIcon = sharedHeader.querySelector('.mega-robot-icon img');
25    [headerRobot, headerRobotIcon].forEach(image => {
26      if (image instanceof HTMLImageElement) {
27        image.src = '/robots/robot-general-brain-clean.png';
28        image.width = 900;
29        image.height = 720;
30      }
31    });
32    const themeButton = sharedHeader.querySelector('[data-theme-toggle]');
33    if (themeButton && !themeButton.querySelector('svg')) {
34      themeButton.innerHTML = '<svg class="icon-sun" viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.42 1.42M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.42-1.42M17.66 6.34l1.41-1.41"/></svg><svg class="icon-moon" viewBox="0 0 24 24" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79Z"/></svg>';
35    }
36  }
37  const header = document.querySelector('[data-header]');
38  const menuToggle = document.querySelector('[data-menu-toggle]');
39  const mobileMenu = document.querySelector('[data-mobile-menu]');
40  const themeToggle = document.querySelector('[data-theme-toggle]');
41  const navItem = document.querySelector('.nav-item');
42  const navTrigger = document.querySelector('.nav-menu-trigger');
43  const mobileSticky = document.querySelector('[data-mobile-sticky]');
44  const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');
45  const mobileViewport = window.matchMedia('(max-width: 760px)');
46  const systemTheme = window.matchMedia('(prefers-color-scheme: light)');
47  const english = root.lang === 'en';
48  root.classList.add('has-js');
49  let lastFocused = null;
50  let stickyDismissed = false;
51
52  const setAttribution = scope => {
53    const params = new URLSearchParams(window.location.search);
54    scope.querySelectorAll('input[name="page_url"]').forEach(input => { input.value = window.location.href; });
55    scope.querySelectorAll('input[name="source_campaign"]').forEach(input => { input.value = params.get('utm_campaign') || params.get('source') || ''; });
56  };
57  const emitConversion = (action, extra = {}) => {
58    const detail = { action, path: window.location.pathname, language: root.lang || 'sr', ...extra };
59    window.dispatchEvent(new CustomEvent('cybercompany:conversion', { detail }));
60    if (Array.isArray(window.dataLayer)) window.dataLayer.push({ event: 'cybercompany_conversion', ...detail });
61  };
62  setAttribution(document);
63
64  document.addEventListener('invalid', event => {
65    if (event.target.matches('input, select, textarea')) event.target.setAttribute('aria-invalid', 'true');
66  }, true);
67  document.addEventListener('input', event => {
68    if (event.target.matches('input, select, textarea') && event.target.checkValidity()) event.target.removeAttribute('aria-invalid');
69  });
70  document.addEventListener('change', event => {
71    if (event.target.matches('input, select, textarea') && event.target.checkValidity()) event.target.removeAttribute('aria-invalid');
72  });
73
74  try { stickyDismissed = sessionStorage.getItem('cc-product-sticky-dismissed') === 'true'; } catch {}
75
76  const hasThemeOverride = () => {
77    const queryTheme = new URLSearchParams(window.location.search).get('theme');
78    if (queryTheme === 'light' || queryTheme === 'dark') return true;
79    try {
80      const saved = localStorage.getItem('cc-theme');
81      return saved === 'light' || saved === 'dark';
82    } catch {
83      return false;
84    }
85  };
86  const setTheme = (theme, persist = true) => {
87    root.dataset.theme = theme;
88    if (persist) {
89      try { localStorage.setItem('cc-theme', theme); } catch {}
90    }
91    themeToggle?.setAttribute('aria-label', theme === 'dark'
92      ? (english ? 'Switch to light theme' : 'Prebaci na svetlu temu')
93      : (english ? 'Switch to dark theme' : 'Prebaci na tamnu temu'));
94  };
95  setTheme(root.dataset.theme === 'light' ? 'light' : 'dark', false);
96  themeToggle?.addEventListener('click', () => setTheme(root.dataset.theme === 'dark' ? 'light' : 'dark'));
97  systemTheme.addEventListener?.('change', event => {
98    if (!hasThemeOverride()) setTheme(event.matches ? 'light' : 'dark', false);
99  });
100
101  const updateSticky = () => {
102    const visible = mobileViewport.matches && !stickyDismissed && window.scrollY > Math.min(520, window.innerHeight * .62) && !body.classList.contains('is-locked');
103    mobileSticky?.classList.toggle('is-visible', visible);
104    mobileSticky?.setAttribute('aria-hidden', String(!visible));
105    if (mobileSticky) mobileSticky.inert = !visible;
106    body.classList.toggle('has-mobile-sticky', visible);
107  };
108  const updateChrome = () => {
109    header?.classList.toggle('is-scrolled', window.scrollY > 16);
110    updateSticky();
111  };
112  updateChrome();
113  window.addEventListener('scroll', updateChrome, { passive: true });
114  window.addEventListener('resize', updateSticky, { passive: true });
115
116  document.querySelector('[data-sticky-close]')?.addEventListener('click', () => {
117    stickyDismissed = true;
118    try { sessionStorage.setItem('cc-product-sticky-dismissed', 'true'); } catch {}
119    updateSticky();
120  });
121
122  const closeMenu = () => {
123    menuToggle?.setAttribute('aria-expanded', 'false');
124    mobileMenu?.classList.remove('is-open');
125    mobileMenu?.setAttribute('aria-hidden', 'true');
126    if (mobileMenu) mobileMenu.inert = true;
127    header?.classList.remove('is-menu-open');
128  };
129  menuToggle?.addEventListener('click', () => {
130    const open = menuToggle.getAttribute('aria-expanded') !== 'true';
131    menuToggle.setAttribute('aria-expanded', String(open));
132    mobileMenu?.classList.toggle('is-open', open);
133    mobileMenu?.setAttribute('aria-hidden', String(!open));
134    if (mobileMenu) mobileMenu.inert = !open;
135    header?.classList.toggle('is-menu-open', open);
136  });
137  mobileMenu?.querySelectorAll('a').forEach(link => link.addEventListener('click', closeMenu));
138
139  navTrigger?.addEventListener('click', () => {
140    const open = navTrigger.getAttribute('aria-expanded') !== 'true';
141    navTrigger.setAttribute('aria-expanded', String(open));
142    navItem?.classList.toggle('is-open', open);
143  });
144  document.addEventListener('click', event => {
145    if (navItem && !navItem.contains(event.target)) {
146      navItem.classList.remove('is-open');
147      navTrigger?.setAttribute('aria-expanded', 'false');
148    }
149  });
150
151  const openOverlay = element => {
152    if (!element) return;
153    setAttribution(element);
154    lastFocused = document.activeElement;
155    element.inert = false;
156    element.classList.add('is-open');
157    element.setAttribute('aria-hidden', 'false');
158    body.classList.add('is-locked');
159    updateSticky();
160    window.setTimeout(() => element.querySelector('input,select,textarea,button,[href]')?.focus(), 60);
161  };
162  const resetOverlayForm = overlay => {
163    overlay?.querySelectorAll('[data-overlay-form]').forEach(form => {
164      form.reset();
165      form.hidden = false;
166    });
167    overlay?.querySelectorAll('[data-overlay-success]').forEach(success => { success.hidden = true; });
168  };
169  const closeOverlay = element => {
170    if (!element?.classList.contains('is-open')) return;
171    element.classList.remove('is-open');
172    element.setAttribute('aria-hidden', 'true');
173    element.inert = true;
174    window.setTimeout(() => resetOverlayForm(element), 210);
175    if (!document.querySelector('.modal-backdrop.is-open,.preview-lightbox.is-open')) {
176      body.classList.remove('is-locked');
177      updateSticky();
178      if (lastFocused instanceof HTMLElement) lastFocused.focus();
179    }
180  };
181
182  document.querySelectorAll('[data-modal-open]').forEach(button => button.addEventListener('click', () => {
183    closeMenu();
184    const modalName = button.dataset.modalOpen || 'consultation';
185    const modal = document.querySelector(`[data-modal="${modalName}"]`);
186    openOverlay(modal);
187  }));
188  document.querySelectorAll('[data-modal-close]').forEach(button => button.addEventListener('click', () => closeOverlay(button.closest('[data-modal]'))));
189  document.querySelectorAll('[data-modal]').forEach(modal => modal.addEventListener('click', event => { if (event.target === modal) closeOverlay(modal); }));
190  document.querySelectorAll('[data-overlay-form]').forEach(form => form.addEventListener('submit', event => {
191    event.preventDefault();
192    emitConversion('form-submit', { form: 'consultation' });
193    const success = form.parentElement?.querySelector('[data-overlay-success]');
194    form.hidden = true;
195    if (success) {
196      success.hidden = false;
197      success.focus();
198    }
199  }));
200
201  document.querySelectorAll('[data-main-form]').forEach(form => form.addEventListener('submit', event => {
202    event.preventDefault();
203    emitConversion('form-submit', { form: 'main-contact' });
204    const success = form.querySelector('[data-main-success]');
205    if (success) {
206      success.hidden = false;
207      success.focus();
208    }
209  }));
210
211  const caseDataNode = document.querySelector('#case-study-data');
212  let caseStudies = [];
213  try { caseStudies = JSON.parse(caseDataNode?.textContent || '[]'); } catch {}
214  const caseModal = document.querySelector('[data-case-modal]');
215  const setCaseText = (selector, value) => { const node = caseModal?.querySelector(selector); if (node) node.textContent = value || ''; };
216  const fillCaseList = (selector, items) => {
217    const list = caseModal?.querySelector(selector); if (!list) return;
218    list.replaceChildren(...items.map(item => { const li = document.createElement('li'); li.textContent = item; return li; }));
219  };
220  const openCaseStudy = slug => {
221    const item = caseStudies.find(entry => entry.slug === slug); if (!item || !caseModal) return;
222    caseModal.style.setProperty('--case-accent', item.accent);
223    setCaseText('[data-case-symbol]', item.symbol); setCaseText('[data-case-category]', item.category); setCaseText('[data-case-client]', item.client); setCaseText('[data-case-industry]', item.industry); setCaseText('[data-case-title]', item.title); setCaseText('[data-case-summary]', item.summary);
224    const logo = caseModal.querySelector('[data-case-logo]');
225    if (logo) { logo.src = item.logo || ''; logo.alt = `${item.client} logo`; logo.hidden = !item.logo; }
226    fillCaseList('[data-case-challenge]', item.challenge); fillCaseList('[data-case-approach]', item.approach); fillCaseList('[data-case-outcomes]', item.outcomes);
227    const proof = item.metrics.length ? item.metrics : item.highlights.map((label, index) => ({ value: `0${index + 1}`, label }));
228    const metrics = caseModal.querySelector('[data-case-metrics]');
229    if (metrics) metrics.replaceChildren(...proof.map(metric => { const span = document.createElement('span'); const strong = document.createElement('strong'); const small = document.createElement('small'); strong.textContent = metric.value; small.textContent = metric.label; span.append(strong, small); return span; }));
230    const note = caseModal.querySelector('[data-case-note]'); if (note) { note.textContent = item.note || ''; note.hidden = !item.note; }
231    const link = caseModal.querySelector('[data-case-link]'); if (link) link.href = item.path;
232    openOverlay(caseModal);
233  };
234  document.querySelectorAll('[data-case-open]').forEach(button => button.addEventListener('click', () => openCaseStudy(button.dataset.caseOpen)));
235  document.querySelectorAll('[data-case-filter]').forEach(button => button.addEventListener('click', () => {
236    const category = button.dataset.caseFilter; document.querySelectorAll('[data-case-filter]').forEach(filter => filter.setAttribute('aria-pressed', String(filter === button)));
237    let visible = 0; document.querySelectorAll('[data-case-card]').forEach(card => { const show = category === 'all' || card.dataset.caseCategory === category; card.hidden = !show; if (show) visible += 1; });
238    const empty = document.querySelector('[data-case-empty]'); if (empty) empty.hidden = visible > 0;
239  }));
240  caseModal?.querySelector('[data-case-close]')?.addEventListener('click', () => closeOverlay(caseModal));
241  caseModal?.addEventListener('click', event => { if (event.target === caseModal) closeOverlay(caseModal); });
242  caseModal?.querySelector('[data-modal-open]')?.addEventListener('click', () => closeOverlay(caseModal));
243
244  document.querySelectorAll('[data-carousel]').forEach(carousel => {
245    const slides = [...carousel.querySelectorAll('[data-carousel-slide]')];
246    const dots = [...carousel.querySelectorAll('[data-carousel-dot]')];
247    const pauseButton = carousel.querySelector('[data-carousel-pause]');
248    let index = 0;
249    let paused = reducedMotion.matches;
250    let timer = null;
251
252    const show = next => {
253      index = (next + slides.length) % slides.length;
254      slides.forEach((slide, slideIndex) => {
255        const active = slideIndex === index;
256        slide.classList.toggle('is-active', active);
257        slide.setAttribute('aria-hidden', String(!active));
258      });
259      dots.forEach((dot, dotIndex) => {
260        const active = dotIndex === index;
261        dot.classList.toggle('is-active', active);
262        dot.setAttribute('aria-selected', String(active));
263      });
264    };
265    const schedule = () => {
266      window.clearInterval(timer);
267      if (!paused && slides.length > 1) timer = window.setInterval(() => show(index + 1), 5200);
268    };
269    carousel.querySelector('[data-carousel-prev]')?.addEventListener('click', () => { show(index - 1); schedule(); });
270    carousel.querySelector('[data-carousel-next]')?.addEventListener('click', () => { show(index + 1); schedule(); });
271    dots.forEach((dot, dotIndex) => dot.addEventListener('click', () => { show(dotIndex); schedule(); }));
272    pauseButton?.addEventListener('click', () => {
273      paused = !paused;
274      pauseButton.textContent = paused ? (english ? '▶ RESUME' : '▶ NASTAVI') : (english ? 'Ⅱ PAUSE' : 'Ⅱ PAUZA');
275      pauseButton.setAttribute('aria-label', paused ? (english ? 'Resume automatic rotation' : 'Nastavi automatsku promenu') : (english ? 'Pause automatic rotation' : 'Pauziraj automatsku promenu'));
276      schedule();
277    });
278    carousel.addEventListener('keydown', event => {
279      if (event.key === 'ArrowLeft') { event.preventDefault(); show(index - 1); schedule(); }
280      if (event.key === 'ArrowRight') { event.preventDefault(); show(index + 1); schedule(); }
281      if (event.key === ' ') { event.preventDefault(); pauseButton?.click(); }
282    });
283    show(0);
284    schedule();
285  });
286
287  const lightbox = document.querySelector('[data-lightbox]');
288  const lightboxBody = lightbox?.querySelector('[data-lightbox-body]');
289  const lightboxLabel = lightbox?.querySelector(':scope > span');
290  document.querySelectorAll('[data-preview-open]').forEach(trigger => trigger.addEventListener('click', event => {
291    if (event.target.closest?.('.carousel-controls')) return;
292    const activeSlide = trigger.querySelector('.app-slide.is-active') || trigger.querySelector('.app-slide');
293    if (!activeSlide || !lightboxBody) return;
294    lightboxBody.replaceChildren(activeSlide.cloneNode(true));
295    if (lightboxLabel) lightboxLabel.textContent = activeSlide.classList.contains('app-slide-screenshot')
296      ? (english ? 'Available demo application view' : 'Prikaz dostupne demo aplikacije')
297      : (english ? 'Clearly labelled illustrative view' : 'Jasno označen ilustrativni prikaz');
298    openOverlay(lightbox);
299  }));
300
301  document.querySelectorAll('[data-product-tour]').forEach(tour => {
302    const dataNode = tour.querySelector('[data-product-tour-data]');
303    let items = [];
304    try { items = JSON.parse(dataNode?.textContent || '[]'); } catch {}
305    const tabs = [...tour.querySelectorAll('[data-product-tour-tab]')];
306    const media = tour.querySelector('[data-product-tour-media]');
307    const image = tour.querySelector('[data-product-tour-image]');
308    const kicker = tour.querySelector('[data-product-tour-kicker]');
309    const title = tour.querySelector('[data-product-tour-title]');
310    const description = tour.querySelector('[data-product-tour-description]');
311    const points = tour.querySelector('[data-product-tour-points]');
312
313    const showItem = index => {
314      const item = items[index];
315      if (!item) return;
316      tabs.forEach((tab, tabIndex) => {
317        const active = tabIndex === index;
318        tab.classList.toggle('is-active', active);
319        tab.setAttribute('aria-selected', String(active));
320        tab.tabIndex = active ? 0 : -1;
321      });
322      if (media) {
323        media.dataset.productTourZoom = item.src;
324        media.setAttribute('aria-label', `${english ? 'Enlarge' : 'Uvećaj'} ${item.label}`);
325      }
326      if (image) {
327        image.src = item.src;
328        image.alt = item.alt;
329      }
330      if (kicker) kicker.textContent = item.kicker;
331      if (title) title.textContent = item.title;
332      if (description) description.textContent = item.description;
333      if (points) points.replaceChildren(...item.points.map(point => {
334        const listItem = document.createElement('li');
335        listItem.textContent = point;
336        return listItem;
337      }));
338    };
339
340    tabs.forEach((tab, index) => {
341      tab.addEventListener('click', () => showItem(index));
342      tab.addEventListener('keydown', event => {
343        if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
344        event.preventDefault();
345        const direction = event.key === 'ArrowRight' ? 1 : -1;
346        const nextIndex = (index + direction + tabs.length) % tabs.length;
347        showItem(nextIndex);
348        tabs[nextIndex]?.focus();
349      });
350    });
351    showItem(0);
352  });
353
354  document.querySelectorAll('[data-illustrative-tour]').forEach(tour => {
355    const tabs = [...tour.querySelectorAll('[data-illustrative-tour-tab]')];
356    const panels = [...tour.querySelectorAll('[data-illustrative-tour-panel]')];
357    const showPanel = index => {
358      tabs.forEach((tab, tabIndex) => {
359        const active = tabIndex === index;
360        tab.classList.toggle('is-active', active);
361        tab.setAttribute('aria-selected', String(active));
362        tab.tabIndex = active ? 0 : -1;
363      });
364      panels.forEach((panel, panelIndex) => {
365        const active = panelIndex === index;
366        panel.hidden = !active;
367        panel.classList.toggle('is-active', active);
368      });
369    };
370    tabs.forEach((tab, index) => {
371      tab.addEventListener('click', () => showPanel(index));
372      tab.addEventListener('keydown', event => {
373        if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
374        event.preventDefault();
375        const next = (index + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length;
376        showPanel(next);
377        tabs[next]?.focus();
378      });
379    });
380    showPanel(0);
381  });
382
383  document.querySelectorAll('[data-package-browser]').forEach(browser => {
384    const tabs = [...browser.querySelectorAll('[data-package-tab]')];
385    const panels = [...browser.querySelectorAll('[data-package-panel]')];
386    const showPackage = id => {
387      tabs.forEach(tab => {
388        const active = tab.dataset.packageTab === id;
389        tab.classList.toggle('is-active', active);
390        tab.setAttribute('aria-selected', String(active));
391        tab.tabIndex = active ? 0 : -1;
392      });
393      panels.forEach(panel => {
394        const active = panel.dataset.packagePanel === id;
395        panel.hidden = !active;
396        panel.classList.toggle('is-active', active);
397      });
398    };
399    tabs.forEach((tab, index) => {
400      tab.addEventListener('click', () => showPackage(tab.dataset.packageTab));
401      tab.addEventListener('keydown', event => {
402        if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return;
403        event.preventDefault();
404        const next = (index + (event.key === 'ArrowRight' ? 1 : -1) + tabs.length) % tabs.length;
405        showPackage(tabs[next].dataset.packageTab);
406        tabs[next].focus();
407      });
408    });
409  });
410
411  document.querySelectorAll('.package-tier-grid').forEach(grid => {
412    const packageDetails = [...grid.querySelectorAll('.package-tier > details')];
413    let syncingPackageDetails = false;
414    packageDetails.forEach(detail => detail.addEventListener('toggle', () => {
415      if (syncingPackageDetails) return;
416      syncingPackageDetails = true;
417      packageDetails.forEach(item => { item.open = detail.open; });
418      window.requestAnimationFrame(() => { syncingPackageDetails = false; });
419    }));
420  });
421
422  document.querySelectorAll('[data-track]').forEach(element => element.addEventListener('click', () => {
423    emitConversion(element.dataset.track);
424  }));
425
426  document.querySelectorAll('[data-product-tour-zoom], [data-product-mobile-zoom]').forEach(trigger => trigger.addEventListener('click', () => {
427    const source = trigger.dataset.productTourZoom || trigger.dataset.productMobileZoom;
428    const mobileView = Boolean(trigger.dataset.productMobileZoom);
429    if (!source || !lightboxBody) return;
430    const slide = document.createElement('article');
431    slide.className = `app-slide app-slide-screenshot is-active${mobileView ? ' is-mobile-shot' : ''}`;
432    const preview = document.createElement('img');
433    preview.src = source;
434    preview.alt = trigger.querySelector('img')?.alt || (english ? 'Enlarged demo application view' : 'Uvećani prikaz demo aplikacije');
435    slide.append(preview);
436    lightboxBody.replaceChildren(slide);
437    if (lightboxLabel) lightboxLabel.textContent = mobileView
438      ? (english ? 'Mobile application view' : 'Mobilni prikaz aplikacije')
439      : (english ? 'Available demo application view' : 'Prikaz dostupne demo aplikacije');
440    openOverlay(lightbox);
441  }));
442  document.querySelector('[data-lightbox-close]')?.addEventListener('click', () => closeOverlay(lightbox));
443  lightbox?.addEventListener('click', event => { if (event.target === lightbox) closeOverlay(lightbox); });
444
445  document.querySelectorAll('[data-accordion]').forEach(accordion => accordion.querySelectorAll('.faq-item > button').forEach(button => button.addEventListener('click', () => {
446    const item = button.closest('.faq-item');
447    const open = !item.classList.contains('is-open');
448    accordion.querySelectorAll('.faq-item').forEach(other => {
449      other.classList.remove('is-open');
450      other.querySelector(':scope > button')?.setAttribute('aria-expanded', 'false');
451    });
452    if (open) {
453      item.classList.add('is-open');
454      button.setAttribute('aria-expanded', 'true');
455    }
456  })));
457
458  document.querySelectorAll('[data-assessment]').forEach(assessment => {
459    const steps = [...assessment.querySelectorAll('[data-assessment-step]')];
460    const progress = assessment.querySelector('[data-assessment-progress]');
461    const count = assessment.querySelector('[data-assessment-count]');
462    const result = assessment.querySelector('[data-assessment-result]');
463    let stepIndex = 0;
464    const showStep = index => {
465      stepIndex = Math.max(0, Math.min(steps.length - 1, index));
466      steps.forEach((step, current) => step.classList.toggle('is-active', current === stepIndex));
467      if (progress) progress.style.width = `${(stepIndex / steps.length) * 100}%`;
468      if (count) count.textContent = `${stepIndex} od ${steps.length} oblasti`;
469      steps[stepIndex]?.querySelector('input')?.focus();
470    };
471    const showResult = () => {
472      const score = steps.reduce((total, step) =>
472 total + Number(step.querySelector('input:checked')?.value || 0), 0);
473      steps.forEach(step => step.classList.remove('is-active'));
474      if (!result) return;
475      result.hidden = false;
476      const scoreNode = result.querySelector('[data-assessment-score]');
477      const titleNode = result.querySelector('[data-assessment-title]');
478      const copyNode = result.querySelector('[data-assessment-copy]');
479      const linksNode = result.querySelector('[data-assessment-links]');
480      if (scoreNode) scoreNode.textContent = `${score} / 10`;
481      if (titleNode) titleNode.textContent = score >= 8 ? 'Slučaj primene je spreman za pilot pripremu.' : score >= 5 ? 'Postoji osnova, ali nekoliko preduslova treba razjasniti.' : 'Prvi korak treba da bude uređenje problema, procesa ili izvora.';
482      if (copyNode) copyNode.textContent = score >= 8 ? 'Sledeći korak je razgovor o obimu, integracijama, rizicima i merilu uspeha.' : score >= 5 ? 'Pre pilota definišite vlasnika teme, odobrene izvore i granice ljudske potvrde.' : 'Počnite procenom ili kratkom AI strategijom pre izbora proizvoda.';
483      if (progress) progress.style.width = '100%';
484      if (count) count.textContent = `${steps.length} od ${steps.length} oblasti`;
485      if (linksNode) {
486        const recommendation = score >= 8 ? ['/kako-radi/implementacija/', 'Pripremite pilot'] : score >= 5 ? ['/kako-radi/integracije/', 'Pripremite izvore i integracije'] : ['/ai-resenja/ai-strategija/', 'Počnite AI strategijom'];
487        linksNode.innerHTML = `<button class="button button-primary" type="button" data-modal-open="consultation">Zakažite AI konsultaciju</button><a class="button button-secondary" href="${recommendation[0]}">${recommendation[1]}</a>`;
488        linksNode.querySelector('[data-modal-open]')?.addEventListener('click', () => openOverlay(document.querySelector('[data-modal="consultation"]')));
489      }
490      result.focus();
491    };
492    assessment.querySelectorAll('[data-assessment-next]').forEach((button, index) => button.addEventListener('click', () => {
493      const selected = steps[index]?.querySelector('input:checked');
494      if (!selected) {
495        steps[index]?.querySelector('label')?.focus?.();
496        return;
497      }
498      if (index === steps.length - 1) showResult(); else showStep(index + 1);
499    }));
500    assessment.querySelectorAll('[data-assessment-prev]').forEach((button, index) => button.addEventListener('click', () => showStep(index - 1)));
501    showStep(0);
502  });
503
504  const trapFocus = (event, overlay) => {
505    const focusable = [...overlay.querySelectorAll('button:not([disabled]),a[href],input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])')].filter(element => !element.hidden && element.offsetParent !== null);
506    if (!focusable.length) return;
507    const first = focusable[0];
508    const last = focusable[focusable.length - 1];
509    if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
510    else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
511  };
512  document.addEventListener('keydown', event => {
513    const openOverlayElement = document.querySelector('.modal-backdrop.is-open,.preview-lightbox.is-open');
514    if (event.key === 'Tab' && openOverlayElement) trapFocus(event, openOverlayElement);
515    if (event.key !== 'Escape') return;
516    if (openOverlayElement) closeOverlay(openOverlayElement);
517    closeMenu();
518    navItem?.classList.remove('is-open');
519    navTrigger?.setAttribute('aria-expanded', 'false');
520  });
521
522  document.querySelectorAll('[data-chat-journey]').forEach(journey => {
523    const steps = [...journey.querySelectorAll('[data-chat-step]')];
524    const messages = [...journey.querySelectorAll('[data-chat-message]')];
525    const observations = [...journey.querySelectorAll('[data-chat-observation]')];
526    const selectStep = index => {
527      steps.forEach((step, stepIndex) => {
528        const active = stepIndex === index;
529        step.classList.toggle('is-active', active);
530        step.setAttribute('aria-selected', String(active));
531      });
532      messages.forEach((message, messageIndex) => message.classList.toggle('is-active', messageIndex === index));
533      observations.forEach((observation, observationIndex) => {
534        const active = observationIndex === index;
535        observation.hidden = !active;
536        observation.classList.toggle('is-active', active);
537      });
538    };
539    steps.forEach((step, index) => {
540      step.addEventListener('click', () => selectStep(index));
541      step.addEventListener('mouseenter', () => {
542        if (window.matchMedia('(hover: hover)').matches) selectStep(index);
543      });
544    });
545    messages.forEach((message, index) => message.addEventListener('mouseenter', () => {
546      if (window.matchMedia('(hover: hover)').matches) selectStep(index);
547    }));
548  });
549
550  document.querySelectorAll('[data-chat-admin]').forEach(admin => {
551    const tabs = [...admin.querySelectorAll('[data-chat-admin-tab]')];
552    const panels = [...admin.querySelectorAll('[data-chat-admin-panel]')];
553    const selectPanel = name => {
554      tabs.forEach(tab => {
555        const active = tab.dataset.chatAdminTab === name;
556        tab.classList.toggle('is-active', active);
557        tab.setAttribute('aria-selected', String(active));
558      });
559      panels.forEach(panel => {
560        const active = panel.dataset.chatAdminPanel === name;
561        panel.hidden = !active;
562        panel.classList.toggle('is-active', active);
563      });
564    };
565    tabs.forEach(tab => tab.addEventListener('click', () => selectPanel(tab.dataset.chatAdminTab)));
566  });
567
568  const packageHelpTargets = [...document.querySelectorAll('[data-package-help]')];
569  if (packageHelpTargets.length) {
570    const tooltip = document.createElement('div');
571    tooltip.className = 'package-help-tooltip';
572    tooltip.setAttribute('role', 'tooltip');
573    tooltip.setAttribute('aria-hidden', 'true');
574    body.appendChild(tooltip);
575    let activeHelp = null;
576    const placeHelp = target => {
577      const bounds = target.getBoundingClientRect();
578      tooltip.style.left = '12px';
579      tooltip.style.top = '12px';
580      const tooltipBounds = tooltip.getBoundingClientRect();
581      let left = bounds.left + bounds.width / 2 - tooltipBounds.width / 2;
582      left = Math.max(12, Math.min(left, window.innerWidth - tooltipBounds.width - 12));
583      let top = bounds.top - tooltipBounds.height - 10;
584      if (top < 12) top = Math.min(window.innerHeight - tooltipBounds.height - 12, bounds.bottom + 10);
585      tooltip.style.left = `${Math.round(left)}px`;
586      tooltip.style.top = `${Math.round(top)}px`;
587    };
588    const showHelp = target => {
589      activeHelp = target;
590      tooltip.textContent = target.dataset.packageHelp || '';
591      tooltip.setAttribute('aria-hidden', 'false');
592      tooltip.classList.add('is-visible');
593      placeHelp(target);
594    };
595    const hideHelp = target => {
596      if (activeHelp !== target) return;
597      tooltip.classList.remove('is-visible');
598      tooltip.setAttribute('aria-hidden', 'true');
599      activeHelp = null;
600    };
601    packageHelpTargets.forEach(target => {
602      target.addEventListener('mouseenter', () => showHelp(target));
603      target.addEventListener('mouseleave', () => hideHelp(target));
604      target.addEventListener('focusin', () => showHelp(target));
605      target.addEventListener('focusout', () => hideHelp(target));
606    });
607    window.addEventListener('resize', () => activeHelp && placeHelp(activeHelp), { passive: true });
608    window.addEventListener('scroll', () => activeHelp && placeHelp(activeHelp), { passive: true, capture: true });
609  }
610
611  const contextualRobotAssets = {
612    '#7C3AED': '/robots/robot-ceo-kpi-clean.png',
613    '#6B21A8': '/robots/robot-ceo-kpi-clean.png',
614    '#FACC15': '/robots/robot-cfo-finance-clean.png',
615    '#EAB308': '/robots/robot-cfo-finance-clean.png',
616    '#22C55E': '/robots/robot-sales-funnel-clean.png',
617    '#EC4899': '/robots/robot-marketing-campaign-clean.png',
618    '#84CC16': '/robots/robot-hr-people-clean.png',
619    '#3B82F6': '/robots/robot-legal-contract-clean.png',
620    '#F97316': '/robots/robot-it-diagnostics-clean.png',
621    '#14B8A6': '/robots/robot-procurement-suppliers-clean.png',
622    '#64748B': '/robots/robot-manufacturing-line-clean.png',
623    '#94A3B8': '/robots/robot-cybercore-core-clean-v2.png',
624    '#6D28D9': '/robots/robot-cybercore-core-clean-v2.png',
625    '#06B6D4': '/robots/robot-customer-success-clean.png'
626  };
627  const assistantRouteRobotAssets = {
628    '/ai-resenja/ai-asistenti/ai-ceo-asistent/': '/robots/robot-ceo-kpi-clean.png',
629    '/ai-resenja/ai-asistenti/ai-cfo-asistent/': '/robots/robot-cfo-finance-clean.png',
630    '/ai-resenja/ai-asistenti/ai-prodavac/': '/robots/robot-sales-funnel-clean.png',
631    '/ai-resenja/ai-asistenti/ai-marketing-asistent/': '/robots/robot-marketing-campaign-clean.png',
632    '/ai-resenja/ai-asistenti/ai-hr-asistent/': '/robots/robot-hr-people-clean.png',
633    '/ai-resenja/ai-asistenti/ai-pravni-asistent/': '/robots/robot-legal-contract-clean.png',
634    '/ai-resenja/ai-asistenti/ai-it-servisni-asistent/': '/robots/robot-it-diagnostics-clean.png',
635    '/ai-resenja/ai-asistenti/ai-nabavka/': '/robots/robot-procurement-suppliers-clean.png',
636    '/ai-resenja/ai-asistenti/ai-proizvodnja/': '/robots/robot-manufacturing-line-clean.png',
637    '/ai-resenja/ai-asistenti/ai-customer-success/': '/robots/robot-customer-success-clean.png',
638    '/ai-resenja/ai-asistenti/cybercore/': '/robots/robot-cybercore-core-clean-v2.png'
639  };
640  const contextualRobotRules = [
641    ['.page-chatbot-detail,.chatbot-portfolio,.chatbot-hero-visual,.chatbot-journey,.chatbot-handoff-scene,.chatbot-handoff-warm,.chat-admin-shell,.chat-story', '/robots/robot-chatbot-conversation-clean-v2.png'],
642    ['.ai-assessment-experience,.assessment-report-simulation,.assessment-ribbon,.assessment-hero-visual', '/robots/robot-assessment-radar-clean-v2.png'],
643    ['.strategy-hero-visual,.strategy-opportunity-scene,.strategy-governance-scene', '/robots/robot-strategy-roadmap-clean.png'],
644    ['.package-choice-scene,.package-browser,.packages-hero-visual', '/robots/robot-assessment-radar-clean-v2.png'],
645    ['.case-hero-visual,.page-case-study,.related-proof-section', '/robots/robot-report-dashboard-clean.png'],
646    ['.how-story-section,.how-hero-visual', '/robots/robot-general-brain-clean.png'],
647    ['.solution-entry-section,.solution-context-strategy', '/robots/robot-strategy-roadmap-clean.png'],
648    ['.hero-ai-world', '/robots/robot-general-brain-clean.png']
649  ];
650  const motionTypeForRobot = source => {
651    const entries = [
652      ['ceo-kpi', 'kpi'], ['cfo-finance', 'finance'], ['sales-funnel', 'funnel'],
653      ['marketing-campaign', 'campaign'], ['hr-people', 'people'], ['legal-contract', 'legal'],
654      ['it-diagnostics', 'diagnostics'], ['customer-success', 'customer'],
655      ['procurement-suppliers', 'procurement'], ['manufacturing-line', 'manufacturing'],
656      ['cybercore-core', 'core'], ['cybercore-brain', 'brain'], ['general-brain', 'brain'], ['chatbot-conversation', 'chat'],
657      ['assessment-radar', 'assessment'], ['strategy-roadmap', 'strategy'], ['report-dashboard', 'report']
658    ];
659    return entries.find(([token]) => source.includes(token))?.[1] || 'general';
660  };
661  const accentRobotFor = image => {
662    const accentScope = image.closest('[style*="--accent"]') || body;
663    const accent = getComputedStyle(accentScope).getPropertyValue('--accent').trim().toUpperCase();
664    return contextualRobotAssets[accent];
665  };
666  const contextualRobotFor = image => {
667    if (image.matches('.mega-feature-robot')) return '/robots/robot-general-brain-clean.png';
668    const linkedPath = image.closest('a[href]')?.getAttribute('href')?.replace(/^\/en(?=\/)/, '');
669    if (linkedPath && assistantRouteRobotAssets[linkedPath]) return assistantRouteRobotAssets[linkedPath];
670    for (const [selector, source] of contextualRobotRules) {
671      if (image.closest(selector)) return source;
672    }
673    const route = window.location.pathname.replace(/^\/en(?=\/)/, '');
674    const normalizedRoute = route.endsWith('/') ? route : `${route}/`;
675    if (assistantRouteRobotAssets[normalizedRoute]) return assistantRouteRobotAssets[normalizedRoute];
676    if (route.startsWith('/ai-resenja/ai-cetbotovi/')) return '/robots/robot-chatbot-conversation-clean-v2.png';
677    if (route === '/ai-resenja/ai-strategija/' || route === '/ai-resenja/ai-strategija') return '/robots/robot-strategy-roadmap-clean.png';
678    if (route === '/paketi/' || route === '/paketi') return '/robots/robot-assessment-radar-clean-v2.png';
679    if (route === '/procena-ai-spremnosti/' || route === '/procena-ai-spremnosti') return '/robots/robot-assessment-radar-clean-v2.png';
680    if (route.startsWith('/studije-slucaja/')) return '/robots/robot-report-dashboard-clean.png';
681    if (route === '/kako-radi/' || route === '/kako-radi') return '/robots/robot-general-brain-clean.png';
682    return accentRobotFor(image) || '/robots/robot-general-brain-clean.png';
683  };
684  document.querySelectorAll('img[src$="/ai-ceo-robot.webp"],img[src="/ai-ceo-robot.webp"]').forEach(image => {
685    if (!(image instanceof HTMLImageElement)) return;
686    const source = contextualRobotFor(image);
687    image.src = source;
688    if (image.closest('.mega-menu-v2')) return;
689    image.removeAttribute('width');
690    image.removeAttribute('height');
691    const wrapper = image.parentElement;
692    if (!wrapper) return;
693    wrapper.classList.add('has-contextual-robot');
694    wrapper.style.setProperty('--robot-image', `url("${source}")`);
695    image.closest('.catalog-bot')?.style.setProperty('--robot-image', `url("${source}")`);
696    wrapper.dataset.robotMotion = motionTypeForRobot(source);
697    if (!wrapper.querySelector(':scope > .contextual-robot-motion')) {
698      const motion = document.createElement('span');
699      motion.className = 'contextual-robot-motion';
700      motion.setAttribute('aria-hidden', 'true');
701      wrapper.appendChild(motion);
702    }
703  });
704  document.querySelectorAll('img[src*="/robots/robot-"]').forEach(image => {
705    if (image.hasAttribute('data-robot-preserve')) return;
706    if (image.closest('.mega-menu-v2')) return;
707    const resolvedSource = contextualRobotFor(image);
708    if (resolvedSource && image.getAttribute('src') !== resolvedSource) image.src = resolvedSource;
709    const wrapper = image.parentElement;
710    if (!wrapper) return;
711    wrapper.classList.add('has-contextual-robot');
712    wrapper.style.setProperty('--robot-image', `url("${resolvedSource || image.getAttribute('src')}")`);
713    image.closest('.catalog-bot')?.style.setProperty('--robot-image', `url("${resolvedSource || image.getAttribute('src')}")`);
714    image.closest('.assistant-card')?.style.setProperty('--robot-image', `url("${resolvedSource || image.getAttribute('src')}")`);
715    image.closest('.identity-robot-card')?.style.setProperty('--identity-robot-image', `url("${resolvedSource || image.getAttribute('src')}")`);
716    wrapper.dataset.robotMotion ||= motionTypeForRobot(resolvedSource || image.getAttribute('src') || '');
717    if (!wrapper.querySelector(':scope > .contextual-robot-motion')) {
718      const motion = document.createElement('span');
719      motion.className = 'contextual-robot-motion';
720      motion.setAttribute('aria-hidden', 'true');
721      wrapper.appendChild(motion);
722    }
723  });
724
725  const touchCards = [...document.querySelectorAll([
726    '.assistant-bot-card', '.portfolio-ceo', '.portfolio-core', '.chatbot-product-card', '.context-card',
727    '.assistant-flow-grid article', '.journey-steps article', '.solution-grid > a', '.evidence-grid article',
728    '.role-grid article', '.resource-grid a', '.trust-grid article', '.related-grid > a',
729    '.next-step-grid a', '.scope-grid article', '.blog-grid >
729 a', '.bullet-grid article', '.landing-flow-card',
730    '.assistant-architecture a', '.network-hotspot'
731  ].join(','))];
732  const coarsePointer = window.matchMedia('(hover: none) and (pointer: coarse)');
733  const closeTouchCards = except => touchCards.forEach(card => {
734    if (card !== except) card.classList.remove('is-touch-active');
735  });
736  touchCards.forEach(card => {
737    card.classList.add('touch-card');
738    card.addEventListener('click', event => {
739      if (!coarsePointer.matches || event.target.closest('button,input,select,textarea')) return;
740      if (!card.classList.contains('is-touch-active')) {
741        event.preventDefault();
742        closeTouchCards(card);
743        card.classList.add('is-touch-active');
744      } else if (!(card instanceof HTMLAnchorElement)) {
745        card.classList.remove('is-touch-active');
746      }
747    });
748  });
749  document.addEventListener('pointerdown', event => {
750    if (!coarsePointer.matches || event.target.closest('.touch-card')) return;
751    closeTouchCards();
752  }, { passive: true });
753
754  const revealTargets = [...document.querySelectorAll('main section > .container,.catalog-card,.assistant-flow-grid article,.journey-steps article,.landing-flow-card')]
755    .filter(element => !element.parentElement?.classList.contains('native-blog-index') && !element.closest('.native-blog-article'));
756  revealTargets.forEach((element, index) => {
757    element.classList.add('cc-reveal', `cc-reveal-delay-${index % 3}`);
758  });
759  if (reducedMotion.matches || !('IntersectionObserver' in window)) {
760    revealTargets.forEach(element => element.classList.add('is-visible'));
761  } else {
762    const revealObserver = new IntersectionObserver(entries => {
763      entries.forEach(entry => {
764        if (!entry.isIntersecting) return;
765        entry.target.classList.add('is-visible');
766        revealObserver.unobserve(entry.target);
767      });
768    }, { rootMargin: '0px 0px -8% 0px', threshold: .08 });
769    revealTargets.forEach(element => revealObserver.observe(element));
770  }
771
772  document.querySelectorAll('[data-tilt]').forEach(element => {
773    element.addEventListener('pointermove', event => {
774      if (reducedMotion.matches || event.pointerType === 'touch') return;
775      const bounds = element.getBoundingClientRect();
776      const x = (event.clientX - bounds.left) / bounds.width - .5;
777      const y = (event.clientY - bounds.top) / bounds.height - .5;
778      element.style.setProperty('--rx', `${(-y * 3.5).toFixed(2)}deg`);
779      element.style.setProperty('--ry', `${(x * 4.5).toFixed(2)}deg`);
780    });
781    element.addEventListener('pointerleave', () => {
782      element.style.setProperty('--rx', '0deg');
783      element.style.setProperty('--ry', '0deg');
784    });
785  });
786
787  document.querySelectorAll('[data-year]').forEach(node => { node.textContent = String(new Date().getFullYear()); });
788})();

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.