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

BUILT FOR REAL GARAGE USE

Concrete Garage Slabs Built for Metro Vancouver Properties

Whether you’re managing your own garage construction, working with a framing contractor, or preparing for a prefabricated garage package, a properly constructed concrete garage slab provides the foundation every project depends on. Designed for detached garages, workshops, custom shops, and accessory buildings throughout Metro Vancouver.

Site-specific planning for loads, drainage and connected work.

Backyard newly installed garage slabs still with the wooden forms up and the Valroc service truck up on the hill
LOAD PLANNING

Built for vehicles and equipment

Designed to support the weight of vehicles, equipment, and structures above

SITE PREPARATION

Prepared for stable long-term performance

Includes proper grading, subbase compaction, and moisture protection

FUTURE FEATURES

Plan heating, hoists and drainage early

Available with added reinforcement options for hoists, floor heating, or drainage

BUILT FROM THE GROUND UP

How a garage slab is constructed for lasting performance

A dependable garage floor begins below the finished surface. The construction sequence must establish stable subgrade conditions, accurate elevations and formwork, suitable reinforcement, controlled concrete placement and a finish appropriate for the way the garage will be used. Planning these stages together helps protect doorway transitions, drainage paths and the long-term performance of the slab.

  1. VALROC removing organics for footing installation of detached garage

    Step 1

    Concrete Garage Slab Site Prep

    Site prep for garage slabs includes grading, compaction, and subbase installation to support vehicle loads and prevent slab movement or cracking.

    • Site Preparation
    • Concrete Garage Slabs
    • Compacted granular base
  2. Compacting subbase material for a mono, poor footings and foundation.

    Step 2

    Garage Slab Formwork

    Garage slab formwork defines elevation, slope, entry clearance, and edge thickness, shaping the structure for performance, load-bearing, and drainage.

    • slab layouts
    • concrete garage layout
    • garage slab formwork
  3. Installing rebar for the concrete pour on our garage foundation and slab

    Step 3

    Garage Slab Reinforcement

    Reinforcement grids and steel dowels prevent cracking, increase durability, and ensure the slab withstands heavy loads, seasonal stress, and structural tie-ins.

    • Structural Reinforcement
    • Garage Concrete Slabs
    • load-bearing slab design
  4. Placing concrete in custom built, detached garage

    Step 4

    Garage Slab Placement

    Professional placement ensures structural performance, proper surface slope, smooth integration with rebar, and optimal slab curing.

    • Concrete Placement
    • Garage Slabs
    • concrete consolidation
  5. Concrete foundation curing while in wooden forms

    Step 5

    Garage Slab Finishing

    Surface finishing ensures a clean, slip-resistant garage slab that’s prepped for coatings, vehicles, and long-term wear, including wooden form removal and slab preparation.

    • Surface Finishing
    • Garage Slabs
    • finished garage floor
  6. Team preparing slab area with focus on final cleanup & inspection phase

    Step 6

    Project Completion

    We complete every garage slab with full cleanup, remove forms, clear debris, and prepare the site for either your next contractor or continuation with our build team.

    • Cleanup
    • Final inspections
    • final slab walkthrough

PLANNED AROUND USE

A garage slab specified for the way you will use it

The right garage slab is defined by what it needs to carry and how the building will function. Vehicle traffic, stored equipment, workshop use, a future hoist, radiant heating and drainage can change the required thickness, reinforcement, preparation and sequencing. VALROC coordinates the concrete scope with property access, garage packages, framing schedules and adjoining work so customers understand what is included before construction begins.

LOADS AND USE

Specify the slab around real garage use

Vehicle weight, stored equipment, workshop activity and a future hoist can change slab thickness, reinforcement and localized support requirements.

SITE CONDITIONS

Build from stable, drained conditions

Subgrade, granular base, moisture conditions, finished elevations and water movement are reviewed before the concrete scope is finalized.

PROJECT COORDINATION

Coordinate the slab with the whole garage

Garage packages, framing schedules, door thresholds, heating, drainage and adjoining concrete are planned early so one decision does not compromise another.

OPTIONS WITH A PURPOSE

Choose garage slab details around use and upkeep

Garage slab choices respond to vehicle loads, workshop use, drainage and maintenance. Reinforcement, thickness, finish and surface protection are coordinated before concrete placement.

Concrete garage slab installation support

