Public/Invoke-ArAudit.ps1

function Invoke-ArAudit {
    <#
.SYNOPSIS
    Azure resource posture assessment.
.DESCRIPTION
    Requires an active session from Connect-ArSession.
    Discovers checks from Checks/ and runs them against JSON exports
    produced by Invoke-ArExport. Generates HTML report, JSON, and CSV.

    Copyright (c) NCS Dojo. All Rights Reserved.
.PARAMETER ExportPath
    Directory containing JSON exports. Defaults to temp NCS/AzResourceAnalyzer.
.PARAMETER MinSeverity
    Minimum severity to include. Default: Info.
.PARAMETER ListChecks
    Print available checks and exit.
#>

    [CmdletBinding()]
    param(
        [Alias('ExportDir','InputPath')][string]$ExportPath,
        [Alias('OutputDir')][string]$OutputPath,
        [ValidateSet('Critical','High','Medium','Low','Info')][string]$MinSeverity = 'Info',
        [switch]$ListChecks,
        [string]$CheckPath
    )

if (-not $ExportPath) { $ExportPath = Join-Path (Join-Path ([System.IO.Path]::GetTempPath()) 'NCS') 'AzResourceAnalyzer' }
if (-not $OutputPath) { $OutputPath = $ExportPath }

if (-not (Test-Path $ExportPath -PathType Container)) {
    Write-Host " ERROR: Export directory not found: $ExportPath" -ForegroundColor Red
    Write-Host " Run Invoke-ArExport first to generate exports." -ForegroundColor Yellow
    return
}
if (-not (Test-Path $OutputPath -PathType Container)) {
    New-Item -Path $OutputPath -ItemType Directory -Force | Out-Null
}




if (-not $script:_ArSession -or -not $script:_ArTenantId) {
    Write-Warning "No active session. Run Connect-ArSession first to authenticate.`n`n Example:`n Connect-ArSession -TenantId 'contoso.onmicrosoft.com'`n Invoke-ArAudit`n"
    return
}
$TenantId = $script:_ArTenantId


$CISSections = @{
    "2" = @{ Name="Analytics Services";          Total=12 }
    "3" = @{ Name="Compute Services";            Total=24 }
    "5" = @{ Name="Identity Services";           Total=17 }
    "6" = @{ Name="Management and Governance";   Total=26 }
    "7" = @{ Name="Networking Services";         Total=21 }
    "8" = @{ Name="Security Services";           Total=50 }
    "9" = @{ Name="Storage Services";            Total=23 }
}

$SeverityOrder = @{ "Critical"=5; "High"=4; "Medium"=3; "Low"=2; "Info"=1 }
$SeverityColors = @{ "Critical"="#B91C3A"; "High"="#C4650A"; "Medium"="#8B7231"; "Low"="#2B6CB0"; "Info"="#6B7280" }




if (-not $CheckPath) { $CheckPath = Join-Path $script:_ArModuleRoot "Checks" }

Write-Host ""
Write-Information "`n NCS Dojo — AzResourceAnalyzer" -InformationAction Continue
Write-Information " Tenant: $TenantId" -InformationAction Continue
Write-Host ""

$checkFiles = Get-ChildItem -Path $CheckPath -Filter "*.ps1" -Recurse -ErrorAction SilentlyContinue |
    Sort-Object DirectoryName, Name

if ($checkFiles.Count -eq 0) {
    Write-Host " ERROR: No check files found in $CheckPath" -ForegroundColor Red
    return
}

$Checks = [System.Collections.Generic.List[hashtable]]::new()
$loadErrors = 0

foreach ($file in $checkFiles) {
    try {
        $definition = . $file.FullName
        if ($null -ne $definition -and $definition -is [hashtable]) {
            $requiredFields = @('CIS', 'Title', 'Severity', 'Automated', 'Check')
            $missing = @($requiredFields | Where-Object { -not $definition.ContainsKey($_) })
            if ($missing.Count -gt 0) {
                Write-Host " WARN: $($file.Name) missing fields: $($missing -join ', ')" -ForegroundColor Yellow
                continue
            }
            $definition['_sourceFile'] = $file.FullName
            $Checks.Add($definition)
        }
    }
    catch {
        $loadErrors++
        Write-Host " ERROR: $($file.Name): $_" -ForegroundColor Red
    }
}

# Sort by CIS number
$Checks = [System.Collections.Generic.List[hashtable]]@(
    $Checks | Sort-Object {
        $parts = $_.CIS -split '\.'
        ($parts | ForEach-Object { [int]$_ }) -join '.'
        # Numeric sort: pad each segment
        ($parts | ForEach-Object { '{0:D4}' -f [int]$_ }) -join '.'
    }
)

Write-Host " Discovered $($Checks.Count) checks ($loadErrors errors)" -ForegroundColor $(if ($loadErrors -eq 0) { 'Green' } else { 'Yellow' })

# Group by section
foreach ($sec in ($CISSections.Keys | Sort-Object { [int]$_ })) {
    $count = @($Checks | Where-Object { $_.CIS.Split('.')[0] -eq $sec }).Count
    if ($count -gt 0) {
        Write-Host " Section $sec ($($CISSections[$sec].Name)): $count checks" -ForegroundColor Gray
    }
}


if ($ListChecks) {
    Write-Host ""
    Write-Host " Available Checks:" -ForegroundColor Cyan
    Write-Host " $('-' * 80)" -ForegroundColor Gray
    $lastSection = ""
    foreach ($chk in $Checks) {
        $sec = $chk.CIS.Split('.')[0]
        if ($sec -ne $lastSection) {
            $secName = if ($CISSections.ContainsKey($sec)) { $CISSections[$sec].Name } else { "Unknown" }
            Write-Host ""
            Write-Host " [Section $sec — $secName]" -ForegroundColor Yellow
            $lastSection = $sec
        }
        $autoFlag = if ($chk.Automated) { "AUTO" } else { "MANUAL" }
        Write-Host " CIS $($chk.CIS.PadRight(10)) $($chk.Severity.PadRight(10)) $autoFlag $($chk.Title)" -ForegroundColor Gray
    }
    Write-Host ""
    return
}


Write-Host ""
Write-Host " Loading from $ExportPath" -ForegroundColor Gray

$D = @{}
$jsonFiles = Get-ChildItem $ExportPath -Filter "*.json" -ErrorAction SilentlyContinue |
    Where-Object { $_.Name -notmatch 'Manifest|ExportLog|CIS_' } |
    Sort-Object LastWriteTime -Descending

$loaded = @{}
foreach ($file in $jsonFiles) {
    $prefix = $file.BaseName -replace '_\d{8}_\d{6}$', ''
    if ($loaded.ContainsKey($prefix)) { continue }
    $loaded[$prefix] = $true

    try {
        $json = Get-Content $file.FullName -Raw | ConvertFrom-Json
        $data = @()
        if ($null -ne $json -and $json.PSObject.Properties['data']) {
            $raw = $json.data
            if ($null -ne $raw) {
                $data = if ($raw -is [array]) { $raw } else { @(,$raw) }
            }
        }
        $D[$prefix] = $data
        Write-Host " $($prefix.PadRight(28)) $($data.Count)" -ForegroundColor Gray
    }
    catch {
        Write-Host " $($prefix.PadRight(28)) LOAD ERROR" -ForegroundColor Red
    }
}

Write-Host " $($D.Count) sources loaded" -ForegroundColor $(if ($D.Count -gt 0) { 'Green' } else { 'Yellow' })

# Schema validation
$schemaExpected = @{
    VirtualMachines         = @('vmName','securityType','secureBootEnabled','vtpmEnabled','encryptionAtHost','managedIdentityType')
    StorageAccounts         = @('storageAccountName','httpsOnly','minimumTlsVersion','networkDefaultAction','allowBlobPublicAccess')
    NSGs                    = @('nsgName','ruleName','ruleDirection','ruleAccess','destPortRange','ruleKind')
    VNets                   = @('vnetName','subnetName','encryptionEnabled')
    KeyVaults               = @('vaultName','enableRbacAuthorization','enableSoftDelete','enablePurgeProtection')
    AppServices             = @('appName','httpsOnly','minTlsVersion','ftpsState','remoteDebugging','publicNetworkAccess')
    SQLServers              = @('serverName','minTlsVersion','publicNetworkAccess','azureAdOnlyAuth')
    CosmosDBAccounts        = @('accountName','publicNetworkAccess','disableLocalAuth')
    PostgreSQLServers       = @('serverName','publicNetworkAccess','geoRedundantBackup')
    MySQLServers            = @('serverName','publicNetworkAccess','geoRedundantBackup')
    RedisCaches             = @('cacheName','enableNonSslPort','minimumTlsVersion','publicNetworkAccess')
    DataFactories           = @('factoryName','publicNetworkAccess','managedIdentityType')
    ContainerRegistries     = @('registryName','adminUserEnabled','publicNetworkAccess')
    Disks                   = @('diskName','encryptionType','publicNetworkAccess')
    DefenderPricing         = @('planName','pricingTier')
    RBACAssignments         = @('principalId','principalType','roleDefinitionId','roleScope')
    MFARegistration         = @('userPrincipalName','isMfaRegistered')
    BlobContainers          = @('containerName','storageAccount','publicAccess')
    FileShares              = @('shareName','storageAccount')
    SQLAuditSettings        = @('serverName','state')
    SQLThreatDetection      = @('serverName','state')
    BackupPolicies          = @('policyName','vaultName','retentionDailyCount')
    PostgreSQLConfigs       = @('serverName','configName','configValue')
    MySQLConfigs            = @('serverName','configName','configValue')
}

$schemaWarnings = @()
foreach ($source in $schemaExpected.Keys) {
    if (-not $D.ContainsKey($source)) { continue }
    $items = @($D[$source])
    if ($items.Count -eq 0) { continue }
    $sample = $items[0]
    $missing = @()
    foreach ($prop in $schemaExpected[$source]) {
        if (-not $sample.PSObject.Properties[$prop]) { $missing += $prop }
    }
    if ($missing.Count -gt 0) {
        $warn = "$source — missing properties: $($missing -join ', ')"
        $schemaWarnings += $warn
        Write-Host " SCHEMA WARNING: $warn" -ForegroundColor Yellow
    }
}
if ($schemaWarnings.Count -gt 0) {
    Write-Host " $($schemaWarnings.Count) schema warning(s)" -ForegroundColor Yellow
} else {
    Write-Host " Schema validation passed" -ForegroundColor Green
}

# Subscription extraction
$subscriptionMap = @{}
$subscriptionNames = @{}
$nameFields = @('vmName','storageAccountName','appName','serverName','vaultName','nsgName',
    'cacheName','factoryName','registryName','groupName','diskName','peeringName','vnetName',
    'accountName','policyName','shareName','containerName','slotName','userPrincipalName','configName')

foreach ($key in $D.Keys) {
    foreach ($item in @($D[$key])) {
        if (-not $item.PSObject.Properties['subscriptionId'] -or -not $item.subscriptionId) { continue }
        $subId = [string]$item.subscriptionId
        if (-not $subscriptionNames.ContainsKey($subId)) { $subscriptionNames[$subId] = $subId }
        foreach ($nf in $nameFields) {
            if ($item.PSObject.Properties[$nf] -and $item.$nf) {
                $subscriptionMap[[string]$item.$nf] = $subId
            }
        }
    }
}

# Friendly names from Subscriptions export
$subFiles = Get-ChildItem $ExportPath -Filter "Subscriptions_*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($subFiles) {
    try {
        $subJson = Get-Content $subFiles.FullName -Raw | ConvertFrom-Json
        $subData = if ($subJson.PSObject.Properties['data']) { $subJson.data } else { $subJson }
        foreach ($sub in @($subData)) {
            if ($sub.PSObject.Properties['subscriptionId'] -and $sub.PSObject.Properties['displayName']) {
                $subscriptionNames[$sub.subscriptionId] = $sub.displayName
            }
            elseif ($sub.PSObject.Properties['subscriptionId'] -and $sub.PSObject.Properties['subscriptionName']) {
                $subscriptionNames[$sub.subscriptionId] = $sub.subscriptionName
            }
        }
    } catch { }
}

