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

Concrete Steps & Staircases

Custom Concrete Steps & Staircase Construction in Metro Vancouver

Custom-built steps and staircases in concrete, block, and wood, designed for durability, accessibility, and aesthetic appeal.

Review the stair layout onsite and receive a clearly defined written scope.

Man working on staircase construction in Vancouver, BC, residential project, building new wooden steps.
Everyday access

Safe, durable concrete steps

Concrete steps and staircases built for safe access, long-term durability, and daily use in Metro Vancouver conditions.

Site preparation

Accurate footings and grades

Precision forming, proper footings, and accurate grading to prevent movement, cracking, and uneven settlement.

Connected layout

Stairs that meet the surrounding work

Full-service execution from demolition to final pour, ensuring clean integration with sidewalks, patios, and landings.

Built in stages

How we build concrete steps and staircases

Full-Service Execution with Equipment & Expertise. Demolition handled in-house using specialized hydraulic and pneumatic tools. Precision forming and leveling with rotary lasers and grade references. Ability to pour onto deep footings, existing structures, or hardpan substrates. Concrete step construction begins with the levels, access and condition of the existing site. Depending on the property, the work can involve removing deteriorated steps, preparing and laying out the site, forming and reinforcing the staircase, placing concrete and finishing the surface. The sequence and details are confirmed for your project rather than assumed from a standard layout.

  1. Demolition & Removal phase underway for concrete steps and staircase project by VALROC

    Step 1

    Stair Demolition

    We remove old concrete steps and staircases using professional machinery and safe demolition methods, clearing the way for structurally sound new installations.

    • Step demolition
    • Stair removal
    • Existing access
  2. Team member demoing, pre-existing stairs and changing elevation for new installation

    Step 2

    Concrete Stair Site Prep

    We begin each staircase project with precise site preparation and layout, ensuring ground stability, proper elevation, and structural tie-ins for long-term safety and durability.

    • Site preparation
    • Stair layout
    • Landing levels
  3. Entry stairs being formed and finished during rebar & structural reinforcement process for residential

    Step 3

    Concrete Stair Formwork

    We build strong, accurate formwork for staircases using wood, metal, or paper-based materials, designed for stability, safety, and a clean finished shape before the concrete pour.

    • Stair formwork
    • Step geometry
    • Landing forms
  4. Formwork & Step Layout phase underway for concrete steps and staircase placing rebar on the first step project by VALROC

    Step 4

    Concrete Stair Reinforcement

    We install steel rebar, mesh, or custom structural support to ensure your steps are strong, safe, and meet engineering standards for long-term use.

    • Stair reinforcement
    • Structural support
    • Step construction
  5. Concrete Pouring phase underway for residential concrete steps and staircase project by VALROC

    Step 5

    Concrete Stair Placement

    We pour high-quality, premixed concrete for long-lasting staircases, with full control over additives, finishes, and mix specifications stored for future reference.

    • Concrete placement
    • Stair pour
    • Step construction
  6. Stamped concrete gray stairs finish

    Step 6

    Concrete Stair Finishing

    We finish each staircase with professional detail, offering smooth, broom, or stamped textures, while preparing surfaces for coatings, sealants, or specialty upgrades that match your design goals.

    • Step finishing
    • Surface texture
    • Outdoor traction

Property-specific stair construction

Specialized Stair Solutions Tailored to Your Property

Exterior concrete steps need to make a change in level usable while fitting the entrance, landing and surrounding ground. A site review helps determine whether new steps, a replacement staircase or a revised landing is appropriate. The layout must consider pedestrian movement, drainage and the condition of the adjoining structure.

Everyday access

Stair geometry for daily use

Designed for safety, durability, and accessibility. We assess the rise, run, landing space and adjoining entrance before recommending a concrete step layout.

Site capability

Equipment matched to the property

Fleet includes dump trucks, skid steers, excavators, and material trailers. The equipment plan depends on access, removal needs and ground conditions.

Clear scope

Concrete staircase work, clearly defined

A written scope distinguishes the steps and landings from separately priced finishes or adjoining walkway and patio work.

Finishes and choices

Concrete step finishes and stair choices

Concrete stair choices should support safe daily use, comfortable dimensions and the surrounding entrance or landscape. Finish, edging, lighting, handrails and surface protection are coordinated with the stair design.

