Modules/ScubaConfigApp/ScubaConfigAppHelpers/ScubaConfigAppResultsHelper.psm1

Function Initialize-ResultsTab {
    <#
    .SYNOPSIS
    Initializes the Results tab and scans for existing ScubaGear reports.
    .DESCRIPTION
    This function sets up the Results tab, scans the output directory for existing reports,
    and creates tabs for each found report with formatted timestamps.
    #>

    # Enable the Results tab
    $syncHash.ResultsTab.IsEnabled = $true
    $syncHash.ResultsTab.Header = $syncHash.UIConfigs.Reports.tabName

    # Add event handlers for control buttons
    $syncHash.ResultsRefresh_Button.Add_Click({
        Update-ResultsTab

    })

    $syncHash.ResultsOpenFolder_Button.Add_Click({
        Open-ResultsFolder
    })

    $syncHash.ResultsDefaultScanPath_Button.Add_Click({
        Set-DefaultResultsScanPath
    })

    $syncHash.ResultsBrowseScanPath_Button.Add_Click({
        $folderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
        $folderDialog.Description = "Select the folder to scan for existing ScubaGear results"
        $folderDialog.ShowNewFolderButton = $true
        $folderDialog.RootFolder = [System.Environment+SpecialFolder]::MyComputer

        $currentPath = $syncHash.ResultsScanPath_TextBox.Text
        $defaultPath = Get-ResolvedOutputPath

        if (-not [string]::IsNullOrWhiteSpace($currentPath) -and $currentPath.Trim() -ne "." -and (Test-Path $currentPath) -and $currentPath.Trim() -ne $defaultPath.Trim()) {
            $folderDialog.SelectedPath = $currentPath
        }

        $result = $folderDialog.ShowDialog()
        if ($result -eq [System.Windows.Forms.DialogResult]::OK) {
            $syncHash.ResultsScanPath_TextBox.Text = $folderDialog.SelectedPath
            Update-ResultsTab
        }
    })

    # Initialize the scan path to the current output path.
    Set-DefaultResultsScanPath -NoRefresh

    # Initial scan for existing results
    Update-ResultsTab
}

Function Get-ResolvedOutputPath {
    <#
    .SYNOPSIS
    Resolves the configured ScubaGear output path to the same runtime value used for generating reports.
    #>

    $configuredPath = $syncHash.AdvancedSettingsData["OutPath"]
    if ([string]::IsNullOrEmpty($configuredPath)) { $configuredPath = $syncHash.OutPath_TextBox.Text }
    if ([string]::IsNullOrEmpty($configuredPath)) { $configuredPath = $syncHash.ScubaDefaults.OutPath }

    if ([string]::IsNullOrEmpty($configuredPath) -or $configuredPath.Trim() -eq ".") {
        $configuredPath = [Environment]::GetFolderPath('MyDocuments')
    }

    return $configuredPath
}

Function Set-DefaultResultsScanPath {
    <#
    .SYNOPSIS
    Resets the Results scan path back to the current output path without changing the actual report output settings.
    #>

    param(
        [switch]$NoRefresh
    )

    $outputPath = Get-ResolvedOutputPath
    $syncHash.ResultsScanPath_TextBox.Text = $outputPath

    if (-not $NoRefresh) {
        Update-ResultsTab
    }
}