Included in our service:

  • • Garage use and slab load review

  • • Excavation and compacted subbase preparation

  • • Forms set to approved dimensions

  • • Drainage slopes and doorway transitions

  • • Reinforcement placed as specified

  • • Trackable concrete from established suppliers

  • • Concrete placement and professional finishing

  • • Control joints planned for the slab layout

  • • Material delivery and access coordination

  • • Written scope, warranty and final walkthrough

Options for loads, appearance and maintenance

Concrete mix options for garage slab placement

Performance option · Reliable slab placement
Garage slab concrete · Placement conditions · Concrete performance

Sealer for concrete garage floors

Protective finish · Easier floor care
Garage floor sealing · Concrete protection · Surface care

Colour options for concrete garage slabs

Appearance choice · Coordinated garage finish
Garage slab colour · Coloured concrete · Floor finish

Garage slab choices should solve a specific use or maintenance need. Thickness and reinforcement respond to expected loads; mix adjustments respond to placement conditions; heating and drainage must be coordinated before the pour; and colour, stain or sealer choices affect appearance and ongoing care. Each selection should be confirmed against the garage design, site conditions and the work included in the proposal.

How the options affect the slab

View other available options

Garage Floor Colour Options

Garage floor colour can be considered when the concrete slab is part of a finished workshop or vehicle space, with the selected treatment reviewed for traffic and maintenance needs. | Additional cost

Stained Garage Floor Finish

A stained garage floor can add variegated colour to a suitable concrete slab, with surface preparation and expected vehicle or workshop use reviewed before selection. | Additional cost

Custom Garage Slab Formwork

Custom formwork can define a garage slab's non-standard footprint, doorway transitions or required edge elevations before reinforcement and concrete placement. | Additional cost

Garage Slab Thickness Upgrades

If a garage slab will support heavier vehicles, equipment or a planned hoist, thickness upgrades should be assessed against the actual loads and any applicable design requirements. | Additional cost

BEYOND THE GARAGE FLOOR

Coordinate access and drainage before concrete placement

A garage slab rarely ends at the garage wall. The driveway approach, side-door sidewalks and entrance grades affect how vehicles and people reach the building. Trench drains can collect water at the door, while perimeter drainage or an approved rock-pit system may manage water around the foundation. These are related scopes, not automatic slab inclusions, and should be coordinated before concrete placement.

Driveways, sidewalks and drainage systems are related opportunities, not automatic garage slab inclusions. Confirm each requirement and separate scope before construction.

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

    Trench drains at garage entrances

    • Garage trench drainage
    • Entrance runoff
    • Surface water

    Coordinate a trench drain where driveway runoff, snowmelt or wash water may reach the garage entrance.

    Explore garage trench drainage
  • Concrete driveways for garage slabs

    • Garage driveway transition
    • Vehicle access
    • Slab approach

    Coordinate the driveway approach, threshold transition and finished elevations with the garage slab.

    Explore connected concrete driveways
  • Concrete sidewalks for garage access

    • Garage sidewalk
    • Pedestrian paths
    • Finished elevations

    Plan side door walkways and pedestrian routes with the garage elevations and adjoining concrete edges.

    Explore garage access sidewalks
  • Excavating a rock pit beside a detached garage for rainwater management

    Rock pit drainage for garage runoff

    • Garage rock pit
    • Drainage discharge
    • Subsurface drainage

    Where site conditions permit, assess an aggregate rock pit as a destination for collected garage area water.

    Explore aggregate rock pit drainage
  • Perimeter drainage around the garage

    • Garage drainage
    • Foundation protection
    • Water control

    Coordinate foundation drainage, pipe routes and discharge before adjoining slabs and hardscape are completed.

    Explore garage perimeter drainage

DOCUMENTED EXPERIENCE

Concrete garage slab experience built around real use

VALROC builds garage slabs for parking, storage and workshop use across Metro Vancouver. Our experience helps coordinate excavation, base preparation, reinforcement, drainage, door elevations and the finished surface with the intended garage. We have completed concrete work for garages and workshops throughout Brookswood, Willoughby, Walnut Grove, Cloverdale, South Surrey, White Rock and Burnaby, coordinating structural requirements and inspections where applicable.

CLEAR SCOPE FIRST

Build the estimate around the property and intended use

