Invoke-LicenseScan.ps1

<#
.SYNOPSIS
    Free, read-only Microsoft 365 unused-license scan.
    Cross-checks every assigned license against real activity and reports the
    seats you're paying for but nobody is using.
 
.DESCRIPTION
    Community edition of the ServerBridge License Auditor. Connects to Microsoft
    Graph with delegated, READ-ONLY scopes via device-code sign-in (nothing to
    register, no app, no secrets). It never writes, removes, or changes anything
    in your tenant, and it stores nothing.
 
    Activity signal (chosen automatically):
      * If the tenant has Microsoft Entra ID P1/P2, it uses directory
        sign-in activity (signInActivity) - the cleanest signal.
      * Otherwise it falls back to the Microsoft 365 usage reports
        (getOffice365ActiveUserDetail), which need no premium license.
      * Disabled accounts that still hold paid licenses are always flagged -
        that works on every tenant regardless of the above.
 
    Outputs:
      * A console summary of wasted spend by SKU
      * A CSV of every dormant licensed user (for your own follow-up)
 
    What it deliberately does NOT do (that's the paid ServerBridge License Auditor):
      * A formatted PDF report, costed recommended actions, scheduled re-audits
        with drift tracking, multi-tenant roll-up, and support.
    See https://server-bridge.com/license-auditor.html
 
.PARAMETER InactiveDays
    A licensed user with no activity in this many days is counted as dormant.
    Default 90. Range 1-3650. (The usage-report fallback resolves to the nearest
    supported window: 30, 90 or 180 days.)
 
.PARAMETER OutputCsv
    Path for the CSV export. Default: .\license-scan_<tenant>_<date>.csv
 
.PARAMETER IncludeGuests
    Include guest (external) users. Off by default.
 
.PARAMETER PassThru
    Emit the dormant-seat objects to the pipeline in addition to the CSV.
 
.EXAMPLE
    .\Invoke-LicenseScan.ps1
 
.EXAMPLE
    .\Invoke-LicenseScan.ps1 -InactiveDays 60 -OutputCsv C:\reports\waste.csv
 
.NOTES
    Requires the Microsoft Graph PowerShell SDK:
        Install-Module Microsoft.Graph -Scope CurrentUser
    Delegated scopes requested (all read-only):
        User.Read.All, Organization.Read.All, AuditLog.Read.All, Reports.Read.All
    You must be able to consent to these (Global Reader is sufficient).
    Prices are public list-price ESTIMATES (USD/user/month) - adjust the price
    table below to match your actual contract for exact figures.
 
    Project: https://github.com/lbcrowe-del/ServerBridge-LicenseScan
    License: MIT
#>


[CmdletBinding()]
param(
    [ValidateRange(1, 3650)]
    [int]$InactiveDays = 90,

    [string]$OutputCsv,

    [switch]$IncludeGuests,

    [switch]$PassThru
)

$script:RequiredScopes = @('User.Read.All', 'Organization.Read.All', 'AuditLog.Read.All', 'Reports.Read.All')

# --- Approximate public list prices, USD / user / month, keyed by SkuPartNumber.
# --- ESTIMATES so the tool can put a dollar figure on waste out of the box.
# --- Edit to match your real per-seat cost for precise numbers.
$script:SkuPrice = @{
    'O365_BUSINESS_ESSENTIALS' = 6.00    # Microsoft 365 Business Basic
    'O365_BUSINESS_PREMIUM'    = 12.50   # Microsoft 365 Business Standard
    'SPB'                      = 22.00   # Microsoft 365 Business Premium
    'ENTERPRISEPACK'           = 36.00   # Office 365 E3
    'ENTERPRISEPREMIUM'        = 57.00   # Office 365 E5
    'SPE_E3'                   = 36.00   # Microsoft 365 E3
    'SPE_E5'                   = 57.00   # Microsoft 365 E5
    'SPE_F1'                   = 8.00    # Microsoft 365 F3
    'DESKLESSPACK'             = 4.00    # Office 365 F3
    'EXCHANGESTANDARD'         = 4.00    # Exchange Online Plan 1
    'EXCHANGEENTERPRISE'       = 8.00    # Exchange Online Plan 2
    'MCOEV'                    = 8.00    # Teams Phone Standard
    'POWER_BI_PRO'             = 10.00   # Power BI Pro
    'POWER_BI_PREMIUM_PER_USER' = 20.00  # Power BI Premium Per User
    'PROJECTPROFESSIONAL'      = 30.00   # Project Plan 3
    'PROJECTPREMIUM'           = 55.00   # Project Plan 5
    'VISIOCLIENT'              = 15.00   # Visio Plan 2
    'Microsoft_365_Copilot'    = 30.00   # Microsoft 365 Copilot
    'FLOW_FREE'                = 0.00    # Power Automate (free)
    'TEAMS_EXPLORATORY'        = 0.00    # Teams Exploratory (free)
    'POWERAPPS_VIRAL'          = 0.00    # Power Apps (free/viral)
}
$script:DefaultPrice = 20.00  # fallback for SKUs not in the table

# ---------------------------------------------------------------------------
# Pure, testable helpers (no Graph calls - safe to dot-source)
# ---------------------------------------------------------------------------

function Get-SkuMonthlyPrice {
    [CmdletBinding()]
    param([Parameter(Mandatory)][string]$PartNumber)
    if ($script:SkuPrice.ContainsKey($PartNumber)) { return [double]$script:SkuPrice[$PartNumber] }
    return [double]$script:DefaultPrice
}

function Test-UserDormant {
    <# $true when a user has no activity since $Cutoff (never-active counts as dormant). #>
    [CmdletBinding()]
    param(
        [Parameter()][Nullable[datetime]]$LastActivity,
        [Parameter(Mandatory)][datetime]$Cutoff
    )
    if ($null -eq $LastActivity) { return $true }
    return ($LastActivity -lt $Cutoff)
}

function Get-DormantLicenseRow {
    <#
    Pure projection: given normalized user objects (DisplayName, UserPrincipalName,
    UserType, AccountEnabled, LastActivity, AssignedLicenses[].SkuId), a
    SkuId->PartNumber map and a cutoff, emit one row per dormant user per priced
    license. No Graph, no I/O - unit-testable.
 
    A user is dormant when disabled, OR (when an activity signal is available) when
    last activity is older than the cutoff or unknown. With no activity signal only
    disabled accounts are flagged.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][AllowNull()][AllowEmptyCollection()][object[]]$Users,
        [Parameter(Mandatory)][hashtable]$SkuMap,
        [Parameter(Mandatory)][datetime]$Cutoff,
        [bool]$ActivitySignalAvailable = $true,
        [switch]$IncludeGuests
    )

    foreach ($u in @($Users | Where-Object { $null -ne $_ })) {
        if (-not $IncludeGuests -and $u.UserType -eq 'Guest') { continue }

        $dormant = $false
        $reason = $null
        if (-not $u.AccountEnabled) {
            $dormant = $true; $reason = 'disabled'
        } elseif ($ActivitySignalAvailable -and (Test-UserDormant -LastActivity $u.LastActivity -Cutoff $Cutoff)) {
            $dormant = $true; $reason = 'inactive'
        }
        if (-not $dormant) { continue }

        foreach ($lic in $u.AssignedLicenses) {
            $part = $SkuMap[$lic.SkuId]
            if (-not $part) { continue }
            $price = Get-SkuMonthlyPrice -PartNumber $part
            if ($price -le 0) { continue }  # skip free SKUs

            [pscustomobject]@{
                DisplayName       = $u.DisplayName
                UserPrincipalName = $u.UserPrincipalName
                UserType          = $u.UserType
                AccountEnabled    = $u.AccountEnabled
                Reason            = $reason
                Sku               = $part
                LastActivity      = if ($u.LastActivity) { ([datetime]$u.LastActivity).ToString('yyyy-MM-dd') } else { 'never/unknown' }
                MonthlyCost       = $price
                AnnualCost        = [math]::Round($price * 12, 2)
            }
        }
    }
}