Function Update-ResultsTab {
    <#
    .SYNOPSIS
    Scans for ScubaGear result folders and creates/updates result tabs using simple background jobs.
    .DESCRIPTION
    This function searches the output directory for M365BaselineConformance folders,
    parses timestamps, and creates tabs with properly formatted dates.
    Uses PowerShell jobs for background processing to prevent UI blocking.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseUsingScopeModifierInNewRunspaces", "")]
    [CmdletBinding()]
    param()

    # Prevent multiple concurrent scans
    if ($syncHash.ResultsScanInProgress) {
        Write-DebugOutput -Message "Results scan already in progress, skipping" -Source $MyInvocation.MyCommand -Level "Info"
        return
    }

    $syncHash.ResultsScanInProgress = $true

    # Show progress indicator immediately
    $syncHash.ResultsScanProgress.Visibility = "Visible"
    $syncHash.ResultsScanStatus.Text = "Scanning for ScubaGear reports..."
    $syncHash.ResultsProgressBar.IsIndeterminate = $true

    # Clear existing result tabs (keep ResultsEmptyTab)
    $tabsToRemove = @()
    foreach ($tab in $syncHash.ResultsTabControl.Items) {
        if ($tab.Name -ne "ResultsEmptyTab") {
            $tabsToRemove += $tab
        }
    }
    foreach ($tab in $tabsToRemove) {
        $syncHash.ResultsTabControl.Items.Remove($tab)
    }

    # Show loading state
    $syncHash.ResultsEmptyTab.Visibility = "Visible"
    $syncHash.ResultsTabControl.SelectedItem = $syncHash.ResultsEmptyTab
    Update-ResultsCount 0

    # Update empty tab content to show loading message
    $loadingContent = @"
<ScrollViewer xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        VerticalScrollBarVisibility="Auto"
        HorizontalScrollBarVisibility="Auto">
    <StackPanel Margin="20" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="Loading: Scanning for ScubaGear reports..."
                   FontSize="16"
                   TextAlignment="Center"
                   Foreground="{DynamicResource MutedTextBrush}"
                   Margin="10"/>
        <TextBlock Text="Please wait while we search for existing reports..."
                   FontSize="12"
                   TextAlignment="Center"
                   Foreground="{DynamicResource MutedTextBrush}"
                   Margin="10"/>
    </StackPanel>
</ScrollViewer>
"@

    try {
        $loadingControl = [Windows.Markup.XamlReader]::Parse($loadingContent)
        $syncHash.ResultsEmptyTab.Content = $loadingControl
    } catch {
        Write-DebugOutput -Message "Error creating loading content: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Warning"
    }

    # Get configuration values for the background job.
    # Resolution order: AdvancedSettingsData (user-configured) → TextBox.Text (UI default)
    # → ScubaDefaults (ScubaConfigDefaults.json) → UIConfigs.defaultAdvancedSettings (app JSON)
    $folderName = $syncHash.AdvancedSettingsData["OutFolderName"]
    if (-not $folderName) { $folderName = $syncHash.OutFolderName_TextBox.Text }
    if (-not $folderName) { $folderName = $syncHash.ScubaDefaults.OutFolderName }
    if (-not $folderName) { $folderName = $syncHash.UIConfigs.defaultAdvancedSettings.OutFolderName_TextBox }

    $jsonfilename = $syncHash.AdvancedSettingsData["OutJsonFileName"]
    if (-not $jsonfilename) { $jsonfilename = $syncHash.OutJsonFileName_TextBox.Text }
    if (-not $jsonfilename) { $jsonfilename = $syncHash.ScubaDefaults.OutJsonFileName }
    if (-not $jsonfilename) { $jsonfilename = $syncHash.UIConfigs.defaultAdvancedSettings.OutJsonFileName_TextBox }

    $configuredPath = Get-ResolvedOutputPath

    $scanPathText = $syncHash.ResultsScanPath_TextBox.Text
    $scanPath = $null
    if (-not [string]::IsNullOrWhiteSpace($scanPathText)) {
        $scanPath = $scanPathText.Trim()
        if ($scanPath -eq ".") {
            $scanPath = [Environment]::GetFolderPath('MyDocuments')
        }
    }

    if ([string]::IsNullOrWhiteSpace($scanPath)) {
        $scanPath = $configuredPath
    }

    Write-DebugOutput -Message "Results scan: folder='$folderName', json='$jsonfilename', outPath='$configuredPath', scanPath='$scanPath'" -Source $MyInvocation.MyCommand -Level "Info"

    # Build a deduplicated list of paths to search:
    # 1. The explicit Results scan path override (if set)
    # 2. The configured/resolved OutPath (where ScubaGear wrote the last run)
    # 3. Shell-resolved Documents (handles OneDrive redirection automatically)
    # 4. Any additional OneDrive\Documents variants found under USERPROFILE
    # 5. $env:USERPROFILE home dir (PSGallery default CWD when run standalone)
    $searchPaths = [System.Collections.Generic.List[string]]::new()

    if (![string]::IsNullOrEmpty($scanPath) -and (Test-Path $scanPath)) {
        $searchPaths.Add($scanPath)
    }
    if (![string]::IsNullOrEmpty($configuredPath) -and (Test-Path $configuredPath) -and !$searchPaths.Contains($configuredPath)) {
        $searchPaths.Add($configuredPath)
    }

    $fallbacks = [System.Collections.Generic.List[string]]::new()
    # Shell-resolved Documents - always correct even with OneDrive redirection
    $fallbacks.Add([Environment]::GetFolderPath('MyDocuments'))
    # Enumerate every OneDrive-variant Documents folder (personal, corporate, named accounts)
    Get-Item (Join-Path $env:USERPROFILE 'OneDrive*') -ErrorAction SilentlyContinue | ForEach-Object {
        $fallbacks.Add((Join-Path $_.FullName 'Documents'))
    }
    # Plain local Documents and home dir as final catch-all
    $fallbacks.Add((Join-Path $env:USERPROFILE 'Documents'))
    $fallbacks.Add($env:USERPROFILE)

    foreach ($fb in $fallbacks) {
        if (![string]::IsNullOrEmpty($fb) -and (Test-Path $fb) -and !$searchPaths.Contains($fb)) {
            $searchPaths.Add($fb)
        }
    }

    # Convert to plain string[] - List[string] doesn't survive Start-Job serialization reliably
    $searchPathsArray = [string[]]$searchPaths

    Write-DebugOutput -Message "Results search paths ($($searchPathsArray.Count)): $($searchPathsArray -join '; ')" -Source $MyInvocation.MyCommand -Level "Info"

    $maximumResults = $syncHash.UIConfigs.MaximumResults

    # Join paths with | delimiter - [string[]] arrays don't survive Start-Job CLIXML serialization
    $searchPathsPiped = $searchPathsArray -join '|'

    # Start background job with simple script block
    $job = Start-Job -ScriptBlock {
        param([string]$SearchPathsPiped, [string]$FolderName, [string]$JsonFileName, [int]$MaxResults)

        # Split the delimiter-joined string back into an array
        $SearchPaths = $SearchPathsPiped -split '\|' | Where-Object { $_ -ne '' }

        # Diagnostic log accumulates inside the job and is returned alongside results
        $diagLog = [System.Collections.Generic.List[string]]::new()
        $diagLog.Add("Job started. FolderName='$FolderName' JsonFileName='$JsonFileName' MaxResults=$MaxResults SearchPaths=$($SearchPaths.Count)")

        function Get-ResultsReportTimeStamp {
            param([string]$Name, [string]$SearchPrefix)
            if ($Name -match "${SearchPrefix}_(\d{4})_(\d{2})_(\d{2})_(\d{2})_(\d{2})_(\d{2})") {
                $year = $matches[1]; $month = $matches[2]; $day = $matches[3]
                $hour = $matches[4]; $minute = $matches[5]; $second = $matches[6]
                $timestamp = Get-Date -Year $year -Month $month -Day $day -Hour $hour -Minute $minute -Second $second
                $tabHeader = "$year-$month-$day ($hour`:$minute`:$second)"

                $now = Get-Date
                $timespan = $now - $timestamp
                if ($timespan.TotalDays -lt 1) {
                    if ($timespan.TotalHours -lt 1) {
                        if ($timespan.TotalMinutes -lt 1) {
                            $relativeTime = "Just now"
                        } else {
                            $relativeTime = "$([math]::Floor($timespan.TotalMinutes)) minutes ago"
                        }
                    } else {
                        $relativeTime = "$([math]::Floor($timespan.TotalHours)) hours ago"
                    }
                } elseif ($timespan.TotalDays -lt 7) {
                    $relativeTime = "$([math]::Floor($timespan.TotalDays)) days ago"
                } else {
                    $relativeTime = $timestamp.ToString("MMM dd, yyyy")
                }

                return [PSCustomObject]@{
                    TabHeader = $tabHeader
                    TimeStamp = $timestamp
                    RelativeTime = $relativeTime
                }
            } else {
                return [PSCustomObject]@{
                    TabHeader = $Name
                    TimeStamp = "Unknown time"
                    RelativeTime = "Unknown time"
                }
            }
        }

        $resultsData = @()
        foreach ($searchPath in $SearchPaths) {
            $diagLog.Add("Checking path: '$searchPath' (exists=$(Test-Path $searchPath))")
            if (Test-Path $searchPath) {
                $filter = "${FolderName}_*"
                $diagLog.Add(" Searching with filter: '$filter'")
                $allDirs = Get-ChildItem -Path $searchPath -Directory -Filter $filter -ErrorAction SilentlyContinue
                $diagLog.Add(" Raw match count: $($allDirs.Count)")
                if ($allDirs.Count -gt 0) {
                    $diagLog.Add(" Matched folders: $($allDirs.Name -join ', ')")
                }

                $foldersInPath = $allDirs |
                    Sort-Object -Property LastWriteTime -Descending |
                    Select-Object -First $MaxResults |
                    ForEach-Object {
                        $jsonFile = Get-ChildItem -Path $_.FullName -Filter "${JsonFileName}*.json" -ErrorAction SilentlyContinue |
                            Select-Object -First 1 -ExpandProperty FullName
                        $diagLog.Add(" Folder '$($_.Name)': json='$jsonFile'")
                        $timeNameInfo = Get-ResultsReportTimeStamp -SearchPrefix $FolderName -Name $_.BaseName
                        [PSCustomObject]@{
                            ReportName = $_.BaseName
                            TabHeader = $timeNameInfo.TabHeader
                            ReportTimeStamp = $timeNameInfo.TimeStamp
                            RelativeTime = $timeNameInfo.RelativeTime
                            ReportPath = $_.FullName
                            JsonResultsPath = $jsonFile
                        }
                    }
                $resultsData += $foldersInPath
            }
        }

        $diagLog.Add("Job complete. Total results: $($resultsData.Count)")
        return [PSCustomObject]@{ Results = $resultsData; DiagLog = $diagLog }

    } -ArgumentList $searchPathsPiped, $folderName, $jsonfilename, $maximumResults

    # Use a timer to poll job status instead of events
    $timer = New-Object System.Windows.Threading.DispatcherTimer
    $timer.Interval = [TimeSpan]::FromMilliseconds(500)  # Check every 500ms

    $timer.Add_Tick({
        if ($job.State -eq 'Completed' -or $job.State -eq 'Failed' -or $job.State -eq 'Stopped') {
            $timer.Stop()

            try {
                if ($job.State -eq 'Completed') {
                    $jobOutput = Receive-Job -Job $job -ErrorAction Stop

                    # Job now returns a PSCustomObject with Results + DiagLog
                    if ($jobOutput -is [PSCustomObject] -and $jobOutput.PSObject.Properties['DiagLog']) {
                        foreach ($line in $jobOutput.DiagLog) {
                            Write-DebugOutput -Message "[Job] $line" -Source "Update-ResultsTab" -Level "Info"
                        }
                        $resultsData = @($jobOutput.Results)
                    } else {
                        # Fallback for legacy format
                        $resultsData = @($jobOutput)
                    }

                    Write-DebugOutput -Message "Job completed. Results count: $($resultsData.Count)" -Source "Update-ResultsTab" -Level "Info"

                    # Hide progress indicator
                    $syncHash.ResultsScanProgress.Visibility = "Collapsed"
                    $syncHash.ResultsScanInProgress = $false

                    if ($resultsData -and $resultsData.Count -gt 0) {
                        # Store results
                        $syncHash.ResultsJsonData = $resultsData

                        # Hide empty tab and create result tabs
                        $syncHash.ResultsEmptyTab.Visibility = "Collapsed"

                        # Create tabs for each result folder
                        foreach ($Report in $syncHash.ResultsJsonData) {
                            New-ResultsReportTab -Report $Report
                        }

                        # Select the most recent tab (first in sorted list)
                        if ($syncHash.ResultsTabControl.Items.Count -gt 1) {
                            $syncHash.ResultsTabControl.SelectedIndex = 1  # Skip ResultsEmptyTab at index 0
                        }

                        Update-ResultsCount $syncHash.ResultsJsonData.Count
                        Write-DebugOutput -Message "Successfully loaded $($syncHash.ResultsJsonData.Count) results" -Source "Update-ResultsTab" -Level "Info"

                    } else {
                        # Show empty results
                        $syncHash.ResultsEmptyTab.Visibility = "Visible"
                        $syncHash.ResultsTabControl.SelectedItem = $syncHash.ResultsEmptyTab
                        Update-ResultsCount 0

                        # Reset empty tab content to default "no results" message
                        $emptyContent = @"
<ScrollViewer xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        VerticalScrollBarVisibility="Auto"
        HorizontalScrollBarVisibility="Auto">
    <StackPanel Margin="20" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="No ScubaGear reports found"
                   FontSize="16"
                   TextAlignment="Center"
                   Foreground="{DynamicResource MutedTextBrush}"
                   Margin="10"/>
        <TextBlock Text="Run ScubaGear to generate reports that will appear here."
                   FontSize="12"
                   TextAlignment="Center"
                   Foreground="{DynamicResource MutedTextBrush}"
                   Margin="10"/>
    </StackPanel>
</ScrollViewer>
"@

                        try {
                            $emptyControl = [Windows.Markup.XamlReader]::Parse($emptyContent)
                            $syncHash.ResultsEmptyTab.Content = $emptyControl
                        } catch {
                            Write-DebugOutput -Message "Error creating empty content: $($_.Exception.Message)" -Source "Update-ResultsTab" -Level "Warning"
                        }

                        Write-DebugOutput -Message "No results found in search paths" -Source "Update-ResultsTab" -Level "Info"
                    }

                } else {
                    # Job failed or stopped
                    $jobErrors = Receive-Job -Job $job -ErrorAction SilentlyContinue
                    $errorMessage = if ($jobErrors) { $jobErrors -join "; " } else { "Job failed with state: $($job.State)" }

                    Write-DebugOutput -Message "Job failed with state: $($job.State). Error: $errorMessage" -Source "Update-ResultsTab" -Level "Error"

                    # Hide progress indicator
                    $syncHash.ResultsScanProgress.Visibility = "Collapsed"
                    $syncHash.ResultsScanInProgress = $false

                    # Show error state
                    $syncHash.ResultsEmptyTab.Visibility = "Visible"
                    $syncHash.ResultsTabControl.SelectedItem = $syncHash.ResultsEmptyTab
                    Update-ResultsCount 0

                    $errorContent = @"
<ScrollViewer xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        VerticalScrollBarVisibility="Auto"
        HorizontalScrollBarVisibility="Auto">
    <StackPanel Margin="20" HorizontalAlignment="Center" VerticalAlignment="Center">
        <TextBlock Text="Error scanning for reports"
                   FontSize="16"
                   TextAlignment="Center"
                   Foreground="Red"
                   Margin="10"/>
        <TextBlock Text="$errorMessage"
                   FontSize="12"
                   TextAlignment="Center"
                   Foreground="{DynamicResource MutedTextBrush}"
                   TextWrapping="Wrap"
                   MaxWidth="400"
                   Margin="10"/>
    </StackPanel>
</ScrollViewer>
"@

                    try {
                        $errorControl = [Windows.Markup.XamlReader]::Parse($errorContent)
                        $syncHash.ResultsEmptyTab.Content = $errorControl
                    } catch {
                        Write-DebugOutput -Message "Error creating error content: $($_.Exception.Message)" -Source "Update-ResultsTab" -Level "Warning"
                    }
                }

            } catch {
                Write-DebugOutput -Message "Error processing job results: $($_.Exception.Message)" -Source "Update-ResultsTab" -Level "Error"

                # Hide progress indicator
                $syncHash.ResultsScanProgress.Visibility = "Collapsed"
                $syncHash.ResultsScanInProgress = $false

                # Show error state
                $syncHash.ResultsEmptyTab.Visibility = "Visible"
                $syncHash.ResultsTabControl.SelectedItem = $syncHash.ResultsEmptyTab
                Update-ResultsCount 0
            } finally {
                # Clean up the job
                Remove-Job -Job $job -Force -ErrorAction SilentlyContinue
            }
        } else {
            # Job still running - update status if needed
            $syncHash.ResultsScanStatus.Text = "Scanning for ScubaGear reports..."
        }
    }.GetNewClosure())

    # Start the timer
    $timer.Start()

    Write-DebugOutput -Message "Background job started for results scanning (Job ID: $($job.Id))" -Source $MyInvocation.MyCommand -Level "Info"
}

