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

ENGINEERED RETAINING WALLS

Engineered and Concrete Retaining Walls in Metro Vancouver

Engineered retaining wall systems built to stabilize slopes, manage drainage, and support long-term structural loads.

Wall size, reinforcement and drainage are defined for the actual site and approved design.

A freshly stripped concrete retaining wall at a luxury residential build in Brookswood, Langley, BC, showing the VALROC excavator and crew preparing the drainage trench behind the wall.
GRADE SUPPORT

Hold the planned grade

Engineered retaining wall systems built to stabilize slopes, manage drainage, and support long-term structural loads.

SITE-SPECIFIC DESIGN

Account for soil and water

Site-specific construction that accounts for soil conditions, water management, and municipal or strata requirements.

USABLE SPACE

Make a slope work harder

Durable, reinforced concrete walls that increase usable space, safety, and overall property value.

FROM EXCAVATION TO HANDOFF

How an engineered concrete retaining wall takes shape

Retaining-wall construction follows the approved design and site conditions: excavation, footings, reinforcement, concrete placement, drainage, backfill and final review. The sequence can change with access, weather, inspections and wall geometry.

  1. Soil evaluation during foundation dig

    Step 1

    Retaining Wall Site Prep

    Excavation and prep for retaining walls ensures structural integrity by creating stable footing zones, engineered support, and long-term water drainage protection.

    • Wall excavation
    • Soil conditions
    • Site preparation
  2. Beginning stages of installing plywood, verticals for a large concrete retaining wall

    Step 2

    Retaining Wall Formwork

    Footings and formwork for retaining walls ensure stability, load distribution, and compliance with engineer specs, built to resist soil pressure, water buildup, and time.

    • Wall footings
    • Engineered formwork
    • Footing elevations
  3. Finishing off the top exposed part of an engineered wall as another team member starts to do cleanup

    Step 3

    Retaining Wall Placement

    Concrete placement for retaining walls follows strict engineering specs, with proper reinforcement, lift control, and finish prep for long-term strength and aesthetics.

    • Wall reinforcement
    • Concrete placement
    • Wall consolidation
  4. Stripping wood forms from small retaining wall

    Step 4

    Form Stripping - Retaining Wall

    Form stripping reveals the finished wall face, clears debris, and prepares the surface for optional treatments like textured coatings, inlays, or architectural finishing.

    • Form stripping
    • Wall inspection
    • Concrete surface
  5. Crew installing drainage pipes beside concrete retaining wall

    Step 5

    Retaining Wall Drainage

    Drainage and backfill protect retaining walls by relieving water pressure and stabilizing surrounding soil with clean gravel, filter fabric, and weeping tile systems.

    • Wall drainage
    • Structural backfill
    • Water pressure
  6. Ongoing construction of engineered wall shown during finishing & curing stage

    Step 6

    Retaining Wall Finishing

    Custom wall finishes include colour, texture, and material inlays that enhance curb appeal, match surrounding surfaces, and integrate design into structural performance.

    • Wall finishes
    • Architectural concrete
    • Surface treatments
  7. VALROC team stripping wood supports for a residential retaining wall that separates two properties and provides structural reinforcement for a concrete driveway that will be installed.

    Step 7

    Retaining Wall Completion

    Final cleanup includes removal of debris, safety checks, and a client walkthrough to review finishes, drainage, and future care instructions for the retaining wall.

    • Site cleanup
    • Drainage handoff
    • Wall care

Tony’s Homeowner Testimonial

Homeowner testimonial from Brookswood, Langley featuring an asphalt driveway, drainage improvements, retaining wall, and complete property transformation.

can you 

BUILD FOR THE GROUND AND THE USE

A retaining wall should solve the right site problem

Retaining walls transform steep or uneven ground into stable, level terraces that can be used for patios, gardens, walkways, parking areas, or expanded outdoor living spaces. By holding back soil and reshaping the landscape, these structures make areas of the property that were previously difficult to use both accessible and functional.

GRADE CONTROL

Retain soil on a slope