# ---------------------------------------------------------------------------
# Graph I/O (kept in functions so tests can dot-source without a tenant)
# ---------------------------------------------------------------------------

function Get-SignInActivityMap {
    <#
    Returns @{ ok=$bool; map=@{ upn(lower) -> [datetime] } }. ok=$false when the
    tenant lacks Entra ID P1 (signInActivity is premium-only) so the caller can
    fall back. Other errors are rethrown.
    #>

    [CmdletBinding()] param()
    $map = @{}
    try {
        $users = Get-MgUser -All -Property 'userPrincipalName,signInActivity' `
            -Filter 'assignedLicenses/$count ne 0' -ConsistencyLevel eventual `
            -CountVariable siaCount -ErrorAction Stop
        foreach ($u in $users) {
            if ($u.UserPrincipalName) { $map[$u.UserPrincipalName.ToLower()] = $u.SignInActivity.LastSignInDateTime }
        }
        return @{ ok = $true; map = $map }
    } catch {
        $msg = "$($_.Exception.Message)"
        if ($msg -match 'NonPremium' -or $msg -match 'premium') { return @{ ok = $false; map = $map } }
        throw
    }
}

function Get-UsageReportActivityMap {
    <#
    Fallback that needs no Entra premium: pulls the Microsoft 365 active-user
    detail report and returns @{ ok=$bool; map=@{ upn(lower) -> [datetime] } }.
    ok=$false when the tenant de-identifies report data (UPNs masked) so activity
    can't be matched to users.
    #>

    [CmdletBinding()] param([int]$InactiveDays = 90)

    # NB: the Graph reports API keeps the legacy "Office365" name for this function.
    $period = if ($InactiveDays -le 30) { 'D30' } elseif ($InactiveDays -le 90) { 'D90' } else { 'D180' }
    $uri = "https://graph.microsoft.com/v1.0/reports/getOffice365ActiveUserDetail(period='$period')"
    $resp = Invoke-MgGraphRequest -Method GET -Uri $uri -OutputType HttpResponseMessage -ErrorAction Stop
    $csv = $resp.Content.ReadAsStringAsync().Result
    $rows = @($csv | ConvertFrom-Csv)

    $map = @{}
    $sawUpn = $false
    foreach ($r in $rows) {
        $upn = $r.'User Principal Name'
        if (-not $upn) { continue }
        if ($upn -like '*@*') { $sawUpn = $true }
        # Take the most recent activity across every per-service "* Last Activity Date" column.
        $maxDate = $null
        foreach ($p in $r.PSObject.Properties) {
            if ($p.Name -like '*Last Activity Date' -and $p.Value) {
                $d = $p.Value -as [datetime]
                if ($d -and ($null -eq $maxDate -or $d -gt $maxDate)) { $maxDate = $d }
            }
        }
        $map[$upn.ToLower()] = $maxDate
    }
    if (-not $sawUpn -and $rows.Count -gt 0) { return @{ ok = $false; map = @{} } }
    return @{ ok = $true; map = $map }
}

function Test-SignInTimedOut {
    <#
    $true when Connect-MgGraph gave up because the device code wasn't used in time. Microsoft's
    Graph PowerShell module allows 2 minutes and the limit can't be changed, so the scan offers a
    new code instead of making the admin start over.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory)][AllowEmptyString()][string]$Message)
    return ($Message -match 'timed out after \d+ seconds')
}

function ConvertTo-SafeCsvValue {
    <#
    Spreadsheet apps run a cell as a formula when it starts with = + - @ (or tab/CR).
    Directory text such as a guest's display name is attacker-controllable, so prefix a
    single quote to keep it as plain text when the CSV is opened in Excel.
    #>

    [CmdletBinding()]
    param([Parameter(Mandatory)][AllowNull()][AllowEmptyString()][object]$Value)
    if ($Value -is [string] -and $Value -match '^[=+\-@\t\r]') { return "'" + $Value }
    return $Value
}

function Get-ConcealedNamesHelpText {
    <# The one-minute fix for de-identified usage reports, as lines of text (testable). #>
    [CmdletBinding()] param()
    @(
        'Microsoft is hiding user names in your usage reports, so this scan cannot tell who is inactive.'
        'Fix it in about a minute (needs a Global Administrator):'
        ' 1. Open https://admin.microsoft.com'
        ' 2. Go to Settings > Org settings > Services > Reports'
        ' 3. Untick "Conceal user, group, and site names in all reports", then click Save'
        'This only changes what admins see in Microsoft''s own reports. You can turn it back on after the audit.'
    )
}

function Test-GraphSdkPresent {
    <#
    $true when the Microsoft Graph PowerShell SDK is installed; otherwise prints the install hint
    and returns $false. Shared so every command in this module gives the same first-run message.
    #>

    [CmdletBinding()] param()
    if (Get-Module -ListAvailable -Name Microsoft.Graph.Authentication) { return $true }
    Write-Host 'Microsoft Graph PowerShell SDK not found.' -ForegroundColor Yellow
    Write-Host 'Install it with: Install-Module Microsoft.Graph -Scope CurrentUser' -ForegroundColor Yellow
    return $false
}