# Manifest subscription names
$manifestFiles = Get-ChildItem $ExportPath -Filter "Manifest_*.json" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($manifestFiles) {
    try {
        $manifest = Get-Content $manifestFiles.FullName -Raw | ConvertFrom-Json
        if ($manifest.PSObject.Properties['subscriptions']) {
            foreach ($sub in @($manifest.subscriptions)) {
                $subId = $null; $subName = $null
                if ($sub.PSObject.Properties['subscriptionId']) { $subId = $sub.subscriptionId }
                elseif ($sub.PSObject.Properties['id']) { $subId = $sub.id }
                if ($sub.PSObject.Properties['displayName']) { $subName = $sub.displayName }
                elseif ($sub.PSObject.Properties['name']) { $subName = $sub.name }
                if ($subId -and $subName) { $subscriptionNames[$subId] = $subName }
            }
        }
    } catch { }
}

foreach ($key in @($subscriptionNames.Keys)) {
    if ($subscriptionNames[$key] -eq $key) { $subscriptionNames[$key] = $key.Substring(0, 8) + '...' }
}

Write-Host " $($subscriptionNames.Count) subscription(s) detected" -ForegroundColor Gray
Write-Host ""

if ($D.Count -eq 0) {
    Write-Host " No JSON exports found in $ExportPath" -ForegroundColor Red
    Write-Host " Run Invoke-ArExport first." -ForegroundColor Yellow
    return
}


$sevThreshold = $SeverityOrder[$MinSeverity]
$findings = [System.Collections.Generic.List[object]]::new()
$checksRun = 0
$checksPassed = 0

Write-Host " Running $($Checks.Count) checks..." -ForegroundColor Gray

foreach ($check in $Checks) {
    $checksRun++
    $cisRef    = $check.CIS
    $section   = [int]($cisRef.Split('.')[0])
    $secName   = if ($CISSections.ContainsKey("$section")) { $CISSections["$section"].Name } else { "Unknown" }

    try {
        $rawResults = & $check.Check $D
        $results = @()
        if ($null -ne $rawResults) {
            foreach ($item in @($rawResults)) {
                if ($null -ne $item) { $results += $item }
            }
        }
    }
    catch {
        $results = @(@{
            AffectedResources = @("Check execution error: $_")
            Description       = "Internal error running check $cisRef"
        })
    }

    try {
        if ($results.Count -eq 0) {
            $checksPassed++
            $findings.Add([PSCustomObject]@{
                CIS = $cisRef; Section = $section; SectionName = $secName; Title = $check.Title
                Severity = "Pass"; Automated = $check.Automated; Status = "Pass"
                Description = ""; AffectedResources = @(); Recommendation = ""
            })
        }
        else {
            foreach ($r in $results) {
                $isManual = $false
                if ($r -is [hashtable] -and $r.ContainsKey('Manual') -and $r['Manual']) { $isManual = $true }
                if ($isManual) { continue }
                $sev = $check.Severity
                $status = "Fail"
                $desc = if ($r -is [hashtable] -and $r.ContainsKey('Description')) { $r['Description'] } else { "" }
                $rec = ""
                if ($r -is [hashtable] -and $r.ContainsKey('Recommendation')) { $rec = $r['Recommendation'] }
                elseif ($check.ContainsKey('Recommendation') -and $check['Recommendation']) { $rec = $check['Recommendation'] }
                $affected = @()
                if ($r -is [hashtable] -and $r.ContainsKey('AffectedResources')) { $affected = @($r['AffectedResources']) }

                if ($SeverityOrder.ContainsKey($sev) -and $SeverityOrder[$sev] -ge $sevThreshold) {
                    $findings.Add([PSCustomObject]@{
                        CIS = $cisRef; Section = $section; SectionName = $secName; Title = $check.Title
                        Severity = $sev; Automated = $check.Automated; Status = $status
                        Description = $desc; AffectedResources = $affected; Recommendation = $rec
                    })
                }
            }
        }
    }
    catch {
        Write-Host " $cisRef error: $_" -ForegroundColor Red
        $findings.Add([PSCustomObject]@{
            CIS = $cisRef; Section = $section; SectionName = $secName; Title = $check.Title
            Severity = "Info"; Automated = $check.Automated; Status = "Fail"
            Description = "Result processing error: $_"; AffectedResources = @(); Recommendation = ""
        })
    }
}

# PS7 safe conversion
[array]$findings = @($findings.ToArray())

# Tag with subscription IDs
foreach ($f in $findings) {
    $subs = @()
    foreach ($r in @($f.AffectedResources)) {
        $rStr = [string]$r
        if ($subscriptionMap.ContainsKey($rStr)) { $subs += $subscriptionMap[$rStr] }
        else {
            $token = ($rStr -split '[\s:/—\-]')[0]
            if ($token -and $subscriptionMap.ContainsKey($token)) { $subs += $subscriptionMap[$token] }
        }
    }
    $f | Add-Member -NotePropertyName 'Subscriptions' -NotePropertyValue @($subs | Select-Object -Unique) -Force
}

$failFindings = @($findings | Where-Object { $_.Status -eq 'Fail' })
$passFindings = @($findings | Where-Object { $_.Status -eq 'Pass' })


$sectionMetrics = @{}
foreach ($sec in $CISSections.Keys) {
    $secFindings = @($findings | Where-Object { $_.Section -eq $sec })
    $secFail = @($secFindings | Where-Object { $_.Status -eq 'Fail' })
    $secPass = @($secFindings | Where-Object { $_.Status -eq 'Pass' })
    $total = $CISSections[$sec].Total
    $automated = $secPass.Count + $secFail.Count
    if ($automated -eq 0) { continue }
    $score = [math]::Round(($secPass.Count / $automated) * 100)
    $sectionMetrics[$sec] = @{
        Name = $CISSections[$sec].Name; Total = $total; Automated = $automated
        Passed = $secPass.Count; Failed = $secFail.Count; Score = $score
    }
}

$totalAuto = 0; $totalPassed = 0
foreach ($m in @($sectionMetrics.Values)) { $totalAuto += $m.Automated; $totalPassed += $m.Passed }
$overallScore = if ($totalAuto -gt 0) { [math]::Round(($totalPassed / $totalAuto) * 100) } else { 0 }

$findingsJson = @($findings | Where-Object { $_.Status -ne 'Pass' } | ForEach-Object {
    @{
        cis = $_.CIS; section = $_.Section; title = $_.Title; severity = $_.Severity
        status = $_.Status; desc = $_.Description; rec = $_.Recommendation
        affected = @($_.AffectedResources).Count; subs = @($_.Subscriptions)
    }
}) | ConvertTo-Json -Depth 5 -Compress
if (-not $findingsJson -or $findingsJson -eq 'null') { $findingsJson = '[]' }

$reportDataJson = @{
    score = $overallScore; generated = (Get-Date -Format "yyyy-MM-dd HH:mm")
    counts = @{
        critical = @($failFindings | Where-Object { $_.Severity -eq 'Critical' }).Count
        high     = @($failFindings | Where-Object { $_.Severity -eq 'High' }).Count
        medium   = @($failFindings | Where-Object { $_.Severity -eq 'Medium' }).Count
        low      = @($failFindings | Where-Object { $_.Severity -eq 'Low' }).Count
        passed   = $passFindings.Count
    }
    subscriptions = $subscriptionNames; schemaWarnings = $schemaWarnings
} | ConvertTo-Json -Depth 3 -Compress

$sectionMetricsJson = $sectionMetrics | ConvertTo-Json -Depth 3 -Compress