Function New-ResultsReportTab {
    <#
    .SYNOPSIS
    Creates a native WPF tab displaying ScubaGear results from ScubaResults JSON data.
    #>

    param(
        [Parameter(Mandatory)]
        [PSCustomObject]$Report
    )

    # Create new TabItem
    $newTab = New-Object System.Windows.Controls.TabItem

    # Create folder-style header using text prefix
    $newTab.Header = "Report: $($Report.TabHeader)"

    $newTab.Name = "Result_$($Report.ReportName -Replace '\W+', '_')"

    # Load content immediately instead of lazy loading
    try {
        $jsonResultsPath = [string]$Report.JsonResultsPath
        if (-not [string]::IsNullOrWhiteSpace($jsonResultsPath) -and (Test-Path -LiteralPath $jsonResultsPath)) {
            $jsonContent = Get-Content -LiteralPath $jsonResultsPath -Raw
            $scubaData = $jsonContent | ConvertFrom-Json

            # Validate the data
            $isValidData = Test-ResultsDataValidity -ScubaData $scubaData -ReportPath $Report.ReportPath

            if ($isValidData) {
                $relativeTimeString = $Report.RelativeTime + " (" + $Report.ReportTimeStamp + ")"
                $reportContent = New-ResultsContent -ScubaData $scubaData -ReportPath $Report.ReportPath -RelativeTime $relativeTimeString
                $newTab.Content = $reportContent
            } else {
                $errorTab = New-ResultsNoDataTab -ReportPath $Report.ReportPath -Message "Report data appears to be corrupted or incomplete.`n`nThe ScubaResults JSON file contains invalid summary data.`n`nThis may indicate the ScubaGear scan was interrupted or encountered errors."
                $newTab.Content = $errorTab
            }
        } else {
            $errorTab = New-ResultsNoDataTab -ReportPath $Report.ReportPath -Message "JSON file not found or unavailable: $jsonResultsPath"
            $newTab.Content = $errorTab
        }
    } catch {
        Write-DebugOutput -Message "Error loading report content: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
        $errorTab = New-ResultsNoDataTab -ReportPath $Report.ReportPath -Message "Error loading report content: $($_.Exception.Message)"
        $newTab.Content = $errorTab
    }

    # Add to main tab control
    $syncHash.ResultsTabControl.Items.Add($newTab)
    Write-DebugOutput -Message "Added report tab with immediate content loading: $($Report.TabHeader)" -Source $MyInvocation.MyCommand -Level "Info"
}

Function Test-ResultsDataValidity {
    <#
    .SYNOPSIS
    Validates ScubaGear data to ensure it's not corrupted or malformed.
    .DESCRIPTION
    Checks for common issues like invalid Summary data where all values are "9" or other placeholder values.
    #>

    param(
        [Parameter(Mandatory)]
        [PSCustomObject]$ScubaData,
        [string]$ReportPath
    )

    try {
        Write-DebugOutput -Message "Validating ScubaData for report: $ReportPath" -Source $MyInvocation.MyCommand -Level "Debug"
        Write-DebugOutput -Message "ScubaData type: $($ScubaData.GetType().FullName)" -Source $MyInvocation.MyCommand -Level "Debug"

        # Check if Summary exists
        if (-not $ScubaData.Summary) {
            Write-DebugOutput -Message "No Summary section found in ScubaData" -Source $MyInvocation.MyCommand -Level "Error"
            return $false
        }

        # Check if Results exists
        if (-not $ScubaData.Results) {
            Write-DebugOutput -Message "No Results section found in ScubaData" -Source $MyInvocation.MyCommand -Level "Error"
            return $false
        }

        # Check for invalid Summary data (common issue: all values are "9")
        $summaryProducts = $ScubaData.Summary | Get-Member -MemberType NoteProperty
        if ($summaryProducts -and $summaryProducts.Count -gt 0) {
            $invalidDataCount = 0
            $totalProductsChecked = 0

            foreach ($productName in $summaryProducts.Name) {
                $productSummary = $ScubaData.Summary.$productName
                $totalProductsChecked++

                # Check if all summary values are the same invalid placeholder (like "9")
                $passes = [string]$productSummary.Passes
                $warnings = [string]$productSummary.Warnings
                $failures = [string]$productSummary.Failures
                $manual = [string]$productSummary.Manual
                $errors = [string]$productSummary.Errors

                # Flag as invalid if all values are the same non-zero number (common corruption pattern)
                if ($passes -eq $warnings -and $warnings -eq $failures -and $failures -eq $manual -and $manual -eq $errors -and $passes -ne "0") {
                    Write-DebugOutput -Message "Invalid summary data detected for product $productName`: all values are '$passes' (likely corrupted scan result)" -Source $MyInvocation.MyCommand -Level "Error"
                    $invalidDataCount++
                }

                # Special check for the common "9" corruption pattern
                if ($passes -eq "9" -and $warnings -eq "9" -and $failures -eq "9" -and $manual -eq "9" -and $errors -eq "9") {
                    Write-DebugOutput -Message "Detected '9' corruption pattern for product $productName - this indicates an incomplete or failed ScubaGear scan" -Source $MyInvocation.MyCommand -Level "Error"
                    $invalidDataCount++
                }

                # Also check for obviously invalid values (non-numeric or negative)
                $allValues = @($passes, $warnings, $failures, $manual, $errors)
                foreach ($value in $allValues) {
                    if (-not [int]::TryParse($value, [ref]$null) -or [int]$value -lt 0) {
                        Write-DebugOutput -Message "Invalid summary value detected for product $productName`: '$value' is not a valid non-negative integer" -Source $MyInvocation.MyCommand -Level "Error"
                        $invalidDataCount++
                        break
                    }
                }
            }

            # If more than half the products have invalid data, consider the whole report invalid
            if ($invalidDataCount -gt ($totalProductsChecked / 2)) {
                Write-DebugOutput -Message "Report validation failed: $invalidDataCount of $totalProductsChecked products have invalid summary data" -Source $MyInvocation.MyCommand -Level "Error"
                return $false
            }
        } else {
            Write-DebugOutput -Message "No products found in Summary section" -Source $MyInvocation.MyCommand -Level "Error"
            return $false
        }

        # Check if Results has valid structure
        $resultProducts = $ScubaData.Results | Get-Member -MemberType NoteProperty
        if (-not $resultProducts -or $resultProducts.Count -eq 0) {
            Write-DebugOutput -Message "No products found in Results section" -Source $MyInvocation.MyCommand -Level "Error"
            return $false
        }

        # Check if Results actually contains meaningful data (not just empty arrays)
        $hasValidResults = $false
        foreach ($productName in $resultProducts.Name) {
            $productResults = $ScubaData.Results.$productName
            if ($productResults -and $productResults.Count -gt 0) {
                # Check if any result has meaningful data (Groups with Controls)
                foreach ($group in $productResults) {
                    if ($group.Controls -and $group.Controls.Count -gt 0) {
                        $hasValidResults = $true
                        break
                    }
                }
                if ($hasValidResults) { break }
            }
        }

        if (-not $hasValidResults) {
            Write-DebugOutput -Message "Results section exists but contains no meaningful compliance data" -Source $MyInvocation.MyCommand -Level "Error"
            return $false
        }

        # Basic validation passed
        Write-DebugOutput -Message "ScubaData validation passed for report: $ReportPath" -Source $MyInvocation.MyCommand -Level "Debug"
        return $true

    } catch {
        Write-DebugOutput -Message "Error during ScubaData validation: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
        return $false
    }
}

