/*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 patios · Metro Vancouver

Concrete Patio Installations Built for Metro Vancouver Homes

Transform your backyard or outdoor space with a professionally installed concrete patio. Built for durability and low maintenance, it provides a seamless, stylish area for relaxation and entertaining.

Discuss your patio layout, finishes and site conditions with a free onsite estimate.

Reinforcing steel for concrete patio installation in Vancouver.
Outdoor living

A patio shaped around everyday use

Custom concrete patios built for durability, comfort, and year-round outdoor use in Metro Vancouver conditions.

Property connections

Room for the features you plan

Options to integrate lighting, drainage, outdoor kitchens, and entertainment features for a fully functional space.

Concrete finishes

Choose a surface you can look after

Low-maintenance concrete finishes that provide long-lasting performance while enhancing everyday outdoor living.

From removal to final cleanup

How your concrete patio is built

Seven coordinated construction stages take your patio from the existing site to a finished outdoor space. Removal, reinforcement and drainage requirements are confirmed against the actual property and agreed scope.

  1. Valroc crew performing patio demolition using compact excavator and manual labor with wheelbarrow.

    Step 1

    Concrete Patio Demolition

    Proper demolition of existing patios ensures a clean and stable foundation for new concrete installation. This process involves breaking, removal, and site clearing to prepare for the next phase.

    • Patio demolition
    • Efficient removal
    • Site clearing
  2. Digging out footings for balcony patio

    Step 2

    Concrete Patio Base Preparation

    Proper site preparation for a concrete patio includes grading, subgrade stabilization, and compaction to create a strong foundation that prevents settling and ensures durability.

    • Foundation preparation
    • Patio stability
    • Subgrade compaction
  3. Valroc crew preparing formwork for a new concrete patio at a custom home construction site.

    Step 3

    Concrete Patio Formwork

    Concrete formwork defines the patio’s shape, ensuring structural stability, clean edges, and a smooth, level finish before concrete placement.

    • Concrete formwork
    • Structural integrity
    • Patio dimensions
  4. Rebar grid installed for structural reinforcement prior to concrete pour on a residential patio.

    Step 4

    Concrete Patio Reinforcement

    Reinforcement strengthens the patio structure by preventing cracks and enhancing load-bearing capacity, using rebar, mesh, or fiber additives for long-term durability.

    • Structural reinforcement
    • Patio strength
    • Crack resistance
  5. Valroc crew member hand-troweling freshly poured concrete for a backyard patio surface.

    Step 5

    Concrete Patio Placement

    Concrete placement ensures a strong, even patio surface through proper pouring, leveling, and compaction, optimizing durability and long-term performance.

    • Concrete placement
    • Placement precision
    • Even compaction
  6. Owner applying top colour to backyard concrete patio

    Step 6

    Concrete Patio Finishing

    Concrete finishing enhances patio durability, safety, and aesthetics using techniques like broom finishing, stamping, and sealing for long-lasting results.

    • Patio finish durability
    • Patio appearance
    • Surface traction
  7. Stamped concrete for patio

    Step 7

    Concrete Patio Cleanup

    The final cleanup phase ensures a spotless, well-finished patio through thorough site cleaning, quality inspection, and post-construction care guidance.

    • Final cleanup
    • Site preparation
    • Ready-to-use patio

Property-specific planning

A patio planned as part of your property

VALROC Development Ltd. creates custom concrete patios tailored to each property’s layout, usage goals, and long-term plans. Our patio builds are designed for durability and comfort, ideal for backyard lounging, entertaining, or expanding your functional outdoor space. Our expert team specializes in Patio Installations to enhance your outdoor experience.

The practical value of a coordinated patio project is in the decisions made before and during construction: a defined scope, suitable materials and a plan for working around the property. These capabilities support the patio’s intended use rather than treating it as an isolated slab.

Defined scope

Know what your patio project includes

The onsite estimate and written scope separate the patio installation from optional finishes, drainage connections and other property work. That gives you a clearer basis for comparing choices.

Coordinated construction

Plan access before equipment arrives

Backyard access, material movement and space for the crew affect how the work is organized. Coordinating those requirements with the construction stages helps keep the project practical for an occupied property.

Materials and use

Match the slab to how you will use it

A seating patio and an area supporting heavier built-in features do not necessarily need the same design. Confirm ground preparation, thickness, reinforcement and surface texture against the intended use.

Concrete patio options

Choose the finish, shape and details that suit your space

Start with how you will use the patio, then compare appearance, surface texture and maintenance. The selected finish, layout and any decorative choices should be clearly identified in the proposal.

Concrete patio installation support

Included in our service:

  • • Patio layout and elevation planning

  • • Excavation and premium compacted subbase

  • • Forms built for the approved patio shape

  • • Drainage slopes directed away from structures

  • • Trackable concrete from established suppliers

  • • Concrete placement and professional finishing

  • • Control joints planned for the patio layout

  • • Broom finish included when specified

  • • Material ordering and delivery coordination

  • • Written scope, warranty and final walkthrough

Additional-cost patio choices

Integral colour for concrete patio finishes

Colour choice · Coordinated outdoor design
Concrete patio colour · Coloured concrete · Outdoor finishes

Stamped concrete patio finish

Creative option · Custom patio patterns
Stamped concrete patio · Patterned concrete · Patio finish

Decorative edging for concrete patios

Finishing touch · Defined patio borders
Concrete patio edging · Decorative borders · Patio details

Functional Designs That Go Beyond Basic Slabs Benefits of Choosing Custom Concrete Patios Choosing custom concrete patios comes with various benefits. Unlike standard patio options, custom designs allow you to incorporate personal touches that reflect your style and enhance your property’s curb appeal. You can select from various concrete colors, stamped patterns, and concrete finish textures that complement your home’s exterior. For instance, using stamped concrete mimics the appearance of natural stone or wood, providing aesthetic charm without sacrificing longevity. Moreover, the ability to integrate specific features such as seating ledges or planters can create a more cohesive outdoor environment. Available in a variety of sizes, layouts, and textures to suit your lifestyle Custom designs offer aesthetic appeal, making your outdoor space visually striking. Concrete blends and finishes available for both utility and curb appeal Broom texture, exposed aggregate, stamped patterns, smooth trowel finishes, colour and staining offer different appearances and surface characteristics. Consider rain exposure and how the patio will be used alongside the look you prefer. Thickness, reinforcement, joints and mix adjustments should follow the site and design requirements, not simply a desire to add upgrades. Sealing adds a separate maintenance decision; it does not replace drainage or proper preparation.

Appearance is only part of the decision

View other available options

Integrated French Drainage

If runoff or wet ground affects a concrete patio site, integrated French drainage may be scoped alongside grading and base preparation to help manage water near the patio. | Additional cost

Smooth Trowel Concrete Finish

A smooth trowel finish can give selected concrete patio areas a clean appearance, but its suitability and traction need careful review where rain and foot traffic are expected. | Additional cost

Custom Concrete Forming

Custom forming lets a concrete patio follow curved edges, angled layouts or adjoining landscape features, shaping the slab to the backyard rather than forcing a standard rectangle. | Additional cost

Custom Concrete Thickness

Where a concrete patio will support heavier features or unusual loads, slab thickness can be specified alongside the base, reinforcement and intended use. | Additional cost

Mixed-Finish Inlay Designs for Concrete

Mixed-finish inlays can define dining, walking or border zones within a concrete patio by coordinating contrasting textures or colours during layout and placement. | Additional cost

Concrete Additives : Accelerators, Retarders, Binders & Hardeners

For a concrete patio pour with particular weather, placement or finishing demands, a suitable mix additive may be considered after checking compatibility with the chosen finish. | Additional cost

Concrete Staining Options

Concrete staining can add variegated colour to a patio surface, helping the outdoor living area relate to the home and surrounding hardscape when the concrete is suitable. | Additional cost

Rebar Reinforcement Add-on for Our Concrete Installations

If the patio design or site conditions call for additional reinforcement, a rebar upgrade can be specified with the slab layout and joints; it does not replace sound base preparation. | Additional cost

Exposed Aggregate Concrete Finish

An exposed aggregate finish gives a concrete patio a visible stone texture that can complement walkways and planting areas, with the surface selected for appearance and appropriate traction. | Additional cost

Concrete Sealing for Surface Protection

A compatible sealer may be selected for a concrete patio to support cleaning and surface care, with the finish, foot traffic and maintenance needs reviewed first. | Additional cost

Beyond the patio edge

Plan the connections before the concrete goes in

A new patio can change how you move through the backyard and how adjoining spaces function. Consider paths, steps, drainage, retained ground and outdoor cooking areas together so separately scoped work can be coordinated with the patio layout.

These are related-service opportunities, not automatic patio inclusions. Confirm each connection, required design work and separate scope before construction.

  • Concrete sidewalks that connect to your patio

    • Patio walkway
    • Adjoining sidewalk
    • Access path

    Connect the concrete patio to side entrances, garden paths and other outdoor areas. Coordinate the route, finished levels and adjoining surfaces so the sidewalk and patio work together.

    Explore connecting concrete sidewalks
  • Steps and stairs between your patio and home

    • Patio steps
    • Patio elevation transitions
    • Custom Options

    Where the patio and doorway sit at different heights, plan the steps, landings and access route together. Resolve the elevation changes and available space before setting the patio layout.

    Explore patio steps and staircases
  • Retaining walls for raised patio areas

    • Patio retaining walls
    • Retained ground levels
    • Retain space

    Where sloping ground needs to be retained, coordinate a retaining wall with the concrete patio layout to define a usable outdoor area. Assess the wall design, drainage and supported loads before construction; not every raised patio requires the same solution.

    Explore retaining walls for patio level changes
  • Covered patio deck under construction with brick home exterior and VALROC excavator on site

    Outdoor kitchens and BBQ areas beside your patio

    • Outdoor kitchen
    • Patio cooking areas
    • Entertainment

    Plan cooking, dining and movement through the outdoor space alongside the concrete patio. Confirm the kitchen footprint, appliance requirements and any utility connections before the slab is placed, with kitchen installation identified as separate work.

    Explore outdoor kitchens and BBQ areas

DOCUMENTED EXPERIENCE

Concrete patio experience for Metro Vancouver properties

VALROC builds concrete patios for homes, strata properties and outdoor gathering spaces across Metro Vancouver. Our experience helps coordinate the patio base, drainage, finished elevations, access and connections to walkways, steps and outdoor living areas. We have completed outdoor concrete work throughout Brookswood, Willoughby, Walnut Grove, South Surrey, White Rock and established Burnaby neighbourhoods, working with homeowners, strata representatives and project designers where required.

Free onsite patio estimate

Price the patio around the work it actually needs

Imagine enjoying a summer evening under the stars with friends, all while sitting comfortably on your beautifully designed patio. Let VALROC Development Ltd. help you create an outdoor oasis that reflects your personal style and accommodates your lifestyle. An onsite estimate connects your design choices with the conditions on your property. Discuss the patio footprint, existing concrete removal, access, ground preparation, finish selection and any separately scoped drainage or adjoining work before comparing prices.

  • Show us the space

    Describe the intended patio use, approximate layout, existing surface and any access constraints. Bring photos or design ideas if you have them.

  • Compare the scope and choices

    Review removal, preparation, finishes and optional adjoining work together. Identify which choices change the cost and what still needs investigation.

  • Review the written proposal

    Check the included work, exclusions, selected finish and next steps before committing. Keep future additions separate from the current installation.

  • What affects the cost of a concrete patio?

    Size is one factor, but access, demolition, excavation, disposal, ground preparation, slab specification and finish complexity also affect the scope. Curved layouts, decorative edges, mixed finishes, drainage connections and heavier built-in features may require additional work. A site-specific estimate is more useful than a single price per square foot that assumes every backyard is the same.

    What affects the cost of a concrete patio?

    Size is one factor, but access, demolition, excavation, disposal, ground preparation, slab specification and finish complexity also affect the scope. Curved layouts, decorative edges, mixed finishes, drainage connections and heavier built-in features may require additional work. A site-specific estimate is more useful than a single price per square foot that assumes every backyard is the same.

    Request Your Free On-site Estimate

    Before the design is settled

    Check the ground, access and connections first

    A useful patio recommendation starts with the property rather than a finish sample. Access, existing surfaces, ground conditions, water movement and the intended use all affect what needs to be built beneath and around the slab.

  • 01

    Understand everyday use

    Confirm seating, entertaining, circulation and any planned outdoor kitchen or heavier feature. Identify what belongs in this phase and what may come later.

  • 02

    Inspect the existing site

    Look at the existing slab or ground, changes in level, nearby structures and equipment access. Identify where removal or further investigation is needed.

  • 03

    Resolve water and connections

    Consider surface runoff, adjoining paths, steps and drainage connections. Where a retaining structure or structural foundation is involved, establish the required design input.

  • 04

    Agree on the recommendation

    Set out the preparation, concrete specification, finish choices and scope boundaries before construction. Confirm any outstanding site or approval requirements.

  • A site visit comes before a final specification

    Slab thickness, reinforcement, footing design and drainage outlets cannot be selected reliably from a keyword list. They depend on the ground, intended loads, adjoining construction and project requirements. The estimate should identify any further investigation or design work rather than assuming every patio needs the same solution.

    A site visit comes before a final specification

    Slab thickness, reinforcement, footing design and drainage outlets cannot be selected reliably from a keyword list. They depend on the ground, intended loads, adjoining construction and project requirements. The estimate should identify any further investigation or design work rather than assuming every patio needs the same solution.

    Details that shape the decision

    Concrete patio questions, answered

    Explore how finishes, reinforcement, groundwork and foundations affect a concrete patio project. These answers explain the decisions to discuss during planning without replacing a site-specific design.

    What customization options does Valroc offer for patio construction, including textures and colours?

    Concrete patios can be customized to match both function and appearance.

    • Surface textures can improve traction or create a specific visual style.
    • Colour options help tie the patio into surrounding architecture.
    • Inlays or borders add definition to larger outdoor spaces.

    VALROC helps balance design preferences with durability so the patio looks good and performs well over time.

    What customization options does Valroc offer for patio construction, including textures and colours?

    Concrete patios can be customized to match both function and appearance.

    • Surface textures can improve traction or create a specific visual style.
    • Colour options help tie the patio into surrounding architecture.
    • Inlays or borders add definition to larger outdoor spaces.

    VALROC helps balance design preferences with durability so the patio looks good and performs well over time.

    Can I reinforce my patio for extra durability or custom structural designs?

    Yes, patios can be reinforced to support heavier use or more complex design features.

    • Thicker concrete slabs help support outdoor kitchens, fire features, and heavy furnishings.
    • Rebar grids or fibre reinforcement improve strength and reduce long-term cracking.
    • Multi-level layouts or built-in elements require additional structural planning.

    VALROC adjusts reinforcement based on how the patio will be used and what structures are being integrated.

    Can I reinforce my patio for extra durability or custom structural designs?

    Yes, patios can be reinforced to support heavier use or more complex design features.

    • Thicker concrete slabs help support outdoor kitchens, fire features, and heavy furnishings.
    • Rebar grids or fibre reinforcement improve strength and reduce long-term cracking.
    • Multi-level layouts or built-in elements require additional structural planning.

    VALROC adjusts reinforcement based on how the patio will be used and what structures are being integrated.

    How does Valroc integrate patios into environments that require additional groundwork or preparation?

    Some patios require more than a simple pour to remain stable over time.

    • Grading and excavation are used to correct slope and improve access.
    • Drainage systems help prevent water pooling and soil saturation.
    • Soft or sloped ground may require engineered fill or reinforcement layers.

    VALROC completes all necessary groundwork before pouring to ensure the patio stays level and performs as intended.

    How does Valroc integrate patios into environments that require additional groundwork or preparation?

    Some patios require more than a simple pour to remain stable over time.

    • Grading and excavation are used to correct slope and improve access.
    • Drainage systems help prevent water pooling and soil saturation.
    • Soft or sloped ground may require engineered fill or reinforcement layers.

    VALROC completes all necessary groundwork before pouring to ensure the patio stays level and performs as intended.

    What types of footings and foundations can Valroc prepare for an outdoor patio?

    The right footing depends on whether the patio is structural, elevated, or tied to another feature.

    • Reinforced concrete slabs are common for ground-level patios.
    • Pier or sonotube footings support covers, pergolas, or raised sections.
    • Specialized foundations may be used on soft or unstable soil.

    VALROC selects footing types based on load requirements, soil conditions, and overall patio design.

    What types of footings and foundations can Valroc prepare for an outdoor patio?

    The right footing depends on whether the patio is structural, elevated, or tied to another feature.

    • Reinforced concrete slabs are common for ground-level patios.
    • Pier or sonotube footings support covers, pergolas, or raised sections.
    • Specialized foundations may be used on soft or unstable soil.

    VALROC selects footing types based on load requirements, soil conditions, and overall patio design.

    Why is Valroc uniquely qualified for patio construction compared to other contractors?

    Patio construction requires coordination between excavation, concrete work, and finishing.

    • Accurate base preparation reduces settlement and surface cracking.
    • Proper forming and layout allow for clean lines and consistent finishes.
    • Structural planning matters when patios connect to other features.

    VALROC handles the full process in-house, allowing each stage to be built with the next one in mind.

    Why is Valroc uniquely qualified for patio construction compared to other contractors?

    Patio construction requires coordination between excavation, concrete work, and finishing.

    • Accurate base preparation reduces settlement and surface cracking.
    • Proper forming and layout allow for clean lines and consistent finishes.
    • Structural planning matters when patios connect to other features.

    VALROC handles the full process in-house, allowing each stage to be built with the next one in mind.

    Metro Vancouver patio construction

    Local planning for your concrete patio

    Based from our Langley construction yard, VALROC builds concrete patios for outdoor living areas in Brookswood, Willoughby, Walnut Grove and surrounding communities. Patio layout, excavation, compacted base, forming and concrete delivery are coordinated around doors, finished elevations, drainage and backyard access.

    Projects in Surrey and White Rock often connect patios with stairs, retaining walls, covered areas or outdoor kitchens, while Burnaby properties may require tighter equipment and material access. Each patio is planned around how the space will be used and how it meets the wider property.