function Connect-ScanGraph {
    <#
    Device-code sign-in with the read-only scopes, offering a fresh code when Microsoft's
    2-minute limit expires. Returns $true once connected, $false if the user quit or it failed.
 
    Shared by every command in this module: this retry behaviour was a real bug fix (1.1.1) and
    must not be reimplemented per command.
    #>

    [CmdletBinding()] param()
    $signedIn = $false
    while (-not $signedIn) {
        try {
            Write-Host 'Signing you in. A device code will appear below - enter it within 2 minutes.' -ForegroundColor Cyan
            Connect-MgGraph -Scopes $script:RequiredScopes -UseDeviceCode -NoWelcome -ContextScope Process -ErrorAction Stop
            $signedIn = $true
        } catch {
            $message = "$($_.Exception.Message)"
            if ((Test-SignInTimedOut -Message $message) -and -not [Console]::IsInputRedirected) {
                Write-Host 'The sign-in code expired (Microsoft allows 2 minutes).' -ForegroundColor Yellow
                $answer = Read-Host 'Press Enter for a new code, or type Q to quit'
                if ($answer -match '^\s*[qQ]') { return $false }
                continue
            }
            Write-Host "Sign-in failed: $message" -ForegroundColor Red
            return $false
        }
    }
    if (-not (Get-MgContext)) { Write-Host 'Sign-in cancelled.' -ForegroundColor Red; return $false }
    return $true
}

function Resolve-ActivityMap {
    <#
    Picks the best available activity signal and returns
    @{ available=$bool; map=@{ upn(lower) -> [datetime] }; signalName=[string] }.
 
    Prefers directory sign-in activity (needs Entra ID P1), else the Microsoft 365 usage reports.
    When the tenant conceals user names in those reports it shows the one-minute fix and re-checks
    on Enter without a new sign-in. available=$false means the admin skipped, so only disabled
    accounts can be judged.
 
    Shared by every command in this module.
    #>

    [CmdletBinding()] param([int]$InactiveDays = 90)

    $sia = Get-SignInActivityMap
    if ($sia.ok) {
        return @{ available = $true; map = $sia.map; signalName = 'directory sign-in activity' }
    }

    Write-Host 'Sign-in activity needs Entra ID P1; falling back to Microsoft 365 usage reports...' -ForegroundColor Yellow
    $rep = $null
    $attempt = 0
    while ($true) {
        try {
            $rep = Get-UsageReportActivityMap -InactiveDays $InactiveDays
        } catch {
            $rep = $null
            Write-Host "Usage reports unavailable: $($_.Exception.Message)" -ForegroundColor Yellow
            break
        }
        if ($rep.ok) { break }

        $attempt++
        Write-Host ''
        if ($attempt -gt 1) {
            Write-Host 'Names are still hidden. Microsoft can take a few minutes to apply the change.' -ForegroundColor Yellow
        } else {
            Get-ConcealedNamesHelpText | ForEach-Object { Write-Host $_ -ForegroundColor Yellow }
        }
        if ([Console]::IsInputRedirected) { break }
        $answer = Read-Host 'Press Enter when done to check again (no new sign-in), or type S to skip'
        if ($answer -match '^\s*[sS]') { break }
    }

    if ($rep -and $rep.ok) {
        return @{ available = $true; map = $rep.map; signalName = 'Microsoft 365 usage reports' }
    }
    return @{ available = $false; map = @{}; signalName = $null }
}

function Invoke-LicenseScanMain {
    [CmdletBinding()]
    param(
        [int]$InactiveDays,
        [string]$OutputCsv,
        [switch]$IncludeGuests,
        [switch]$PassThru
    )

    Write-Host ''
    Write-Host 'ServerBridge - Free M365 License Scan (read-only)' -ForegroundColor Green
    Write-Host '-------------------------------------------------' -ForegroundColor DarkGray

    if (-not (Test-GraphSdkPresent)) { return }
    if (-not (Connect-ScanGraph)) { return }

    try {
        $org = Get-MgOrganization -ErrorAction Stop
        $tenantName = ($org.DisplayName | Select-Object -First 1)
        Write-Host "Connected to: $tenantName (signed in as $((Get-MgContext).Account))" -ForegroundColor Green

        $skuMap = @{}
        foreach ($s in (Get-MgSubscribedSku -All -ErrorAction Stop)) { $skuMap[$s.SkuId] = $s.SkuPartNumber }

        $select = 'id,displayName,userPrincipalName,userType,accountEnabled,assignedLicenses'
        $rawUsers = Get-MgUser -All -Property $select -Filter 'assignedLicenses/$count ne 0' `
            -ConsistencyLevel eventual -CountVariable userCount -ErrorAction Stop
    } catch {
        Write-Host "Graph read failed: $($_.Exception.Message)" -ForegroundColor Red
        Write-Host 'Confirm you consented to User.Read.All, Organization.Read.All, AuditLog.Read.All and Reports.Read.All.' -ForegroundColor Yellow
        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
        return
    }

    # Resolve an activity signal: prefer sign-in activity (premium), fall back to usage reports.
    $signal = Resolve-ActivityMap -InactiveDays $InactiveDays
    $activity = $signal.map
    $signalAvailable = $signal.available
    $signalName = $signal.signalName
    if (-not $signalAvailable) {
        Write-Host ''
        Write-Host 'PARTIAL AUDIT: only disabled accounts that still hold licenses can be checked.' -ForegroundColor Yellow
    }

    if ($signalAvailable) {
        Write-Host "Flagging licensed users dormant $InactiveDays+ days (via $signalName)..." -ForegroundColor Cyan
    }

    $normalized = foreach ($u in $rawUsers) {
        [pscustomobject]@{
            DisplayName       = $u.DisplayName
            UserPrincipalName = $u.UserPrincipalName
            UserType          = $u.UserType
            AccountEnabled    = $u.AccountEnabled
            AssignedLicenses  = $u.AssignedLicenses
            LastActivity      = if ($u.UserPrincipalName) { $activity[$u.UserPrincipalName.ToLower()] } else { $null }
        }
    }

    if ($signalAvailable) {
        $resolved = @($normalized | Where-Object { $_.LastActivity }).Count
        Write-Host (" activity resolved for {0} of {1} licensed users" -f $resolved, @($normalized).Count) -ForegroundColor DarkGray
    }

    $cutoff = (Get-Date).AddDays(-1 * $InactiveDays)
    $dormant = @(Get-DormantLicenseRow -Users $normalized -SkuMap $skuMap -Cutoff $cutoff `
            -ActivitySignalAvailable $signalAvailable -IncludeGuests:$IncludeGuests)

    Write-Host ''
    Write-Host 'Wasted spend by license' -ForegroundColor Green
    Write-Host '-----------------------' -ForegroundColor DarkGray
    if ($dormant.Count -eq 0) {
        Write-Host ' Nothing obvious to reclaim.' -ForegroundColor Green
    } else {
        $dormant | Group-Object Sku | ForEach-Object {
            [pscustomobject]@{
                License      = $_.Name
                DormantSeats = $_.Count
                'Annual $'   = [int][math]::Round((($_.Group | Measure-Object AnnualCost -Sum).Sum), 0)
            }
        } | Sort-Object 'Annual $' -Descending | Format-Table -AutoSize
    }

    $totalAnnual = [math]::Round((($dormant | Measure-Object AnnualCost -Sum).Sum), 0)
    Write-Host (" Reclaimable seats : {0:N0}" -f $dormant.Count) -ForegroundColor White
    Write-Host (" Wasted spend : `${0:N0} / year" -f $totalAnnual) -ForegroundColor Green
    Write-Host ''

    if (-not $OutputCsv) {
        $safeTenant = ($tenantName -replace '[^\w]', '')
        $OutputCsv = Join-Path (Get-Location) ("license-scan_{0}_{1}.csv" -f $safeTenant, (Get-Date -Format 'yyyyMMdd'))
    }
    if ($dormant.Count -gt 0) {
        $dormant | Sort-Object AnnualCost -Descending | ForEach-Object {
            $row = $_.PSObject.Copy()
            foreach ($name in 'DisplayName', 'UserPrincipalName', 'UserType', 'Sku') {
                $row.$name = ConvertTo-SafeCsvValue -Value $row.$name
            }
            $row
        } | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
        Write-Host "Full per-user list saved to:" -ForegroundColor Cyan
        Write-Host " $OutputCsv" -ForegroundColor White
        Write-Host ''
    }

    Write-Host 'Prices are list-price estimates - edit the price table for exact figures.' -ForegroundColor DarkGray
    Write-Host 'Want the PDF report, costed recommended actions and scheduled re-audits?' -ForegroundColor DarkGray
    Write-Host ' https://server-bridge.com/license-auditor.html' -ForegroundColor Cyan
    Write-Host ''

    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    if ($PassThru) { $dormant }
}