Function New-ResultsContent {
    <#
    .SYNOPSIS
    Creates the content for a results tab using the provided ScubaData.
    Optimized for faster loading with simplified product tabs.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "ReportPath")]
    param(
        [PSCustomObject[]]$ScubaData,
        [string]$ReportPath,
        [string]$RelativeTime
    )

    Write-DebugOutput -Message "New-ResultsContent called for report: $ReportPath" -Source $MyInvocation.MyCommand -Level "Debug"

    # Safety check - validate data before processing
    if (-not $ScubaData) {
        return New-ResultsNoDataTab -ReportPath $ReportPath -Message "ScubaData is null - returning error tab"
    }

    if (-not $ScubaData.Summary -or -not $ScubaData.Results) {
        return New-ResultsNoDataTab -ReportPath $ReportPath -Message "ScubaData missing Summary or Results sections - returning error tab"
    }

    # Create simplified XAML template (without heavy product tabs initially)
    $xamlTemplate = @"
<ScrollViewer xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        VerticalScrollBarVisibility="Auto"
        HorizontalScrollBarVisibility="Auto">
    <StackPanel Margin="8">
 
        <!-- Tenant Header Card -->
        <Border Style="{DynamicResource Card}" Margin="0,0,0,8">
            <Grid Margin="12">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
 
                <!-- Left side - Tenant info -->
                <StackPanel Grid.Column="0">
                    <TextBlock Text="ScubaGear Assessment Report" FontSize="18" FontWeight="Bold" Margin="0,0,0,6"/>
 
                    <!-- Tenant Info Table -->
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="160"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                            <RowDefinition Height="Auto"/>
                        </Grid.RowDefinitions>
 
                        <TextBlock Grid.Row="0" Grid.Column="0" Text="Tenant Display Name" FontWeight="SemiBold" Margin="0,2"/>
                        <TextBlock Grid.Row="0" Grid.Column="1" Name="TenantDisplayName" Text="{DISPLAY_NAME}" Margin="6,2,0,2"/>
 
                        <TextBlock Grid.Row="1" Grid.Column="0" Text="Tenant Domain Name" FontWeight="SemiBold" Margin="0,2"/>
                        <TextBlock Grid.Row="1" Grid.Column="1" Name="TenantDomainName" Text="{DOMAIN_NAME}" Margin="6,2,0,2"/>
 
                        <TextBlock Grid.Row="2" Grid.Column="0" Text="Tenant ID" FontWeight="SemiBold" Margin="0,2"/>
                        <TextBlock Grid.Row="2" Grid.Column="1" Name="TenantId" Text="{TENANT_ID}" Margin="6,2,0,2"/>
 
                        <TextBlock Grid.Row="3" Grid.Column="0" Text="Report Date" FontWeight="SemiBold" Margin="0,2"/>
                        <TextBlock Grid.Row="3" Grid.Column="1" Name="ReportDate" Text="{REPORT_DATE}" Margin="6,2,0,2"/>
 
                        <TextBlock Grid.Row="4" Grid.Column="0" Text="Scuba Version" FontWeight="SemiBold" Margin="0,2"/>
                        <TextBlock Grid.Row="4" Grid.Column="1" Name="ScubaVersion" Text="{SCUBA_VERSION}" Margin="6,2,0,2"/>
                    </Grid>
                </StackPanel>
 
                <!-- Right side - Action buttons -->
                <StackPanel Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top">
                    <Button Name="OpenHtmlBtn" Style="{DynamicResource PrimaryButton}" Content="Open Full HTML Report"
                            Margin="0,0,0,6" Width="190" Height="32"/>
                    <Button Name="OpenFolderBtn" Style="{DynamicResource SecondaryButton}" Content="Open Report Folder"
                            Width="190" Margin="0,0,0,8" Height="32"/>
                    <Button Name="OpenYamlBtn" Style="{DynamicResource SecondaryButton}" Content="View Configuration"
                            Width="190" Margin="0,0,0,8" Height="32" Visibility="Collapsed"/>
 
                    <StackPanel VerticalAlignment="bottom">
                        <TextBlock Text="Report UUID:" FontSize="10" Foreground="LightGray"/>
                        <TextBlock Text="{REPORT_UUID}" FontSize="10" FontFamily="Consolas" Margin="0,0,0,0" Foreground="Gray"/>
                    </StackPanel>
                </StackPanel>
            </Grid>
        </Border>
 
        <!-- Summary Section Only (Fast Loading) -->
        <Border Style="{DynamicResource Card}" Margin="0,0,0,8">
            <StackPanel Margin="12">
                <TextBlock Text="Baseline Conformance Summary" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,8"/>
                <ItemsControl Name="SummaryItemsControl">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Border Style="{DynamicResource Card}" Margin="0,0,0,4">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="280"/>
                                        <ColumnDefinition Width="*"/>
                                    </Grid.ColumnDefinitions>
 
                                    <TextBlock Grid.Column="0" FontSize="12" Text="{Binding Product}" FontWeight="SemiBold"
                                            Foreground="Blue" TextDecorations="Underline" Cursor="Hand"
                                            VerticalAlignment="Center"/>
 
                                    <ItemsControl Grid.Column="1" ItemsSource="{Binding StatusItems}"
                                                VerticalAlignment="Center">
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <WrapPanel Orientation="Horizontal"/>
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <Border Background="{Binding Color}" CornerRadius="8" Padding="6,2" Margin="0,0,4,2">
                                                    <TextBlock Text="{Binding Text}" Foreground="{Binding TextColor}"
                                                            FontSize="12" FontWeight="SemiBold"/>
                                                </Border>
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>
                                    </ItemsControl>
                                </Grid>
                            </Border>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </Border>
 
        <!-- Product Detail Tabs -->
        <Border Style="{DynamicResource Card}" Margin="0,0,0,8">
            <StackPanel Margin="12">
                <TextBlock Text="Detailed Product Results" FontSize="16" FontWeight="SemiBold" Margin="0,0,0,8"/>
 
                <TabControl Name="ProductTabControl" Margin="0,8,0,0">
                    {PRODUCT_TABS}
                </TabControl>
            </StackPanel>
        </Border>
 
    </StackPanel>