Concrete stair and step installation support

Included in our service:

  • • Stair layout and elevation planning

  • • Rise, run and landing dimensions reviewed

  • • Excavation and structural base preparation

  • • Reinforcement and formwork installation

  • • Trackable concrete from established suppliers

  • • Concrete placement and selected finish

  • • Handrail and lighting interfaces coordinated

  • • Drainage around stairs and landings

  • • Inspection coordination when required

  • • Final safety review and project walkthrough

Additional-cost stair finishes

Stamped concrete steps and staircase finish

Design favourite · Custom patterns and texture
Stamped concrete steps · Patterned stair finish · Stair texture

Decorative edging for concrete steps

Refined detail · Defined step edges
Concrete step edging · Decorative borders · Stair details

Exposed aggregate concrete steps

Traction choice · Textured step surface
Aggregate concrete steps · Textured finish · Stair surface

Available in a wide range of finishes, textures, and stair nosing profiles. VALROC designs with both structural integrity and customer lifestyle in mind. We build with a range of materials: concrete block, wood-framed, composite, and steel. Custom landing pads, step transitions, and integrated lighting available. For this concrete staircase, finish, nosing and landing details should be selected for the measured layout and expected foot traffic. Alternative materials, decorative treatments and adjoining work are separate choices, not automatic concrete-step inclusions.

Choosing finishes and related details

View other available options

Stamped concrete stair finish

Stamped concrete can give a staircase a patterned surface when the steps and landings are designed together. Confirm the pattern and finish as an additional-cost item in the written stair scope. | Additional cost

Mixed-finish details for concrete staircases

Mixed-finish inlays can coordinate concrete stairs with surrounding paths or patios while distinguishing transitions. The pattern and finish require site-specific design and separate scope. | Additional cost

Decorative edging for concrete stairs

Decorative edging can define concrete step edges or adjoining landings as a separately scoped finish. The detail should suit the stair geometry and intended use. | Additional cost

Exposed aggregate concrete steps

Exposed aggregate can add a textured finish to concrete steps and adjacent landings where that surface is suitable. Its appearance and upkeep should be reviewed with the stair layout before it is added to the scope. | Additional cost

Stained concrete steps and landings

Concrete staining can add colour to finished steps and landings to coordinate with an entrance or adjacent hardscape. Surface preparation, finish compatibility and the selected colour treatment should be confirmed and priced separately. | Additional cost

Concrete step sealing for surface protection

Sealing may be considered for completed concrete steps as a separate maintenance and protection choice. The product and timing should suit the selected finish and site exposure. | Additional cost

Concrete additives for staircase placement

Accelerators, retarders, binders or hardeners may be specified for concrete staircase work when placement conditions or performance requirements call for them. The selected additive and its cost depend on the mix design and site conditions; it is not automatically included. | Additional cost

Curb and retaining-wall integration at concrete steps

Where concrete steps meet a curb or retaining wall, the interface may require coordinated elevations, drainage and detailing. Curb or wall work is a separate scope unless expressly included in the proposal. | Additional cost

Beyond the stair edge

More Than Just Concrete Stairs

Built to integrate seamlessly with sidewalks, patios, or elevated landings. Concrete steps often sit where an entrance meets a walkway, driveway, patio or change in yard level. Assessing those transitions together helps identify grades, drainage and access issues before the stair scope is settled. Adjoining improvements are separate work unless they are expressly included in the proposal.

Adjoining improvements are separate work unless they are expressly included in the proposal.

  • 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 steps meeting a sidewalk

    • Concrete stair access
    • Sidewalk transitions
    • Landing grades

    Where a concrete staircase meets a sidewalk, landing elevation, walking route and drainage need to work together. Sidewalk construction is coordinated with the stair layout but remains a separate scope unless listed in the proposal.

    Explore concrete sidewalks
  • Concrete patio steps and landings

    • Patio steps
    • Backyard stair access
    • Landing drainage

    Steps can connect a patio to the house or a change in yard level. Coordinating patio grades, landing space and surface drainage early helps the separate patio and stair scopes meet cleanly.

    Explore concrete patios
  • Steps connecting driveways to entrances

    • Driveway stair access
    • Entrance grades
    • Concrete transitions

    A driveway approach may affect the grade and route to an entry staircase. Plan the step location and landing against the finished driveway elevation; driveway work is separately scoped unless the proposal includes it.

    Explore concrete driveways
  • Concrete entry stairs for custom homes

    • New home stairs
    • Entry landing plan
    • Stair sequencing

    For a new custom home, the stair rise, entry threshold and landing can be coordinated with the building plans and construction sequence. Concrete steps are specified for the actual entrance design, not assumed to be part of every home-build package.

    Explore custom home builds
  • Concrete stair upgrades during home renovations

    • Stair replacement
    • Entryway access
    • Existing steps

    An entrance renovation may change the doorway, walkway or finished grades around existing steps. Reviewing those conditions together helps define whether the concrete staircase should be repaired, replaced or reconfigured as separately identified work.

    Explore home renovations