# Run unless dot-sourced (e.g. by Pester tests, where InvocationName is '.').
if ($MyInvocation.InvocationName -ne '.') {
    Invoke-LicenseScanMain -InactiveDays $InactiveDays -OutputCsv $OutputCsv `
        -IncludeGuests:$IncludeGuests -PassThru:$PassThru
}

# SIG # Begin signature block
# MII2xAYJKoZIhvcNAQcCoII2tTCCNrECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDdHpgzpY+m03ZZ
# zD/JSwFm3055JQ5Ar+O3W1yG3y6kE6CCG2MwggXMMIIDtKADAgECAhBUmNLR1FsZ
# 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
# 03u4aUoqlmZpxJTG9F9urJh4iIAGXKKy7aIwggbBMIIEqaADAgECAhMzAAaJW8dB
# dB4thrTTAAAABolbMA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBBT0MgQ0EgMDQwHhcNMjYwOTE1MTYwNjE3WhcNMjYwOTE4
# MTYwNjE3WjCBgjELMAkGA1UEBhMCVVMxETAPBgNVBAgTCEtlbnR1Y2t5MRIwEAYD
# VQQHEwlMZXhpbmd0b24xJTAjBgNVBAoTHExlZSBDcm93ZSBTb2Z0d2FyZSBTb2x1
# dGlvbnMxJTAjBgNVBAMTHExlZSBDcm93ZSBTb2Z0d2FyZSBTb2x1dGlvbnMwggGi
# MA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCSd2o228mobihJsbTETtFQiSIC
# 8UbmRzlNiSApgpG7cUd3Po8ivrlNphmBVqMexSQ8890yYs2Iqi27XypNC7eHrflt
# PNl0g2q08orcJWWN3SoHlr8neihm6T7vUkygzJONHsuHSG1rip5A3VmtxdaNPaHn
# TTP29w4wNv8RUUq8rUo1FEYJp1ns63YFDaqFitGIffnWvPyDIX5t/Lu9R8NFxXU+
# OYzRFPKC3Pi2eKnDBVdzAxzMWk5hNapcp57Ps9LIt/xKaM48DAwfjekjL0qIM7oy
# KgxpJ6UrbyMATX+7Qhpw3GmMQZcwzGGPRGTOGqMHrqpOqIcHvPC0j9xYn/0xEBoC
# JT+jCrdh7xt8aGafPcqxDg1ohPlqsdbZKoMSvtzCjoYOLi+/YXr2XvykYfWkWgwg
# 3BaiXbjPsJUS6DPFhFnoz4vFcYrUfeBBmvN0s4FAbk4O27/6iMehA/Xpd5BGP5sX
# PrXGFjyN/0psyna4yO+4EaFOkTDp72gIDMabIZkCAwEAAaOCAdUwggHRMAwGA1Ud
# EwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMDwGA1UdJQQ1MDMGCisGAQQBgjdhAQAG
# CCsGAQUFBwMDBhsrBgEEAYI3YfX3xXqCm/n9aIL52tE6goGumFEwHQYDVR0OBBYE
# FJQn6rcAFckIFI60xdwIYMH3RYlqMB8GA1UdIwQYMBaAFGslQd77a3z9GIAKLX+P
# dl2qcz24MGcGA1UdHwRgMF4wXKBaoFiGVmh0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMElEJTIwVmVyaWZpZWQlMjBDUyUyMEFP
# QyUyMENBJTIwMDQuY3JsMHQGCCsGAQUFBwEBBGgwZjBkBggrBgEFBQcwAoZYaHR0
# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBJ
# RCUyMFZlcmlmaWVkJTIwQ1MlMjBBT0MlMjBDQSUyMDA0LmNydDBUBgNVHSAETTBL
# MEkGBFUdIAAwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMA0GCSqGSIb3DQEBDAUAA4ICAQCx
# D2ogYhKLISVDZbdmf9Cam9XrIk3dptSXa+J91tIlShUdV6M2lzL4f5bL+kIGpqRX
# y6J1hWQdI+iexz38wG/xG+hkyiC2zRU+VrzKqHdrLR6qgPvb8z2M4wvgtfVEDsSX
# GJiR3ZPIxclPxqfMB7wFS3lXiLgor9qshZjteVrfJep7i8SZnnEh6UzftKhFQ5Ou
# mn9+iqypnP0IA7N4pdwBQX8Kb7caH7PzCbg+8w9z5T93PxEA2Z9UpLTANXGKR4Kr
# LCBfoF0Wrot8K1TZh87peRCNvCapEAFFdJQJjoTFzavSuLPVuBcCWKlhaueZc4pL
# kPIyxEhKnk3fXnhsezSbSMFxa0nAWidg/E0VtemcicCaaU/gAw74F4+u5RxZ/b1M
# fjkYRNbpB/Kh5G418/z/4uCkAvnRCvRpdU8XizotUGyeXBJshkOoZHc5k7AfXEK+
# 1FUyIl4bv+JQuAZ0Q9WtzRagWH8/5o8YfFL326wzN5UWORBSTWeBRFfB5E0FzG4A
# zzqMtyUksU9qNuvMx9nywO35QwauLcqeqXEsLRWS/WZAT3J0ByuFgcXgsJmlfy6i
# yFzbC9DczsUlZuyEKFlB5Cqw+BtaO+ZJcjP6LtSA9QWGHGCIdfq8U2xUSNbIG0yu
# IdRonMxAo0u+t/KswaU0zI0svOa1TmAGTXhQkH3+4jCCBygwggUQoAMCAQICEzMA
# AAAWMZKNkgJle5oAAAAAABYwDQYJKoZIhvcNAQEMBQAwYzELMAkGA1UEBhMCVVMx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE0MDIGA1UEAxMrTWljcm9z
# b2Z0IElEIFZlcmlmaWVkIENvZGUgU2lnbmluZyBQQ0EgMjAyMTAeFw0yNjAzMjYx
# ODExMjlaFw0zMTAzMjYxODExMjlaMFoxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJRCBWZXJp
# ZmllZCBDUyBBT0MgQ0EgMDQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDKVfrI2+gJMM/0bQ5OVKNdvOASzLbUUMvXuf+Vl7YGuofPaZHVo3gMHF5inT+G
# MSpIcfIZ9qtXU1UG68ry8vNbQtOL4Nm30ifXpqI1+ByiAWLO1YT0WnzG7XPOuoTe
# eWsNZv5FmjxCsReBZvyzyzCyXZbu1EQfJxWTH4ebUwtAiW9rqMf9eDj/wYhiEfNt
# eJV3ZFeibD2ztCHr9JhFdd97XbnCHgQoTIqc02X5xlRKtUGBa++OtHBBjiJ/uwBn
# zTkqu4FjpZjQeJtrmda+ur1CT2jflWIB/ypn7u7V9tvW9wJbJYt/H2EtJ0GONWxJ
# Z7TEu8jWPindOO3lzPP7UtzS/mVDV94HucWaltmsra6zSG8BoEJ87IM8QSb7vfm/
# O41FhYkUv89WIj5ES2O4kxyiMSfe95CMivCuYrRP2hKvx7egPMrWgDDBkxMLgrKZ
# O9hRNUMm8vk3w5b9SogHOyJVhxyFm8aFXfIxgqDF4S0g4bhbhnzljmSlCLlumMZc
# XFGDjpF2tNoAu3VGFGYtHtTSNVKvZpgB3b4ynaoDkbPf+Wg4523jt4VneasBgZhC
# 1srZI2NCnCBBfgjLq04pqEKAWEohyW2K29KSkkHvt5VaE1ac3Yt+oyiOzMS57tXw
# QDJLGvLg/OXFO0VNvczDndfIfXYExB/ab2PuMSwd5VIBOwIDAQABo4IB3DCCAdgw
# DgYDVR0PAQH/BAQDAgGGMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRrJUHe
# +2t8/RiACi1/j3ZdqnM9uDBUBgNVHSAETTBLMEkGBFUdIAAwQTA/BggrBgEFBQcC
# ARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRv
# cnkuaHRtMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMBIGA1UdEwEB/wQIMAYB
# Af8CAQAwHwYDVR0jBBgwFoAU2UEpsA8PY2zvadf1zSmepEhqMOYwcAYDVR0fBGkw
# ZzBloGOgYYZfaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWlj
# cm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENvZGUlMjBTaWduaW5nJTIwUENBJTIw
# MjAyMS5jcmwwfQYIKwYBBQUHAQEEcTBvMG0GCCsGAQUFBzAChmFodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMElEJTIwVmVy
# aWZpZWQlMjBDb2RlJTIwU2lnbmluZyUyMFBDQSUyMDIwMjEuY3J0MA0GCSqGSIb3
# DQEBDAUAA4ICAQAG1VBeVHTVRBljlcZD3IiMxwPyMjQyLNaEnVu5mODm2hRBJfH8
# GsBLATmrHAc8F47jmk5CnpUPiIguCbw6Z/KVj4Dsoiq228NSLMLewFfGMri7uwNG
# LISC5ccp8vUdADDEIsS2dE+QI9OwkDpv3XuUD7d+hAgcLVcMOl1AsfEZtsZenhGv
# SYUrm/FuLq0BqEGL9GXM5c+Ho9q8o+Vn/S+GWQN2y+gkRO15s0kI05nUpq/dOD4r
# i9rgVs6tipEd0YZqGgD+CZNiaZWrDTOQbNPncd2F9qOsUa20miYruoT5PwJAaI+Q
# QiTE2ZJeMJOkOpzhTUgqVMZwZidEUZKCqudaeQA08WwnkQMfKyHzaU8j48ULcU4h
# UwvMsv7fSurOe9GAdRQCPvF8WcSK5oDHe8VVJM4tv6KKCm91HqLx9JamBgRI6R2S
# fY3nu26EGznu0rCg/769z8xWm4PVcC2ZaL6VlKVqFp1NsN8YqMyf5t+bbGVb09no
# FKcJG/UwyGlxRmQBlfeBUQx5/ytlzZzsEnhrJF9fTAfje8j3OdX5lEnePTFQLRlv
# zZFBqUXnIeQKv3fHQjC9m2fo/Z01DII/qp3d8LhGVUW0BCG04fRwHJNH8iqqCG/q
# ofMv+kym2AxBDnHzNgRjL60JOFiBgiurvLhYQNhB95KWojFA6shQnggkMTCCB54w
# ggWGoAMCAQICEzMAAAAHh6M0o3uljhwAAAAAAAcwDQYJKoZIhvcNAQEMBQAwdzEL
# MAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjFIMEYG
# A1UEAxM/TWljcm9zb2Z0IElkZW50aXR5IFZlcmlmaWNhdGlvbiBSb290IENlcnRp
# ZmljYXRlIEF1dGhvcml0eSAyMDIwMB4XDTIxMDQwMTIwMDUyMFoXDTM2MDQwMTIw
# MTUyMFowYzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjE0MDIGA1UEAxMrTWljcm9zb2Z0IElEIFZlcmlmaWVkIENvZGUgU2lnbmlu
# ZyBQQ0EgMjAyMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALLwwK8Z
# iCji3VR6TElsaQhVCbRS/3pK+MHrJSj3Zxd3KU3rlfL3qrZilYKJNqztA9OQacr1
# AwoNcHbKBLbsQAhBnIB34zxf52bDpIO3NJlfIaTE/xrweLoQ71lzCHkD7A4As1Bs
# 076Iu+mA6cQzsYYH/Cbl1icwQ6C65rU4V9NQhNUwgrx9rGQ//h890Q8JdjLLw0nV
# +ayQ2Fbkd242o9kH82RZsH3HEyqjAB5a8+Ae2nPIPc8sZU6ZE7iRrRZywRmrKDp5
# +TcmJX9MRff241UaOBs4NmHOyke8oU1TYrkxh+YeHgfWo5tTgkoSMoayqoDpHOLJ
# s+qG8Tvh8SnifW2Jj3+ii11TS8/FGngEaNAWrbyfNrC69oKpRQXY9bGH6jn9NEJv
# 9weFxhTwyvx9OJLXmRGbAUXN1U9nf4lXezky6Uh/cgjkVd6CGUAf0K+Jw+GE/5Vp
# IVbcNr9rNE50Sbmy/4RTCEGvOq3GhjITbCa4crCzTTHgYYjHs1NbOc6brH+eKpWL
# tr+bGecy9CrwQyx7S/BfYJ+ozst7+yZtG2wR461uckFu0t+gCwLdN0A6cFtSRtR8
# bvxVFyWwTtgMMFRuBa3vmUOTnfKLsLefRaQcVTgRnzeLzdpt32cdYKp+dhr2ogc+
# qM6K4CBI5/j4VFyC4QFeUP2YAidLtvpXRRo3AgMBAAGjggI1MIICMTAOBgNVHQ8B
# Af8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNlBKbAPD2Ns72nX
# 9c0pnqRIajDmMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRw
# Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0w
# GQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV
# HSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYDVR0fBH0wezB5oHegdYZz
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIw
# SWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlmaWNhdGUlMjBB
# dXRob3JpdHklMjAyMDIwLmNybDCBwwYIKwYBBQUHAQEEgbYwgbMwgYEGCCsGAQUF
# BzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jv
# c29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIwUm9vdCUyMENlcnRpZmlj
# YXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwLQYIKwYBBQUHMAGGIWh0dHA6Ly9v
# bmVvY3NwLm1pY3Jvc29mdC5jb20vb2NzcDANBgkqhkiG9w0BAQwFAAOCAgEAfyUq
# nv7Uq+rdZgrbVyNMul5skONbhls5fccPlmIbzi+OwVdPQ4H55v7VOInnmezQEeW4
# LqK0wja+fBznANbXLB0KrdMCbHQpbLvG6UA/Xv2pfpVIE1CRFfNF4XKO8XYEa3oW
# 8oVH+KZHgIQRIwAbyFKQ9iyj4aOWeAzwk+f9E5StNp5T8FG7/VEURIVWArbAzPt9
# ThVN3w1fAZkF7+YU9kbq1bCR2YD+MtunSQ1Rft6XG7b4e0ejRA7mB2IoX5hNh3UE
# auY0byxNRG+fT2MCEhQl9g2i2fs6VOG19CNep7SquKaBjhWmirYyANb0RJSLWjin
# MLXNOAga10n8i9jqeprzSMU5ODmrMCJE12xS/NWShg/tuLjAsKP6SzYZ+1Ry358Z
# TFcx0FS/mx2vSoU8s8HRvy+rnXqyUJ9HBqS0DErVLjQwK8VtsBdekBmdTbQVoCgP
# Cqr+PDPB3xajYnzevs7eidBsM71PINK2BoE2UfMwxCCX3mccFgx6UsQeRSdVVVNS
# yALQe6PT12418xon2iDGE81OGCreLzDcMAZnrUAx4XQLUz6ZTl65yPUiOh3k7Yww
# 94lDf+8oG2oZmDh5O1Qe38E+M3vhKwmzIeoB1dVLlz4i3IpaDcR+iuGjH2TdaC1Z
# OmBXiCRKJLj4DT2uhJ04ji+tHD6n58vhavFIrmcxghq3MIIaswIBATBxMFoxCzAJ
# BgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNV
# BAMTIk1pY3Jvc29mdCBJRCBWZXJpZmllZCBDUyBBT0MgQ0EgMDQCEzMABolbx0F0
# Hi2GtNMAAAAGiVswDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAig
# AoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgEL
# MQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg/fu+UyF8M89MtMPql7BR
# FmPFz+bT4V89kQSaX4jul88wDQYJKoZIhvcNAQEBBQAEggGAQ68RRBeggdyyxmiQ
# M9qkDY2dfQnv3zib9uf+EcHuV6mVuL6c/4Js/tPNWN6MmH0DwNhtx3hDfjyfOS/P
# YnqaUgSR6rBdxR6ZLveUyr9Zrv5icA0CpuyszYB1Fe/OtHZro2v5pF4mTJpj7/Gl
# 08lIBagqN4KWw3cGfX3HHds7SNNUjTmHRR8yI7LWJ7Amt+KJ/pVwLW81fqtRaM64
# pkxsYXTZTDLG0wrYn+GRFYwMLum4xD/AuaVpbAxOAhr9Zo1xpJIIr3jJrsD6luYK
# 1ux2+Qy6ByvystlteN7uW/QFhVHQAUav63evmpYRClYMKzrfIz+AlGT82ljBREbg
# o6elLEPHT29/gWryIM6z62DNpTKvtfyH/Usz1ftS3GKwJwUfZaRkSiRykSZyGOSn
# yik6gJAdsGPG7JyWQAijujg9lqkbEM96p9sPI9m+st/9WlD4SybbQCBjcsuoWyuT
# PTFG0oMPmOFg4ROA/1S5RELa0+qYkRsxEzrUtY1J8Fz4UTGXoYIYEDCCGAwGCisG
# AQQBgjcDAwExghf8MIIX+AYJKoZIhvcNAQcCoIIX6TCCF+UCAQMxDzANBglghkgB
# ZQMEAgEFADCCAWEGCyqGSIb3DQEJEAEEoIIBUASCAUwwggFIAgEBBgorBgEEAYRZ
# CgMBMDEwDQYJYIZIAWUDBAIBBQAEIHVJF2o52yds6YE4ZNWaJh6TRzwZ27e+VQd/
# 9ZdFurpbAgZqqXgAJGwYEjIwMjYwOTE2MjMxMjU0LjA2WjAEgAIB9KCB4aSB3jCB
# 2zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMc
# TWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBU
# U1MgRVNOOjdEMDAtMDVFMC1EOTQ3MTUwMwYDVQQDEyxNaWNyb3NvZnQgUHVibGlj
# IFJTQSBUaW1lIFN0YW1waW5nIEF1dGhvcml0eaCCDyEwggeCMIIFaqADAgECAhMz
# AAAABeXPD/9mLsmHAAAAAAAFMA0GCSqGSIb3DQEBDAUAMHcxCzAJBgNVBAYTAlVT
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xSDBGBgNVBAMTP01pY3Jv
# c29mdCBJZGVudGl0eSBWZXJpZmljYXRpb24gUm9vdCBDZXJ0aWZpY2F0ZSBBdXRo
# b3JpdHkgMjAyMDAeFw0yMDExMTkyMDMyMzFaFw0zNTExMTkyMDQyMzFaMGExCzAJ
# BgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNV
# BAMTKU1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWVzdGFtcGluZyBDQSAyMDIwMIIC
# IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnnznUmP94MWfBX1jtQYioxwe
# 1+eXM9ETBb1lRkd3kcFdcG9/sqtDlwxKoVIcaqDb+omFio5DHC4RBcbyQHjXCwMk
# /l3TOYtgoBjxnG/eViS4sOx8y4gSq8Zg49REAf5huXhIkQRKe3Qxs8Sgp02KHAzn
# Ea/Ssah8nWo5hJM1xznkRsFPu6rfDHeZeG1Wa1wISvlkpOQooTULFm809Z0ZYlQ8
# Lp7i5F9YciFlyAKwn6yjN/kR4fkquUWfGmMopNq/B8U/pdoZkZZQbxNlqJOiBGgC
# Wpx69uKqKhTPVi3gVErnc/qi+dR8A2MiAz0kN0nh7SqINGbmw5OIRC0EsZ31WF3U
# xp3GgZwetEKxLms73KG/Z+MkeuaVDQQheangOEMGJ4pQZH55ngI0Tdy1bi69INBV
# 5Kn2HVJo9XxRYR/JPGAaM6xGl57Ei95HUw9NV/uC3yFjrhc087qLJQawSC3xzY/E
# XzsT4I7sDbxOmM2rl4uKK6eEpurRduOQ2hTkmG1hSuWYBunFGNv21Kt4N20AKmbe
# uSnGnsBCd2cjRKG79+TX+sTehawOoxfeOO/jR7wo3liwkGdzPJYHgnJ54UxbckF9
# 14AqHOiEV7xTnD1a69w/UTxwjEugpIPMIIE67SFZ2PMo27xjlLAHWW3l1CEAFjLN
# Hd3EQ79PUr8FUXetXr0CAwEAAaOCAhswggIXMA4GA1UdDwEB/wQEAwIBhjAQBgkr
# BgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUa2koOjUvSGNAz3vYr0npPtk92yEwVAYD
# VR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9z
# b2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggr
# BgEFBQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAPBgNVHRMBAf8EBTAD
# AQH/MB8GA1UdIwQYMBaAFMh+0mqFKhvKGZgEByfPUBBPaKiiMIGEBgNVHR8EfTB7
# MHmgd6B1hnNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNy
# b3NvZnQlMjBJZGVudGl0eSUyMFZlcmlmaWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZp
# Y2F0ZSUyMEF1dGhvcml0eSUyMDIwMjAuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDCB
# gQYIKwYBBQUHMAKGdWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2Vy
# dHMvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIw
# Q2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNydDANBgkqhkiG9w0BAQwF
# AAOCAgEAX4h2x35ttVoVdedMeGj6TuHYRJklFaW4sTQ5r+k77iB79cSLNe+GzRjv
# 4pVjJviceW6AF6ycWoEYR0LYhaa0ozJLU5Yi+LCmcrdovkl53DNt4EXs87KDogYb
# 9eGEndSpZ5ZM74LNvVzY0/nPISHz0Xva71QjD4h+8z2XMOZzY7YQ0Psw+etyNZ1C
# esufU211rLslLKsO8F2aBs2cIo1k+aHOhrw9xw6JCWONNboZ497mwYW5EfN0W3zL
# 5s3ad4Xtm7yFM7Ujrhc0aqy3xL7D5FR2J7x9cLWMq7eb0oYioXhqV2tgFqbKHeDi
# ck+P8tHYIFovIP7YG4ZkJWag1H91KlELGWi3SLv10o4KGag42pswjybTi4toQcC/
# irAodDW8HNtX+cbz0sMptFJK+KObAnDFHEsukxD+7jFfEV9Hh/+CSxKRsmnuiovC
# WIOb+H7DRon9TlxydiFhvu88o0w35JkNbJxTk4MhF/KgaXn0GxdH8elEa2Imq45g
# aa8D+mTm8LWVydt4ytxYP/bqjN49D9NZ81coE6aQWm88TwIf4R4YZbOpMKN0Cyej
# aPNN41LGXHeCUMYmBx3PkP8ADHD1J2Cr/6tjuOOCztfp+o9Nc+ZoIAkpUcA/X2gS
# MkgHAPUvIdtoSAHEUKiBhI6JQivRepyvWcl+JYbYbBh7pmgAXVswggeXMIIFf6AD
# AgECAhMzAAAAVdndaSYo+fjiAAAAAABVMA0GCSqGSIb3DQEBDAUAMGExCzAJBgNV
# BAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMT
# KU1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWVzdGFtcGluZyBDQSAyMDIwMB4XDTI1
# MTAyMzIwNDY0OVoXDTI2MTAyMjIwNDY0OVowgdsxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9w
# ZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo3RDAwLTA1RTAtRDk0
# NzE1MDMGA1UEAxMsTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBB
# dXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9uR+SHllI
# s/QwJRZp9rD8pmhVm72JDHyvknCFm92tSLzpSMIIVA42NBqesjEYX2FEYhktBnnS
# AessL7h+lQQl9/m3ThXAHJYLb9tY66To2ZpOH0mk9kNwbM1H3lCWvKN8SO2X6DGP
# XbM08R0AM+mVV/O3xxhFYUHH8Vt9yHTyTo/2nuNfarWMU9tTFZgn7E7IYLVoqEMZ
# jlv7zAvf2/qoLQcUjH+/fL5t6n5oReigrxWh5Yr6zN9oWNejxhNy9DxQvizO70cV
# O5k2/q++gnsm76jlpOPnWymH7T4VdbfxOUv+sMF3mJrv2OyQu054dsOORuWOKXDN
# 6BzG/2Lj0XTlmtL/kQtkIJjVVqo7sQ4spVrHF0A7mjLW9vQHHRlFVfWbEWNjNrLY
# QLTnWTrIYkebnzLWh7YgpFr9IzX4FMax7q8c2LlDZ3lmehH0A4BQMPAkgipEjitn
# PYxKKeHXVatdMb26sXa6jJ3lV77yHF6z0AF4/Y9hAqVdhMDG91p5qcNND+/Cacz7
# JNxbOtWbzhnfxdUXDgbun9k1naexy+/q6u7YB69dzJXW3yFruJaaGGBNYE0GtWK4
# OVzeI+87PZJU9s96qHJj81fA1kICBzYfmk7O27ozBDEMiO17dcz8WQoHEeh9LZps
# 1P/Qcb7Fm0WpQkNrGBslrqU3XOHuymO5DwIDAQABo4IByzCCAccwHQYDVR0OBBYE
# FFYEXxBt3AgD8Mi/qckWysHXrGW2MB8GA1UdIwQYMBaAFGtpKDo1L0hjQM972K9J
# 6T7ZPdshMGwGA1UdHwRlMGMwYaBfoF2GW2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
# bS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFB1YmxpYyUyMFJTQSUyMFRpbWVzdGFt
# cGluZyUyMENBJTIwMjAyMC5jcmwweQYIKwYBBQUHAQEEbTBrMGkGCCsGAQUFBzAC
# hl1odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29m
# dCUyMFB1YmxpYyUyMFJTQSUyMFRpbWVzdGFtcGluZyUyMENBJTIwMjAyMC5jcnQw
# DAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8E
# BAMCB4AwZgYDVR0gBF8wXTBRBgwrBgEEAYI3TIN9AQEwQTA/BggrBgEFBQcCARYz
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1JlcG9zaXRvcnku
# aHRtMAgGBmeBDAEEAjANBgkqhkiG9w0BAQwFAAOCAgEAUh7hklR72pQpxZ5bKlyE
# Hnx9cT9kha/YPlc/n+T+0HssI30G+Y1JUpndV5yVAz3vzB8S+690xBJS/pjbRugg
# zwMrUrUhT1w/bUwbQTGIfFqqOuKR/apt+tciKngR/e/Zs1gpDELE3dJzOnVJfQfu
# 6orYvk6F8MSJd/XmKi7mGH4Q9pqqnj1zM1CkkM5H+98mCFRz+pyyUM+GgJmlnHxv
# Y4O/LAZA1fCqVuyYJLbi4aYSRDdQfklR43pz3XJqxVyFLvyuIyubpH1mkCI7ml80
# owZTYwubUDemnT3wNxsVMBz3keHpC+SH//bwX9d7ZswVvoMvtLDRk73m/SC/RlPI
# l/FL8sLF+tp4Qgj0VIU4oAwSnXM0VKza57QYaMG33IQQxTC/Gr0TEXPRpnNibyK8
# l99+khUOdf/6tVFNhzEiRDIViyUiFiVYX1KMLDmvj2pqSMxE2Hxb07tpqiiVJVmV
# 5BmMa3QrwnMyXKnqGnaVtbpepHHZw4dtvEkPGYQ3OiEZTOIjXeUjaDYF/mqJt8Lh
# so1Gkmj2VsTwdRtjSomITy7dJTx4NBrJI9c4SEmPFEJDDA696NiYEbk/sJyRA0FK
# eeXXb4UpEqA+iPQy/7Pk4yGP3PYy2luccsCR6nSh1AKUTLIIb+5Hm0rmtbqZkfk6
# rnpRZLQ0jo1XUkZLsmuLqMUxggdDMIIHPwIBATB4MGExCzAJBgNVBAYTAlVTMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m
# dCBQdWJsaWMgUlNBIFRpbWVzdGFtcGluZyBDQSAyMDIwAhMzAAAAVdndaSYo+fji
# AAAAAABVMA0GCWCGSAFlAwQCAQUAoIIEnDARBgsqhkiG9w0BCRACDzECBQAwGgYJ
# KoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNjA5MTYy
# MzEyNTRaMC8GCSqGSIb3DQEJBDEiBCCphRfN7LhE5lQ7XMmjFEwoQkQbeBk8AlWz
# zwtng5cqlDCBuQYLKoZIhvcNAQkQAi8xgakwgaYwgaMwgaAEINi5PJdkhmK7v33+
# /g9qqyZ5LMHGHSuqRiruxhhq+P7NMHwwZaRjMGExCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBQ
# dWJsaWMgUlNBIFRpbWVzdGFtcGluZyBDQSAyMDIwAhMzAAAAVdndaSYo+fjiAAAA
# AABVMIIDXgYLKoZIhvcNAQkQAhIxggNNMIIDSaGCA0UwggNBMIICKQIBATCCAQmh
# geGkgd4wgdsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD
# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAj
# BgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5T
# aGllbGQgVFNTIEVTTjo3RDAwLTA1RTAtRDk0NzE1MDMGA1UEAxMsTWljcm9zb2Z0
# IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHmiIwoBATAHBgUrDgMC
# GgMVAB07VAGCZb+24FlXkQaOF+xXhw3qoGcwZaRjMGExCzAJBgNVBAYTAlVTMR4w
# HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m
# dCBQdWJsaWMgUlNBIFRpbWVzdGFtcGluZyBDQSAyMDIwMA0GCSqGSIb3DQEBCwUA
# AgUA7lVIAjAiGA8yMDI2MDkxNjE2NTMyMloYDzIwMjYwOTE3MTY1MzIyWjB0MDoG
# CisGAQQBhFkKBAExLDAqMAoCBQDuVUgCAgEAMAcCAQACAibEMAcCAQACAhIzMAoC
# BQDuVpmCAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEA
# AgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBABEofIT7H4/Z8Vze
# FAhrP5WuJsDAy17TXhk6eRVLPo7vYL+TTUXQxcsasIaKk0SEmn8XxJW5wHUujsVj
# nSBgGuBvVZrXRV5wHnj+u2r+AmLBnQiTcrBhziD7y+jC9gD265aPAEivQPoW8Zjs
# Nrd8q35XzHm5/GY9SdM4KFUMT7VP2Fy1covIblYFXl0KucKn9fAScdCZdyNmE6XA
# NxYlnSxaonQHdBtBuezAPS0VTDWNmAQrj3diPgdA5kVbTCBwZyt4NgrDnigbDcKp
# R8OKCCEiRc8srL45QoRxZkvB4d74Wv6BBMMTDhbjTy4Xc0RHJletMqCkBNGSfHm+
# MpmQDRAwDQYJKoZIhvcNAQEBBQAEggIApwOJasCnxwNch1qPE9YO/mxGnpIEr7DZ
# KGAKa4p6TwxlCAoaurdFFDa0mfNfVIyZcE6MPHnPWlu5gyRu5tus3lPil0NYkvpK
# Wx5cAD/e034/2YgVvZhHMGeMfjsrSLKfuti00uz0CeTW29z12KT78/eyffNGG8eL
# S6i4Yi148Xeru0PloMnFjgF9sA+aUlJoNLS+IVO8S4zfa12nzR/n17pbGVg39Hdk
# evF9VjotqMgx10Iw2PB3eaSeyRhT5F4M8mPCegAqp59BVi40kFTMjw+kSs9sVr+B
# /S/L7Pz2UM8gk7mr7pgMvZxDP3+pAV8DMjbgfcu2tOP+uV0hx8mhbXl2dP/I2QBS
# 0EO93lP5URoMEK+7fwnlr4hqDpkZw5zeZVii4/f9L9sLOP53NazHom6p3Ao+Pd8F
# yfFdgi8hVbSt0Ch9C46X27pZLSLf7A2fTEhWIgIYUBaDpiGYWiPWKep9KSbUp45Q
# 2DAAJPNuuP6qdsUJR68w0nCvIDQow0dNa8F9lpY2WUk43RZYCw+sx4P8cV+xH2Tn
# olm/HAHk9CvzkpBRwPP0WwIfNs3mx7jANY5FveDeagtxfLUKNd0aXtfrF2tIkP3n
# cg3OxIr41xBGroJbpZx1o/MqXjnS13bxPmfhdNuwCwcYB1m6geQpg0P+nyGNJEJE
# GJ3lm5R4iyM=
# SIG # End signature block