PageSourceSearch

https://www.hammer-ims.com/about

html hammer-ims.com collected 2026-10-05 20:05:32 UTC 63,597 bytes, 1,148 lines download raw bytes

1<!DOCTYPE html>
2<!--
3
4  Powered by...
5          _           _
6    _ __ (_)_ __ ___ | |__  _   _
7   | '_ \| | '_ ` _ \| '_ \| | | |
8   | | | | | | | | | | |_) | |_| |
9   |_| |_|_|_| |_| |_|_.__/ \__,_|
10
11  Interested? Visit https://nimbu.io
12
13--> <html class='no-js' itemscope itemtype='http://schema.org/WebPage' lang='en'> <head> <meta charset='utf-8'> <title>About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control</title> <meta content='hammer-ims' name='author'> <meta content='width=device-width, initial-scale=1, shrink-to-fit=no' name='viewport'> <meta content='Hammer-IMS, quality control, process control, innovative solutions, sustainability, precision, industrial technology, customer-centric solutions, quality, innovation, collaboration, process optimization' name='keywords'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' name='description'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/apple-touch-icon.png?8xqb1ia' rel='apple-touch-icon' sizes='180x180'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/favicon-32x32.png?8xqb1ia' rel='icon' sizes='32x32' type='image/png'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/favicon-16x16.png?8xqb1ia' rel='icon' sizes='16x16' type='image/png'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/manifest.json?8xqb1ia' rel='manifest'> <link color='#000000' href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/safari-pinned-tab.svg?8xqb1ia' rel='mask-icon'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/favicon.ico?8xqb1ia' rel='shortcut icon'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/favicon/browserconfig.xml?8xqb1ia' name='msapplication-config'> <meta content='#ffe431' name='theme-color'> <meta content='616924933' property='fb:admins'> <meta content='About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control' property='og:title'> <meta content='website' property='og:type'> <meta content='https://www.hammer-ims.com/about' property='og:url'> <meta content='hammer-ims.com' property='og:site_name'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' property='og:description'> <meta content='About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control' itemprop='name'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' itemprop='description'> <meta content='summary_large_image' name='twitter:card'> <meta content='@hammer-ims' name='twitter:site'> <meta content='About Us | Hammer-IMS - Innovative Solutions for Quality and Process Control' name='twitter:title'> <meta content='At Hammer-IMS, we shape the future of quality and process control with innovative, reliable solutions. Discover how we revolutionize the industry with precision and efficiency, and our commitment to sustainability, quality, and customer-centric solutions. Learn more about our vision and values.' name='twitter:description'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' name='twitter:image'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' property='og:image'> <meta content='1200' itemprop='og:image:width'> <meta content='630' itemprop='og:image:height'> <meta content='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' itemprop='image'> <link href='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/facebook_post_image.jpg?8xqb1ia' rel='image_src' type='image/jpeg'> <link href="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/stylesheets/app_v2.css?8xqb1ia" media="screen,print" rel="stylesheet" type="text/css"/> <meta name="csrf-param" content="authenticity_token"/> <meta name="csrf-token" content="WBjM_xudgw17VUlGPBK2gnnGx8hlXqOEp7JgoVRkXV3D4FmjwyMN6_egXakzO8ilX4uQO_h9u7Lk3FbM8I0zpA"/> <link rel="dns-prefetch" href="https://cdn.nimbu.io"/> <link rel="dns-prefetch" href="https://static.nimbu.io"/> <meta http-equiv="x-dns-prefetch-control" content="on"/> <link rel="canonical" href="https://www.hammer-ims.com/about"/> <link rel="alternate" href="https://www.hammer-ims.com/de/uber-uns" hreflang="de"/>
13 <link rel="alternate" href="https://www.hammer-ims.com/about" hreflang="en"/> <link rel="alternate" href="https://www.hammer-ims.com/fr/about" hreflang="fr"/> <link rel="alternate" href="https://www.hammer-ims.com/nl/over-ons" hreflang="nl"/> <meta name="referrer" content="strict-origin-when-cross-origin"/> 
13<script>
14      window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);}
15      gtag('consent', 'default', { 'ad_personalization': 'denied', 'ad_user_data': 'denied' });
16    </script>
16 
16<script data-type="text/javascript" type="opt-in" data-name="google_ads">
17      gtag('consent', 'update', {'ad_personalization': 'granted', 'ad_user_data': 'granted'});
18    </script>
vendor: 64 bytes, line 18
18 <script async src="https://www.googletagmanager.com/gtag/js?id=
18AW-959597372
vendor: 12 bytes, line 18
18"></script> 
18<script>
19      
vendor: 146 bytes, lines 19-23
19window.dataLayer = window.dataLayer || [];
20      function gtag(){dataLayer.push(arguments);}
21      gtag('js', new Date());
22
23      gtag('config', '
23AW-959597372
vendor: 8 bytes, lines 23-24
23');
24    
24</script>
24 
24<script>
25      gtag('event', 'conversion', {'send_to': 'AW-959597372/LD5yCLPnieIBELyWyckD'});
26    </script>
26 
26<script data-type='text/javascript' type='opt-in' data-name='hotjar'>
vendor: 131 bytes, lines 26-29
26
27      (function(h,o,t,j,a,r){
28          h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
29          h._hjSettings={hjid:
294941049
vendor: 272 bytes, lines 29-35
29,hjsv:6};
30          a=o.getElementsByTagName('head')[0];
31          r=o.createElement('script');r.async=1;
32          r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
33          a.appendChild(r);
34      })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
35    
35</script>
35 
35<script type="application/json" id="nimbu-consent-config">{"privacyPolicy":"/privacy","purposes":{"functional":"Basic website operation (functional - necessary)","analytics":"Tracking statistics","content":"Content from external sources","advertising":"Personalised advertising"},"apps":[{"name":"cms","title":"CMS","description":"Cookies that ensure that this website works.","purposes":["functional"],"required":true,"optOut":false,"default":true,"cookies":[],"placeholderMessage":"","placeholderAction":""},{"name":"nimbu_analytics","title":"Nimbu Analytics","description":" Built-in visitor statistics in the nimbu CMS (platform on which the website is built).","purposes":["analytics"],"required":false,"optOut":false,"default":true,"cookies":["_nimbu_session","_nimbu_secure_session","__cfduid","_nimbu_id","_nimbu_visit_id"],"placeholderMessage":"","placeholderAction":""},{"name":"google_analytics","title":"Google Analytics","description":"Visitor statistics that are tracked by Google and that help us improve the content of the website.","purposes":["analytics"],"required":false,"optOut":false,"default":true,"cookies":[["__utma","/",".hammer-ims.com"],["__utmt","/",".hammer-ims.com"],["__utmb","/",".hammer-ims.com"],["__utmc","/",".hammer-ims.com"],["__utmz","/",".hammer-ims.com"],["__utmv","/",".hammer-ims.com"],["/^_ga(_.*)?/","/","."]],"placeholderMessage":"","placeholderAction":""},{"name":"youtube","title":"Youtube","description":"Embedded YouTube videos","purposes":["content"],"required":false,"optOut":false,"default":true,"cookies":[["GPS","/",".youtube.com"],["IDE","/",".doubleclick.net"],["VISITOR_INFO1_LIVE","/",".youtube.com"],["YSC","/",".youtube.com"]],"placeholderMessage":"Hier wordt inhoud van Youtube geblokkeerd.","placeholderAction":"Klik hier om uw instellingen te wijzigen"},{"name":"google_ads","title":"Google Ads","description":"Display of personalised advertisements","purposes":["advertising"],"required":false,"optOut":false,"default":true,"cookies":[],"placeholderMessage":"","placeholderAction":""},{"name":"hotjar","title":"Hotjar","description":"Track user behavior and improve user experience on the website","purposes":["analytics"],"required":false,"optOut":false,"default":true,"cookies":[["/^_hj(_.*)?/","/","."]],"placeholderMessage":"","placeholderAction":""},{"name":"zoho","title":"Zoho","description":"Tracking visitor statistics","purposes":["analytics"],"required":false,"optOut":false,"default":false,"cookies":[["663a60c55d","/","salesiq.zoho.com"],["LS_CSRF_TOKEN","/","salesiq.zoho.com"],["ZohoMarkRef","/",".zoho.com"],["ZohoMarkSrc","/",".zoho.com"],["cookie-uid","/",".zoho.com"],["hammer-_zldp","/",".hammer-ims.com"],["hammer-_zldt","/",".hammer-ims.com"],["zld113379000000002043float","/",".hammer-ims.com"]],"placeholderMessage":"","placeholderAction":""}],"message":"We use cookies for the following purposes: {purposes}.","changed":"We added new applications that use cookies since your last visit, please adjust your preferences.","learnMore":"Read more","ok":"OK","save":"Save","decline":"Reject","close":"Close","modal":{"title":"We use cookies","message":"We use cookies. Below you can choose which applications you wish to allow.","privacyPolicy":{"name":"Privacy policy","text":"To know more, read our {privacyPolicy}."}}}</script>
35 
35<script defer type="application/javascript" src="https://cdn.nimbu.io/js/consent/nimbu-consent-manager-v1.1.4.js" data-style-prefix="nimbuCookie"></script>
35 
35<script>
36  window.dataLayer = window.dataLayer || [];
37  function gtag(){dataLayer.push(arguments);}
38  gtag('consent', 'default', {
39    'ad_storage': 'denied',
40    'analytics_storage': 'denied',
41    'ad_personalization': 'denied',
42    'ad_user_data': 'denied' ,
43    'wait_for_update': 500
44  });
45  gtag('set', 'ads_data_redaction', true);
46</script>
46 
46<script>
vendor: 327 bytes, lines 46-50
46(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
47new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
48j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
49'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
50})(window,document,'script','dataLayer','
50GTM-53XGBWK
vendor: 3 bytes, line 50
50');
50</script>
50 
50<script type="opt-in" data-type="text/javascript" data-name="google_ads">
51  gtag('consent', 'update', {'ad_storage': 'granted', 'ad_personalization': 'granted', 'ad_user_data': 'granted'});
52</script>
52 
52<script type="opt-in" data-type="text/javascript" data-name="google_analytics">
53  gtag('consent', 'update', {'analytics_storage': 'granted'});
54</script>
54</head> <body class='body-page_v2' id='body'> <div aria-describedby='unsupportedBrowserDesc' aria-labelledby='unsupportedBrowserTitle' aria-modal='true' data-nosnippet id='unsupported-browser' role='dialog' style='display: none'> <div> <img aria-hidden='true' loading='lazy' src='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/browser-unsupported.svg?8xqb1ia' alt="browser unsupported"> <div class='h2' id='unsupportedBrowserTitle'>Je browser wordt niet langer ondersteund</div> <div class='h3' id='unsupportedBrowserDesc'> Om onze website te gebruiken kan je één van de volgende browsers proberen: <a href='https://www.google.com/chrome/'>Google Chrome,</a> <a href='https://www.mozilla.org/nl/firefox/new/'>Mozilla Firefox</a> of <a href='https://www.microsoft.com/nl-nl/edge'>Microsoft Edge</a> </div> <button onclick='ignoreOldBrowser();'> Ga toch door </button> <div class='unsupported-browser-risk'> Door de website toch te gebruiken met deze browser kan de website er anders uitzien dan bedoelt en kunnen wij de veiligheid niet garanderen </div> </div> </div> <style>
55  @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
56  #unsupported-browser {
57    display: block !important;
58    font-family: Helvetica, sans-serif;
59  }
60  }
61  #unsupported-browser {
62    position: fixed;
63    top: 0;
64    left: 0;
65    right: 0;
66    bottom: 0;
67    background-color: #f6f6f6;
68    z-index: 99999999999;
69    text-align: center;
70  }
71  #unsupported-browser .h3 {
72    font-size: 18px;
73    text-transform: initial;
74    line-height: 27px;
75    width: 400px;
76    max-width: 100%;
77    margin-left: auto;
78    margin-right: auto;
79    margin-top: 0;
80  }
81  #unsupported-browser .h2 {
82    font-size: 25px;
83    line-height: 30px;
84    font-weight: 600;
85    text-transform: initial;
86    margin-bottom: 10px;
87    margin-top: 30px
88  }
89  #unsupported-browser > div {
90    position: absolute;
91    top: 50%;
92    left: 50%;
93    transform: translate(-50%, -50%);
94    max-width: 100%;
95    padding: 30px;
96  }
97  #unsupported-browser img {
98    width: 500px;
99    height: 209px;
100    max-width: 100%;
101  }
102  #unsupported-browser button {
103    border: 1px solid #888 ;
104    background-color: transparent;
105    padding: 7px 15px;
106    color: #888;
107    margin-top: 60px;
108  }
109  .unsupported-browser-risk {
110    margin-left: auto;
111    margin-right: auto;
112    max-width: 80%;
113    width: 500px;
114    margin-top: 10px;
115    font-size: 14px;
116    line-height: 16px;
117    color: #888;
118  }
119</style> 
119<script>
120  var getCookieValue = document.cookie.match('(^|;) ?' + "ignore_old_browser" + '=([^;]*)(;|$)');
121  var cookieValue = getCookieValue ? getCookieValue[2] : null;
122
123  if (cookieValue != null) {
124    var oldBrowserMessage = document.getElementById("unsupported-browser")
125    oldBrowserMessage.parentNode.removeChild(oldBrowserMessage);
126  }
127
128  function ignoreOldBrowser() {
129    document.cookie="ignore_old_browser=true"
130    var oldBrowserMessage = document.getElementById("unsupported-browser")
131    oldBrowserMessage.parentNode.removeChild(oldBrowserMessage);
132  }
133</script>
133 <div class='page-container'> <header class='main-header'> <div class='top-nav'> <div class='container'> <nav id='top-nav'> <ul class='nav'> <li id="top-about-us-link" class="link current first"><a href="/about">About us</a></li> <li id="top-news-link" class="link"><a href="/news">News</a></li> <li id="top-careers-link" class="link"><a href="/careers">Careers</a></li> <li id="top-locations-link" class="link"><a href="/locations">Locations</a></li> <li id="top-exhibitions-link" class="link"><a href="/exhibitions">Exhibitions</a></li> <li id="top-customer-support-link" class="link"><a href="/login">Customer support</a></li> <li id="top-representatives-link" class="link last"><a href="/representatives">Representatives</a></li></ul> <div class='social-medias'> <div class='language-buttons nav-link d-flex justify-content-center align-items-center'> <a class='' href='/de/uber-uns'> de </a> <a class='active order-first' href='/en/about'> en </a> <a class='' href='/fr/about'> fr </a> <a class='' href='/nl/over-ons'> nl </a> </div> <div class='social-media nav-link'> <a href='https://www.linkedin.com/company/hammer-ims/' target='_blank'> <i class='fab fa-linkedin'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.youtube.com/@Hammer-IMS/videos' target='_blank'> <i class='fab fa-youtube'></i> </a> </div> <div class='search-btn nav-link'> <button type='button'> <i class='far fa-search'></i> </button> </div> </div> </nav> </div> </div> <div class='main-nav-container'> <div class='container'> <a class='logo' href='/'> <img alt='logo' src='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/logo_v2.png?8xqb1ia'> </a> <nav id='main-nav'> <ul class='nav'> <li id="main-div-style-text-align-center-line-height-1-5-measuring-br-technologies-div-link" class="link first"><a href="/measuring-technologies"><div style="text-align:center;line-height:1.5;">Measuring <br> technologies</div></a></li> <li id="main-div-style-text-align-center-line-height-1-5-camera-br-technologies-div-link" class="link"><a href="/camera-technologies"><div style="text-align:center;line-height:1.5;">Camera<br> technologies</div></a></li> <li id="main-industries-link" class="link"><a href="/industries">Industries</a></li> <li id="main-products-link" class="link"><a href="/products">Products</a></li> <li id="main-documentation-link" class="link"><a href="/documentation">Documentation</a></li> <li id="main-references-link" class="link"><a href="/our-customers">References</a></li> <li id="main-faq-link" class="link"><a href="/faq">FAQ</a></li> <li id="main-book-a-demo-link" class="link"><a href="/book-a-demo">Book a demo</a></li> <li id="main-contact-us-link" class="link last"><a href="/contact">Contact us</a></li></ul> </nav> </div> <a class='hamburger hamburger--collapse' href='javascript:void(0)'> <span class='hamburger-box'> <span class='hamburger-inner'></span> </span> </a> </div> <div class='search-bar with-topnav'> <form action='/search' data-leads-ignore="true"> <input name='q' placeholder='Search' type='text'> </form> <button class='search-close'> <i class='far fa-times'></i> </button> </div> </header> <div aria-atomic='true' aria-live='polite' class='position-relative' id='toast-live-container'> <div class='toast-container position-fixed top-0 end-0 p-3'> </div> </div> <div id='main'> <section class='heading-section overlay-black overlay-opacity-opaque ' style=''> <div class='overlay'></div> <div class='container'> <div class='row align-items-center'> <div class="col-md-9 col-lg-8 col-xxl-6"> <div class='title-wrapper'> <h1 class='title mb-0'>About us</h1> <ul class='breadcrumb'><li itemscope itemtype="http://data-vocabulary.org/Breadcrumb"><a href='/' itemprop="url"><span itemprop="title">Home</span></a></li><li class='current' itemscope itemtype="http://data-vocabulary.org/Breadcrumb"><a href='/about' itemprop="url"><span itemprop="title">About us</span></a></li></ul> </div> </div> </div> </div> </section> <section class='content-section text-and-image-section theme-auto default-image-size spacing-options-both '> <div class='container'> <div class='row align-items-center'> <div class='text-wrapper col-md-5 wysiwyg order-md-last offset-md-1'> <p><strong><span style="font-size: 24px;"><strong></strong><span style="color: rgb(0, 0, 0);">Shaping tomorrow's&nbsp;quality and process control</span></span></strong></p> <p>At Hammer-IMS, our mission is to provide innovative, high-tech, and reliable quality and&nbsp;process control solutions that help our customers optimize their production processes,&nbsp;improve efficiency, and promote sustainability. We strive to remain at the forefront of the&nbsp;industry by constantly innovating and anticipating our customers' needs.</p> <div class="btns-wrapper d-flex flex-wrap"> </div> </div> <div class='image-wrapper col-md-6 wysiwyg text-center mt-md-0 mt-5 order-md-first'> <img alt='' src='https://cdn.nimbu.io/s/4j0nxju/pages/hs0pbal/1720441834227/team.jpg?ba62c12'> </div> </div> </div> </section> <section class='content-section text-and-image-section theme-gray default-image-size spacing-options-both '> <div class='container'> <div class='row align-items-center'> <div class='text-wrapper col-md-5 wysiwyg '> <p><strong><span style="font-size: 24px;"><span style="color: rgb(0, 0, 0);">
133Revolutionizing the industry with precision and efficiency</span></span></strong></p> <p>Our vision is to lead in a world where advanced quality and process control&nbsp;technologies are seamlessly integrated into industrial processes, maximising production&nbsp;efficiency and quality. We aim to lead the industry into a future where accurate, non-destructive measurement methods are the norm, enabling companies to increase&nbsp;productivity and reduce waste.</p> <div class="btns-wrapper d-flex flex-wrap"> </div> </div> <div class='image-wrapper col-md-6 wysiwyg text-center mt-md-0 mt-5 offset-md-1'> <img alt='' src='https://cdn.nimbu.io/s/4j0nxju/pages/1pfm0mi/172044137235/spartanburg.jpg?x4b9ya8'> </div> </div> </div> </section> <section class='content-section text-section theme-auto spacing-options-both'> <div class='container'> <div class='row justify-content-center'> <div class='text-wrapper col-md-8 wysiwyg text-columns-1'> <h3 style="text-align: center;"><strong><strong><span style="font-size: 30px;">Our guiding values</span></strong></strong></h3> <p style="text-align: center;">Our values shape everything we do. They inspire our team, drive our actions, and ensure we deliver the best results.<br> </p> </div> </div> </div> </section> <section class='content-section cards-section theme-auto spacing-options-bottom'> <div class='container cards-container'> <div class='row'> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720431809897/artboard-22.png" data-image="tadbi4sg1ivw" width="104" height="104" style="width: 104px; max-width: 104px; height: 104px;"></figure> <p><br></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Commitment to sustainability&nbsp;</strong></strong></span></p> <p>We are dedicated to sustainability in all its forms—economic, ecological, and social. Our machines do not contain nuclear components, prioritizing environmentally friendly and safe technologies.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720431998791/artboard-22_1.png" data-image="uongku8vv4ir" width="106" height="106" style="width: 106px; max-width: 106px; height: 106px;"></figure> <p><br></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Focus on quality&nbsp;</strong></strong></span></p> <p>Quality is at the heart of everything we do. We ensure that our products and services meet the highest standards and consistently exceed customer expectations.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720432099896/artboard-22_2.png" data-image="zb1b6i77fqvb" width="107" height="107" style="width: 107px; max-width: 107px; height: 107px;"></figure> <p><span style="font-size: 24px;"><strong><span style="font-size: 18px;"></span><br></strong></span></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Passion for innovation&nbsp;</strong></strong></span></p> <p>We embrace continuous innovation and strive to develop groundbreaking solutions that set new standards in industrial measurement te
133chnologies. We stay ahead of industry trends and anticipate our customers' evolving needs.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720432138504/artboard-22_3.png" data-image="sxwuxs3rnhhf" width="105" height="105" style="width: 105px; max-width: 105px; height: 105px;"></figure> <p><br></p> <p><span style="font-size: 28px;"><strong><strong><span style="color: rgb(0, 0, 0);">Customer-centric solutions&nbsp;</span></strong></strong></span></p> <p>We listen to our customers' needs and are committed to delivering tailored solutions that meet their specific requirements and expectations. Our customer-centric approach drives us to provide exceptional service and support.</p></div> </div> </div> </div> <div class='col-12 col-md-6 col-lg-4 d-flex'> <div class='card base-card card-theme-gray'> <div class='card-body'> <h5 class='card-title'></h5> <div class='card-text'><figure style="margin-left: auto; margin-right: auto; text-align: center;"><lt-highlighter contenteditable="false" style="display: none; z-index: 1 !important;" data-lt-linked="1"><lt-div spellcheck="false" class="lt-highlighter__wrapper" style="width: 0px !important; height: 19.5px !important; transform: none !important; transform-origin: 0px 9.75px !important; zoom: 1 !important; margin-left: -9999px !important;"><lt-div class="lt-highlighter__scroll-element" style="top: 0px !important; left: 0px !important; width: 0px !important; height: 19.5px !important;"></lt-div></lt-div></lt-highlighter><img src="https://cdn.nimbu.io/s/4j0nxju/assets/1720432201812/artboard-22_4.png" data-image="1dzn1u69eyjq" width="103" height="103" style="width: 103px; max-width: 103px; height: 103px;"></figure> <p><br></p> <p><span style="font-size: 28px; color: rgb(0, 0, 0);"><strong><strong>Stronger together&nbsp;</strong></strong></span></p> <p>We believe that success is achieved through collaboration. We value teamwork and foster a cooperative environment, both internally and with our external partners. Collective effort and shared success are key to our approach.</p></div> </div> </div> </div> </div> </div> </section> </div> <footer class="footer-v2"> <div class='container'> <div class='footer-content'> <div class="row justify-content-between footer-top-row"> <div class="col-md-6 col-lg-4 text-center text-md-start"> <a class='logo-footer-link' href='/'> <img alt='logo in footer' class='logo-footer' src='https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/images/logo_white_v2.png?8xqb1ia'> </a> </div> <div class="col-md-6 d-flex justify-content-center justify-content-md-end mt-4 mt-md-0"> <div class="btns-wrapper d-flex"> <a href="/book-appointment" class="btn btn-outline-white first"> Book a demo </a> <a href="/contact" class="btn btn-primary last"> Contact us </a> </div> </div> </div> <div class='row footer-bottom-row'> <div class='col-md-3 col-lg-3'> <div class="tagline"> Hammer-IMS is your one-stop shop for advanced quality and process control solutions. We specialize in measurement systems for thickness and basis weight, as well as surface inspection systems for anomaly detection. Our cutting-edge M-Ray technology ensures non-nuclear, radiation-free solutions, delivering precision and safety in every application. </div> </div> <div class='col-6 col-md-2 col-lg-2'> <ul class='footernav mt-4 mt-md-0'> <li id="footer-measuring-technologies-link" class="link first"><a href="/measuring-technologies">Measuring technologies</a></li> <li id="footer-camera-technologies-link" class="link"><a href="/camera-technologies">Camera technologies</a></li> <li id="footer-industries-link" class="link"><a href="/industries">Industries</a></li> <li id="footer-products-link" class="link"><a href="/products">Products</a></li> <li id="footer-documentation-link" class="link"><a href="/documentation">Documentation</a></li> <li id="footer-references-link" class="link"><a href="/our-customers">References</a></li> <li id="footer-faq-link" class="link last"><a href="/faq">FAQ</a></li></ul> </div> <div class='col-6 col-md-2 col-lg-2'> <ul class='footernav mt-4 mt-md-0'>
133 <li id="footer-about-us-link" class="link current first"><a href="/about">About us</a></li> <li id="footer-news-link" class="link"><a href="/news">News</a></li> <li id="footer-careers-link" class="link"><a href="/careers">Careers</a></li> <li id="footer-locations-link" class="link"><a href="/locations">Locations</a></li> <li id="footer-exhibitions-link" class="link"><a href="/exhibitions">Exhibitions</a></li> <li id="footer-partners-link" class="link"><a href="/measuring-technologies/suppliers">Partners</a></li> <li id="footer-feeds-link" class="link last"><a href="https://feeds.hammer-ims.com/">Feeds</a></li></ul> <div class='social-medias-footer mt-3 mt-md-0'> <div class='social-media nav-link'> <a href='https://www.linkedin.com/company/hammer-ims/' target='_blank'> <i class='fab fa-linkedin'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.facebook.com/hammerims' target='_blank'> <i class='fab fa-facebook-f'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.instagram.com/hammer_ims/' target='_blank'> <i class='fab fa-instagram'></i> </a> </div> <div class='social-media nav-link'> <a href='https://www.youtube.com/@Hammer-IMS/videos' target='_blank'> <i class='fab fa-youtube'></i> </a> </div> </div> </div> <div class="col-md-5 col-lg-5"> <div class="row"> <div class="col-12"> <div class='footer-company mt-3 mt-md-0'> <div class="footer-subtitle">Contact us</div> </div> </div> <div class="col-12 col-lg-6"> <div class='footer-company'> <p><strong>Belgium</strong><br>Industrieweg 1401 <br>3540 Herk-de-Stad <br><a href="mailto:[email protected]">[email protected]</a><br>+32 11 36 55 01<br>VAT (BE) 0648.896.643 RPR<br>Antwerp, Hasselt branch</p> </div> </div> <div class="col-12 col-lg-6"> <div class='footer-company'> <p><strong>United States</strong> <br>7092 Howard St Unit J <br> Spartanburg, SC 29303<br><a href="mailto:[email protected]">[email protected]</a> <br>+1 864 310 6050</p> </div> </div> </div> </div> </div> </div> <div class='footer-credentials '> <div class="row d-flex justify-content-between"> <div class="col-md-3 text-center text-md-start mb-2 mb-md-0"> &#169; 2026 Hammer-IMS </div> <div class="col-md-6 text-center d-flex justify-content-center mb-2 mb-md-0 flex-wrap"> <a href='/privacy'>Privacy</a> <a href='/terms-conditions'>Terms & Conditions</a> <a href='/cookie-policy' target='_blank'>Cookie Policy</a> <a href='http://www.nimbu.io' target='_blank'>Powered by Nimbu</a> </div> <div class="col-md-3 text-center text-md-end"> All rights reserved </div> </div> </div> </div> </footer> </div> 
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/polyfills.js?8xqb1ia" type="text/javascript"></script>
133 
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/vendor.swiper.js?8xqb1ia" type="text/javascript"></script>
133 
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/vendor.core.js?8xqb1ia" type="text/javascript"></script>
133 
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/vendor.js?8xqb1ia" type="text/javascript"></script>
133 
133<script src="https://cdn.nimbu.io/s/4j0nxju/themes/g9vdvso/javascripts/app_v2.js?8xqb1ia" type="text/javascript"></script>
133 
133<script data-environment="prod">
134/**
135* @module LeadsScript
136*/
137
138(function () {
139 "use strict";
140 // ============================================================================
141 // ATTRIBUTION TRACKER CLASS
142 // ============================================================================
143
144 /**
145  * @class AttributionTracker
146  */
147 class AttributionTracker {
148   /**
149    * @param {Object} options - Configuration options
150    * @param {number} options.trackingDays - Days to keep attribution data (default: 30)
151    * @param {string} options.sessionKey - sessionStorage key (default: "leads_attribution_session")
152    * @param {string} options.dataKey - localStorage key (default: "leads_attribution_data")
153    */
154   constructor(options = {}) {
155     this.sessionKey = options.sessionKey || "leads_attribution_session";
156     this.dataKey = options.dataKey || "leads_attribution_data";
157     this.trackingDays = options.trackingDays || 30;
158
159     this.init();
160   }
161
162   /**
163    * @private
164    */
165   init() {
166     // Generate session ID if not exists
167     try {
168       if (!sessionStorage.getItem(this.sessionKey)) {
169         sessionStorage.setItem(this.sessionKey, this.generateSessionId());
170       }
171     } catch (e) {
172       console.warn(
173         "[Leads Script] Session storage unavailable, attribution session disabled:",
174         e
175       );
176     }
177
178     // Clean old data
179     this.cleanOldData();
180
181     // Track this page visit
182     this.trackPageVisit();
183   }
184
185   /**
186    * @returns {string} Unique session identifier
187    * @private
188    */
189   generateSessionId() {
190     return (
191       "sess_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9)
192     );
193   }
194
195   /**
196    * @private
197    */
198   trackPageVisit() {
199     const currentUrl = window.location.href;
200     const currentPage = window.location.pathname;
201     const referrer = document.referrer;
202     const urlParams = new URLSearchParams(window.location.search);
203
204     // Get existing attribution data
205     let attributionData = this.getAttributionData();
206
207     // If this is the first visit (no landing page recorded) or data is expired
208     if (
209       !attributionData.landing_page ||
210       this.isDataExpired(attributionData)
211     ) {
212       attributionData = this.resetAttributionData();
213       attributionData.landing_page = currentUrl;
214       attributionData.landing_timestamp = new Date().toISOString();
215
216       // Determine lead source for new session
217       attributionData = this.determineLeadSource(
218         attributionData,
219         referrer,
220         urlParams
221       );
222     }
223
224     // Always update current page info
225     attributionData.current_page = currentPage;
226     attributionData.current_url = currentUrl;
227     attributionData.last_visit = new Date().toISOString();
228
229     // Track page views
230     attributionData.page_views = (attributionData.page_views || 0) + 1;
231
232     // Add current page to journey
233     if (!attributionData.page_journey) {
234       attributionData.page_journey = [];
235     }
236     attributionData.page_journey.push({
237       page: currentPage,
238       timestamp: new Date().toISOString(),
239       title: document.title
240     });
241
242     // Keep only last 20 pages to avoid storage bloat
243     if (attributionData.page_journey.length > 20) {
244       attributionData.page_journey = attributionData.page_journey.slice(-20);
245     }
246
247     // Store updated data
248     this.saveAttributionData(attributionData);
249   }
250
251   /**
252    * @param {Object} data - Attribution data object to update
253    * @param {string} referrer - Document referrer URL
254    * @param {URLSearchParams} urlParams - Current page URL parameters
255    * @returns {Object} Updated attribution data
256    * @private
257    */
258   determineLeadSource(data, referrer, urlParams) {
259     // Check UTM parameters first (highest priority)
260     if (urlParams.get("utm_source")) {
261       data.utm_source = urlParams.get("utm_source") || "";
262       data.utm_medium = urlParams.get("utm_medium") || "";
263       data.utm_campaign = urlParams.get("utm_campaign") || "";
264       data.utm_term = urlParams.get("utm_term") || "";
265       data.utm_content = urlParams.get("utm_content") || "";
266
267       // Determine lead source category based on UTM medium
268       const medium = data.utm_medium.toLowerCase();
269       if (medium === "cpc" || medium === "paid" || medium === "ppc") {
270         data.lead_source = "Paid";
271       } else if (medium === "social") {
272         data.lead_source = "Social";
273       } else if (medium === "email") {
274         data.lead_source = "Email";
275       } else if (medium === "referral") {
276         data.lead_source = "Referral";
277       } else {
278         data.lead_source = "Campaign";
279       }
280
281       // Use UTM term as search term if available
282       if (data.utm_term) {
283         data.search_term = data.utm_term;
284       }
285     } else if (referrer && referrer !== window.location.href) {
286       // Check referrer if no UTM parameters
287       data.referrer_url = referrer;
288
289       try {
290         const referrerUrl = new URL(referrer);
291         data.referrer_domain = referrerUrl.hostname;
292
293         const currentHost = window.location.hostname.toLowerCase();
294         const refHost = referrerUrl.hostname.toLowerCase();
295
296         // Treat same-domain traffic as Direct rather than Referral
297         if (currentHost === refHost) {
298           data.lead_source = "Direct";
299           return data;
300         }
301
302         // Check if it's from a search engine
303         if (this.isSearchEngine(referrer)) {
304           data.lead_source = "Organic";
305           data.search_term = this.extractSearchTerm(referrer);
306           data.search_engine = this.getSearchEngine(referrer);
307         } else if (this.isSocialMedia(referrer)) {
308           data.lead_source = "Social";
309           data.social_network = this.getSocialNetwork(referrer);
310         } else {
311           // External referral
312           data.lead_source = "Referral";
313         }
314       } catch (error) {
315         console.warn("Error parsing referrer URL:", error);
316         data.lead_source = "Referral";
317       }
318     } else {
319       // Direct traffic (no referrer or same-site referrer)
320       data.lead_source = "Direct";
321     }
322
323     return data;
324   }
325
326   /**
327    * @param {string} url - URL to check
328    * @returns {boolean} True if URL is from a search engine
329    * @private
330    */
331   isSearchEngine(url) {
332     const searchEngines = [
333       "google.com",
334       "google.co.uk",
335       "google.ca",
336       "google.com.au",
337       "bing.com",
338       "yahoo.com",
339       "duckduckgo.com",
340       "baidu.com",
341       "yandex.com",
342       "yandex.ru",
343       "ask.com",
344       "aol.com",
345       "ecosia.org",
346       "startpage.com",
347       "searx.org"
348     ];
349
350     try {
351       const domain = new URL(url).hostname.toLowerCase();
352       return searchEngines.some((engine) => domain.includes(engine));
353     } catch (error) {
354       console.warn("Error checking if URL is from search engine:", error);
355       return false;
356     }
357   }
358
359   /**
360    * @param {string} referrer - Referrer URL
361    * @returns {string} Search term or "(not provided)"
362    * @private
363    */
364   extractSearchTerm(referrer) {
365     try {
366       const url = new URL(referrer);
367       const params = new URLSearchParams(url.search);
368
369       // Common search parameter names
370       const searchParams = ["q", "query", "search", "p", "wd", "text"];
371
372       for (const param of searchParams) {
373         const term = params.get(param);
374         if (term) {
375           return decodeURIComponent(term).trim();
376         }
377       }
378
379       return "(not provided)";
380     } catch (error) {
381       console.warn("Error extracting search term from referrer:", error);
382       return "(not provided)";
383     }
384   }
385
386   /**
387    * @param {string} referrer - Referrer URL
388    * @returns {string} Search engine name or "Other"/"Unknown"
389    * @private
390    */
391   getSearchEngine(referrer) {
392     try {
393       const domain = new URL(referrer).hostname.toLowerCase();
394       if (domain.includes("google")) return "Google";
395       if (domain.includes("bing")) return "Bing";
396       if (domain.includes("yahoo")) return "Yahoo";
397       if (domain.includes("duckduckgo")) return "DuckDuckGo";
398       if (domain.includes("baidu")) return "Baidu";
399       if (domain.includes("yandex")) return "Yandex";
400       return "Other";
401     } catch (error) {
402       console.warn("Error getting search engine from referrer:", error);
403       return "Unknown";
404     }
405   }
406
407   /**
408    * @param {string} url - URL to check
409    * @returns {boolean} True if URL is from social media
410    * @private
411    */
412   isSocialMedia(url) {
413     const socialPlatforms = [
414       "facebook.com",
415       "twitter.com",
416       "x.com",
417       "linkedin.com",
418       "instagram.com",
419       "youtube.com",
420       "tiktok.com",
421       "pinterest.com",
422       "reddit.com",
423       "snapchat.com",
424       "whatsapp.com",
425       "telegram.org"
426     ];
427
428     try {
429       const domain = new URL(url).hostname.toLowerCase();
430       return socialPlatforms.some((platform) => domain.includes(platform));
431     } catch (error) {
432       console.warn("Error checking if URL is from social media:", error);
433       return false;
434     }
435   }
436
437   /**
438    * @param {string} referrer - Referrer URL
439    * @returns {string} Social network name or "Other Social"/"Unknown"
440    * @private
441    */
442   getSocialNetwork(referrer) {
443     try {
444       const domain = new URL(referrer).hostname.toLowerCase();
445       if (domain.includes("facebook")) return "Facebook";
446       if (domain.includes("twitter") || domain.includes("x.com"))
447         return "Twitter/X";
448       if (domain.includes("linkedin")) return "LinkedIn";
449       if (domain.includes("instagram")) return "Instagram";
450       if (domain.includes("youtube")) return "YouTube";
451       if (domain.includes("tiktok")) return "TikTok";
452       if (domain.includes("pinterest")) return "Pinterest";
453       if (domain.includes("reddit")) return "Reddit";
454       return "Other Social";
455     } catch (error) {
456       console.warn("Error getting social network from referrer:", error);
457       return "Unknown";
458     }
459   }
460
461   /**
462    * @returns {Object} Attribution data object
463    * @private
464    */
465   getAttributionData() {
466     try {
467       const stored = localStorage.getItem(this.dataKey);
468       if (stored) {
469         return JSON.parse(stored);
470       }
471     } catch (error) {
472       console.warn("Error parsing attribution data:", error);
473     }
474
475     return this.resetAttributionData();
476   }
477
478   /**
479    * @param {Object} data - Attribution data to save
480    * @private
481    */
482   saveAttributionData(data) {
483     try {
484       localStorage.setItem(this.dataKey, JSON.stringify(data));
485     } catch (error) {
486       console.warn("Error saving attribution data:", error);
487     }
488   }
489
490   /**
491    * @returns {Object} Fresh attribution data object
492    * @private
493    */
494   resetAttributionData() {
495     return {
496       lead_source: "Direct",
497       landing_page: "",
498       current_page: "",
499       search_term: "",
500       search_engine: "",
501       referrer_url: "",
502       referrer_domain: "",
503       social_network: "",
504       utm_source: "",
505       utm_medium: "",
506       utm_campaign: "",
507       utm_term: "",
508       utm_content: "",
509       landing_timestamp: "",
510       last_visit: "",
511       page_views: 0,
512       page_journey: []
513     };
514   }
515
516   /**
517    * @param {Object} data - Attribution data to check
518    * @returns {boolean} True if data is expired
519    * @private
520    */
521   isDataExpired(data) {
522     if (!data.landing_timestamp) return true;
523
524     const landingDate = new Date(data.landing_timestamp);
525     const now = new Date();
526     const daysDiff = (now - landingDate) / (1000 * 60 * 60 * 24);
527
528     return daysDiff > this.trackingDays;
529   }
530
531   /**
532    * @private
533    */
534   cleanOldData() {
535     const data = this.getAttributionData();
536     if (this.isDataExpired(data)) {
537       try {
538         localStorage.removeItem(this.dataKey);
539       } catch (e) {
540         console.warn("Error cleaning old attribution data:", e);
541         // Ignore storage failures; tracking degrades gracefully
542       }
543     }
544   }
545
546   /**
547    * @returns {string} Session ID or empty string if unavailable
548    * @private
549    */
550   getSessionId() {
551     try {
552       return sessionStorage.getItem(this.sessionKey) || "";
553     } catch (e) {
554       console.warn("Error getting session ID:", e);
555       return "";
556     }
557   }
558
559   /**
560    * @returns {Object} Complete attribution data object
561    * @public
562    */
563   getAllData() {
564     const data = this.getAttributionData();
565     const sessionId = this.getSessionId();
566
567     return {
568       ...data,
569       session_id: sessionId,
570       current_timestamp: new Date().toISOString()
571     };
572   }
573 }
574
575 // ============================================================================
576 // INITIALIZATION
577 // ============================================================================
578
579 /**
580  * @private
581  */
582 function initializeScript() {
583   if (document.readyState === "loading") {
584     document.addEventListener("DOMContentLoaded", initializeLeadsScript);
585   } else {
586     initializeLeadsScript();
587   }
588 }
589
590 /**
591  * @private
592  */
593 function initializeLeadsScript() {
594   const config = loadConfiguration();
595   if (!config) {
596     return; // Early exit if configuration fails
597   }
598
599   createAttributionTracker(config);
600   // Sets up form handling for both static and dynamic forms
601   setupFormObserver();
602
603   console.log("Leads script initialized successfully");
604 }
605
606 /**
607  * @returns {Object|null} Configuration object or null if script tag not found
608  * @private
609  */
610 function loadConfiguration() {
611   const scriptTag = document.currentScript || document.querySelector('script[data-environment]');
612
613   if (!scriptTag) {
614     console.error("Leads script: No script tag found");
615     return null;
616   }
617
618   // Only read data-environment, API path is derived from environment
619   const environment = scriptTag.getAttribute("data-environment") || "dev";
620   const apiPath = getDefaultApiPath(environment);
621
622   return {
623     apiPath: apiPath,
624     environment: environment
625   };
626 }
627
628 /**
629  * @param {string} environment - Environment name ('prod' or 'dev')
630  * @returns {string} API endpoint URL (defaults to 'dev' if invalid)
631  * @private
632  */
633 function getDefaultApiPath(environment) {
634   const endpoints = {
635     prod: "https://api.gushwork.ai/seo-v2/lead",
636     dev: "https://api-dev.gushwork.ai/seo-v2/lead"
637   };
638
639   return endpoints[environment] || endpoints.dev;
640 }
641
642 /**
643  * @param {Object} config - Configuration object with apiPath and environment
644  * @returns {AttributionTracker} Initialized tracker instance
645  * @private
646  */
647 function createAttributionTracker(config) {
648   // Store config globally for form submission functions
649   // Form handlers need API path but aren't passed config directly
650   window.__leadsConfig = config;
651
652   // Create tracker instance - constructor calls init() synchronously
653   // This means tracker is fully initialized when this function returns
654   const tracker = new AttributionTracker({
655     trackingDays: 30, // Attribution data expires after 30 days
656     sessionKey: "leads_attribution_session", // sessionStorage key
657     dataKey: "leads_attribution_data" // localStorage key
658   });
659
660   // Store tracker globally for access by other parts of the system
661   window.attributionTracker = tracker;
662   return tracker;
663 }
664
665 // Start initialization
666 initializeScript();
667
668 // ============================================================================
669 // FORM SUBMISSION UTILITIES
670 // ============================================================================
671
672 /**
673  * @param {HTMLFormElement} form - Form element to extract data from
674  * @returns {Object} Raw form data object with original field names
675  * @private
676  */
677 function extractFormData(form) {
678   const data = {};
679
680   const inputs = form.querySelectorAll("input, select, textarea");
681   inputs.forEach(function (input) {
682     // Skip submit buttons
683     if (input.type !== "submit") {
684       // Use name attribute, fall back to id if name doesn't exist
685       const fieldName = input.name || input.id;
686
687       if (fieldName) {
688         // Trim string values, preserve other types as-is
689         data[fieldName] =
690           typeof input.value === "string" ? input.value.trim() : input.value;
691       }
692     }
693   });
694
695   return data;
696 }
697
698 /**
699  * @returns {string} Product name or empty string
700  * @private
701  */
702 function getProductContext() {
703   let productName = "";
704
705   if (window.location.pathname.includes("/product/")) {
706     productName = document.title.split(" | ")[0] || document.title;
707   }
708
709   return productName;
710 }
711
712 /**
713  * @param {Object} formData - Raw form data object (all fields as-is)
714  * @param {string} requestType - Type of request (e.g., 'contact', 'blog_newsletter')
715  * @param {Object} additionalInfo - Additional form information
716  * @param {string} additionalInfo.productContext - Product context string
717  * @param {string} additionalInfo.formId - Form ID
718  * @param {Object} config - Configuration object
719  * @param {string} config.apiPath - API endpoint URL
720  * @returns {Promise<Object>} Submission result
721  * @private
722  */
723 async function submitToBackend(
724   formData,
725   requestType,
726   additionalInfo,
727   config
728 ) {
729   additionalInfo = additionalInfo || {};
730   const productName = additionalInfo.productContext || getProductContext();
731
732   let form_details = {
733     productName: productName,
734     requestType: requestType,
735     formId: additionalInfo.formId
736   };
737
738   try {
739     if (
740       window.attributionTracker &&
741       typeof window.attributionTracker.getAllData === "function"
742     ) {
743       const attributionData = window.attributionTracker.getAllData();
744       form_details = Object.assign({}, form_details, attributionData);
745     }
746   } catch (error) {
747     console.warn("Error getting attribution data for form submission:", error);
748     // No attribution data found, continue with form details
749   }
750
751   const apiPayload = {
752     request_origin: window.location.hostname.toLowerCase(),
753     leads_info: formData,
754     form_details: form_details
755   };
756
757   try {
758     const response = await fetch(config.apiPath, {
759       method: "POST",
760       headers: {
761         "Content-Type": "application/json"
762       },
763       body: JSON.stringify(apiPayload)
764     });
765
766     if (!response.ok) {
767       throw new Error("HTTP error! status: " + response.status);
768     }
769
770     const result = await response.json();
771
772     return { success: true, data: result };
773   } catch (error) {
774     console.error("API Error:", error);
775     return {
776       success: false,
777       error: error instanceof Error ? error.message : "Unknown error occurred"
778     };
779   }
780 }
781
782 /**
783  * @param {Event} event - Form submit event
784  * @param {string} requestType - Type of request (e.g., 'contact', 'blog_newsletter')
785  * @param {Object} options - Options object
786  * @param {Function} options.onSuccess - Callback on successful submission
787  * @param {Function} options.onError - Callback on error
788  * @param {string} options.productContext - Product context string
789  * @param {string} options.formId - Form ID
790  * @returns {Promise<Object>} Submission result
791  * @public
792  */
793 async function handleFormSubmission(event, requestType, options) {
794   options = options || {};
795
796   if (event && event.preventDefault) {
797     event.preventDefault();
798   }
799
800   const form = event ? event.currentTarget || event.target : null;
801   if (!form) {
802     const errorMsg = "Form element not found";
803     if (options.onError) {
804       options.onError(errorMsg);
805     }
806     return { success: false, error: errorMsg };
807   }
808
809   const formData = extractFormData(form);
810
811   const additionalInfo = {
812     formId: options.formId || form.getAttribute("data-form-id") || form.id || "",
813     productContext:
814       options.productContext ||
815       (form.dataset && form.dataset.productContext) ||
816       ""
817   };
818
819   // Get config from global window object (set during initialization)
820   const config = window.__leadsConfig || {};
821   if (!config.apiPath) {
822     const errorMsg =
823       "Leads script not correctly configured. Missing apiPath.";
824     console.error(errorMsg);
825     if (options.onError) {
826       options.onError(errorMsg);
827     }
828     return { success: false, error: errorMsg };
829   }
830
831   const result = await submitToBackend(
832     formData,
833     requestType,
834     additionalInfo,
835     config
836   );
837
838   if (result.success) {
839     // Mark that a form has been successfully submitted
840     try {
841       localStorage.setItem("formSubmitted", "true");
842     } catch (e) {
843       // localStorage might not be available, ignore silently
844       console.warn("Could not save form submission state:", e);
845     }
846     if (options.onSuccess) {
847       options.onSuccess(result.data);
848     }
849   } else {
850     if (options.onError) {
851       options.onError(result.error || "Submission failed");
852     }
853   }
854
855   return result;
856 }
857
858 // ============================================================================
859 // FORM HANDLING & OBSERVERS
860 // ============================================================================
861
862 /**
863  * @private
864  */
865 function setupFormObserver() {
866   // Process a newly added form
867   function processForm(form) {
868     // Attach auto-submission handler to all forms (defaults to "contact" type if not specified)
869     attachAutoSubmissionHandlerToForm(form);
870   }
871
872   // Process all existing forms on initialization
873   const existingForms = document.querySelectorAll("form");
874   existingForms.forEach(processForm);
875
876   // Watch for dynamically added forms
877   const observer = new MutationObserver(function (mutations) {
878     mutations.forEach(function (mutation) {
879       mutation.addedNodes.forEach(function (node) {
880         if (node.nodeType === Node.ELEMENT_NODE) {
881           if (node.tagName === "FORM") {
882             // Direct form node
883             processForm(node);
884           } else {
885             // Container node - check for nested forms
886             const forms =
887               node.querySelectorAll && node.querySelectorAll("form");
888             if (forms) {
889               forms.forEach(processForm);
890             }
891           }
892         }
893       });
894     });
895   });
896
897   observer.observe(document.body, {
898     childList: true,
899     subtree: true
900   });
901 }
902
903 /**
904  * @param {HTMLFormElement} form - Form element to attach handler to
905  * @private
906  */
907 function attachAutoSubmissionHandlerToForm(form) {
908   // Skip if already attached
909   if (form.dataset.leadsHandlerAttached === "true") {
910     return;
911   }
912
913   // Skip if form has data-leads-ignore attribute
914   if (form.getAttribute("data-leads-ignore") === "true") {
915     return;
916   }
917
918   // Mark as attached
919   form.dataset.leadsHandlerAttached = "true";
920
921   // Get configuration from form data attributes
922   const requestType = form.getAttribute("data-request-type") || "contact";
923   const productContext = form.getAttribute("data-product-context") || "";
924   const formId = form.getAttribute("data-form-id") || form.id || "";
925   // Check if UI gratification (success/error messages) should be shown
926   const showGratification = form.getAttribute("data-gratification") === "true";
927
928   // Attach submit handler
929   form.addEventListener("submit", async function (e) {
930     e.preventDefault();
931
932     const submitButton = form.querySelector(
933       'button[type="submit"], input[type="submit"]'
934     );
935     let errorContainer = null;
936     let successContainer = null;
937
938     // Store original button text BEFORE changing it
939     const originalButtonText = submitButton
940       ? submitButton.textContent || submitButton.value
941       : "";
942
943     // Only create UI elements if gratification is enabled
944     if (showGratification) {
945       errorContainer = getOrCreateErrorContainer(form);
946       successContainer = getOrCreateSuccessContainer(form);
947
948       // Hide previous messages
949       hideElement(errorContainer);
950       hideElement(successContainer);
951
952       // Show loading state on button
953       if (submitButton) {
954         submitButton.disabled = true;
955         if (submitButton.tagName === "BUTTON") {
956           submitButton.textContent = "Submitting...";
957         } else {
958           submitButton.value = "Submitting...";
959         }
960       }
961     }
962
963     // Submit form
964     try {
965       const result = await handleFormSubmission(e, requestType, {
966         productContext: productContext,
967         formId: formId,
968         onSuccess: function (_data) {
969           // Show success message only if gratification is enabled
970           if (showGratification && successContainer) {
971             showSuccessMessage(form, successContainer);
972           }
973           // Reset form
974           form.reset();
975           // Close modal if form is inside one
976           closeModalIfPresent(form);
977         },
978         onError: function (errorMsg) {
979           // Show error message only if gratification is enabled
980           if (showGratification && errorContainer) {
981             showErrorMessage(form, errorContainer, errorMsg);
982           }
983         }
984       });
985
986       if (!result.success && result.error) {
987         // Show error message only if gratification is enabled
988         if (showGratification && errorContainer) {
989           showErrorMessage(form, errorContainer, result.error);
990         }
991       }
992     } catch (error) {
993       const errorMsg =
994         error instanceof Error ? error.message : "Form submission failed";
995       // Show error message only if gratification is enabled
996       if (showGratification && errorContainer) {
997         showErrorMessage(form, errorContainer, errorMsg);
998       }
999     } finally {
1000       // Restore button state only if gratification is enabled
1001       if (showGratification && submitButton) {
1002         submitButton.disabled = false;
1003         if (submitButton.tagName === "BUTTON") {
1004           submitButton.textContent = originalButtonText;
1005         } else {
1006           submitButton.value = originalButtonText;
1007         }
1008       }
1009     }
1010   });
1011 }
1012
1013 // ============================================================================
1014 // UI HELPER FUNCTIONS
1015 // ============================================================================
1016
1017 /**
1018  * @param {HTMLFormElement} form - Form element
1019  * @returns {HTMLElement} Error container element
1020  * @private
1021  */
1022 function getOrCreateErrorContainer(form) {
1023   let container = form.querySelector(".leads-error-message");
1024   if (!container) {
1025     container = document.createElement("div");
1026     container.className = "leads-error-message";
1027     container.style.cssText =
1028       "padding: 12px; background-color: #fee; border: 1px solid #fcc; color: #c33; border-radius: 4px; margin-bottom: 16px; display: none;";
1029     form.insertBefore(container, form.firstChild);
1030   }
1031   return container;
1032 }
1033
1034 /**
1035  * @param {HTMLFormElement} form - Form element
1036  * @returns {HTMLElement} Success container element
1037  * @private
1038  */
1039 function getOrCreateSuccessContainer(form) {
1040   let container = form.querySelector(".leads-success-message");
1041   if (!container) {
1042     container = document.createElement("div");
1043     container.className = "leads-success-message";
1044     container.style.cssText =
1045       "padding: 12px; background-color: #efe; border: 1px solid #cfc; color: #3c3; border-radius: 4px; margin-bottom: 16px; display: none;";
1046     form.insertBefore(container, form.firstChild);
1047   }
1048   return container;
1049 }
1050
1051 /**
1052  * @param {HTMLFormElement} form - Form element
1053  * @param {HTMLElement} container - Error container element
1054  * @param {string} message - Error message to display
1055  * @private
1056  */
1057 function showErrorMessage(form, container, message) {
1058   container.textContent = message;
1059   showElement(container);
1060   // Auto-hide after 5 seconds
1061   setTimeout(function () {
1062     hideElement(container);
1063   }, 5000);
1064 }
1065
1066 /**
1067  * @param {HTMLFormElement} form - Form element
1068  * @param {HTMLElement} container - Success container element
1069  * @private
1070  */
1071 function showSuccessMessage(form, container) {
1072   container.textContent = "Thank you! We'll be in touch soon.";
1073   showElement(container);
1074   // Auto-hide after 5 seconds
1075   setTimeout(function () {
1076     hideElement(container);
1077   }, 5000);
1078 }
1079
1080 /**
1081  * @param {HTMLElement} element - Element to show
1082  * @private
1083  */
1084 function showElement(element) {
1085   element.style.display = "block";
1086 }
1087
1088 /**
1089  * @param {HTMLElement} element - Element to hide
1090  * @private
1091  */
1092 function hideElement(element) {
1093   element.style.display = "none";
1094 }
1095
1096 /**
1097  * @param {HTMLFormElement} form - Form element that was submitted
1098  * @private
1099  */
1100 function closeModalIfPresent(form) {
1101   // Check if form is inside a bifrost modal overlay
1102   const bifrostModal = form.closest(".modal-overlay, .modal-overlay2");
1103   if (bifrostModal) {
1104     // Close bifrost modal
1105     bifrostModal.classList.remove("active");
1106     bifrostModal.style.display = "none";
1107     document.body.style.overflow = "";
1108     form.reset();
1109     return;
1110   }
1111
1112   // Check if form is inside a React/Radix Dialog modal
1113   // Try multiple selectors for Radix Dialog
1114   const reactModal = form.closest(
1115     '[role="dialog"], [data-slot="dialog-content"], [data-radix-dialog-content]'
1116   );
1117   if (reactModal) {
1118     // Try to find and trigger the close button (multiple selectors)
1119     const closeButtonSelectors = [
1120       'button[aria-label*="close" i]',
1121       'button[aria-label*="Close" i]',
1122       "[data-radix-dialog-close]",
1123       '[data-slot="dialog-close"]',
1124       'button[type="button"]' // Last resort - might match wrong button
1125     ];
1126
1127     let closeButton = null;
1128     for (const selector of closeButtonSelectors) {
1129       closeButton = reactModal.querySelector(selector);
1130       if (closeButton) {
1131         break;
1132       }
1133     }
1134
1135     if (closeButton) {
1136       closeButton.click();
1137     }
1138   }
1139 }
1140
1141 if (typeof module !== "undefined" && module.exports) {
1142   module.exports = {
1143     AttributionTracker: AttributionTracker,
1144     handleFormSubmission: handleFormSubmission
1145   };
1146 }
1147})();
1148</script>
vendor: 68 bytes, line 1148
1148 <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
1148GTM-53XGBWK
vendor: 83 bytes, line 1148
1148" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript> 
1148</body> </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.