/*prevent FOUC on mobile when using sidebar */
.dwc-mobile :is(.bricks-is-frontend.brx-header-left, .bricks-is-frontend.brx-header-right) #brx-header {
  position: relative;
  inline-size: 100%;
  flex-direction: column;
}

.dwc-mobile .bricks-is-frontend:is(.brx-header-left, .brx-header-right) :is(#brx-content, #brx-footer) {
  margin-inline-start: 0;
}

/*prevent FOUC on desktop when using sidebar */
.bricks-is-frontend:is(.brx-header-left, .brx-header-right):not(.show-nav) .dwc-nav-wrapper {
  display: none;
}

/*=== sidebar css ===*/
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header {
  flex-direction: column;
  box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.7);  
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]).no-scroll {
  overflow: visible;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
  max-block-size: 100dvb;
  padding-block-end: 12rem;
  overscroll-behavior: contain;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nav-wrapper {
  position: relative;
  overflow: hidden;
  block-size: 100%;
  transform: translateX(0%);
  visibility: visible;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu .brxe-toggle {
  display: none !important;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu {
  display: flex;
  flex-direction: column;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-header > div {
  display: flex;
  grid-template-columns: 1fr;
  block-size: 100%;
  flex-direction: column;
  justify-content: flex-start;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-menu-wrap {
  inline-size: 100%;
  display: grid;
  grid-template-columns: 1fr;
  block-size: 100%;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header :is(.brxe-code, .dwc-nest-menu-overlay) {
  display: none;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-header {
  inline-size: 100%;
  padding-inline: 0;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu-top {
  min-block-size: var(--top-offset);
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu:not( [data-hide-close-bar = 'true']) .brx-dropdown-content {
  inset-block-start: calc(var(--top-offset) + 1px) !important;

}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu:not( [data-submenu-reveal = 'slide']) .brx-dropdown-content:not([data-submenu-reveal = 'slide'] *) {  
  inset-inline-start: 0;
  inset-block-start: unset !important;
  overflow: hidden;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu:not([data-submenu-reveal = 'slide']) .brxe-dropdown:not(.open.active) > .brx-dropdown-content > .brxe-dropdown:not([data-submenu-reveal = 'slide'] *){
  visibility: hidden;  
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested.brx-open .brxe-dropdown > .brx-dropdown-content {
  overscroll-behavior: contain;
  min-inline-size: var(--mobile-menu-width);

}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu .brxe-dropdown.open > .brx-submenu-toggle button:not([data-submenu-reveal = 'expand'] button)  {
  min-block-size: calc(var(--top-offset) - 1px);
  inset-block-start: 0;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu .brxe-dropdown.open[data-submenu-reveal = 'slide'] > .brx-submenu-toggle button  {
  min-block-size: calc(var(--top-offset) - 1px);
  inset-block-start: 0;
}

html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu .brxe-dropdown .brx-submenu-toggle button {
  min-block-size: 0;    
}

/* sidebar css ends*/



/*sidebar in builder*/
/*=== sidebar css ===*/
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header {
  flex-direction: column;
  box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.7);
  --top-offset: 40px;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
  max-block-size: 100dvb;
  padding-block-end: 12rem;
  overscroll-behavior: contain;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nav-wrapper {
  position: relative;
  overflow: hidden;
  block-size: 100%;
  transform: translateX(0%);
  visibility: visible;
}

body:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu .brxe-toggle {
  display: none !important;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu {
  display: flex;
  flex-direction: column;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-header > div {
  display: flex;
  grid-template-columns: 1fr;
  block-size: 100%;
  flex-direction: column;
  justify-content: flex-start;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-menu-wrap {
  inline-size: 100%;
  display: grid;
  grid-template-columns: 1fr;
  block-size: 100%;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-header {
  inline-size: 100%;
  padding-inline: 0;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu-top {
  min-block-size: var(--top-offset);
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brx-dropdown-content {
  inset-block-start: calc(var(--top-offset) - 1px) !important;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested.brx-open .brxe-dropdown > .brx-dropdown-content {
  overscroll-behavior: contain;  
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brxe-dropdown.open > .brx-submenu-toggle button {
  min-block-size: calc(var(--top-offset) - 1px);
  inset-block-start: 0;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brxe-dropdown .brx-submenu-toggle button {
  min-block-size: 0;    
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu {
  margin: 0 !important;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] .brx-nav-nested-items {  
  flex-direction: column !important;  
}
   
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-toggle--open.brxe-toggle {
  display: flex !important;
}
 
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu .brx-nav-nested-items {
  position: relative !Important;
  background: var(--mobile-menu-bg) !important;
  align-items: stretch;
  flex: 1;
}

:where(.brx-header-left, .brx-header-right)[data-builder-mode]:not(:has(.dwc-sidebar)) .dwc-nest-menu-top::before {
    content: 'this space is the back text bar';
    padding: 1rem;
    background-color: gray;
    color: white;
    width: 100%;
    text-transform: uppercase;
    font-size: 12px;
    font-weight: bold;
    letter-spacing: 1px;
}
 
/* MENU CTA (LAST BUTTON) */
:is(.brx-header-left, .brx-header-right)[data-builder-window] [data-last-item-is-button="true"].dwc-nest-menu .brx-nav-nested-items > .menu-item:last-of-type {
  padding-inline: var(--menu-item-inline-padding) !important;
  padding-block: var(--menu-item-block-padding) !important;
}
 
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-menu-top {
  min-block-size: 80px !important;
}
 
:is(.brx-header-left, .brx-header-right)[data-builder-window] .dwc-nest-nav-items {
  overflow-y: scroll;
}

:is(.brx-header-left, .brx-header-right)[data-builder-mode] .brx-dropdown-content {
  min-inline-size: var(--mobile-menu-width);
  position: static;
}

:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
  flex-wrap: nowrap;
}
:is(.brx-header-left, .brx-header-right)[data-builder-window] #brx-header .dwc-nest-menu .brx-dropdown-content {   
  visibility: visible !important;
  opacity: 1;
}

/*OVERLAY SIDEBAR*/


html:not(.dwc-mobile):has([data-overlay-sidebar=true])  {
 --mobile-menu-bg: rgb(255 255 255 / 0%);
  --menu-item-border: solid 1px rgb(255 255 255 / 50%);
 
}

html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) :is(main, footer){
  margin: 0 !important
}

html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) :is(main, footer) :where(section):not(section>section) {
  padding-inline-start: calc(var(--mobile-menu-width) + clamp(1.5rem, calc(0.625vw + 1.375rem), 1.875rem));
  max-inline-size: 100%
}

html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) #brx-header {
  border-radius: var(--overlay-sidebar-radius);
  overflow: hidden;
  background: var(--overlay-sidebar-bg);
  box-shadow: var(--overlay-sidebar-shadow) !important;
  inset: var(--overlay-sidebar-inset);
}

html:not(.dwc-mobile):has([data-overlay-sidebar=true]) :is(.brx-header-left, .brx-header-right):not([data-builder-modee]) .dwc-nest-header{
  backdrop-filter: blur(13px);
  background: transparent !important;
}

html:not(.dwc-mobile):has([data-overlay-sidebar=true]):not([data-builder-modee])  .brx-dropdown-content {
   background-color: rgb(255 255 255 / 100%);
}

/*NO BRX-OPEN STYLES*/
html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) #brx-header .dwc-nest-menu.brxe-nav-nested .brx-nav-nested-items {
  display: flex;
  flex-wrap: nowrap;
  flex-direction: column !important;
}


  html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu.brxe-nav-nested .brxe-dropdown .brx-dropdown-content {
      visibility: visible;
      min-inline-size: var(--mobile-menu-width) !important;
  }


  html:not(.dwc-mobile) :is(.brx-header-left, .brx-header-right):not([data-builder-window]) .dwc-nest-menu[data-submenu-reveal="expand"] .brxe-dropdown.open>.brx-dropdown-content {
   position: static;
}


class SidebarNavigation {
  constructor(options = {}) {
    // Basic configuration properties
    this.config = {
      minWidth: options.minWidth || MegaMenuCONFIG.minWidth, // Using external minWidth variable
      menuSelector: options.menuSelector || '.dwc-nest-menu',
      openClass: options.openClass || 'brx-open',
      activeClasses: options.activeClasses || ['open', 'active'],
      leftHeaderClass: options.leftHeaderClass || 'brx-header-left',
      rightHeaderClass: options.rightHeaderClass || 'brx-header-right',
      debounceDelay: options.debounceDelay || 100,
      menuItemClickDelay: options.menuItemClickDelay || 300
    };
    
    // Set dependent selectors
    const menuSelector = this.config.menuSelector;
    this.config.submenuToggleSelector = options.submenuToggleSelector || `${menuSelector} .brx-submenu-toggle`;
    this.config.dropdownSelector = options.dropdownSelector || `${menuSelector} .brxe-dropdown`;
    this.config.dropdownContentSelector = options.dropdownContentSelector || `${menuSelector} .brx-dropdown-content`;
    
    // State
    this.previousHeaderClass = null;
    this.dropdownClickHandlers = new Map();
    this.menuHoverHandlers = null;
    this.menuItemClickTimeout = null;
    this.keyboardNavHandler = null;
    this.cachedFocusableElements = null;
    this.cachedElements = {
      menuElement: null,
      navElement: null,
      dropdowns: null,
      dropdownToggles: null,
      menuItems: null
    };
    
    // Bind methods to this instance
    this.handleResize = this.debounce(this.handleMenu.bind(this), this.config.debounceDelay);
    this.handleOutsideClick = this.handleOutsideClick.bind(this);
  }
  
  // Initialize everything - called once
  init() {
    // Wait for DOM to be fully loaded before attaching events
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => {
        this.initAfterDOMLoaded();
      }, { once: true });
    } else {
      this.initAfterDOMLoaded();
    }
    
    return this;
  }
  
  // Separate initialization method to run after DOM is loaded
  initAfterDOMLoaded() {
    // Cache DOM elements once
    this.cacheElements();
    
    // Setup resize event with passive flag
    window.addEventListener('resize', this.handleResize, { passive: true });
    
    // Setup mutation observer for critical class changes only
    this.setupMutationObserver();
    
    // Initial setup based on current screen size
    this.handleMenu();
    
    // Cache focusable elements once if header class is present
    if (this.hasHeaderClass()) {
      this.cacheFocusableElements();
      this.setupMenuFocusNavigation();
    }
  }
  
  // Cache all required DOM elements upfront
  cacheElements() {
    this.cachedElements.menuElement = document.querySelector(this.config.menuSelector);
    
    if (this.cachedElements.menuElement) {
      this.cachedElements.navElement = this.cachedElements.menuElement.querySelector('.dwc-nest-nav-items');
      this.cachedElements.dropdowns = Array.from(document.querySelectorAll(this.config.dropdownSelector));
      this.cachedElements.dropdownToggles = Array.from(document.querySelectorAll(this.config.submenuToggleSelector));
      this.cachedElements.menuItems = Array.from(document.querySelectorAll(`${this.config.menuSelector} .menu-item`));
    }
  }
  
  // Set up a focused mutation observer only for dropdown state changes
  setupMutationObserver() {
    if (!this.cachedElements.dropdowns || this.cachedElements.dropdowns.length === 0) return;
    
    const callback = (mutations) => {
      for (let mutation of mutations) {
        if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
          const target = mutation.target;
          const prevClassList = mutation.oldValue ? mutation.oldValue.split(' ') : [];
          const hadBothBefore = prevClassList.includes('open') && prevClassList.includes('active');
          const hasBothNow = target.classList.contains('open') && target.classList.contains('active');
    
          if (hadBothBefore !== hasBothNow) {
            this.updateDropdownAccessibility();
            break; // Only need to update once per batch
          }
        }
      }
    };
    
    // Create observer with optimized options
    this.classObserver = new MutationObserver(callback);
    
    // Observe only the dropdown elements
    this.cachedElements.dropdowns.forEach(dropdown => {
      this.classObserver.observe(dropdown, { 
        attributes: true, 
        attributeFilter: ['class'], 
        attributeOldValue: true 
      });
    });
  }
  
  // Cache focusable elements for keyboard navigation
  cacheFocusableElements() {
    if (!this.cachedElements.navElement) return;
    
    // Get direct children of nav
    const directChildren = Array.from(this.cachedElements.navElement.children);
    
    // Find the first focusable element within each direct child
    this.cachedFocusableElements = directChildren.map(child => {
      // Check if the child itself is focusable
      if (child.matches('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])')) {
        return child;
      }
      // Otherwise, find the first focusable element within this child
      return child.querySelector('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
    }).filter(Boolean); // Remove null/undefined values
  }
  
  // Clean up all event listeners and observers
  destroy() {
    // Clean up the mutation observer
    if (this.classObserver) {
      this.classObserver.disconnect();
      this.classObserver = null;
    }
    
    // Clean up resize listener
    window.removeEventListener('resize', this.handleResize);
    
    // Clean up click handlers
    if (this.dropdownClickHandlers.size > 0) {
      this.dropdownClickHandlers.forEach((handler, toggle) => {
        toggle.removeEventListener('click', handler);
      });
      this.dropdownClickHandlers.clear();
    }
    
    // Clean up hover handlers
    this.cleanupMenuHover();
    
    // Clean up menu item click handlers
    this.cleanupMenuItemClicks();
    
    // Clean up outside click handler
    document.removeEventListener('click', this.handleOutsideClick);
    
    // Clean up keyboard navigation
    if (this.keyboardNavHandler) {
      document.removeEventListener('keydown', this.keyboardNavHandler);
      this.keyboardNavHandler = null;
    }
    
    // Clear any pending timeouts
    if (this.menuItemClickTimeout) {
      clearTimeout(this.menuItemClickTimeout);
      this.menuItemClickTimeout = null;
    }
  }
  
  // Utility methods
  hasHeaderClass() {
    return document.body.classList.contains(this.config.leftHeaderClass) || 
           document.body.classList.contains(this.config.rightHeaderClass);
  }
  
  debounce(func, delay) {
    let timer;
    return (...args) => {
      clearTimeout(timer);
      timer = setTimeout(() => func(...args), delay);
    };
  }

  // Check if an element has all the required active classes
  hasAllActiveClasses(element) {
    return this.config.activeClasses.every(className => element.classList.contains(className));
  }
  
  // Toggle all active classes on an element
  toggleActiveClasses(element) {
    this.config.activeClasses.forEach(className => {
      element.classList.toggle(className);
    });
  }
  
  // Core functionality methods
  handleMenu() {
    if (!this.cachedElements.menuElement) return;
    if (!this.hasHeaderClass() && !this.previousHeaderClass) return;

    const screenWidth = window.innerWidth;
    const isLargeScreen = screenWidth >= this.config.minWidth;
    const menuElement = this.cachedElements.menuElement;

    if (!isLargeScreen) {
      // Save which class was present before removal
      if (this.hasHeaderClass()) {
        this.previousHeaderClass = document.body.classList.contains(this.config.leftHeaderClass) 
          ? this.config.leftHeaderClass 
          : this.config.rightHeaderClass;
        
        // Remove header classes
        document.body.classList.remove(this.config.leftHeaderClass, this.config.rightHeaderClass);
        menuElement.classList.remove(this.config.openClass);
        
        // Reset accessibility attributes
        this.resetAccessibilityAttributes();
      }
      
      // Clean up event handlers for mobile view
      this.cleanupMenuHover();
      this.cleanupMenuItemClicks();
      this.cleanupDropdownHandlers();
      document.removeEventListener('click', this.handleOutsideClick);
      
      return;
    }

    // Large screen behavior
    if (!this.hasHeaderClass() && this.previousHeaderClass) {
      document.body.classList.add(this.previousHeaderClass);
    }

    if (this.hasHeaderClass()) {
      if (!menuElement.classList.contains(this.config.openClass)) {
        menuElement.classList.add(this.config.openClass);
      }
      
      // Setup elements for large screen view
      this.setupMenuHover();
      this.setupMenuItemClicks();
      this.setupDropdownHandlers();
      this.setupMenuFocusNavigation();
      this.updateDropdownAccessibility();
      
      // Ensure outside click handler is set up
      document.removeEventListener('click', this.handleOutsideClick);
      document.addEventListener('click', this.handleOutsideClick, { passive: false });
    }
  }
  
  // Reset accessibility attributes when switching to mobile
  resetAccessibilityAttributes() {
    if (!this.cachedElements.dropdowns) return;
    
    // Remove all inert attributes from dropdown contents
    this.cachedElements.dropdowns.forEach(dropdown => {
      const content = dropdown.querySelector(this.config.dropdownContentSelector);
      if (content) {
        content.removeAttribute('inert');
      }
      
      const button = dropdown.querySelector('button');
      if (button) {
        button.setAttribute('aria-expanded', 'false');
      }
    });
  }
  
  setupMenuFocusNavigation() {
    // Only run if hasHeaderClass() is true and we have focusable elements
    if (!this.hasHeaderClass() || !this.cachedFocusableElements || this.cachedFocusableElements.length === 0) {
      return;
    }
    
    // Clean up previous handler if it exists
    if (this.keyboardNavHandler) {
      document.removeEventListener('keydown', this.keyboardNavHandler, true);
      this.keyboardNavHandler = null;
    }
    
    const navMenu = this.cachedElements.menuElement;
    const focusableElements = this.cachedFocusableElements;
    const firstFocusableElement = focusableElements[0];
    const lastFocusableElement = focusableElements[focusableElements.length - 1];
    
    // Find adjacent focusable elements outside the menu (only once during setup)
    const headerElement = navMenu.closest('header') || document.querySelector('header');
    
    // Prepare variables to hold adjacent elements
    let prevFocusableElement = null;
    let nextFocusableElement = null;
    let firstElementAfterHeader = null;
    
    if (headerElement) {
      // Get all focusable elements within the header - do this once and cache the result
      const headerFocusables = Array.from(
        headerElement.querySelectorAll('a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])')
      ).filter(el => window.getComputedStyle(el).display !== 'none');
      
      // Find the index of our first and last menu elements in one pass
      const menuStartIndex = headerFocusables.indexOf(firstFocusableElement);
      const menuEndIndex = headerFocusables.indexOf(lastFocusableElement);
      
      // Cache the adjacent elements
      if (menuStartIndex > 0) {
        prevFocusableElement = headerFocusables[menuStartIndex - 1];
      }
      
      if (menuEndIndex !== -1 && menuEndIndex < headerFocusables.length - 1) {
        nextFocusableElement = headerFocusables[menuEndIndex + 1];
      }
      
      // Pre-calculate the first element after header - but only if needed
      if (!nextFocusableElement) {
        // Use a more efficient selector that targets immediate children of body that aren't the header
        const selector = 'body > *:not(header)';
        const nonHeaderElements = document.querySelectorAll(selector);
        
        // Only process if we have elements
        if (nonHeaderElements.length > 0) {
          // Create a function to find the first focusable element (used later if needed)
          this.findFirstFocusableAfterHeader = () => {
            for (const element of nonHeaderElements) {
              const focusable = element.querySelector('a:not([tabindex="-1"]), button:not([tabindex="-1"]), input:not([tabindex="-1"]), select:not([tabindex="-1"]), textarea:not([tabindex="-1"]), [tabindex]:not([tabindex="-1"])');
              if (focusable && window.getComputedStyle(focusable).display !== 'none') {
                return focusable;
              }
            }
            return null;
          };
        }
      }
    }
    
    // Create keyboard navigation handler with closure over the cached elements
    this.keyboardNavHandler = (e) => {
      // Quick check for Tab key first
      if (e.key !== 'Tab') return;
      
      // Then check if focus is inside the menu
      if (!navMenu.contains(document.activeElement)) return;
      
      let targetElement = null;
      
      // Handle tab navigation at boundaries only
      if (!e.shiftKey && document.activeElement === lastFocusableElement) {
        // Forward tab from last element
        e.preventDefault();
        e.stopPropagation();
        
        if (nextFocusableElement) {
          targetElement = nextFocusableElement;
        } else if (this.findFirstFocusableAfterHeader) {
          // Only search for elements after header if needed and not already found
          firstElementAfterHeader = this.findFirstFocusableAfterHeader();
          targetElement = firstElementAfterHeader;
        }
        
        // Focus on the target or body as fallback
        setTimeout(() => {
          if (targetElement) {
            targetElement.focus();
          } else {
            document.body.setAttribute('tabindex', '-1');
            document.body.focus();
            document.body.removeAttribute('tabindex');
          }
        }, 10);
      } 
      else if (e.shiftKey && document.activeElement === firstFocusableElement) {
        // Backward tab from first element
        e.preventDefault();
        e.stopPropagation();
        
        setTimeout(() => {
          if (prevFocusableElement) {
            prevFocusableElement.focus();
          } else {
            document.body.setAttribute('tabindex', '-1');
            document.body.focus();
            document.body.removeAttribute('tabindex');
          }
        }, 10);
      }
    };
    
    // Use capture phase for the event
    document.addEventListener('keydown', this.keyboardNavHandler, true);
  }
  
  setupMenuHover() {
    const menuElement = this.cachedElements.menuElement;
    if (!menuElement) return;
    
    // Clean up existing hover handlers first
    this.cleanupMenuHover();
    
    // Create event handlers
    const mouseenterHandler = () => {
      menuElement.classList.add(this.config.openClass);
    };
    
    const mouseleaveHandler = () => {
      menuElement.classList.remove(this.config.openClass);
    };
    
    // Add event listeners with passive flag for better performance
    menuElement.addEventListener('mouseenter', mouseenterHandler, { passive: true });
    menuElement.addEventListener('mouseleave', mouseleaveHandler, { passive: true });
    
    // Store the handlers for cleanup
    this.menuHoverHandlers = {
      element: menuElement,
      mouseenter: mouseenterHandler,
      mouseleave: mouseleaveHandler
    };
  }
  
  cleanupMenuHover() {
    if (this.menuHoverHandlers) {
      const { element, mouseenter, mouseleave } = this.menuHoverHandlers;
      element.removeEventListener('mouseenter', mouseenter);
      element.removeEventListener('mouseleave', mouseleave);
      this.menuHoverHandlers = null;
    }
  }
  
  setupMenuItemClicks() {
    if (!this.cachedElements.menuItems || this.cachedElements.menuItems.length === 0) return;
    
    // Clean up existing handlers first
    this.cleanupMenuItemClicks();
    
    const menuElement = this.cachedElements.menuElement;
    const menuItemHandlers = new Map();
    
    this.cachedElements.menuItems.forEach(item => {
      const clickHandler = () => {
        if (this.hasHeaderClass()) {
          // Clear any existing timeout
          if (this.menuItemClickTimeout) {
            clearTimeout(this.menuItemClickTimeout);
          }
          
          // Set timeout before adding the class
          this.menuItemClickTimeout = setTimeout(() => {
            if (!menuElement.classList.contains(this.config.openClass)) {
              menuElement.classList.add(this.config.openClass);
            }
          }, this.config.menuItemClickDelay);
        }
      };
      
      menuItemHandlers.set(item, clickHandler);
      item.addEventListener('click', clickHandler);
    });
    
    this.menuItemClickHandlers = menuItemHandlers;
  }
  
  cleanupMenuItemClicks() {
    if (this.menuItemClickHandlers && this.menuItemClickHandlers instanceof Map) {
      this.menuItemClickHandlers.forEach((handler, item) => {
        item.removeEventListener('click', handler);
      });
      this.menuItemClickHandlers.clear();
    }
    
    if (this.menuItemClickTimeout) {
      clearTimeout(this.menuItemClickTimeout);
      this.menuItemClickTimeout = null;
    }
  }
  
  setupDropdownHandlers() {
    if (!this.hasHeaderClass() || !this.cachedElements.dropdownToggles) return;
    
    // Clean up existing handlers first
    this.cleanupDropdownHandlers();
    
    this.cachedElements.dropdownToggles.forEach(toggle => {
      const clickHandler = (event) => {
        event.stopPropagation();
        event.preventDefault();
        
        const dropdown = toggle.closest(this.config.dropdownSelector);
        if (dropdown) {
          this.toggleActiveClasses(dropdown);
          this.updateDropdownAccessibility();
        }
      };
      
      this.dropdownClickHandlers.set(toggle, clickHandler);
      toggle.addEventListener('click', clickHandler);
    });
  }
  
  cleanupDropdownHandlers() {
    if (this.dropdownClickHandlers.size > 0) {
      this.dropdownClickHandlers.forEach((handler, toggle) => {
        toggle.removeEventListener('click', handler);
      });
      this.dropdownClickHandlers.clear();
    }
  }
  
  handleOutsideClick(event) {
    if (event.target.tagName === 'A') return;
    if (!event.target.closest(this.config.dropdownSelector)) return;
    if (!event.target.closest(this.config.submenuToggleSelector)) {
      event.preventDefault();
      event.stopPropagation();
    }
  }
  
  updateDropdownAccessibility() {
    // Only run if hasHeaderClass() is true
    if (!this.hasHeaderClass() || !this.cachedElements.dropdowns) return;
    
    this.cachedElements.dropdowns.forEach(dropdown => {
      const content = dropdown.querySelector(this.config.dropdownContentSelector);
      const button = dropdown.querySelector('button');
      
      // Check if dropdown has all active classes
      const isOpen = this.hasAllActiveClasses(dropdown);
      
      if (content) {
        if (isOpen) {
          content.removeAttribute('inert');
        } else {
          content.setAttribute('inert', '');
        }
      }
      
      if (button) {
        button.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
      }
    });
  }
}


const sidebarNav = new SidebarNavigation().init();

ASPHALT ROADWAY INSTALLATION

Asphalt Roadway Installation for Strata & Multi-Unit Developments in Metro Vancouver

VALROC delivers high-quality asphalt roadways designed for heavy traffic, smooth surfaces, and long-lasting durability.

Private roadway paving planned around traffic use, base support, drainage and access for the people who rely on the site.

Valroc crew performing full asphalt roadway demolition in a strata complex, removing failed pavement and preparing for new subbase and catch basin elevation adjustments.
BELOW THE SURFACE

Build for the route

Base design and asphalt depth respond to the road's actual vehicle use and ground conditions.

GRADES AND DRAINAGE

Plan the water path

Roadway elevations, catch basins and adjoining entries need to work together before paving.

SITE ACCESS

Coordinate each phase

A property contact can organize notices, service routes and practical access during the work.

FROM REMOVAL TO ROAD ACCESS

How an asphalt roadway is constructed

The five connected Process records cover roadway removal, site preparation and granular support, hot-mix placement, rolling and final finishing, then cleanup. Work phases and access routes depend on the site’s active users and written scope.

  1. Repairing sections of asphalt roadway

    Step 1

    Roadway Demolition

    Roadway demolition removes failed asphalt surfaces while protecting underground services, managing traffic, and preparing the site for grading and safe reconstruction.

    • Roadway demolition
    • Failed pavement
    • Removal boundary
  2. Asphalt road paving equipment in action

    Step 2

    Asphalt Roadway Site Prep

    Site preparation for roadways includes grading, subbase stabilization, and drainage control to support high-volume traffic and create a durable, long-lasting asphalt driving surface.

    • Road base
    • Aggregate compaction
    • Drainage grade
  3. Municipal road paving service

    Step 3

    Roadway Asphalt Placement

    Asphalt placement for roadways involves multi-lift paving, slope management, and compaction techniques to create a smooth, durable surface for safe daily traffic and long-term performance.

    • Hot-mix paving
    • Roadway depth
    • Asphalt placement
  4. Compaction of asphalt roadway with roller

    Step 4

    Final Finishing for Roadways

    Final finishing ensures roadways are compacted, smoothed, and slope-checked for optimal drainage, driving safety, and a seamless transition into public or private use.

    • Roller compaction
    • Lane transitions
    • Finished edges
  5. valroc finished product for asphalt roadway

    Step 5

    Roadway Cleanup

    Final cleanup removes construction debris, clears travel lanes, and prepares the asphalt roadway for line painting, inspections, and safe public or private use.

    • Site cleanup
    • Access handoff
    • Resident notice

Asphalt Paving, Roadways, Driveways, Repair...

This video showcases VALROC’s asphalt paving projects across Metro Vancouver, including driveways, strata roadways, and commercial parking lots. Watch our in-house crews and equipment deliver structured, durable asphalt installations built for long-term performance.

can you 

BUILT FOR DAILY SITE USE

Private asphalt roads for strata and multi-unit access

VALROC Development Ltd. provides professional-grade asphalt roadway installations designed to support high traffic volume, long-term durability, and smooth daily use. We serve strata complexes, multiplex developments, and private properties throughout Metro Vancouver with solutions that prioritize both structure and function. Our expertise in Asphalt Roadway Installation ensures that every project meets the highest standards of quality and performance. Our commitment to excellence in Asphalt Roadway Installation makes us the preferred choice in the region.

TRAFFIC LOADS

Design for real vehicles

Resident cars, waste collection, deliveries and maintenance vehicles shape the required road and base specification.

SURFACE WATER

Manage grades and inlets

The paved surface should connect to the planned catch-basin and runoff route, not create a new low point.

ACTIVE SITES

Keep access in the plan

Phasing, resident notices and temporary vehicle routes can be considered before construction begins.

ADJOINING SPACES

Coordinate the edges

Parking, sidewalks, curbs and entrances need their transitions assessed alongside the roadway profile.

SCOPE THE CONNECTED WORK

Roadway options for circulation and drainage

Roadway choices should support traffic loads, drainage and safe property access. Pavement depth, markings, catch basins, speed control features and edge treatments are coordinated with the site plan.

Asphalt roadway construction support

Included in our service:

  • • Roadway alignment and traffic use review

  • • Subgrade preparation and proof review

  • • Aggregate base placement and compaction

  • • Drainage grades and edge conditions

  • • Hot mix asphalt supply coordination

  • • Paving with organized equipment and crews

  • • Tie ins to existing pavement

  • • Access and traffic staging coordination

  • • Testing or inspection coordination when required

  • • Final surface review and documented handoff

Additional private-road choices

Private asphalt roadway markings and layout

Route planning · Clearer vehicle guidance
Asphalt roadway markings · Traffic layout · Lane guidance

Catch basin drainage for asphalt roadways

Drainage priority · Controlled surface runoff
Roadway catch basins · Asphalt drainage · Surface runoff

Asphalt speed bumps for private roadways

Speed management · Safer shared access
Private roadway speed bumps · Traffic calming · Shared access

Line markings guide movement; a catch basin manages a particular water path; a speed bump addresses a separate traffic-calming decision. Each option has different approval, construction and access implications and is not an automatic roadway inclusion.

Treat the road as a working network

View other available options

Curbs and supported road edges

Concrete curbing or retaining support can help define a road edge at a grade change. Its structure and connection to asphalt must be separately specified. | Additional cost

Private roadway widening

A wider roadway may improve a particular turning or passing area, but the added pavement and road base extend the contracted footprint. | Additional cost

Tapered tie-ins to existing pavement

A tapered asphalt transition can connect the reconstructed road to an existing lane or parking entrance at a changed elevation. The grade must respect access and water flow. | Additional cost

Catch-basin lid at finished road grade

An existing basin rim may need adjustment where a new road surface changes the surrounding elevation. The inlet and pavement perimeter are reviewed together and separately scoped if needed. | Additional cost

WHERE THE ROAD MEETS THE SITE

Coordinate roadway edges and adjoining areas

Integrated services including curbing, signage, drainage upgrades, and internal stall layout.

Parking-lot reconstruction, curbs, retaining structures, drainage systems and speed bumps are separate services unless the roadway proposal expressly includes them.

  • Excavation site preparation with John Deere excavator and laser rotary measurement tool in use

    Site preparation for asphalt roadway construction

    • Roadway site preparation
    • Subgrade preparation
    • Construction access

    Private roadway performance begins below the asphalt. Clearing, rough grading and access preparation create the conditions for a stable road base and an efficient paving sequence.

    Explore site preparation services
  • Valroc excavators clearing residential demolition debris in Metro Vancouver

    Excavation and grading for private roadways

    • Roadway excavation
    • Road grading
    • Material removal

    Roadway excavation shapes the corridor, removes unsuitable material and establishes the grades needed for base construction. Coordinating machines, trucking and paving helps control elevations and material movement.

    Explore excavation and machine services
  • Workers performing trench excavation with shovels on construction site

    Utility trenching before asphalt roadway paving

    • Roadway trenching
    • Underground services
    • Pavement planning

    Installing or repairing buried services before paving helps avoid cutting into a new asphalt roadway later. Trenching can be coordinated with road grades, backfill requirements and the construction schedule.

    Explore trenching services
  • Catch basin being installed on subbase ground while contractor holds level and excavator helps lift in the catch basin

    Catch basins for roadway surface drainage

    • Roadway catch basins
    • Surface runoff
    • Road drainage

    Catch basins collect runoff at roadway low points and help protect travel surfaces from persistent ponding. Their elevations and pipe connections should be established before final asphalt placement.

    Explore catch basin installation
  • asphalt patch and repair services.

    Asphalt patching for roadway repairs

    • Roadway asphalt repair
    • Utility cut patch
    • Pavement restoration

    Utility cuts, localized failures and damaged pavement do not always require full roadway replacement. Asphalt patch and repair work can restore targeted areas and create smoother transitions to the surrounding surface.

    Explore asphalt patch and repair

DOCUMENTED EXPERIENCE

Asphalt roadway experience for active properties

VALROC works on asphalt roadways serving strata, commercial and light industrial properties across Metro Vancouver. Our experience helps coordinate pavement structure, drainage, traffic movement, curbs and access while respecting how the property operates. Our roadway experience includes strata and commercial properties in Langley, Surrey, Burnaby, Richmond and Coquitlam, with practical knowledge of traffic planning, municipal requirements, inspections and communication with property managers.

DEFINE PHASES AND SCOPE

Request an onsite roadway estimate

Able to complete projects in multiple stages to reduce environmental disruption and maintain site accessibility.

  • Share the road and users

    Provide a site plan or photos, traffic concerns and a property contact.

  • Walk the existing conditions

    Review failed pavement, base, water movement, service vehicles and adjoining entrances.

  • Review the written phases

    Confirm reconstruction limits, access plan, asphalt specification and separately scoped options.

  • What changes a roadway paving estimate?

    Road area, removal and disposal, base reconstruction, asphalt depth, drainage and inlet elevations, utility interfaces, traffic phasing, equipment access and any separately chosen markings or curbing determine the scope. A site visit is needed before a project-specific price.

    What changes a roadway paving estimate?

    Road area, removal and disposal, base reconstruction, asphalt depth, drainage and inlet elevations, utility interfaces, traffic phasing, equipment access and any separately chosen markings or curbing determine the scope. A site visit is needed before a project-specific price.

    Request Your Free On-site Estimate

    FIRST, UNDERSTAND THE ROUTE

    Assess the private roadway before rebuilding it

    An onsite review should establish where the road has failed, who uses it, where water drains, which covers and entries set the grade, and how residents, deliveries and emergency access will be addressed during construction.

  • 01

    Review traffic and failure

    Identify pavement defects and the cars, trucks or service vehicles that use each lane.

  • 02

    Check base, grades and inlets

    Assess supporting ground, low spots, catch basins, utility covers and connected entries.

  • 03

    Plan practical phases

    Discuss resident and business access, temporary routes, parking changes and a site communication contact.

  • 04

    Confirm the written build

    Define removal limits, base and asphalt specification, transitions, drainage work and handoff.

  • The road beneath the surface

    Visible cracking or settlement does not by itself reveal the full condition of the road base. Drainage, traffic load and material depth need to be assessed onsite, and some conditions only become clear after the existing pavement is removed.

    The road beneath the surface

    Visible cracking or settlement does not by itself reveal the full condition of the road base. Drainage, traffic load and material depth need to be assessed onsite, and some conditions only become clear after the existing pavement is removed.

    PRIVATE-ROAD DECISIONS

    Asphalt roadway questions

    The connected questions address durability, weather, pricing, client types and contractor selection. Their answers should be checked against the actual road, traffic use and written design rather than treated as universal performance claims.

    What makes VALROC’s asphalt roadways more durable than others?

    Durability comes from how the roadway is built, not just the surface material.

    • Premium asphalt blends suited to British Columbia’s climate.
    • Properly prepared and compacted sub-base to prevent settlement.
    • Precision paving methods to achieve consistent thickness and finish.
    • Drainage solutions that prevent standing water and erosion.

    This combination helps reduce cracking, potholes, and premature surface failure over time.

    What makes VALROC’s asphalt roadways more durable than others?

    Durability comes from how the roadway is built, not just the surface material.

    • Premium asphalt blends suited to British Columbia’s climate.
    • Properly prepared and compacted sub-base to prevent settlement.
    • Precision paving methods to achieve consistent thickness and finish.
    • Drainage solutions that prevent standing water and erosion.

    This combination helps reduce cracking, potholes, and premature surface failure over time.

    How does VALROC’s asphalt withstand British Columbia’s harsh weather?

    British Columbia’s climate places heavy demands on paved surfaces.

    • Use of moisture-resistant asphalt mixes to limit water penetration.
    • Drainage systems designed to move water away from the roadway surface.
    • Cold-weather paving considerations that reduce freeze-thaw cracking.
    • Protective surface sealants that reduce wear from rain, UV exposure, and traffic.

    These measures help extend the lifespan of asphalt roadways in wet and variable conditions.

    How does VALROC’s asphalt withstand British Columbia’s harsh weather?

    British Columbia’s climate places heavy demands on paved surfaces.

    • Use of moisture-resistant asphalt mixes to limit water penetration.
    • Drainage systems designed to move water away from the roadway surface.
    • Cold-weather paving considerations that reduce freeze-thaw cracking.
    • Protective surface sealants that reduce wear from rain, UV exposure, and traffic.

    These measures help extend the lifespan of asphalt roadways in wet and variable conditions.

    How does VALROC Development Ltd. keep asphalt roadway projects competitive in price?

    We understand that cost matters, and pricing is managed carefully without sacrificing quality.

    • Sourcing high-quality materials at industry-best rates.
    • Using efficient paving techniques that reduce labour time and material waste.
    • Relying on an experienced crew to complete work on schedule.
    • Managing the full project scope from excavation through final compaction to avoid delays.

    This approach allows roadway projects to stay competitive while maintaining long-term performance.

    How does VALROC Development Ltd. keep asphalt roadway projects competitive in price?

    We understand that cost matters, and pricing is managed carefully without sacrificing quality.

    • Sourcing high-quality materials at industry-best rates.
    • Using efficient paving techniques that reduce labour time and material waste.
    • Relying on an experienced crew to complete work on schedule.
    • Managing the full project scope from excavation through final compaction to avoid delays.

    This approach allows roadway projects to stay competitive while maintaining long-term performance.

    What industries and clients does VALROC Development Ltd. work with?

    VALROC provides asphalt roadway services across a wide range of sectors.

    • Municipal road paving and repair projects.
    • Private developments and subdivisions.
    • Industrial and commercial roadway expansions.
    • Strata and residential community access roads.
    • Parking lots, pathways, and large-scale paving projects.

    Work is completed in coordination with developers, municipalities, property owners, and general contractors.

    What industries and clients does VALROC Development Ltd. work with?

    VALROC provides asphalt roadway services across a wide range of sectors.

    • Municipal road paving and repair projects.
    • Private developments and subdivisions.
    • Industrial and commercial roadway expansions.
    • Strata and residential community access roads.
    • Parking lots, pathways, and large-scale paving projects.

    Work is completed in coordination with developers, municipalities, property owners, and general contractors.

    Why should I choose VALROC Development Ltd. for my asphalt roadway project?

    Choosing VALROC means working with a contractor that manages roadway construction from excavation through final surfacing.

    • Extensive experience with commercial, private, and municipal roadway projects.
    • Full-service delivery including base preparation, paving, and finishing.
    • Proven results across Metro Vancouver and surrounding regions.
    • Project-specific solutions based on traffic load, site conditions, and use.

    Our roadways are built for durability, safety, and cost-effective long-term ownership.

    Why should I choose VALROC Development Ltd. for my asphalt roadway project?

    Choosing VALROC means working with a contractor that manages roadway construction from excavation through final surfacing.

    • Extensive experience with commercial, private, and municipal roadway projects.
    • Full-service delivery including base preparation, paving, and finishing.
    • Proven results across Metro Vancouver and surrounding regions.
    • Project-specific solutions based on traffic load, site conditions, and use.

    Our roadways are built for durability, safety, and cost-effective long-term ownership.

    METRO VANCOUVER PRIVATE ROADS

    Roadway work for strata and business properties

    VALROC coordinates asphalt roadway construction from its Langley yard, supporting private roads, strata access lanes and service routes. Subgrade preparation, aggregate base, compaction, drainage grades and asphalt placement are organized around expected traffic, construction access and connections to existing pavement.

    Roadway projects in Surrey and Delta may include longer residential, agricultural or light industrial approaches, while work in Burnaby and White Rock can require more detailed traffic staging and grade control. Inspection, testing and access requirements are confirmed for the site.