</ScrollViewer>
"@


    # Process the data and replace placeholders
    $processedXaml = $xamlTemplate

    # Replace tenant information placeholders with proper XML escaping
    function XmlEscape([string]$s) {
        if ($null -eq $s) { return "" }
        $s = [string]$s
        # Remove control characters that are illegal in XML
        $s = $s -replace '[\x00-\x08\x0B\x0C\x0E-\x1F]', ''
        # Replace special XML characters with entities
        $s = $s -replace '&', '&amp;'
        $s = $s -replace '<', '&lt;'
        $s = $s -replace '>', '&gt;'
        $s = $s -replace '"', '&quot;'
        $s = $s -replace "'", '&apos;'
        return $s
    }

    $displayName = XmlEscape([string]$ScubaData.MetaData.DisplayName)
    $domainName = XmlEscape([string]$ScubaData.MetaData.DomainName)
    $tenantId = XmlEscape([string]$ScubaData.MetaData.TenantId)
    $scubaVersion = XmlEscape($(if ($ScubaData.MetaData.ToolVersion) { [string]$ScubaData.MetaData.ToolVersion } else { "Unknown" }))
    $reportUuid = XmlEscape($(if ($ScubaData.MetaData.ReportUUID) { [string]$ScubaData.MetaData.ReportUUID } else { "Unknown" }))

    $processedXaml = $processedXaml -replace '{DISPLAY_NAME}', $displayName
    $processedXaml = $processedXaml -replace '{DOMAIN_NAME}', $domainName
    $processedXaml = $processedXaml -replace '{TENANT_ID}', $tenantId
    $processedXaml = $processedXaml -replace '{REPORT_DATE}', (XmlEscape([string]$RelativeTime))
    $processedXaml = $processedXaml -replace '{SCUBA_VERSION}', $scubaVersion
    $processedXaml = $processedXaml -replace '{REPORT_UUID}', $reportUuid

    # Generate product tabs
    $productTabsXaml = ""
    foreach ($productAbbr in ($ScubaData.Summary | Get-Member -MemberType NoteProperty).Name) {
        # Try to get display name from UIConfigs first, fallback to switch statement
        $productDisplayName = $syncHash.UIConfigs.products | Where-Object { $_.Id -eq $productAbbr } | Select-Object -ExpandProperty Name

        # Escape the product display name for XML
        $safeProductDisplayName = $productDisplayName -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'

        # Generate groups content for this product
        $groupsContent = ""
        if ($ScubaData.Results.$productAbbr) {
            foreach ($group in $ScubaData.Results.$productAbbr) {
                $groupNumber = if ($group.GroupNumber) { [string]$group.GroupNumber } else { "" }
                $groupName = if ($group.GroupName) { [string]$group.GroupName } else { "Unknown Group" }
                $groupHeader = if ($groupNumber) { "$groupNumber. $groupName" } else { $groupName }

                # Escape XML characters
                $safeGroupHeader = $groupHeader -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'

                # Generate controls for this group
                $controlsContent = ""
                if ($group.Controls) {
                    foreach ($control in $group.Controls) {
                        $controlId = if ($control."Control ID") { [string]$control."Control ID" } else { "Unknown" }
                        $result = if ($control.Result) { [string]$control.Result } else { "N/A" }
                        $criticality = if ($control.Criticality) { [string]$control.Criticality } else { "" }
                        $requirement = if ($control.Requirement) { [string]$control.Requirement } else { "" }

                        # Truncate requirement if too long
                        if ($requirement.Length -gt 100) {
                            $requirement = $requirement.Substring(0, 100) + "..."
                        }

                        # Color based on result
                        $resultColor = switch ($result) {
                            "Pass" { "#28a745" }
                            "Fail" { "#dc3545" }
                            "Warning" { "#ffc107" }
                            "Manual" { "#6f42c1" }
                            "Error" { "#fd7e14" }
                            default { "#6c757d" }
                        }

                # Escape XML characters more thoroughly
                $safeControlId = $controlId -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'
                $safeResult = $result -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'
                $safeCriticality = $criticality -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'
                $safeRequirement = $requirement -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'

                        $controlsContent += @"
<Border BorderBrush="LightGray" BorderThickness="1" Margin="0,2" Padding="8" CornerRadius="3">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="120"/>
            <ColumnDefinition Width="60"/>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock Grid.Column="0" Text="$safeControlId" FontWeight="SemiBold" VerticalAlignment="Top"/>
        <TextBlock Grid.Column="1" Text="$safeResult" Foreground="$resultColor" VerticalAlignment="Top"/>
        <TextBlock Grid.Column="2" Text="$safeCriticality" VerticalAlignment="Top"/>
        <TextBlock Grid.Column="3" Text="$safeRequirement" TextWrapping="Wrap" VerticalAlignment="Top"/>
    </Grid>
</Border>
"@

                    }
                }

                if ([string]::IsNullOrWhiteSpace($controlsContent)) {
                    $controlsContent = @"
<TextBlock Text="No control data available" FontStyle="Italic" Foreground="Gray" Margin="8"/>
"@

                }

                $groupsContent += @"
<Expander Header="$safeGroupHeader" Margin="0,0,0,8" IsExpanded="False">
    <StackPanel Margin="16,8,8,8">
        $controlsContent
    </StackPanel>
</Expander>
"@

            }
        }

        if ([string]::IsNullOrWhiteSpace($groupsContent)) {
            $groupsContent = @"
<TextBlock Text="No group data available for this product" FontStyle="Italic" Foreground="Gray" Margin="8"/>
"@

        }

        $productTabsXaml += @"
<TabItem Header="$safeProductDisplayName">
    <ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Margin="8">
        <StackPanel>
            $groupsContent
        </StackPanel>
    </ScrollViewer>
</TabItem>
"@

    }

    # Replace the product tabs placeholder
    $processedXaml = $processedXaml -replace '\{PRODUCT_TABS\}', $productTabsXaml

    # Sanitize XAML before parsing to prevent XML entity errors
    # Remove any illegal control characters
    if ($processedXaml -match '[\x00-\x08\x0B\x0C\x0E-\x1F]') {
        Write-DebugOutput -Message "Warning: Removing illegal control characters from XAML" -Source $MyInvocation.MyCommand -Level "Warning"
        $processedXaml = $processedXaml -replace '[\x00-\x08\x0B\x0C\x0E-\x1F]', ''
    }

    # Parse the XAML into a WPF control
    try {
        # Add debug logging before parsing
        Write-DebugOutput -Message "About to parse XAML of length: $($processedXaml.Length)" -Source $MyInvocation.MyCommand -Level "Debug"

        try {
            $reportControl = [Windows.Markup.XamlReader]::Parse($processedXaml)
        } catch [System.Xml.XmlException] {
            # Provide detailed context to help diagnose the malformed XML
            $xmlEx = $_.Exception
            Write-DebugOutput -Message "XmlException parsing XAML: $($xmlEx.Message) at Line $($xmlEx.LineNumber), Position $($xmlEx.LinePosition)" -Source $MyInvocation.MyCommand -Level "Error"

            # Dump the XAML to a temp file for debugging
            $dumpPath = Join-Path ([IO.Path]::GetTempPath()) ("ScubaGear_FailingReport_{0}.xaml" -f ([Guid]::NewGuid().ToString()))
            try {
                Set-Content -Path $dumpPath -Value $processedXaml -Encoding UTF8 -ErrorAction Stop
                Write-DebugOutput -Message "Dumped failing XAML to: $dumpPath" -Source $MyInvocation.MyCommand -Level "Error"
            } catch {
                Write-DebugOutput -Message "Failed to write XAML dump: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
            }

            # Try a conservative sanitization: escape stray '&' characters and retry once
            $sanitized = $processedXaml -replace '&(?!amp;|lt;|gt;|quot;|apos;|#\d+;)', '&amp;'
            Write-DebugOutput -Message "Attempting to parse sanitized XAML (escaped stray ampersands)" -Source $MyInvocation.MyCommand -Level "Warning"
            try {
                $reportControl = [Windows.Markup.XamlReader]::Parse($sanitized)
                Write-DebugOutput -Message "Successfully parsed sanitized XAML after fallback" -Source $MyInvocation.MyCommand -Level "Debug"
            } catch {
                Write-DebugOutput -Message "Sanitized XAML parse failed: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
                throw $xmlEx # Rethrow the original XML exception with context
            }
        }
        Write-DebugOutput -Message "Successfully parsed simplified XAML report content" -Source $MyInvocation.MyCommand -Level "Debug"

        # Set up button event handlers
        $openHtmlBtn = $reportControl.FindName("OpenHtmlBtn")
        $openFolderBtn = $reportControl.FindName("OpenFolderBtn")
        $openYamlBtn = $reportControl.FindName("OpenYamlBtn")

        if ($openHtmlBtn) {
            $openHtmlBtn.Add_Click({
                #minimize main window
                $syncHash.Window.WindowState = [System.Windows.WindowState]::Minimized

                $htmlFile = Get-ChildItem -Path $ReportPath -Name "*.html" | Select-Object -First 1
                if ($htmlFile) {
                    $htmlPath = Join-Path $ReportPath $htmlFile
                    Start-Process $htmlPath
                } else {
                    $syncHash.ShowMessageBox.Invoke($syncHash.UIConfigs.localeErrorMessages.HtmlReportNotFound, $syncHash.UIConfigs.localeTitles.ReportNotFound, [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Information)
                }
            }.GetNewClosure())
        }

        if ($openFolderBtn) {
            $openFolderBtn.Add_Click({
                Start-Process "explorer.exe" -ArgumentList $ReportPath
            }.GetNewClosure())
        }

        # YAML Configuration button
        if ($openYamlBtn) {
            $yamlConfigPath = Join-Path $ReportPath "ScubaGearConfiguration.yaml"
            if (Test-Path $yamlConfigPath) {
                $openYamlBtn.Visibility = "Visible"
                $openYamlBtn.Add_Click({
                    try {
                        Show-ConfigurationViewer -ConfigFilePath $yamlConfigPath
                    } catch {
                        $syncHash.ShowMessageBox.Invoke($syncHash.UIConfigs.localeErrorMessages.ConfigurationViewerError -f $_.Exception.Message, $syncHash.UIConfigs.localeTitles.ConfigurationViewerError, [System.Windows.MessageBoxButton]::OK, [System.Windows.MessageBoxImage]::Error)
                    }
                }.GetNewClosure())
            }
        }

        # Populate summary data (this is fast)
        $summaryItems = @()
        foreach ($productAbbr in ($ScubaData.Summary | Get-Member -MemberType NoteProperty).Name) {
            $productData = $ScubaData.Summary.$productAbbr

            $displayName = $syncHash.UIConfigs.products | Where-Object { $_.Id -eq $productAbbr } | Select-Object -ExpandProperty Name

            # Build status badges quickly
            $statusItems = @()
            $passes = [string]$productData.Passes
            $warnings = [string]$productData.Warnings
            $failures = [string]$productData.Failures
            $manual = [string]$productData.Manual
            $errors = [string]$productData.Errors

            if ([int]$passes -gt 0) {
                $statusItems += [PSCustomObject]@{ Text = "PASS: $passes pass$(if([int]$passes -gt 1){'es'})"; Color = "#28a745"; TextColor = "White" }
            }
            if ([int]$warnings -gt 0) {
                $statusItems += [PSCustomObject]@{ Text = "WARN: $warnings warning$(if([int]$warnings -gt 1){'s'})"; Color = "#ffc107"; TextColor = "#212529" }
            }
            if ([int]$failures -gt 0) {
                $statusItems += [PSCustomObject]@{ Text = "FAIL: $failures failure$(if([int]$failures -gt 1){'s'})"; Color = "#dc3545"; TextColor = "White" }
            }
            if ([int]$manual -gt 0) {
                $statusItems += [PSCustomObject]@{ Text = "MANUAL: $manual manual check$(if([int]$manual -gt 1){'s'})"; Color = "#6f42c1"; TextColor = "White" }
            }
            if ([int]$errors -gt 0) {
                $statusItems += [PSCustomObject]@{ Text = "ERROR: $errors error$(if([int]$errors -gt 1){'s'})"; Color = "#fd7e14"; TextColor = "White" }
            }

            $summaryItems += [PSCustomObject]@{ Product = $displayName; ProductAbbr = $productAbbr; StatusItems = $statusItems }
        }

        $summaryItemsControl = $reportControl.FindName("SummaryItemsControl")
        if ($summaryItemsControl) {
            $summaryItemsControl.ItemsSource = $summaryItems

            # Wire up click handlers on the product-name TextBlocks after the items are rendered
            $productTabControl = $reportControl.FindName("ProductTabControl")
            $summaryItemsControl.Add_Loaded({
                # Walk each generated item container and find the blue product TextBlock
                foreach ($item in $summaryItemsControl.Items) {
                    $container = $summaryItemsControl.ItemContainerGenerator.ContainerFromItem($item)
                    if (-not $container) { continue }
                    # FindVisualChild helper: first TextBlock with TextDecorations
                    $queue = [System.Collections.Generic.Queue[System.Windows.DependencyObject]]::new()
                    $queue.Enqueue($container)
                    while ($queue.Count -gt 0) {
                        $node = $queue.Dequeue()
                        if ($node -is [System.Windows.Controls.TextBlock] -and
                            $node.TextDecorations -and $node.TextDecorations.Count -gt 0) {
                            $capturedItem  = $item
                            $capturedTabs  = $productTabControl
                            $node.Add_MouseLeftButtonUp({
                                if (-not $capturedTabs) { return }
                                for ($i = 0; $i -lt $capturedTabs.Items.Count; $i++) {
                                    $tab = $capturedTabs.Items[$i]
                                    if ($tab.Header -eq $capturedItem.Product) {
                                        $capturedTabs.SelectedIndex = $i
                                        $tab.BringIntoView()
                                        break
                                    }
                                }
                            }.GetNewClosure())
                            break
                        }
                        $childCount = [System.Windows.Media.VisualTreeHelper]::GetChildrenCount($node)
                        for ($ci = 0; $ci -lt $childCount; $ci++) {
                            $queue.Enqueue([System.Windows.Media.VisualTreeHelper]::GetChild($node, $ci))
                        }
                    }
                }
            }.GetNewClosure())
        }

        return $reportControl

    } catch {
        Write-DebugOutput -Message "Error parsing XAML: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"

        # Save the processed XAML to a temp file for debugging
        $dumpPath = $null
        try {
            $dumpPath = Join-Path ([IO.Path]::GetTempPath()) ("ScubaGear_FailingReport_{0}.xaml" -f ([Guid]::NewGuid().ToString()))
            Set-Content -Path $dumpPath -Value $processedXaml -Encoding UTF8 -ErrorAction Stop
            Write-DebugOutput -Message "Dumped failing XAML to: $dumpPath" -Source $MyInvocation.MyCommand -Level "Error"
        } catch {
            Write-DebugOutput -Message "Failed to write XAML dump: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
        }

        $errorMessage = "Error creating simplified report: $($_.Exception.Message)"
        if ($dumpPath) {
            $errorMessage += "`n`nFailing XAML saved to: $dumpPath"
        }

        return New-ResultsNoDataTab -ReportPath $ReportPath -Message $errorMessage
    }
}