A wall can hold a designed grade where erosion, steep terrain or adjacent improvements make an unsupported edge unsuitable. Soil, groundwater and loading determine the engineered solution.

USABLE LAND

Create level outdoor space

Terraced yard areas can make room for a patio, garden, path or parking area. The proposed wall height and footprint must fit the site, drainage and approvals.

ASSET PROTECTION

Support nearby improvements

Driveways, parking pads, garages and building work near a grade change may need designed support. A retaining wall is sized to the loads and distances in the approved plan.

PRIVACY AND EDGES

Define a property boundary

Where allowed, a designed wall can establish a more private edge or transition between levels. Height, setbacks and any guard or fence need separate review.

DESIGN-LED DETAILS

Choose retaining-wall details for the design

Retaining wall choices respond to wall height, retained soil, groundwater and nearby loads. Footing geometry, reinforcement, drainage, colour and surface finish are coordinated with the engineered design.

Engineered retaining wall construction support

Included in our service:

  • • Slope, soil and wall location review

  • • Engineer and permit coordination when required

  • • Excavation and structural base preparation

  • • Reinforcement and formwork installation

  • • Drainage pipe, rock and outlet coordination

  • • Trackable concrete from established suppliers

  • • Concrete placement and professional finishing

  • • Stair, driveway and landscape interfaces

  • • Inspection scheduling and progress walkthroughs

  • • Final grading and documented handoff

Wall finishes and design-specific choices

Integral colour for engineered concrete retaining walls

Design choice · Coordinated wall appearance
Retaining wall colour · Coloured concrete · Architectural finish

Smooth formed finish for concrete retaining walls

Architectural finish · Clean modern surface
Smooth wall finish · Formed concrete · Surface appearance

Custom formwork for engineered retaining walls

Site specific solution · Fits complex grades
Retaining wall formwork · Wall geometry · Grade changes

Wall height is not selected from a generic size chart. A short landscape terrace and a taller wall carrying driveway or building loads require different engineering, reinforcement, footings, drainage and potentially permits. Confirm the drawings and written scope before construction.

Size the wall for its job

View other available options

Specified concrete mix additives

A specified concrete admixture may respond to placement conditions or performance requirements in the approved mix design. It should not be assumed in every retaining-wall pour. | Additional cost

Retaining-wall staining

Concrete staining can change the appearance of an exposed retaining-wall face after the wall is built and prepared. The colour treatment is optional and does not replace structural design. | Additional cost

Exposed-wall sealing

A compatible sealer may be considered for an exposed concrete wall face where the finish and exposure warrant it. It is a separate maintenance choice, not a substitute for drainage behind the wall. | Additional cost

BEYOND THE WALL FACE

Coordinate the wall with what it protects

Retaining walls are often used to support areas of a property that must carry significant weight, such as driveways, parking pads, garages, or building expansions. By holding back surrounding soil and stabilizing the grade, the wall creates a solid structural edge that allows these surfaces to remain level, secure, and properly supported over time.