# NCS Logo (inline SVG)
$logoSvg = @'
<svg xmlns="http://www.w3.org/2000/svg" width="315" height="80" viewBox="0 0 3150 800" preserveAspectRatio="xMidYMid meet">
  <defs>
    <linearGradient id="grayGrad" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0%" stop-color="#99999b"/>
      <stop offset="100%" stop-color="#64686e"/>
    </linearGradient>
    <linearGradient id="blueGrad" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0%" stop-color="#53aede"/>
      <stop offset="100%" stop-color="#196ca6"/>
    </linearGradient>
  </defs>
  <path d="M-0.00,404.23 L-0.00,8.47 L362.50,11.73 L725.00,15.00 L792.34,46.90 C846.25,72.44 865.57,88.09 889.22,125.38 C933.76,195.61 940.00,246.01 940.00,535.49 L940.00,800.00 L836.78,800.00 L733.56,800.00 L737.95,557.50 C742.54,303.89 737.28,254.66 700.65,208.09 C690.04,194.61 663.80,178.31 642.33,171.88 C598.11,158.63 273.04,145.55 228.90,155.24 L200.00,161.59 L200.00,480.79 L200.00,800.00 L100.00,800.00 L0.00,800.00 L-0.00,404.23 Z M1320.00,786.14 C1161.43,745.65 1082.70,634.51 1072.37,436.56 C1058.98,179.95 1162.48,36.50 1376.14,15.55 C1478.32,5.53 1715.79,2.49 1877.08,9.14 L2019.17,15.00 L1981.56,80.00 L1943.96,145.00 L1684.48,150.16 C1400.24,155.82 1359.98,163.41 1321.26,218.64 C1274.93,284.74 1267.68,461.66 1307.62,551.94 C1344.58,635.49 1410.25,650.00 1751.52,650.00 L2000.31,650.00 L1960.15,713.58 C1938.07,748.55 1920.00,782.30 1920.00,788.58 C1920.00,804.35 1382.75,802.16 1320.00,786.14 Z" fill="url(#grayGrad)" fill-rule="evenodd"/>
  <path d="M2031.54,792.88 C2033.44,788.55 2054.64,752.37 2078.64,712.50 L2122.28,640.00 L2483.64,639.44 C2736.16,639.04 2855.48,635.28 2879.79,626.94 C2918.42,613.68 2936.87,578.74 2926.68,538.12 C2914.89,491.15 2886.24,486.28 2580.00,479.26 C2282.57,472.45 2248.93,467.85 2172.28,423.57 C2109.61,387.37 2078.82,335.12 2074.74,258.05 C2070.34,174.88 2088.96,123.94 2140.74,77.51 C2218.13,8.12 2232.05,6.30 2717.50,2.36 C2955.38,0.42 3150.00,2.02 3150.00,5.92 C3150.00,9.81 3130.88,45.86 3107.50,86.01 L3065.00,159.03 L2704.54,162.01 C2376.54,164.73 2341.59,166.63 2316.47,183.10 C2293.29,198.30 2289.36,207.12 2291.93,238.10 C2294.58,269.93 2300.49,277.74 2335.00,294.92 C2371.20,312.94 2402.10,315.37 2660.00,320.41 C2967.43,326.42 2990.79,330.11 3070.19,385.13 C3123.25,421.90 3150.00,480.03 3150.00,558.56 C3150.00,661.79 3111.56,717.02 3004.97,766.92 L2945.00,795.00 L2486.54,797.88 C2234.38,799.47 2029.63,797.22 2031.54,792.88 Z" fill="url(#blueGrad)" fill-rule="evenodd"/>
</svg>
'@