Function New-ResultsGroupExpanderXaml {
    [OutputType([System.Windows.UIElement])]
    param(
        [Parameter(Mandatory=$true)]
        $GroupData
    )

    try {
        # Simple validation
        if (-not $GroupData) {
            Write-DebugOutput -Message "GroupData is null, returning null" -Source $MyInvocation.MyCommand -Level "Error"
            return $null
        }

        # Create XAML for group expander
        $expanderXaml = @"
<Expander xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Header="&#x1F512; {GROUP_HEADER}"
        Margin="0,0,0,8"
        IsExpanded="False">
    <StackPanel Margin="16,8,8,8">
    {CONTROLS_CONTENT}
    </StackPanel>
</Expander>
"@


        # Build header - simple string conversion with fallbacks
        $groupNumber = if ($GroupData.GroupNumber) { [string]$GroupData.GroupNumber } else { "" }
        $groupName = if ($GroupData.GroupName) { [string]$GroupData.GroupName } else { "Unknown Group" }

        $header = if ($groupNumber) { "$groupNumber. $groupName" } else { $groupName }

        # Escape special XML characters in header
        $safeHeader = $header -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'

        # Build controls content
        $controlsContent = ""
        if ($GroupData.Controls) {
            foreach ($control in $GroupData.Controls) {
                try {
                    # Safe string extraction with simple fallbacks
                    $controlId = if ($control."Control ID") { [string]$control."Control ID" } else { "Unknown" }
                    $result = if ($control.Result) { [string]$control.Result } else { "N/A" }
                    $criticality = if ($control.Criticality) { [string]$control.Criticality } else { "" }
                    $requirement = if ($control.Requirement) { [string]$control.Requirement } else { "" }

                    # Truncate requirement
                    $truncatedRequirement = if ($requirement.Length -gt 100) {
                        $requirement.Substring(0, 100) + "..."
                    } else {
                        $requirement
                    }

                    # Escape special XML characters in all text
                    $safeControlId = $controlId -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'
                    $safeResult = $result -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'
                    $safeCriticality = $criticality -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'
                    $safeRequirement = $truncatedRequirement -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'

                    # Color for result
                    $resultColor = switch ($result) {
                        "Pass" { "Green" }
                        "Fail" { "Red" }
                        "Warning" { "Orange" }
                        "Error" { "Purple" }
                        "N/A" { "Gray" }
                        "Manual" { "Blue" }
                        default { "Black" }
                    }

                    $controlsContent += @"
<Border BorderBrush="LightGray" BorderThickness="1" Margin="0,2" Padding="8" CornerRadius="3">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="120"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="80"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
 
        <TextBlock Grid.Column="0" Text="$safeControlId" FontWeight="SemiBold" VerticalAlignment="Top"/>
        <TextBlock Grid.Column="1" Text="$safeResult" Foreground="$resultColor" VerticalAlignment="Top"/>
        <TextBlock Grid.Column="2" Text="$safeCriticality" VerticalAlignment="Top"/>
        <TextBlock Grid.Column="3" Text="$safeRequirement" TextWrapping="Wrap" VerticalAlignment="Top"/>
    </Grid>
</Border>
"@

                } catch {
                    Write-DebugOutput -Message "Error processing control: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
                    # Skip this control and continue
                    continue
                }
            }
        }

        # If no controls were processed successfully, add a placeholder
        if ([string]::IsNullOrWhiteSpace($controlsContent)) {
            $controlsContent = @"
<TextBlock Text="No control data available" FontStyle="Italic" Foreground="Gray" Margin="8"/>
"@

        }

        # Replace placeholders
        $processedXaml = $expanderXaml -replace '\{GROUP_HEADER\}', $safeHeader
        $processedXaml = $processedXaml -replace '\{CONTROLS_CONTENT\}', $controlsContent

        # Debug: Log the processed XAML length for troubleshooting
        Write-DebugOutput -Message "Processed XAML length: $($processedXaml.Length) characters for group: $safeHeader" -Source $MyInvocation.MyCommand -Level "Debug"

        # Parse and return - ensure single object
        Write-DebugOutput -Message "Creating group expander for: $safeHeader" -Source $MyInvocation.MyCommand -Level "Info"
        $parsedControl = [Windows.Markup.XamlReader]::Parse($processedXaml)

        # Ensure we return a single control object, not an array
        if ($parsedControl -is [System.Array]) {
            Write-DebugOutput -Message "XAML parser returned array, taking first element" -Source $MyInvocation.MyCommand -Level "Debug"
            return $parsedControl[0]
        } else {
            Write-DebugOutput -Message "XAML parser returned single object of type: $($parsedControl.GetType().FullName)" -Source $MyInvocation.MyCommand -Level "Debug"
            return $parsedControl
        }

    } catch {
        Write-DebugOutput -Message "Error creating group expander: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
        #return New-ResultsNoDataTab
    }
}