When retaining walls, driveways, stairs, walkways, garages, and drainage are planned together, the finished property can be safer, easier to use, and better protected from water and grade changes.

  • Integrate steps and stairs with retaining wall elevation changes

    • retaining wall stairs
    • elevation access
    • integrated lighting

    Concrete steps and stairs create safe access where a retaining wall separates two elevations. Coordinating their foundations, landings, drainage, lighting, and wall geometry helps the entire transition perform as one system.

    Explore concrete steps and stairs
  • Support asphalt driveways beside sloped ground

    • asphalt driveway support
    • stable pavement edge
    • slope retention

    A retaining wall can stabilize sloped ground beside or below an asphalt driveway, helping create a level driving surface and a supported pavement edge. Planning the wall, base preparation, drainage, and paving together helps the driveway perform well over time.

    Explore asphalt driveways
  • Coordinate concrete driveways with retaining wall support

    • concrete driveway support
    • grade stability
    • drainage planning

    Concrete driveways often rely on retaining walls where the property grade changes beside a parking or driving surface. Planning wall elevations, driveway base, drainage, and concrete edges together helps protect long term stability and usability.

    Explore concrete driveways
  • Create level, protected space around detached garages

    • garage slope support
    • foundation protection
    • usable space

    When a garage is cut into a slope, a retaining wall can hold soil and moisture away from the structure while creating level, usable space around it. Wall placement should be coordinated with excavation, foundations, access, and drainage.

    Explore detached garages
  • 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.

    Create usable sidewalk access across changing grades

    • level walkway access
    • grade transition
    • foundation drainage

    Retaining walls and concrete sidewalks can work together to create safe, level access across changing grades. Coordinating the path, wall edge, drainage, and nearby foundations helps move water appropriately and makes difficult areas more usable.

    Explore concrete sidewalks
  • Use retaining walls to solve renovation grade and moisture problems

    • moisture management
    • foundation protection
    • usable exterior space

    During a renovation, cutting back the surrounding grade and adding a retaining wall can move wet soil away from footings and foundations, improve access, and create usable exterior space. The solution can be coordinated with drainage, walkways, fencing, and other renovation work.

    Explore home renovations

DOCUMENTED EXPERIENCE

Engineered retaining wall experience across Metro Vancouver

VALROC builds engineered concrete retaining walls where slopes, structures and usable property space require dependable support. Our experience helps coordinate excavation, drainage, wall construction and connections to driveways, garages, stairs and landscaping. Our retaining wall experience extends through steep and established properties in Langley, South Surrey, White Rock, Burnaby, Coquitlam and New Westminster, with regular coordination involving local engineers, architects, municipalities and strata representatives.

DEFINED WALL SCOPE

Get a retaining-wall estimate built around the site

