From 23ccd346f9fc3e7763c48a8977fb05247ad17666 Mon Sep 17 00:00:00 2001 From: Chidozie DAVID <100468311+Dozie2001@users.noreply.github.com> Date: Sun, 26 Jan 2025 16:48:40 +0100 Subject: [PATCH] fix: improve web vitals (#250) Co-authored-by: Rosco Kalis --- app/Analytics.tsx | 6 ++-- components/common/Logo.tsx | 4 +-- components/common/donate/DonateButton.tsx | 12 ++------ components/common/donate/DonateModal.tsx | 4 +-- components/exploits/ExploitChecker.tsx | 4 +-- components/footer/ColorThemeSelect.tsx | 4 +-- components/footer/LanguageSelect.tsx | 4 +-- components/header/SearchBar.tsx | 14 ++++----- components/landing/DemoVideo.tsx | 5 +-- .../cells/CancelMarketplaceCell.tsx | 4 +-- .../signatures/cells/CancelPermitCell.tsx | 8 +++-- lib/hooks/ethereum/EthereumProvider.tsx | 6 ++-- lib/hooks/ethereum/useAllowances.tsx | 4 +-- lib/hooks/ethereum/useDonate.tsx | 4 +-- lib/utils/allowances.ts | 6 ++-- lib/utils/analytics.ts | 29 ++++++++++++------ lib/utils/batch-revoke.ts | 4 +-- lib/utils/index.ts | 4 +-- lib/utils/risk.tsx | 4 +-- lib/utils/tokens.ts | 4 +-- .../images/thumbnail/demo-thumbnail.webp | Bin 0 -> 21902 bytes 21 files changed, 71 insertions(+), 63 deletions(-) create mode 100644 public/assets/images/thumbnail/demo-thumbnail.webp diff --git a/app/Analytics.tsx b/app/Analytics.tsx index 0f79e3cfe..c2db70247 100644 --- a/app/Analytics.tsx +++ b/app/Analytics.tsx @@ -1,7 +1,7 @@ 'use client'; import { usePathname } from 'lib/i18n/navigation'; -import { init, track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import Script from 'next/script'; import { useEffect } from 'react'; @@ -9,12 +9,12 @@ const Analytics = () => { const path = usePathname(); useEffect(() => { - init(); + analytics.init(); }, []); useEffect(() => { if (!path) return; - track('Viewed Page', { path }); + analytics.track('Viewed Page', { path }); }, [path]); // SimpleAnalytics diff --git a/components/common/Logo.tsx b/components/common/Logo.tsx index fc309c1df..5b322684d 100644 --- a/components/common/Logo.tsx +++ b/components/common/Logo.tsx @@ -1,7 +1,7 @@ 'use client'; import Image from 'next/image'; -import { useState } from 'react'; +import { memo, useState } from 'react'; import { twMerge } from 'tailwind-merge'; import PlaceholderIcon from './PlaceholderIcon'; @@ -54,4 +54,4 @@ const Logo = ({ src, alt, size, square, border, className }: Props) => { ); }; -export default Logo; +export default memo(Logo); diff --git a/components/common/donate/DonateButton.tsx b/components/common/donate/DonateButton.tsx index 4e296cfb6..18e7735d5 100644 --- a/components/common/donate/DonateButton.tsx +++ b/components/common/donate/DonateButton.tsx @@ -16,21 +16,13 @@ const DonateButton = ({ size, style, className, type }: Props) => { const t = useTranslations(); const [open, setOpen] = useState(false); - const handleOpen = () => { - setOpen(true); - }; - - const handleClose = () => { - setOpen(false); - }; - return ( <> - - (open ? handleOpen() : handleClose())} type={type} /> + ); }; diff --git a/components/common/donate/DonateModal.tsx b/components/common/donate/DonateModal.tsx index e0bbd553f..dfe6fa7af 100644 --- a/components/common/donate/DonateModal.tsx +++ b/components/common/donate/DonateModal.tsx @@ -5,7 +5,7 @@ import Button from 'components/common/Button'; import Modal from 'components/common/Modal'; import { useDonate } from 'lib/hooks/ethereum/useDonate'; import { useTranslations } from 'next-intl'; -import { useEffect, useState } from 'react'; +import { memo, useEffect, useState } from 'react'; import { useAsyncCallback } from 'react-async-hook'; import { useChainId } from 'wagmi'; import Input from '../Input'; @@ -79,4 +79,4 @@ const DonateModal = ({ open, setOpen, type }: Props) => { ); }; -export default DonateModal; +export default memo(DonateModal); diff --git a/components/exploits/ExploitChecker.tsx b/components/exploits/ExploitChecker.tsx index 0dc3f8888..e810e8be3 100644 --- a/components/exploits/ExploitChecker.tsx +++ b/components/exploits/ExploitChecker.tsx @@ -10,7 +10,7 @@ import { } from 'lib/hooks/page-context/AddressPageContext'; import { isNullish } from 'lib/utils'; import { getAllowanceKey } from 'lib/utils/allowances'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { getEventKey } from 'lib/utils/events'; import { type Exploit, getExploitStatus } from 'lib/utils/exploits'; import ExploitStatus from './ExploitStatus'; @@ -29,7 +29,7 @@ const ExploitChecker = ({ exploit }: Props) => { queryKey: ['exploit-status', exploit.slug, allowances?.map(getAllowanceKey), events?.map(getEventKey)], queryFn: () => { const status = getExploitStatus(events!, allowances!, exploit); - track('Exploit Checked', { exploit: exploit.slug, account: address, chainId: selectedChainId, status }); + analytics.track('Exploit Checked', { exploit: exploit.slug, account: address, chainId: selectedChainId, status }); return status; }, enabled: !isNullish(address) && !isNullish(events) && !isNullish(allowances) && !isNullish(selectedChainId), diff --git a/components/footer/ColorThemeSelect.tsx b/components/footer/ColorThemeSelect.tsx index 1aab202b4..c0b299859 100644 --- a/components/footer/ColorThemeSelect.tsx +++ b/components/footer/ColorThemeSelect.tsx @@ -4,7 +4,7 @@ import { ComputerDesktopIcon, MoonIcon, SunIcon } from '@heroicons/react/24/outl import Select from 'components/common/select/Select'; import { useColorTheme } from 'lib/hooks/useColorTheme'; import { useMounted } from 'lib/hooks/useMounted'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { useTranslations } from 'next-intl'; const ColorThemeSelect = () => { @@ -19,7 +19,7 @@ const ColorThemeSelect = () => { ] as const; const selectTheme = (option: (typeof options)[number]) => { - track('Changed Color Theme', { theme: option.value }); + analytics.track('Changed Color Theme', { theme: option.value }); setTheme(option.value); }; diff --git a/components/footer/LanguageSelect.tsx b/components/footer/LanguageSelect.tsx index e2363ea5a..1f06f6f30 100644 --- a/components/footer/LanguageSelect.tsx +++ b/components/footer/LanguageSelect.tsx @@ -5,7 +5,7 @@ import Select from 'components/common/select/Select'; import type { Locale } from 'lib/i18n/config'; import { useCsrRouter } from 'lib/i18n/csr-navigation'; import { usePathname } from 'lib/i18n/navigation'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { useLocale } from 'next-intl'; import type { FormatOptionLabelMeta } from 'react-select'; @@ -36,7 +36,7 @@ const LanguageSelect = () => { const selectLanguage = (option: Option) => { const newLocale = option.value; - track('Changed language', { from: locale, to: newLocale }); + analytics.track('Changed language', { from: locale, to: newLocale }); router.replace(path, { locale: newLocale, scroll: false, showProgress: false, retainSearchParams: ['chainId'] }); persistLocaleCookie(newLocale); }; diff --git a/components/header/SearchBar.tsx b/components/header/SearchBar.tsx index f57f89e5c..840772670 100644 --- a/components/header/SearchBar.tsx +++ b/components/header/SearchBar.tsx @@ -4,7 +4,7 @@ import AddressSearchBox from 'components/common/AddressSearchBox'; import Button from 'components/common/Button'; import { useCsrRouter } from 'lib/i18n/csr-navigation'; import { useTranslations } from 'next-intl'; -import { useRef, useState } from 'react'; +import { useCallback, useRef, useState } from 'react'; import { twMerge } from 'tailwind-merge'; import { useAccount } from 'wagmi'; @@ -16,21 +16,21 @@ const SearchBar = () => { const { address } = useAccount(); const timerRef = useRef(); - const onFocus = () => { + const onFocus = useCallback(() => { clearTimeout(timerRef.current); setIsFocused(true); - }; + }, []); - const onBlur = () => { + const onBlur = useCallback(() => { timerRef.current = setTimeout(() => setIsFocused(false), 200); - }; + }, []); - const onClick = () => { + const onClick = useCallback(() => { if (address) { setValue(address); router.push(`/address/${address}`, { retainSearchParams: ['chainId'] }); } - }; + }, [address, router]); return (
diff --git a/components/landing/DemoVideo.tsx b/components/landing/DemoVideo.tsx index 67607dd2c..1a73b97a8 100644 --- a/components/landing/DemoVideo.tsx +++ b/components/landing/DemoVideo.tsx @@ -6,10 +6,11 @@ const DemoVideo = () => { controls muted loop - preload="metadata" + preload="metadata" // Preload the video fully for faster LCP playsInline + poster="/assets/images/thumbnail/demo-thumbnail.webp" > - + ); }; diff --git a/components/signatures/cells/CancelMarketplaceCell.tsx b/components/signatures/cells/CancelMarketplaceCell.tsx index 2476d2ff1..cf942c35d 100644 --- a/components/signatures/cells/CancelMarketplaceCell.tsx +++ b/components/signatures/cells/CancelMarketplaceCell.tsx @@ -3,7 +3,7 @@ import { useHandleTransaction } from 'lib/hooks/ethereum/useHandleTransaction'; import { useAddressPageContext } from 'lib/hooks/page-context/AddressPageContext'; import { type Marketplace, type OnCancel, type TransactionSubmitted, TransactionType } from 'lib/interfaces'; import { waitForTransactionConfirmation } from 'lib/utils'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { usePublicClient, useWalletClient } from 'wagmi'; import CancelCell from './CancelCell'; @@ -21,7 +21,7 @@ const CancelMarketplaceCell = ({ marketplace, onCancel }: Props) => { const sendCancelTransaction = async (): Promise => { const hash = await marketplace?.cancelSignatures(walletClient!); - track('Cancelled Marketplace Signatures', { + analytics.track('Cancelled Marketplace Signatures', { chainId: selectedChainId, account: address, marketplace: marketplace.name, diff --git a/components/signatures/cells/CancelPermitCell.tsx b/components/signatures/cells/CancelPermitCell.tsx index d846193d7..11ebf02f8 100644 --- a/components/signatures/cells/CancelPermitCell.tsx +++ b/components/signatures/cells/CancelPermitCell.tsx @@ -4,7 +4,7 @@ import { useHandleTransaction } from 'lib/hooks/ethereum/useHandleTransaction'; import { useAddressPageContext } from 'lib/hooks/page-context/AddressPageContext'; import { type OnCancel, type TransactionSubmitted, TransactionType } from 'lib/interfaces'; import { waitForTransactionConfirmation } from 'lib/utils'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { permit } from 'lib/utils/permit'; import { type PermitTokenData, isErc721Contract } from 'lib/utils/tokens'; import { usePublicClient, useWalletClient } from 'wagmi'; @@ -25,7 +25,11 @@ const CancelPermitCell = ({ token, onCancel }: Props) => { if (isErc721Contract(token.contract)) throw new Error('Cannot cancel ERC721 tokens'); const hash = await permit(walletClient!, token.contract, DUMMY_ADDRESS, 0n); - track('Cancelled Permit Signatures', { chainId: selectedChainId, account: address, token: token.contract.address }); + analytics.track('Cancelled Permit Signatures', { + chainId: selectedChainId, + account: address, + token: token.contract.address, + }); const waitForConfirmation = async () => { // TODO: Deduplicate this with the CancelMarketplaceCell diff --git a/lib/hooks/ethereum/EthereumProvider.tsx b/lib/hooks/ethereum/EthereumProvider.tsx index ade262979..26205997b 100644 --- a/lib/hooks/ethereum/EthereumProvider.tsx +++ b/lib/hooks/ethereum/EthereumProvider.tsx @@ -4,7 +4,7 @@ import { abstractWalletConnector } from '@abstract-foundation/agw-react/connecto import { useCsrRouter } from 'lib/i18n/csr-navigation'; import { usePathname } from 'lib/i18n/navigation'; import { ORDERED_CHAINS, createViemPublicClientForChain, getViemChainConfig } from 'lib/utils/chains'; -import { type ReactNode, useEffect } from 'react'; +import { type ReactNode, memo, useEffect } from 'react'; import type { Chain } from 'viem'; import { WagmiProvider, createConfig, useAccount, useConnect } from 'wagmi'; import { coinbaseWallet, injected, safe, walletConnect } from 'wagmi/connectors'; @@ -50,7 +50,7 @@ export const EthereumProvider = ({ children }: Props) => { ); }; -const EthereumProviderChild = ({ children }: Props) => { +const EthereumProviderChild = memo(({ children }: Props) => { const { connectAsync, connectors } = useConnect(); const { connector, address } = useAccount(); const router = useCsrRouter(); @@ -105,7 +105,7 @@ const EthereumProviderChild = ({ children }: Props) => { }, [address]); return <>{children}; -}; +}); const isIframe = () => { return typeof window !== 'undefined' && window?.parent !== window; diff --git a/lib/hooks/ethereum/useAllowances.tsx b/lib/hooks/ethereum/useAllowances.tsx index a6498ce96..3559dcb53 100644 --- a/lib/hooks/ethereum/useAllowances.tsx +++ b/lib/hooks/ethereum/useAllowances.tsx @@ -9,7 +9,7 @@ import { getAllowancesFromEvents, stripAllowanceData, } from 'lib/utils/allowances'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { type TimeLog, type TokenEvent, getEventKey } from 'lib/utils/events'; import { hasZeroBalance } from 'lib/utils/tokens'; import { useLayoutEffect, useState } from 'react'; @@ -30,7 +30,7 @@ export const useAllowances = (address: Address, events: TokenEvent[] | undefined queryKey: ['allowances', address, chainId, events?.map(getEventKey)], queryFn: async () => { const allowances = getAllowancesFromEvents(address, events!, publicClient, chainId); - track('Fetched Allowances', { account: address, chainId }); + analytics.track('Fetched Allowances', { account: address, chainId }); return allowances; }, // If events (transfers + approvals) don't change, derived allowances also shouldn't change, even if allowances diff --git a/lib/hooks/ethereum/useDonate.tsx b/lib/hooks/ethereum/useDonate.tsx index 3e532bf37..a1fc2cd84 100644 --- a/lib/hooks/ethereum/useDonate.tsx +++ b/lib/hooks/ethereum/useDonate.tsx @@ -4,7 +4,7 @@ import type { DonateButtonType } from 'components/common/donate/DonateModal'; import { DONATION_ADDRESS } from 'lib/constants'; import { type TransactionSubmitted, TransactionType } from 'lib/interfaces'; import { waitForTransactionConfirmation } from 'lib/utils'; -import { track } from 'lib/utils/analytics'; +import analytics from 'lib/utils/analytics'; import { type DocumentedChainId, getChainName, getChainNativeToken, getDefaultDonationAmount } from 'lib/utils/chains'; import { type SendTransactionParameters, parseEther } from 'viem'; import { usePublicClient, useWalletClient } from 'wagmi'; @@ -65,7 +65,7 @@ export const getTipSelection = (chainId: DocumentedChainId, amount: string) => { export const trackDonate = (chainId: DocumentedChainId, amount: string, type: DonateButtonType) => { if (!Number(amount)) return; - track('Donated', { + analytics.track('Donated', { chainId, chainName: getChainName(chainId), nativeToken: getChainNativeToken(chainId), diff --git a/lib/utils/allowances.ts b/lib/utils/allowances.ts index 515e203d8..3beb33db3 100644 --- a/lib/utils/allowances.ts +++ b/lib/utils/allowances.ts @@ -6,7 +6,7 @@ import { type TransactionSubmitted, TransactionType } from 'lib/interfaces'; import type { TransactionStore } from 'lib/stores/transaction-store'; import { type Address, type PublicClient, type WalletClient, type WriteContractParameters, formatUnits } from 'viem'; import { deduplicateArray, isNullish, waitForTransactionConfirmation, writeContractUnlessExcessiveGas } from '.'; -import { track } from './analytics'; +import analytics from './analytics'; import { isNetworkError, isRevertedError, isUserRejectionError, parseErrorMessage, stringifyError } from './errors'; import { type Erc20ApprovalEvent, @@ -514,7 +514,7 @@ export const prepareUpdateErc20Allowance = async ( export const trackRevokeTransaction = (allowance: TokenAllowanceData, newAmount?: string) => { if (isErc721Contract(allowance.contract)) { - track('Revoked ERC721 allowance', { + analytics.track('Revoked ERC721 allowance', { chainId: allowance.chainId, account: allowance.owner, spender: allowance.payload?.spender, @@ -525,7 +525,7 @@ export const trackRevokeTransaction = (allowance: TokenAllowanceData, newAmount? const isRevoke = !newAmount || newAmount === '0'; - track(isRevoke ? 'Revoked ERC20 allowance' : 'Updated ERC20 allowance', { + analytics.track(isRevoke ? 'Revoked ERC20 allowance' : 'Updated ERC20 allowance', { chainId: allowance.chainId, account: allowance.owner, spender: allowance.payload?.spender, diff --git a/lib/utils/analytics.ts b/lib/utils/analytics.ts index 4cc8faf2e..a820f520a 100644 --- a/lib/utils/analytics.ts +++ b/lib/utils/analytics.ts @@ -1,15 +1,26 @@ import mixpanel from 'mixpanel-browser'; -export const init = () => { - if (process.env.NEXT_PUBLIC_MIXPANEL_API_KEY) { - mixpanel.init(process.env.NEXT_PUBLIC_MIXPANEL_API_KEY, { ip: false }); - } -}; +const analytics = { + isInitialized: false, + // init only when first used + init() { + if (this.isInitialized) return; + const apiKey = process.env.NEXT_PUBLIC_MIXPANEL_API_KEY; + if (apiKey && typeof window !== 'undefined') { + mixpanel.init(apiKey, { ip: false }); + this.isInitialized = true; + } + }, -export const track = (eventName: string, eventProperties: any) => { - if (typeof window === 'undefined') return; + track(eventName: string, eventProperties?: Record) { + if (typeof window === 'undefined' || !process.env.NEXT_PUBLIC_MIXPANEL_API_KEY) return; + // lazy initialize if not already done + if (!this.isInitialized) { + this.init(); + } - if (process.env.NEXT_PUBLIC_MIXPANEL_API_KEY) { mixpanel.track(eventName, eventProperties); - } + }, }; + +export default analytics; diff --git a/lib/utils/batch-revoke.ts b/lib/utils/batch-revoke.ts index 214d1ff10..b2923f4e8 100644 --- a/lib/utils/batch-revoke.ts +++ b/lib/utils/batch-revoke.ts @@ -1,6 +1,6 @@ import { getTipSelection } from 'lib/hooks/ethereum/useDonate'; import type { TokenAllowanceData } from './allowances'; -import { track } from './analytics'; +import analytics from './analytics'; export type BatchType = 'eip5792' | 'queued'; @@ -11,7 +11,7 @@ export const trackBatchRevoke = ( tipAmount: string, batchType: BatchType, ) => { - track('Batch Revoked', { + analytics.track('Batch Revoked', { chainId, address, allowances: allowances.length, diff --git a/lib/utils/index.ts b/lib/utils/index.ts index 3a5fdbec1..c4ec984ed 100644 --- a/lib/utils/index.ts +++ b/lib/utils/index.ts @@ -17,7 +17,7 @@ import { pad, slice, } from 'viem'; -import { track } from './analytics'; +import analytics from './analytics'; import type { Log, TokenEvent } from './events'; export const assertFulfilled = (item: PromiseSettledResult): item is PromiseFulfilledResult => { @@ -126,7 +126,7 @@ export const throwIfExcessiveGas = (chainId: number, address: Address, estimated // Track excessive gas usage so we can blacklist tokens // TODO: Use a different tool than analytics for this - track('Excessive gas limit', { chainId, address, estimatedGas: estimatedGas.toString() }); + analytics.track('Excessive gas limit', { chainId, address, estimatedGas: estimatedGas.toString() }); throw new Error( 'This transaction has an excessive gas cost. It is most likely a spam token, so you do not need to revoke this approval.', diff --git a/lib/utils/risk.tsx b/lib/utils/risk.tsx index 9ba089f6e..30170a8cb 100644 --- a/lib/utils/risk.tsx +++ b/lib/utils/risk.tsx @@ -1,6 +1,6 @@ import { ExclamationCircleIcon, ExclamationTriangleIcon, InformationCircleIcon } from '@heroicons/react/24/solid'; import type { RiskFactor, RiskLevel } from 'lib/interfaces'; -import { track } from './analytics'; +import analytics from './analytics'; export const RiskFactorScore: Record = { allowlist: -100, @@ -20,7 +20,7 @@ export const RiskFactorScore: Record = { export const filterUnknownRiskFactors = (riskFactors: RiskFactor[]): RiskFactor[] => { return riskFactors.filter((riskFactor) => { if (RiskFactorScore[riskFactor.type] === undefined) { - track('Unknown Risk Factor', riskFactor); + analytics.track('Unknown Risk Factor', riskFactor); return false; } diff --git a/lib/utils/tokens.ts b/lib/utils/tokens.ts index b5bb3c44c..c23315333 100644 --- a/lib/utils/tokens.ts +++ b/lib/utils/tokens.ts @@ -14,7 +14,7 @@ import { toHex, } from 'viem'; import { deduplicateArray } from '.'; -import { track } from './analytics'; +import analytics from './analytics'; import { type TimeLog, type TokenEvent, TokenEventType, isApprovalTokenEvent, isTransferTokenEvent } from './events'; import { formatFixedPointBigInt } from './formatting'; import { withFallback } from './promises'; @@ -364,7 +364,7 @@ export const getPermitDomain = async (contract: Erc20TokenContract): PromiseYNTV3w5UDajVwr$(C>vZ?G_T6`#^Do~y-zR5e#26z+ zq@skVXov~`fSQP)ysA7qf#;uf4FymZAQb@U1Q2hWc(znwc~KeB9cG?A5~P{!_xCaU zQ#WK69liDNyPBLWoqWbnTDK^dnXXdkqu^V?tjaP@lkdRsR?{=Y=BiT98~R7}0eEK& zj352y`!n$ed9km6Pu>UkRddad);r-s%&}j@x5*dw%=J6pKDW_})Yta2oqo(%cfoh< zL&baaj!eoIdahnwx8ryF?(A3d>$j?(^f&Q$&Ib5v^>fSzZ@FK~XWm`a`}VusC*Qd* zpkKA0-}m^J+h+Mk&bQu;587Ab*Zp_#drkoUDqL%J+%9y|hao%==Wt0%EgN!z+@3W# zS0fX4lxpIY@u}Ghw{JB*pBs)u<$0_VFU%rV9$*Zt36-q%wB$I*K?@h`5Ssje}kQ8 z*;ctswJviWYoBL1{aso`-nSQ$TxeFcbaO@<2+tUNFO^r(PyH@ia|+Vj`xt-I)4_eD zoxihlMzdbHvj7jkGPvC6OGTT$<~tM8>Q=#en%o3wkiZ*9sRgby7S=Z6)Y4eiJ86eBt+8V8(aOpYm$h!r9iXzG0cJVFCa@ zss1WSVzx>wqHb@3pN3aiR@o(EHYQP&1eiw*2wo~-qCAuB9(vljQU1P;IdWL= zIsQp|P(R;l)(|9(Fb<)fkCJOB4kv{~8$F~gu9a$gcuKd|0{7A{pStPc;r>3h0_*TUDEuGH>MYD-temsdw5YXOZ%egbR%GBRF*3!NtFIW* zAL~~X(7XSL4ITBSr@Un|*K&)Cm@PuzY8D5M52}jrUcZ}2@X+-=0{23=nKDJ-GZyu(rhk-m zZ)V%6__cFymHkJZ?gxq8Lw8P$NP?3j~WvhGPKAL}|$tEW(;hxxElV@H~|HR6G!udH^Y zca@NWgEWlqXcjlmV^xPCdJ&o#I{rGwiOY>n(sjCsyI$-YA)Xo0)IUer{3@2g8gJ4; zJ!}ogO!P=}m#NTaPYv!IN4W9B8K+cEw)@`Sd6G4qX3Xe=6DU=WM_-xHtVLYG=4b{g zg5B})JC^m2aY~34Bnx}+^kjc*cAcD#420NpfCv>t=`c9w0WMz1(4^?)u{`FNkGEaa%(F6-YZWyTiR%d zHJRzJD57D(Dz=+wVmP0T`$Sqp{)Bwk_uQR<_YXR$08ql$$VJ$Hg`Y_#63!dtmJ5WY z3Ajrs;ad5-PINK%J)z-wFO7=DdkI2wkOlQK$9F$E{ToP}Rr-x>J7Hfcmn>P9&ISB@Up(V+#NT>f>* z%CfI&f8ZO4HM7$|V|Tc0dgOhr`7v)OhdZi3Nq_(T4KY%V9yanal5ZTBNOqOxTf%Ol zjlg2Kf1Fqc`R^#wCdOI{|99(06%Sj3_LSdXpc=N5^8OOAnnd}_*uOv3+#GHc>b<|u zkF${V6cG;tP46|Wf|_kM|L`c}chTQdNi}g$FS?69V+8g5Vr;UJpfDp`{PXfky#jR8 zdcuUBNghtM9&u;8Qwe!jO_kM!g_ZeFsO1v=}qd2nrtr3UQu6a=JM+ z%u{x_$Nm-AgAShoDdWHR!EH|j5M+6yBj8gE^ZmQ`8^+I~2mot=xapr9!H{fEN zXr+o>(A`D}IfE;XMMxQt^radzwIBG&(*I|CZawommL>j(dnXxGhN2{v+TVs;geR|T zh|{2E)>9m`g*~;bP57J0h&PLz&UtJ%Y{u~yr#6oQbWCtBeHaa)lu#WT4if# z*~}IeJH0OLga9)$%s8dpyvQCx)xQLEfnrDlsKZeTAmohG6&0_90&5QQz_z81Tdl_$w>WSCTCLQKLTkriUZr|j_u zVb4tO7{R9!Bp#D02j`Q`4+aQili-{~l4*pX9WO}e1yYY2bM%D#19%`7q0Lez^G;5` zP5w0JG``H9EMeb2T>E2p*d2;v$uK7aWzGBIWoNn!L_4z&OBSar8Aq}c!|QQjJSn-c zHKy>uv_(VwgW$T{r=)nw>p7`}cxasZ=jEvcu%r@h6Ks>|)}bVqZ~yWI+FmMsE#>dt zCaW}93}3(||Ip(nj9EX@>Fg|Gi7E`K(T{7&b|#L zz0KF4*nXMgUpBAx`g6r74qqCXazjub7TKIxmG5_GGmVu@JDA-+3*XdQ@PFHfHZAy~ z%3s#&MuHM;B;W672tAqV`by{IyLiV=8|JdaZWq+AH4V5DBV_#L1L-T z32lq(7$oBuyXVM=Z0E8G+)Ez*dY7kPPNqCN|2p}QOJ_m{6fS8Xak`nENY{}|DBD?f(z|CbR}53$mXE|Ue`{HIjj-!m!(>mS^m z|9cAhkO@ARf60IxEx!FbEUmPiRzi&^^}m$=wIXezf8^38KltreIkYdHmUE^oM zz-x&^cpwmdNgNNn75$do#P-Wk%ZS<%T(93v(frRP6WK4M$JPB$E-ivmQMy15Jm;VzVpu>k5iAf<3^{~0c@3{R2A}ey z$3ciPdvfSLR;7vbPha|8jDB&^i7wp57iNl1Elm2;Dnh5ft5jAC{#cD};9Q)h zrF%R?bC}Hxwv?X*tgD@+8S=_t@z@V;>O8Ykv6%spJ|=(Aw`w8nh9~!TN>oH^rRhNb z*O$B7#nk!PbfEq|mI9k!(#P}Gmb6J<1~mPd)4&*oy%b$oueAVOb3xs)m_taS!_cml zr`aIq&jS+8HM?f0Y28}D?l&MCopI*btn&=EMw zI!)UIrN2lJj`_&=;KD!CNX39EL;nvhX41te4bbpiyR=sDV#Hc#rWfpv>8&yv4OY{o z7PW!ZLB3Az*rY4oqrS$E>Yn9R@5Q$_@v8j3oka6g!k8blQOQFpXAzIs6Sgec$mn0K zauN5|l*leaEyobp`mweA6JNs)I-zh%YLw-Pi(`}~ed<9!VXk2<|6$iuj5{;HJJLEP zZe#GgBbT9XmfSFgR9qc>R9mqcd#wLqf7zfWt32037v5pHff;PoZmvpBCA6%6(ZII@ zyJmOHs&Q>u?h+1_`1!w~`#AXKC50z)S0g2U!U_E2xTISa>;I0}@qwAFzpF`I2LBs9 zkIi`*d~_LO|2pbFxcSYOZpIDNOI9l&Bn)iJRREKqxStpflU6oZ8={ahY%`l`&KRAy zZT;CK0ajRU^>4)g>t-gp@O8?y*u6X$uB-l=8SLB4hdBQ+<L3r774ln_Yu7o3Y|ti6ImZLn>Z>GbV;@odSwKr zf3-a}gqVWl)xL9+=ktO~iA|imtMmYINnt>`_IR>+WWUdlQou?S+zgAwo860Eq#!Ui76$P{ zInb;Ln3fID)MPm<2s_L9v3SAHn*~%ZOrp=Ml*J4n^2R%in!21+mQpU%cCpt>J-&2n zGsJ`<6S`MOmAt$px*2U?-jw8iKD>ueTR>O<39TYLO`S$LDHc3G{HH(2sc^8xb)Xy) zJ+=1w-ngNEK)a5=5HgUA?SYn%)htKpz=Vb|hToOh_(}qEEqMXR+hJ9nP|1mEtyn!h z<$Ffj?!fvwtx~ju*DbOj@XZ6jjGEEP1WJ^&l;PfsqAzPa){ha1yr}QLoKYQsGy@8+ z>1DpEU~`)o#fwrDvtb=kjw^%m0c9-5!)K$rL8tm+2lCzNZC;-bTceOW_IbwG&GnY2 zJA?mN>=y=%2c(u26PlJ>FzQd`<~!YSmxq+(?H^Do@D`KTkyd}2m&zV$B6LR;6x^Wz zJD(gT3Dn#AOth{Q)Du9y{DC@4&_;LL#oFkp6LsPG7$IiLpHu=Yv93iI#Q>iv0z+t` zRvOqfoO+^)6s6n9I>g3tvvmzoVspluUMuC$JV$s-flrOewoOtKbPU=2?)bseH<0@D zeBbi33}bYe8xdc`)!9Z50mgk==`3YuD^j830zCYq2D<{GqCKdWT7&wTRr87Gw~wfC z`>woJOz8__HW=b)0q{m?1LPf-#9q1_LNRIVqNLxR5Iz-i!4Mf&U+-YANqe?6#=5h= z!4k*PAm5R=?O!VI};j-UHZAIL~jGhH)} z3HhO)Li^pY62^=nqfh%6PsVd#c;74Yams{<2{b56!fX>3rEN!0n2xV_ziiTx0=CzxNk3A?~wKsR^R##q~Bk*7e%mM)L zdM_iQHO%Z)|B0)JF^1u`FY!hM{GHB5qNvi*x`zZD(+LuZU<0d(#WGmM{eWwzV!o?Tv&I?8CadPhqsw09`o#~~U zuGP5gXfQ?uaJvIsh6-}TiIO7-TQ53B0@N=9xI~(mH|Kk!z7HNVlmZ4(3uQrBWK|E? zQiqh8a0+l9B0v!|0mcoJODUM3SG1amcbFhOKx@Uu(h}TuJ$4ARb~W)g@V%|1iW6)) zxUxp7g+r9FOEjoje%&C1R-HQY2Pst3>?`+cEJC2RNZAn-1!oO9`1bbbV?m~NFB=k* zaBjYw8=mA4#vKx7rkk|>Vh2#|QpO&&T9W`cTJiC^h3h0-`la$H8RwOEQXEf5NJj5- zfg@YJwKRXW_glj8M?Off!7A|R(VeZGAy`C@X@77CbAGvI9l@aGY!oaFlZbc8Zday| zty4PITc_Qs9{&aq{{!Y)7K#lo2yB=`NO-tR(8dkWMbVU03?bHS?n00o18< zv`X`)9_U5!B-y9DB@`+6A11k)`u4-^qrr3Mry4gJD%^WIx}=nIn(Jk=A&qzOGw~vgO%K zNG$9Mb)H*wNmbZB3Fdlwt;k;sP}D#JEJv>q>C!sYUbl;9Hufy6k)&+pq!6t+))m0P zGhY4%U)5K%0suJ#(UlAgN<_v=IHm~sY6Z<|0c2KOHX$|BHIvN@9qj_GnA3ZzE{J=t zg?b52-#dD<(o*oXzps-d@QS(F%XLDDnnuFO%u$6gY2x)3pfyDOS^sq);cNn%e-V@u}+T<}FMh!d4=wvYK;FgN59W6XOquQr61EZ=cc z>IL6V7K-)=qt=<>C;TC=I09m6Ac{2_rTPE>(vbo%wlO5{Z*wuJu3ux#U`E8#6`yq4 zCq2c>_EQ*$()k>nD^)}^;Wag2-|Sg?9dw&;jUi&u-9V+_vB z+4!wbV;s5nL=Y5pB}J z<0#Pl;xTqC|Lil~W7H70EgJ?xPsao7lPjVI=Qwgmv!qzR#v=O)rJUU9HkRZf)L@Fv z9qL2ecoFFX)3hdtf8*Gq=to+Sf;Dj4Ocx+d4?S!8z!OlSfKPqNV#y7{$LdniG4O|U z$z!V_2=(x?;Bfu10dnTfXh@joK7%RZ&VyI<7`;e9%%ykVPGc`)W;^G}n#8e6*s{pd zHWP&LEuvLT&U(LvVg$JSW9VDt@@Uecp87FZ@6$Dg0RZs)}XdEM$zlHMG_NEHX2tA zIa_9S;F~8|$2jt`VKK~E$RUgaF-CXf_*vW1010DHJ?XIcd}slZ|~rP9EZ6eaz3hoO{Y4g&LSTr>Ux>1a?Y5Gh4Gmm zZpH~J;FuSNH*vQ{6H{C2r`>>FaJ`o0Gbm@aDf=);@>(IWWx;|WkSFLukWp|sfL;>( zw;DD;!!MoDQ?OJrsN+US96XNo;2Pm^RKMbZ2!JhYE{RqXi2V0sYu`aSY(OkXLw7GC z^cec)i%m11*dIqA_@wD402TYKY{sQSMv*19U<|aakNhprswRKkf|(p%*n?qI3at$y zU`e7ujv&cazc~IDwi_YyBv;8W`U>BvkrkCXbcS!Zn-(;KkA6v+0cR6kS{|W#FKgRp z23wAvd*eG@_#KuHL*=y8d>fC~_aJ(m>{p?7Qq2D;{s?FD67%!q*EY?!diAVqz+2oI z&iXAr^ennH2q>O%ktz<1fTGuY=TX1`{)uPm7U0}k*_NkT(2>5b* z6iCV;D+Db|jv$pYf!GL?d412j{fP566k$WpQcuk(2u(8lFc%X)ZkDD^D`2ng!J?%I z_bn7l)Mtp>iI$x*G{57tpG&KWvY=;X`M1aIPEG_}1Z zb^}(cOOixVdd=P~r`@IQUiZJy(fW;IGM6MWFYoDU`qAjSUeSAE)G6DFmU=piOK8ih zzEEwqap>-AEJLge<<6T{;lLo>{GFOYpn7xoO%}-u$|~NSPM4Vth2J=?kEhFH17NtH zXU$LRA<_c2ab4&mZ%`){7PvN_mV-!iqvopjhZD?LYS*71&{XQ;R@HW|cCSLqyzEo8 z5`Xzr2Q&P$djPN-VChhlyaIVz)La6H0DG*{VXwfx{C{5}=?mF)4jkeTzTkQfti|2< zyfJ81L`6?bqwVE4GrLF)eWMCn}|TfNx4KE|QAvKYCnW5plv{YgQ7l@0}7OE_6CzfB3N>^*wCqO#BPUK6 z`u#Jl4YR zq0!IrtYd#75u3p%WOMe!?f&NNU-OdRuM}xwVovCEh=tl$_*-MeVo$!0n3J_AdV-kx z^g0Jl6YJBaBiMH(Je#7l3+>Dao}LF#p^80s*&SebPN2o-HnyyWjjW<{VI3axS`PFj z=qGi+v%1BIZNNu`wrf8Uaomn5++RQUe%X0LaaFy-1z$oPT3x`9G8>!^Fdl+V%IN7B zF8{Q~U4^F&0rr?VWK57Fhy=*AWT-A%pAuLNPcLZ5{-?40r;~ zP^CMpv^62BX&(A!f`_H`h-^?|ZmblZsLk;VxScmQCVQa(gMj1(Dq7QSF9ZrXXx#xQ zeC=*A)2xTvga|*LLRjT;0>_;Xe#*R861M79KT6+50=u6-3n0i7 zE8G-w{|8_^Q_(rLe*7#Okyx&=qTYm)6j+rwPDIMIL420b2?S8-a(Tm^h2vW zqkfTSW=U}|$jODCJ4t*7Cj`?e(a`0?LViYQH39Y!0v`IDLvex{KTdD`4`pYJ8>h!V-$A|1JrPeJ_UqGRkSD_xbt-$y{ezFWgj zG{Rt1L*O-5{DSAZ$T#^&=R|=7WESqbO`#oemgKLU*32@W@)@}zj$)KzuWYQPr`p(| zRBHA;00Stwn)#N&{I+D-s+&b;eNtn*O^&;}t_EmOnzY^Qgai_;Cy>b7ZBolngFLgs zuP%{IAM^o~bm)=(v5HP33x0-;cDL?;i}zOE@(25x*V{b68rweJs1sj;TgEA!`rbIo zKwPEA)(QzdWo0AP*`}Zfu$ddgc=3f6R^p3Yv%&8p zv6l02kJ^ov@C-GBN}NQ8XR*j#EIU3dF&Q~Sfu$+?;0%$u6s z+S}C0MxEoN7xs&Jv=3Y;U9Ybo%ws#?lQNep>L;S*1Tu|F^nMnR54{{xxUV?E z+wr1D_^Ss8$QbgVar*8eZecFK9boMmW>Qvg7Ee*V)WvAPwD|tfBl-TP5}X{17%#@F zzRzc;YyK)tc&MW_oyZr;`lQ!gx%boQhpYURP8dT-G*V(^=Z|iLx`7_mm(8%QE$c!1 zl!dEOp4j@cQ4HERK(~7#jB=<~uqPh`{d#ggOdbGz(9VqctDqENxo65ZeE}K5aPIzX z-~_wbhH3h+oLz0)_UbNWpZcmN$S;H}A+tr{R=;hD1U`_XLCH(GBXH_%p7^&$j$0$<=SBb=6C)U)@d` zDc-rewNd9Px`pSTqpEh0&1*;;U|fK^uUGEpaxUYGdPU!WOFI<5-BQs}D$4a@`Iev} zz8$XN)5+W<9dg|5)6O3gtrxUx0rsKxtX@Ef7wT2lQu)8~<_w!-UQqda2OWeRUS!BO z=`l!M+^D1u^F4Yr1S1&d$;od(;-Nax3lPs5wM)~6N8+F?H3kuy($>y77@6>$n^Z07 zJVhQ8y7r$bYpa<2liIzxCTN$>2sl486x6~q$c}qknkqDoMxWLBT=;GU*3UAOh7egH zkMM@-=EoMwg@4QRCQk@)XIJO1dA9(8sjozwx zF;n}3417;A`+rC-Wzt^{q*}OKehXM(*3*`K67uva$X^-l4`Xs(ET>bUC-v9}t{-z0 z1&Ogl^dl9)N`b^H?$WH^TBse91UhUb7R)Q`dC&m)YEkdwOgFvPob#(z$D zjH#Dog9?_EahN}>hD<5sJwbfLrzrTEq&Et^chiY=mj)Wp9-i*a~s+X_o-%1^)E{6vjPn4=hx5arcSm zpeCjjw)5)z>|8Ck_KULQ7`F?rvlr#b$Tj2@)0hZn_Bl!BX1OSv7sc54>*i)ntXHme>}omro~+ZrCXy(S5>h=7?_m}3nCN-V z;(&0F@W+jNT8hPjSQP#TXz1T712oPp>SWZ0iS%&&qwHZ^@qtS$s90b#NaHtsLEsi= z5FZywF<3;6S>c~Qy*o*~nvd`c)`HDg^^&KPfkfXc3kwl1R7@-{#+L%8$%#N%R5%M3qSvm7FqcDECqnWd4GLJ z)`4r!K#Rsu_-PXL6+qBKmpUy%F4bpRDH_1dSdEYDaDSon5YMxM?&yysZ}XS@b$6&Qgw!GwdD+yac^k^ ztacOUx(vmU%`cMRhnKQp+~i%i*H%y0%jtJ9nm|%yIi9J2AU?NQEzP?HI^*H+jf5}Z z7}VzbNRrI_J4J8SSp7~_-{`C<7c&o?iy1~xNxn=+wz28=dSqkQ~=1fp5 zm_`*78DM`ZbyT&wzhIp)vK$Q#gh}a;W zDf6Pr*4y#Ag6yVYtB1+EWC}YJFIXE{#d}c=UXhS41qGGEf1vN1%ti`@V-fjm46czU z`1j>cvp0K+(z(s>Bg1htq(prcu}_7ih%#hq-mY~=>tUh`e=u zv~Sxvft7w$&`Tu6Ocs=5$uTzM^PlsG?m5}%Qn4Se(w7~k1gmwv-#l2pUIZ}uhO4k} zCb*b-DH;JqRfv+<;beg}x&xFxLD?nCQq!8{LVDA+WO>-6u<(^erQ{GbndxuxsG^HWc$q82E3ddVK>sU7R_~c1XF54A z1Igemz17UH#T`<6Gr)5d71F8RlCIC9(Mq@NDMf$B`T;`dc@iZ$@e@C$`ot5UXCY$b z=3yGdu=@3gfU_i%nw~&9Oze0^5l!{3ov<<(#&;DmW>zJmnQ#9XMTQT~eCAn)By5_V z46#SrghK;Cg*GWrJ|*fjttZa5B88x)_0|%sT4rOPYVzLPl%NUsflVHTZ)xlfc1tG| zY#E3D6GlxY2}!95P}+{eHhG!O&d4m{9;WHkpjMmn1BzT;g_tGjgnyTNjah&G0WyOy zZY8x1s2Sa>Cd3TtR>b=g3uFyc;?|E>?y|=s-k=p_=2s2d^Mpz?Pf4SSmxCK_W@@|H zw4!vGkuTfB3v5a2PaM#4-<9k4)kL+-mS*-F7&i#T z%~XcqasZ1!qBTcMRL<&;(ca|_7CUu^VyrzYu2}M72t+}_l$TV8SBGM#5o>u4j$1-k zUse1{3z}BQy((1-d6Kh%^{(cY2XWF8ccgO2`lHbrW!y+c7^!E@cDEa5V@G?b6VaYk zIy>{ZAUDkcbELeHGf+PXoB<~lD^vk>hJRtoJo)}oluj(>m3iRrgSCL8kUGKMtq}84 zGR}&m3+?coj|He8b%bjY>30e7p2vh=M)C?5qI`^oVjt>EV|t}J1`%ER34MkVnu5SEB^0}gf-_*o`0@R1&ANi4k1p*M zX=8~K?rAHO=bSSy$0DriizX&S@AGxT6hY@c7B{f*hcd|?0+_1DPJyO2De4|004GQ8 z;-ZtN+S6SJv|3Mh9A<(LHMU!bMug(`jsyvswI;w%a#2no?GjP^9B3|NLAwe?(--#m z%|XhH&OX5bwZ~6F+ozRD>ydA{_G19>lN218jl1r%5M?9t%^3 zoQuYMwSKb462#{#ng=`oIvUFHSd!Nhz@Dkg7Yrh4d+;4LYe3$n0wpq({tmC4@1df$ z5Jec_X;4KAp(`gy$%Y)z;HwV4S=E=_d@NG_9vQW1aPOJdHI_~u41(aNnjKRR-5s4Y zuRaV;x>pX4j5ky3%~b=Fb*a-+SaR+?Tr=`aFfS$2NypOr zb*GVPpu$wPD_yGgR-MhWl9C@?2d1|8kamp`tR?B~OR52}eIz+@4!Psf@C8Z+!ihlo z#S&WM9uTuXZ~VC_l41zzV|{X~N9}ayg6?Wih$p)&EAmg3q*T7|ZB4LW(iF?uQ`+n6{ z70!F81mi2Tq4A99XgXV#OWKoRi3JPcLod}(sgpAk}d#O>AaGL}C-{gfCr>X;^yMG&I$eBI|zD~!BIc_+R)QSqKRIdd8 zZfXg!PEXx;UmJjLP&>4KMG7KanH@p+b=V6p#7;`oFwWfF6dJHRKDgvvGLD&uO2%`gXT|AbO7@8nyR7tS2H3bZM<63- zF>KhHVB-ux9CuhTpMIT_&$yNnn-nmkJ%^oC7Z6_P?GW-SG&O|_Md0hLlX?IlC}x54 zCB}0pS-&mr;sWN4JX#Ranvp6B0QChT8e*OV@i9_2$GS}#v`%L-zGCq$ML*IAVYM9L z_3R~%`HiKl%!!0zhu}@?E>8SYj;l9mC9h;p*l!8_^ty8 zbnx=8xNDv^;Z)=MMy3oNF0SFh)`9xQ#$Kg{Tc+Mo;XE8M3oy&Cu{zh;@DVoc2CC_>=%>$b@B-aO~yYo55T-=7z8}yZy*=v&VBRI>t9_ z_Lf1xO8;DA^R9ia7?+F1Bpv5?C)gRN!bn5I>V-y0hw8$+TGWnzE6Y(C-t^f zxqQ7N^6o|)>uU{@l5;b+048m9)UmVLluiu%=Y+(mTrQxCdm{nlO?aLOx+!Oj9a^Hk=6q~*sW z57V*T7Cq&%?KB6Vulnn7Q6d~OabZn zs|bQ@CqXx(FQ^vewa^1!>5dC?UWN5K{E#)QF2+PyLPHP0m(3ggGTw!PoJi4BvR@hC zMdN$%fj^^T-d8b(B6(re&MPZ4JC=EO*b`-W^*(`O=?`3A^_-yfcHR<#yR4?|E2ADq zmm}Yfq_ZTO&ZSI-=9i2VY}mjTd(`1vnlN}O2U8bvRwYh4Li6^D3VOrHf%SdZ-+-w$ zOOs9+QbR0=gsCn=TPikBgV|Q59wFZgoBZL%r4SGy-z|VT2ZVkE$mv(viHH!6*XWX= zW)a&#zXn{^6!o7c+UvApyR}-%A%iB%n^L7J+u37edq-ln84W7hgRpC#bo8K#xa#?sn>v7L@#ivZWzo_mR+h`^G0;aU zFL9ihmTIK_8Hn>*wYQt7VyRY%PDxI53YIh2f9&CXI8T(WDg&@ZNf7D%o`pJAMB{0At)QR@T%i|*$721#$ytwlys+b)rDiz)Ha6)6Taq6wY4le zmq^m-*1g#H*6Z{x_$dHKzQi4|&db;ChsLbnA-9b<*N$?M#VjU(e#}y z6^1P)-(eJE5#{?XhKJDM51`aCU9^#hOtKt^2=zJ#9@Ud#x0^i30~3fewm7$S;u9I0 zD@9Qu--XLhXG9Qwv-f#&wJ6PHDBRIjJyOCus3V?-E!+=lVbGt`8+z)0Kfr+qYF9UW}GyV7} zGyoj!G_~xL=BrPboON%NZ*{~~eIx|$cLwOX8n*!`@f`7+6*^2r=`~2`Y(m7M{ha@< ze+=dAJriibSg%=U9fh=ZNlzsR&j+`PAP|y)zGCFHD zHFCfjFkPOr#mc4jR1Iku3I~83{v+NUhg_{CY=XfCDY{}oOAo2;lkZAF)?2Y+f6F<2 zic;?2Rw7G-@Ytydlh{?8KH;%jBOV#}XCAW8na@|9>XnBVK+>ZjOtk`p&b&WAYccb2 z9zVT+$@hs6KO`OVYN1t|4Mlvy{YsME6j0%~v%7Ln3f4)L^Tk7k`=oQ+^Ds~|4bZVc zm4Oi2^qE54JkGwO$MNJZamWdNI-nuZK>W>ZD>QAJsGUIG?jQ8sOhCpe8#4Shr4$d9 ziNcFUo?ttd`}hcpLwnC19)U+Qz7~6Dtijr_NTT(1donD!nJQt#w7WM(_jsUTwXhfk zDi97Df=iZQR2!&6g2W%`v`U+bZP#YLrM0^iMRzUc*DfU*&*AADS!4Zc=s<|`Y?5fi z^vpAbKu1Hpd7L=) zomZLs?;ZYt+vM`%X5w%afZ%)AD>(-H6E`x$VoJGcdDdvvRnj^FlaR zj|Z%uM|S$8j$7i@rD@;6VY_rbE$%S9lR}s7#GwT9EKQX}ot7I~!xX0aqW0uMF)!DR zS+enWkJivj8=F0?$M2>+JM$<*FVXW9*+=m_Z8NJ{FV5cL^|!1_*>Q$sZ6|61Y;k!@ zy6moal{2Q%2O~g1PG$v5w>hYQMsCMw?%dYbFOcsgHDxxuB7>uzd&Q1`N^g!6tlB z11^I|aZv$`1LyPS{?SrF4(k}@yRhYm;KG;3{p`4XFeLocS3UMjpiVqS701QOl4^Gb zIdk&N?V&f!06$q{^5?n|(XxQvTiptda+;{%ojif{*jJ7Z$Jg=JZODWTf(+1n82?Rg z?$<>Ii;3-Rw=beB}x^VF5#?I33<`cUFYB+o;*ff^x<+L8Pf_S|_t&{c$981M7pjDnz<)a#CU z6su#!`H#Job!(v1O$$u2J;FN$1wELsLoID3jyrYFy~((6;W&PKA5l3V37|>AmxLq( z?1R`Um*2&mpWs&nhmJWGQX301yr^Gw>7O8S@*?f+V_G|L^f7aqU_D}Uml-T_Bal~L z!mQjGm(WXm20Vo~k*F?5aKSC5O11Arq*NiOTo-bFu!O(mS$VufGxT7vIJyajN^l>w zI+2`bQE!y}pkpwGC3|Zk<|{(kgIC-~mEkl>;7w$ySHOlfX)^1jEpp3uQ(LLs^?#BxB$`EVFPX+H0w@a|Mv7@A@& z>{8A?Pn1g`0VPoEHPTO1^q4e4k` zO=qbssUcp3>H&o+IXeYm*1V`^np0&4fH3^VfgW_qO=3PW_GrZgN(5lMpEk{zsSTu` z4a?~nGb&YCl~93RhX9<8yra3KZH4UQ^?f9*$7>#~Z@9|1S+&*vw78uohk82EiC;;= zyagMOn6)(8Qmf%psJ)S29U2UjyFnZ2K;Q}ge*%#cZtP{d@9t-rzc%`Xv5lI4#(+V+ zk)06|y5e`PMa`<|hj4e+NwZz2DJ!d9adVGCSr2^vK;M|0A2r|w@Bvh`wBdQ^Di}6$ zf3=8SLrZIbB$3Txc!p>qb>MdK`gp6{UzH;7v(LTMS(&tj}Zj=Y38LDcZ!)-ZdxqqN-S% z9SLRt?0G`ClI<^vfg2Z~tMKEJ|Nea$Q2Ve(_JIUyu5Xj}3V$M_sT*TZe#9XBeh;DQ zPUl7JV|Sc)0v5A=j8wX`QKrynJ3pFh-5yJQXV{_~=>YCqalMnshi+6H1aH|4x$Vzr z4Sox+iaxwQxXP2Lr_8LLEG}|)j^xXzQT9Dld-C2e>2a%aRycRdtI#d^ZH3f4l^66! zK}}yLq8RX5HIj%mRlfw((t2eR)#B6lbnYDssOuC`m#Tq_@p8^6C`?1@Qhos*fPDsH zF0!Cj0IfGLYPSVOtD!tKUgz!n_D)uU3HDSQTl~dgRs_Joy0=Z&onvqyB>s{LiYSVk z55#np!YY#R`T!0iA$<t~u5(=Rh@V;0ld^k!lf~aj7M!!}!;L zKESiyh&<;)wcE!JgP1=f0c$aO0r)UTq7l-!L~Z(VyCgjX{p!#zDq+`CarffI#d0w+ zau)^ad{WKn)93gL5VOQZeyo??^t=r!*Jn_Sr6&97#K|)zxf6~u&=sG74D?&t8fJmy ztQ$a9h^JHz3J|s6t!7XNn5{PFR^ox=dUvHgv+%Z=-cX$Rvg)h5ve27p%qp8TgIrh3 zZ5TK7N^9J+LysBP;Pb2z)BSR1raGnMB&e&bDG9>cb4m~KhbV!ZTI@GidS0CHk1v1% zceX*wCboapQsR867UrEXUSb~M{)KGZaxB!+tE`Z`;sfx`)9u*ucD#?q*mKT8dZc+T zjSA}ID}V!vWcNwMFRSlgvyf~LyPcI(NFh%wCcqU`Y)LY(pNLfefdH>15$ycSg$Nzl zaR5`7OE$6i5GSd(oH!Z!jAoP9#FBm|XQc|NSgIQgSLPv83jP_k+o@bH(bs-ywy7CN z+KmExpDafRmufo)%2PoT>1o%8#z4<1_sRME1R&OPM^gOqP zj1bXjN@GyTumkP+CD%|J-BPkf2S;J6sqzruIRnD1M`J&o6*yL~0QZVIQ4oqZdvPbB zL=9R_y^P5{fKbW>Ru|4L=x4%VI-{oef_gxCQggcPmU7Qvv_k$-$Cj)Drs+uT($?@J zvTkCYJ3L&B1hlcR@;WmDO@_QJmu$xiv;U{f?P53yC~XzfxbJ8-BJ{_DNi34)4gj8L z8MlmfRZ00I25qVqpat0inh#rche9m6<&56qB%;jhSA`oLKvLmuhGVG-aq7E&R9i%k z+_f#age*AebDm@h1IBnL@4k42Z{#mrLBshPHeLwW8Aol34y=43!WmL% zu5gkT7zG6owtB4lF8+&JBsi4l?#v2NU&0 zRr-4Hb@}L7miPdlWK_Y`T@U|gYnO1BL>RjvUk326vpZvpUX)PM9-a0WLmlf7-`!pN z%dGw0Z=kGndcn%tRFwV8Yc4*s2g7Ylp6u6aWqd4s&t@S}3b~&oOC1l-lv2@pRXoDU7(O?t>Uk{B@*P_k)5F zS*~#oAOa0S1ErT6-oyvcZmgs73IJYX1GWaYVXRryyVx*{r=ypWf|SU-Dr966wwp)F z0S^iP@hG697348G%zDzwyG31#Bo?%SQ8o-6DDjD31A^mp&rE+JJ3yf_-Sp~_AfuwJ z%PbBtS0gKfpA+jhcKw&WF%C-!|}T0S+Ag2!ay; z0000mY!*Jf0@vGTD8^5)7|Y>ximN&?TAbD(NaM1fXb}NXP=Ejc1A9tI1Kr%LnJ_E> zR1gV-KJRzIP~DmkZUYI`^8CY~XU)rT9uo9=ldkEI)+lUXkln@Ckf9XVb_BtLj zpR;8nHSMf%q1pUBg-o-IbD&-+hP0stryLFz$unR3Ym?8$5uKq5f0Yd`s5eyg(Kea^ z8c`!WJb%%JxX3H9TEGuq?-zfN(`h&<-Cu)w0SvF4wfh?W0ZnkE;G%tkP-tZ@ZT|&b zaO9KykhQtuo=wkQ^a}IU`62Y=>XG{(3h}SIxC{nX36;sVw{})wY{x!wa@h3MErw1K z{Oh^UGAOk)arupvQZV{7Ld|#Lb*DOB9_*lFesZ+hzuvA}9}Xk;dROGl`{X#T)4Yq| z>RL6olU6I{)OX>@^CkQW_pb@(XgSQfWhLg_n(gL^t%UCujf(wvj^7bD)zGi?LxG_B z5U0K2l|JZCH?M`CB{D3 zdF@e8*z@h|>sh)Z;(rik+e*GPiXyPwc-nKl;bX$*lSwbMGenQ6S+_0_L_xa{enP&70NtHQ0Mn_VX1rObW$dTQZ-~W)j}< zD>u7+nBSKdnwfFH+0p0-ri|P$V;j--x;)Dlz9CyJ^mPDLXSn)kVMW0j6ZP~Sa@fF(# z@OG{g<8}iCVZpQC3^Ji&K9&~F@?((W%JD5W=F-ixs?zOuP4YE}PqqUlMC*GY>I@*6F}BvTsY9j_Hv#{uE^EbbI>rSn~+#Y1ldQXJGy-=9E{7>2V47i!G&-_CIlzB z`%*MA`hlbXHc$W}6|S=g^*q@h24M&2rvFr^{rTD94WU2-vII{vIBS_Guy+>BlTqgXbzDvp& zXO;eGDg?tgV+gqWW|9RTWa0rah+KJXsn)p>^ z53+Dx&u^lD8uOwFkceFmh`Yd8g;HCE3&P_I=hEp;a?EBOOdAm$sJuA6UA{uylveCj zi)0~`CDWu|Is(CPL|GUca5N!9ivtZb$9w|}CE~`Nh(1LuFH&R^SZp9E*o^neMmXcz zkchT{ml z94L^TEy>3Is#{ciI!}NV{dDz+7FS7V#QaQ7(@0l|lJU5ZN#os7JlVR)_e! zn)pl|R|BP=NZW(J<0i8yxo7$qk%86`zHmZ~?3!W$SelFDmROF@EMrgM?yxY&ovw)2 zhu^ZGc5P(9MP;IY@AzOJS>NO9r)~iS>7_Tjgu%h2c=vyg)2#_6obdt`*@n;+Izp$+ zf#xTl9ixO060+)khhB3*(lQgI;n{QBBgrI(1}k z#v+8vElv|0Uexm+*;s@wvN7C=Nsq6x!iLl5`{6^;DI8Kj&3mz?QdOko`w>O^s0W!g z(i{(vNf?mP&`)HFgzqs)aZs-ReG2{_xNuS}1JuNNWVmVj%`z{BF;67PM2I65{;H+q zio4BxS-LAi!W-Rl^nCA5w0+ak_7+wl?P1ekorOEs%j>4hHEPBjS7qLM9$QR!!Fm=v zo0SO$+&yPo&Gcv+oz;MyOY{BBiX_5i8yQ+78EFu|%1*03Ym~=3r5+{rD(?ZLC)2U^K~oUgrq-ucgN$!u6<2@HcV?XTAjb)KAaKKD7k*y92PG5h zudtcEs&mMt`MHk7FEP^BUfOZiCDQO}?*>XKRGQM6tvq%BU;q5MnQ-9;_^sBhw?4i` zmRF{9DM5d;Y&FQb7Xd-wOZoHxtO+OEK=0eSoh!`;q&&;bnlg(HMD>yOOEQB~##jyM zwA=WskZCc0mENlh5xPv}Qt#6X00ZGO)p*5$W^+MzG$xVjdlJB>jUCDk*5K5u`zYXC z^)4(#a}^?)fk=PG2@~Z1T7z7-q5zCP?kG%P021f`@>EcropgSIPc1>uYA2c}n1LvP zD1j(|-~ntL%%HD9PW}@uPSY>X+dr$D$t25~MxWW@*nux^+@34jt((kZ@iQ*7%d4C8 zn@kM0*%>|?WC z(%?2xO(l`QF6(ftRjXH36Sgf0?ZIT*-&vJby~B`G%=U(#rGSt&t&&jIMGG01(B1kB&T^Ju1on&MbCzErPY6YMx(7r9%KM&VefYeRPXQZEY}4 zuC)9RAV8+OMl?Q9ObF;xDdWl(xwydv#Vq^s-boO_+;UE6C;8X~h5%l5yW~M;EfyCK z()=L#O2Abt7$z35rk2SvNo&DNz_9~1e-wp%fD#jv=>kkSCE>L}!@1;p1h z&Wn8(P*6~yv*@a&jWgDaPCvS43K5!zq%2u?rkimgLB`spJYpTH2tU?EhOHdI;82gwvfXE%af4Z2$-YcwV<&3vuUt3N&y&_`%H{SnkE21O)Z*P0l|AOc zd+yrQ`+&Qf&DCAi!OysFmV*BCSUF5~tW&Jhmh2zSGgSI%%MT3Ek&Yklq2r|h0001r Ci-Umx literal 0 HcmV?d00001