Function New-ResultsNoDataTab {
    <#
    .SYNOPSIS
    Creates a "No Data" tab for the results view using XAML.
    #>

    [OutputType([System.Windows.UIElement])]
    param(
        [string]$ReportPath,
        [string]$Message
    )

    Write-DebugOutput -Message "Creating NoDataTab for reason: $Message, ReportPath: $ReportPath" -Source $MyInvocation.MyCommand -Level "Debug"

    # Escape any special XML characters in the message text
    $safeMessageText = $Message -replace '&', '&amp;' -replace '<', '&lt;' -replace '>', '&gt;' -replace '"', '&quot;' -replace "'", '&apos;'

    Write-DebugOutput -Message "NoDataText message set to: $($safeMessageText.Substring(0, [Math]::Min(100, $safeMessageText.Length)))..." -Source $MyInvocation.MyCommand -Level "Debug"

    # Create XAML template for the no data content
    $xamlTemplate = @"
<ScrollViewer xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        VerticalScrollBarVisibility="Auto"
        HorizontalScrollBarVisibility="Auto">
    <StackPanel Margin="20" HorizontalAlignment="Center" VerticalAlignment="Center">
    <TextBlock Text="$safeMessageText"
                FontSize="16"
                TextAlignment="Center"
                Foreground="{DynamicResource MutedTextBrush}"
                TextWrapping="Wrap"
                MaxWidth="600"
                Margin="10"/>
    </StackPanel>
</ScrollViewer>
"@


    try {
        # Parse the XAML into a WPF control
        $noDataControl = [Windows.Markup.XamlReader]::Parse($xamlTemplate)
        Write-DebugOutput -Message "NoDataTab XAML parsed successfully with type: $($noDataControl.GetType().FullName)" -Source $MyInvocation.MyCommand -Level "Debug"

        # Return the parsed control directly
        return $noDataControl

    } catch {
        Write-DebugOutput -Message "Error parsing NoDataTab XAML: $($_.Exception.Message)" -Source $MyInvocation.MyCommand -Level "Error"
    }
}

Function Update-ResultsCount {
    <#
    .SYNOPSIS
    Updates the results count badge in the Results tab header.
    #>

    param([int]$Count)

    $syncHash.ResultsCountText.Text = if ($Count -eq 0) { "No Reports" } elseif ($Count -eq 1) { "1 Report" } else { "$Count Reports" }
}

Function Get-ResultsRelativeTime {
    <#
    .SYNOPSIS
    Returns a human-readable relative time string.
    #>

    param([DateTime]$DateTime)

    $now = Get-Date
    $timespan = $now - $DateTime

    if ($timespan.TotalDays -lt 1) {
        if ($timespan.TotalHours -lt 1) {
            if ($timespan.TotalMinutes -lt 1) {
                return "Just now"
            } else {
                return "$([math]::Floor($timespan.TotalMinutes)) minutes ago"
            }
        } else {
            return "$([math]::Floor($timespan.TotalHours)) hours ago"
        }
    } elseif ($timespan.TotalDays -lt 7) {
        return "$([math]::Floor($timespan.TotalDays)) days ago"
    } else {
        return $DateTime.ToString("MMM dd, yyyy")
    }
}

