/*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();

PARKING LOT CONSTRUCTION

Expert Asphalt Parking Lot Installations Across Metro Vancouver

Asphalt parking lot installation begins with the property’s traffic, ground and water conditions. VALROC plans the contracted paving area for business, light-industrial and strata use, including practical access while the work is under way.

Review grades, base support and working-site access before the parking-lot paving scope is confirmed.

Hot asphalt patching in Vancouver parking lot - professional asphalt repair services.
BASE AND LOADS

Build beneath the surface

Lot base and asphalt specifications respond to ground conditions and the vehicles using the property.

WATER MOVEMENT

Plan grades and inlets

Surface elevations should work with the agreed drainage path and existing catch basins.

ACTIVE PROPERTY

Coordinate access

Work phases and site notices can be planned with the property contact before paving begins.

FROM EXISTING LOT TO NEW SURFACE

How an asphalt parking lot is built

The five connected Process records cover removal where existing pavement is being replaced, site and base preparation, hot-mix placement, finishing and compaction, then cleanup and access handoff. New construction does not automatically require demolition.

  1. Subbase installation for asphalt parking lot

    Step 1

    Parking Lot Demolition

    Parking lot demolition clears deteriorated surfaces while protecting curbs, sidewalks, and drainage systems, setting the stage for safe, efficient reconstruction across strata, retail, and commercial properties.

    • Lot demolition
    • Failed asphalt
    • Removal limits
  2. removing old asphalt parking lot

    Step 2

    Asphalt Parking Lot Site Prep

    Site preparation for asphalt parking lots includes grading, subbase installation, and drainage adjustments to support high-traffic use and prevent surface failures across commercial and strata properties.

    • Lot base
    • Aggregate compaction
    • Drainage grade
  3. Crack filling in asphalt parking lot

    Step 3

    Parking Lot Paving

    Asphalt placement for parking lots involves multi-layer paving, rolling, and seamless transitions to deliver a durable, professional surface designed to handle daily traffic, weather, and long-term wear.

    • Hot-mix paving
    • Asphalt depth
    • Lot placement
  4. Asphalt parking lot installation underway

    Step 4

    Parking Lot Finishing

    Final finishing ensures parking lots have smooth transitions, clear edges, and properly compacted surfaces that enhance durability, drainage, and professional appearance across all commercial property types.

    • Roller compaction
    • Aisle transitions
    • Finished edges
  5. Commercial parking lot asphalt paving

    Step 5

    Parking Lot Cleanup

    Final cleanup clears debris, inspects surface drainage, and prepares the lot for reopening, delivering a polished, safe, and professional space ready for tenants, customers, or public access.

    • Site cleanup
    • Access handoff
    • Tenant 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 THE PROPERTY

Asphalt parking lots for daily circulation

Commercial and strata parking areas need more than a smooth surface. Vehicle loads, loading routes, parking access, base conditions and surface water all affect the installation plan.

VEHICLE USE

Plan for the traffic

Customer cars, delivery vehicles and waste-collection routes may impose different loads and turning demands.

GRANULAR BASE

Support the pavement

Excavation, base depth and compaction respond to the existing ground and specified lot use.

DRAINAGE

Give water a path

Paving grades and existing inlets should be checked before the final asphalt elevation is set.

WORKING SITE

Coordinate access

Property contacts can plan notices, pedestrian routes and phased vehicle access during construction.

CHOICES WITH A SITE PURPOSE

Parking lot options for access, water and traffic

Parking lot choices should support vehicle circulation, drainage and building access. Pavement depth, markings, catch basins, curbs and traffic control features are coordinated with the approved layout.

Asphalt parking lot installation support

Included in our service:

  • • Parking layout and traffic flow review

  • • Existing surface removal when required

  • • Subgrade and aggregate base preparation

  • • Drainage grades and catch basin interfaces

  • • Hot mix asphalt from established suppliers

  • • Paving with organized equipment and crews

  • • Building entrance and sidewalk transitions

  • • Business and strata access coordination

  • • Line marking readiness and final walkthrough

  • • Written scope, schedule and project handoff

Additional parking-lot choices

Asphalt parking lot stall markings and circulation

Essential layout · Clearer traffic flow
Parking stall lines · Traffic circulation · Fire lanes

Catch basin drainage for asphalt parking lots

Water management · Faster surface drainage
Parking lot catch basins · Lot drainage · Surface runoff

Asphalt speed bumps for parking lot traffic

Traffic control · Calmer vehicle speeds
Parking lot speed bumps · Traffic calming · Private parking

Stall markings organize movement; a catch basin serves a particular water path; a speed bump addresses a separate traffic concern. Each changes the project in a different way and should be chosen for an identified property need, not assumed as part of routine paving.

One coordinated parking-area plan

View other available options

Formed parking-lot edge details

A formed edge can help define a curve or pavement boundary where a standard tie-in is unsuitable. Its profile should follow the agreed lot layout and adjacent ground. | Additional cost

Curbs and protected lot edges

Concrete curbs or retaining support can define an edge, protect an asset or respond to a grade change. Structural details and the asphalt interface are separately specified. | Additional cost

Additional parking-lot area

Widening or extending the lot can change stall count or turning space, but it also expands excavation, base and paving quantities. Suitability depends on the property layout. | Additional cost

Catch-basin lid at finished grade

An existing basin lid may need a rim adjustment where the new asphalt elevation changes around it. The inlet and surrounding pavement should be evaluated together. | Additional cost

WHERE THE LOT MEETS THE SITE

Coordinate parking-lot edges and adjacent work

The paved lot connects to building entrances, loading zones, sidewalks, waste collection, curbs and drainage inlets. These interfaces should be reviewed with the lot grade and circulation plan before construction.

New drainage systems, curbs, retaining walls, bollards, speed bumps and expanded paving areas are not automatic parking-lot inclusions. Confirm each separately in the proposal.

  • 1. Service trucks for construction and development projects in Metro Vancouver.

    Asphalt speed bumps for calmer parking lots

    • Parking lot speed control
    • Traffic calming
    • Pedestrian safety

    Speed bumps can help reduce vehicle speeds near storefronts, walkways and shared parking areas. Coordinating them with the parking lot layout supports safer circulation and clearer driver expectations.

    Explore asphalt speed bump installation
  • Catch basin being installed on subbase ground while contractor holds level and excavator helps lift in the catch basin

    Catch basins for asphalt parking lot drainage

    • Parking lot catch basins
    • Surface drainage
    • Stormwater collection

    Large paved areas collect significant runoff during Metro Vancouver rain. Catch basins placed at planned low points help move surface water away from parking stalls, entrances and pedestrian routes.

    Explore catch basin installation
  • Aaron team member installing a trench drain in front of a garage diverting water that has been damaging the concrete and undermining the foundation

    Trench drains across parking lot entrances

    • Parking lot trench drain
    • Entrance drainage
    • Pavement grades

    A trench drain can intercept runoff across loading areas, gates or building entrances where water moves over a wide paved surface. Planning the drain with asphalt grades creates a cleaner and more effective transition.

    Explore trench drain installation
  • Sidewalk leading up to the front door through engineered concrete walls. The sidewalk still have the wooden forms as they are freshly poured with concrete.

    Concrete sidewalks beside asphalt parking areas

    • Parking lot sidewalks
    • Pedestrian access
    • Asphalt concrete transition

    Sidewalks define safer pedestrian routes between parking stalls and building entrances. Coordinating curb edges, elevations and drainage helps the concrete walkway meet the asphalt surface cleanly.

    Explore concrete sidewalk installation
  • Exterior facade renovation with scaffolding on downtown commercial building

    Parking lot improvements for tenant spaces

    • Tenant parking
    • Commercial access
    • Customer circulation

    A tenant improvement can extend beyond the interior when customer access, delivery routes or exterior circulation need attention. Parking layout, paving and pedestrian connections can strengthen the usability of the finished business space.

    Explore commercial tenant improvements

DOCUMENTED EXPERIENCE

Parking lot experience for Metro Vancouver properties

VALROC builds and renews asphalt parking areas for strata, business and commercial properties across Metro Vancouver. Our experience helps coordinate grading, drainage, traffic flow, entrances, curbs, sidewalks and active site access. VALROC supports parking areas in Langley City, Surrey City Centre, Newton, Guildford, Burnaby, Richmond and other busy Metro Vancouver business districts, coordinating access, strata needs and municipal requirements around active properties.

DEFINE THE LOT SCOPE

Request an onsite parking-lot estimate

A site visit helps establish the paving area, existing conditions, vehicle use, water path and access constraints before a project-specific proposal is prepared. The written scope should distinguish ordinary installation from any chosen drainage, marking or protective features.

  • Share the property

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

  • Walk the existing lot

    Review base, grades, water, utilities, loading, pedestrians and work-zone access.

  • Confirm the proposal

    Define paving quantities, phases, asphalt specification and separately scoped additions.

  • What affects a parking-lot paving estimate?

    Lot area, demolition and disposal where needed, base reconstruction, asphalt depth, drainage and inlet elevations, utility interfaces, access phasing and any separately chosen markings or curbs affect the work. A site visit is needed before a property-specific price.

    What affects a parking-lot paving estimate?

    Lot area, demolition and disposal where needed, base reconstruction, asphalt depth, drainage and inlet elevations, utility interfaces, access phasing and any separately chosen markings or curbs affect the work. A site visit is needed before a property-specific price.

    Request Your Free On-site Estimate

    FIRST, UNDERSTAND THE LOT

    Assess the site before paving the parking area

    An onsite assessment should establish the contracted paving footprint, traffic and loading patterns, existing pavement and base, utility interfaces, catch basins, surface-water movement and access for people still using the property.

  • 01

    Map daily use

    Review stalls, drive aisles, delivery routes, pedestrians and the property's active hours.

  • 02

    Check ground and water

    Assess existing pavement, supporting base, low points, inlets and utilities.

  • 03

    Plan safe work phases

    Discuss temporary routes, notices and a site contact for tenant or business coordination.

  • 04

    Define the written build

    Confirm the paving area, base and asphalt specification, transitions and any separately chosen options.

  • What lies beneath the lot

    Surface cracking or ponding does not by itself reveal the entire condition of the supporting ground. Base, drainage and utility conditions need to be assessed onsite, and some findings may only become clear after existing pavement is removed.

    What lies beneath the lot

    Surface cracking or ponding does not by itself reveal the entire condition of the supporting ground. Base, drainage and utility conditions need to be assessed onsite, and some findings may only become clear after existing pavement is removed.

    SITE-SPECIFIC ANSWERS

    Asphalt parking-lot questions

    The connected questions cover contractor choice, active-site safety, service life, construction duration and upkeep. Their answers must be checked against actual lot conditions and the written installation scope rather than used as universal promises.

    What makes VALROC Development Ltd. the best choice for asphalt parking lot paving?

    VALROC Development Ltd. builds asphalt parking lots designed for long-term performance, safety, and appearance across commercial and industrial properties.

    • Engineered pavement structures built for high-traffic and heavy-use conditions.
    • Proper grading and drainage to reduce standing water and surface deterioration.
    • Premium asphalt mixes selected for strength, flexibility, and longevity.
    • Accurate line striping and accessibility markings that meet current codes.

    Each parking lot is planned and built to function reliably while presenting a clean, professional first impression.

    What makes VALROC Development Ltd. the best choice for asphalt parking lot paving?

    VALROC Development Ltd. builds asphalt parking lots designed for long-term performance, safety, and appearance across commercial and industrial properties.

    • Engineered pavement structures built for high-traffic and heavy-use conditions.
    • Proper grading and drainage to reduce standing water and surface deterioration.
    • Premium asphalt mixes selected for strength, flexibility, and longevity.
    • Accurate line striping and accessibility markings that meet current codes.

    Each parking lot is planned and built to function reliably while presenting a clean, professional first impression.

    How does VALROC ensure safety while working in active public spaces?

    Safety is critical when paving in active commercial and public environments.

    • Clear traffic control plans for vehicles and pedestrians.
    • Strict compliance with WorkSafeBC regulations and safety standards.
    • Carefully planned scheduling to reduce disruption to businesses and users.
    • Experienced crews operating modern, well-maintained equipment.

    This approach allows work to proceed efficiently without compromising public or worker safety.

    How does VALROC ensure safety while working in active public spaces?

    Safety is critical when paving in active commercial and public environments.

    • Clear traffic control plans for vehicles and pedestrians.
    • Strict compliance with WorkSafeBC regulations and safety standards.
    • Carefully planned scheduling to reduce disruption to businesses and users.
    • Experienced crews operating modern, well-maintained equipment.

    This approach allows work to proceed efficiently without compromising public or worker safety.

    What factors impact the lifespan of an asphalt parking lot in British Columbia?

    Asphalt lifespan in British Columbia is influenced by both construction quality and environmental conditions.

    • Proper subgrade preparation and base compaction.
    • Exposure to rainfall and freeze-thaw cycles.
    • Effective drainage that prevents water pooling.
    • Ongoing maintenance such as crack sealing and sealcoating.

    When these factors are addressed correctly, parking lots can perform reliably for decades.

    What factors impact the lifespan of an asphalt parking lot in British Columbia?

    Asphalt lifespan in British Columbia is influenced by both construction quality and environmental conditions.

    • Proper subgrade preparation and base compaction.
    • Exposure to rainfall and freeze-thaw cycles.
    • Effective drainage that prevents water pooling.
    • Ongoing maintenance such as crack sealing and sealcoating.

    When these factors are addressed correctly, parking lots can perform reliably for decades.

    How long does it take to pave a new asphalt parking lot?

    Project timelines vary by size, access, and site conditions, but most parking lots follow a predictable sequence.

    • Site preparation and excavation typically take 1 to 3 days.
    • Asphalt paving and compaction generally require 1 to 2 days.
    • Line striping and final details are completed in about 1 day.

    For larger properties, phased construction can keep portions of the lot operational during work.

    How long does it take to pave a new asphalt parking lot?

    Project timelines vary by size, access, and site conditions, but most parking lots follow a predictable sequence.

    • Site preparation and excavation typically take 1 to 3 days.
    • Asphalt paving and compaction generally require 1 to 2 days.
    • Line striping and final details are completed in about 1 day.

    For larger properties, phased construction can keep portions of the lot operational during work.

    How does VALROC Development Ltd. help businesses maintain their parking lots?

    Ongoing maintenance is essential to protect asphalt and extend its service life.

    • Crack sealing and pothole repairs to prevent water intrusion.
    • Sealcoating to protect surfaces from UV exposure and moisture.
    • Line striping and accessibility markings to maintain compliance.
    • Drainage inspections to ensure proper runoff and surface protection.

    Our maintenance services help businesses protect their investment and avoid premature replacement.

    How does VALROC Development Ltd. help businesses maintain their parking lots?

    Ongoing maintenance is essential to protect asphalt and extend its service life.

    • Crack sealing and pothole repairs to prevent water intrusion.
    • Sealcoating to protect surfaces from UV exposure and moisture.
    • Line striping and accessibility markings to maintain compliance.
    • Drainage inspections to ensure proper runoff and surface protection.

    Our maintenance services help businesses protect their investment and avoid premature replacement.

    METRO VANCOUVER LOTS

    Parking-lot work for commercial and strata sites

    From Langley, VALROC builds asphalt parking lots for strata, business, commercial and light industrial properties. Parking layout, subgrade, aggregate base, drainage, catch basin interfaces and asphalt placement are coordinated with building entrances, loading areas and the need to maintain practical property access.

    Lots in Surrey may serve busy commercial and mixed use properties, Burnaby projects often require constrained traffic staging, and Delta sites can involve larger service or industrial areas. Work in White Rock may place added focus on grade changes and pedestrian connections. Each plan responds to actual circulation and drainage.