/* ═══════════════════════════════════════════════════════════════════════════ 1. DESKTOP MEGA-MENU — universal hover fix (all browsers) Elementor Pro adds .e-active to show dropdowns; this guarantees hover works even when Elementor JS fails to initialise on inner pages. ═══════════════════════════════════════════════════════════════════════════ */ (function () { var header, menuItems; function closeAll(except) { menuItems.forEach(function (other) { if (other === except) return; var oc = other.querySelector('.e-n-menu-content'); var op = oc && oc.querySelector('.e-con'); if (oc) { /* Suppress transition so the outgoing panel disappears instantly. This makes the switch feel like a single shared dropdown whose content updates, rather than two separate panels cross-fading. */ oc.classList.add('e-no-anim'); oc.classList.remove('e-active'); requestAnimationFrame(function () { oc.classList.remove('e-no-anim'); }); } if (op) op.classList.remove('e-active'); other.classList.remove('ktm-open'); var btn = other.querySelector('.e-n-menu-dropdown-icon'); if (btn) btn.setAttribute('aria-expanded', 'false'); }); } function initDesktopMenu() { header = document.querySelector('.darknav'); menuItems = document.querySelectorAll('.darknav .e-n-menu-item'); if (!menuItems.length) return; /* ── Fix full-width + correct vertical position ────────────────────── Elementor's .e-n-menu has position:relative, so its child .e-n-menu-content uses top:100% relative to .e-n-menu — NOT relative to the full header. This places the dropdown INSIDE the header instead of below it. We fix this by: 1. Setting --stretch-left/right/width so it spans the full viewport. 2. Setting --ktm-top = header.bottom - nMenu.top (px), so the dropdown starts at the exact bottom of the sticky header. */ var nMenu = document.querySelector('.darknav .e-n-menu'); function fixLayout() { if (!nMenu || !header) return; var r = nMenu.getBoundingClientRect(); var h = header.getBoundingClientRect(); nMenu.style.setProperty('--stretch-left', (-r.left) + 'px'); nMenu.style.setProperty('--stretch-right', (-(window.innerWidth - r.right)) + 'px'); nMenu.style.setProperty('--stretch-width', '100vw'); /* Own variables Elementor never writes to — used in our CSS override below so Elementor's first-hover recalc of --stretch-left on the child .e-n-menu-wrapper cannot shift the dropdown position. */ nMenu.style.setProperty('--ktm-left', (-r.left) + 'px'); nMenu.style.setProperty('--ktm-right', (-(window.innerWidth - r.right)) + 'px'); /* top value that places dropdown flush with header bottom */ var ktmTop = Math.round(h.bottom - r.top); nMenu.style.setProperty('--ktm-top', ktmTop + 'px'); /* Stretch the menu heading to fill the header height so nav items extend right to the dropdown top — eliminating the hover gap. */ var heading = nMenu.querySelector('.e-n-menu-heading'); if (heading) heading.style.height = ktmTop + 'px'; } fixLayout(); window.addEventListener('resize', fixLayout); window.addEventListener('scroll', fixLayout, { passive: true }); var s = document.createElement('style'); s.textContent = /* Position dropdown flush with header bottom. Use --ktm-left/right (set only by us, never by Elementor) so that Elementor's first-hover recalc of --stretch-left on .e-n-menu-wrapper cannot override our horizontal position and cause a shift. */ '.darknav .e-n-menu:not([data-layout=dropdown]) .e-active.e-n-menu-content{' + 'top:var(--ktm-top,100%)!important;' + 'padding-block-start:0!important;' + '}' + '.darknav .e-n-menu:not([data-layout=dropdown]) .e-n-menu-content{' + 'left:var(--ktm-left)!important;' + 'right:var(--ktm-right)!important;' + 'width:100vw!important;' + '}' + '.darknav .e-n-menu-content>.e-con{' + 'width:100%!important;max-width:100%!important;' + '}' + /* Stretch li items to fill the full heading height (Elementor overrides align-items:center on the heading which prevents stretching) */ '.darknav .e-n-menu-heading{align-items:stretch!important;}' + /* Stretch .e-n-menu-title to fill the full li height. The title div itself already has align-items:center so the text stays vertically centred inside it. */ '.darknav .e-n-menu-item{align-items:stretch!important;}' + /* Suppress Elementor's border-bottom underline on the title — we replace it with an ::after so we can position it flush under the text rather than at the bottom of the (tall) title element. */ '.darknav .e-n-menu-title{' + 'position:relative;z-index:2147483621!important;' + 'border:none!important;' + '}' + /* ::after is already used by Elementor for inter-item dividers, so we use ::before for the pink underline. Positioned 3px below the text baseline using top:calc(50% + 11px) so it stays close to the text regardless of how tall the title element is. */ '.darknav .e-n-menu-title::before{' + 'content:"";' + 'position:absolute;' + 'left:16px;right:16px;' + 'height:2px;' + 'top:calc(50% + 11px);' + 'background:transparent;' + 'transition:background 0.2s ease;' + 'pointer-events:none;' + '}' + /* Show on hover, on the current page, and while the dropdown is open */ '.darknav .e-n-menu-title:hover::before,' + '.darknav .e-n-menu-title.e-current::before,' + '.darknav .e-n-menu-item.ktm-open>.e-n-menu-title::before{' + 'background:#D76AB3!important;' + '}' + /* ── Show/hide animation ────────────────────────────────────────── Pure opacity fade — no translateY so the panel feels like a fixed shared surface rather than individual popups appearing from above. Elementor also fires its own CSS animation on the inner .e-con (open_animation: fadeIn); we suppress that so only our transition runs and the two don't stack. */ '.darknav .e-n-menu-content{' + 'opacity:0;' + 'transform:translateY(-6px);' + 'visibility:hidden;' + 'pointer-events:none;' + 'transition:opacity 0.18s ease,transform 0.18s ease,visibility 0s linear 0.18s;' + '}' + '.darknav .e-n-menu-content.e-active{' + 'opacity:1;' + 'transform:translateY(0);' + 'visibility:visible;' + 'pointer-events:all;' + 'transition:opacity 0.22s ease,transform 0.22s ease,visibility 0s linear 0s;' + '}' + /* Instant hide when switching items (added/removed in closeAll) */ '.darknav .e-n-menu-content.e-no-anim{transition:none!important;}' + /* Kill Elementor's own fadeIn CSS animation on the inner container */ '.darknav .e-n-menu-content>.e-con{animation:none!important;}' + /* No ::before hover bridge needed — the nav items fill the full header height (set in fixLayout), so their bottom edge is flush with the dropdown top and there is no gap to bridge. */ ''; document.head.appendChild(s); /* ── Hover handlers ───────────────────────────────────────────────── */ menuItems.forEach(function (item) { var content = item.querySelector('.e-n-menu-content'); if (!content) return; var panel = content.querySelector('.e-con'); if (!panel) return; var timer; function show() { clearTimeout(timer); closeAll(item); item.classList.add('ktm-open'); content.classList.add('e-active'); if (panel) panel.classList.add('e-active'); var btn = item.querySelector('.e-n-menu-dropdown-icon'); if (btn) btn.setAttribute('aria-expanded', 'true'); } function hide() { timer = setTimeout(function () { item.classList.remove('ktm-open'); content.classList.remove('e-active'); if (panel) panel.classList.remove('e-active'); var btn = item.querySelector('.e-n-menu-dropdown-icon'); if (btn) btn.setAttribute('aria-expanded', 'false'); }, 350); } /* When the cursor enters the nav item title, always show (and cancel any in-flight hide timer for THIS item). */ item.addEventListener('mouseenter', show); /* When the cursor leaves the nav item, schedule a hide — but only execute it if the cursor did NOT immediately enter the content panel (handled by the mouseenter on content below). */ item.addEventListener('mouseleave', function (e) { /* If the cursor is moving INTO the content panel, don't hide. */ if (content.contains(e.relatedTarget)) return; hide(); }); /* Keep the panel open while the cursor is inside it. */ content.addEventListener('mouseenter', function () { clearTimeout(timer); }); /* When the cursor leaves the content panel, hide — but if it's moving back to the same e-n-menu-item, cancel immediately. */ content.addEventListener('mouseleave', function (e) { /* relatedTarget is the element the cursor is moving INTO. */ if (item.contains(e.relatedTarget)) { /* Moving back to the nav item title — keep open, cancel hide. */ clearTimeout(timer); return; } hide(); }); }); document.addEventListener('click', function (e) { if (!e.target.closest('.darknav .e-n-menu-item')) { closeAll(null); } }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initDesktopMenu); } else { initDesktopMenu(); } })(); /* ═══════════════════════════════════════════════════════════════════════════ 2. MOBILE OFF-CANVAS MENU Slides from the right, accordion sections, matches the Advania design. ═══════════════════════════════════════════════════════════════════════════ */ (function () { /* ── Menu data (matches desktop navigation) ─────────────────────────── */ var MENU = [ { label: 'WHAT WE DO', subsections: [ { heading: 'Technology sourcing', items: [ { text: 'Data Centre & Networking', url: '/technology-sourcing/data-centre-and-networking/' }, { text: 'AV & Collaboration', url: '/technology-sourcing/av-collaboration/' }, { text: 'Devices & Management', url: '/technology-sourcing/devices-and-management/' }, { text: 'Print & Office Solutions', url: '/technology-sourcing/print-and-office-solutions/' }, { text: 'Software & Licensing', url: '/technology-sourcing/licensing/' } ], viewAll: { text: 'VIEW TECHNOLOGY SOURCING', url: '/technology-sourcing.1.html' } }, { heading: 'Solutions', items: [ { text: 'AI Transformation', url: '/our-solutions/ai-transformation/index.html' }, { text: 'Cloud, Platform & Network', url: '/our-solutions/cloud-platform-network/' }, { text: 'Cyber Resilience', url: '/our-solutions/cyber-resilience/index.html' }, { text: 'Data', url: '/our-solutions/data/' }, { text: 'Digital Employee Experience', url: '/our-solutions/digital-employee-experience/index.html' }, { text: 'High-Performance Computing', url: '/our-solutions/high-performance-compute/index.html' }, { text: 'Microsoft Dynamics 365 & Business Applications', url: '/our-solutions/dynamics-365-and-business-applications/' }, { text: 'Unified Communications', url: '/our-solutions/unified-communications/index.html' } ], viewAll: { text: 'VIEW ALL SOLUTIONS', url: '/our-solutions.html' } }, { heading: 'Services', items: [ { text: 'Managed IT Services', url: '/our-services/managed-it-services/index.html' }, { text: 'IT Consulting Services', url: '/our-services/professional-services/' }, { text: 'IT Services for SMBs', url: '/our-services/it-services-for-smb/index.html' } ], viewAll: { text: 'VIEW ALL SERVICES', url: '/our-services.html' } } ] }, { label: 'SECTORS', heading: 'Sectors', items: [ { text: 'Finance', url: '/solutions-by-sector/finance/index.html' }, { text: 'Insurance', url: '/solutions-by-industry/insurance/index.html' }, { text: 'Legal & Professional', url: '/solutions-by-sector/legal-and-professional/index.html' }, { text: 'Public Sector', url: '/solutions-by-sector/public-sector/index.html' }, { text: 'Leisure & Hospitality', url: '/solutions-by-sector/leisure-and-hospitality/index.html' }, { text: 'Not For Profit', url: '/solutions-by-sector/not-for-profit/index.html' }, { text: 'Retail', url: '/solutions-by-sector/retail/index.html' } ], viewAll: { text: 'VIEW ALL SECTORS', url: '/solutions-by-sector.html' } }, { label: 'PARTNERS', heading: 'Partners', items: [ { text: 'Apple', url: '/partners/apple/' }, { text: 'Cisco', url: '/partners/cisco/index.html' }, { text: 'Dell', url: '/partners/dell/index.html' }, { text: 'HP', url: '/partners/hp/index.html' }, { text: 'HPE', url: '/partners/hpe/index.html' }, { text: 'Lenovo', url: '/partners/lenovo/index.html' }, { text: 'Microsoft', url: '/about-us/microsoft-partnership.1.html' }, { text: 'Microsoft Surface', url: '/partners/microsoft-surface/index.html' } ], viewAll: { text: 'VIEW ALL PARTNERS', url: '/partners/index.html' } }, { label: 'ABOUT US', heading: 'About Us', items: [ { text: 'About KTechUk', url: '/about-us.1.html' }, { text: 'Case Studies', url: '/about-us/case-studies/index.html' }, { text: 'People, Culture & Careers', url: '/about-us/people-culture-and-careers/index.html' }, { text: 'Sustainability & Social Value', url: '/sustainability/index.html' }, { text: 'Frameworks', url: '/about-us/frameworks/index.html' }, { text: 'Leadership', url: '/about-us/leadership/index.html' }, { text: 'Financing Options', url: '/about-us/financing/index.html' } ], } ]; /* ── Inject CSS ─────────────────────────────────────────────────────── */ var style = document.createElement('style'); style.textContent = [ /* Overlay */ '.ktm-ov{', ' display:none;position:fixed;inset:0;background:rgba(0,0,0,.45);', ' z-index:99998;opacity:0;transition:opacity .3s;', '}', '.ktm-ov.ktm-ov--open{display:block;}', '.ktm-ov.ktm-ov--vis{opacity:1;}', /* Panel */ '.ktm-p{', ' position:fixed;top:0;right:-100%;width:100%;max-width:430px;height:100%;', ' background:#fff;z-index:99999;', ' display:flex;flex-direction:column;', ' transition:right .32s cubic-bezier(.4,0,.2,1);', ' overflow:hidden;', '}', '.ktm-p.ktm-p--open{right:0;}', /* Head */ '.ktm-hd{', ' display:flex;align-items:center;justify-content:space-between;', ' padding:0 20px;height:64px;', ' border-bottom:1px solid #e5e5e5;flex-shrink:0;', '}', '.ktm-hd__logo{display:flex;align-items:center;text-decoration:none;}', '.ktm-hd__logo img{height:34px;width:auto;display:block;}', '.ktm-hd__close{', ' background:none!important;background-color:transparent!important;', ' border:none!important;cursor:pointer;padding:6px;', ' color:#1a1a1a;line-height:1;font-size:22px;font-weight:300;', ' display:flex;align-items:center;justify-content:center;', ' border-radius:4px;transition:color .2s;box-shadow:none!important;', '}', '.ktm-hd__close:hover{color:#D76AB3;}', '.ktm-hd__close:focus-visible{outline:2px solid #D76AB3;outline-offset:2px;}', /* Scrollable nav area */ '.ktm-nav{flex:1;overflow-y:auto;-webkit-overflow-scrolling:touch;}', /* Accordion item row */ '.ktm-row{border-bottom:1px solid #e5e5e5!important;border-left:none!important;border-right:none!important;border-top:none!important;}', '.ktm-row__btn{', ' display:flex;align-items:center;justify-content:space-between;', ' width:100%;padding:0 20px;height:58px;', ' background:none!important;background-color:transparent!important;', ' background-image:none!important;', ' border:none!important;border-radius:0!important;', ' box-shadow:none!important;cursor:pointer;', ' font-family:inherit;font-size:13px;font-weight:700;', ' letter-spacing:.08em;color:#1a1a1a!important;text-transform:uppercase;', ' text-align:left;', '}', '.ktm-row__btn:focus-visible{outline:2px solid #D76AB3;outline-offset:-2px;}', '.ktm-row__chevron{', ' flex-shrink:0;width:18px;height:18px;', ' transition:transform .25s ease;', ' display:flex;align-items:center;justify-content:center;', '}', '.ktm-row__chevron svg{width:14px;height:14px;stroke:currentColor;border:none!important;outline:none!important;}', '.ktm-row.is-open .ktm-row__chevron{transform:rotate(180deg);}', /* Dropdown panel — gradient top line via ::before pseudo-element */ '.ktm-drop{overflow:hidden;max-height:0;transition:max-height .35s ease;border:none!important;position:relative;}', '.ktm-row.is-open .ktm-drop{max-height:2400px;}', '.ktm-row.is-open .ktm-drop::before{', ' content:"";display:block;height:2px;', ' background:linear-gradient(135deg,#D76AB3 0%,#C89AE8 40%,#7EC7E8 75%,#F5B38A 100%);', '}', /* Sub-section heading inside dropdown */ '.ktm-subsec__hd{', ' display:block;padding:20px 28px 8px;', ' font-size:17px;font-weight:500;color:#1a1a1a;', ' letter-spacing:0;border:none!important;', '}', /* Separator between sub-sections */ '.ktm-subsec-sep{height:1px;background:#e5e5e5;margin:6px 20px;border:none!important;}', /* Sub-links */ '.ktm-drop__list{list-style:none!important;margin:0!important;padding:0 0 2px!important;}', '.ktm-drop__list li{list-style:none!important;padding:0!important;margin:0!important;border:none!important;}', '.ktm-drop__list li a{', ' display:block;padding:9px 28px!important;', ' font-size:14px;color:#555;text-decoration:none;letter-spacing:0;border:none!important;', '}', '.ktm-drop__list li a:hover{color:#D76AB3;}', /* VIEW ALL link */ '.ktm-drop__all{', ' display:flex;align-items:center;gap:6px;', ' padding:10px 28px 18px!important;', ' font-size:13px;font-weight:600;color:#1a1a1a;text-decoration:none;', ' letter-spacing:.04em;border:none!important;outline:none!important;', '}', '.ktm-drop__all:hover{color:#D76AB3;}', '.ktm-drop__all svg{flex-shrink:0;width:14px;height:14px;stroke:#D76AB3;fill:none;border:none!important;outline:none!important;display:block;}', /* Contact Us row */ '.ktm-contact-row{border-bottom:1px solid #e5e5e5;}', '.ktm-contact-row a{', ' display:flex;align-items:center;', ' padding:15px 32px 15px 32px;height:58px;', ' font-size:13px;font-weight:700;letter-spacing:.08em;', ' color:#1a1a1a;text-decoration:none;text-transform:uppercase;', '}', '.ktm-contact-row a:hover{color:#D76AB3;}', /* CTA button */ '.ktm-cta{padding:20px;flex-shrink:0;background:#fff;}', '.ktm-cta a{', ' display:block;width:100%;padding:16px 24px;', ' background:#D76AB3;', ' border-radius:50px;text-align:center;', ' font-family:inherit;font-size:13px;font-weight:700;letter-spacing:.1em;', ' color:#fff;text-decoration:none;text-transform:uppercase;', ' transition:opacity .2s,box-shadow .2s;', '}', '.ktm-cta a:hover{opacity:.9;box-shadow:0 4px 20px rgba(215,106,179,.4);}', /* Body scroll lock */ 'body.ktm-locked{overflow:hidden;}' ].join('\n'); document.head.appendChild(style); /* ── Build HTML ─────────────────────────────────────────────────────── */ var chevronDown = ''; var arrowRight = ''; function buildSubsection(sub, isFirst) { var items = sub.items.map(function (it) { return '
' +
'' +
'' +
'