Function Open-ResultsFolder {
    <#
    .SYNOPSIS
    Opens the main results folder in Windows Explorer.
    #>


    $outputPath = $syncHash.AdvancedSettingsData["OutPath"]
    if ([string]::IsNullOrEmpty($outputPath) -or $outputPath.Trim() -eq ".") {
        $outputPath = [Environment]::GetFolderPath('MyDocuments')
    }

    if (Test-Path $outputPath) {
        Start-Process "explorer.exe" -ArgumentList $outputPath
    } else {
        $syncHash.ShowMessageBox.Invoke(
            "Results folder not found: $outputPath",
            "Folder Not Found",
            [System.Windows.MessageBoxButton]::OK,
            [System.Windows.MessageBoxImage]::Warning
        )
    }
}
# SIG # Begin signature block
# MIIu9gYJKoZIhvcNAQcCoIIu5zCCLuMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCADWwNpEcRB7QDQ
# a6eEeR28FFD3MrmABZL5lN82Gtf5j6CCE6MwggWQMIIDeKADAgECAhAFmxtXno4h
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggdXMIIFP6ADAgECAhAMM6tnPejLgA9WVhXroQvSMA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjYwMTE0MDAwMDAwWhcNMjcwMTEzMjM1OTU5WjBfMQsw
# CQYDVQQGEwJVUzEdMBsGA1UECBMURGlzdHJpY3Qgb2YgQ29sdW1iaWExEzARBgNV
# BAcTCldhc2hpbmd0b24xDTALBgNVBAoTBENJU0ExDTALBgNVBAMTBENJU0EwggIi
# MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCuXYolNHqlh6smLTE592waXheZ
# 8VHzxeds4pMaepGuwmjf8d1jG9wUNuJX9/qb0a1dgGz5D/EAz5NRTIin4SZYQEE8
# qvdl2yQJ5uWxXIjsFbrOyc1fWscUXw0Kt7OPLOafcEkdDoe8K0tO4h2GL3RWRzjp
# uLfQhhnAmD6NT1l+ughnfmarV/ODgIn/RFR4YORlu4YP2xQX6KRxeTDslg7F+z6X
# +t87/U8m8gQ9XTm5kBmteP4GcE/ytnyI+ScIxNRybzGomWIBm848XDE5yYhlYQ2R
# SnCoo6M4CRqp9WFGVyoLkoPP0OlxzryKWaE1/nuPbYG/kf/rUB1OhqxvSSGwmNhs
# vkkjsC0Z9H5Jy6heFdoxOu/+ZQksKoP/fMvHxuCCtkIJbV8tk0oT6MQ8EJbgsWDZ
# TKhui1wxW6JIZyBOMPWoZUOouOzo2h5Cz7LBPKME5FkcUzcs47lpRlDkJco4PLcj
# wJSo4XPnx3G/2DIjNEFNyfKCWfH8uW6nJjmDBiveFZ2j0YvgdQ+7MOjQnw7R/MAD
# DTagrKl3rLV60+X2TY6/onKhCUuU3pMAjVbOwZ3PkzDLZnsEGRfm6hgp6014aXml
# t8h4nu+uC41U8vUSGHl0vqKuzvmShLmnI+Iv0l95pmnqomuCZzRDrjEoaLPx7OxL
# Dy/Id9E7yQDip4jBdQIDAQABo4ICAzCCAf8wHwYDVR0jBBgwFoAUaDfg67Y7+F8R
# hvv+YXsIiGX0TkIwHQYDVR0OBBYEFN30sVU+fpfQQfPQWMhkO1qd+MxoMD4GA1Ud
# IAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cuZGlnaWNl
# cnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMw
# gbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0MDk2U0hBMzg0MjAyMUNBMS5j
# cmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0
# ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3JsMIGUBggrBgEF
# BQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29t
# MFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydDAJ
# BgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQA77T42YiOx5wWPItgo+kvB+Gzb
# ZFCRHRFfTAZZNQt9o/0CqNmyA2xFklX4t5Z9VNdiIOx14AnmJdQkcRdk3vsU5gby
# jKEup7LTWtvcWrl6hQwGNt3l892BgUbPKsPBE+AriktVqn5yMSXVVzeboqsqAG8e
# Syei0B54/QdgR4whfHvQ/qpCACsJTlJgAykXVgDPJNKnQ7wc17loLQutqF1JUcbO
# XKWt1AA9Zas5q30LDZzZeK4B56yojK68CTQXN7toSRFIuZDMKKDfIZpCX8cmbaaO
# DFOOu44/QWMv+Xc6+ISYGkrTTzqWhOqiXgLVBeXGn/WrOJJ8R29mZMneCpBesCLs
# YII1gCFOo7Vt6mvOKxAPQ3KhJYBFEHkp+GI65koaQkO2xv50iLS0+/j2YC66uviU
# MFe0JEOdXuE7Rn/OmWNSzQ+6kPNYDJQASQ974C3wUejJoMtGZEzoTbly/HufQTrd
# rhcL2aC69CxSN+idTXPLC9UT3xo4sFdOw+hXkmbXtoB1GDsd5p1TWFgRbnTXDkbM
# YMBWYVB6/Tk1bzwj4iTp4g0YrtB628FXPX/ko+JWZlv0Ea865S23w1uGlnDNVxIi
# +8oi74G5DM66Q6ENt6+3WoRGRrdoyE6uCh1haY+oPYSgumb0ozzrp8tw89TRKVrK
# KSXGBxlExEvjQ6dYwjGCGqkwghqlAgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNV
# BAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0
# IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4NCAyMDIxIENBMQIQDDOrZz3oy4AP
# VlYV66EL0jANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgACh
# AoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAM
# BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDO5PZtd01farpfGJRJCbgR7vHc
# Sn7VKEThytwhV3nFpzANBgkqhkiG9w0BAQEFAASCAgCPsDYZAoUt8kIf9w8CtNai
# lYZSCfMlUcTpeHPlx8SCpmDDqBc1/3F59Jl60S0l3v6rqNcp8KWxcIbrxlb7OdAn
# ef503RjSQUHAUkqFi9Iv6A3dTHDYw7pFytxZPxB1hCPCnIuCXR1TIAS/cnae5VXI
# zKKxoXhBPAqAUrYWjnIZX4Y97oLg59xGR6mkjqd+UiN+PO7KK9Xe/Blyvbp6xEUs
# qFh8KDSnTzcjmTsD6vn3BCnrjKC1lKhbdqMewK459KVyKdP1qfOqM7mrmvqNMogr
# DcqqTW/MJKMutNWMRJDqG1BjQC+zvP43UG4kZE/lH3aUoZViS9H3u3Acf2kIjFT2
# LPiUFfD2kPwxA+9xVqF5D45hL/jv3HQtdV7hqmZJ3IMy8GEDovDDNj8mlNNlP7Hh
# OK1J548v+lStASPwSIW5n0BW9yyuCefgkfy+fk2il18yEUYpFLtMXQj6hvgIftUU
# 9sJK39kRR2LMaqkIb8eTvmIuUNWkD/UcysAp8D0TmyhV7rExoL3mdlNJVOqekfT0
# VBVwrCPk5y5hqRZRmeU+XCW8/ZY+CC535o68CANOq9ZL+j9xIfPPgeZeSEvjDsYw
# fCV9BRgXn9IJ6lUX8qqVJ4sJAu19Yxz2NEPjOKHCTMn+7don5rNzeX5WeMtLDwl4
# qCYjIT4z5EMpjQijh0QOWaGCF3YwghdyBgorBgEEAYI3AwMBMYIXYjCCF14GCSqG
# SIb3DQEHAqCCF08wghdLAgEDMQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcNAQkQ
# AQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCDrXnL4OBST
# 0t9NjtdA+oHY2VVkIhjm6oG0t9KqvkuKWgIQYwGa/tv/ozm6GYXypHH9RBgPMjAy
# NjEwMjIyMTMwNDVaoIITOjCCBu0wggTVoAMCAQICEAhP3DNPfkVO28MPj/mSGDUw
# DQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0
# LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGlu
# ZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNjA4MDUwMDAwMDBaFw0zNzEx
# MDQyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5j
# LjE7MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQwOTYgVGltZXN0YW1wIFJl
# c3BvbmRlciAyMDI2IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2
# e6byyf7NSvjUm0xls/04xjD4fAkOkbnGQi7+Wpx81iYxfzViaxSIctuH3KSl5YEY
# pMuFgGsA31N2D9ATMbfZdw5uaAhuWevQKhDdZIB4NnqcfpfpWQXJiQnDdAElETC+
# bhSEvNLGbA8DtwUpFMQ4yyYQSPqomT92osQAv6hBi47ATZS6JfVWe6XxhF4jJZ3i
# SAuf2Cros1czRSmWRHqMv9AfGZvp8ygYElhudpQjtcPpwoOl6QrZJUyV3iINvN4c
# O05prGV0fkjG426xDr2d3z9lcSIHkdvGPdGUrXdxfVbgOUVcp2/8ISEzwKPW++Wa
# +E2ujI91EZtukGWDJ/xZ27k3oHKEXBRGfRTqjOU+jE3ba/5++JSE/7oNHnjs5mek
# ExYN96LV/mxUbCKJb8pBNY4r3uD7hEmk/M81XhVgwDA7aMzYC3LZBg9WY5BMmbSa
# y5ecmtJuXaB/0nKWmQmVZeqTVDgsmzHP5MQuhAJkiWNuC9MmCg9TZHXbJ2/yLVSo
# v9p16UDTLtT0+aa1vN71fHeu1qMLlLNB3WOB/ADCxr3S/1hxI92Z6jKgEED/btwI
# vbfuXkNNhg8MtDg43c4tMZae9FvqMOt/9PvmAxF9TNIsIFB8G6yb36ZJZGUL8N/p
# L971DyLXcK6HM5PYnH5X+eVtczhCgHCVQCF6XDAlPQIDAQABo4IBlTCCAZEwDAYD
# VR0TAQH/BAIwADAdBgNVHQ4EFgQUFMljijAu1Er7bpTz5uNAfvXszeIwHwYDVR0j
# BBgwFoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0PAQH/BAQDAgeAMBYGA1Ud
# JQEB/wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSBiDCBhTAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsGAQUFBzAChlFodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBp
# bmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0fBFgwVjBUoFKgUIZOaHR0
# cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1w
# aW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQC
# MAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAjcU6YR6dUgrfmawJgH59
# KECxa9Ji8sEi2g10CBDaMiqsaxWyW5cwlT/6ZF5sFznazqVsoC85U9dqLOYqQwst
# +UQQoNlDHgKRLa3xoc+OReFreFhnTXSG0Vrd2E2CZqUfm+5a+He1MJ/h+tNLuA+0
# Zzhn/Fo+FDYAHWZHx4R79ZsfRFYe9UiXpXBDf6DkUo183Y38NYmR/XfDYf7YZ+oR
# 9t3flbDwK+hgGMs0gNNp1w9Z2CyOyI5or/sSwomAuNQ0hWC9xoU4stD8aWsD7Rkc
# mgVRs6vlIk3zPKQ+ylcheWkMlj+CoVRlFE55pv0ZWCaFt04lwP/rdGHE9qEVQZty
# RE42ox7oNgC/r+Y4bSlZ3dw9K2x1xLtu6PkPKeLBFjzKigwfqm3Hm+k/+lnME8F5
# kPZTgiy2HLEHklpryqs6QHnPXrRNeIzkAMyylnRN8P0wmirS0WkU+ywpEWFZ4QNg
# +9xS43tTuW9x0eXh7NDc1P/sV+zWxHXKH8tFt1ncHdVzqrZaYPyYMLSn2TOXajve
# JW1L3joiQSPsWRGxkbDDW15jERFE4LvjnGu2O9zD1nLJSMdlYZEikl4w2w+q4IN/
# R+TIe0H4ngCI1moJCTbevGH4punIxM1Uoi0nmX3ZK+XbRT01uowE5ViXWHng0Rgs
# mrX/EdYUo80r3TfMlkD0/YMwgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmG
# MA0GCSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lD
# ZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5
# NTlaMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8G
# A1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBT
# SEEyNTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0
# eDHTCphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+Ruw
# OnPhof6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4B
# t0mAxAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1U
# kxBvMgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTca
# arps0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zb
# CclF83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnG
# pTXiUOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/
# AMOMCZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v
# 5ydPpOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoi
# wOrUG2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm
# 2qA+sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYD
# VR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04w
# HwYDVR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGG
# MBMGA1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcw
# AYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8v
# Y2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBD
# BgNVHR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl
# cnRUcnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgB
# hv1sBwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Q
# h8WIGjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3
# YTj+IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQ
# wr8Myb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/
# wdG2th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81
# hjajV/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00
# TYr2Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNj
# qFzeGxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0
# cuJG7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9
# sC+NJpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0
# LckTetiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2
# tszWkPZPubdcMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG
# 9w0BAQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkw
# FwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1
# cmVkIElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBi
# MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
# d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg
# RzQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAi
# MGkz7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnny
# yhHS5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE
# 5nQ7bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm
# 7nfISKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5
# w3jHtrHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsD
# dV14Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1Z
# XUJ2h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS0
# 0mFt6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hk
# pjPRiQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m8
# 00ERElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+i
# sX4KJpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB
# /zAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReui
# r/SSy4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0w
# azAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUF
# BzAChjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVk
# SURSb290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAG
# BgRVHSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9
# mqyhhyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxS
# A8hO0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/
# 6Fmo8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSM
# b++hUD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt
# 9H5xaiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMYIDfDCC
# A3gCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x
# QTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQw
# OTYgU0hBMjU2IDIwMjUgQ0ExAhAIT9wzT35FTtvDD4/5khg1MA0GCWCGSAFlAwQC
# AQUAoIHRMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUx
# DxcNMjYxMDIyMjEzMDQ1WjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBRR2avaA0lz
# 2E9CZqykgkjms2nEOTAvBgkqhkiG9w0BCQQxIgQgULqE04x/i/6+9LTEgH8tIGDW
# vKoo3VhGb+fCjCgqm/QwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgLaCdp/QTH5/n
# LbbF5unJZWdVrwQ/HqdCzA0hIOFB6/wwDQYJKoZIhvcNAQEBBQAEggIAQ7gUF7Qc
# i52VNTAHfRAh65GrVs/wHj1aAuEe3nk7XvEoOHH6BVQZnr+uevgfBn9IGtNDP00K
# BKfob+fgFD5b20kKmGb0AosYOJHUt0XOxQf8guajs3GLS6gObW+G14Q54QVP4K8Y
# DpYsAzuawlpHZzbg+Z6kja0G1JQVSgcExJpg+qOz9y5KELZ9fEHwjwHRaS7cU+0H
# GgA6RFmAwTTkoUhRBUCmNWrgBUPcl1zt8bWjaFw6t2pN2OJRhvZIVuRvfbULT015
# rKSA11JkTgUCaSZF8v5xMsGmsLzf6aNUFIth/GA2SzwAzBrA+n/T7A15w0zOj8E2
# HpXZsVx5uR94Xp9BUtV5m9kAQ2prxl8XnVMsdZcO8qupp53KlPWkxq1wMb9XoZSy
# s7K14miKclxoiE2EF1R7snxAnNMDPU54kHSWJJ1v7rMKc/tS3p/LC5LVtaxSJ30E
# iZvQkc95ASFqE9aYQyKv4Uu8ujUOhJlpkYD8iZ8oPLLDetuHusdjaBUuO+Ml4plJ
# ZqvGjPb5Qv8M5Ssl/tSCLmWkJy4HPXIUa9WQ2dTrBpNAP6S+tRmEbDL+J4t+rwdP
# sEAF38qQiY+NAgkffLfYskWS7Z8/NPHdBsjUZeJMnl1FS6sLxfuRaVAd6hZUAduj
# DxOdblqTXiqVFvRawFKOwjx0DA3piOZMMBs=
# SIG # End signature block