Garage slab cost depends on more than square footage. Excavation depth, access, base preparation, slab thickness, reinforcement, concrete volume, drainage, surface treatment and coordination for heating or a vehicle hoist can all affect the scope. A free onsite estimate allows these requirements to be identified before the proposal is prepared, giving the customer a clearer basis for comparing the work included.

  • Meet at the property

    Review the garage footprint, access, existing grades and the way the finished space will be used.

  • Confirm technical requirements

    Discuss excavation, base preparation, thickness, reinforcement, drainage, heating, hoist loads and adjoining work.

  • Receive a defined scope

    Use the onsite findings to prepare a proposal that identifies the work, selected options and important project boundaries.

  • What affects the cost of a concrete garage slab?

    Garage slab cost depends on more than square footage. Excavation depth, access, base preparation, slab thickness, reinforcement, concrete volume, drainage, surface treatment and coordination for heating or a vehicle hoist can all affect the scope. A free onsite estimate allows these requirements to be identified before the proposal is prepared, giving the customer a clearer basis for comparing the work included.

    What affects the cost of a concrete garage slab?

    Garage slab cost depends on more than square footage. Excavation depth, access, base preparation, slab thickness, reinforcement, concrete volume, drainage, surface treatment and coordination for heating or a vehicle hoist can all affect the scope. A free onsite estimate allows these requirements to be identified before the proposal is prepared, giving the customer a clearer basis for comparing the work included.

    Request Your Free On-site Estimate

    BEFORE THE POUR

    Confirm the loads, levels and site conditions first

    An onsite assessment should confirm the garage footprint, grades, soil and moisture conditions, concrete access and the loads the floor must support. It should also identify doorway elevations, driveway and sidewalk transitions, drainage outlets, under-slab services and any planned hoist or radiant-heating system. These decisions allow the slab scope to be based on the property and intended use rather than a one-size-fits-all specification.

  • 01

    Confirm intended use and loads

    Identify vehicle types, stored equipment, workshop use and any planned hoist so thickness and reinforcement can be evaluated appropriately.

  • 02

    Review grades, access and subgrade

    Check excavation access, soil and moisture conditions, doorway elevations and the base preparation required below the slab.

  • 03

    Coordinate services and water management

    Locate radiant-heating components, drains, sleeves, plumbing and electrical requirements before concrete placement.

  • 04

    Define the complete proposal scope

    Document preparation, reinforcement, concrete, finishing, transitions and related work so the customer can see what is included.

  • What the assessment needs to resolve

    An onsite assessment should confirm the garage footprint, grades, soil and moisture conditions, concrete access and the loads the floor must support. It should also identify doorway elevations, driveway and sidewalk transitions, drainage outlets, under-slab services and any planned hoist or radiant-heating system. These decisions allow the slab scope to be based on the property and intended use rather than a one-size-fits-all specification.

    What the assessment needs to resolve

    An onsite assessment should confirm the garage footprint, grades, soil and moisture conditions, concrete access and the loads the floor must support. It should also identify doorway elevations, driveway and sidewalk transitions, drainage outlets, under-slab services and any planned hoist or radiant-heating system. These decisions allow the slab scope to be based on the property and intended use rather than a one-size-fits-all specification.

    PLAN WITH BETTER ANSWERS

    Questions to resolve before the garage slab is built

    Use these garage slab questions to understand the decisions that affect performance before construction: thickness, reinforcement, radiant heating, crack control, drainage and support for heavy equipment or a vehicle hoist.

    How thick should a garage slab be?

    Garage slab thickness varies based on how the space will be used.

    • Standard residential garages are typically poured at 4 inches thick.
    • Workshops or garages with heavier vehicles usually require 5 to 6 inches.
    • Commercial or industrial garages may need 6 to 8 inches or more.

    VALROC adjusts slab thickness and reinforcement to suit vehicle loads, equipment, and site conditions.

    How thick should a garage slab be?

    Garage slab thickness varies based on how the space will be used.

    • Standard residential garages are typically poured at 4 inches thick.
    • Workshops or garages with heavier vehicles usually require 5 to 6 inches.
    • Commercial or industrial garages may need 6 to 8 inches or more.

    VALROC adjusts slab thickness and reinforcement to suit vehicle loads, equipment, and site conditions.

    Do I need rebar reinforcement in a garage slab?

    Reinforcement is recommended in most garage slabs to improve strength and control cracking.

    • Rebar is commonly used for heavier loads or unstable soil conditions.
    • Fiber mesh helps control surface cracking in lighter-duty applications.
    • Wire mesh may be used in standard residential garages.

    VALROC selects reinforcement based on load requirements, soil conditions, and how the garage will be used.

    Do I need rebar reinforcement in a garage slab?

    Reinforcement is recommended in most garage slabs to improve strength and control cracking.

    • Rebar is commonly used for heavier loads or unstable soil conditions.
    • Fiber mesh helps control surface cracking in lighter-duty applications.
    • Wire mesh may be used in standard residential garages.

    VALROC selects reinforcement based on load requirements, soil conditions, and how the garage will be used.

    Can you install a heated garage slab?

    Garage slabs can be built with radiant heating systems embedded directly in the concrete.

    • Hydronic tubing provides consistent heat across the slab.
    • Radiant systems reduce cold spots and winter frost buildup.
    • Heated slabs are commonly used in workshops and high-use garages.

    VALROC coordinates slab design, insulation, and heating layout before the pour to ensure reliable performance.

    Can you install a heated garage slab?

    Garage slabs can be built with radiant heating systems embedded directly in the concrete.

    • Hydronic tubing provides consistent heat across the slab.
    • Radiant systems reduce cold spots and winter frost buildup.
    • Heated slabs are commonly used in workshops and high-use garages.

    VALROC coordinates slab design, insulation, and heating layout before the pour to ensure reliable performance.

    How does VALROC prevent cracking in garage slabs?

    Preventing cracks starts with proper preparation and controlled construction, not just the concrete itself.

    • Building a stable, compacted sub-base to reduce movement.
    • Placing control joints to manage natural expansion and contraction.
    • Using reinforcement to limit shifting under load.
    • Managing curing conditions so the slab gains strength evenly.

    VALROC focuses on these fundamentals to reduce cracking over the life of the slab.

    How does VALROC prevent cracking in garage slabs?

    Preventing cracks starts with proper preparation and controlled construction, not just the concrete itself.

    • Building a stable, compacted sub-base to reduce movement.
    • Placing control joints to manage natural expansion and contraction.
    • Using reinforcement to limit shifting under load.
    • Managing curing conditions so the slab gains strength evenly.

    VALROC focuses on these fundamentals to reduce cracking over the life of the slab.

    How do you ensure proper drainage in a garage slab?

    Proper drainage in a garage slab is planned during grading and slab design to prevent water from collecting on or beneath the concrete.

    • Sloping the slab surface to direct water toward the door opening or a designated drain.
    • Installing floor drains where snowmelt, wash-down water, or spills are expected.
    • Sealing the concrete surface to reduce moisture penetration over time.

    VALROC designs garage slabs with drainage in mind before concrete is poured, not as an afterthought.

    How do you ensure proper drainage in a garage slab?

    Proper drainage in a garage slab is planned during grading and slab design to prevent water from collecting on or beneath the concrete.

    • Sloping the slab surface to direct water toward the door opening or a designated drain.
    • Installing floor drains where snowmelt, wash-down water, or spills are expected.
    • Sealing the concrete surface to reduce moisture penetration over time.

    VALROC designs garage slabs with drainage in mind before concrete is poured, not as an afterthought.

    What is the recommended slab thickness for a heavy-duty vehicle hoist?

    Slab thickness for a vehicle hoist depends on the hoist type, vehicle weight, and intended use.

    • Light-duty residential hoists typically require a minimum 6-inch reinforced slab.
    • Commercial two-post or four-post hoists often need 8 inches or more with rebar.
    • Industrial hoists may require 10 to 12 inches with additional footings at column locations.

    VALROC designs hoist slabs to match manufacturer requirements and real-world loading conditions.

    What is the recommended slab thickness for a heavy-duty vehicle hoist?

    Slab thickness for a vehicle hoist depends on the hoist type, vehicle weight, and intended use.

    • Light-duty residential hoists typically require a minimum 6-inch reinforced slab.
    • Commercial two-post or four-post hoists often need 8 inches or more with rebar.
    • Industrial hoists may require 10 to 12 inches with additional footings at column locations.

    VALROC designs hoist slabs to match manufacturer requirements and real-world loading conditions.

    METRO VANCOUVER DELIVERY

    Garage slab construction supported from Langley

    VALROC builds concrete garage slabs from its Langley construction yard, coordinating excavation, compacted subbase, forms, reinforcement, concrete delivery and finishing. Slab thickness, doorway elevations, drainage and control joints are planned around vehicle storage, workshop activity and any specified equipment loads.

    Garage slabs in Surrey and Delta may support larger shops, equipment or planned vehicle lifts, while Burnaby and White Rock properties can require careful access and grade transitions. Each slab is coordinated with the garage design and surrounding driveway or walkway.