DOCUMENTED EXPERIENCE

Concrete step and staircase experience across Metro Vancouver

VALROC builds concrete steps and staircases that connect entrances, patios, walkways and changing property elevations. Our experience helps coordinate comfortable travel, landings, drainage, lighting opportunities and surrounding concrete or retaining walls. Our concrete crews work throughout Langley, Cloverdale, South Surrey, White Rock, Burnaby and New Westminster, adapting stairs and landings to established homes, strata properties, local grades and inspection requirements.

Free onsite estimate

Plan a concrete steps estimate

A concrete step estimate depends on the number and shape of the steps, the level change, site access, removal and disposal, preparation, formwork, finish and any separately scoped adjoining work. An onsite review allows the written proposal to identify what the stair installation includes and which choices remain optional. No price or schedule should be assumed before that review.

  • Describe the stair project

    Tell us whether you need new entry steps, replacement stairs or a concrete landing, and how the current access works.

  • Review the property

    Assess the level change, site access, existing support and any adjoining sidewalk or patio transition.

  • Receive a defined scope

    Review the proposed preparation, stair geometry, finish and any separately priced options before work is scheduled.

  • What affects the cost of concrete steps?

    The number and shape of steps, landing size, site access, removal, ground preparation, structural requirements and selected finish all affect the scope. Adjoining sidewalk, patio or drainage work is priced separately when requested; the written estimate follows a site review.

    What affects the cost of concrete steps?

    The number and shape of steps, landing size, site access, removal, ground preparation, structural requirements and selected finish all affect the scope. Adjoining sidewalk, patio or drainage work is priced separately when requested; the written estimate follows a site review.

    Request Your Free On-site Estimate

    Plan for your property

    Assessing the concrete stair layout

    Custom heights, widths, and layouts to fit site-specific conditions. A useful recommendation starts with measuring the level change, available run and landing area, then checking the existing support, nearby entrances and water movement. Those findings shape the removal, preparation, formwork and finish scope. Stair dimensions, structural requirements and any municipal approvals must be established for the specific property, not assumed from a standard layout.

  • 01

    Measure the level change

    Review the rise, available run, entry clearances and landing area so the concrete step layout fits the property.

  • 02

    Check support and water movement

    Look at the existing base, adjoining structure, grades and drainage before specifying preparation or reinforcement.

  • 03

    Define the stair scope

    Confirm removal, formwork, finish and any permit or engineered-detail requirements in the written plan.

  • What determines concrete stair dimensions?

    The usable rise, run and landing area determine the initial step layout. Existing support, drainage, intended use and applicable municipal requirements can change the final dimensions or structural detail, so those decisions are confirmed for the specific site.

    What determines concrete stair dimensions?

    The usable rise, run and landing area determine the initial step layout. Existing support, drainage, intended use and applicable municipal requirements can change the final dimensions or structural detail, so those decisions are confirmed for the specific site.

    Common questions

    Concrete steps and staircase FAQs

    Concrete stairs can be built in different configurations, with finish choices shaped by appearance, traction and upkeep. Installation timing depends on the site, the stair design and the work needed before and after placement. We can also explain how to care for the finished surface; the dimensions and schedule for your project are confirmed during assessment.

    What types of concrete steps and staircases does VALROC Development Ltd. offer?

    Concrete steps and staircases can be built in many configurations depending on access needs, elevation changes, and surrounding structures.

    • Front entrance steps designed to handle daily foot traffic.
    • Patio and hot tub access stairs integrated into outdoor spaces.
    • Commercial and industrial staircases built for durability and safety.
    • Steps tied into retaining walls or sloped landscapes.

    VALROC designs each staircase to suit the site conditions, intended use, and overall layout of the property.

    What types of concrete steps and staircases does VALROC Development Ltd. offer?

    Concrete steps and staircases can be built in many configurations depending on access needs, elevation changes, and surrounding structures.

    • Front entrance steps designed to handle daily foot traffic.
    • Patio and hot tub access stairs integrated into outdoor spaces.
    • Commercial and industrial staircases built for durability and safety.
    • Steps tied into retaining walls or sloped landscapes.

    VALROC designs each staircase to suit the site conditions, intended use, and overall layout of the property.

    What finishes and colours are available for concrete stairs?

    Concrete stairs can be finished in several ways to balance appearance, traction, and durability.

    • Broom finishes for a clean, slip-resistant surface.
    • Exposed aggregate for texture and visual depth.
    • Stamped finishes that mimic stone, brick, or other patterns.
    • Integral colour or surface staining for a custom look.

    VALROC helps select finishes that suit the environment and how the stairs will be used.

    What finishes and colours are available for concrete stairs?

    Concrete stairs can be finished in several ways to balance appearance, traction, and durability.

    • Broom finishes for a clean, slip-resistant surface.
    • Exposed aggregate for texture and visual depth.
    • Stamped finishes that mimic stone, brick, or other patterns.
    • Integral colour or surface staining for a custom look.

    VALROC helps select finishes that suit the environment and how the stairs will be used.

    Can VALROC build staircases with different materials like block, wood, and concrete?

    Staircases can be built using different materials depending on structural needs and design goals.

    • Fully poured concrete staircases for long-term durability.
    • Concrete block stairs for cost-effective structural applications.
    • Wood-framed stairs with concrete treads or landings.
    • Concrete stairs finished with stone or veneer surfaces.

    VALROC matches materials and construction methods to the demands of each specific project.

    Can VALROC build staircases with different materials like block, wood, and concrete?

    Staircases can be built using different materials depending on structural needs and design goals.

    • Fully poured concrete staircases for long-term durability.
    • Concrete block stairs for cost-effective structural applications.
    • Wood-framed stairs with concrete treads or landings.
    • Concrete stairs finished with stone or veneer surfaces.

    VALROC matches materials and construction methods to the demands of each specific project.

    How long does it take to install concrete steps or staircases?

    The installation timeline depends on the size of the staircase and how it connects to the surrounding structure.

    • Site preparation and base work typically take one to two days.
    • Concrete placement and finishing usually follow shortly after.
    • Curing time is required before full use.

    VALROC sequences the work so the stairs are installed efficiently without rushing critical steps.

    How long does it take to install concrete steps or staircases?

    The installation timeline depends on the size of the staircase and how it connects to the surrounding structure.

    • Site preparation and base work typically take one to two days.
    • Concrete placement and finishing usually follow shortly after.
    • Curing time is required before full use.

    VALROC sequences the work so the stairs are installed efficiently without rushing critical steps.

    How do I maintain concrete stairs to extend their lifespan?

    Concrete stairs are durable, but routine care helps keep them performing and looking their best.

    • Keeping surfaces clean reduces wear from dirt and debris.
    • Sealing helps protect against moisture and surface staining.
    • Small cracks should be addressed early to prevent expansion.

    VALROC builds stairs with proper drainage and finishing so long-term maintenance stays straightforward.

    How do I maintain concrete stairs to extend their lifespan?

    Concrete stairs are durable, but routine care helps keep them performing and looking their best.

    • Keeping surfaces clean reduces wear from dirt and debris.
    • Sealing helps protect against moisture and surface staining.
    • Small cracks should be addressed early to prevent expansion.

    VALROC builds stairs with proper drainage and finishing so long-term maintenance stays straightforward.

    Metro Vancouver

    Concrete steps across Metro Vancouver

    Based from our Langley construction yard, VALROC builds concrete steps and staircases for entrances, yards, patios and elevation changes. Rise, run, landing dimensions, reinforcement, formwork and concrete finish are coordinated with daily use, drainage and the adjoining walkway or retaining wall.

    Properties in Surrey and White Rock may need stairs across changing grades, while Burnaby sites can involve compact entrances and limited forming access. In Delta, step transitions may connect flatter lots with raised doors, patios or foundations. Handrail and lighting interfaces are reviewed with the stair design.