# Report CSS
$cssBlock = @'
:root{--navy:#0B1929;--navy2:#122338;--gold:#B4975A;--bg:#0B1929;--surface:#122338;--surface2:#1A3050;--card:#162D4A;--border:#1E3A5F;--border-light:#1A3050;--text:#E2E8F0;--muted:#8899AA;--text3:#5A7A9A;
--crit:#EF4444;--high:#F59E0B;--med:#EAB308;--low:#60A5FA;--info:#94A3B8;--pass:#10B981;
--accent:#3B82F6;--serif:Georgia,'Times New Roman',serif;--sans:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}
*{margin:0;padding:0;box-sizing:border-box}
body{background:var(--bg);color:var(--text);font-family:var(--sans);line-height:1.6;-webkit-font-smoothing:antialiased}
.container{max-width:1100px;margin:0 auto;padding:0 2.5rem 4rem}
header{position:relative;padding:2rem 0 1.5rem;border-bottom:1px solid var(--border);text-align:center}
.logo{position:absolute;left:0;top:2rem}
.logo svg{width:120px;height:auto}
.header-content{text-align:center}
header h1{font-family:var(--serif);font-size:1.75rem;font-weight:400;color:var(--text)}
header .subtitle{color:var(--muted);margin-top:6px;font-size:.8rem;letter-spacing:.02em}
.donut-wrap{display:flex;flex-direction:column;align-items:center;margin:24px auto}
.donut-chart{position:relative;width:180px;height:180px}
.donut-chart svg{transform:rotate(-90deg)}
.donut-chart .donut-center{position:absolute;top:0;left:0;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center}
.donut-chart .donut-pct{font-family:var(--serif);font-size:2.2rem;font-weight:700;color:var(--text)}
.donut-chart .donut-label{font-size:.65rem;color:var(--text3);text-transform:uppercase;letter-spacing:.1em;margin-top:2px}
.donut-legend{display:flex;gap:24px;margin-top:16px;font-size:.75rem}
.donut-legend span{display:flex;align-items:center;gap:6px;color:var(--muted)}
.donut-legend .leg-dot{width:10px;height:10px;border-radius:50%;display:inline-block}
.metrics{display:flex;gap:0;justify-content:center;padding:1.5rem 0;border-bottom:1px solid var(--border)}
.metric{flex:1;padding:0 1.5rem;border-right:1px solid var(--border);text-align:left}
.metric:first-child{padding-left:0}.metric:last-child{border-right:none}
.metric .val{font-family:var(--serif);font-size:1.75rem;font-weight:400;line-height:1}
.metric .lbl{font-size:.65rem;color:var(--text3);text-transform:uppercase;letter-spacing:.1em;margin-top:4px;font-weight:500}
.sections{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;margin:24px 0}
.sec-card{background:#F8F9FA;padding:16px;cursor:pointer;border:1px solid #E2E8F0;transition:border-color .2s}
.sec-card:hover,.sec-card.active{border-color:#3B82F6}
.sec-card h3{font-family:var(--serif);font-size:.9rem;font-weight:400;color:#1A1A2E;margin-bottom:8px}
.sec-bar{height:6px;background:#E2E8F0;overflow:hidden;margin:8px 0;border-radius:3px}
.sec-bar-fill{height:100%;transition:width .3s;border-radius:3px}
.sec-stats{display:flex;gap:12px;font-size:.7rem;color:#4A5568}
.sec-stats span{display:flex;align-items:center;gap:4px}
.dot{width:8px;height:8px;border-radius:50%;display:inline-block}
.filters{display:flex;gap:5px;margin:24px 0;flex-wrap:wrap;align-items:center}
.filters label{font-size:.65rem;color:var(--text3);text-transform:uppercase;letter-spacing:.08em;margin-right:4px;font-weight:500}
.btn{padding:3px 12px;border:1px solid var(--border);background:transparent;color:var(--muted);cursor:pointer;font-family:var(--sans);font-size:.65rem;font-weight:500;letter-spacing:.03em;text-transform:uppercase;transition:all .15s}
.btn:hover{border-color:var(--accent);color:var(--text)}
.btn.active{background:var(--accent);color:#fff;border-color:var(--accent)}
.finding{background:#F8F9FA;padding:14px 0;margin:8px 0;border-bottom:none;border-left:3px solid var(--border);border-radius:4px}
.finding.Critical{border-left-color:var(--crit)}.finding.High{border-left-color:var(--high)}
.finding.Medium{border-left-color:var(--med)}.finding.Low{border-left-color:var(--low)}
.finding.Info{border-left-color:var(--info)}
.finding .fhead{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;padding-left:16px;padding-right:16px}
.finding .cis-ref{font-family:monospace;font-size:.78rem;color:#2B6CB0;font-weight:600;letter-spacing:.02em}
.badge{display:inline-block;padding:2px 12px;border-radius:16px;font-size:.6rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#fff}
.badge.Critical{background:var(--crit)}.badge.High{background:var(--high);color:#000}
.badge.Medium{background:var(--med);color:#000}.badge.Low{background:var(--low)}
.badge.Info{background:var(--info)}
.finding .ftitle{font-size:.85rem;font-weight:400;color:#1A1A2E;padding-left:16px;margin-bottom:4px}
.finding .desc{font-size:.78rem;color:#4A5568;margin:4px 0;padding-left:16px;padding-right:16px}
.finding .affected{font-size:.72rem;color:#2D3748;margin:8px 16px 0;padding:10px 16px;background:#EDF2F7;border-left:3px solid var(--gold);font-family:monospace;max-height:400px;overflow-y:auto;white-space:pre-wrap;border-radius:2px}
.finding .rec{font-size:.78rem;color:#276749;margin-top:8px;padding-left:16px;font-style:italic}
.empty{text-align:left;color:var(--text3);padding:2rem 0;font-size:.85rem;font-style:italic}
.sub-filter{margin-left:auto}
.sub-filter select{padding:3px 10px;border:1px solid var(--border);background:var(--surface);color:var(--text);font-family:var(--sans);font-size:.65rem;font-weight:500;letter-spacing:.03em;text-transform:uppercase;cursor:pointer;appearance:auto;min-width:160px}
.sub-filter select:focus{border-color:var(--accent);outline:none}
.schema-warnings{background:var(--surface);border-left:3px solid var(--high);padding:12px 16px;margin:16px 0;font-size:.78rem;color:var(--muted);border-radius:4px}
.schema-warnings strong{color:var(--text);font-size:.8rem}
footer{text-align:center;color:var(--text3);font-size:.7rem;padding:1.5rem 0;border-top:1px solid var(--border);margin-top:3rem}
@media(max-width:600px){.container{padding:0 1rem 3rem}.metrics{flex-wrap:wrap;gap:1rem}.metric{border-right:none;padding:0}header{flex-direction:column;text-align:center}}
'@


# Report JavaScript
$jsBlock = @'
(function(){
"use strict";
var report=JSON.parse(document.getElementById("report-data").textContent);
var findings=JSON.parse(document.getElementById("findings-data").textContent);
var sections=JSON.parse(document.getElementById("sections-data").textContent);
var activeSection=null,activeSev="all",activeSub="all";

function mk(tag,cls){var e=document.createElement(tag);if(cls)e.className=cls;return e;}
function txt(tag,cls,t){var e=mk(tag,cls);e.textContent=t;return e;}

document.getElementById("subtitle").textContent="Control Gap Analysis \u2014 Generated "+report.generated;
var total=report.counts.passed+(report.counts.critical+report.counts.high+report.counts.medium+report.counts.low);
var passRatio=total>0?report.counts.passed/total:0;
var failRatio=1-passRatio;
var circ=2*Math.PI*70;
var passArc=passRatio*circ;
var failArc=failRatio*circ;
var passEl=document.getElementById("donut-pass");
var failEl=document.getElementById("donut-fail");
passEl.setAttribute("stroke","#10B981");passEl.setAttribute("stroke-dasharray",passArc+" "+circ);passEl.setAttribute("stroke-linecap","round");
failEl.setAttribute("stroke","#EF4444");failEl.setAttribute("stroke-dasharray",failArc+" "+circ);failEl.setAttribute("stroke-dashoffset","-"+passArc);failEl.setAttribute("stroke-linecap","round");
document.getElementById("donut-pct").textContent=report.score+"%";
document.getElementById("leg-pass").textContent=report.counts.passed+" Compliant";
document.getElementById("leg-fail").textContent=(report.counts.critical+report.counts.high+report.counts.medium+report.counts.low)+" Non-Compliant";

var mbar=document.getElementById("metrics-bar");
var mdef=[["crit","Critical",report.counts.critical],["high","High",report.counts.high],["med","Medium",report.counts.medium],["low","Low",report.counts.low],["pass","Passed",report.counts.passed]];
mdef.forEach(function(m){
  var d=mk("div","metric");
  var v=txt("div","val",String(m[2]));v.style.color="var(--"+m[0]+")";
  d.appendChild(v);d.appendChild(txt("div","lbl",m[1]));mbar.appendChild(d);
});

if(report.schemaWarnings&&report.schemaWarnings.length>0){
  var warnDiv=mk("div","schema-warnings");
  warnDiv.appendChild(txt("strong","","Schema Validation Warnings"));
  report.schemaWarnings.forEach(function(w){warnDiv.appendChild(txt("div","",w));});
  document.getElementById("sections").parentNode.insertBefore(warnDiv,document.getElementById("sections"));
}

function renderSections(){
  var el=document.getElementById("sections");
  while(el.firstChild)el.removeChild(el.firstChild);
  var keys=Object.keys(sections).sort(function(a,b){return parseInt(a)-parseInt(b)});
  keys.forEach(function(k){
    var s=sections[k];
    var scoreColor=s.Score>=80?"var(--pass)":s.Score>=50?"var(--med)":"var(--crit)";
    var card=mk("div","sec-card"+(activeSection===parseInt(k)?" active":""));
    card.appendChild(txt("h3","","Section "+k+": "+s.Name));
    var bar=mk("div","sec-bar");var fill=mk("div","sec-bar-fill");
    fill.style.width=s.Score+"%";fill.style.background=scoreColor;bar.appendChild(fill);card.appendChild(bar);
    var stats=mk("div","sec-stats");
    var sp=mk("span","");var d1=mk("span","dot");d1.style.background="var(--pass)";sp.appendChild(d1);sp.appendChild(document.createTextNode(s.Passed+" passed"));stats.appendChild(sp);
    var sf=mk("span","");var d2=mk("span","dot");d2.style.background="var(--crit)";sf.appendChild(d2);sf.appendChild(document.createTextNode(s.Failed+" failed"));stats.appendChild(sf);
    var sc=mk("span","");sc.style.marginLeft="auto";sc.style.fontWeight="600";sc.style.color=scoreColor;sc.textContent=s.Score+"%";stats.appendChild(sc);
    card.appendChild(stats);
    card.addEventListener("click",function(){activeSection=activeSection===parseInt(k)?null:parseInt(k);renderSections();renderFindings();});
    el.appendChild(card);
  });
}

function renderFindings(){
  var el=document.getElementById("findings-list");
  while(el.firstChild)el.removeChild(el.firstChild);
  var filtered=findings.filter(function(f){
    if(activeSection&&f.section!==activeSection)return false;
    if(activeSev!=="all"){if(f.severity!==activeSev)return false;}
    if(activeSub!=="all"){if(!f.subs||f.subs.length===0){}else if(f.subs.indexOf(activeSub)===-1)return false;}
    return true;
  });
  if(filtered.length===0){el.appendChild(txt("div","empty","No findings match the current filters."));return;}
  filtered.sort(function(a,b){var so={Critical:5,High:4,Medium:3,Low:2,Info:1};return(so[b.severity]||0)-(so[a.severity]||0);});
  filtered.forEach(function(f){
    var badgeClass=f.severity;
    var card=mk("div","finding "+f.severity);
    var head=mk("div","fhead");head.appendChild(txt("span","cis-ref","CIS "+f.cis));head.appendChild(txt("span","badge "+badgeClass,badgeClass));card.appendChild(head);
    card.appendChild(txt("div","ftitle",f.title));
    if(f.desc)card.appendChild(txt("div","desc",f.desc));
    if(f.affected&&f.affected>0){var aff=mk("div","affected");aff.textContent=f.affected+" affected resource(s)";card.appendChild(aff);}
    if(f.subs&&f.subs.length>0){
      var subLabels=f.subs.map(function(s){return report.subscriptions&&report.subscriptions[s]?report.subscriptions[s]:s.substring(0,8)+"...";});
      card.appendChild(txt("div","desc",""+subLabels.join(", ")));
    } else {
      card.appendChild(txt("div","desc","Tenant-wide"));
    }
    if(f.rec)card.appendChild(txt("div","rec",""+f.rec));
    el.appendChild(card);
  });
}

var filterBar=document.getElementById("filters");
["all","Critical","High","Medium","Low"].forEach(function(sev){
  var b=mk("button","btn"+(sev==="all"?" active":""));b.textContent=sev==="all"?"All":sev;
  b.addEventListener("click",function(){
    activeSev=sev;filterBar.querySelectorAll(".btn").forEach(function(x){x.classList.remove("active");});b.classList.add("active");renderFindings();
  });
  filterBar.appendChild(b);
});

if(report.subscriptions&&Object.keys(report.subscriptions).length>0){
  var subWrap=mk("div","sub-filter");
  var sel=document.createElement("select");
  var allOpt=document.createElement("option");allOpt.value="all";allOpt.textContent="All subscriptions";sel.appendChild(allOpt);
  var subKeys=Object.keys(report.subscriptions).sort(function(a,b){
    return(report.subscriptions[a]||a).localeCompare(report.subscriptions[b]||b);
  });
  subKeys.forEach(function(subId){
    var opt=document.createElement("option");opt.value=subId;
    opt.textContent=report.subscriptions[subId]||subId.substring(0,13)+"...";
    sel.appendChild(opt);
  });
  sel.addEventListener("change",function(){activeSub=sel.value;renderFindings();});
  subWrap.appendChild(sel);filterBar.appendChild(subWrap);
}

renderSections();
renderFindings();
})();
'@


# CSP hashes
$sha = [System.Security.Cryptography.SHA256]::Create()
$cssHash  = [Convert]::ToBase64String($sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($cssBlock)))
$jsHash   = [Convert]::ToBase64String($sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($jsBlock)))
$sha.Dispose()

$cspContent = "default-src 'none'; script-src 'sha256-$jsHash'; style-src 'sha256-$cssHash'; img-src data:; font-src data:; base-uri 'none'; form-action 'none'; object-src 'none'"


$htmlTemplate = @"
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Security-Policy" content="$cspContent">
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>NCS Dojo — AzResourceAnalyzer</title>
<style>$cssBlock</style>
</head>
<body>
<div class="container">
<header>
<div class="logo">$logoSvg</div>
<div class="header-content">
<h1>AzResourceAnalyzer</h1>
<div class="subtitle" id="subtitle"></div>
</div>
</header>
<div style="text-align:center">
<div class="donut-wrap">
<div class="donut-chart" id="donut-chart">
<svg viewBox="0 0 180 180" xmlns="http://www.w3.org/2000/svg">
<circle cx="90" cy="90" r="70" fill="none" stroke-width="20" id="donut-fail"/>
<circle cx="90" cy="90" r="70" fill="none" stroke-width="20" id="donut-pass"/>
</svg>
<div class="donut-center"><span class="donut-pct" id="donut-pct"></span><span class="donut-label">Compliance</span></div>
</div>
<div class="donut-legend"><span><span class="leg-dot" style="background:var(--pass)"></span><span id="leg-pass"></span></span><span><span class="leg-dot" style="background:var(--crit)"></span><span id="leg-fail"></span></span></div>
</div>
<div class="metrics" id="metrics-bar"></div>
</div>
<div class="sections" id="sections"></div>
<div class="filters" id="filters"><label>Filter:</label></div>
<div id="findings-list"></div>
<footer>Copyright &copy; NCS Dojo. All Rights Reserved.<br>AzResourceAnalyzer | Auditor: $($script:_ArTenantId) — Generated from Azure Resource Graph, Microsoft Graph, and Key Vault data plane exports.</footer>
</div>
<script type="application/json" id="report-data">$reportDataJson</script>
<script type="application/json" id="sections-data">$sectionMetricsJson</script>
<script type="application/json" id="findings-data">$findingsJson</script>
<script>$jsBlock</script>
</body>
</html>
"@



$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"

# JSON findings
$jsonPath = Join-Path $OutputPath "CIS_Findings_$timestamp.json"
@($findings) | ConvertTo-Json -Depth 5 | Set-Content -Path $jsonPath -Encoding UTF8

$htmlPath = Join-Path $OutputPath "CIS_Report_$timestamp.html"
$htmlTemplate | Set-Content -Path $htmlPath -Encoding UTF8

$csvPath = Join-Path $OutputPath "CIS_Findings_$timestamp.csv"
@($findings) | Where-Object { $_.Status -ne 'Pass' } |
    Select-Object CIS, SectionName, Title, Severity, Status, Description, @{N='AffectedResources';E={$_.AffectedResources -join '; '}}, @{N='Subscriptions';E={$_.Subscriptions -join '; '}}, Recommendation |
    Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8

$scoreColor = if ($overallScore -ge 80) { "Green" } elseif ($overallScore -ge 50) { "Yellow" } else { "Red" }
Write-Host ""
Write-Host " Score: $overallScore% | $checksPassed passed | $($failFindings.Count) findings" -ForegroundColor $scoreColor
Write-Host " Report: $htmlPath" -ForegroundColor Gray
Write-Host " Findings: $jsonPath" -ForegroundColor Gray
Write-Host " CSV: $csvPath" -ForegroundColor Gray
Write-Host ""

Write-Warning "Output files contain sensitive tenant data. Treat $OutputPath as confidential."
try {
    if ($IsMacOS) {
        & '/usr/bin/open' $htmlPath
    } elseif ($IsLinux) {
        & 'xdg-open' $htmlPath
    } else {
        Start-Process -FilePath $htmlPath
    }
} catch {
    Write-Host " Open the report manually: $htmlPath" -ForegroundColor Gray
}

}

# SIG # Begin signature block
# MII9GgYJKoZIhvcNAQcCoII9CzCCPQcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAhAeDBOJ23RKce
# Ltt25/2+rk2aYQfRFkDvAzbwX8XSeKCCIdwwggXMMIIDtKADAgECAhBUmNLR1FsZ
# lUgTecgRwIeZMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVu
# dGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAy
# MDAeFw0yMDA0MTYxODM2MTZaFw00NTA0MTYxODQ0NDBaMHcxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv
# c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
# b3JpdHkgMjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALORKgeD
# Bmf9np3gx8C3pOZCBH8Ppttf+9Va10Wg+3cL8IDzpm1aTXlT2KCGhFdFIMeiVPvH
# or+Kx24186IVxC9O40qFlkkN/76Z2BT2vCcH7kKbK/ULkgbk/WkTZaiRcvKYhOuD
# PQ7k13ESSCHLDe32R0m3m/nJxxe2hE//uKya13NnSYXjhr03QNAlhtTetcJtYmrV
# qXi8LW9J+eVsFBT9FMfTZRY33stuvF4pjf1imxUs1gXmuYkyM6Nix9fWUmcIxC70
# ViueC4fM7Ke0pqrrBc0ZV6U6CwQnHJFnni1iLS8evtrAIMsEGcoz+4m+mOJyoHI1
# vnnhnINv5G0Xb5DzPQCGdTiO0OBJmrvb0/gwytVXiGhNctO/bX9x2P29Da6SZEi3
# W295JrXNm5UhhNHvDzI9e1eM80UHTHzgXhgONXaLbZ7LNnSrBfjgc10yVpRnlyUK
# xjU9lJfnwUSLgP3B+PR0GeUw9gb7IVc+BhyLaxWGJ0l7gpPKWeh1R+g/OPTHU3mg
# trTiXFHvvV84wRPmeAyVWi7FQFkozA8kwOy6CXcjmTimthzax7ogttc32H83rwjj
# O3HbbnMbfZlysOSGM1l0tRYAe1BtxoYT2v3EOYI9JACaYNq6lMAFUSw0rFCZE4e7
# swWAsk0wAly4JoNdtGNz764jlU9gKL431VulAgMBAAGjVDBSMA4GA1UdDwEB/wQE
# AwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTIftJqhSobyhmYBAcnz1AQ
# T2ioojAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQwFAAOCAgEAr2rd5hnn
# LZRDGU7L6VCVZKUDkQKL4jaAOxWiUsIWGbZqWl10QzD0m/9gdAmxIR6QFm3FJI9c
# Zohj9E/MffISTEAQiwGf2qnIrvKVG8+dBetJPnSgaFvlVixlHIJ+U9pW2UYXeZJF
# xBA2CFIpF8svpvJ+1Gkkih6PsHMNzBxKq7Kq7aeRYwFkIqgyuH4yKLNncy2RtNwx
# AQv3Rwqm8ddK7VZgxCwIo3tAsLx0J1KH1r6I3TeKiW5niB31yV2g/rarOoDXGpc8
# FzYiQR6sTdWD5jw4vU8w6VSp07YEwzJ2YbuwGMUrGLPAgNW3lbBeUU0i/OxYqujY
# lLSlLu2S3ucYfCFX3VVj979tzR/SpncocMfiWzpbCNJbTsgAlrPhgzavhgplXHT2
# 6ux6anSg8Evu75SjrFDyh+3XOjCDyft9V77l4/hByuVkrrOj7FjshZrM77nq81YY
# uVxzmq/FdxeDWds3GhhyVKVB0rYjdaNDmuV3fJZ5t0GNv+zcgKCf0Xd1WF81E+Al
# GmcLfc4l+gcK5GEh2NQc5QfGNpn0ltDGFf5Ozdeui53bFv0ExpK91IjmqaOqu/dk
# ODtfzAzQNb50GQOmxapMomE2gj4d8yu8l13bS3g7LfU772Aj6PXsCyM2la+YZr9T
# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggabMIIEg6ADAgECAhMzAAGSX9Z/
# ePB9sQgIAAAAAZJfMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQwHhcNMjYwNjAxMTc1MjU4WhcNMjYwNjA0
# MTc1MjU4WjBfMQswCQYDVQQGEwJDQTEQMA4GA1UECBMHQWxiZXJ0YTEQMA4GA1UE
# BxMHQ2FsZ2FyeTEVMBMGA1UEChMMRGFycmVuIE1heWVzMRUwEwYDVQQDEwxEYXJy
# ZW4gTWF5ZXMwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC6vpd+d66I
# l6cpo6XDaTIvujkV3rekVoeig10+dG/OTYmX57PD3Rum9xsPx2BdX8lzvGnQq7Pa
# v6KacGC8N9f5XmVTJhc8k341QPXbSVsj61bYPVcV3ZnEr5zX2ciTGqXOgN/woD1A
# QccrGB8mCfWn9sNyVRTzNtoJz3/BrZl2Au+4nK2clIPX1a1k/6r2wTeFiBFAh7fp
# GZUHRNl0rcAzogwi5qYykxCDz7Wzu73tuUfhxcbn2XzoqfqKd83VSIN+osxnOmrS
# 0vMT5gnJC4B8RAGoGIqOle4wexQOn1Zrx8B2l1ZaIyLWf8bhcugcvTDT6o/oC1VV
# CVQqymqGTAhyIfIMSV31wMQDmEyXpa9SQFV3A4rUL+ILNDTGcca8GFZ6a1kqRe83
# xM/LTqupi6OSQ8ruxEHzJsW3No7J4rsNRljjSf7MGXFrKQjPBVFnjN2tHOPShdYB
# Z60NS6q6pVj+xq5FBVGHzV8XtKdV0e3jigsEyXkEl0jcQ8/K2LIxphMCAwEAAaOC
# AdMwggHPMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMDoGA1UdJQQzMDEG
# CisGAQQBgjdhAQAGCCsGAQUFBwMDBhkrBgEEAYI3YYuPpXGB3qaBQNv/tAST5rkV
# MB0GA1UdDgQWBBSvmzOJzfqlOfze0TuSX3fnqAlzZDAfBgNVHSMEGDAWgBSa8VR3
# dQyHFjdGoKzeefn0f8F46TBnBgNVHR8EYDBeMFygWqBYhlZodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVk
# JTIwQ1MlMjBFT0MlMjBDQSUyMDA0LmNybDB0BggrBgEFBQcBAQRoMGYwZAYIKwYB
# BQUHMAKGWGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj
# cm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwRU9DJTIwQ0ElMjAwNC5jcnQw
# VAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTANBgkqhkiG9w0B
# AQwFAAOCAgEABRiO07jmU6SjeUNkagy4Me5BFFY3b/5L1BRn/Gf9C8kXZjlIyzMH
# 7FcbslPw6ySkN3AIikz4rDwk8QQ+fc/dp+/ru35kqbbXm/bcK0AVjQ3ZOy8kRAjG
# qmFJff56lydGk3ybw1KCBHYgSs9KXVxC45348w4T9pjSViDcGko8g4Z+m19GW7ov
# 6srqiP72u5zVIwIHuxP/g08zaf2wynKz9duWzmZS8LftUawJI8il/5pLFjNBlCeJ
# q4MWNfI4N4d1a2VCEhs12HLqax/C/PyQP3qRUI7PB5IfKYltajewMbYVw+rQDE4f
# ugNEu14zUYPislGt36hd/Tk1Dn8uIT6P+9RyMyIX19bH9uTghE132/HEwuVNL34R
# kxJYMIG+ESF6fs+G9RDkWyGAi1FlLWl6ptszZ8ThL22/Sc11b6FlR8s+pCnjSQ6T
# KWnmWGEwoG43uqFXfLcchz2m8AEE3xvj+XpwkujUSrayTGy5E/olv946WtyEq4po
# NlZsND6CbrWT6VNd4VvmrRbYZUThdX3wjrpBssSRY3a7T9ra9KyYFFeAFulbvdaq
# o1DgPBVAWgbskiHEdkSrySbbhTHmCWltbGzlPU3kqTwSrjw0CUOfry9Ve7iYtitt
# 6LVu6DqEOY/3TBfLnubeZyjd1LpkIQWPU4u9AA4d9L2SUKY5Ziyo+18wggabMIIE
# g6ADAgECAhMzAAGSX9Z/ePB9sQgIAAAAAZJfMA0GCSqGSIb3DQEBDAUAMFoxCzAJ
# BgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNV
# BAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBFT0MgQ0EgMDQwHhcNMjYwNjAx
# MTc1MjU4WhcNMjYwNjA0MTc1MjU4WjBfMQswCQYDVQQGEwJDQTEQMA4GA1UECBMH
# QWxiZXJ0YTEQMA4GA1UEBxMHQ2FsZ2FyeTEVMBMGA1UEChMMRGFycmVuIE1heWVz
# MRUwEwYDVQQDEwxEYXJyZW4gTWF5ZXMwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAw
# ggGKAoIBgQC6vpd+d66Il6cpo6XDaTIvujkV3rekVoeig10+dG/OTYmX57PD3Rum
# 9xsPx2BdX8lzvGnQq7Pav6KacGC8N9f5XmVTJhc8k341QPXbSVsj61bYPVcV3ZnE
# r5zX2ciTGqXOgN/woD1AQccrGB8mCfWn9sNyVRTzNtoJz3/BrZl2Au+4nK2clIPX
# 1a1k/6r2wTeFiBFAh7fpGZUHRNl0rcAzogwi5qYykxCDz7Wzu73tuUfhxcbn2Xzo
# qfqKd83VSIN+osxnOmrS0vMT5gnJC4B8RAGoGIqOle4wexQOn1Zrx8B2l1ZaIyLW
# f8bhcugcvTDT6o/oC1VVCVQqymqGTAhyIfIMSV31wMQDmEyXpa9SQFV3A4rUL+IL
# NDTGcca8GFZ6a1kqRe83xM/LTqupi6OSQ8ruxEHzJsW3No7J4rsNRljjSf7MGXFr
# KQjPBVFnjN2tHOPShdYBZ60NS6q6pVj+xq5FBVGHzV8XtKdV0e3jigsEyXkEl0jc
# Q8/K2LIxphMCAwEAAaOCAdMwggHPMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQD
# AgeAMDoGA1UdJQQzMDEGCisGAQQBgjdhAQAGCCsGAQUFBwMDBhkrBgEEAYI3YYuP
# pXGB3qaBQNv/tAST5rkVMB0GA1UdDgQWBBSvmzOJzfqlOfze0TuSX3fnqAlzZDAf
# BgNVHSMEGDAWgBSa8VR3dQyHFjdGoKzeefn0f8F46TBnBgNVHR8EYDBeMFygWqBY
# hlZodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQl
# MjBJRCUyMFZlcmlmaWVkJTIwQ1MlMjBFT0MlMjBDQSUyMDA0LmNybDB0BggrBgEF
# BQcBAQRoMGYwZAYIKwYBBQUHMAKGWGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIwRU9D
# JTIwQ0ElMjAwNC5jcnQwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEW
# M2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5
# Lmh0bTANBgkqhkiG9w0BAQwFAAOCAgEABRiO07jmU6SjeUNkagy4Me5BFFY3b/5L
# 1BRn/Gf9C8kXZjlIyzMH7FcbslPw6ySkN3AIikz4rDwk8QQ+fc/dp+/ru35kqbbX
# m/bcK0AVjQ3ZOy8kRAjGqmFJff56lydGk3ybw1KCBHYgSs9KXVxC45348w4T9pjS
# ViDcGko8g4Z+m19GW7ov6srqiP72u5zVIwIHuxP/g08zaf2wynKz9duWzmZS8Lft
# UawJI8il/5pLFjNBlCeJq4MWNfI4N4d1a2VCEhs12HLqax/C/PyQP3qRUI7PB5If
# KYltajewMbYVw+rQDE4fugNEu14zUYPislGt36hd/Tk1Dn8uIT6P+9RyMyIX19bH
# 9uTghE132/HEwuVNL34RkxJYMIG+ESF6fs+G9RDkWyGAi1FlLWl6ptszZ8ThL22/
# Sc11b6FlR8s+pCnjSQ6TKWnmWGEwoG43uqFXfLcchz2m8AEE3xvj+XpwkujUSray
# TGy5E/olv946WtyEq4poNlZsND6CbrWT6VNd4VvmrRbYZUThdX3wjrpBssSRY3a7
# T9ra9KyYFFeAFulbvdaqo1DgPBVAWgbskiHEdkSrySbbhTHmCWltbGzlPU3kqTwS
# rjw0CUOfry9Ve7iYtitt6LVu6DqEOY/3TBfLnubeZyjd1LpkIQWPU4u9AA4d9L2S
# UKY5Ziyo+18wggcoMIIFEKADAgECAhMzAAAAFydFCQuLh6/GAAAAAAAXMA0GCSqG
# SIb3DQEBDAUAMGMxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xNDAyBgNVBAMTK01pY3Jvc29mdCBJRCBWZXJpZmllZCBDb2RlIFNp
# Z25pbmcgUENBIDIwMjEwHhcNMjYwMzI2MTgxMTMxWhcNMzEwMzI2MTgxMTMxWjBa
# MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSsw
# KQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ1MgRU9DIENBIDA0MIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgsdk/gMPZioBlcyfk6tDzJ+PRt4r
# SLGKW8ewpS0kRxXtURC3T3GdbCKljobEn8ussqhGqQpRh/SXvRVwNXEIGb76UG5I
# PkCJ1S6/9BD61QQsKzPepW0SNj8TXgsFxvS7MltoRuikIIp7Q5jQgaOM6QyK9++6
# ZVXUpYmZulAe6x8JrwZ0dNkE+rZ66lqtoocwepUSVUxM7odDmn8yDHjJ2DNPsfr3
# uRDix3X4qvh14jH/SW+2Cx7WIMhyIiQO201i6hUixmk4e2ZW8W7C1wPdTjq6BKb+
# zo8xbrt7ZKQvRX5QOA6dhLquPqj5sVKnxqfk19IC0SafTSTs8yC43Ew965BRRW8V
# L9ccoOmr4rxQy7aCgYTNk3dd/LphNaTTmnGp7kmLTxyHkB5geoWhYuuGrywS8E0w
# Jv0W4rfOtHBV0e9sKvuUIeIUpnsx6ilxEVj6VQXvgD6yeCKnPmj3jJiJKAlmUDtt
# h5yzRVBUl44sMiG4L5R/yyACRKk2n088Q2YCoZS1O86+oMLKt1jaXGECOjbsVp8I
# d1VQw8he6J0KirOS5e25XlTdGPFb6oBOOaacgW78Kjf0bp+XzAgkc92mDGNJGYSj
# vdnj+7eMx6meW0DAIGdLRNj8/429MIspFBfz3KDqqpN71S4kQ2LLer3dxhDDczKV
# FL0HLwRuOvgjiG8CAwEAAaOCAdwwggHYMA4GA1UdDwEB/wQEAwIBhjAQBgkrBgEE
# AYI3FQEEAwIBADAdBgNVHQ4EFgQUmvFUd3UMhxY3RqCs3nn59H/BeOkwVAYDVR0g
# BE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0
# LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4K
# AFMAdQBiAEMAQTASBgNVHRMBAf8ECDAGAQH/AgEAMB8GA1UdIwQYMBaAFNlBKbAP
# D2Ns72nX9c0pnqRIajDmMHAGA1UdHwRpMGcwZaBjoGGGX2h0dHA6Ly93d3cubWlj
# cm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQl
# MjBDb2RlJTIwU2lnbmluZyUyMFBDQSUyMDIwMjEuY3JsMH0GCCsGAQUFBwEBBHEw
# bzBtBggrBgEFBQcwAoZhaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# ZXJ0cy9NaWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ29kZSUyMFNpZ25pbmcl
# MjBQQ0ElMjAyMDIxLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAkHVaGf1NJt/Jdoim
# mRZbMWr6baaDi8mkdWvWStk0hdZDpxSYTA7HuipAoLL3qIhI101XOl7fOiCh5++j
# ZOamQdAV79ojEUNoIgCZmL2XJrLaGanwdjNynecJyYVCTrRf2+h7KknpWOp4axdO
# s6K9ZQ5g0IsQWXCwfc0dfkSkLKNY3pDcWLlJPh2jd5NUue6pNDv/2G5MFNJhCwlt
# ODebyAjGceU+XOzav+7i721YQnQ+39m2aQOFO7zpAdaKAeAGhEd6Y6CdDGneSxco
# ujWvafWbv4ay3jo1ORSLUuWMbKr5X18QE4Sde+gppGLLSkZsrUh2eyYSkX1envWX
# 7ZPzg2/wiuKRlQFarDn+N9+20BqzhxwkNyLzfYJp1Lg4fCXb24XqFjx8SDdRgebF
# ImOfOLVze8XQ/CwkrEaib0PHu2t4GVk4FYroEbNUFqvjdBvTY3uiR5TdQoyXoYHv
# h+TxpLSY2vo7hhK9D/rpEpHC+qmmcRUE4d0gyO9Zb1vvt25fxM3ekjvDfVHcPq3q
# Mr0Rwsk4krKZWUEgU1SXT5qN6gqRrshxbT6OQgZ9/xT04qiXdzPQR6KindBvSpoO
# nxnALxcJyzVwNpKL+9u8EZYy98qX6i+4gE/2J6cbpekcB0ZXDn/XQxoNUUb6/djT
# /wllVyG+vIHkdq71PzbH5rYxdcAwggeeMIIFhqADAgECAhMzAAAAB4ejNKN7pY4c
# AAAAAAAHMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jvc29mdCBJZGVudGl0
# eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAyMDAe
# Fw0yMTA0MDEyMDA1MjBaFw0zNjA0MDEyMDE1MjBaMGMxCzAJBgNVBAYTAlVTMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xNDAyBgNVBAMTK01pY3Jvc29m
# dCBJRCBWZXJpZmllZCBDb2RlIFNpZ25pbmcgUENBIDIwMjEwggIiMA0GCSqGSIb3
# DQEBAQUAA4ICDwAwggIKAoICAQCy8MCvGYgo4t1UekxJbGkIVQm0Uv96SvjB6yUo
# 92cXdylN65Xy96q2YpWCiTas7QPTkGnK9QMKDXB2ygS27EAIQZyAd+M8X+dmw6SD
# tzSZXyGkxP8a8Hi6EO9Zcwh5A+wOALNQbNO+iLvpgOnEM7GGB/wm5dYnMEOguua1
# OFfTUITVMIK8faxkP/4fPdEPCXYyy8NJ1fmskNhW5HduNqPZB/NkWbB9xxMqowAe
# WvPgHtpzyD3PLGVOmRO4ka0WcsEZqyg6efk3JiV/TEX39uNVGjgbODZhzspHvKFN
# U2K5MYfmHh4H1qObU4JKEjKGsqqA6RziybPqhvE74fEp4n1tiY9/ootdU0vPxRp4
# BGjQFq28nzawuvaCqUUF2PWxh+o5/TRCb/cHhcYU8Mr8fTiS15kRmwFFzdVPZ3+J
# V3s5MulIf3II5FXeghlAH9CvicPhhP+VaSFW3Da/azROdEm5sv+EUwhBrzqtxoYy
# E2wmuHKws00x4GGIx7NTWznOm6x/niqVi7a/mxnnMvQq8EMse0vwX2CfqM7Le/sm
# bRtsEeOtbnJBbtLfoAsC3TdAOnBbUkbUfG78VRclsE7YDDBUbgWt75lDk53yi7C3
# n0WkHFU4EZ83i83abd9nHWCqfnYa9qIHPqjOiuAgSOf4+FRcguEBXlD9mAInS7b6
# V0UaNwIDAQABo4ICNTCCAjEwDgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQD
# AgEAMB0GA1UdDgQWBBTZQSmwDw9jbO9p1/XNKZ6kSGow5jBUBgNVHSAETTBLMEkG
# BFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br
# aW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIA
# QwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUyH7SaoUqG8oZmAQHJ89Q
# EE9oqKIwgYQGA1UdHwR9MHsweaB3oHWGc2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9u
# JTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcmwwgcMG
# CCsGAQUFBwEBBIG2MIGzMIGBBggrBgEFBQcwAoZ1aHR0cDovL3d3dy5taWNyb3Nv
# ZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJZGVudGl0eSUyMFZlcmlm
# aWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAu
# Y3J0MC0GCCsGAQUFBzABhiFodHRwOi8vb25lb2NzcC5taWNyb3NvZnQuY29tL29j
# c3AwDQYJKoZIhvcNAQEMBQADggIBAH8lKp7+1Kvq3WYK21cjTLpebJDjW4ZbOX3H
# D5ZiG84vjsFXT0OB+eb+1TiJ55ns0BHluC6itMI2vnwc5wDW1ywdCq3TAmx0KWy7
# xulAP179qX6VSBNQkRXzReFyjvF2BGt6FvKFR/imR4CEESMAG8hSkPYso+GjlngM
# 8JPn/ROUrTaeU/BRu/1RFESFVgK2wMz7fU4VTd8NXwGZBe/mFPZG6tWwkdmA/jLb
# p0kNUX7elxu2+HtHo0QO5gdiKF+YTYd1BGrmNG8sTURvn09jAhIUJfYNotn7OlTh
# tfQjXqe0qrimgY4Vpoq2MgDW9ESUi1o4pzC1zTgIGtdJ/IvY6nqa80jFOTg5qzAi
# RNdsUvzVkoYP7bi4wLCj+ks2GftUct+fGUxXMdBUv5sdr0qFPLPB0b8vq516slCf
# RwaktAxK1S40MCvFbbAXXpAZnU20FaAoDwqq/jwzwd8Wo2J83r7O3onQbDO9TyDS
# tgaBNlHzMMQgl95nHBYMelLEHkUnVVVTUsgC0Huj09duNfMaJ9ogxhPNThgq3i8w
# 3DAGZ61AMeF0C1M+mU5eucj1Ijod5O2MMPeJQ3/vKBtqGZg4eTtUHt/BPjN74SsJ
# syHqAdXVS5c+ItyKWg3Eforhox9k3WgtWTpgV4gkSiS4+A09roSdOI4vrRw+p+fL
# 4WrxSK5nMYIalDCCGpACAQEwcTBaMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSswKQYDVQQDEyJNaWNyb3NvZnQgSUQgVmVyaWZp
# ZWQgQ1MgRU9DIENBIDA0AhMzAAGSX9Z/ePB9sQgIAAAAAZJfMA0GCWCGSAFlAwQC
# AQUAoF4wEAYKKwYBBAGCNwIBDDECMAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcC
# AQQwLwYJKoZIhvcNAQkEMSIEIKtDWPfKJ9nbbW/3MA4SAi2YcHKIBJRgMimTKlZ3
# ajOaMA0GCSqGSIb3DQEBAQUABIIBgB1xKUTUD4W/Be6ugy8SyPTt+TGwBHUWMO+Q
# 84iO1BFz4hD7aDfaPQlBdEUP3LwYU0O/IPJK0R2HeH63yroeK6VI5PHRaR9J3iAQ
# Rp6H1h+T9CGCmUrhjFPVCdOCVd9kKlJUDdsvY+49674PSNltVUIVajzKmIBooQxy
# uF593U3wku7H5KttMZoXRdvWR7odkSCbGJVQE6HdOT4rOYxSBj69AiMCiTXfMrh8
# pum6emed/oiEsaLuUslncxeThzBTHx5bh/Km+6wJyvedWM2f2MynEjvMWDQ6Js3T
# +N6vl1pvFP6MeRvEf9EW/LcO8YiE3r3pQyqqUoJ/8apIaeQKb1ZFI7bgmjGCwmab
# OsLEkgPOGTiycRn3lhsFfVhi7kV+wIlJIoYAczqS7K60CbrpOpxIisw853iQspCD
# V9c+VmbRR3qGcWD9iNnEO2A3OdozMK7i2cNwDxiD1F17FTottVzU6SsMQpJP/6S3
# 9qpZPLWN5XwHz3cCqYozK2L9aESqX6GCGBQwghgQBgorBgEEAYI3AwMBMYIYADCC
# F/wGCSqGSIb3DQEHAqCCF+0wghfpAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFiBgsq
# hkiG9w0BCRABBKCCAVEEggFNMIIBSQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBbdtuaDDOrUaAfzeEAbkC7ECVtUiD1aQwZhQB6KfWSaAIGaeiBVogV
# GBMyMDI2MDYwMjAwMzg0Ny4wMzRaMASAAgH0oIHhpIHeMIHbMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l
# cmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046N0QwMC0w
# NUUwLUQ5NDcxNTAzBgNVBAMTLE1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWUgU3Rh
# bXBpbmcgQXV0aG9yaXR5oIIPITCCB4IwggVqoAMCAQICEzMAAAAF5c8P/2YuyYcA
# AAAAAAUwDQYJKoZIhvcNAQEMBQAwdzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjFIMEYGA1UEAxM/TWljcm9zb2Z0IElkZW50aXR5
# IFZlcmlmaWNhdGlvbiBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDIwMB4X
# DTIwMTExOTIwMzIzMVoXDTM1MTExOTIwNDIzMVowYTELMAkGA1UEBhMCVVMxHjAc
# BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0
# IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCefOdSY/3gxZ8FfWO1BiKjHB7X55cz0RMFvWVGR3eR
# wV1wb3+yq0OXDEqhUhxqoNv6iYWKjkMcLhEFxvJAeNcLAyT+XdM5i2CgGPGcb95W
# JLiw7HzLiBKrxmDj1EQB/mG5eEiRBEp7dDGzxKCnTYocDOcRr9KxqHydajmEkzXH
# OeRGwU+7qt8Md5l4bVZrXAhK+WSk5CihNQsWbzT1nRliVDwunuLkX1hyIWXIArCf
# rKM3+RHh+Sq5RZ8aYyik2r8HxT+l2hmRllBvE2Wok6IEaAJanHr24qoqFM9WLeBU
# Sudz+qL51HwDYyIDPSQ3SeHtKog0ZubDk4hELQSxnfVYXdTGncaBnB60QrEuazvc
# ob9n4yR65pUNBCF5qeA4QwYnilBkfnmeAjRN3LVuLr0g0FXkqfYdUmj1fFFhH8k8
# YBozrEaXnsSL3kdTD01X+4LfIWOuFzTzuoslBrBILfHNj8RfOxPgjuwNvE6YzauX
# i4orp4Sm6tF245DaFOSYbWFK5ZgG6cUY2/bUq3g3bQAqZt65KcaewEJ3ZyNEobv3
# 5Nf6xN6FrA6jF9447+NHvCjeWLCQZ3M8lgeCcnnhTFtyQX3XgCoc6IRXvFOcPVrr
# 3D9RPHCMS6Ckg8wggTrtIVnY8yjbvGOUsAdZbeXUIQAWMs0d3cRDv09SvwVRd61e
# vQIDAQABo4ICGzCCAhcwDgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEA
# MB0GA1UdDgQWBBRraSg6NS9IY0DPe9ivSek+2T3bITBUBgNVHSAETTBLMEkGBFUd
# IAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w
# cy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsG
# AQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgw
# FoAUyH7SaoUqG8oZmAQHJ89QEE9oqKIwgYQGA1UdHwR9MHsweaB3oHWGc2h0dHA6
# Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElkZW50
# aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9y
# aXR5JTIwMjAyMC5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMIGBBggrBgEFBQcwAoZ1
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQl
# MjBJZGVudGl0eSUyMFZlcmlmaWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUy
# MEF1dGhvcml0eSUyMDIwMjAuY3J0MA0GCSqGSIb3DQEBDAUAA4ICAQBfiHbHfm21
# WhV150x4aPpO4dhEmSUVpbixNDmv6TvuIHv1xIs174bNGO/ilWMm+Jx5boAXrJxa
# gRhHQtiFprSjMktTliL4sKZyt2i+SXncM23gRezzsoOiBhv14YSd1Klnlkzvgs29
# XNjT+c8hIfPRe9rvVCMPiH7zPZcw5nNjthDQ+zD563I1nUJ6y59TbXWsuyUsqw7w
# XZoGzZwijWT5oc6GvD3HDokJY401uhnj3ubBhbkR83RbfMvmzdp3he2bvIUztSOu
# FzRqrLfEvsPkVHYnvH1wtYyrt5vShiKheGpXa2AWpsod4OJyT4/y0dggWi8g/tgb
# hmQlZqDUf3UqUQsZaLdIu/XSjgoZqDjamzCPJtOLi2hBwL+KsCh0Nbwc21f5xvPS
# wym0Ukr4o5sCcMUcSy6TEP7uMV8RX0eH/4JLEpGyae6Ki8JYg5v4fsNGif1OXHJ2
# IWG+7zyjTDfkmQ1snFOTgyEX8qBpefQbF0fx6URrYiarjmBprwP6ZObwtZXJ23jK
# 3Fg/9uqM3j0P01nzVygTppBabzxPAh/hHhhls6kwo3QLJ6No803jUsZcd4JQxiYH
# Hc+Q/wAMcPUnYKv/q2O444LO1+n6j01z5mggCSlRwD9faBIySAcA9S8h22hIAcRQ
# qIGEjolCK9F6nK9ZyX4lhthsGHumaABdWzCCB5cwggV/oAMCAQICEzMAAABV2d1p
# Jij5+OIAAAAAAFUwDQYJKoZIhvcNAQEMBQAwYTELMAkGA1UEBhMCVVMxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1
# YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjAwHhcNMjUxMDIzMjA0NjQ5WhcN
# MjYxMDIyMjA0NjQ5WjCB2zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUG
# A1UECxMeblNoaWVsZCBUU1MgRVNOOjdEMDAtMDVFMC1EOTQ3MTUwMwYDVQQDEyxN
# aWNyb3NvZnQgUHVibGljIFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0eTCCAiIw
# DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL25H5IeWUiz9DAlFmn2sPymaFWb
# vYkMfK+ScIWb3a1IvOlIwghUDjY0Gp6yMRhfYURiGS0GedIB6ywvuH6VBCX3+bdO
# FcAclgtv21jrpOjZmk4fSaT2Q3BszUfeUJa8o3xI7ZfoMY9dszTxHQAz6ZVX87fH
# GEVhQcfxW33IdPJOj/ae419qtYxT21MVmCfsTshgtWioQxmOW/vMC9/b+qgtBxSM
# f798vm3qfmhF6KCvFaHlivrM32hY16PGE3L0PFC+LM7vRxU7mTb+r76CeybvqOWk
# 4+dbKYftPhV1t/E5S/6wwXeYmu/Y7JC7Tnh2w45G5Y4pcM3oHMb/YuPRdOWa0v+R
# C2QgmNVWqjuxDiylWscXQDuaMtb29AcdGUVV9ZsRY2M2sthAtOdZOshiR5ufMtaH
# tiCkWv0jNfgUxrHurxzYuUNneWZ6EfQDgFAw8CSCKkSOK2c9jEop4ddVq10xvbqx
# drqMneVXvvIcXrPQAXj9j2ECpV2EwMb3Wnmpw00P78JpzPsk3Fs61ZvOGd/F1RcO
# Bu6f2TWdp7HL7+rq7tgHr13MldbfIWu4lpoYYE1gTQa1Yrg5XN4j7zs9klT2z3qo
# cmPzV8DWQgIHNh+aTs7bujMEMQyI7Xt1zPxZCgcR6H0tmmzU/9BxvsWbRalCQ2sY
# GyWupTdc4e7KY7kPAgMBAAGjggHLMIIBxzAdBgNVHQ4EFgQUVgRfEG3cCAPwyL+p
# yRbKwdesZbYwHwYDVR0jBBgwFoAUa2koOjUvSGNAz3vYr0npPtk92yEwbAYDVR0f
# BGUwYzBhoF+gXYZbaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwv
# TWljcm9zb2Z0JTIwUHVibGljJTIwUlNBJTIwVGltZXN0YW1waW5nJTIwQ0ElMjAy
# MDIwLmNybDB5BggrBgEFBQcBAQRtMGswaQYIKwYBBQUHMAKGXWh0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwUHVibGljJTIw
# UlNBJTIwVGltZXN0YW1waW5nJTIwQ0ElMjAyMDIwLmNydDAMBgNVHRMBAf8EAjAA
# MBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDBmBgNVHSAE
# XzBdMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wCAYGZ4EMAQQC
# MA0GCSqGSIb3DQEBDAUAA4ICAQBSHuGSVHvalCnFnlsqXIQefH1xP2SFr9g+Vz+f
# 5P7QeywjfQb5jUlSmd1XnJUDPe/MHxL7r3TEElL+mNtG6CDPAytStSFPXD9tTBtB
# MYh8Wqo64pH9qm361yIqeBH979mzWCkMQsTd0nM6dUl9B+7qiti+ToXwxIl39eYq
# LuYYfhD2mqqePXMzUKSQzkf73yYIVHP6nLJQz4aAmaWcfG9jg78sBkDV8KpW7Jgk
# tuLhphJEN1B+SVHjenPdcmrFXIUu/K4jK5ukfWaQIjuaXzSjBlNjC5tQN6adPfA3
# GxUwHPeR4ekL5If/9vBf13tmzBW+gy+0sNGTveb9IL9GU8iX8UvywsX62nhCCPRU
# hTigDBKdczRUrNrntBhowbfchBDFML8avRMRc9Gmc2JvIryX336SFQ51//q1UU2H
# MSJEMhWLJSIWJVhfUowsOa+PampIzETYfFvTu2mqKJUlWZXkGYxrdCvCczJcqeoa
# dpW1ul6kcdnDh228SQ8ZhDc6IRlM4iNd5SNoNgX+aom3wuGyjUaSaPZWxPB1G2NK
# iYhPLt0lPHg0Gskj1zhISY8UQkMMDr3o2JgRuT+wnJEDQUp55ddvhSkSoD6I9DL/
# s+TjIY/c9jLaW5xywJHqdKHUApRMsghv7kebSua1upmR+TquelFktDSOjVdSRkuy
# a4uoxTGCB0YwggdCAgEBMHgwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0Eg
# VGltZXN0YW1waW5nIENBIDIwMjACEzMAAABV2d1pJij5+OIAAAAAAFUwDQYJYIZI
# AWUDBAIBBQCgggSfMBEGCyqGSIb3DQEJEAIPMQIFADAaBgkqhkiG9w0BCQMxDQYL
# KoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDYwMjAwMzg0N1owLwYJKoZI
# hvcNAQkEMSIEIPGjMxAcHx3luFhzgBj04jT+pI3kctJKbc84ldRbj0ZdMIG5Bgsq
# hkiG9w0BCRACLzGBqTCBpjCBozCBoAQg2Lk8l2SGYru/ff7+D2qrJnkswcYdK6pG
# Ku7GGGr4/s0wfDBlpGMwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGlt
# ZXN0YW1waW5nIENBIDIwMjACEzMAAABV2d1pJij5+OIAAAAAAFUwggNhBgsqhkiG
# 9w0BCRACEjGCA1AwggNMoYIDSDCCA0QwggIsAgEBMIIBCaGB4aSB3jCB2zELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9z
# b2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNO
# OjdEMDAtMDVFMC1EOTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVibGljIFJTQSBU
# aW1lIFN0YW1waW5nIEF1dGhvcml0eaIjCgEBMAcGBSsOAwIaAxUAHTtUAYJlv7bg
# WVeRBo4X7FeHDeqgZzBlpGMwYTELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFB1YmxpYyBSU0Eg
# VGltZXN0YW1waW5nIENBIDIwMjAwDQYJKoZIhvcNAQELBQACBQDtyGQwMCIYDzIw
# MjYwNjAxMjAwNDAwWhgPMjAyNjA2MDIyMDA0MDBaMHcwPQYKKwYBBAGEWQoEATEv
# MC0wCgIFAO3IZDACAQAwCgIBAAICBaACAf8wBwIBAAICGlAwCgIFAO3JtbACAQAw
# NgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgC
# AQACAwGGoDANBgkqhkiG9w0BAQsFAAOCAQEAcYKZNNhAKlpwU6xnsnt6QPfdCycb
# LmT785CQMq0d2g9pBJe1cDNlWI8jbYzoojWScCTqxDumGqTLeNd1eU7UIxM1TWdy
# oEgREQkvvhTlp9LZnDB9wVDq0U9H5uTBsrTly4sew/UgNF6gNQRBLyHkVCRGLcYO
# +t4Gtqr0UK0LwEq3jBo0awGisryLm+MvCeY/ocHM5HixJWhgrDVXwCPJPlAs2qNN
# zJ/52gru8oyWSjxXBuUXf87j4ghf94DJisrUgwVua9qBnlN8lCgfur0eNA6mU3gv
# J2y+7kwF/6S8SvyrbPTFrdxYRY8q/9kVYhlmdjfCkMcBwj+65jClFmbUvjANBgkq
# hkiG9w0BAQEFAASCAgBQ2wa5/7cZZha8o7JGfA2biDKpchws6Iss7zj9OVXon8m1
# Oolbbq0WCBZBw5hz/sMnbg/Jy2BoHkCE6TBTmykVpJbHknVIgPvWYWJOd55a5/C4
# XP226sAYgF5CL+qTBSrBXQWu6ozk/jAzcLtUNurghLiw3whPI95olcfn63FwpqUP
# jobLWx6O6a/KLz9lZYEIALVUt1ghuuoqfe5Pa1IJVcObKHjschIsn37pHetACUcp
# MmcfcvDKF9KwAjgy+P8RvC6TvWTJv6vVfG7LCMI6XQUEpUWB0CLw9o0Dp5YV9OwH
# 0414hagIYsQQLM3KMpTGXpovusBg7YIoQZPSFsxbTrJFKbZNohzHhmXWzgjXCDUe
# vbDDeQPre3KYhA7I54b4jXJnRsnPljymKq8pwlEeBMzW8NVoCyb8WhGrMhbuxaWK
# 3kIcZZYq8Q5dipfYNaMwP5s0z7H7TPtt6rpZlcUvhIAqHlLrnI34rvD5SREpYUhp
# aAZGoabYPl2a+pdSCGgKugCLA4C9sMr+fdrTdig0auw7A2WqRQOyW+F2lsRNmKcB
# Ilhu7ycRB9jbAQvFEqNgUTFx+zEoUEaKd6d5q5TuBJk/8KzvABEa14E+IMCoI8rz
# wbZ1cPPcjTKE0JwR7R+M93nr7ASp9XQlPfdCw/bf6r/4zO+CTdL3Gs8ruiLgPA==
# SIG # End signature block