Bring photos, plans and the purpose of the proposed wall to an onsite discussion. VALROC can review the grade, access, drainage and nearby assets before preparing a scope for the appropriate design and approvals.

  • Explain the wall's purpose

    Describe whether the priority is holding ground, creating usable space, protecting a driveway or structure, or defining an edge.

  • Review the property

    Measure grade changes and access; review water, soil, utilities, adjacent loads and existing walls.

  • Receive a defined proposal

    Confirm engineering and approvals, included wall work, drainage, finish choices and any separate connected scopes.

  • What affects retaining-wall cost?

    Height, length, retained ground, structural loads, soil and groundwater conditions, excavation access, engineered footing and reinforcement details, drainage, finish choices and approval requirements all affect scope and price. An onsite review and design are needed for a project-specific estimate.

    What affects retaining-wall cost?

    Height, length, retained ground, structural loads, soil and groundwater conditions, excavation access, engineered footing and reinforcement details, drainage, finish choices and approval requirements all affect scope and price. An onsite review and design are needed for a project-specific estimate.

    Request Your Free On-site Estimate

    BEFORE THE WALL IS DESIGNED

    Assess the grade, ground and loads

    An onsite review establishes what the retaining wall must support and where water can go. Wall height, slope, soil conditions, nearby structures, utilities, access and municipal requirements must be checked before a construction scope is fixed.

  • 01

    Identify the purpose and proposed height

    Is the wall retaining a garden grade, creating a terrace, defining privacy or supporting a driveway or structure? Measure the grade change and available footprint.

  • 02

    Check ground, water and loads

    Review soil, groundwater, surface runoff, utilities and loads near the wall. Flag drainage and property-boundary interfaces.

  • 03

    Confirm design and approvals

    Use the applicable drawings and engineer's requirements; verify permits, setbacks, inspections and guard requirements with the relevant authority.

  • 04

    Define the written scope

    Separate structural wall work, optional finishes and connected driveway, drainage or landscape work before pricing.

  • What determines wall size?

    Height alone does not determine a safe wall. Retained soil, slope, water, surcharge loads, footing conditions and neighbouring improvements affect the design. No universal height, footing or reinforcement specification is implied here.

    What determines wall size?

    Height alone does not determine a safe wall. Retained soil, slope, water, surcharge loads, footing conditions and neighbouring improvements affect the design. No universal height, footing or reinforcement specification is implied here.

    PRACTICAL WALL ANSWERS

    Questions about concrete retaining walls

    These answers help frame wall types, cracking, timing and maintenance. Dimensions, drainage, structural loads and inspections are site-specific; the approved design and written scope control the actual work.

    What types of concrete walls and slabs does VALROC Development Ltd. offer?

    Concrete walls and slabs are built differently depending on their structural role, load requirements, and exposure conditions.

    • Structural walls and slabs support buildings, garages, and retaining systems.
    • Decorative walls and slabs focus on finish, texture, and appearance.
    • Commercial and industrial slabs are designed for heavier loads and repeated use.

    VALROC designs and builds each wall or slab based on how it will function, not as a one-size-fits-all solution.

    What types of concrete walls and slabs does VALROC Development Ltd. offer?

    Concrete walls and slabs are built differently depending on their structural role, load requirements, and exposure conditions.

    • Structural walls and slabs support buildings, garages, and retaining systems.
    • Decorative walls and slabs focus on finish, texture, and appearance.
    • Commercial and industrial slabs are designed for heavier loads and repeated use.

    VALROC designs and builds each wall or slab based on how it will function, not as a one-size-fits-all solution.

    How does VALROC ensure concrete walls and slabs remain crack-resistant?

    Crack resistance starts with proper design, preparation, and concrete placement.

    • Stable subbase preparation helps prevent movement beneath the slab.
    • Reinforcement controls stress from loads and temperature changes.
    • Control joints guide where natural cracking occurs.

    VALROC focuses on structure, sequencing, and curing so cracks are controlled rather than unpredictable.

    How does VALROC ensure concrete walls and slabs remain crack-resistant?

    Crack resistance starts with proper design, preparation, and concrete placement.

    • Stable subbase preparation helps prevent movement beneath the slab.
    • Reinforcement controls stress from loads and temperature changes.
    • Control joints guide where natural cracking occurs.

    VALROC focuses on structure, sequencing, and curing so cracks are controlled rather than unpredictable.

    How long does it take to install a concrete wall or slab?

    Installation time depends on project size, site access, and curing requirements.

    • Preparation includes excavation, forming, and reinforcement placement.
    • Concrete placement and finishing are typically completed in one to two days.
    • Curing time is required before the concrete reaches full strength.

    VALROC sequences each phase so the work progresses efficiently without compromising performance.

    How long does it take to install a concrete wall or slab?

    Installation time depends on project size, site access, and curing requirements.

    • Preparation includes excavation, forming, and reinforcement placement.
    • Concrete placement and finishing are typically completed in one to two days.
    • Curing time is required before the concrete reaches full strength.

    VALROC sequences each phase so the work progresses efficiently without compromising performance.

    What maintenance is required for concrete walls and slabs?

    Concrete walls and slabs are low maintenance, but periodic care helps extend their lifespan.

    • Keeping water away from the concrete reduces long-term wear.
    • Small cracks should be monitored before they expand.
    • Surface sealing helps protect against moisture and staining.

    VALROC installs concrete with durability in mind so maintenance remains simple and manageable.

    What maintenance is required for concrete walls and slabs?

    Concrete walls and slabs are low maintenance, but periodic care helps extend their lifespan.

    • Keeping water away from the concrete reduces long-term wear.
    • Small cracks should be monitored before they expand.
    • Surface sealing helps protect against moisture and staining.

    VALROC installs concrete with durability in mind so maintenance remains simple and manageable.

    METRO VANCOUVER

    Retaining-wall projects across Metro Vancouver

    Based from our Langley construction yard, VALROC builds engineered concrete retaining walls that stabilize slopes, support driveways and create usable property space. Excavation, footings, reinforcement, formwork, drainage and concrete placement are coordinated with engineered plans and the loads acting on the wall.

    Retaining wall projects in Surrey and White Rock often respond to elevation changes, while Burnaby infill properties can require tight access and careful protection of nearby structures. In Delta, wall drainage and finished grading remain important connections. Permits and inspections are coordinated for the actual design.