From 22359be40ed530a7134cf5ebb1735f37dfc4ed72 Mon Sep 17 00:00:00 2001 From: keyhan Date: Thu, 2 Jul 2026 19:35:07 +0330 Subject: [PATCH] fix(platform): apply production hardening from audit plan Close billing, tenancy, migration, build, and CI/CD gaps identified in the audit: wallet/gateway guards, full-UUID namespaces, idempotent migrations with base schema, stateful service stability, safer Dockerfiles/git builds, and platform chart hardening (Redis auth, RollingUpdate, backups, Swagger off). Co-authored-by: Cursor --- .gitea/workflows/build-deploy.yaml | 67 +- .github/workflows/ci.yml | 8 + AUDIT-REPORT.fa.pdf | Bin 0 -> 449710 bytes .../helm/cloudhost-app/templates/_helpers.tpl | 18 +- .../cloudhost-app/templates/_log-shipper.tpl | 2 +- .../templates/db-deployment.yaml | 8 +- .../cloudhost-app/templates/deployment.yaml | 2 +- .../elasticsearch-credentials-secret.yaml | 9 +- .../templates/rabbitmq-deployment.yaml | 24 +- .../templates/redis-deployment.yaml | 29 +- backend/helm/cloudhost-app/values.yaml | 6 + .../migrations/000_base_schema.sql | 1638 +++++++++++++++++ .../migrations/001_service_access_grants.sql | 26 +- .../015_application_product_type.sql | 2 +- .../templates/backend-deployment.yaml | 13 +- .../templates/migrations-job.yaml | 19 +- .../templates/postgres-backup.yaml | 2 + .../templates/postgres-deployment.yaml | 4 + .../templates/redis-deployment.yaml | 17 + .../cloudhost-platform/templates/secret.yaml | 5 + .../values-production.example.yaml | 28 + backend/helm/cloudhost-platform/values.yaml | 45 +- backend/k8s/logging/elasticsearch-stack.yaml | 22 +- backend/migrations/000_base_schema.sql | 1638 +++++++++++++++++ .../migrations/001_service_access_grants.sql | 26 +- .../015_application_product_type.sql | 2 +- backend/scripts/generate-base-schema.mjs | 94 + backend/src/app.module.ts | 1 + .../applications/applications.controller.ts | 23 + .../src/applications/applications.service.ts | 15 +- .../entities/application.entity.ts | 14 +- .../src/billing/billing-wallet.controller.ts | 15 +- backend/src/billing/billing.service.ts | 105 +- backend/src/billing/payment-gateway.util.ts | 49 +- backend/src/build/build-progress.store.ts | 47 + backend/src/build/build.service.ts | 310 +++- backend/src/config/configuration.ts | 15 + .../config/validate-production-config.spec.ts | 12 + .../src/config/validate-production-config.ts | 8 + .../src/deployments/deployments.service.ts | 68 +- .../src/kubernetes/elasticsearch.service.ts | 19 +- .../k8s-client-v1-migration.spec.ts | 8 +- backend/src/kubernetes/k8s-workload.util.ts | 13 +- .../src/kubernetes/kubernetes.service.spec.ts | 4 +- backend/src/kubernetes/kubernetes.service.ts | 367 ++-- backend/src/kubernetes/logs.controller.ts | 3 +- backend/src/main.ts | 33 +- backend/src/users/entities/user.entity.ts | 3 + backend/src/users/users.service.ts | 6 +- .../app/[lang]/dashboard/apps/[id]/page.tsx | 2 +- frontend/src/types/index.ts | 2 + scripts/.gitignore | 2 + scripts/audit-report.fa.html | 317 ++++ scripts/generate-audit-pdf.mjs | 41 + scripts/package.json | 8 + 55 files changed, 4883 insertions(+), 381 deletions(-) create mode 100644 AUDIT-REPORT.fa.pdf create mode 100644 backend/helm/cloudhost-platform/migrations/000_base_schema.sql create mode 100644 backend/migrations/000_base_schema.sql create mode 100644 backend/scripts/generate-base-schema.mjs create mode 100644 scripts/.gitignore create mode 100644 scripts/audit-report.fa.html create mode 100644 scripts/generate-audit-pdf.mjs create mode 100644 scripts/package.json diff --git a/.gitea/workflows/build-deploy.yaml b/.gitea/workflows/build-deploy.yaml index d2441ef..08f85a1 100644 --- a/.gitea/workflows/build-deploy.yaml +++ b/.gitea/workflows/build-deploy.yaml @@ -71,6 +71,63 @@ jobs: ENDSCRIPT chmod +x wait_for_job.sh + - name: Run backend tests (Job) + shell: sh + run: | + JOB_NAME="test-be-$(echo $IMAGE_TAG | tr '.:' '-' | cut -c1-50)" + cat <+uc z&*<~K?|bk4ejjq?oU_l~Yp?ZR|F!lW7l%PhPJx4mlOKy=YJJi9}-V&;4X>RWfbX2l5wQ#U>cbBzsb9S`k;^7zJIn1(p8QeM*4atU)WZ_WEGNp#%`3<)1i&G}Bf`(a#?8$H z`~v;doh| z&eGfii%Z+n%;QgsvZJZBB^H;St%ZjTRDhQsi%ZGU*4oAc%EQkGDDLdw?56EvY7UH* zv-Gkxw|t@~1I%UX;jU%rChP3z;_PJUSP@`-s-ya>PFe#2kER2ckS`gbKx_}e#VTuP}#-F>TPlC zu$Hdxn9b9^!XJcZW3JK$?Sy063^Fjn0z_!6nd~OV_wrJH_Pl8OwQyyz;z1o&MKx&t zt7+1THT~$uqF3a)RK$n3=FVehiDhalMbck1;K*Z#JK0uX2;!deS#anQvRgEyCOpP8 zlpkZ#pWcKa`9U0y>O5UxcUx7`I&bj9*L2Ins-uC;iM>mMRT8=G zU*C${P}(?arwMUR<5Imp8K`lcb}Ij6*?27Y+h#|1b)Q(ze4+kqUNB?h>BW~P z&e~Zbj=r3=WV|(+CD$zUj&V1U$quCmf$rm`_)~V*8SwU*cz26r7k=e zxFfUK)jU6NyV0JaC^R-U;mc^*9t`xI$C~irLO5?oeSQA%>g8fqX5j(X+!Kix4bqEs z*AGo{sOF{?!i-wBuAc5>`36cPkoVVpdZTr?+rYfI7)^XwKqYn-y>qyx0sEMh!n=){{frF_=BupVsqGdc!H=-vP+#XajI{i&_xqM+au{WP zvic}pL&xyx79OADYwY?m`J>H@?tilU`Yg*bgmr<3u0*b1ydwTG#?K)+tK9acaADaa zalvC#mT-9|^??(cu@mi_aPY7D6$ApA+k3mL8@hTtzfuwlgsS4Dr`i1y|S(GF1;=XWqOnvuKCvLc?1aiGj+9iZiZvlP=(LYXBN zTDsF9I9Ni%{~)YXU1i=lZO-`p}7E8ZTQhI$p`T)5|GV zAhQypvVPojOtVQ1a6Z3SlZSO!2wDXXUBB5un;O_EmAiz7SFAvOEbU0W5>*}Sey|75 zfO5VamtJtD#LULegqGR_Zwb4#Cq7A{luO;JMyTg#fHRW!hTRwuc&uX8QE0~%EoeNN zzE~1s9wd8cu4C%GxILI=&*>4|T*GKIi@L#x9{i&GB}_9KQP>xC8b(;b;7Tl6`wHbY z-50LpzTAp0>rYxHr9}~53zY42U*Q<2_j~Y_Z#=yy<)*gEB6pz4EPKDEU}`vvFo@LD z&K?5SY-R)|w~64VY)`r6EQY^W|0yqpN&5~r*xD~Fd9u~)l#+_Ug4VYst0#h zz!B=;Ehy_OjDM6Ku_B6LK2J0)=`#$Ivkr^fg7$yTH+Xej|29OIa1wrpwIp0U|UxB5;tC8c31&~27Beh7&Npt z{5TnHBoM?SsROE*3WaFE7&(! zOT;YNCuCX?+k#LWW-;?e^1VNu5N)Cug{q5BqqPFshe4%t?6u!uL_IL^?a#pEf4gAx z!nV+97U$>=$VAMbX-XR4dM_{!CaoT>Ss6X9SqnYxN&#?G=it&Z!q#SBazgk>?blji z!??7=hen&6f>Ek^E`TqPCLQGif*)fe5xwXifmBQ@f_Of-|2q}ZaFuPSMYMjl$R8X2 zA`!(3f{9_&I9+kNwObK@MsQp1ftHt`ldXR-q6e8W0;axk2lK*6MW<$mf2l4(hySrM z`wTbR1~=FXyqvwQzk%^J2udk8JIpO`G5e>(U#t+=q8M3Syoy`b4F6)!90x}z0qUHr zL2W>-R?53{w|)FRUkCtTkzMRMVETG$4_v|YFrr=m+PE8vdhL1<NbkpEa%oREEkQo6>zYVNEeEx$J zYX^*e?LU|SQyPJmDM2)12!=SQnVX!|U-gqI+aq^801TRt<`FHzTr8=w+}giC5kV;a zk9q8Wu#O(~QpY6?<|Z?N&KKac=E zkr2kPBaD=4zuqkD$6p0lz6$-&j`YAcm`*CZyBSWZj!S!uyFxDs8-~z08>t%cyoPs& z!;{hfWzHF8yXFU^aBZTtss9=aL|QjYn2|pceU}bFl7IxF?e5>lo;y(D5?r&#YY5AD zx@5YGc35%nKN=zN7sg1X%rRiU!M?_r$M-h`X8bqk5wysRqUC5*TgY``4Q~FcDbm4@ zQE_GZ@J)kX%z#DOe(?c;{TD20P#guQLQWLJmaxw^`e+KeB8sT_@~%;lD;S4H+4; zir1@qjxWSAVpJK}WvtIpW%EbaOfHW6(WmpzhW~)#QyjS9e z<{frL_D?QA%2Qeb(nJOQzzUajbiw}r&ZM*ll}p8NZ=Wq$`_Mp)4oq++jKPHS_!Ve* z1uDJ|0274^s<5EPNTjR1QU=Gd<+%+Sg&FbSw86xIG#7vjsYn1vxf)@Nc<3NPJ^dIG z#|j(ogNE)yu{NRThkyvAXYjjV`jqX=Za{*0QT2Ojns*3a_&T@v2_Rdl$)48Kz zo6(CPPDBxI!Uz>lFqsQD*AvX(3a$f=m!9D5(&SL216o@y)6Vc`USV37qL%$53D7zI zZ;CQKALqts`5!HgGT>0kb~074RWuaNH--I|RRzvS$fF-Cp01Psoih!vTi>)%o`_ zFuv8jz0tA3q-Z*#uz+t0hST;xSSv%?;}fCDf{Tu0_wS=WPW^)nSk*7VE%03 z@QXMJB+#~CHs9zVn9cz7WE+{nOCfS)Ik+p1fWe;1lp!f}5i3y1#CDiJ3m_CZECmM2 zazi^s#ET(*#!0vjn0KHdLnv1mF}2_hX2elT{|pc5vG#> zXD&ykIX2uj_Tmn-fRI_J%Z6dye-oY37|i9ylheIj7(4p_2ok?D5Kzmt%B;XHwxHM>441}LShvazoTlhyznO(Xsip91fW@v*(>hN#><_(2p zAUlQl-nqlv+Q|dIK^4Y;NW-3oCI8*-?YICjK757v9If25++TxhU7Aw-pSXd9B+<12 zG?P=@1vtx%z~B8RN|)8ZhS5r{s}X#9$kiB`>#|EBXpLsHm@9MrK!B3|lg|_q0hhJ+ zN|CVLrMt%{S%Zb$J5oNocjzd9;+Q~juAsP7ICqk2A~Nylgd;VbR5bD?@UtUD!_F3^ z42k<=9||Zg0zOPAcchd;=duyOH)Z;7(teDIOxN2$P~ZOtKjc!x2P5hOm@kw1;IP;g z*?&b>?eEfZ6bDz@qI&_@g7xn@M4I$BeTyr85x;L;3Q+pI{}FQ2>jGwQ2jkvs`mfi0 zM-KUih&fA8^eyP`b?E)7VS@Jh47x85miixAmr@lc15=wIcUZUHU{_$B7Dc=k0d%2X z2jIYNJN(zZ2FSEpevRSz7rrb1e>NNle_@OokE;Q|&bT!UCqe+lkh(us$uWpvRKbtl z!Ci8~7p7@|)@aHukC^*_PY3_IFJ>y1V3G9tb8LHNyN)xY{J;4%X9cPh zyv6YlezEG!b%YTt^9}j}EWnFk7e?6g2>{!TtT0KRmzCj7!b z7Q6=!f{ph>u{NN^Xpg}l)8{;oCSY?{lYlW>W$+i6!Ro4Oz@oC!?}^}>8u0x~K%PoS zJJvR^vR_a(Jv;Qy#{e-q0|$UuMVdo21M_DOhm&%`um@o|s^DpFuy@zG z3s?%+puYM!0EkVOCJISBv- zkz<6kKbaT7xW%o4_u}920hKu5i)iG!Yzd`Izs0zDNX7K3+ zBC!S5es0y?yg(bY%mM5mfxVfLguovq zg8)VsJOH`Ih5pSzuOp1dbg+QbZQqY%+6MB&pg6hc6_6mZWgsCXfm z*G28>wedlTu4*sJp)+{QDBJZRPpRj-f8M@veYEE zGSH3wC&K=PMQNv8pQK`HM5o-CSdj${@s_fUU4xzSrd(jA+fCN4ruv)KZ8|=Ug!m}J zoqAagv1_RZZjD>WLdZV_g?~yS0{`D?hnA@aP?B&0Dl>mC{?tH#Qi+x+P#yaBM#t8{ z*2((cI~f^gALuh~KoU?3LDoQwvAFcLJuF>-dqFHNgFlx%`~p~9Pb}S?J>ASL-Jw9` z3n=}VYg>9ipZ%%&aOqh3ctE-SlvZT^Im`ZYRt5yh`*+?gA)f=E0`9R&&!_5{@3|VaBh62?$?ms_3%?kJx=07oe=YU!2nzgH z1EADn{H%<=&h=HM$w-VB%IWwUy%!bV>RIoZeUB&ZR(+l@p`cLC*2N~$J%ksr#~3-r zHzpxrJ@~=X;`<~bf0};x>S;x{U^~m5btF;eZhz^Xi+0`dRCn zdl`I5PduEyB}c)RR*qhUagrXQ>;;XGSC2+esmWPDxarMDoI1u z`_I)x-}_|x?WEE*NZP@SUc-^`bv%u#5rSJ?q+EasvDRv9`}&=!RlW_FouZF3sa|ay zOOUh+pYMTU5jv=o$v`tV(1ku?nZ^5q{-bohZkCX&3O-@y?u~`S?Rxc2$9NvWfH{F> zTZy!U8vIo0oqGO}l6Ou73zK;XuybZK_WeU2v;i;%9_7r8JeB3;qRul_|xAAW~ z7rh)}jRfV^r~22L?y4IvPw`rAua-}TzTf=%xp{5kbh{UNcz8#Dcd>pm)R^Y%bFuwC zqp2sKS(GJdTgrR!pjo0T{$^~)AZ5#io5z<*jdnBiu$j!tF*7ig(d%@Gzv-p>Y1eh& zOVSrU*XIYzuRy3ByvD5WI$*cg^Fp$h$Cvr@tJH_yLV;JiLyLD;mxqUeCC=ZDal8+` zgfw44rJCL?On+Pr>Y)&I31zs+na(&O}H`diQ8!`*<5ue06qte-@T^hkkKhfz56X**9zC#PgCTX_zp6V#F6QVN7Kz|Nf|; z5j@G6Li#Y|OUqQobXiLFVcr+JrxN=YGujrD#s_CU{?|vpD`6GpXH*Y^4%4F^LP)yu zoL20A1YVEF<(CND9nNbM+e6yVLR;HkDAH9d`WOum;aGl$KqLhE3;Vi`{{KCd6!JbCc zbZR69jlyHUDxTg;_7N0&@v-ZgVc~&#_Lp8;Ph+Y>;zn?gRUP9mR(DmKZc#j^Y@)>x zPm8^RfdJk6PA<#yhI@{C>6(UpyMslG3(vdK#hIofiM~@lGJZtp>-MJBZ)6o+s8ON( zoM`EmEdA2aaJyfq_$cJjTwT!TlEcI~yF)tp=VI`To->~Z#h&B`_p7cA2MV&84eYz$ zASR+lQ(qn|F9=-uj~Yt&Q-0_Xp>yTY3mj}BLUD3^(6!fwR{gcgmOCz(LM=8S1ZCe~ z)%D|m=*MVOGdzl5p%(#BaC%iNhjI|D9gOk3xWHF1fx#JqHD@bpxm%TENYO^JGk$q4 zQ}Crt*emJ46lYaazhUp>=-vfSNwp6JL`=fNSohx1*ekTE1PH^#;qA?@mZ?;)I-bJMCVMj_jCRRDjv?G49sr@9&sO>Y7#?dAH@0HXTUy7!1a*q67@kN#N$~v znEy>|Gh_aRO%BI%D+=~y+Dd(3tCFtz%( z>2;L`G$ng<$I{@A9{IFPkC7?|qMldLc)8x#(L8l96-2!^_ZCNeQ?A9mk|@fk?>3eY z>-&**O3%!uwc-Tjrj_0;jFdkV#6_F@(G9L*2zNO3|3ME;5Xuu(9BlDF$T(R9-H+!~ zgGa`ie6N3N>|*PJe;R~(FSQ-tqEC(7(kl5GWv^gFWP;wedYrG8Jc8`@18FvuzVELq zhvm*YP(n1kTt=+?wWty>Lml$FUrry~2e;*qPFs4taC$`C^G&jfqs7IACwb1=r{#1c zrbX(&A3HQ1ev%L~f}*v}*Q_`bY)R38AD~uwsCD(${S*`Xp1GZBY#tiaEq8KK(0dx5m*!5;#+1mT0^@x1`Q>M15QS4Lw(4uA z>`eQ86e>@p`dYGB_>>`#WHs^2E6LPT+(Lc`ql3MLI4P3wshv^q2w@gmLtt920xA z6aA19)>)g`E=ZmiyUlF0y5ohJ+LFRxl+m|j5hGzfw})fhrX6}}t6ba03_;RYh%G^7 zn(o7B!#s>r_9q9%xiMOKY-fcsD$JixiaS!2CQayzA|xI&CQv8%g4adtBxreQ=)2V% z$rbrel(uuK8CXV1J(Gm({YV1?=U6z(hFI;j77O=6$&C$?ZBu)3Cnj|KCWuS=4RfP; z1K%JZi5ZYWO1DirEyd=Gtm*k)Nz0FqJ|(i0(PpW=dPKPnNr+_;;#K~1BKXuM(d5ZW ziMak#wDs+xHs(y>BI1~@iiZY?T7J`y_0kBFN={`dIzVQIjjpFkZrVb>PM_kEOZUrio~dW%GbePuRNaTyZKvf|wd z;7<@Gd*FrwvE;LhXownkLr&U6fsMwj{K;Sy0gvQh7XI@4gLpB+d(R)N)a;HAh;5xY zN*=iLw{SX=5exj{Qa7`a0TI#j>AZLhCt`R;UtR#ivt=t+f)lCIe9!*46jdEQF<##F zq*&VlMcHuu@Hvb{m8GSNt=p1p14EzO(oQ!^xG(wr(xaKG^v@0-yS|N*$N$2THr!AX z*AX789I&XRq)nU*z;>8s0S-Ovz4Q{&A0qkOOEIg8!nou*KJ2|Fv7pzvp46zSvmxX5)m!uTu-Qwl0U2fHI*W7GmlBr~lQs2= zf{3`bXZe zh^gL>dWBk{Ehq$f+R+v5 zJClA$b}{AH-hwqp`Hi^kG#&16cC4FJBgCsVf^SW!oi5o1Dm{ay z##}6Ifrp}KOL_PF7!;TGT-y~ZJgW!QPL8f*b)5B_25XFfRe?6*9i^^~is2jf+E&hN z{>Nq6d}hKykX+9-C3*jLVOLz~Xkwi;o7Ze^)h{APdXEU^u!U99^qZ*)WlE39$`Pk1jfmpp$@_}Och1E!;+2hv$ed%PQV{d_;pHzkCmam$<`c8{(T~!g zjc(QJ6wr7{m`89j!>BT{uuNhV^9$P`)g6sq$FahXEJ!@oLcGw)b4xCMauUgkp}r+< zEXPe7VrgNr9&ZqIoF_)jbdHO% z;GIw3(vja$tk}3c;aR1Bob^3Q{fx1@0IeF9&De`2CJ^Tg{cNZ3#_Z{9ehd55$#2F_ zJ3{Y8=2v>tG=2S|0vh-@y2(ZC*!#`o0bVT;bv3@Iv zvV9QMyQO>1a#~C}zvq*PVR`U*jk)Wig<5^AGHGIdA@67Z_;{!FuSMj{TzV7om$DB? z6E}-l3nbXp9lB0UKLtjoUX!hXF}EMD-qo#qn++>O|8=>$CPcI-VeRzNX!-ji!P8&E zmxt+1XEI&KD2tj}ZL&G-556D5lWE}j(bfE{L-wb# z{8MQH1Vx$MC#l;ll*lzAotCvvPH_vf$VPv(u+ zgzbh828epCAf`|XT}$s3cACHalFHBTFqxAlW6xlMWyq$SUS31+1D!g}hAn+s=i|N6 zJl02bf*GSIb+#t(wx<$oW>hsopLVmy$&A<>%VIQiVP9SNsr@FvpR!!Bp0T;>ywAqg zdsbEHk|;a?*0m{N&kLUHtpzR|Tr4vyCnH7%iuJ;^CRu9@l5ER`$YJ zmUT;ui(b3Ed}^HFuG4K;KT;GtGasF5m~l+@O*9>S(w!dvYk1+%*v;w(N5i*eu}$|p z0XfWim>|bUwZ}($0OBufap%gyh>9u}t}Pwh~Kj!H+trwtNLE{WcoJ!h8=O-f;c zq-}5L>~{LyB;c^-=ez?L^8dFXHwAG8%!cls~5c9b2vpR0Z z)%AW+$oEE?_2H9=Uk|U;Zdo69zTpnQ^>%c*e&S2%?0(C^MzJ%=)xf=PdEhhim|hg!xYXhUV`y9(%RoI`&2ID?riTaeeex7ZFjQbq`*st#N^6Z1!Tvlr;sZ;D zyP>1-gvZlIKD(XIx;q~RtvACFj)XKOnpPxSy<$N_;p+Jx%T#KjonlIxIj+bKJ;etE zc127;rE12^-lQ91xP9GgjV09O_}_ZMXrGuna);=0l5Ocz3|W`RTHo(H{nE%l=EANP zxS$?c^-wChzORIdypcj;!@z1YfcQ{luWWO4b@P3=p6gi0=N4r>Nk_QyLEB~gRP*5R zLEG|Mb04AUheuBPbDWLhdEZnSTtKnR9Y$zVZ>yAcNP1)f9aE+g)Qk&@BOud zlw>@3R0gN3)o1E$orOKx@$a4=9Jypn=g+7;84NQa#t5PkAqF4k-A4;QuEUuabS+lz z$Nk~}SFejnoxEs0gXvAM7C1P1M~4>q0-L}Es8bR3ZcCLHb^W>EfC_(6a=SavM>7?0oSZS(Kif^lJ^ z^>-|)UCg?cEhcYbRq@9JdaIKn7>G4f9Z22bch3rRQ<@)7+%)(0gubzB=F#yxpT@0rEd_sZVy}XdbVlOLob4sIC&b9BRBGF#x?6BF z3OUz3(&9$fmQ0;Lwr4nhRYGk$vDlDA@$5dU#P9uQQ`TJ-H?No30?1rsDQDag{Y7Bz ziG-bLz3R1-DTcciztC$wXkdCyFC`P~*c9*ms1EVf$Tqoh9FS$YY`f@CwG1m}M~9!cy%i0MU!tnQq0a}k z?dHTcw{Ny4ud^|8rD$(`M(ECOoc(5qx)^WzDd5yO|GE6R=fd)ZUc_uZXK8Y`zw5Sa zWvI&K5?=)M(fp&)0KX@G8xpY1#^W-d*-I?4us7GWgKUH}LP_ELa7jk5U>fucDHZ`q zZCeCLx(JUiMaLz!l&*)T>T^CWBFe~@bcP~#n11n%eoSVD`w5Dg$VryO2EkV`Ri8Sm z#Slp-QuXCll_C)-=}vdHg1;z|A*p&AW(|<=OrRp81ikFzwflD$wIk-%7)6H zPd#M2x*8OCMMWD1^>Fh9-Y@w-z4XE(@?T;9w@L*sHy`)^`>m9KXE6-Q@sobH5vLeR z?H4z+H3~&VrG`HG^j?Z9Z{9!MFUOc*=Wc|)lDz8YI)B;TBmQ38z<566<8*X&1mW_5 z7tXJte!QyJZVeIa={lCIpZ(<0yu0pEs7oyH&xM+?G$B$0Y>#N1AuzKj=BV~Ru3aWj6&XOWC%$?1WP z9dk@#kGfpQ6Om~Soz=*?ipRz-t(kjC@5)4&!27yQb+>0fdmKAAlQSIGmi*b7>Mpfp zN}KW3F+S#|K5;Y`I01z&9hXfc{pgk?GSQ?O>Z#y}YNe17dD%235(fIh`pGrFO!MBV z>WfdYJZBzQ$tnVp{MnHL>9l#qZZ2;_AG99QB^87qFu%}7l4xSu8yIoEMOfD6hj?WkII#D}B4V!%w zs;l?**}Bw3lR?_-by#W8mf1i`B9N!aCZ6s``{7RB zlimiyo5AR=q;%iP({YT+XR5AjV4?igbw6KRnJl8=TTpto`ng>E12c6_(Igq`-3@=< z+=s4&`dYK)%H^>BEahXJ`{2)PH5KNpQSJ)FgPBS-(jFCg-|R4Gf0(Ap;3Yl~kz+IC ze)J4cLrhaERpDl7^vaLcJxd7hJ3&&HAe}{@6UO|DmsCzdjx@*Z4P;Y#R?^XHQ(rjFRc{jbivk{KW)`jNg8Z9Ear}q0jU0~*yVq=~@pbPBpRciLz;ShN_ahP>$l6fme8~y|?;W*JAq2HcIoVgcH@bO0o#=1nR zPYmP2r|@4kEC0Qe`4ryLVrr7@?7g(PkVmB^Y%=wt?ehLhtT;F(_-zRbC;S)LNx~et zT95;+PKItf6M}F&lX!>;p^eoDbo60NMwxi9H^==jAV`|55uFf99gGS7)f#eoYs7u; zgeDo~7_26IIQZc21LNXS7yf(MJ!TQi$u2HB*Dvn3%W~gaNqKR9O_rOcU7L(VBH1R8 zy;Y8`!ZJ0+oK2z3Dl-2&{cHgttyqz}^*gcmz<+!jX$r1ZpF5l|W&RLRi>{=jIj9;V z3vOGmRUs0oWc0tbk$I5#bv42EJSPWb&ojTWJn(~|{0N#o^yN)qF@#F3hU#8O8M*p5 zD)y0(x^1v}P;Crj)4*mnW~{vuqYMAC=LxE23gMNihtSX ziiQXizTeJ>r3$%MSstZBUj0J`;l)BD*JvbzP{=-lW#)~1KQVhtmgChg3S*J$LFO zdQyd~GPq9@!_&Fgs6=)OKdp^CeIHiTVnw|o;o*ZvxgZ<%Yk2wJ%SFpAm(4s`=2}JE z<{tuXM;#D45Ew5GKJ?RQ4dq1A?Wi8vFeHyFxycuYS`gOVy6M5`hH?oO3HlNP@sHl5 z1;ygUp(2nEf=1RF6mX%kWa5grP%2swI0le&ODYoNgMdhj6N&`+BOoC@tgG3Ixce~- zZZ`C_@~5s2bz2h-s~*V~IG!>VBAy2h=9|26JRf#@ywWB=sQ+Fg6odCX_JXtrc=5_= z{h&|}r|p^uai$h2_*lu8&O7k@9Z`_Po(}POhxTV?w--3{cv6Xpvabs#>GYX(>B6A8 zK~8ubfG)%5ak9(@NSkQfFgmCKn|=We%Ybsp=7RmNwE)+e$bta3GP-S{n7z&NNea>; z;M^w{2}-4X;YhCwom}fD5DkgvNvM{?g(~o&eNh!ah*baZW<^0vBpzJmSB5-^&H`{4 zzSqu|h1z{Re6N~Hv|1IDa%P=Cl%L=Y?iD`{WfF<~)<<~GFf3m-3so~i24u__LFobO zIXTZAa(YWCn&z;w1S3h>oUG~{6!~-Qd3ii_ zbs0~HM1f2k>On8lLQMM4aqW|xq9NINld9t*D#@lWyu;=1ei#3_JQVc*;(kh#p4=S_79(G2#%l_Yx`CS1}A7_vk|?u{#f$=lxJc7#IP z-cx}lZ$2cC2c^yi2r?qezcE+wpgYX`A#j6==-Q$gkLW;sqV@^-5Eb!4`S~YMEL}** zze-uL_Em< zn?|s{d4X+Q5Nq1hV95w-H;ZAWh%ZV7<0dhhbMfXNhxFUL)xa=)J?{{WU>`gfVGIN)_?8(dY8FDh~#S}|mdA#SZbVaNwz~Jd>x8C2{k?`sy0XVQHlTOQVfUcV9uwT&zaAlt`oOs#(WQD zg~m&1GCh(~dZ7!qXB^p3|AQvQEs9cFydAe^%{`wO3fh!i`EgHB4*buX~M)1zq=0vU?c!gTpM^czl3VE*|8wF?WCMd;J@*rlrx@ zR@P009Nt^GNhNBmj8bp#l*FyONRLRe9JeS<&okFY`cb1Ic8@3y z^@*e>U)?xoG*zWVkn3J7!_)o=0gvwZr<0ocR|%ClnQwZlKqWbbQ^Pc>8qZAfn?KfD zwz#e(J)W^*F~@ra3B#_pyyYlNt!Kd8V0h8ZkumK4 zE!J;=rqADrCBhPi#I4WD1H!SEbWUop?lu%_Wd3F@p5;B!z+5G#{X>#UE1M1T(2b_| zwCDkO3fRBBa7Z_VS)M1KX1Tn!(imwoqKhEDGDNSf4jAbpXHz?m{rSOxURD#EDU(|s zp57G;b>pMA+l$ZBsys~{RgI2bGcsP$!EnpacJ1zqkBE~!C{lXmx3fFEF}26h)CD>` zZuRp&qN4uKj&*0Uq%;TVVeP~XZHv<|J5LAaY^f~~9q=44=+A_JeR>`KBS zsb@${G=35L?1|y?`|!EZskXNA58Jh&59-(3IUd6#-&v$QOErFO9z4f;az6~MKKFnF z9~l425(mRg6~hC9x0WQlB?9TT9%FOwQ#gZQuYIWPu8?*ji?*kaWgw|^fy64BS#GrV z`l}W9U$^%ja|<4m#+QZFz!$R(*9A2w^4zoC0JD_ z{7y~eL0D6a3Ta~@P2}ES=y2t#_N5hx2>)y2Z7tH1%N)bCv@qPthtv(C{^I#sGF>EL zCc)EU{;|4YF@_B^`_TkB-%8u%-8mUBW?zm%_hax?z#pnUgKXam8OR8)?!e~jePivS z`qv3&)_1mH>LZ+A)>YnWs!Jz~7m?UuX;vXE{Nb{38GNUnV*smOY)#Bi+M$y3@%zs-gAK+gkN z)o$e?!8oCP6-7yJyN%)5DgUdM>vQkL+k@G~fa^x*z+d}^%{N_V`@aHe#g207&+S%r zX4uZ1y-O*pzuK2|X1eZwJTUSTI9Qs2iLK7CaT{IV_=7XLOWi(`qox>0h#~xjaAy{( zNTlbBEKK*|Jcbr-^~L?@Djc`*%<1?|dME9oql;(KAEZcSa8dMD@LFhg-LIUV$hCSXk=&N# z&mH5~T$z&ooe1NEM%!h4x*#Y{J2hUZ**=2(QaIkpEmRw?+gihurb{CtSJKB0TEA0R zIR9QYG0dC<$GQu*ZLs2@t?S;PkNaFM$&Sx@ zd(f)1nw>vrTd5|*>&GWRcuez1C)L;A6Gr|m79?fAir(j2YY4S81zf$P%)fe>5p#7Z zc|1>bqffi=X)0HTt=GQvT(|y{mqC32ar$LR?JKO%h7cCX9nsMA{o1Xkv^~^?TSdEO zG?|hbH8A5WySJk1lU$xT(XE6^Qt)d3Tz5cWZKY71(^cyGLT{*U}vm=zH~j@#gaG_Ok5m zWHa#h+2Y-D*`3?fMP25H+oNB1yVSRr`I)yz%eyVNnSmGQ`BHZ+ff6@qcUR7L``a;r z;>(JG7iJaI{#Rpxew8J9c@iRef_gcPxys9D`7Po%7EzZMU1OQIVf;zrr`?&&{Ed%BA;j+Jxg=R`s*62Zp)Nwu2Rc{f_&K3*xA*WvGA z1DV`hr3AE)cR%*98oF+`sWp9U8&)tSkA$V3)73|+TyiZ(WcnBUCUjod;4n?;+q7SZ zmpauPJ6w%%?01*(A^Do&H8a~4 zb78PgJ^G%;C`|x^Zv9T?3b&mz05gq8@P6+Ir|i7Z;05F%SzKwgHMV7B&n2y#buO8C z_p|WIN6^jJB_i}1zh$EKr+CP0oyd;9#>MqVEne&mM{>7w=n<~Dh<`LP*mWg6GFUz~ zex!wp({WXTWBM?6WtStNWQk&WCg>!Tp;w5u6Jk!+5Pxj?XeTLGYNZ*1G%aASP2ZGEx1v2AT^TbtzOU-#wSx-a)(x~He-oI2BWPFL6YP4}2N={~)T zMq9Wdl~szmPPf0UE{-ntkr_4kj^#vb9W#aJ7)GzaGckp5P$@G#WEfRKW5x;cfP1ib}(Fr z?rKRIQHH+JyZ5x|5BLBG9HCVgWnYg`DIr!L|g04vg2Uzs&;XteCxbxRLr zQWl7AR#X%NHKB5=1#<3z_OqC`%+Tyd^P%iHOr@$`T{CgFvi6SLYH;l^)3{1JLI;tvG^q;)EjBIoRn#A=Id_q-P&i6!@1Y|s`wc?E zha`KAO$R^V6~$Lpi5Gs7L^lHls3 z;)QQ8V>f2?>EJXYbjRqV*{bEv&_6`NIT@2|IGnt-e$e17wN=JZCFEcz1dw{DExfovAlF*oAv!;Kqyt!MkVGI!1{rcM4&R&On@{S8CE)%|!6VPr9 zYhZDxDJ4Y{6nZXV>A~I?jG1{vG#H9UFVNchTXNs<1q0j+a zUgkvVd{9zfGVqdGK@#^Fv{M4hBV<}&0@TDBtKy~Ge^|)RceZUt$Z$Yu@ol<9yLN2W zEXEURLufdlBc~qKx`u`_;D_lo>aqJ+?V5Ry>DO~y?V;u4dp%L-jHA3^{iZ)l%1ZSX zfp19dwnISCuj#D^WIlPKtrSd_0`T+;A^}TOuBP)aM~=fLjz34T-hY>d!!PjHLWG_g zoXJBNk5Y4rEG4cOp2Zym1v=)9wvFK&wnGApx8E8PYjV$ze})SGI`l*38>-igjsk=7k8Ru(v!sU+n>AcboVI>`8cD&{ilPE&$Sm&h{>}>qlj?%|+R1OUz)QKPX?dU})4BN)mAsEQAidK(ZfoLROB~Bj}-33OmH-6j` zY+5hdU;73$)z-}sb<6;_!c!@7q${7Kg&lah8blb{bp)1V;|}Ob$)biNO1x7~xS zOwAD@ww)B)u!j-(4ow-B0l4RK=)ZBRU7owx^Y&oO?D{m3f|A(^TK{qM^cM5=PcyK1 z<5|+-Q_vR(N_`?ots8~Bh2{v=;0XAtUddxLX1(H(Y$LdE8Jd1uvg@{JH~1v(neQNH zd#V}jXo3_iw~bv2i6yX7cMz5G*)Lkkx~i8O+b7p^l8@HMe9eQ#U5J{h8h1&NTj04D^t76!8ukvrU>47ee~f>W&1 z*WQ&P);+E?eMu05{jDlnQe=vSrdWqZXpzkl(iTF*lP~CTLpD482cQS@5-Wos1g0_` zK>IQZ8twu?6yyg&6l4XIGF265@T8X9am{+?--70#RAUGM2oJ7nLLvl`%WKt1rl^!qf4`hL^mir zpj4DtWmG!6FCd>JgdD)uzK-mI2)HO*=saQ(s`3W|@W~ya1zk|O$X;L$z6~_=y$l{= zmADCJU&LzsO`7Zf__db9ZBqjNjQoV37ukI${Tt$R}ssv2|DYhgpJC7EwbIdD{ zlwo~jnhOOxQr+9@3h$5fvO%<@3rjBn?wEn_N?r(=@qY6!^^v1-$2E~t+$mVuI=GC`X*W~ zGd$d|wagOLb6j1E*TnPiwEi^OKN=ylz~NDHGVY<%LG7EycB}~beO^$8&+}J3=Q{Z$($Y3l!WWHe!(?-$4PWaz`7ioDe zBXHG8fZsYB&I2Z%5ha3vo*fgd7>RC=0=iu+K3Ra+qb`w^m3smEt!RfqF)}esuw^E^2bfL?d&8 zchO-4Oml9GQS)eQwc;5$n0?I)wqnnYVEc{CfW7Bh^aaTfEduS+_HCOW5beD;54`xo zSUw^jGUhH}ykd|*omP;vL6Iv&F_tJ!)Qj}?T=kyln_EGoS?b?r zO=6HI*q*%j28m!W()S59ZLGXWJ{&ku8*Z}0s43qe^=_aab*V8#fYJX6Pto0&N6>TK zN*Y$as$;(W#bWaEDgrrY*Nu*uN8@2J+!~S>-jYTau~q-repOCX*g({6v!-#xW$c-c zSroi`#a-!j$@3@N-IQhGXKBQ=Mf@9O7O3yuAZ}RFBM02FGULE^7u54+NvYcSWE2Jf zHuJ+4+ozk31o>Njtf`g~IF92$0MFd5BB?gM$Y=W=Cvh3TZ&BobQsZ4_ep=BngbPAc z6H5r>{u5SVtl&3BpaP{O1zr-!*=H&8N$}3uQw*vOZ!WAaj4rH84UWDqS3G8FHyVDQ zIC-R;2N%e4Y~Sl{gg6Cq2*OjKCAM+Pmyc9kfG13Z0=+!TA&nya!}bK)LwXjQz?Rz! zh7^VTvd|RKx!nlykWzwkE(8iHYP;Ns1~;?6(h4o=kIqq*6@w1mko%*CUVYfFzGtn0 zG2EdPRCtV#mO9;A8(JJVJ2FsJluc5bA52oJ6@PdWw#r1c6iZS6X$RJfwb_(r& zFiP8K?-h1j&T!X-^$d$;uo*6Z+Md`7hI2r?268+!<9`n?yK0HWf8XC|64e$vM&Q3y z2*U*RVZ^z8xKR55Ein}2l<8pDqK9Nz@(32QrdG8z$lE}6@yt0?YnU@oYnVMGYnXYQ zVvj+BcU}U?r?c7nQS)dKWNx?w%!%Fxn>f#p2oN!r5Ax?$L{#eyVk$YxX z15>$@o!a|6SAK!c_H-#}N>5l0Wmu(xVY*O^P~?~sD!JmBe8&mTMz@M5KY8gzVJUwj zAK9Aa3fI!_oC9_0yRu^63!m^z4=I6(+|Yu(pJa$R?Sbq~MMyQZDNiq3RHlBJGzn5*j|&hP|3sheqJ6D`xshxK>b8`Lqa1YK z`95F@ea!p6)A_WS%fMc#pV=)RrVM)_mxzL(J#25gYd2qD<#g^CrYh%UH2aHUaixOI zMGQRVuQ&PhzQ>pn-1w%+yn+G@7ZPdBO!rf@!l-RQ3d35&Ob;^{~?dp%(`ycsZsva8_Fos(hqUNf|K%k&^f$rzcV& zu0PYAY~RQDa94JIb8?#S71h>jRKUb?Pv6fGQ)x08hce3&N)D zp*<1}{%u`NheWIR4(|#*=O1`*Ry5MHshSn|z8|r|;t-jqCF|S)}=X-M!>U-4gf4eZ7|n zbzZb~=lw$*@Mp?la#gBq-dF?&iEs$C?DAumyO?F9@Vc2sV9w+vxGsHGe}*7M$k5lv z8Fk9j_(dqq9GCE`$a9ZhDxT#Z^=KPO;|yednzh>XMNZe^+EP+T45+5ZTX_!x^c?@I zOtxAyA-|7Q%L%Bi0J?xtbXmyyiO3QX$mZSrnItMRF0szh|3$U9S0#+5(sOs zQrWPW$IO!lcBh@?1@kD3|C&b-6snTlVO^f*FhWSoEyK3KD%J1AW?z*x>-1feE#?6d zUX^u!rH8_miB{qW%W9_xW8qY5zeLl^m0A@PJS3kCwmbJ&qL%hrr5KYnG@xnBaSZb) zG#SEP0)PdPY?E;a<{qe~@mio(;SFL2;#Iz&byHlbUGSO&xmS|89EIh+&S3$vCTy|X zlr}hHt%cV)1jR*yf;YKW(3(hm$~L8qHwjj0sv)y1lhH_-jv>Ahg)Z|mB{_~q7_M@Y z)l&Xu@eMk|$gze-3;SuCzW~oT2p(|rIS_(S+dCWkh_A>ue8~W;A@cPgnaXMOSgUq$ zv25aaHGP2J%Q(S0(0_oRpF1mcTK;Y^{v!P1y(B}87F8ZF+NP|TBwh``W55iNqFR; z=Mh?bEI(Dkm6&)xTPm_6Bfrr1gOY6^noR+XX}?LoY0vB^oo9g@?W^?C?!w-Jb83ne z6PlxuoZH4iW^{EjQMfXLVorlk&ScsnRbBLwz9Md2Ta~S)u{hDjQpS5}x^(0!)jMEr zy0l@J+;dGWVdqJ0;}z$XrLGxJ--xw=N^oJf*fZ*AVKY*rZ|4|PVV`DVQv}}+th1_& zmQwq+0SMMXZY~GI?;lOutB%*HgCz-f%~}L$l0Mkl)Y9I?$zXzymV-O2B4~F`V6X+ z`af0qk3eZE?xGj9dHC4BRED|qS$pQ1#a0@)dQ99tiVz%nwst7*m-*oQFI)$c%lZ_& zaW|8vE35@gozw(vo#tqaV(mqE2U}2)Zescr4rBTxo`XaB?0+F4L2)gyL2+db?O5<3 zIN)}|Z#&VPtuZ*Uo?>qXdm%U^jg9PJ4vIN_8+3=W0CiQc4bmBFGuREmK}29x7X>*+ z9;^+`cY|^0?;TK$?)zg1IQ=L-wpszy=Cw^I0r4#Y|3H zi!H25y}T{{&d<Fh)E$uA-n|Mz0>)LhtrqcKDGOk3-xVzi+L#GUs<5QLYiHXrG%f_s(Jkx{pA7S9 zGt`hdSu>p+Sw`}4ckSxpGrZK8+Xm_Ll#+dfRNruI@PF^Y;l%Y(sY5v%N`9T7nta9* zfw?jfffLZnFok4x^Ft3;DcTWZ5@kX8)gD4OJaXT@4#~{DiwbpobZ3txLgmaUIOlzh zIK82+<)!DwyD3 zLVfFf^J3y2HBhLBi!QUDy0{x0#YOB-`z9Rwh7Pv#2UAvIqW*Ubw0}1L7QQ_;3)GxV zcJa1|VayN-fZ`D~3^9BF7-L@u-~ZWoy0B&6k9Sbb9M1@?@I2exdKXE~dcNddH~xFp z81`0!u*V*;XSn6yCEsbK2VZk-^l+GZVQo3Cqu}OrzFQq_ROG~!Bb!zby7tYU}%p0p~2AKqfxDAfERHsnBxmRxXAbqXeQsAZy-jl1vzWDau4d{Sca*>G^^l3sr1s+DF9qWWSEc!IDH->mPFG*388to`~ z9HxHj%_HL@gDjfP6*me^XaXuE%-o`?g?Sk|E{}bmR)r60tCd``XTcT`97<@L20&Fu zGzm@+7#9w}xdj1s>jupvpwK|I&-#`!ke_o?jrm^Iy@oF8H}oG zNBsrCh_@u8>?8TtY;L+aZLb{Luq77X9u{b8&dj+e*+jFdNiei%gBAg0yDQ3coop-H zoM67DtWUA7WmnW(U&tLDP2*PRS{ehV)NHXZwv&|trv#*ii)Z;?}vyWIz99*#)_ zIgQ8iI1IPxq=g^09fGWB8)ieE+265_#`xR(Wo#VNG6^Z9axM`6@?v|8|-deKLWk?Q(>d-J$ z+aGS!re&xlkIMDFY8EigMoZj|al>1g5Kd34CHHfqCiN@)WLp_l{cTwoHD+fn$elE#=1sm! zpucxN+q79TqB|Tnf&}B$2eh3^Ox#cxx!Fi6ggV{_xp}rB z;{De?G1x(>6aFt8hDFf{`RUkI-Ev4F;t}F$OdDg(9FX>BW%x!xiHF03^&MHd?m0$X z96)MVaBoS3$i(pSao{Rj>uXNA;u^^suONzVP0TdJU@Gslt0Zv3U%B9%43b$_@J&l9 z-si|i_g1BsJk}NM5Gg_+PwDk!BkxkG-TCVq8o)iQ{i}Y|E@c&h<{B)7VDkL zG{-Z5p@l6#>zp11om6cAnxHiT_wE=58&IG*yX_2)m?gj<9L8$59 zaTE5uF;b~(%iC-*3Z7h7lZ3L@MxV(NT$=-LmGJS<6wvD2B<_Ftvye) zHklDAX6hVRky5W^h}wA!QsQMT840`F#2;;PYhu*Q=4O0@(ms<(=Wy83-jv7lV&8Ar z(leGT5({Ru0O_!>9bMl7Q;Q*EM73@=vK zv$rBaO~z%R8?Bxt3)!vr)sWdu@Hr)&Gh6>ezl-O2OW6~V^YR>_@s-K#f17!Sr(7vu ztD@JHoiio36@#GnWuKF*IkUfRU>{gtC)#30TC~PO#dNC=_;sS(E(R`MK<^3|G+n5F zW)|%WlFHC}5w4pA`B_D$WQ#~)n6@X$G`Muo7`8SZ!$trL#xG(L?Gij_<&ZH@2r`%H z?d#DLYoG1wh3%k|76&0=#Bx4Qp25xvSTG~Phqe9$5aO8z`B=q3?$xkhN>8uXoyS1l z6|iYG)VmMM0{@C+{nFzz9aC733@2pwd3yMH#cTRlU!WA-0(#aMNH-fN@~a=QHh(Mh zZBKkAr~Zw#=9@%4i@mIEmAi)XnWADfo1(GlWzi+rS>aJ8lGv{`NpD(k#xPU|iq$lplzsOym6qE?#GG=-ORs*f2*a6srfZ z`Ts>QWE`I&a$iY;*VmJes*clWp2ei_JF!qQR>j=A+nxzGBgE}&8XV+!9}93Z+NpB- z_8w;Y$}wZW65TYWw|}pk2O<(&eKN3%2ws>JHueXhdb(@T}m;VKn=m7mN>hZwJOk{4^y?K=@z$|FP?i7k0O z-`dA(G2K5?u1?H_!7*QIr9GmSQDPA@tz=i`Y+-WD*GWNedfZGiX&dYv>K|dZxR-ZI z#^1rDaVB~24L8WBG>5DH%|5Jz{^hy*WN&VJWL<-Fra=E{GVX5>Z_d>E)&4cQx0Vz% zvYxon=eI2&DD5nF`LOezI#y>@ya2aX;ni(t=3tc+S{YciNhq5uuv1P-(*@@!C%L21 zhENfrz)LG8NX^?-Y3{inP4*kMHuKRmiC7(~ixx>w8m?!cYHu|O()v%;{GZIg@|()k z%~(Lv4rLE5A_#m0xFHAKNpSfK?by{*Ebop6kiIsCsJHrZ?>q^)RNsH#(25Eo7`gE< zUJz!mMQytFp)ZyamzN)f&q|sK%%rwxEa}O(MRgt$3{@X5hml*xn3UuK zJ=kLk^m~Zamhnf&eeV4Z!DW{J4F3>fACqnCzgB9w4sLx01W{_kZin%fCT|A*oO?)8 z^TS}!xC1woh|ctz!76)H@-Izgt4h4KKAD_8fc_)9$}n}7g;Gq+TD!;MNWt?BkR*Hq zBwo`64a74t^|K3Lb-~{NiGQ8Yr(Kb)m|0JAGTWHDZ``v~ zm&rqSn0W!r^#H$+k5fUluvbyA5AGYLbGiEh)TcL; ze!RpH2~GP5OKrcr!4(4m6Yq4ZxquD0z7LAljzQ!4y8KRzxZQL5255k#~P1L&!${W)ATLF%lrOG?B z>Cb*M-==pnPmBWdlc?BB-=9jv*WC{SY&iqPDBV(F$3&kbm;{ib-o?tqBa)RO01>Py z$?5l#^T!!QzHv{pv`w*P8#`k?aLAD<(T?ZrHbw<=Y(hRJ1q!jMZnEn^eFl;yM|J|v zJfGdli@Vm>Iybq6Q9X55`0I2vvjm|=I_0Try%9jiQ@R? zF7^r$k-8b%)?W?Awt6hP##_1Et@7Al+IPVA!M+{$B82SUlS*_|PU zzt>q5-1IB`tKenBt`V@=QfcWmP z_V|&~mp`nf$|VR@IdrE=#$&rVD6Lq?`DbJ4(Pr&hOmKW1x>hx;c}@?ft!=BY$JFdz zpQdPkvLxQw);@KPdnu{7z9b<~w0vj8QBzZW<24XQv=pE>T=5kP^$#O~%blt7@JTag zRd2Jxc$PM%aQTSWWl0J>@+CY5$+YyGGG6cAL5@ZiJ^GMlh?VjlCQJd zs|L3V7t_0v$W&R4$$U||N-I(QD3cT#KQ+A4h#*sMd`|fTy5WB?_l~irHSmOK3hb0o zit0Beue2B1%V!bl$t$SMvW0Er?KSnS2c)$y8Eo$z#5zgMI9vqubdEbn9{rIeMt26*XD6WczLNq|$AAvgZ~-Z_pzt?v284t_Q3DoaGU~W}N{tn)qVS zVP!`?EPt4d{1LHsqyw-5{;a2z(aOLE*EUjaw(h}JLSMO$oUxov!$MbPihd`@z>W>x zFMQm2JXWVcEw5xr;9Yk@N3j(!h+|8js*eZ!S!r8dqF`O$3{&YMQB^|w z+*C)~sBK%P=sl|gq~+8RO@oxD`%_76Q=x^S4Wymu2SQqDAU3H2ZK5%o&AaE{S}TJZ zVAP~G3=M3LkFcMbU0u;Jj~^NO_w)}p@#`@KgzRJZ;G)xDC3kOXY#icZ2lUvvq;{Fp zCLt?g?)t|=$?>Q4n7PV^cSC_Pj0-x9TvV?rJ2vh41rKMWDiw&Pz?AQ$YBE|;|Lc%; zO~in)Ezwi1SJ$J)V#lGdRkexkO&x>8u-l_;7fmWJKew05xW;7&Mbp|i-ZH~DQXBK1 zeIJ{jOh}Z-WFy^JIudN?4NJ+Y5?^+zq0E?Sxw2-6+u#{&c1}q!tBX7|TPt&lhfWt2 z?+G4^?HZ4la}iYY8&gQguM)z8>oZW&>g|x)_0y`__0XU%yRr$v93ar~bMODzvO?*1 zn7+-qOV>m^+(FdEmT)F<7c8>YNiSip6U#e?1$Y`zJBzTPdM>>*c(Ch;E z$bI*o^9y*%^&MJyoSN#KIoiz)qKI(~(d$>RZWBr$^KsZ>1ff?`>RdX-yMFtUUx~Gl zsCz%8=-)Qpo2|*;rCnBM3{8(q=z$Z=V)9ULoL2BERXiqXTS5K^08sa)FF;P(3Jb6F zh#k@Yi7hJgzCwIOtK_Vnu`qpx)ZLibzHW{D{B&BA?s>6ka-JjaA_sFb~}0* z-qtr?g%aTR8?TEiFL%erQssGQCx9&J^XYH&&!?3hf(EVBytb;rr#LXGnfERt8sr^9 z`AcB9YV9=7jIMixeXr|uyPP{Yv7(4SA>9-E*Rc3Y{ zv;nw%Ks)-&$%U^0BT`dRK$EFakWmgG z4oLej(x=*>b4@|W7kUTJhNbln_+#8|izm+ZejDNoL2*AINUC{&n8gfUaB2DVxWm+zNWrK-q zTQ@)?=1qtiy|w#Ab*z|Ex_ur%7ranyuh+Zhl<3RZ23C7w+VVaru@7&+((^PrF7rKOc=PjP0~IsK!JjG7kdiRh zwFVr2-QYI3g!@M!z5Y@`9Qq-|{T-VnsG~{%ickDPi_jpejFq4VQ81WMw1&)wpLI;<7 z74J05QAre>U3e+mO>FYM>MTMeTil3%E3R_t+#?W!Dbs6GJTC_VnZ)eKwW^b9fuUkn z?0VyXDqMCMZFrYsc0Y#JOLX~jVq-jDrkdb=YC3sw%I9Q`?1@qM%fg`e`U}Xa!D+dm!Fipm|6qZB*U7_XvZ^_p)z22KZU zvnRR#BB8xRCt0WMnTdWuX-#TGLQ{dS_l*U%xv~;p=Ezf@#w3BdUz-W@ac3mX@MI(c z6ibtb#*%rn<-gEY%{@cv{N##1qzHL34$q7EG7jlVm5g8iW+dtN#Jn}o8!EqdIp`l8 zMpxta+)}R08=buj-*;wTS?9k*4#dx8eYAh~@w_Pq!tSz{*6+>`Iwz~Rvk@3td2x;= zoLDD$XLz=@#$bB0l1OCI6~o9acySUL+*#>E2gd4}6ag|W3Oy<8M!d$V$*0v5)nsE1 zIc|M@rUF_ZPCuFuTi4P)le`HiL{NowEmH{G% zp9#E2IQ5;{EJ4#7vuyE$fXhgx$v2iu^1HDx{mS$W9Yp7*oL7$i)tbLXpU%^+hTO+^ zolOIIqqd1VsgF3k)35QpI7OS`kTS1!v~D zK8`(T-8QANn$m22$-q}ttN>snHGv9bee|VXBQcA#yxFk`MR{$@^r>n`=#@*oBfO{tBCd!w)wrIQzoRMDj8rf;$ZISR>0exkOoCW@MC zp6jFZuKj@Z3I^z@lsWQBTzW8Wx-_;t!~ptNJC~xP<&#Es`0(w@S6*Jhsq;B+D=3s1 zE!R*td-gKv73D_2o6QPG0kDz6Ni?fLi7hXEE|ZLdLR76n-HE**Rzkk9Q5~BjS}s)c zzFVt{gGC-g1GAIF(Ov>$w7=>0@P}?84|0PxyJ>@itNke4 zG>6?0S{7T^x|tT0;V6jGXF>&~Vv<50t4K@0IFdIk2CEB|8t&Mod<6xx#2ql#juwok ztAhCeSEhM1Qjvn(lFv;e=N1vxzN9C6_Gpj$eh39%qP49aJLT8YQsq0 z_S)u-A~3Fu;=`<(i}2(@33u~`kHd6qpxPip#8|dr*d#d>-YRS%)I!H+3{^Aj)WPF~ zsQ&b?j4pME>H6JEfj*!B#AimVQ%m!+#=ddo`L)tpw^&^eY-D7U;M8j_6zGm-vfDLy z*vv3nH4ou`wq=+rQj8CsJ=ucPTJqui(STP9xLV~K`p(yS>^#LK>1}sACS(89(mVJ> zJhgHB8{bl~1Qxyk)xq!>#vtzfxApa|7t+>umWZk8IJ9F#mxo<(-h8e(xo=_ zssw;cTA&e*3kF9Czt#;5#D05xjK(fIC9lVjkv$*<77vA=#PRfs^gc);a`ntq7!u5u z1unqiSNtn6gyMOOZrs*-++So;bkqJ13v)h$))9J)My#VWJoxyJd<3e>_ZaP*9T&){ z_^~%Qt|9avwivXm_+f%e#g-qd*30eeli%{qZvCw4FuyZB4D$-@d!bHh3r^m?lWl^G z(gC2roNu72FlE0I-`XtS{bDd1)anBy<_PzapEj!sOh-r+FB)2v3Al>2_C)k7)-i@O zJN$fOq}x>Qj3E>myRx18GQA3jd5NqiVz})zP3M5Jj(1KbE*%?GsZ1{=Xf9`AP);st z@Y)NFDUxrf@mlm5AI#*YWum*^qn@R&j&-@9LIAj2NEI?)nI%Mm14a8WFjhoOxQ9pg zc#rynfsGaz*yjV8tjh@+vOlv^9B;x>+-{sI??d7~&JHQH$}Jb4i7*Xg$huamxck#y z<}{vAm+)-cc#5%Z;~sYwh&DK?4d{JQx`i5z0MgHcj)ITLN(&1@`yspmOAnlo0(yL) zX-lJ@sYSErMWa7?#+|QX8(%Y3K(uQTi65$|jfSqw&&=I?$yCeT&XzoMiRM8fc&xVM zWJ!fl#8Q1;_m_XOM@W_#9Jf9bUl!;7nB|S9#F4ou3DE^trP7_MM@lYQt+!yXWZ7E| z`hHSRs`6jcw<*FgX_1ny;cu?%rEB@A%6I;jAON9RAWE=;)%kwK!J0 zr&^@}5B|c~i5UBE#v^fCQ@ml8Ownj7KKuq}bNbfHKQcUtxH0tv#xzp+Awlznjj|Yn z9vMmUxeim^VOMvvo6(ny*E8NAT&eX(^UQ=3)h_3hZ8gG-;9L#Wnl<)ce%dQ~c$nt9 zA~oPG4L5WvVNTlVnZ70a!w;lhi;Fco{2JoJvE(xQLG&(f4w%y{@|b%pM2Q|bJXJ$l zI;nxj zqpAQk(_5#zY@|D`zo6Y-FlUi!2nYYtTKdq<`pD&+E)}*~dT0t?+WV9iK>%CtGmtP+ zZhZEM?jG}}rw-QHh|}$hNI6a{QFV zXzWXToG0TskZC$T9rMbs2FaeZt9QkM)2bx1KbGU>v2`u(8p!;vCgtlM~8|YzHHw_K(C-SODSYYvYo9KR;IlhRrK!+Bh@45AQN8o{m}|R8 zXc3y-KO=+)Xrz|ph{OY_7Uz$RA6sy;LUOa=h3Ssp>>1uUo8m-E<)D=Sf1&#K>NJ`g zId(&IxJ7+lftO?rndSBrQPQH6=rAfanO4$zr>Zc+p04-zx2& ztB~Riva>!GtI13|U4$>sOVxE;4r`Br8sIuNGpee!{fO?h4;Cpi`j#L6L;m!TTZo9t z@E>{_;RpF8enKlQKNjx+-X%<$2Xg!eCYMuaYXG2QeqH=!!z65@y$znNCU{ja0N1I& z>;68a?{C$wo;A>|%dz_v!sjlDoJ`BsHby(RwXWAV*UUH4Nb66JL-?AQ&nGVGZ32x_ zNBbvt3nrx<&vWTctzrgAGCunOClYTZw5PLB4)0gj-3#FcqF*IeW7EIX|IKrt3(`vk zeQtujPS_G6Iwf|_v}eG-9(p)OGXiAKxFR+PN{xV^kGquy#I25@z3GWwxA{*?w=)_< zCD?;!a;e)Xz#Zhjq?ebJ|V5oK$H=>Fd-f2^$kzruWM9L)dY5ToxSgmkq5 z`mKomLcO9hkfceZG%A1o{ITOY5rboAnLRezBc~pg+2@uB@E19w|1Qgj0|+UthtB0W z?$n{aDs*!#xcWjPmo_}Osf=kiz0R!ptI#V?O(AxT;nT0vP#g@GAzL6`PGKt{d%dmr z^&7v?I6S2vq;@rK*4tcM+biGN_ben|+f)27P@AN*$=-cW zWyPSclmR_A6_^RBqoA!7WRnyG>Y>oiN(P28Kn}l@TT{A;tt|5{EK*)lt&_fl=YnZ2 zlU^VKT^MA1%Av229_@x^&YtdKUk%IVNlcLpipiMG+$!7MY@;6EW$ z_fNihlNS*oU6;1~N#~sy?NVZXN&LxJDkTVwjEk(793!8*NU%~FT z29RC(Dl+yHX%L)dK5139*%=3gz0N%PR$M#gVm($jxw2kSZXt+6aY?JXlq635$c=*| z4RTpfm?bl7OulUqN3MfkPoa6>&c&W7!`E1c7`y{)nOWyDy&dhUl%vj7Vzd05f(_0| zPmD<$Pk0O1;xVoc;ba;uLfM+)-t0Q8bDldCCR>FaYOI9?iYQv8&l*%Aj$T`!5$~%E zG2p`>Cf?Y;Xy8h6$~VQToG%BVc2`uHo#~GLj@tlZ6$3!({Y00vQ2nR>Z;s>4I?4WHK=)ww*V&C${ZmVmq1GwvCBxCvR-qwr!q# z=Ul9N*ZFZ)|LNLYy?a$x_3o{wp8w}`um8oiC^C}lKyY}ZL6K*2>qqm&dwJK#`y4yL z=9kL@QC4Qh>*;$n2K4ONk}1Z~+)BpD!jkRR$2YT)Z5Q{~&Dv8}QGNAfvD()A*;gP(?Grbvv2L3^YQOqI_2R|XZkQbnY``)QZVosHj>&o#n=BJFW1 z8Bj}?ki7|HidHD!Lc;fP-9u$49EeaMPIf1;Ue`(;5mu%lFKRm@nXGO$R=ISV%0|V* zz<_7~WHmhaoTo>*B$g=T#^i{s6POI6c46| zx@JcF(+mhcI0RndAeLE{3;%%hA(*c$q`&J3Qm)^FAWw_L(TTJTLnYoY>ti?0^IeNGQ^&XYIl?$!qYWKlBzmzM5YTXNSXdFAq-8c0#L5yz`$-y08QZZ z0)$wsPK8rMSN`w75%11VrYJ}$BwN#9Or|~|IjFava-=D`ygg@3$UKxke#6KgN*_)n z(td~!h9o=Gno6PC_&Mg;L82Xq+|I?5H7$-U8KaPh1bg7mW>`er9% z$Fy*Ivx-1j&U8-|r}toDz2B6iGKh;a-#ep-bRcrTmciSZH}#micY_2#QKHt+8BPE4w4<6hR5r98-Si*)fsIBKSPXT#tR zpf6fkqQBw=+(sSX5Bz&OBEi$N^5K^dypHEPAOg0ZTq7J2Bn$+VZ5;$x&=V+ZC+x^_5P`GEc@R6ooKj zQl`X6T1-6}@{IIZeX(o?x}5Rwi*jF9R&`aZ>Yn$%cZ(9c^2?tnUhE3f>?Izcw-_NE z@%ucv$l(8##a~9`idg6xB1v}Qt<~3+{W~oS+D_Uu3PH#X(JJK{>YMfrzw7G#4caB( z>#{g2+P+A0<{L;ufj+t{zuH|x#&r?fNC=~9-(tw@M>5~vb!j|7WGP zy+lD^(*s3XA%97;Dv!oujOoR#`Cp_nPr9sacuf-=Og+Lkz*-tF(TFdj*_=fL@^N^Rz z`?SvCwKSi~YJ4`C91YyKvoj*errNfg;nq)bByz~oL;IB5`z5sF2dM#OU*P-d3Y(s>ZiFPP zaj?csX%HHwi3;^CZhxWQNouE`;m(f!=$L2I?jESvq`_6)^xT>wcJ{$b3y zULZ8BhL{Eg!mwAO&+hnoil73if_`G+uHsLtj~1;b- zuyhZ#l$Zj=?BFvi#+)goaK^`bGpO!!pj1?4BTUlDCDy$q?y$xRA}MIX#+!@S3*I}D z4bed!Gtq2(Vi8)T$h3}e>lS_V%CSP84IFbsTX6Ab8_kZs4j`1)JSdl!eY{WR0sY5B;DejzBeD^^$Y(>affax}yfq*PZL9fIEGw$)3Xja0YMClnMb5?P)RwDh_S$_s?s+ucr5h z>nAX5NL_qQzS1?H1V$~C?z;e)TN_3yaBXRbs}%lsmz81f_q&}9Tl<$geuS$}yyT~= zt&htcem;Q9%VEYFA|hmauA?_JCv7+F^IXrqkvSvhFMcAj*kYSN{?C`|=k}0*8(2Z7 zF~r*uW6f;D;Bb6m*y+DGeSdh!B8Xmz>*75Gsv(?}pB1v8 zCLdn6dL*AejpC1fa5%-h)Y@Tyf_8Txu3)lgQNP~%?TPRE(0M$)V!=N8euyyrsCXEG z9Bg_Z6o`ZCesKT6oL`H9A3TNPD6IWky$Ch(wcE}594d79u;>`qT}6VhZaSX2mJM`8 z+fPO`jRLZoa3$xq?o~ZLyu9V!Anm~HHQW`GY(nLg_|*`(kXY-t+i^WOV?rxp^nD{8 zc{$h>ESTe>r8)*BJDUc&0HPz>6FwsbDovcC&EQCg9xe9yUg9P#HJxE2Z72g(Mky~i zS)2gl6_1R>8No*g+V(&;dZ_@{#3Yg$03;vgff@KdtrfxVsa*Fr76q7fC?w&yF59a zloDn$75CWpHAidqn9-4qNNNv6k8+kRJfR)|=iCav{I+zAZ7fWsOr|!Ge1(V262J0S zH+?!2sZZ6hE($fx z#$ufb2+sM7dL&m)XN$0{vgGqJbdy41Uud9@_z!v{TVH1fS$1Ij7kJ@M2V;RxA_b07 zJhX>YG$WRb6TbBD!l03HN0MUX!__*2ShpEA%E3vhGGQ%d?%*#Q2SraX$Z#SAcc^-`L`}NPC>W<^O-5)+<*Ot9bE9|J4gH=zT+G5y&@*szO_T;!x&T?`s6VI~u>R{p-{BhO@bK z{Tckk-qE%5K@WLFpShV(4D*DkW*1`*`vlPH;~g}#o0>&iH(c-K9gFVf9VD@v5-iQP zh!80OSF>J)Dr6|vA+(vn-(zerJ^6N3lc-1Jqc~Ykp(9xi8BK-hOjCS}03kB#V!c(> ze!Wq^JwTfTIGjAsr(8gJ^Q}W&fJ!<1ZX#A(!2M2y>*7m;fGiw+~eJ8gxrGfx$i;fn;mHWRI{2`esn^1T@MQN+egx zXJbmUQBHkBM01QS6Dhv8P4h@CQGR=FRH+8uC0M9V{#v4yc5L+6gM(wNiBhfyYiOtR zN3e0`p_5rH9Po*GwBAW)6hnjl;I0|w{$TIt{fB1I!(JNL*~1#-;*yI+N%ck;_NnF2 z_c&Ka1m?7zS_Pt#OOAQo^{gawy|SdPxvqKiZi5*WUl}nyxAS$iEpZE|n6Kcf2d+Ij z1KHc?E6=gqD++27R?Ry5B(99K4%QU(?Z-d}tConL!>dYx}K z1Y;jp_y||onL66qe4i)VqvR0He?tc2&`i>U4)zd3p3rA}>OJ4D`r2*3j@r9AeZDR) z=j6WLFJJbazMkf8KflSbM<67(09&5**X5g9o}P(gZUNmi&C^a18}?G3^UWaVEeAUS z8-lK-Teq9LI!%jt14D@KS7G?O^XqTdoLAm2=%KH)=HK)91!Aj?QmHm!6t>a9I2~&9DPfVvDI-NPdkBndfWD{Zi+W^ z6}zQhX}AFLw{DaKDC3A((dCBg>t=rMEi_*h(B90#hIe^BA}~Tyl92??4Dn#WVDLKo zCM33BGLKYsP;PLk;gN#zjXmSn67f4o($Q2mNsN%Aox?ng(VBaCceSC~PMN7Qw*&!p z4l_40(e&et0Ssz0N#77pa}~F+1e5EHNu!+F&GXS!Y`tTlR<|ApFujOUPtU`c!F)2S zB?h7eh-!j>G=OKMIqbm&G|@zW{P#7lMbx?&FV zv_h(4ax{;cinxJ{m6{So6Vux`T%(h_l(!g>{sbr zr#CgWmR}Tp1xowX2l$*MiyG5uD zOXAj*w3ALQN&O-7$=aL)K%xF zhaNjPVv0*giWtu+&_jEBOd4F5;4S-jfoE}$x9?7>2%o_?xFmgelph(99&sukKil?w zEumX)_A_d+ilB>Lokv7ydzM_C!&+xe!%i{?@yRsJo|R*3kF9zJOqh)d;A)s1Krv&RD$X<=ZV;#+tjVS@K)FeZ* zrNyF1Bh4ZP5y>9OR7Lz&UC~RZn<`+AY*F@YKJuHvzDN0>!3P!tPF4wnpMr2A!wWkL* zESzFmg=~Mcf6Tpv#y4bkz1G32u431Ex$y;xLv*CLA?gi}xuQE+x2G<$TjFyvPs=j|WX(<~?lekz=E@tc)8j{A1 z3M)OeOa&2rx^$sY6k~0K6G`puu)Yxvb9vf9#(v%AIX_Zbu=1*RyZ}j!Pdf#GZ`zz%?7<{U*+mpG86x^%*%|=)o7iK0PBItdm4yX^|E4&Q&p2f6b65xiq>?UF$j| z4>FA>E)e9v8bq~0h4$!lq(WJ^?%Ayo0~zGbYlF&H2BjpvYFMjp9kB@7R~UVa{&_{< z-JXcpf!c=WDWXF&9#LQJm(1LAh#cgMCKm2+txpSKxrTbW*K-Y(K4yiA0rsweZnV^! zGG==+&w#Yp_gkM2AvC+D@;EcyUbhDQ&6wKUT^<8Y1d31FTv^R@k3cpmuC=OUa2U~p zT!x@XYcfNu>QuXZaQ~<}Vy__i?TtMhM)sfyvE8D$!Fz6wE@`W1R?m`pf|FxJaz;(J zBxES$3+)hfIn7}2?R6@;{f+^MextMr? zYn$LWUU0TkUrzj5U#jBGXG_+_H@IS0o?otF_9FlqF^FO5-!yG&4jNeKU{UI=+SEk{ z!=&Gr$(S{L*<%!XuWsbl8J%u6B62lpvJy(z82Z?Pe6MIg^su38vyQMYhU1*`-ot^j zxL>}9T1Ntqr4>&%fjlLEcd(VPMpuu$3-nXUcgq}nX=rw#!7K(%kBrP`Swg(?=i|{& z2VKn$y0u*zBqdrNZ}sX=f`b9|y%`x2cmrd`x$=KK8p1fgNh;QFTIL2nq{Bx#tsF+C z&3+^{9MM*LWDT?fdGcY^jcE|q>kYS^jaojJcj8O8%E_oKuMC9xg9mZ}Ht|tG)|^th z_|x1T;g%;vspn(=es2tvI?%FG#Jfe_axVG^9f(u&nH-6!roHnnTJo!(bu?b~Xo>*= z9xp}%S?dJ#VJ6#NQjPUJ7rGhzIb?UM)3ny9iaC7^uG6KDvoy$6ygrF@{)9q%f556O z0>`EGQD^Cw_QKHOMf%UTsZWhOIPKMCTl40JA1OTz^^CHA%tc9rjwl-?`O}4v{9?}Y z`=fzrq!7KLlGYq&tjjT@Y-A?U6mfGXm}5*7NX(vMJ}kf`U!^^Y3_EhDt5&6z{bFcn zRqxx)rs(Q}FvbIur3;MfhF0JyZK{ogJ}7wYW-8L%o7ed9Z*??YK?!z@GGOV{P-D1G zkokVRS2D&H!eD0A=I26%rx@$woq!%z=*jAi87&UL@~2nw6nPW z4j8#v61nAGyctj3f$igYm}m*?x#?d{$*MqIao3XwaKwV=TxlzZkuWY6-l!#{oZ`l~ zT#Y)4N#hOXgIdyTeypv!2lm_>476`3HT&4hSkX6b4b7{M=I0wWDR51^374=yCV&!) z2|e5!(GFNpoSs71hPrldw_N+n{1_J=W~;ckK16GuqGeg<)!E{uqgv`v4(Z4Y3Y~;U zCx-FJV<5qH{XxsD^V}cQQ}^3IK9K|if&eT{;=8v15N==+CYrd?PdflUqrm>MM)Hp^ zASZ!%8D|l0Acez_5fYBT!6qJK`xa}8i0aI~3(lgaku*;6lx!h{LX7Dc zOs(y{QCX_pP&o+Wk#O)hw0`XCB()w5h?6c0XLo=7dn|jo6{CCSs&LoE-s9*Jp7-Q2wNRFX`Pvr zxNr(vpsodi<_SPE4S9S0pX$`bTUu``?B_ zCvkZdoM~Ll*z=BBBiaBNn4R_F?!ywAs})!lIT;QO)8?Yg)<(E-KKFd9Xc^9tK{`e! zTjO#ksk)&Kcy5b_>C1~{YVqUyXx=_7^S*U?J@d#C;YFESt+!XFCKQlL3ph1BO6K9J zS0tWEXQ}<0(L1Zk<)d*3kNNJb({?EOambNp;?9r4{x(2YA?&Ud?Tnc$Hyf+xNaJsd z=msF$2V3E`(!cMh?Vah7;Sp_wUEPD<`L-Kp`qgzhSGQUg6f`D^Y|WWu)b8h0HJP8- zo*}aG38DuPOBF20(t(WE%=urPveEL-+_>w=IsQ!>b4ORP$BsMyJ7c;XC+|u5s~=07 z_NZ&lBQO}(RFj3Ry2-l5o%?sF_I|terwc&utql1LU4E4P)x;R5^jm|m&W5;=t<38l zpEI!2yJTiJ$w(e2`XtAfS(>A|tPI4SzW#wWbxB+h)o@)2l}WKAl84b&t0PgmwgO!) z`nA^5VfPd5CdY_LMf$8U7aOX{@8Xn+yVg-n&IBWV_K_KTpcCC&tCUC0yiv>g+*)Yi zs#f^#Mx%s+eHcZn+rT@u=Hk(w%34L$-Z=N{;aw@IBSMK82uu>-@lU z#f6^#ux4c}hSTQpwjNL@Tt3^Hc}NI@4Kb!jm4zYVKxceX>NLrtg28h-%Q?1K=vb#X zIne4P>yQoB?C?xA7LRK+^O1HQH_HjTROp!eZ3rNu7EltIYk1P;GzpiC-JZSlouLt ztkkN~s^ZrD2_osMeTlL_{`}mA@`S?06!&3O%De7Yzd>S#C z^EdjUn`|W(2jC1vGLCE4s z57QFrBj6oy&h2VnJ3qJCuy|x;^!L?}Qhlgkww%WiitSP{uwFWKaZle0-CE9rB7^Ne zS$y+U+9vs57NUn~+VtSUuPsu3Q{~SOC%abf@6o)VzKh5<6Kk}i58PNPJp8M3v=0lW z6;|fFOKrt$xyPG^YS?mFakNi%v3ygzq`Kml&}`mugD*75z3Po^C76!wObH)U9&4FA6Q?o0wEHC4;OLtm22Aj)`!m|LtP-IBPgFw;Nw3VWy!av{ z66;~PgJ$HXV|L5Qf8%#lU3L@aPiRS*UvFE-!*kV`5%0}Qh>K6)O;)UMlgJ>UvAfz4 zjWTBWYfZP#Kqlr?4Cazv-DEGHpqZ}DQSBy0L-%bZ6Xc`KE7nyJ?eR^XZ2E<5r!TV2Dqz803NU^! zE_F<)dET%Nfq#8y$8BCG0F9;O&ht`RD} z^uZXsMc_?O@I`>8Df05aSyE&FKif{p$jQLT^#55?^U{bRm2lYf_z3shhtZGRf@Dz7 zZ7)cI7;u+Q{&xY+>xi>bdCSIt& zu8d?4?~iakH2gQpDjPF9KX*>_vaOslN?Fs~>qe|^_N^E*TqkonXl-alUgrpWzV7+I z#!kLznhr*67e!u5_-3ZQ*4t7^I@^-WW>zCO*%2Lk6*rMfXB?Qa*ru4BHg z?_cQod980ywc(5ThZ8D4?=Q?@!vo=b*YZX_8wTVf=6dm^G;j@~li4DMdZO)Tj0@QM9+JdjWc~{b`-0IG+|0Jk*VLh1$?4^+n|MaAXn;c;jg46ICB~Mkr9%J9UQf1=R5?^<`lS4( zFV_8>NiLBmkmPdnBo*rs8F}W>AMiNJeCIO2f4P64($|G{LNO~D%=&>YAB70hw<`O~ zl9rPrD9wuD4yqy%$@uWs^KJ3ZeYBK_UI&KDMBl?7RVeB(-I@@*;W8?IjQj4Rxms{^ ztjneMA^$NOfBsKq<5T}DLJ$i^wwWcxMlJD8y1pOkLUqYUY8&WQEPhK08>)4T<{I{8 z-gk7^QZ1lx_avxA*lZb!nsM-E<0DKOAm!iClzk~yo6Wy!=z?JBdGJZU++W=#84I`MR7MQ*IACvhbM2CiUAg^wmTws@xow zjZ~LipKM{iW|3k4+7072i~(9bI*jqs0(8TWN*oV_1x%bm3x`U_|WvqW&4>bcZ8;$|R@3JiP& zunYuf#zJtQa5F0?Bh>oD!a0eUb;6Kynd&b$_^CeY`r{ej_U*2<|#gY)`4= z2oBHm79toU=#b&q3DO;@tLxZ#x}k3l<6kbF^ZIgHthIlN^%E}fAAhg#EeC$| zwO9HyKU_6^ZY*}Ky}D|B*_?ejS$$mDaLhHA|9D?tT9*d8ny@}>E(VlBTG^fgX-%bh zbGenStoc&iyDMm%O;3R`VoHeHoAVwxF-7>@+a0D#h^f#9J7~6GpV#H3kn zJey#1G<^o=!LVVxnPqtO>5_ZDCm|DLG-QCym-?rmYZ>m{2;cVuOELZnGn3PYn1Okk zP-p2cY?^#VfMq2k|E!H6UIC=@0JjpqoltmVek^MZ@mgh(cWjW;D8M1SP!z1 z9K@M)AG1azobuklcu^;#wLwDhJCOZB2zfXZ!H-BuE|0bHHdcUXQ%E(#IfMaG2@y+A z8ki!sQXCnx*s-wAw;Bno27jfj7{s3j);=}*-;VWK@*186FhXh}E>A8(VkR}63t)p7 zMyGw-!5VC=hY`8fVeiKJ;9F!y{cVOtT|_D3pzUNxTdF%qK2=01B*0^5wrAHDPsWP@ zG}OWB6x5P?ok+*`1yfB4Q9pFCwiH9Xa$lXhw1q)V{ZGuBSs^>hoGD& zK<7jtI;;yib;!^GY>igrR@qDdmab;VPL zj)Hrsz^~E(1G_0hsbjgjdKCxHX@zM*zWj+L|ZG z69hpGum_PXhw<6>5}r^0yWHxOEfkI~*CXJ+D!^PizVG;7)q%M%JcA6%^Wb>;VUe)u zWw%c(@uO$(ZyI~eb&Z6NWVg=c)RW4X#<;#V*eqt_2^AP|6KmkeCDw0V_8WX`h7Bj{ z$7fiJn!+{FX88?6K>u-xqJpM!8C>Lt)B8>LoB_zql5X|pbk!!ufdWH{}hO&LU%2)|pVxhr= zz=-j4Bq63DAn6tZT7^s*>{F^rdHqn`!56z zhxDhGJ%2y3eS3KV^4#pskOA_4mV>#}yntL@J%YPXfBeJp{4ivEhWUdx*EP6m1~+Cw zlN!*Nb4Dn_p*BukzZcA6DMnPj9cHDvm<6bq=YCQDkQ1!`cMRx(D6B`o%I^7NmCHq6 zM|37VMs9n{jx$6KWhKgP))0I_mNFT5 z0ovBJBu7U8$E#1K(vJPQKiV@5*2H`d4Pk7147!fv-u)QN=jJ##4c5kf?|VW2n9w{F z;RW)9@%Enp%B_1&afR@o0MDx*^1m+pz`=NXE>Cm!(w6@Cn^N)de%SEH^33vRm~3=D zWHG!v*EX_@2p3mNkV;A`NlD?w9V+DRh7`xb5trfgp$}IF9pE?oS6bGPnOQv^es)`@ zPUm~O%OJcxp%O=ba3nc2rp6L^s8K*jgNugu1oYSc+wyK%fBetp*ay4m(^W15dNW&I zxI2q2Vvfh^15_OA<7+SH>%{fzameSJe3GPgtEvAphM)iAda3KHtLtm8>+|!9?_+Q4 z^F~jO;Olm74pFaybH(%Z3y5#b4wJHqo2-?WyquY{Jao!K#{|*`yR#|{OeC-*B{iYA-RfL zq&CgokxcK;_PLlrAE)McB2UW5+^Lk|%M1SvOC%wsK~&x-ivH*KIrX<3E&+dSAle&@ zzcv+yaU872gq+RLHs0(LvZGgtpq=576aN>W?Q~_zD{Yy#7^pasG0}vosCPL=@%e5N z;5?nnmZ^W%C_4(eOr4%NQ+jEf#HBj8*bk$Ox&)8-B%=nt5Npz`cB0WyZ=*(_Oa=36GXQe=I1 zH1+g3jB@-ed9q_I_YVxnKNbf8lQ7^r_Z-j2eW)=i zTFp$VHykS-Cf&OVP?Ah3kooAD5q6QO@7lfSo~+p~oq@F95byz{8002x$?+)HM`kg`m|6ua)A{4 zEb2ACh~{4>{Dmc>quuv$)HA6m!ZP7=%Z>m1NUpVbcaPmz2_+~*sSQc!4vS5V%ak#k zNjLzBB3eMHCD-?-{PQPd-G6`IiEjmOdRvMoP8|fIkh1{NS3CUENgOM8LB*9=pr2ht z_y%N!?)CX1OjuJlU6H?Qt`RJTy>U48o0iA%R*C9{srWS=D$T-%xQePDFTD6Pvp<#E z1=*U~kla>nH32^O55YLo1HU~QR(Ow(kvv33{7%}tG`ht#FHRjDfzeG7VXd(h@=}Fa z!my3f!|qD<_1D)cz3i*6(2>3AzPagrVRprewcYNa1$8gDwXKOZ%OHkfO?^N5_E9lB zPWYS&qu<1iMl{f4`|hpY?qvK-c9E9(ZqSx0wZJ0R8pt;fyOv#ZZosGKd<#7`LeVt= z9oV$q;x|k;z~bt7l8$Gro$SK#&#S|Ux&x9u>HE{)<2wBGwTli1zX3SRv6YzhiWv$r zl6x|eWAobd|B=2Q3`G29oQ0cZ7aa7+f~+>pirq+_u$hhsS>i^Rn6kOWk>GoupHfoG zrPi!JK*b1V@60D|`r^Yvsn;KWw&S+=!7F}q!Zq1hFu*%|Z8pKn5y$gh#rS~LkgmjbCf_A`n=5sB4 z-m$Kt@6E&Xd3E@1MbsGE&mufU2egAK+mU))2@vrHXpgJ(Ce4E#ZXO>W?fZxk_;u>r zq+AYY>C$q57SAH655%`BbXgYC*K4N5>Ue%f>~>93LEkH2Ma7xiZ~2 z)hQ$+J2!nA9TX6PPBt|S+5TP6TdJhhErYsbqq+%^pBc&Ei{mq?%&;fN$kt7g35w}J zFmh?j1B)aZ3tRtNO0!rCrd}coljO|uBe|d-)lhr#GD=qx`*&clu%Ky0JSKmQH-({=*lSWVBV6qi*FA@0Rx{}_}VNm~PeqeX_U0Zj*wwa0d{ft?#>$E3RD zh|~zOtB|_Nk{q_sb5QzNa z_7TPUTMiKB>eDi4YB)u|8974IfbDVBn=7l_ewmUfl?4W;2E@iwz(Zjc6vr1iZH{&9 z<|%;&Nypzh^M#(53G%fTY5l_6)=}HV&zB{;1Y%hCEP~V=%Z!fsZbHudjw^z|WJxL{ z1A)kDs#tjYJH6veM3;~J2qMMmGSAKfrfFf|4>BKdY=maK(YKgTTnT?WrpsGLZ*K0;U{YSvK zx}%v%51)j|LMNulX{(jVS2O{7f(fid=CDcpI_ui$6y`SF5TfGxqW0|iWbwomg4Lck zi6$>&`*dQftt&|nrfr6x?Gp(brr{Y_VmlDi+kwaFbsn!J!iAk+Ltw3K?vU(|RlmbV=&Sb|qRmj%|r<<@I>b!_bp;P^VLy+mrqXo_+ER(l|i%ARJjh%~Qqg8?x%m zzBHvn`mub4_Z6^yev}KiBiPaX;?`}|r9weSOO~CO-rgw@@c&ouD)hku9T6Vy>a{K% zwh&{6jl#b{r$G0w?4DY-%Qs|4DkjDA9m{fvG^gr$WNfJJm-3@L>V|#2*O1)1gUrVq ztMpe{#s%I^b=#ZFnB1+O8eQq~RlqwUEXC^>^<$&vN}j4Hpf-4j+JrG}!t=u^u6DwI ztvv9g_<$nhhJH|N%K%eV>Enf(&1Yu89-0<5!>5fL=?NcWsCSwFg;~k7+dA&z>1zga z`8)CD%uwi9pf5gDrIuKU`vp=|oMAbx)XaXau-{IJF6!bXW8S^yu)F+}`yMDJ!nmw~ z12eJ$A)^*hQFOgBun^>%dUiTH_P!3B0e2CN)Rk$5V_d2?UQp0RE}4KE_lwMqhIAYw z1fBEijNycoiv92NYogOGGQsgdeCvnOE!q>Z`%(IR8x+I3ejs12Hb$Yo-BKL2415g| zP?w_t4MbHt?m4Ssu1@1ZK+0K6u7h`9z5v&#D2-}JXV5G9n@_kyZeJw~xf1wX;b z=pVaM_w0LES886aJ{Gr#m)y{0y)<+RUPp4`y=0(TmSPu=!Fxf)5+HIY@qsItG25Z5 z#bC9dLXAflnD{^}Zk~}y5Wwf{@kjUAmTBr9k;!%9Ul{xwZkpDcW@`0OnZ^$;@3w+@ zzX5UuVq=swfJYV`AV14nD4oFB-07x`KfUH6+TtS_tQp5)mJU7{H5!GpsdX3jIgi5S z9Y=Ok)jIJR%1G;-lHU)wp%5EOwz@0#jC;D-D^atCq+>|M2k_8!pl-In2mddFSn@C7=rx4$>#Pyq_Y(!(#W3Nq|{Qq&cmOq$=3tvFB54{WLN z`#*qsMutwAk>D($vlhqqldxl(jn+^zWnd}Z%^jqdYLBK8?01W8!Nk0FoyY=UPsn5+ z1gg2SfzDF=C)}HFTON+S6;^B{A0^nforo*4X4uOlmzhePkB6o%t;WUjt-?uy=`NR_ zVw<)0p=R0hxSI^}0`wMFb=Uk(h^e+pm`+VGczL?og({44Sn%{snC3!52`ITaD{bd* zak`Q2LS8SAKM*JMOe7EVOk)#0X`F97X<={vp=(F88UU_jb$8S>fHBG$ASUZKpgAjE zr<0(8$o_FQxS-p9HS=d5T!+T9UwiDh>!dieZAR8V_Xy90H{zwwb6f8_oJ~^C^|dp# zmcN-@n0)>uQ}~>9j_*va2@V>swI5mOSA*kYN;g~ES*x32r&G06JsI`4*WPQ5tB*P> ze&nk&*$=;X{iEpiaWnI0bG}pd?lGA&%e(y1=c@$hzaO!;?5}U=cPOZS;x+TTN$k76 z;ppjYl}HO{JU&)s!`1*ta~`%QPH~^C>5g5Ri>t%;vOO%tbH7gDhxb#mu7=91cyqeg zx+WKD@3gcJPSvZ4s(Vk};oUA>eC$n=##Voxn6MqBuW4Kdv~SQlAC^NE=2buTDI*H{ z2z6b#pSP|s-PKEO>;Ss5)S5ablQIeq{`KwqeraFqQj*FM(ETcdw zphL-6Al(IK&`FR3pl{4JGrW(|n&b0fVVD~?KL6cW+s#Ur*+6Z?W)7G|+Nm^d_-qienpwJd zQO}jZ_wx(>=K06P`Sp%gR*FCXKsQbQyOX zm2C_nDXS~SIt`i`*244|kW!pwBdgs(?MXW-Nl`!A{~8emKiKs)*XP zO}R8dMuWErLFXuzdY0|EUQ=CmIRz89OZhe@^ zqK^Hp^llQLRAtt0&S8&6V_gI(M-09$wYj#hQxM*cbT@jyv7|vC^%TrQ__D5ri(0%+ zD8rl5mcSrkD#tWN6yYC8RQa0HbY$TpJP%b;d@uyff^f!A(o|%8`&aOH@RD9kSc(hh zloTM>{`ln@9;>Bvs29t;$BHhEXIcx5jT%l`kdSS56q%w2Ng&MML0U8MSg5Rkyx$cc z3ZI{gv7B(6dAWf><#%_4^~rVCo8Ypb`2!ADO0cgn?K%MMRZuGU%8FnY9Z(~*`)fCu zCww@LFbHZsB^hx`gs#7`jS~7=h(*Y%GlIPRw7^2K(uNxO?S`#1L%X6~K!oD4wg%0}ivvZmOGiPB0yhVx-Ypaf8F>5E0)aHQY+&+-rHii6Z7HFRD zIOoX+&uyzZD`rPg+!LvW=X|*wv*F0^t&%vmC$~T^tPWS*N1xhr8$p1Z155d8Gx_U` z?w1#Dcvn;1CK{0<`2g0aAxgYl!R$;<`$T_WIGX#YgPwBdrzbP`;>5t*CA=(KS5vm2 zv`f7vnxS}|Juq8vio{IKO;N3Pk(QCWU5oJjqk39~h`?g!Cgy7$|qZ0Z> zZ=+t}>iOjlL*0qAbE?TJnNy?3qz8r-(?X*9&1Z&WmL}Zuuc5SSz~pL)R!dFxoLDv* zZZa=$DLM8N8`PGEYbMR9xHOP=^fZNc#nhtX{IpkYXEkvyjE-N25IeLkS&XzfS-wK? zgokidm@IE3h*YTAoKB>8Mtij9vQ@L0<~h(u838nQUYV+De{HO14iXe!;|2S|KRr4m z#9CLt65Fr0Ef}P!C4qkd%H9v6--utk6ro0?l>9|@$msC@sWFjPF(Iq_k<*FQDmh8e zVAe`XYwQx$ok?rJ_f<=>OVrm_R7NFWsGY!aALcDa;7WG=QaKwUOme?9Npy!1E7D;_ z;=F;CJTanlEh8srat{-$(%HkV($S}hU@BEWO_2IVb)t_cpWZM~I=IIbikYbsk69=T zCMX$0;+35s6Oo0+4$nm9Kz|)%q_!s+l0QgL6N-bQZn*n>6`m=(pBHvH zj7RAbU4)FgkRP@SV=X>6nZ^$=nDLJ)#tc?PRNC?ZgReW@c*kVBhFA)N$}kJzPH0x^wvB;XQFpEH+! znH4c*+Q=V~umB1Fbuayfz!(}%$xCHr!e*PrN|8%9=vAunkV=k#pLvSIwO)4_o!qiD z*RE6j*AucfAUOJ|^%Byu=Mu8=5NcKM%d-F9wo9<>-$?t}&qkMPBF==ojdprVVaS4yK-a@GVML9z{y;?8K zRU3)MOvHeuPsD(Q<^dRdTsv$%*|e}a8SE83S?xtPx$3Y)3yRNA`x8G#rc`+!KGNm1 zK^QlAUUqP=Lg7@gU~oD={?B`J-86=6)ALHS9l03zPYPg{=FzfY#qld9eg8qi0#T8rw68Z)Xd{_6; zA0k>SY2v6CN+`)dB+nc|Uf1Y!p`Uuq;P2SeBbEIn)nR<4WKNi4@41ujQi z#;Gy%zb^8Z(b+5(*M)cC8TAckj&VI(=AyFmQ(wT0n3I% z*rjah&cTjM4m1C1s3BRzEyE|^DGmD4{Y;d=Az*~WA8U9DmO=N-_2GfP#D79N5tU|y zs~`;Huk)7$O^*?DNbI+e6f8jaBDEJaesA??FXHHplr|yv6Pwcox`t}Np_(0exV++3qxkpz|5&$>04sT7~A?zuGVJQw!ZzraUGR_+OL4cV#HpUxt}dE@cwS(GN_HTor(aWvWd!$;iU_!NFnqZZIg}qsoJO)q zcE7cME#(+RY|GNBaS`tRQ!=yRaOv#nI3=eu-PA$i4v$H;YT+7BhsJ*bQ@E2h7B;AO zXY9f5tr|==BRt#D{-C^b-G5|gZ6RVXOYEgCW&Z4um(`C(-gLBajD?n8aM*8`wQrT> z6}}z~ae4A@L4i&t201W!MBv7W!)W)3qz9wr!95$nOeg#Em=5>2|7df6!%_Q(pv#QR z)IEMvO=OI)Hdq&L7QAh=R8g{g_F-8;50%;o^njWN(KsIbNnJrMxxaAsl#-_#H9;5M zWps)Wbx!0hOjUy|n8SM`xnRXlT+R6huuUD}dny)#Gcu{Auf&1_pB<`~XsDADklGy)R-GLEn;D(fJS3 zMWs8@^`d%))R{Be{?F@vRZxCObG`&Ip{YrX@iqWd=a z3?^(WjkUOE=qwu{4aKJ%`*%k6^V^1jAk=Dk0A+ULdfFnVCD>kxS$IH%F50OVJMYQ~4E zSwl0)is{l>ikyNNzLmi>p?!eL)+N?9mFBIwpuT~@42F77ZJ*Q;dBnp!_Ivawh)J_; zqpIp#fbLG!efb-nO+F1_?R^KHb)vppK>DiJLe>tHf#u*msk8U#VV078=*v3O&+Cu3 zVuVvuCd~Krtt|vv?aAVdF@Wl*3k|3H68@mR(kI(vT{c^oY6=Y3Khi}yoZE)(eCgTU zim__l&#$6}YMLv@k*QG!h@z|2B=C+yHHZAq;I91MHGDcHogZ!vBTgUqTYNfHe&iv^ z6$~PZp`YEx;~z%cZep2I($!*ZL`myLVL$zHWYhPD4A~W4_U0N$2oAG-0nnePk2h`|nGJ~k|1i9KX|#Q|KsD2~!~iSE`oIKu9`cu&UXj3AW#i0QVXvQAwh6*8eH5sh%wcI~ z$#td(hNp%zt>Fx(gBO133pm#-uh8TrO}QvcnHZVXl*ZDAxB!{Ser#g)>1MU$>5Edq z%ee>tKoZj5lnEDxP>mZHqw_}aN#*$N2C-PiVHR^xFa~pC2>Pd>S!{Ym6CW?rvk0sN zr?GrhWoE+%66ABt5^e~AvGKT}7VEY{jAc^-u!=PShJ~SFtwMj1D6k~GYR4Bp2bB!T zAfMxyu%#tMP3&3JOZpxShuUqW4a5WoRq7-(8**iEI(WN?9mx+YgjDq$Vk_w+Q?TMD=*RXM*DQL`q8yae#HrdSpSGEmx*iyF9(}s_ zkG+$iemEy2vNjyUzRJvT3n1-Yy~y+C09Z!NvmnUg z0VI*7YKV@+q)wDIe<#V30R3iVk2o;+BnT23330c?fH>%jWu`ejP|ks&xrah2$%pQl znx;VNE_?8*)%Ks*6HBP}0O6D$$1o89ee!zsOlT;qFC2AsQUtxOsR^RIM^WmJM+riU z_@;#lw#R73hpBWAF)W;(qipof$tH{`LLHSq5fnHqpyp|Gf1y>Io{|{@moz-2u=0Ob z@shgdsZ@25!^bImwER^$4V)mYm0T*Zk((s?6frLR5;1P${KaCDL&;*Ni`?+8#SJGq zf(j=uMj5d_$TML`i8^6jVw`ddWn?fCq5aqVQUH95u?eo7fZ-`rG2k}Zm~k|!p<>K4 zgL`4cW59ZYcc)Im>+i^`xX?mDq^0xL{7FG7PKMZFby-Qh%8R~NvKTevvZs1PFu6*+ zdPKyZzK1 z5yWUSb8-eBI*FWjpKM#=th&8*ad!Qy&7!=e%!tC zDf&aqxmBn-lu*B`js|km7x@&=^JDb2U#6;~UjnqPZfBMHZpk`+9smt=iHhv@q`8GA z#)3Kvi~eWjbLmuTK&tbWJha(TK6KwwAtMd^1`DV0DwRGRvq=I}$S$c0K1O_MdL(t- zH)97G#QK78Fr!5GXB3mt;-r`GbDnJT#**qqW3Bl zqw^tXo}5c=gU~X2`vY)fhFm?$D4`>dZX1H!ukjz7vBelz1>ZcQ?q8!dQ%o412+R+! zPaNO33?9w5c&f*GCdyvh?D46sLb7(5n0^;zkb|?c7R`-!RAO^+1pXNC_=g1QBeCiO zVfQrb;|Z4a;C~X+dR!rV_ug34K6`r!J5`6#>RsSEC&s?{W(SV!?Oj*o*x3b}%#k+| zK#?IDy5bTo(ap{P+a&_muGF^H{Z;^vL zFx6m*9-i%UH0vk9@AFDAl>Y2=UA`|=c-ycSBxx6qlk)7Xo2LnVn&UdKwd0F(`;^Jc&e)9XF{7q8qzR zWL{{eLrtR@V`_uHK##QQLE+d}AqNcJn;fW{bSVuUWekdpne{lu$C}y?(T8-^=!)|M zN}M9*Q{;1h1T_O3sGE_@Bg7Lu`m=s{`{@+#wHEl$YmTz59nwIw0POg6w$1(CqbZVG zXm>jsef{;7nMRz-pq>`I(A0ii@_O3-0|elxWNe;eOjU0QEN$XbF!W-3!JWzc;Ng|e?pGq*op z249VyXtJz!QMWHmOWiBCBU%<#h#WdUKHT@x4B&fdRx@1#t1Kg(`;9t5v3@rH5(Wcw zz{Q*9DQH90)Mr-8$f%ABIkYHdl$jagnk|h_^g>jCshJ}r|16>T`Ko>*2 znkN^#`YgqsIth1_nmspDM>w) z@1Q!67eHIR|Ewq9&a^p2RyxpEE*Rdg5 zoqcTKi7O_res!p|HS<>$=l%>Xgpa>GnaJJMs3z{2Z<1fW1lCHyJ_b zRPLj{Lh$U1qeDA_ed-%PanwYzAc|vWDwguk{TPN?mnh4iR-noKHbe70IgsyMK_bW- zzX~&|(Lt_BpvgievYLZTH#R{3d#agK(Ymdv6 z3HDUsxlvlF!_cI1mul;6#P8eAM)op@UEc`zm?WzaOK`)}VVt=Tq1ol!BS%zIf1Nv{ z7~LY;;?NCGuk-Ft`~5|!+n`azEA55F&{EU^4m_~tSA3CQ1y}vGRV$EQrR~d60l}5l z50|-Pe|ES$XGzRw9WWU%VbqO@Do7zEbmp}Kk=2yW1qDm1%^qlo^Z&UTDNy@|hB%}; z{w8K#VRkP<7FsNq`4egr>*ZUVHK_MXIK4242afD_Xqcc0k5^G{Z z2soge*RZ${P~2r)EDf^UF)w`zru@Dl4Kg*Hm435_5})%%2#nM}I2-kRnEnEx57@D)cMYiGFkBDc!Y&I~tRV(i70vuJoP1zT|yBqW3xWg4xm7L}P zE3a*kA2G5IdCvhekc1I$H;)q5PPZ=zG4@5(LYb)Rc93YV+!9g*g|(5X^7wse%4XKS zK_?q48$jpYFKAC4m5j`b9WLb?P_@$P0bf4fTF{<<*t3dKBi-`gwY-P%`9eO5Nt?hL zGcqO_$I0C%TK6~etJ4U&j570m4&Hl3x$4ZIj;?dlNDQobBaff(d2&#`bI{^PjB>U)0lr3WAL9c#_2$tzlM{YML&m}#Znic;Yo`FPGAIiB785- z$b2uOs75Kcs8Pvw^ge(`a82tDT7|&JI9g%bK4q51WNN> zFiJ}}cy+5Lf4i!f4xH|gcmCHRU4TZ8_0;ak;i!kA0Uzt%-O=fbU-c|dEuzjxL`Frl zV$Pr#_#>qt_L1k4eS~{lAR8l2pw5x!;H+VMnJ6y4heRN;r0htH(gsoI;EtFp)VU}t zX_~Ejp-6>31a^TBNho%$kt;%IX8NT5cJz9~Tz85l$_K*Zk=dD}8FtB$N7%7zV@w0= zlyieEl}`cR*mTiCymW+hYK~eV4JQ3u4vcDz{B&@Pp+=SGic@K|vcFyaW&th-xIh}_ zoKX?%u_jk?FzVA}ntf8?nY^VcS;YQQu!Pi3b6Oc|I#5(P1&xhJr8d&Xlu?iqWAwhP4=#OHe}x174qbRxovNEadF+!`Lp^@Zqez! zLSnCdf7D$JbW;lEg17Qt5k+1IR}9(jWfkfG?#xn)zLAt7B#_*Rn3`2hBJeR*AO~1~ ze`zF(<2dT#F-2CO?J`@DCOMa3Vp{o$egClgF&-g=U~4Owbza&Fu|bs3d2vT&t^O%8 zhtuVpUOT6=1fwTY!_x>U&*RNb3xDZ0y(4HdVDzAvH^lu-$m2n=e!P_+G1bd*s47yl z6Nc6lfwrK|PC%ebEqRWSE7dR)7`~D`=h~6aNY`T|YDl2hC7)s@;OT2lOzEcd6aJ_4 z^!4^hXJx;86eHRQ(_Dv&^kku3<^!<&9WV@ckGglCgGcfr{|i4;l!NOHUhmiGkx}nI zR=xSTMZmdVk{67x88}7Nk0}<-(xYBjJ-R{%J)~L{?8qD1Dh@WuMxbYfcguf5Qssk$ zrVdzOJHs@Mbg%?TwBT5kfGgd5{!k*{CD6j=X-K#NC8^O|IckNJy?~O7H({-j$pa5E{3;8yGAs{qfrCpq=uK=?JjC7ONI*mp#Ql8J-&>_1PNI!0R?-URD_O7IKZL4c%&gFADIPOx>>%j#3_n zpp4VQBC!mP%X}`%b9YLz9sLyT2}Y((;#0MWO;v+UstG%7%O3?N)2rpq2CVp9_O(;3 zSr{R;aZ+nUUV4iTv|d#8Vl7yYjRQlKpFaUjs9ES9nh_U8pONmBUe5Yn_sLTDfB!5? zLHAr=T6*?pnINm#cPa&y5U_HHn*k5ofzoPALJ-s@y!| ze5^J=tZsHKY=}Wq+28vln^vG{&2$}q0|cxnR5zd?VEAe;MXXo?8+w{K$8>N|gD_N! zKIH+6(;}gVE7jD#W+9}T$|4NHw+)!6%uF%*Nk ziMqQ2I}vgW_O5%!F!aaHLt*MZP<pCGFWtp6MR+Wp!ru;Ds^VJIsTpse` zBAW8YK2N$6=}Oa>(a83lX(!0Pi15y=%m)Z4?4G)sW)6Zb#D0zcON%W2IVOEIPA5w> zULEv;Tqm^dWqL=GA6Y1c!qV)x)pwr=5T-L*C?KYUwwb>Yry626kNN;POMLA@%f?IGIzOivmgC1-D?5W)jHO^c#lM(xGRk#G9UH94l0m3|Fz7 zy?K5ex}ticsiHL)uM!j@ZG1hMXMzTPBJm?P)vrg^xODV4Jy^w$0Jl+L-T5AK6iL_*+!+cVOM{L?XEaYCsz2RaiRN4JysU{ocL*ed_Et^* z28R{BeF~OsQ%N1KC1!9$bPM)bWkwNhN_?V1T&=0~um>My2gVtt=TzIPbhg88{^56v z24rG4wK|^f%~yFJ&sTk&0MFOJbEZ^FaDfzNUA{+(HNvr0I~$(lBcPdK32e1a87cQa z!i)JTyW{z+ZyvAEdzyuf(S3xLgUrFrff#Je@b z{ITHNw=5}4yVedtUf)aDN5Fx6jQ-OYYUdb5xLy~?_d`V(@UiX2i)s&za4_e(;-N>fK zwL$Pl0&SKtsaO#d$_%zGT-A>;P3{k;PZr;%aApS{8!_E{;LcimMQshB_DCs{=23;4 z#H}Aj&sMQGjLWy3!(k6=_Uid!5m5QQ%(c|16*%@;$b(_#C~Sja(0pRfIfoh1_vU8! zyIs{rwhoK-TNn;RxGT_L2=Ie4tROI5|A6CEmj2JC2{uSLth~gRfj%&Z@})+VkhI64^J1-^LZ?~*QNhF^KC{hgWF0cGYsVR zZ|*&;o8R;)D&d>*$kyS(z)V*@9K^`RQ)ZcKVpbX0ovaneL!vB^0!Y`a^4T}9e+Yx} z1m7`_Xo`LnmJ=LW&k*ECR1C8!WaoA_VaL95ZO47bz_BhFomWzvyh$DN>vbxkTbGj0 z47|&)$H(bdJjB+GQN31K#4svitg}-)n>)udTJ)ulIBQZznS~ zxBq5RvR9*3)mTjNT)sZiTjKBcX2ffh0#HPp6Sk9wV!=bllsip|~x9NzV(ZsPVqu%`1c4b1SLegep}{wjms{ zs4ym>GdPtugeB%ZQd||gE0bMky(Hi%?aJ08Oieyy{q(d4EptCit;K>kr0a3mL!u{R zSx8H&M6qK_tD63_+}zs^ldB&bX2#2W5n}Ba>*@>B9bc0DsKa+{MeEtz79`qZtmw$u zn`BtRzM0axFuT`T=i*-+`oa*=(dA0}`sv9YW18UM!7!@k#jh;$#S^@uGjlT*ybWgW zeWN=6@bLZ;;f!E$pWVT)`iL$WmpRVLCCsio5Qjj1L4pf=SL$LF8mEEBuLb1FsgGM!o$o!hO92O_p?#wRc=Wb z0{@p*@59TzqKRcM&;ln;9nF}71X|$9j4;dtm=wE)KIqPK2oSs0Hhu%)%ia(Y z_y{z;Hyw5n(0%qyNu7p@-8Oj@&zgqUS}2GQkHy*m8|^ zVEB1}Zo4vZ3Zmyn_D5XPzR=oF#Qlb)!^a>*!t}pi>?K9Vl1A*`%VI7@J^t=>NegqD zs)ejqjE;r}^u)`wp`SqFluZ@U>lvxTR;LQaS9G3}OW_7s^pcenzoHv<*r6%2PWKzmIi4!6;L z*A7xZuOZU<-nY>KR~|rxYZ;K$Iu3|jpkSzDJ0suJ&XLYWw^P$YX*8mN>+55=vS6lQ zI{e+iyMg&zYlLc7?mR2$i&EcM%r~rejmwvj*8^TUd{PBG!M?!^WWlwWBXyH;gGjnM z4FN5+$JiohEUW=y;9zyF($CGK*(p_#af4s_Z(0kr})bo&aGJ%%(h>~&$C!rp&KCC@qipo~na%0M$^F2I52UJZ_OTw#to1Myl=+!D_ z>Pbq%uWR}l{S2n#(B+iSg%8 zcbbAoh(32E{sWveHj2M#%ICRIW&Z>?6qkknDxL>mihm+y6AKA!iclEc5T|AL)c{?x z4JB;2B)y^x`0h%z@^!?Qdn1r75vPr#954D6J}+bMsK zIL`PlZ+rNNvy1tNC%-jwyNI*DTgv%9t!4XW*@YN7IEEN5jI}LaTYWRG_2F$1vjUMI zoQ59LW-JTQk4!EZ1ssM%k%vRs5>I%ndbbUU7iEc_W7zf%25bA;+iRni;HhS1)PpUj zBJ;d1=OJ+8-^hNr1V2C2`-pL(=?NUU_?NfihRa@;y?T)Z(;W>1s0B*vc7^XE|3l#7$@B z{?37=0}I3DDcXml{`&*cX~M-j!Gd1$+75)jq}zvwQewv0I(lD?qjcBmd~@?<>pe|s zIfA5|VL0_BE@NoN?njhBBWa%0C04AJK89s#;Lj zYX{Ni|2k<(sBx#XkD@NLQYJIvr;`@Il?CIdgWJfI(P;55v=l{aU9=Kx08P^lS27YK z_UYtP{vj+xxU*iIQ}Kw0tRP%R+lh{}B`~YqnZ^><7Q50c(7bKSsNwoc35ij(Qpz6n zBa65Imog5(WrP{%QDw4gLF>CCZLzu{O|kksRdLt|`J<>#K94U@Hb$ywEx?7bfG3r! z1)=fNhz-7Qaxo*UGtFH{yx23`v9gG>OcWdEREutE44UQGfO&Ef(X6s*qBBkU*MbaW zO>`sr(P>&YbE*$Tn>2RXDI={6sg*G_zV^}3HrrDbbKZ)V+mmEEp5?-yZVU9472X4fN}v`$M>-;Y|7|InOh#y7 zhS*I$kvOwBpK1R)nf}_sEMB>8_X4OO2&SI| z_9I>1hoW7qH(1{OunleP@78apPNc4{dm0$8N02skx`XGt2b9@f7kT1&FK4h_NOTtRGhpyT3|R*P=c{r z)W@HKyEPPbA*B-@YZjt-$^A?T44y3yln#UuJ?3sgU#L;h zxExgj0JjwOU0Q%tt6T)TFv#ND)&7SR`nN|r#HV7C?l1DE7E1R9oE;pbDI^s%jr}|( z=HFe{vzhvAb&vSzQXtarh*Kh9%jIGy?&WdDB5%e*q_|QYjBDvRo4FR8vT6{++^P6XT?+a?ok)XDoaa+8hf7V1*Y!S#Fy4s zWis;{j`){~lAidvZ+=C|KSBRLa4XLL zGq@EqGc)`D2W~|^=JX%9)w?*3yzB)WBhhSyWiKmHoUiT!NedN>XpEsk;%<(OCnbrj zXPjHbm12u~u<}MO#k=zMWMl)d7@R}*_&0Z20_W$F=r?Vj_0qD2tgg5FTMH%bma(}^ zakizsCJ(3PxiBvXV#B9=6wlhTuqVAKKd=v!=)} z@3dh&m*n+wtIvp78bPJ)HTr+)R=Bee8FJ1OV=I|xdTun^S=~HsQ!jW84G3776_hQn z@`BafebUgo2hGY#<#$mOyM$j4hhzIOOUbLbeCQr`nznpm0zne4|KVGOn24E>bd%H_SJ}# zd1zF=#E5U1umGtfR-f4UzXV-rk^V8sQeHUnten*M6;Uc6)d8o4LIY!>(_8urF}YKA zG_}6Dy-5zjh-ooR#t38uMd%8{%_3W6kx<2ISP)4-R7TjpOQZmKW{xc_h{`rL<}=`> z$+za7(JkI^V?ke;Uva+6$Qu!F`ke@xwZymeAT(v`(T&kWHv31~wv>WodYlw;(9i6C z7g=gqjDTUh`!Hx|wWvE|@}?HI`(XspQ)v*>@Z#)HH$cT0q%n&P93P1zfz=1J9&45e z_XcRO^bR&&?U|%UQ@DSNQv>@US{S@u*6E*Sz&(>QHohXXX=uO6&vM_e(&z~WIU_A^ zFOprG+&p4X{;D!m&E;q3qWx324tv-zmHDMqhKcl!eut#mg&zjAfg*}|f!2!6l30r4 zlD`#qgv}Kn#?cLt6WvDbrRfcD3ob^N#b1-*G3{O&ZGXZVLjN%wHH^27v$_-%$5118 z8odRsn&2b`w$%j{naWkl)x{@7k6cRv%l@m~j-qet(;m&Y>*TNh-Q>rV`tHD;zTcAZ z%@I;(6T0?NN9cNf)jI)UX&ydo=(>^7pySP@aBqIthTEV`Ca7ge-hQ`jd;qln+Z1GA zh-}bs2L#+_)A8zgNL_bv8=Qh{X#$}$$=e>Uh%FlKzlF-ufY*Syt7IVVDoU5G>lyOp z&hivw1E__V3k0fASM5!R<0{1{R@zbg%f7u0Z1E!3?}pX1yYoMK5bX^fLNZH3aE za%2D82(#0i3t*_61$b6|QRc15!zb$nFsp^Y$JArtmi9$hjzTWfk7ayJ4cm!>;?5B> z8lFltj{Zc#Hr(embW#KIUduuv8H)K_)y-P%C91gUS@4uC@%RmXKQsNlk(c zDF5Za_~?5n?PdF9dIqwY0lXiej4}m?^Frs>2Py@1lk>q%?~{(}|L@BLwFPBJ^mx+1 zX;4C1om&Lrb{>75ly>Nc7F#BU7CDg9-d-Ec&R)9~7SA!`%T|(N%SzZ1%2Y36%T#IO z5rGV(=0E+hDk`AyUlfW7#j3?b5xJ8>#Rm4+xY$Nw>|*0BhHaq(C1FYmL{gCVH)sIaH1U6B6!=wD1z8eSO)_BL7 z?^_4*tPBfBVmUI}x0KERI1Y}MS0EKkv=7mFnYVjj?azB)DhXAS7EFs%ukx4iwWPmeP4xjQyl_1G;!pABdN?rDYIF>Z^t2Dzq(ls@c#NGqn{cte zeO^{f+2l}Sv5V4BkdMN&W#$f>0{;J$492g4|Hej>l zoP0m#)E#hD1tgSJ>n0_=N5i(Vi*=}8JY%Zd~aVj@7tK1MSK0gv@ zKffu7H-953Tn`~^U5_)4WvHF#SB!lY+1&6s9&fmk=rRV1nvqb2clVp(Pf@Tm|JX|7 z{LfIs$v=7>O>)7T@5m2B4<<^_#e}(Cr!&i$OrO42GCs{udT$`yN0&zD?AM;g3qTlM zUrrT2*9*v|rZ@13!@prWd>OB$0{H+e)7Zf5l;y68Ky4GJS2W`n__h0QXa*gwj^kdQTU%i}{#b*;{ipS41n zx(=J)*3Uf~pV!Xc9b5m?oSC`$zA|I!+d$KRZCx<78xPsy2LMBYeLl{6KCa|^K4KVr z=23k%q&{}OzfMDd(_qQ>`9+vxd9I=-N1Qz6`hXeAp1N@Dl@e)37+F`Xd%TnB z{oRwp_o&sG6yyrg#7p304*gikG_P{;=>y4k!9jDf90&;RoDvftM z|E*jmkltQEeG1r_KzqLY%un1Xtuv5+DBS3#Z&u2m1BOx%|MwTQNCDYk=N6F6`Nj{DAyP ztM%W&p9+m%Zv`dKuP%fc1oyZ79@o7`S4#X^LJbfZOINNRcP85c(JK{HE)`es*W-eL z>nhJz4Y!B;Kcs8D??x&d7sJ;q_&8e+=~`}2@jvcp!*yJxj#?7AfqrDS*QK4$vlUA< zzRh6&4SsrB{#Qr!sV=>DE#dm|7744ImYDB7lex9a+2p^B>=w0RiQjM$_COmn9u&4u zo9mO6e*egxz671wxlXY(=3?gn{Gd|#kh>@8oHPp|a|6`A z3hd{4gOFEygJ$x+5>-Z1)jxdd#uwOLK}Q`uV>k%JVEG$nYPA|o>dTivBE3k2BgtX8 z^W-9KfTSOmnyGICdhQ;T1XF-YTi1S~{yXWFwFL%;ETZPsC8@1awB`*br#BG6WgnIDY(&fX za@b+N9eb&Da__K~3C!-8nEbEo4rpL@$8uqRC`~AWrdjTsd?X%$}^#q6;!5|$(XL=>BR?-lmJP)cmkAv>5X&^-xVWJe=CdmAS zU|L%s-HSQTt*5|wc;tPfq$rFgy-2AHi85#>NgP?#*fjFc6N8=0gB0;;m`|#}@)mA* zt%^^8wsLjP!s9`%)HiXy0X3Q)a0Ag-G6dl4hgbqx-9AX!4$D`)LV-}p4X-(hi8#*b~SJ_or)C&lh=ki zR0Qpk-*qC%(_NZQhiR8Ljj7ESTODaTb$W+blJ*>-rX>{0t(Dx-@-h%MOv2NpE?m`| z+|VqTy<&W8Wc9SR&dW*b9!#DmDxHP%G|y!165c5qzu8~AA~Aot=(^nFj1W^**eS`s zWxw3>4ei<2RKV99Lzss^SOc-;pW5yM|1NhMwf&yoaCLj_Q8@Ea1z(0>6tPD1`%PX- zW1GbCmS@M2qwqE-(5EMF<3wzm=|k(>`-3~j>Eg3>2Y!^pG0Scf3lBc9#=F7JgxHgL z7|H;0pr;n(7Y`#5pVi8-HYV|l+g6sU+l2OJI8D_LuNbIq$pL;0+?f%x>Q%oj>w6cj zn;gV@Gfl93|JVZr&-0$wWQ;c~B?g`Ae4FlC0i$Cx=sv`sc%X&-i@o zYN4w#*l7F@@3$>8!>cjFtNM|Tm)>p5tsgY(zHaL40#AbhM4X@z7irJcpDU*>F*a^T zyugKLJgph_r<1EKGV5sM+N2e8+E(mQi@t#EsM=$@Z6oRa)~*ck&r^=`%;DrwJ<`|Q zG@PZ^87@}WpDEpe@EU#^27&LoHjih~jE<~Ye5Ut%eDPqX^7wg01HpDTXS$g~0ABdV zK+o3zp>oS|wPqJM0&M$8%S2$OqwWR!`bbGWGv7hNkyr+z=_kg3@3S8L1=@S=az?5Z#Km$<&_@QEE#R z6zCW=sdCgjadA<2S?{QWg`c;{=r>)b2U3Gsm%O_NQtzbm6y&0@{iihaEhj~%Zm=cD z=f?hVt^=;{dUF|xDVV{A?E8^w9k*t_5RM`R0=rifp84Y{hyfQRg7t5|IVbd}3=-`gvHWs(!>Z>wGd9Pux2B zi+g>4Yl6BlPi&e+clw&GCt1U}8a&IB7JEc*FH*#KFDcy8;4i72Tm-4p+`MrI2BkJ? zyIrm7^0vm9NAe0K)n_eng~vIpkl&Jw!^h51pM=$)Pytl_c84JE3KlloRIr*8xAOe2 z(pU56av;mvJdg*CO;dN9wQpZNdeXL51B8;?$b;)h(D=bU1`7<|1kXiZxGmh`tInyh zV8Z<3gTF%{GVyFbcTt3I8FEksp*0!XpZym~?Y7ggUB zUP%|M9Vau<1QTOo?%1}CiET}6W0Hw&Cp)%n+qP}{?C<>N=D(=!Rn@h+`&k$DEY$l} z3T-`aG`mtz=0ov%%mRkGiBcf4!RO6g!IK(I{MPYBpjy@j@0D*LL`YrR5zQur_r;Uv zf!ZJFb2xdi6D-T!lOmVUl;@?Qf26OKCcr;=TqC4}t8l7lC@6YXsDz*R3a)5xG@8tg1E*rg~wsY{@tJK#v6e@Sr)Ie*AmRa4TW z>Eyk~@#C%i1&Y;=RmG8k+pPLfp8Qm3PRxo=>zx>bkF7DtkWA$Jk60?a9!e6-UX=LC zu}0#jKlnos#t7v}z7EVQ=?zed)OQzKVEIqsKVa?s3~`_XV5Oin_6fql*E^4!cNB=X z8aYYfF)s=2!3gG{EI)fSCL(S2fKZ)52<|_UGQ^Sk}PTt9I>RL zNSqAHsN^e`iG*095A{U!>bHLBLfX~kh*OoQ>X&8M;VyumJCM`P8msZH1_*7 zIb!hhqD|=2@(x;3x5r6%!M{kpk&9#EjL#=A2RWspL!lAbst0fk&u_9&Lfq_XC zuiD%o_8!l}sAx{Vt?7PX&bL3sS~t1g^2?=yk4UWp5w-~F@_;oYc(lPg@C9AK5fl13 zB8a@l7*Db@x_#M3X?kv!p^kp`3hN?*HjP8H3wn-=3ARatmheTs@V5mnKJDUG9fPCI zMa~Qh2+VoHIwKa_sefQZb~QSH&Lwjv9-EXX8-jy=L2Lyd`lqE2_!|S)`=yEW@5@be zQrw9ly8C>a<9dXB!>uxNtI5mp(vw+z`*-rYX$$P zIgL$O=v-Q6^o&*ifx`P@xT&(c-`{IC(Ii}B`9mdFRC0?X z$xbB`#h9Gg)z1UXpU!U~;-{9p0Dhz#dv(1)r<2wOImUv_r+*egZep^P>K z)B!L|hMPdFs>FDoKK#nym;TQ=LMzY(MqX?MLi9n_@b8`z%>_GwpTd6Qc`U?Im7vL5#HJwc-z4e53^d6=polWl+fEPEFv)(4$Kg4s( zME|l)s1fjF${1Gv_gqQF_nQTg+CEJln%5mG0+&>^>IWeEH5lC|Wb9#iXsBp)Zuju6 zYYFzVH;i?I?65rqOO4_ePqKtoJ(6$j=w`Z}bJY;M+_ zhLilI0?IDxJEouyGoQ}VjL%$2A6sQ!Pfwva{Nb*mti3S(_oAlaZr_@@Zrnl+o-Uiz z17u&xflDi&L00azFGby%yh@mMVa2f>3oPOFxocTuwg&ert98je#({=4CDN%btii9$! zpr3Fmavezez;rMYyUZ|g-mm5LbCB){Fa03HQR=N-q%?@f;J{H2Mb))uYW32mm-6y{%s|}{{th@IydvLgW@||%LkAR6liz3r7c&`-$j6&zs?NX*VxskK%5)2Cc^A| zn$odptNr10CZxd(E>b_2)AzD&C^1*MHio^xUJdGRJspfX(d#m!a^qq5da)UqGjE01;CFFBaya0CDvgn1HiEbEYzf3{_7)DT} z9gNd9NUun8IGcsulw6!v05hRRrO2xAu(6r|CrbvNf7+%>E$OWJkX8jTfEEW8VJZC- z&z%dR;E^0#RYi|XDgpkhtMZ+HKJo9tAhEH16`ekReB6rUKA-v{v^>6Eog$~W*)9tba;|GqeN9A9v$$M=`rzJE5)9xVv0b{&8i6GRWz?1?HD#nd2O5x0k2BZ86kv%?VBpd3vwx64auy z_r6~K9S&Xcq}3gWRojx!o+WmdeM(pP4&F?@v?Xq(BP^NcONG*xWX@9^kaVWrT5|Xu_ORMbng6bC17QXl-N=PmzeR!%}@g(kQ@TA!55wC>V zRFKalNNv38#!%v$^9K>}z_N9NdV=ABtgTnw_L;lz=UQu}R$iiZxi6Y7j{AH{apiCky&?(Uim=!u*I+#eLvOu#Y6E5&BS}4pJ zD-*#)o0>PWVqB*Jv0T5P1J8y*$(E>d+p1B3WG!o4HMwX1vdWVcbG~+P)6DwETV~aN zt(5E{N9xj}DKOU%UyF{bX^O=jGQGaS+(A2BWP*jSL(uIk>Ux&?NXM1pQ;2hYwR<>Y}A&lpXw6)bKdvulVveMasucSd;F(9hXkoMcWEp@2Mu+ zm=hOy%6ran9}j0p7PYHmzu%%A-5p`$i#sQ6wO$_t2^AgFostgv_B|7%nc}3do$kZ0 z6%J%{^5Sls)29fdlLg{~`^gIg4H7hkXB^41pt@38U;TS)`Gj)MFON-dhg}|6V?$(h zXaL>HQSRn^#I3q3?{ZZiIqNE%vYOk+3GDQ$@ih-x;y>)$OUBu+%edg)cvMh2(7rzH z_P#ElFhIR5){G!kUnM>592Y#EzJl*}m&i45=;k@S|H`R5W8 zDjdZtN}}nkyeG4!%#w$$H(Em%l9h*|6>Dl;odmjbnweL}=Ar0ByN8^6hKV_mupp^} zdippef>}HL2-WR)UWwKv(M}1t@?nPmt$Mi3Cm=jxL`FLcHZH=6!Z6yFk%OXWnHMg zI2YEGyutGL1~=CvvKOjGCm@#MVkmQ*WY2$G_HJR^b&n*JcStiO`lvK@57tMW2?s+X zfV@#-5l2;>81_WPg%JJt=Fw0HCL`LX=_ZLc?rJAQOU7KfUlc=*lutDdoeCmK1`LK|GE@qRTRO%34Ro8HLOKGM&mJFI{r6rSSmrn&Sx zRUq3*dh05XUadjWinKv;S)oBvUAcjCMp`reI7c&HqQFv_lFG7fMs1#4S@#M-ykW(C zs8M-|eebEhE*F$0wrLGJH;xVHTGk$BQ8REL_$e-zgb+8Z=zim`fZ2A0jx{m@E6y zG1O^VP~IL^1^%&Q(H7V(FgZL}%Ri!8C9JUxGy)lx1ZvZ35_p4i?*Ro{jXca-esOQM zo?K~OGJKL}@Y-JP4p^|!V^z)Qdp5XWXt^@D{1sENREpjtIahr=R2z)<^pFVL@Z#_> z8Y-3bM`ucm4d2@mg(Sdbo>&lF!3^3IFi5$p01SzEeitK$*F-tgKD#&-U3@p0Z1w0I z9>lLClBHZ7xYb$I;)K{Xy;1&2s3x7+((UHmuSh}DXpGxa_uSk#;PJc@{pWflwlUtxsYXlUFR zAshwJYr@wlbhJ5vKGdVptL}-?-1Omxwio@S7`xSXy|m97(hq4HOBX$~GU4zQ->rSN zPw%ZRVzaFne7p?f5S^&r`xTiMk@j!VwVJd^9Sude4g0`r^^~^*>YjHV@ukka`05gEQE{c+F=f&X z!eey|5@8L&-zXa)Z!PxEOi<>gb+W*V_u_ zb?)cr`)`lkn0z-vYr|Hg*CA7O!9hCVHWz?Un?H91dSpdU_y1ZMz)um#wJl@}Q zuamk~5=x{=Sbb5z0rBs%>Zc$0vdKxC3RjDRyt$;ltF&R73Vv}St4Q67C!s8U`yPQ$ zENX7Z4rm#oqZY1O2AZ~0Z&vIGhXp9ELl3L# z-m}JRk6HEUfbP`MI*sT3kQEiL+llkbNjx^(M0wo|#0iS2$;G4bjXKHKguoRU@31lJ zwoPcOrHzho{i`Fszx5yCt!;q-QMzP8Y4MZRPB9PpqlPx_XNRo~e6NDoLpfUhRyv)fX4-=d!rKs zCNTyV7fAZm$NGZ1JKk?X7^J?HP24%ZP@i1nQpPhc zS6~h4Ye`4{8QUiqjIg0t$&(Z^2{g)?{H@qMKc0Ma941^_Au5{tv88~FGdlK=Lx)5N zVcBT57%CGdT|qbh1g4%&T0{}HwD>PLJh$3t#oQ%dhN2~J1#ioa-b;M`@IXq)&BsHq zl&<8ZRJ%+7M^)k`E9poOKK!t6E6@Fu-eCE~L&)Lf=7o2QJW2eBSmpg~eL2`**PB_Czy46T?C1(4YJbWqwfCvk};} za!SXtf@}I+ei}7Y>Zye^fgb_ra$xg)kxa|B26#~%{XYeY?iPn`ue)!FBqf!O|X zsb|S?%kW#=xue=$rx8{Rp&5%9b?CU~XA%O*Jw9=&kU6BKO)Wl(oij_yHon5uwEHeE zKa=zEDZ!ZfN)9y6jKHh}#XRuEI3A#gKNz|_=9MtP)ZuMsrA!u;!vm?Mz|M$tMDg_j zu$guU`toTw{nvGAA{L7-FM@`F4%jBc^c&coBnm$I=ZL~56N*1rHhK6MllWqjlL9)i zm|9#bIW47?#pKQ>zIW}k)E5{QE+_9BM1uDe_v_jAV_&xs!8Z_^nY^Xg-AJiDw=aXpt&z9yC( z8;*iexM=HiGDDH=%7)h7l7*NsNO{{0tlQO#f2G0$bh*3sUKn{P%Pbk*I)^CMNnxnZ z4t$L=uG>DJX7`8;P|f?r#3J-QG0a>MPlz}!3vAsRscg>eQRk`%6}3@p=_{|D&%T{f z6)|>(>3rC-v>!$J9kH*H>he||u|{Ok9sb3ksUaLnNTp!5vnu%faqyBQYL@TVA{o>u z;-gYer<;-EQuq;sdy?ard**(c2As7Pb2JfByX;+^JZ7^Q$?dde*DN;w9)NHhO14-% z#o0ln6d~Bb`(mmSw?EQ35K*#LV43*cPQB)~C!^w}@EsJxqVOTWL zCrhqgP*nVuv#4K_*PM$eT&U3Im3*kfqg0-5eFO?$6%=`1x;~;CbZh2%SvoH@_K3B8 z?2D~3u2whgz5zj`C|07cl5enJY@;vAWk^@9jjdlm)qScF@OmAIe$WaeaT{N*;35R| z;cSENp40CE?b9n43RMdd1yquQL+pweAP5m(LG}%RAvnI-E;2*hGkFSA-moUFP~XBz zo&|o6eIXUgf?Gp#p!v*5hBylPWqH@6nLpWC>Y8mIFS~}~?%cix78j2e_$KvhML&~Q z{$$%Ji?w<88em^MQq+bM)>))b7s$eJFz&4KuR*&JHA*ww8~bA*snB6h8Th2x8c*V8 z58`npb+_|ij%o8x_s-753@Ne1j^8et_b72v7T&&5Qb={A7^c)y9n7~>vhjzpxQQ}M zx#)d|exQ3(Ca`0WY~rpImLy^4!MzK<)ATt+oYG1RAmX|d(e~N=G{%H;5f|N(j{iY_ ziG7u7>-|uidBmn??z*|o6Q5|r{gdP=9}j8ZL%@q%<4c%d?QFB0yX$>@D);1tsprX> ztUpz9ph~7+7L(jSr%&Jr)xNJddRQ@?!z(^G`6=SEb3N~-Ac+ji`j=!voyiF9_1#5Q zJ2wv={ZpUY=dr_hpIGlt6g{5TYF#^wu?G{vnVsC>0?7&_U%qEdG=CqPQ@^M5sm&lS ztz%x*j(7e8oR>yVw=FPsnGw<2%j~;7s{M=bvqzpP{m$*~l@EPf z0mt=ePrm8}50g{2Vf4o;!U_ zX@ezJ{|?iuu1jvIQOSj<{ti^$;m{ zzB3|qv+atW6yY6spBqB>c0G+UfMue2+lEXZgn=^XHJo}Pv{Ec>U{xVaPCOoBL7P=E z#r52dE;wLRX(qjw zj4ytuEpI~YnOOGlylwuLVb{BnjvUxkivIL4a2}$8I%V+nI9!yBk!eac{E~U%NZ-M7 z3=_}#)cU=JBCYdB$e-5C^PWD8OOr$5jirj?^boMCZ9i@?fpy2(wv98#p~l_moKWzuyv4pcGGd&qjBzg%&yP@A z!REPqAt*&Kp;Q>oYv2%s53Bvi>`i8J? ztvv1#D_=w5FpkklbS?7%kQp$A10PyEI1QM9R*r`V zVt@^`K2%xH;XGfsZF$5S_GDag>iPX>x(IJp*f2d{m_Gg{=A-CUvBbOQ_QE{W#QiT* zd>y{ux1Tm1N1=?tg-5%?zt#=$ic1=lO%()TLehS_a2At6iuEXqs9}pzdtawQ8k?82 zdpd+6us@6{W0?QYS?|P9LJJqiJ!%ET zzPDAeD)~^WI|D{j@9!qxj67O-N=s7b>ALu8B0xKy@b{rW*5eJJ-k`V~faSN)YFLtt zhiokKLiTRV{k{kr6$nW~rCYE$KP_UabTIk@c$;WH%mJ!lkF97*YQ^l9G>*#1j$2I! zWqlM}9>c4WYS^}75nN7s;Q@gi}0O zf9x1ow#9V#bd>4xsZ7SEh9aHiWW*8|?9Q!j7@!HCx8Qy%1#bEEUgMvT#$KmoY;gye zyW+uKK8NZRf8M0kU5{NXE!_sZ!t~HR+4mf%o-`YL6;3U&q&?|xL%AswPYiwyiozL(`^4HBo_V2yYe*O)}X&2(oaqHGqJZvRL8(%51BxP{vB3t zP#i1F;AgVl6hs>?9TG#P{|1tt*PAl}3Th^#K2jtNP{Hl~U6xxY^>0*FM4)onlu<%S z5VJ8R_U?!pv)QatV)8R?tGAsfZu!V_&+FRV2>UlmX2Z6`Cc!Wix28X0XL#}><9{(z zbAAh%aIi45`eY7!@a6?Xf73G8$h+^g1I5S;eh~opnm7PFd?UTb{N}CNB({^tY8jOQ z*VBUrrGL-!!-6P-kn}-%x%SJTHs~`c#jNx1$~a+f(p_Du{Sid!zz5MEPQ^hXw8?$| z1-3c@Y)f!L}xGn*NPAzCpd9HlgbD`X{y!L#$iL9^MV}}(0QsnE=XtJ$Jd z6bG(-A4dbY=KlIjwj9~!XlD~14BKqzA+6>w&t}g2$jET1kIU6WGEC1%Xv*U zh7z`m7l2{aPtfh%-^t2ItOeKB8PcaiQ$JiW;Ok9u7FlnedJ##hY z0n<}xuGqhI%PL8KMI<0i5ufoY-Q8Kw3(xkU}g#471jKe_%gF`PlvCF zNO`JM!FxJ6tNzZ=4b+3V z;YTnrl?TRbk8*fUjQ$zjMcbtq-W>2ts<! z#@UN@9qS^R0@=c^0h7%K~ z(wf?w{`DOW2rGXFwM-%xTx0JHY&v6Sx9%h5?@wtT9Omrpj=`%%b5vmc!wFMmX; z9y+38UTD%OFY)ozrNR#Ma%C@k4s%VM`Ofo`bU7(8=1>KE!w>{L*i&qrc{E6-(A~gW z>945)4vTb25|YRzRP4*b{+L~VImZb|$X-{*>mJp&3*7Xvv5pNb?2mRy10iU=qE6dz z@b$jOug`c_S{Gt6cEf06le>FQ`K<}h{$b~M)`CUVd}gen8)DzeNz?L42Ikr{A z9{zGS$dJyIn89Id-Pj^|aCl$GrHOymmU%i}ldR0y{hKfuFTcRB*@?jRjmv`pt5 z=pIqw2nVw@$ah^$IPCwaRPb~@hU1oTlJ~UUKi(){O84|o+1y;&Js93<0A=ybgz856ATU5+g<}TdZXMh*_c$=hbK5Ysc%vpQ56ng7-p;Khux;lXvbHAT58dn|N z!{F1O8tlYLS#O&{jt$)gm1 zI~>O-#$21!=c--kGt=Cxx|IhMJ~n=}%Fa;dMz{W;kiu<(PARDnfVxa#ODPKqsPUN4 zn1gjojCp(J%^obK@1FOMA5y4BRU$k&y2B#)Ep(p$<%MlD2!<;R;f;MaEagYh%6ap) z%;d*|<27oGlnSN65Ktl?fFK5wdjMk-|BLZ&-}3ZTJr#>@c*(-92*x^A1Qt-zwG?rg zoN`oo)quU3&w??CaUPN3sQfJJgYOe@nyrITrn;$$;z^9X zO$URtoB=6Zh+^Q_9~l;795epI?{a^JeZ~nhwB!4*phn&k1IJmM#zPtWJ2|CrhZYYX zU50J(+cSebw><3WOVPWdFDtyF;iRpd|D;SGtCow|<%}*j5cyfyUzg<^?$oceJnuk< zv>{&=u~yeuACjGrVo} zrphxPmg_TI5ASQ>0`H5mWu5zT#>>t5JzxFq_*r~!w_}eG;Qti$wL7&SnKNN#&kKNOm+xEF!WDmK8s}n${TniE zHB?Ien%=aB-5c_I-LZzt8~jf_ySHR%?@?<7Onu3+=&!$_OpW*DzdvN%x>$x93`e%# zGt7DpIqv${dA9ST*J!sbEt-cm|J-^m<45c2Fo!(#U4%#jzrSsuZgaOR&pu=YHLf9t zyFPd6avbodc%4YxS8Ld32%{N@|XZH)xPUlvKQAtYj?F(&#vu( zgOiYHDKqc3V(x-hVXG;MoeIQwtCSRFznX|yO2WA#gwVDi^TOpuJid^@rm}teG5B*8 z=G2G^d_Z5gxR`biV}Ee7kuc|9ZU|AmU}$cSb&?9aK>j#Ctp*HlJuV|-PM`5Fk$*N~ z`8L&jTo4K}M8$~I%;ZA#Mhb!>m*^r(;Z#wbyeM-B?J^4IPa+_)2q^FWQhm|y|7s8@ z4rV}quynuj!015edpkf@u)cDc@=*Q;JB$AXa|_lB(Qdf%MEEp<8i)smD9lid5ojUu zaXU{Zo%k@xZE$DoDTf^7+!rJ1KldksL=NlcncsBIaWkoCw4)HVOll$G(GpA0!EFlfdY<( z9I;kAID16eD3T@HDAbTv@vHM)YUsd0nz-DjZX0Cd+$EU7qPT8-8|i)3 zDt->{iqabr=SD*vTs`*|CkrIMo%s)+hk4fbNMV{AO;8q=syy$|Gd51_4F@`fkyojD z;e^7VNJnlRu8n9Ldsw)OnXUr208jp5Kyx?({7r7!uU$1()&3RZc?p>L_t@g%R8r75 zOU)J)vV$N|!dZj`oAguULa;9%VySP=!VOiC?0-}b)Dr3w?oZ>A4uQnTcv16ha%fkH9+V&uqZNsP*ejf*?nIn zESUgFlJLobL{*ctAbi|ezTR9z9k)vk6oW{!Jk0EWa&B_x@C zNLZ5flxn@PXSsO~6DbOxR)zV9i`q;;I%ShC2-f?2FE+ZgWSd{_qXYR9Y}&9yMPNpV z3=qagZ9&ANoEV@KREo{DQGDuuEd78o3a28Tiy-_HvtLssJy%A86>qe>ZZWVqElddJ zP5ET_2WlxJkwsbqrSLIjMnP?A{44PbX!M`KBV(>Gw5an0LjJU(FEx7NrO@Mh(8AOCV>sx5t}YR_s>;>p1=Y~i89F)`R-gga z_q&wR0$WXP1aqOa0e8|{RnnR-)v^0m_9duc`02ug-0^o=rYmGwkv|-A^CLHiWKuyyL~Q`QF+ER!j9uE6%e5itVU& zX71PYh_Pl8n~%m|MOP8!Qw3Pc4GeNAec zwQWsroHbYca#}@n{&`1Hn`*o#M49P;YRa%qqiZ68)tmK|GW}N(_ ziGi8SiZ}jJhlWkqS@Nl}!JCA_V)6U>kxr{?#gNmLChJCeRMX|;z0D%wZ3m6V8?FpIb%09aflQ}sgLC#3;xyk@8(03yP=DBDJ*~;Ey4mB)=E90mC%fMp zs3S=6!@uhQ-#GGwkGSoypP%uveK#6;%5s&5X3XYN{nq76o%vFr%sKH>79b`~;ao&X z+`z%WxnS1!oOZb(VT4xX3EGsfMJti)qfw3_Q#d3_7qk{z6vd7!k72k>yF|p`2YR|{ zsLys3jmH$ApI9!S8!M+{fhx1h+Gh^FU}P^Zc`n-;Cf^Mzzh>zeks9hVU`X~*LJ^GXS7)^&y^Ehe?0fKN7#*Gv{Nlhj!;~&0s*DC7=(#I@ZAIbT|pB-eg&mxt96>x$(o;czHLD+4{zI zba#G8p_8H`hDrEgjxtasS)iq`{`aVqYPrJWsQ^Ji3u+P-(eD$%&^oEcc{WcU;BvsI zbePRR?#4ndRU>PTl!3eB@I@tAPb17cubl_r7ttedrDk7Y61cRefPB*6cQI!L&Rw_! z(JEa9ZC(gE=`XeK46NnL#tR-Gv$q)6*79(=Y#QA~Lxe4j>in z@}(hWt&$0xRhzKrlOSiqVsp4;gB=Rcd5ld6**++Du-V^Lc4QSJCBg9{B0MSKuIkK7SpkE@HtL%K%#`!a>Qc}a(ms96B zW|$mpDPB!40ekL;%5q`j?PG#Xso5jECKj{x8b7(hXp61cB&W>>?y)Z%YzK52*y5{t2!^^KDMJ(QPmW1R z6Db`ddq{-sW!s^v_nYtIs`QmdU`ft{-N^4Sro9gV^Iv>-pr1bkL=67d09hiY8UOcD z1y-ZZApl|9{s2+7Rdpxm^^BS@RQZD@a3zC%k1-@>C#OA}nxI zmfF;7Uqz%9eOA5ReAXr`s|4a_IFRPs7Em>j3Fw&^pezXrW$B<$wx|Xo4FhB225!3q zYb)8|?$J>-XZE;KHOrzVp|8`?X9P$)h$ew@M{t*=u$$*F-BjzrGjFO8*^dvm{dZt~ zCG8m`u z<~%;#xtflY3vn~Hiau{a(X65p{ue9{AW{1P6wOX(Ziipw%?lX>DPW<|63p|eJ4%K9g-&1?WRWrFNBGsRLuCtjJ`lsx2ng8MjIJl- zh_fFrI@PGJRDAynET3Hi%A<3R9F|@z>0{xU2W;o^I&l$P-=1u^(u!_{oL&yyVALNE zOxRh`8@b<7Tg@h28L6JaC;)6hgRvu)IsF#!QQv1zunSqYZ9;SM;{jX6eL zMsVOi=vtJ211{6{|DO8VMPA~Pi|0E|aXz!KJgDzr1723-jL0)OM_+XRxOBpsMF*KI zHuEbBM_!H0urE3{w;yEP7Ve+Te6(|J5$h#8W>l5egeszbAD!Xpn)&rts|anK)i}U# za?+;qB45mIp@X{9lNH}emrXf4qUK~0Wg!lVT4&^Us1G0Tah(kru@HIu;Pq=9KRY-+ zeCsR?-nwoKuGv!R?gH|k?l?_L>&B*!_wdg5emU2MwKab9x#H(sTjrGQsMVt6V2+3N zplpMbxt|dP4qRa})~^Uu9RuBo1)BE?=}z2=C~3GF-@;=y6Qt2I_sg$3+bq6*9XQYt zlRT}M1A$# zP@IRX8l{UPZ#inqBgnXLCBaLBHQ?{fC4Tb4-%*I?$9VX4bf7*TcsJNtOhNQLE*S;M zFO%w2GES&7!Y+n+31nS#6y>b}anXa-SDgsbUm3|FPbR&k5oE}^vhuPPf>b|{*8RxJ z>xXaU0k*Mpui|#>vFa;RR|xy1Zt1ZvMom3!ruYKhk@)@R{abO4;1VM|;h{%~fDnqX zGkQRGMp9UK@TrbybX*WM2h2h7e4vDkDA6c_3QwMlunBp;u1ZW^gv4K((Q1`lp>o*) z25Kl3mi$_w16ER1m5}^faRcVWIiB_Wwq_jjy7X-YWnLLOuyc-{+|lAj9Z_Tt+zT3y z>6&BpnZQk#w=jaB(Z`>>E7_^r3Mg@FYWMqY<;ND;ZB@tqfq&Fy;^%7eV8x$}M3Ir+ z+QJCjpnou@dS#BK1`s@B(K?t($Zbbn7t*07h7X2HJZ^6-W8NcV55;V;J+w(Tx=E;? zKKz+QdR3Vvx%Zjncr5^?I*9^%jZeJUK2%&FMS*D0vE;S4)>vDr_iz?2KY0TKfc2sC zz|BOeVFb#%n4Vv3By)KPlH?ND$q_$mp6e~C;f>!_OqXRA9e?sthgiG8Bidm>cne2V z=;9mY2R@e>TAOwDZf5CWOJmJ-c6ZgmZGUK?gL}o3+BaE0Z(T&K4R-lL63(`n*q&?+ zhd2$5Jgm$iAIZ}1XpRBm<7!O4EkRV%Bcm->^2q3i_}MTuUTTqlv&O9cxlE)_Gp*D8 zTffb(q8kZZ%fAwQ4F(5%%{W5?qVfF*g?PUxLVx$QlfZc&8MjAL(=V7YxxlhJJ|@p! zQ79`|0h7h5JP|IBQG*zdkHG05#)gUQYOD{ckp>wp<<7?@6-w&z|J3DN9+?~~_Y^;u z0|N-|erxE~@D(ZF5V6^$NFgZsqQ7Lbe5P&fe^+QP#`=yyM~!neJP`^#c`=U%2Jj9V z?YEsg5yPy&`-7Z{j(oE?T(q8e`$}X4z9?L8)#in$b@ZN^KKo_^+@Ev26m8!jY~b@f zXl630%>|2G!HE!5aDcF1?KU^WUOaJW{(SrQM3wUfjQt+Bm9RNb^TN@alECl6j|gcx z3ob?z3%MKE`lyrlvpMQObOzh~;eGG$xCd^){swGlAsKyjeDp<^Ue+|J`hS=|L&wUG zYh&RAFkLNS-yeRw%R^W7M3LRw?>u0haQ2mu-2{L3$+SpSu?ZcB_raEhQ_#Y}9_=Vd z@U{uCR7aOLXFEmma%>XfuxhlqzBS%f{DIPXNv(v|dbyAb^chQTB_PyZ+;j;iRD<=poTtmah~*fRI-9OL&} zA+>bAuzL$XL3?OT2>oe$AC2-dS$rlP#nxrPQu! zsx5b!!erNw>AN{kav!~f9Ox+Y#+0Tx|xO%}dxpv|E2)>MqL??2Y z;uw+wuI8W?m_YtW=d3P~ggK(o^kEjU{$xFZ%-#lU4kIwdA{zp$JfrACMxrJe_ihq) z`gaP?lLS$LZa3{(@$^(@M_k8MLp`46Cog>Ak&&qdYnL5{0jS!*!2d9IPEnFH(b{ff zS~G3;v~AnAZQFKF+qP|E+O~~p+g3II`p(6&k_y{H@9)=Yvm8m?XsEx)g!2JzjJ z=xzvluWK$$USt0O3*2{1vDeKz2FsD{@NjlFg_!1AM-_Z;S~K86auYBeVFoTFHv!WT zGawPw?6TbkJuvV$dP<-KdS!%DYL6H;a3NVBWN*4T z34A8&@2v!CRBt8bS;S<$Y2G_cGq_{zHbV@!?OFUHduk5?#-B0hZ0uKDHkF)y?Yq2K zIB}3SV7hSIN&OnfCBXj0fBh}AE@9A1xNT9<ciW1bh zf?Zt|wN^5P@yaU##8oYndsm!?y_ul;kJMP%f5<}qt@6NF ztKHW%m}#xG)7qz+(;enEP`abTsM*)0Yv1qQDjsfo#HC%lvfotgT5Pl2qOTF84jwgH zjeLnkcL-4Ha(@(bv>N6v(wt0>z}LE#aakNV4H?i9lC91!Ss7x|O73PzOSMPAvC3d? zxS)X6f7QAeDFd-^1)!qT2GN={q;YAQMG4%tmjbRPnXTP%g> z$YM*0Zn|iuEYCh@rVOlIEKPJA17-9Rtb@~vmI-z+)PJB`F;st`s~4W`)JWBx?(B18 zy)mjnWCc#vau@r1+e%kd4$o2-dsN3t7yG}?r7pNrg@^5EfYt*Ydaw7ox75rUWPS7j znG*OLt`g>>m=f*5Vky`HWPE91Nur!`vCMMWUt%;#4KmZp$KNGYE?FBHFfvv;nt@B* zjqq4dMs!Ss6QL$nP;H5_M6F~8tZR8P*;3=pA+p)2QZJNlHky;wE@kz{u9_S(dKn>u zbj4xjdw>54*MOdhE19Rrq-%1dA+B0OjjMXT*H!!8<`L|(dHgZ@-R5J~eHr3u1GR_) ztYKwK=M`oTOPXw}%(E#*z~7^|@!{O;F|zIfV}W zPY11xJNBP4leXm`lR5+y3;i@rOVn!rsE=@qaCuhR0*crBtkUh=*%Q0GKXKYO!4$J0 z!vDqRGBf^v;&U0;8U7DGmqgO(KYXqa2_(bF4LBx^!q%cJ>`%z}&H%kvA=WgKV0`x{FLQx>ek>R6KzwmzyHqcPSB0YzTKV3-QS|7KblB;U3a{>eF0Br z`guNxSr^@Z{q9L^0H{9B+VF(Ah4zNl!=sA?e&3Hf--~B?d0$VIc6=sY6#h*7-*7u# zk=oN9w=0eOwlygp8(w38M|OWtuD&lI6PN$fl;`uHm-q8AZ}99S&;Jc*?j5BiaRYH~ z1b%?mL%HEq2deHIFtuAGd2M^ToYrmR+&QiM|M+s$3_(yhIHAjiHR?Cp~|oY_ME9sZ}-j1hdxm?%u7ca4O#I@Mf9z- zZOQU}&daVC#DhMkpFr@fJ>g5b_C3gI&C-&bpY+x+y04jmL_zL{n)GE0df&u~(Nr0| zyrFZ$F*c8|a`+W$Yr0k?FVj@XlDr{xck)cs)T=l*5P$n9zQZJJZ1shD7oQ1>MtE=+ z(1Ixp^LP;)61*oNRgY3nr=z z?6O4xXVA)NPbJ_4s)txZ^6c>lxOeI0SlZk`jy9KSELE8T{8)!C1{zekgc~rb{$VW5 zTWrw-r(r*X@i4XUnJV=-ISG1M;bE`7KA7s`Hhx_iR-<|{(Rj0PfG+H|Z2-fdF#o&p zu^FHgS=hA@x0q^um&y_Fl&#AwLc?o6KRk!-1g_O6zr_G$&<#O`$@mjpul1*MUba{- zt+pF4^k8DZ@}Uz`7n}ytR>=5qbYq97K44n-Hwaw&s?{M>eY$g0R4`~}9?`qpzoNA8xTRHB9+@aj+%p*?YTc@{#%yWRoM~lz`{tgYiqUk-wyQkz4Er zH7@-1Yz(Nz8%6;?Dab&p+BKp_(*<7yYXq@CoTr~mC}1>ZACDz3FelM|y_DoePl2%g_4|Qg zz=*_%MhU@SN+2hu1-lZV75g1RzzTSM8n*}p=+2{$^Cc}u+D}e~eMOk12+NlkxiPb7 z**Nc1BT2r5@G+vw3yUAKF=ExQnI@OXb$FcXlEq$-7v+)viXme^WaIK@3T;403XDcm zlxuo6YbRW!bYrRoBi}ba>1c&9#K5JxuwR z@hwFW6GL=r46&-8z(f=NizJT~Ov=KVE||1h^VbUFvAE~d2IZcZ^=z*=+N0$o;4L++ z^`U~RlHK+Wb!l;@e5VcHpcRFrF}+^MlTr_0iry(k$Zy{#mlE!KCgV@>7&!!f<1bbY zfdp1S1xW+{L?TFH0aG>{CKk)!K>)&{c`;#%4+9|JN*0JJO+wn;K18v}^!cz(_xbvp z{sGW6zJJQFx}Tn0KAcs>PU3ermg!kiIWI;dsM61BDggwZjw_(jzq-e}c7 z3BfA$rVP`nWc=Pe!&9C<7XYD;g z$N1(m;A`#%cG}Pw7KK8OTjM|WGbCGt3F^oQ`ntz~T-SSLTXPw{e8y!f;DM!xuOFrt<^-N}e~_!QqE+Sc|HP$rYhx|gcA1l-jb0ssd_fD`waBZ65S zns1MHD}ZN4)e*rz*p>UZm>VDjB%$F0B}J@&Y33!>Uum@bR8q8P3ZXk@BQM)7A{rR}1Lp798LR#kV_y}{IeGLEqTuyD}wZ4wQe^>bNYBuDR2?f2mIcl|s6 z4ZV8Zl<$3iIy1-MPXSV2U-&Ds zcaNY)SqMIwq6jw7_yMo;Ux&^e`{c7*UnCtbp3+*+_h*@HOY+a;{j+%=mqzcyoEWh8 z_uvEO?*S(#SqXvAQ|@|qJ?8HReh3ja{l#tkfTPU3t_-Qdw2OPc|HMffJ;!rp;HOC11fsXyk!4r z@qyjfrTzEEY|MEDd0y(W>4~9?K}wKy7Z-U|(>vG0WXBOcWrXJ!6BxDPvs+8%M^o=- z)2cewy_%+ArmA0;{ymLa}d zc7-%PmyxuJ)|vGbxLh3XyD6K{oGFxB)h8QHhDmMvf_2yPaj!$ zxc&$FPc>r!c#fgQdHO+gE$19ULUJju9WCpS+_XEt4$I*1EI$K-36;!17|+LpsBPt6 z5*t?TDlfklSBwhYUHarHNcqU}VXEk}9DTwPl~)mvEQc z!qt{>wN2dEYgdK5{3zbd1(xIPrk0I|uC=s#*oGcv^4koLcTEJ}oclnRIud1?&)l)H z^%LI|D=yy89*3f}VO5k1VUmtoYeb?}?x#)Y`=0PYfF#gKX3S{bdzNjX2+h*oP`VUB zH+)ZJ^GHpU5GPZxrCp^I_55gs)``>H7yu$CeyF#J>ak!9td!ysL@x(kqX0g%q5 zXu(JHGmz{*-PSdrHKFCmz_`jY<)P%)1Irx+EuOPpbdZb{@_5TLpXDq~W!Xn^|wOOn0NJT2Az-tIQ?f=j=p? z%y$gu}jA=g~y1-XVD)w? z-X2}Zb}Swl**)|7KJOh4HkK$T{uW2YRd1}K`*z2bq1R0sgvQ9D*Co5=NWaz*cKzO# zzj46y)7zxHGaD85vg>sA_Q5xIe+K{-3OH!n3gy+ey3y4a9zb<_b%^HiZhVG)T> zF(ZlHIo}ey^!#_m3I8gqsMj!GsqUX3u}co}A{L}|bKe*-Z6|40s(r9;bpv(vhB<2CQjXa-@>Q zQJ58i^Awu5>_xSt4T-Y*7J~Uxg?v%tyZ$+kVt8K#dcmXebT%tT zHdEY7qiR78Gc?rVf4-KA|7M%N2aIX_+}r>5^Sn0<=-EF=Es}Q8qsdJAU)L;@8)G>A`yjKNPO<6J*4wQi;LXbzzJP>^ij(1YJLA))$vQVOSqPQz^p z_nN|V%h&n!68ep-)M4KBpcAER+Ii+N=sQ#?e&~k1*~-tl+vq(sf7;j|VyHJ4fG4lxM^_-8h~lqUZV|Xn!JjJ?}a)Kjor)P&Vf!dsz#U zJT9B8z70*D=5Bqw8O~yCCIU{BwF0wjli(i9UZl101%l#nMc7oho2{Du#%pgEIgR#Fo*m+dkdFAy~=h+9nf9GMnY3%iC{b3S(hVo13$4=4Unl@J7l4j5=`Lb@939qzK#@o-NrIB!^SJJlU7u|_#>UqHcg z?{1DdGkT@{>1P1?*$=P22PcasadG+5T{(1ildjnW-rPECj&dI4d3#=D9aP+=jwPS> zo}7A**4fmP^H&F|Gb#TbDBa8Atr1t%aU}>G9PW^z?!)iIFywmgN9RtNuDbO~weeo; z2s9!R3+ZtCV)X7R7N=1LtTA2Mm)-~kF5=ajVp{S1POL2hv7UUDLE!Kq zsp@+5*<;qE|3O^{fYv7JVhFvvqvoq* zl_|rl#GbnQ-8a-Yqdqb>1_9l~)n`z7#Rl?6QJ)Q0nEXN?-VQ`~!k1kF0{*$}N6#51 zcsVW?lqm=@I5}$cJYz^N&%F5D=S330%q38^)YHIaEWmxT3KXYwnTDwY0^*i^)C@$L z)2qokAXJw9G=i?_iQNJs8WA?qK0r#o>r4_;07pv3tkG;wJ%;=Z6^&_zpOY55A)d&mfla zCeW{~&W#oILC$!k0eiHIy7%Np8BoY{H^4@lak{ z3O?sqj8PJ%0?+vt6Y4_hgwoY4lW*j8~`BD~~O>BXxEnOmu6BC=RkijnJrz1k$A z_eO_KEZSiieN%iWL9aOpt`S>XjZREjm)6?QEOKU6H4JiTz^L>CItp}c+X%$188hIT z21>xYk+e1FBZ>luUUaU+eCVr)nyQM~V2aAdp0P2j6xwQ0%!J}q?!{)26Seti=h8|z zt#TvPWeFADY{O`DZo~*rU2dzGs)$oOWm=QcSdiH=USsu{TvL;y0;=$*GN>?AU34PT zB88Xrn5xL?Q8bgllyrlXTJWmlS&S%#8y%H>E8}p7V3fD*LO1f-3eVp=*yyHhCqpUw z2!ECOSyyR>ZQ1vW zJjLZXC$T4Wpvp}%ls7wkx`pZ9EccH^TAKyb&i?(9ectWU2VnW78~F5S+vu@@?j!3~ z(w~;Dhe1N&H?W`FUQPf3xHz}HgltV!WM-|PMb$3IGASqXW7Kb_$2NGRJQ?TAra8muU zg@{+2HC1C(%0bLuXRmFNnpsQ%E9f6^g-_>~xYC>-bNEP?>xssP7Eu)VSzh$u=h!8( zq#mrZA25B+piuP)>Y9@HnWfS&1m=(VlsPP};w=JzUeyZ8_Qln&_H?FW&^pp&wPzj1 zU2R)wEJ+>HWnB#uV~+ii?n2OFx5a5CUX3HhQrWgfYi6}0bm!Oq`)#0kcqax7Dv<^amv%mJo9C5l?fnPrkVLy@=E z1)CIRrMdMq*F%9+l{C&pe&eH;HJJG`lwMA^vX=che_MN!s4KInx}*{Wga(ooztVQh z)>W%PhXX%N{B?*evQt-8k_Q~1@tb}_2t`Pl?YZjgU!qgnh4`-MA(-TM{$g!rlmHJ) zR`h{lo5#W5y` z3BZ!(5}nu5`K8G^&W>sd>2Is5wq)?pRx$Czy7^5(FT`#;*ZBDKgAbQ<|91qgLFw*; zxGZL$|1T3V@d=oZla6<9{Mq>377n$@K6*W0#Hi_7+smc-mRgKeIOyFigvZMIv#Xg{ z`$eUb*tV|gp_6P*4uV{MYCV3->)n6OpMzK9;HR~ta5-<(lbU(& zSMGy|57I#|-?4~H+?Kpr4Z*4t_Lr^Tfn#o5eGA`@HA$Fah<%RjN*rQ{fRq05BOmxfBUKME#I?Y; zq|G&)zkB>W699XVwe9IIMXVaS{f7Yk>-NZUbKvLWET0r68UnX(W;^FSW}g^xTo3&_ zk8xWgQ{T^n8Z+pC7#WME)yt?u8To1uvOUv&e1q&D+c|iH3tAj61&l1ohPyg}of?_r zx(DJD_zU60!EC(;Dv(p|kIIJ9Ln`?xMr-Kk`;2KiP@H-FKXHfHx@GcE_5q6ia-s|Z zCCcA;!mt9r3i&B(62S+E{6U`J{2}1v3&9Oujrc|!;ynxYTH2WglboSLy{GjM^5m;A zJ=52i6OC_eOl;x@CH9Nglh>h#+Xshj$`7e?k~RdNu4>KpLa6Zw=1AIJS2SMWeq?yK z`@3Y#_7FY2;nGcz>KyL2ZvA>HiMs5#NMO7I^d^D|tb-6d4S0>!Ue9>?`1uKh3Y5FM z$umMHHl0{J(jQYZ`_L2xPD}^pQ6mM!)h50hbKvsw!GAp`4zoVLO%Bffwmo(b1`iYC z+S#iU^NOxv7#hfs{pj;TcGOpScrY@eK{tQ646N#N%piZ$Vi5zS#Q6&q+wP%sLbV8 z-){kiD%*b6d}43AL`a6YI;^*Z2aLyI`5($a{qkvMkv(Oahsj0myY3E68_N_Gts`RE zid?cJ^@7_%Hu;;}tMvjsg6Y?$0k;qy>qQg^Od@)Mp$^?ZiG)6c;%QTO^-m|Mmm)dp&&*`4=vap18b4Ycmh|wh&1cRa`Ub0ZWpsCFQW|dpX1se7FPWeI2VJb zk3rZC(Aft2cu!un(mF##dWo7t&Ay8&r38$@?77+Lb~m{^9;W<||5Sai(qfBNVeECb zT;C8^@Bf*TZeA*Emq_+xqnTy3_b!!wf{u<;5RU0&;*tZ1@yIPlCemMs%YJ1J4@4Y2 zED8P585!1@WoN+}S++nF0vVUppOJ2VWG0ssaHeX{B*rwjWfE~#nOv6K@8%Q(qsoQj z6gxgIbN=&%b%?5&nWjwQL}m~OKufj(rQVvRY@7=&-l9yrXAh#@qKUcZ0dAeP3A`5+ zin=gM;0|h1XR#>2O=6_BHi%PFTm!;zp?;e4Nh3|zta=XZQtvpKxbjM9zY;Mpu_wb$&7P(XqLi6eym+0U4zY*k*E`CzZRZP{NuFxCtd2L$mD5OwIsS(KFo|7~n zQKmWA`HypFU{dTk!UmMpC~(ObE{%as(K7<|*{ort@Q6C0QVc~>Pz=+02C;hLQWPf_ zBw>`lx$gR<7H|<5A!8Bkl!*ZPwz;QX|7MdVy6r{ieFfd@p1)R8K&0wesYf)Zv0XS{ zQt+tUwDrQIK&ZxySxlD-uFa%O(gf5vdl06;wLbGl@)g8%Z3>{}w7|#!?NU$Cd~*mf zd@0-@RV=$|02t#|L}pTp=M}qB2!m)cQ^N_Hq35&=;5?4kPe2Ipvjm3Awt>_a70o@G2fXHqiC z_h45NiYTB`izuB~OH@y8Z49JvGfl1d&Zv4OCQbfc%7v%Xr9paluqz%ic(*Z^s`nr< zzN^O6oa8v1#-1}1ldAu#$FD@BpVgghZt#R-N-vJ{=)2YF%+|g>7!V)E+W9&$)pbDM z@8YsLN3g($bHl7NL&TM2l`~57vnlix95TpN9lEOVb;ipmX+Qry*LWZNPH~Uu=t>{DeeMYIAoM-fF_XbqhhIo!=n6yK zp0{~>jzzYgxuq98){=MO32K;qM1J}Amil7F<|xjzpj~EO7nnxS`k7<po zz!*@_wEi}r(%~|lEe&pCVm?g^vH}&_lA_~^T$!Z-3J=x^|Q&BS>$ zP@!u{WVVR5Y)-_1se?~aa|&g_Op&fRO!i=mQFHoWmRchY63>J;&=Bmg)D*+F$dJ~z zT~|Q2Tvuf?#?o%H6&1KO78Sj<2$5p6E0MP_E>W@II07gJjz(!(gB}s{UL%UzPNR#* z*@hV$mL2TuGDxU0PubdGq{tE<*gk@e`s3kJ<^h*2a-D1cLPJn4Ud4n;uy8Qian|v+ z=6T&?MB!@fY+l!2F9^6ykeTjSAGmNfY-g|JiCcY#CE%X8)r+%0$m~7K)gjic4Ah=$#&46YX#cuznM^}KoJHDI7jxSQ#q*1LWQpo2o`6NZ(QRFnIo24 z=nL2UL!~!gtgYpf>sF>5WzS4_lxq6SNOo^9KNxn58@udWE4u!bHZ=*0{DQE(6yh1C z=3ldvpTu4#1?~yCn5HdD2T4t^?LnWL+}&Y4iq})aU3-6X;0D;Ho4uJAeM-PuN9um_ zivl#+U%N&C!jW!n?i5(~8sjdE+fV1G{6bDx;#k^-ON*LVN^s2h%B9{2+vP?Ni#?Ai zn^Jxp>n^M4=zCXe&Nt-$p5hzO@mly>o~UNRV}ZNfcD8-jy;WJ)ik471QRO|EL{2S| zC#O9WlOk8+f)r$1-r4(IR?|a6h0CzI0{A;Q!#igzJ>CX@?<^CI=aDDNhb zHm=ld%wS36U_mRUx81Layr7~p17{+3)8#u{My!7b2O_3ojEPN{M9}F|q)|GEvOCqr z=}7m637Vtzj8kEF%Mpgo=}2+E%hWM4j+w<%z(*E+?rCnJY?ge7c}DXvsPLY@P$zMd z^cXhF?)`gtTu1YI_*^q_lEA(%mv}t^3Dg{U3*)1@!dly_;G&3DY}CS$45b6^O@Hty zP=D<=uHv_2#Scrxu@I%YGt{pVFYC4c-EAM`JoZy69@UiQgB`VrkXwDEEEKNJx| zn z#>CKou{rIXkztcSv1x4i$Js^F5jY_C2*We?h{m_+NKmurNOe8x$ZD(Q$iF`>1*N|z zg<|;cFJ7|f6nVnDvAaJ;^k+twq={cUlx06T0WAKQxx2H(s0suu;j}yW)S$)>gsyZC z|Mc6oN5=ur&R4a4ez3oN57tC`Fm3NKJ@DL{yGm&PyjVL^!amHWSNQf7-dX6qV5ODa z^@=Z%9K-!8{r3lh2Q8^CYT}?xcL<-??HKQTtEYVsY42rjZvT@}uGc<=#wEuao)_t# zoNw9uqp6sZJ&YuJ{DMQ_hEsd-=)*xf3rHJ(ct&+Yy<$)0afpeR7qF&Qz1` zL=%ZmE-2_A;Q)P^sDO9^fJYq+$O|)=$)j{$@6ea442T7L@hpY3K^>TFvZqpElE=-6wK^MGeW|#Q#X-UR6%>rh2gn$p%WKRP1 z_S6;ok%@WU*a`o;886ue>qOwfwJ2h)+p3pH%WJ}5^>7ic7wyQChm9pMw52dFr#-VR zv@!%|hPGb3y;g1lwAS^4x;9_3sFq*Kw8qSetv1$A2u;mjB3czn$=U$p1r16xFgZ`Z zxpkhhC`-;remTzr4y_F|Vo;?BOpQ#oXwoS^ z1*{KewCSI_Bz@t#7_px<>yjzjO8(xHw>xcDYU)=)3!%AS`BiUm zzL%B~z?ey6M(~@dWC~+*^@!jiv~kk3Tom25D=6gPp#F^oJ7dUS8VB(Ohtq;U%`}62 zcE~xeIs1Jft`D(^=2M+(xXIVA%uiyJyIVXD-9lPE_-hN>z3~Jfl@?%W#0SGkEM!h0)^}O!0A;j zz-oEY36g$p=nb~z38XUs3?+h1qkN%#?F3azwCY}rE`nY|+{iXpybY;%xNNt39gOV0 zy=R9i@y5CWPKhdXwq1wjzEV61YE_L!oye>r6+azZ%_=(8j#c6hjPxFMNlI`d*fdU2 zD#`>CYk_HC)|9Gv7V5c?A`Q+8$uwQEt~I_cV{%o+s4#I`HG}{3f{}3~OzF0xI3%?+ zYg{g#@}X6$B5CTBmRa!JS9>%%_%UG$&abt!mi30dTB~LaP4u1iyD&S(j`i2UT`-J@ zKnX!CE6{u`S|{W52Oc9MrfF4~lNJ1iRpeDkLW4|YX{$5g6F<9g9OOj&H1wuRzoyF-QZ$&q$N zO60oH=Q)qcc(yRM$p91BqDS%z%m;(?Da^$2*pXeBRqRfawSU<4gitF}5zUz1G>`R2Ft2d@*JBU% z3M#5@I+^wqLV7iPlWI~gV}j8=581tp7)+6-9&J1E?@>NT)5I86<&Bbj6B&k*SnXrR zU;p%WFcQe--LWV*e-{wZVNqbc@m6aim?JAVaggL6I>-o&gH{mwrUfK|qyI~VNX8A$ zHIxYUs>ZnvRyOi~^PnZZ2>GCgD&BV|p02z9i# zv4FvOs$2eQ5kfCo?Jd1)jn@I8Jo%SvA2;q{RQjuUb~SB+pAFrk!FU{Rx>NsBfG>Jn<@QNR9Gb^?}ucN}y) z$nSyNv zg1N!6$#2VsHlSrPqr{r4st-E^C`v>>u)AY8`_T$eF)S1)Qz;mHiuKt)GSG8(iA&b& zJbrlUE0>xZa{&=4_HsDqD?L1*YR1Io2u5VN=BjgVqZqysEDDisk=}xl+Ete!k}R4@ zh0>J!UxLC=0bE?jrGG`Q$FgC@!O???-Wk>Ar3n<&D~2s|@p<}i!K^E6KJc8}NCD8? zdBc{>y}A>}I;V=cQVs5p5iu1L@4lG$r%Qcn2l zN~h6w_9rYc`R91rpULD0POvLd>h9^Xn*x1cQYWc7;VD+wjzG7!Pc31a5r3fbXZwyjmqMwUYA)IR4i$vd}&i_?XS{{$? zXoWyYcj$$EuS;RrM-c$9EuuJ9j*()#VV^LsU81OHCoYOm-pDa_+BgdaG>GvbJ>#j>BG57^Qlv?Vty_tlF%8n-Qo7H>QU*xfVhX1(;6pgB+RU^J7T7 zZ=YHGVMPQTA%6(4n;>HSPLJdKEHWAD-wGJT&v(P@zIMY#FoU`YIsnf9^WN!kNT%PD z@B=|I&c{5G?Y0r;6v5}5^LncJCl-J6ZvR98|62i8!#kHmK>%8h!RH*U-mP%jKW~Ux zsu#8Izg=^2E;_wg%*?h8E_oluIT?F{?~jyQPv7gSG~JWj`WM4iZ-tqThjeeV>a8e# z)@Aj~SJCw7n_a>P!Tp2o)i~4`&QCt{+Ur$t+U%inK}~MeS;zDQEzyo%$o6+EsnD*Z zG*^49#wB)2J)DkC*3)Zv4H0QPpEql3ug`9KH^=GTqJ^R8?T1*O#u+0Ht`^s^iM3h( z)OZUYpCVr!gtRs!x^bP; ztXfdLG`9Q+?iFUankLV^$OP$G7_Pvp61jFzdO%y)jC0&9k+f!<3?}E|Qzo*VN(5b@ zfRn{tB36c9997!CkrdJteK;FnkmRkxTw9LvDEB>N83?6OcB3rD9Ya&QNaw|vobnoc zY)ELszIfbUVoJO)%JFo$?U~mlW0U+_<>{q0kLG4_Y)Fwo{{5Kl4+As%FG}~AU_h2E zqiS4I%piuiiXD)_0pp)};yJ-QTpyZ2o*h(7WK{bj#j=>fc@pHH>j2es0E;v*Hv3d0 zqcJ-QON`EWf>}FJ>HlwfYpF1r3ON^5!TNEsByR?#{=J2}~Ppws;I#FA*&Ygy+mxO=ZApYM33Az zwneHy33CmUFr+{U)BK-=c>+q9cc6sP_)o&@GpyFOM!a@LVR&IOCjN2Ive}A9H%4x< zTVIp=#cypaDm4tTvmbb^?wx-EdK!p-dR`5UEliDpOyF9gdX~xn;HsO^^ zATXkwFn*jr+Z6m+yoto27CUizr~;VvRC_s};HIgDagdzj9-F#Gd+p96J(dJ0*q#nI z3;OlgZ|YEMYpy=w%eG%z=zX2f1&wyJ==aQX5qwd^--%!Bb)rUw-z2Da!)w1DRmz>w zmsB+M-b5b+LALAu#JIs*-5jrvIJq_Z^~f~sjadIGhxqBmD9oWyL!5J|;?Kh(`s&FN z;0@e*=|w2|ig@B%DwCw@>v!R2?_ZrTJrZMruU$I7Lq5)RYQo8b*cY!rn6RFMZyj^NAPJ89_BTJW`>FvU49XW1Ag1h)an|^=Z>7W%h zPnEziEMYNk%{x5p#xa5W|*uZA)tLmu4^qpWPGl%AT?ssZoN&$-PofeAqt0k zp<^pRGA-aRzE9F#Y~Nz{;~gD^b`%hxNGx9+0%Y zHI^iQ%Qfc=l7;n|ujUF4JORfu&+_Ox7~)}yz?_NytAXTU5rZ{FBdrc|E#2kYzxkv4N<3Qgu;-ik7;?bL}(xZ00=&Vvc|OkF!HnTNR) zG**3GO*Q6!w9eLuU6Fyu6IIO?YTYT+)4l4jOLO5;R%qmF*xZD58*~Z1Zypkd(AA~& z(uZ|R#3VEkA80Wl*>#g{^YrB}x;&5`E>JWu+b94y=3_s%k8<3;OW2+u3wwYuiJ;GyjNjnD16D^8X}@}Z z3KUx~1E|w}CpU9_v9YN5GVaYvW zwMd3PBU9kNrBEu#iNV2bMYpm+uzC45233BKGmgk%ZZn~WwAbfW9}(Kw`ZXw~=g^+L zFQzAe1Uogd#X*Er_AhZYN-W%?D|ABr_^M5%!r1EY%x8p1tR9U>}!P&*H~8-8xwkH4Kb9cz7&=~)@sW^ z^NkgSMY9f9(h8yatPp&+s<<%}sSpb??1-f(6boTuha3MpaE+-*B}Z|-5{~>W$T@&V zEOktqI=RVM$dYCYSjmZ^kX6x;{F$toad$!_B?L(ww2jKfK&uZ&p__hlsF;XS?kh}t z#ISodtglF|Q(E;w1Fq+uFu^*X zhR{Fw@iaBNVBT}gQ!1EDRFh~eXN)aExzL8skO8F_*{om78Eg6TubBh6PoD?2NX5b& z7Q<`=SU~K`EE>YpoEmohd?jvuG_w|!tX2v*TbUrs*(K6xI@G)=n85U2a^(S1Q5Y*< zVOYx)N?-51zvts1ejFtPP7~~Fh)J$X#ITiKO43aPC>&$$ohFG zpF*Vid4|p3*mI1XhKUdijO0QcGGd8d@b{xqpQqF831#9rwbLk7WqxbN(ey??&?6AsW5O;6SwW&Cm2UbNJ_kRxE5L(G;1|@}>t2g>V;1~y zlWSP?f=_i(?pmCJd={Oec^q!lZYQf~F@WxL8_=EB2fEV`KzF(r=uV3O-RVn4O`tn{ z2Xv>)Wj#ANn5b2IhksG3>e_j8lYY5xUK}^iUU~eXv%?V6%XvsIq7B)>bDBwiy28gcEK^gAYYgWt%9aSl}M+oiRSK})4{-|o*$=I#v;2`eFVqH$9+`zElv|`?%_2j z9EjVxEt{th8tiU&h*(gm$-==I*j+~U%h$ro3H?3aT4u2(dP?G&8}wqH2v?a;%Kt~- zn*7zGg$XDMDWYv&@g5`=vy<;TjW6REDl(W)0u(muLpHsgfWG%0ga?22b$ag}B_|Gp z9dxkSc}8O)C<;5Dne8JGd5QPc^T**Z1OhbZ-|HTkNBk$9hk&fkk~e3b=|}c)%?L5q zsyR=YGr_sx@+Y?VrAV~nnj8f7YOP?4Bba<9rv6=V&K(sF70&m=A@o66u8%R#M(AMl zjrV=wU4ncr>$c0_*2oRtQQzxHSr?{!PXf)sLmrcb5xBBi6$g)7Ke&g&b3b^q(KaXWsU#2kz=IJ5$xLNy$~8CT=3tS2dz?Q%s?}%?-ldQKr4jA} zJk470?fp=;RsHDbIc<_Kj+^ESVz05=%kYE*Ca17gjRZBmIlZ(eJdQ|Y{S3jOA zZ_ez9UcN@7%q^;7COO}HpJ^ep-rYevv$W;(;p>_b7)6i%W{{NW9JfJOgWMZY@;|iz zG;*}PC4Q-(B)z$4cE`m{@Pgxh@!-l$(8cclWfB#?y=l4HSyZJIU*=vi-`V=HnVTSm zd`LzZQHHVPvWBxuplhmIpkwNFxj5FNoW3NFVbsbphI6Iye?ZMtitlJeG9|yFJ36Lv zaLii_)ui>om$Qmv7bZKW4t-E~+qk3%&h6Bs1vFBpCveQ~nxzQ9uhY{t)q z)F}yXE?@c9!NIk0fjSvBk23G}wS|VXjgU*nQF}ktiqK)&NLCT=zUsmA^O3*xWdr^p zU&I&PIQ-4ZYvnG=?)EPisE@P1maSPB_fO);p`FlW@qN#+DrFr>s$+d0|MrTh1@Vvc zfH&0H=Rj_`i>Ho*pu30G{w2PSp3MSVP&2NV0oBt!=yps%>P7p0%8~30b6PsV){qvq zs()5E^M1Qen~7nI2P1*`<~V~%pmsB*r|)fVm+18+vF>Uc$;_mq!9a*@^u*5W)@Jl3 zjrIF*LION!$HqH6S@`43d60a!=%G2K(0eX)VdaPA#+5c43lU!6#JJF7a$g z=hp2NgABP6TseX`N6D{lXj7t5(O+}xioFNETH}x(cNOnm*px)Mk0=wu#;%?mz4fg0 zocT;FL)(d1;WWG#PDi&fBym+81oq390MIUAdAc z_O6qy-5ITjzS4K0JN^~KKYeQY;YTYi&5;*kzKKiYHtkqaF&^`3ch)*2Ar|(j#dlNT zjPktpy(SE`O2AaymaH-3tg`wE<2u7{Bo@D0>(QD|XLKyD?(6E&6n=8rYQjnpt>Z^! zqdIF|#lCtVS+U&K*?*or+PnWnZa^af3FQ;;ARX0#l+UdIsB4KZ4-u3E*&|WggOPQyaeFy=7W@9`&{D zYhd}`_1}`R5Ucm|kVi{8@Kb)@NP@@vzK(~@X7WTQjJ2q}m)pSj6`!EJI?QvrCTZXu zYin9BE7kaD+$pkD>T23sUxQzXrLoifDZ$m1s8w;&1&ZGRpJt?{RGH%|)&#uODAu*{ zS0E0R@PRmroMdB^e1zB)DJtEez-SmWQ`@1q>X7}f5e}UzMFI(M=pIM;dYZjLeh?*6 zM@RE-`y64HS=NiJ7AtBdXca4`CiePr#Fq|r#>0U%AB9K{7Ouw*D!C!?ioQ0PfB1pK z=7V8JvsP2Gr>oH4j7<93$8NH1=Tr*(gq~ZlDu&{Qg};PmE$2O56MmbBxhQ{Boaq>q zyAtxT{E_mHFa=LS>DbkQYx<#!XVvjsvYY8e7g_is+S=XG6v)@b%mG@6m!b#+fh#c; z5Hu)}(fD5kNsKtex=ahiD!2+rw?M4B6HraXTD7(@A#nucW8&PV64>f((1{T=HxP7B zqgtg7J812SMdQSQ?LvTGfT%Kw2QT7a(NGp&qfZ~uv9yuhB&H`tT~Ztb?5?mVd#M$cGF-ytD8 zM!ATTkvM=~{j^`VwCuX@9?U~R=HBw^k& zG+KPgWfN0{3et1zR3V$%C=;FdReM>deyqrs@B{C;?u!tk4;kN1dqvEklq@_yM(OFj zh4Ri*rS(Y!$KazulvCP_&_)2wN&6uUb!x1O{Oc&urSq0QhsDE^riY>KUwyr3w0c_= zH^10@mU&IXHNxd2#LJ3r!^6=wU{3zWAL%B_h=MJ=BM6pf_koYD^Fc!GIjPpha%N8hSpQ4~n2u-ge z*2zo*h%_;CC{ftFfPTOwt8$}>XA>jsw2&zXFw z5L54k_>~Lmh4_KuS2M{Le>4kgnaUPVRiS@MD`fM3K+t~wLvolII<%iQR*K&w&@IOS zdf6?-hj>geAxz`1OEG~EP>zP2(~=Bw4~7{SqIP}BfN8sSMzlNSRZ6uNp8m7@uUl!$ zp>77|>A*pee#&kY29+)t&;^Q|JK~`d1WntQ#SU?&1rtKHwk+sYafS_Prg;NiY?c*w zTS6!@jvkB+h}LO8geBV}sF#i&Fz|^SEe4{Su^1v388eJSRv9Z=729*(U6_t5X&f$319V`@W&lR1Bro^Oa@Sm6Hdky6jvesU= zcaA zDKUXdRsnOdEL^QG#iqIp)jRHML9FrIhZ(*pIf^ZT24~h(Ch@`iJOESYl6DZ~VA4Y~ z7Aa~)GQE73lhHyxkGpX|F|9vUG>yg)5DgI@;8pnD!cjPR`w24mUT$O637NcGO4%`ODbPli#eP7qZnqIFAfVW8p+M@7as|>l1G?a z;5kwX#8+wOF58Hp{lIg$bZ9qxU9>IMaOf%FaOkDRX1v1J38OrwxTlb~?O1Sd)~QX# z!_0Pk(wtL{9py*onjNf(dSn*nUu-}`FTB&4}}28AT)!sf*! z;lguvEvx>}#SD&LH_x3lYyERcC5;R2u9+bgN$~%*j%yuXP-ytA+mT4duWx%S9;dAfL?%9Xo`z>O!}}^xdG7R9qJQ^k<#-&u zv~)&XnDRh!yv`d@MC02t6eG+XP<@$SDVMY^K3AkYZn=yqYxIy+bC^ zhM)d3($JGM1au*DZ05NTNw)LMxxiG!(pg#p$(Y%+L1Fbg#V$O}d$b?-5qGUZc$_)R zRvnM75ca=%SjD01Mj5?H{zB^`17B*pbeL5`@;-6^?~v(66}9!uA$@D?Mh$$u;0ZkM z{%=?4qrPs`0Y%T8iCbjdmTUBfFQ|=+jx|1071flDmpW-BOj`4$0_r3dZ4Rk<#($3_ z2TD$qLmW%Y=*(kT^Kk&c)v1-E#}V8iG{p;!FCkC05#JsEv?;~|c&uFN8nwfR=WCU| zvoAOz#Wki#_ryx!@&m#>qicW%M8q;vIyye_|BCjL@=JQW;!QTa<4r_LKcU*nKcQTP z98=edicpPT!+8Qph*luFR6b$t3O7#p2&CPKSB!9A{DV#(vzhQ1%eqc3sX;(>!xJ0990d^C8)J)x%%J+;>? zMs73}unR6U`^1^ehnu^5hPT5aOT*AM)i$)aDwoJXF#Q-Wedi?kc2AhL>bv|iFcD7a z8dA7~w^Y@ejsZ*fYi&iYKM5HXC9|;GFXSS6xQW5*X*Et6s$ieJd<+z8vVY|H{ZFt7 z;{LKG(O;EepfC;vhT3*xDaeHOb2Y=`)+j}ja>Wg>zJ7~`koEC`?#fN&8ha5g$3aVjYcCEehCA&5%zzztbtB&wpD|@90-^ z6V=#8*P|2`j6%#mc22xw^;)*zzk7Nx+Le6fuVUU>e`PziY#un0KFe5v%H=5i=S zh@S&~X9e7EU~OE*h8dgIxq(zZ*nN;&OWgD6;nFG$7q9R6$~$5CF>}0GVrbv%mmvkh z?+xJ$`>Y5ek+n6p^RGw-CUcHJBtHs4C=t%dD=knnP{;?XM}fQO@%PMKl~Dsd50cAJ z^g1@|_itg&gIgw2dFFuW2v(cH)zHETJvrNC=D@XSY3x{^f4PK}QL;kiSrU~D3V|2R zW&&t#-OPHqK4ts~CDR8Yhtq4T@Xa9RawtrC4JQF6F3|Y zkJe3YMN4HP4^tMrrCB*}jAZg<=0ZeGB4$Gxct|a*78a`IZ6fp@3 z;mm$=&3AYF*^`!=l~Dm*JW3dILRO4L>$S8bj6eXt_H@NaW9*@!%NhCKWa668SrmB% zlkrwfqp4iQeS-0gmbEhP{LzSPwCcC7x6&-8w*JSd5K0nSV$-2oRBBxHTCkI$JmrB+ zfPYR=T_6u|D1SEnW>eoA%O+FE^;Z8AhBJMUdmNgCDP!oKF4|!a_LI6>4HfUUJ&w>o zx`4%J#Ed+@L%N^wBaIyl=P9P(IX}E};jS1D>$dqE<>1ymq0;e8l45$C+@TO(_G(t` zom4&-^kV42ysqZC+<3b&nyjE>kZ#$v1C#@s7zw|Fy`<3mA6ZpAYG?LFPK;t_IJ~~~Zwm#$K^jX7~xPjNp5p-Be~;Q3(+Adu(Tazn-kP{FqQG z5cYd~ocw$rGb_LB|9snjHY>Mk&J$NoDPes(9Z_~gW0+c9)`+3nt`NyL`uKPT?Be(- z&L-?C7g}3g`0d%`09g317R8M^4 z{NCuKqhsurj&Ej`hd+HBnJ}SBk5@+~gqK1Sv6I$`HCj^gsC){hxW>ieQ*uC!aO%gc zdBciYB$6d3yw-*m2 zh7mHyyJ2EtNxIU+o3kFNifk&NI0U{haZ{F`Xo?n|L|gNKT`wWhG!%I$kCo1bM{_ez zaKrFaibH&6o`xL?fd!uFXx#RjeWodzRhpIfLvu_Jftr+Tixt%`^0P$ewoKiU8XyMh zbYrphJq2)-;dh}GMZ~iAom>_5G=}4g zEZiP8q6nOjOEIjv@`3(-k_z>koO3cgrs<5}W0WMBOPX#PnwqJ!2m`~5WHDO{tz)qi zQRbl%*N@^1liXA|Ow>jzg<0<6GU7%Xn|b+y%HP6@^1e~ws(b5qxmFyMk3D*!Du1eO zJerA%mtzaN4@|j!iECWB1V>;A6Mll#C&xrq-{07E4=p<^yT3o zIm`HE!+=!LBb`~gz8?h#>Ty(2nydC`CIUN^*_f_CH*zUZ$FwrAhw?i}PdN_s@Y8Ox zomge|qsM^Wxs9so`Ug|_$+R)sFVjM%%|!d+X<-bOt9EMq`xeb6)0@hl9xsctxvqr_ zA@Bm@6rsB1m`o;FGE1V^QQz`D>nA>S4nJ#dKj)u6Pu>FDKLQ3PK0Pb=eNW5zP5;j3 zmh9sHimnVs`1V-`i)n{Si6JZnV29;`bixk`hI!G`DQLZyi#(t1{f^>r^Z0r5AYqGJ z3J;J3I5c6Cw*y0!N{#8d$?q!^{oslP%_ewZ#JJ5Ei~0vi=mSE?HfE`tead^Jo709+ z{aLcL`Xj*YEY2R6Fj`5h762X$7_Y-^!~elq`A(}^6U{Vj-UW=c{kN5!uU%!!xCFsV z@4?7ZYSm<{eFCc5>hxCAx$K0So$rs)Im_@iJk+!{uz@(d#m3hQj2LPg4wqlWOt&G} zmGA#AT`-l%6=e&WF*EHZ;sVb5tfF%%i8A~Cnae$$|3NhV=LARRC2!~dbP=e#6R7u2 zUKAWr$lt?NncxPn|I`!@uvK2x*qSF7YiJG!E%W)6dATAMtX6rkQM{WMVtlOaKPh;K9Y;6yU8dp|p?}B_Q~1^gP@)(eTb#8U zYl*W+pGDzJ0%Eced34M-1l&&czk@iB`Se6G0WiEoli#5$Y3=_V3eoy?M}Gqx+rDuR zNWHI|-VOj?asoca9b5f{*;o>~{N`^irgC7(hpuz30v(5^`^p3F~kJTw2!8h1CiMZaEMqkU{_?t0}7IO`Zb-R+0{e8X?^fIRO-`$d<-~~tmyK+FY%RCEi zDC509_aL_O%&@rZA{^1BobH*+<7Vh8!I6&n)!eFr_l2LzJAl?WblEGUi=>z$b-d$4 zEY_cABDA(k8jzssBpoi=vZubB0S;LiNh6FF%_Modj2M?GQ<^wtp<&61i9J}E#uu4> zN}C*sYMRE_t8V`8F{2U=C>eVru;$oX_Ilq=e5UwlaAVJ2UiwFt9wf=->*j&)0&sQV z&$qb1b4XMy zHuEQ&3-a`Nnq7>h)AH*wp!si4B_PSC%JV3G)(7fznP3Wu`7ex6c5Mc(h|x=ywl3A5 zDpw7ZMn=KFk{@R^VI$cD!|X{Mt_EG25T-u~pa<|H-6^+XudQMOc!v5HKJ(+mY-0m< z0*2{KdvreYV+V9MQHP!;-!3EH;#D}MmV^fQxukt>1))sIsbV`a&wnEKCH(Gb()7Wr z;8ylFdN=(@+k^ksR<36Z*LDVc4j?cJ+HdjXE^ED*;I|K#IcPbzk0EjeB8LosEE~`) zQ3h9hO4{@Wpl2bOC~R~J*&~jJU$_SDJX~0DTZ)#{T+VcJ$6(VCT$9b=B>@BVlD?oS zyWK8>#Q4l4lum$uG6n}bs?fnmko8hlET3F4(%*xwP2_T8XEQ>q0+OW}wn;#t;1{1P zA8mCJljFKP;#5d-UOxT@V)GNo@PNK)l^e#s528StVeE!hY$PQ?O$TfYr@c|iw*0av zE7BrrxDkZNbOS;eB#6Nnn|zG89((N`D#GVRKBuh8y}puUu&zQOat%T-Ce7%Y`K&#~ zKbctLnyy+(9{u`v#>nG!tmq?0#mUH=?%#qh35)P~8m@8C0{l93)g7|R-A^kM7r8ka z+AbrVU5+$exivX-Bf%U}RDl6~c`dYmQ{=ywih=arjy31i-Be#HHMPQv-Mi3%w*5{r z`cY15s~U&xZG!1BZ7NAd&3h#l&>h@FPO39PXAnLBBBy*yC|3MkVXoLOY|5m<_#u1m z8fCCWWZUb!#YdLxUw%%)ogp+0b_ejgyqnYXLD?z7bi6e~us${L7?8y*_3}88#RM;5 zBK@G&3Wfyyu+x}=V-?PWG$>|;$dI+a(TBwQaWT6G*CA<-eG=UfSgz7@3u6WA+?x>G zk^i!Wx%k>5Zct3&v|%1$aOnz(TtS{;rSO+#1<6GyyUhu+4kK=hnf?OP9<@`_P@Hu- zJ^9xB53I|xWst#pDL%*{Nl8DTQzG!9BruFLCNP@NO@?foWPo8@Du`?RGQJbY36%u& z3NDpui|v%!iR}d95q@RLv!<_WsR^lD_06gVNkHHQ&>)QaT$H7 z=F;R@F(}u)RV&PC6UC!HQ*FN|G{RMl^XruCH>~}M)YHRQsGz{Eah#qn0OfW&UUyobB?;^g43pjjLBj9LSw z!YaBALVrNnz)G{Dqwl#7I^@Q+@U!AFh$Z*Zhi1l2Q9zKUWYU8&>dWjW+e#M~V$n^Z z*$HFnV3{r@{5TFNYi;HtpZk^Gls}Nl^Pf{Evdwx=4OM5I8B{QQ~Vnr*74bL zK>xjTsm37*1PtiZ@4qP73MGx%iXwHFAsr_hpdXhB;vBz5>j3gVCCK{(7lYZ=4rW(7 zm|dJ;b|HY-wF?5X3pcfo=Mr;b+@%#25K(GZCQ#?vY+xU-gEUJJ;eE!uxICVzbEQyA z=_M8AASCA&ZA}K(eerN4J?-BQw~_oT$b{eG<$|Ljbfb|YBfh}`4nGT-m4We$dg zs{BKQ_l3Mw(crX}%7RT}Jrno`uF@vE{~^M=t|(u0Gm)u^L7y*X5NnRWt)<1dbs+Xj z9E{EzKJLz{etE&H?`_JWdfboM6X;WspC%dY$x?VAz3T2|C}F=b;i@;hpJ0tGHryu4 z#D%#uFdyWz48>$_zxP$V?xI@uO0sA*STww%$S$y~hH~;)9Fh)_4-XOvK9JuYvPSQhVaE z4nCoq=cHpb-&G#>K7de{Q0BQ1i77Mok9!IQe(lnb;gTIGm1czuY2F^oKD6Jtd$)C( z-KSf3TzM}`McfPg19h}x$r{@P5C{mP6Y{FhSXIHH{UjK6Jg9;2B)n`g7omL1hz!Fy zkR4Bs+NKJ;TS;@h6ryd7o zs$9PS85)U7F|_e>CcB01^@J<@hL{qpp>3Mf{c6btKnl-kVteNs!ZL9!sY z^N~6hkXV`a$*er2K3Q6ETO)m%c8T`r=2A&)X`@Gyxer1giJjh227B@z>-NK z;%SXqvP@7_cC|F>5BKq%W;ijojT6-UkLp0aFyMIPR~9p51JSs*i&1S_bKO292dlC=Lz+12<1k~n6sDqZh#Mtk`u}v#m&LAld9+-4M95aV| zXYMO$*)bkEGC;t}-Lv+pwH^=kfAHu=T4;BgM2+Mvp2|nfOo+nY#EkCsUPe2ZF!zm3 zU|wBaZ5ts(5RYzt?{tO4*0^hR zv;nO*;2Y}S?lZ~KXYzRzDwFj@Hr!ODJ#Uk%9%*vQaSl@x6FtQx2=lLmSr#St`+i$d zw$IuO`}<=hfZr^(Fg`z3qf&n!>P>MqT2NtRm5;+^8DQbr9UBi(9*ADTeO#YY$IWbM zjOj6=(*(sO&x>=xD{17#{9 z>FAKr#t9h5%}+vp4Z!4T73$`Ra+0+uVqI0s#;`!LFlOch_xTAg(d8E)AsChn{Gw=05{nGrd^XA&+K*;`v%U^Ida> z$!88!6nR{wi4yBVwQ4Jup}1hYVi#7kkRmMIEx~&@9d3wiX=WN5R51hy^5jH~LF zTw@n>R(h0iesZ?4>AS_YW0G5t8VDLZ6&XQ7Wf~`A@~=R=?&P*4dV6hCZJ*7|lCzQ~ z)#W6U)}{_dO!EvS2a^FAG0P=1tpOBwneuI%ycmd4T!oWb$+&}AN3Xg=`uIq#f%7yH z-CjKG&2K|SBNC#lK~l!=)Z~W+uzv@_aj66C^|nyIMz+DZ6tf29S}Bmj=S)EGtKo?% zmuA0iTw&P>L+?p~p7T(H2M9)>X;RBcHlp`7xTznNsX9Gvsp)`1ScSqzhKawSLAH|m z;{4Ovsu$~oqfu?4F2$h2+(-p_x)pVY4Q_hy4M*=&pb%$@9e=8cFmD+_N_!>+$)Zbc z$;Wl}+>6S7La-a}r5nN207LK(Y1rQ`Svn12v zV<5qiT?w^AiB&wmOQz)ZcLR{F6Wjs&UK$wVkVGL17U|M1s3fvUqaX@MT_a@2Ngy@G zr3MVZBHaX#Gk90t0}6aoU-c-Sk;UvOgUFf81M5@=hJw+BDE$ZG_&l;N*AFO?5SqV9%d+1~=D7e$&vHnEo{8M45*<=?S-+Z* zvTR?pynp$}Lhw2g=Xm*K(RosApK&)S0<(xkSB!OqTL+dtPhT`@Nq!+!MoCZq0qnr1 zt_T*=T}sCABY1I9Lh8IWQhA6OKypu~i^kD#(XIk^Xr1IJNg~Al5>`&Fxjqe`N6G-SFp@CthP!Xc+Cf z@t{_WsSH`)oBg()HyO0PHxHYwPQb00mnBrL;b6+uUbE%{bu2k`HR}(wBrAj*vv$JG zG9V~6w;(Ip3R$Tz`dOngj;j}Fe_eX1bZd>1vTh}8o%tLhGp9?78O_IOPuth3()0zX zW1k8k74q6@74o{=opXxLO0JW^&3J0MeZ(_Gn-HJ4x*?`O zhqVtp&;Q}$3$QRDCa#0Xuo{cvm|h4ZGSGzt3;TfzEs2;rKdp*cA;Dh-)3A83PS2K* zV97tT?*EqjsU*U{!z3C!Oi01Q1T6VOf+c@6U$Eq#DDM$m!n_>Z!F(Cq0mNQOkv9c~ z$hYI{9NhoWwY8NnC|g8wPJ;#f(Mngx)X_^7U+c-mw?($^$U)D?cPzJogQk{8t^rBZ zyE2dtEU&t9Ko8#JDp}(~EHmnRT28+VHim8EpLiwwWZZ{61}H*RD3Y1(>gM3jgvLe&bu@&s+O#a_+3M33eR%$E4H8f_84UBsVo*nQ|*~4rEi* zR?Dt`$Qac{HLD!9csLe>a7j2$o}x|d^%@j+FXY`TZcVtfk&=S-ubXWu5{nc6U>w-^ z#JSIGpjuuMxmD49$+FFzI2NC#g1?Mvb@0j5u`uAWtc^$A3orqHC`u(V>6n}|qC3Nv zf2+B_GI~wZZ(pA-Q*(k9ax=tyu8l9aqp;`~0PR2#D5gDwPNDk*jj# zJJFQ-v%R!QsN*;(WNv_ZfN5awxH_rd5xv;%`?D}LR1<_YtTxe?c1kh&fwh!fg>hNK zAa)oo(PFBy0gdZVQPwx1eeXqm5{(52chUESkXK_8Y1Gt+1KD^ooXB%V9MRlJlmU+c zJV`d%s7ZU-U#fZjF7s;kr*X{{XWR~ymD`S}#?X(POQxW-M!FmnCzB{CnDP<$&XubfuNE7oQuxg1di5u_ED3s>IM02eh$ zwTZ-4meftPvJB&x7(Z*A>nkc5cxrIClOje1Y)zP~t*Pg;iwJ zNG3r=S4TkFR!MPVMI$Bf+eiWrE1y+&TC@)PaPwd>?1F~Q?6m)lc_XR8lat_;Q(veH=S4Te+0k`#L6MVXea$8z|b<}^4X5l$b(YjA zq4YF(ZM$sRiMaelY8q~!?a}(~7w)Z}w9oZ}BNeH4Yb?gEAh>(6{+8tKuUA7U=dx0> z(h!~y5VQU$DPG%6|ytb++z8~)rs+Ecc@%@ zgVkEpj|$EzPnhqsfxE1mp$*6#KaSuY`m^J1(?)$Vmg_ykKtxdA(O4}qb{iOP)v4*P z*30j|vp2g&#TOk#i<9o}Xd3NbUn9#(q{Gta{Im$0)EZ72eSXIByD;3;-hW&+e>xSG zCl=Q)!%7ZMCeioz$zRsj8!5~<({;n*6=z#+JfhA>uu3M0QFi%AWPF+N6FQ>iPLw@j z*vA!1Mu=MYQbT?mE~~Zb)d)n&$^6;UrOU#tT5Af_ul130r?xSyEoRoBuOUs!VUUaX zE5bmgyCATiwWclEnx$&xe*TASZx*2w)a0}1lKSISICO2n-lw4xboEu=MIVvpVxL+D z3XtQf5A+DyYx!a@!MvEfIWqH~uT%+@dOMcz?9k*Q&0jcOol+82@zwlHT-d?I0roC|-(%bEMsaBCtm1a42WvYT7u%vxQ@KKmYx z<|J_JuX2@KV!rHmRyAHcU9H_SR^^PEL&bZLh5wzKph3>FsajrJcj`3p@XnghB80c8 zsGP4kk^ztoPFf`lPHw#^Jz0Y@A&`U>=<8*jCPp+T!_0;M%y8?OO+Ly-y7-kNfW)+k zokmJwli+Nq^7NsHmnGupvSpZ6gS6gcNIka>>4AQrOLF?*uz9^i@7OCbVG7=^Hu`4x z)SNR0C>RXc4iEecjsh=0KYi!9sB}BsDg;7Y+uyu8B^Ji~n%jpz)l;!Z-t|QkhAxX5 z%J46p;=#r=UfB(2zx9%>#Bjx};R+?Es}{srrCC~%5BK_lh(;lH`xW^vBQ^oQbR;x; z1oM@Yl7IR58*S zli{f_>Hf|}@KRh6yuWYRq`Ad!A*X{jB71C0#p?(AV#%6hPo1dTqOG_pTkIAoSM5L~ zS4j&5P1w^s##({m+d=yh60t5Kd1;I#k~NP@{Bpij!0zjbG{(93s(TwjdI79`t|SHE zELhot5cMA|Ian8qfYFp{W^;}ace^qVPeI7gu@3$Y$Yxvu8%Q)o@?L@vnW`S+7$j>L zUgEqZYewFp$0b1(2-%{MZ8*fL9{!cq=kaC#v5i=;5ghjp#^$zf^eYO8CU0)M%KA#> zB5n>RhnW3q7dxENI2G`8Nu_?97$mlB^0)MiXQ}x%66Voob*dZ_kDR^rCmEyzkpoRjr9k@ct3c$k6nQ;!W?;>0W$OpBZ%ZBmQLB`Dxb3RxfE6ZEF@xe(YY^$!s2zC9v12V1fwbb}N-@~%2XF^912#f6$pNK)A0yi$wcN^ldD+tCO zYKp4-yLV;c&H1wapXE;D;^@K|5r>V|w)}$?W*UU_zMXzigOLGZ%4+Y~U9r3In!$KM zhL1Xbfe~$DLY)3_k(f>CU7f20ICK5(PQ>E*(W=oR4EN*qC~c-j9=tGG*>Uatf012R ztCGMM`%(7liq=#j8XF}$$B!>c$%;@eBUVjM_W889{UlEf1e*Oyuzze$#BC$2xJShN zl9Z7a-69v-)$gKaX;<(gwXvD!j6;~J@Wnwii}Iu4Q#K>5>1##!N^_|iL8M^au}z&A zwjupn<^4pHm&1FhXum@eN$7`*Cv{iCj?10yAMRr{PrCAFste^?wVvVwug3x)&yIEL z(;uouId)sKFPorNuq6rzwnV*yEm7I@y_!f&A=BCG5g>a}{8D=<{3=j&v%xA^jI@3g zLc{W{Sf9Z1cng;8f|2A}hC#%t0sj1(cR4clDK2%T&73LQNz2t?0;^t%fX9e@M;EY3 z-Q#u^QX{b?EvV09&rJn&k{mcG-(xeIwAoB?@ARJF$qaQT_I-#+gekei{$|fJW5EZo{{CC=li$_trDk#*lXAY# z4*ej<8@qNJA^zZBIz|6!RB6Pu=cZip`%}!b;D^63RKT?jH3s=e+#-kwe!VIiD2JAf z{%5O_TITtfD-8Gpx%H0=T6!?&#z?gAjQ28D&ov86nw5IWZ-FdpNirMJzXDlF7;(@t zq-Gc503sz;M~S>WNtI^&yZwQ$c$9<@wLKY;@Y8z>^q1!TQks3-qwQ}-Uy*@O!gsr? zh@2uaR1=*R$g`Up_*L&y*%UyQks{%vxHK0muuh{y&Qe{19GI71-RQF`k08$xCd(by z#gSm;Zc)3Ws2T02M}tv3pXGs_%lp%%OegTaGj+y3zl=V@hC`HLw5X4{;`{U1jLcWI zQ(A6}uJ9JkJyj|+Hw{^ov~Rz1L_MSV1guGCme*Zw!*@J>ddNg1Bj>%dEDE}hz}7al zP%dIbi^F`#$*NOzSb|(?X zxBGC$1%s?Yil#4-Mz4o$J(^+>n12Mbx@6n~C1fu^Gu6 z&;6w6Oh!p9wQRmBwrQAW8*@zPukFG3FH$$KYXDN%IuY(=WRG$xPUlTQoxb0ndzNJ* zL#NbkcjL1hcbQt=rqIELdR}muTAgpf^%DTgNVDxFWh;?>0_=;R_kTXnegeT2*T36; z`;b_ln4tqG!2v$YF}r^V&@YKO7&sY!`%l?w*g_C&l#1&tQVx=FLle}igD~)f^imfJ zH0SHyLU7MVi)PvW7fp55Lnd8;bMhh@L!GPV9NM>o;5UdlQOoBRV^{t%wx0XjX0S+x zatmXEl^@^<;{?Ei%>eWZ1Y3^)?hM%Y8~$6a{v}J;D%z?ZgI&wZ!ce!<+IUn7?|{#- zT()TT4D;{|G3tHt%SP_jqvAQ60$$@u-(BGY<$bP`$inhf+=qp4q;_3?rgG)Pi;m~a zewE|5b@uE%c-pEClo_168L;-^`2C!@g2@fKxj*}QWshFvZpzhNS9i-`UD^-R^MKfV zzn{CTOG?#JD}Q>do`j=8>U!ZY+0I16?>znZ$ai?RB>ymw(kOr()~=v_F%kF4)WIaR zwJUV;D`C*?;jBr>$i`kxu{w65SCFg~Fp1TBlay-`-vVG0(O{5O%z#=f6IIG(YA=l( zD{1L$0wid+4M)-98OyBZT5-BtliItsrBdmnA56EZlAHgY=lnO#4NvL{S|m1fz2jJH z^$z8siR{!0WW`Xu=%iy@jE*-4XEqBc9ZC%NyCQfnxRUFFHkX~aO0aOSA5zoW>84aK zZI0lxz5-vP)A8#|FWQ%fDp=RUW}tSAsMk6RWleyXe$L_2!U2CBxUwu9xFN6m*yrE6 z*elOdrrX};-lr9(5slz?{%CQIvogRbZOo8W7b+bKs?0^7k&bG&L2KUN0giknWj1jBkLZyj)_4Wzqc-US~ z_0E2H`YVg}G8-U84L7<3&wesj&?(8qRS0Ve5pv_1_kQs?@u3a6GQ0A28&_jUL#)uh z`CnQ(ryOI3B%t3peG3`U(KJR2--0W$Eut3qv4^|osbRUM{{c58uxzm`?|*;iIkKN{ zh?8rF76N8Y$NyTw{7pESBI34nbXX_fj7j_go3_{s@$?>USY{QDP7blYvK8mtPL_i_ zq_C!EJH%$GtTb+zLI}S?weMIaV759gp&}gF~gUSp+>**y(2HqP%Woh?Y=L!chQIP&*Hj3F4&6e z2pH^Yevki>0u(rjj;wF`RSIBLm4PYK+-VLCHjG}_a}J-%C~##@tl*zwB|LvxhVoL6e?PKUiaJ>?$eW78opHonVWHt>&;j4U9_Qet6f8(7|x* z9z77sV$WcW_o?V!Bzvg%=AWNRe!H9(R@b_mrZCbW?#b2dQfAhjS&~2Nf)#`A+m=4@ zBa(-ear8n(!&gwScWm*SaMT;C;SVd@=PTLtpvk|dCFiii=Xhp@iX$~X&j}R)%p#v; zQ|OwN|8L)G&i}{tTGQLfoSaSdyQMi2o4UEHqq~c#xhuJ#Ae*X-qp60u8@WE4s-!eI zo2I#!8#$YtJ-DIx{~jg&_b6wG#3tqCCZpl@-Oc`|&a7b*@yyU$93-OWj{V&8%&i}s<2Rk{q{7*{`4svk$ zUzn4e<3E4rKNL8)$iW2+3xj`1Y)a;4*5Ab)y~y?d(~+B>o0Xq~pC3#d!0|s2__-y( z&tdN1=1R^1_#Yw~{}*@f0T)H`gbk}_0&@Z*t_mV3!pv*{b3#QCks#_~07OLr5fMej zi~$e@%n5S@113y}9twyNMa(&2M8%xG-s#%io@Ll!{&(+v@Auu$b6s}mt*U;iy1MI` znvmcLJ|WIt!R$|M46WU~yvIwdT~(T>ekNg9dC~a3fy2jok2a(T9{c}EV;WlbpyU$t zht&^htG3|(Xd{)q6cY3YY-cN>SgZdpwu4FH+Kv*o9j%%el@SpV{@=Ep5Ts?j$NXOu<3#%GzhyfH7Hcl<cyS8dGkz(@?bym`cXj~-h;*4Hc$t5)V-4dG35hN_AT_gQPwPe~Z?Dd&+ASP^?m(5-&z%!>V(Bel+8JyD8*MuB9pw}kz6ReA}jnY0LebOzu^ zBL$R_($K_Us*m+az9-)q?ewup$ty zEE5G-^*9`}*`bXhX)=-itYmHMBEjlPSb;cGAcQqXZ+})tNWw_jD8jYnqF^TDff+@( z6Z?4yjSqtLmarla#+M73iW4nldMC|AK)YQUWEf}&qyUJ>PM=mO~s>k7oM0lcH z2%?;Ml=ID_9U+_QbA1YS9XlKyDmhE|#8RT>WNd-RSqgGA0c8WBGfLQVmwQ>gqIolbEkg~%%uLg?ivt>96bCY+1?T%Xcf z)TssQEn!6<{9Pd`{;S8~C`be}_H%tA6`2Rtv@Q~?tAv$?go#4PDb$u{ok_>?L~K-u zat8&EJJ1B&*w6K;Nmw2PLzZVFo7SfkqC7ysryvo~*w6KeRMhfp(3N0aC9E{0p1aM)km8#OIEr~~=(4~tA3QWav#cucM&0i557X5 zReS0LYRX0>0uiYxn}6;oG?9`}HvimH$jv0h*1q+r$eClxh7UecQb4w7Uh)Ce^>*&! zL{0*Gg}wpR(B>CTiGmM>EBOQrDYl-kZ`2a#&5$92bGg@p9S|2ibk-Phvq2T*ETHWLqKs%t90GcyLLwH56BAiVK*`1A)te?#D2f)f zC{Vb{qoX&uy~S7PlTb)+L|$Qq;_59b)#DExG`Ygkq5?UEYw5arla9k)@lW(dD=RA$ zS8q}4Los@jsZ`N2vkokEaLyMnq#Q#Ed7{94OHrwVv!!5t*5r!Gi*qR%hS(D1 zKOK-`NRc@opCOf~mME2zdb5h?6SGJ@h1qT>MnXP?nQiDjPW36w1ViuHT`YQK1wmM2 z&tA|gb{fy6C;i4jjy~b!&Y(aORG8Ga)6HVz!+9 z562@z9wW~6F_BNl^<^AviE~o^0ZD9$E00q|eO$YrkddpKV2NDgq`bl@Q6Z7SRdW)o zY$d$J6GQP1EhTe9sW_iv>-M^*4Kgz660GbC>5Y;uRwxd@QOFXpz2(zWu$jXMP;D;?i2Bo~hDN!Ak!li7|JH^%d`Sq5O-YKr! zPZ9NgZB&wz-YHn|7t$MfCnf-J^cDqhe!Z2bwx=M^-B5FJQ9jR}!|R47Imenpej}tj z+6Uvqdyew|hWBvXTS)IBp^0^16q+!z1qlg>2Jj*P7wG5gc@27H&sfkan`m&95lSsN zj>a2WR4w7vWF|@+WrBY^G7tz>jydvCn2?|o2oz-O6J#iqU_d3RgDMoH83h>zB^Xsf zru^av5d3;8Q88J;NqyM@ddb5oF1zbk)0Q*cCH64hnNgzh7lo_(9BYd6Fy5gRWFQc% zd+SUO-$ufgbXk^ypwEz+$igd}5|zs+TnHrf7UVJLh{crR zl0o1{fa#EO?C)`u6LJymq!L5|Vh+d%CP*bHa>UNEG+c-%El7~y$iy3^Z8`oW%3*oO zvm;N$1#9*k`6x_|&~XGi(h&sN5+xasbOdp<35wuNXlK=qOrZst6B^Ir)mBx6q%e_! z1fMu6MKOjX1HL%Y0maWcu;ExzD3=saTvdgmiqH=zLB>D{W*imH2&b_Xl`2t~fuI9Q zRFWe{6mlCu)zR|FipAM}ieo1fGwGyziz6sdyj6BPGK2}1*aZ}~Lw8H!syhjGam)vP zy_KkLk1B?oVTAz7MG~(67nyf5dnhptq8J%fHB+hx6@n7Sg(!Ax6&=EkG=LxzuE=VN zSxpF=aF7X9DhhL{(9dCCf@weKJ-aVKI>9~$BM7~R)fhMxSVJz>ghe-= z3AyqhWa-ZwiY%@y5rbS@SuTUV1a$LV9~4< zkH9i5j+end9wPRT9!Ik@0}(Q^IhT%%#OhBAvNF`+rWo<0=msnnv4mI9EN?-CklJKj zYe=Q4AjKI}6r5?6DglIM)^%7?9|t``cot9{l|E>eKtXyPEm%U~K)e{MqT(yV(OqoP zjjiAw}4e>MAedhawZ> z$`VIH)%TPfUh2jRh$NsLne+iR?G!N{;ZquoiL{L_G1r3T6%8Su#+fijX;FKnVn(JV zLruf|G*QyTjYM@*F?lw3^S zp-J8*wvN65i{KHj7f3jqO<_e5E{jk&XtuDdZ*0!Y^8$&yV~rLhT$Eruq8dT7h3*o| zM`NyrE;2b#7qurVWaOPJspip{MwTmT9D+x0V1!>NL-5}Vek$|=3-Kn+WMw| zBOQ$i3pxg)I2IF^hw%@{QCOUZ@DIpQS=>g6e?W4hBuEg@u_3#q(6u3%GzoGdG@*u9 zIFv8ZxU5UMhB#gX|4ftXryxCmt2M4K>u60bpMr!40i{t#CCq8yC@n4p<2NTd)#98n z2;Rk~qd7zt)zYE~GUN>}v|w8V0oBnigT{4wj_&`0<0#Qai{|9sEDzux1`L|?Z^7OM zTx;@95oe)hYkVb_5VuLt0Va9Z1}#WT5SSj6gwwe2NKTJ9dIG;cQe=+DT1KDVMOJ<` z_@mtbOA=GPst}`kg}f9vA~a?%A&g<#8jV>!=sgQ(^a{TR9Uf?C@qi77#={j-j`E_8 zD0o*fk}pn+BT?{=NA5Pnb!?z;wacwLW6Q% zZIvi}q>HpypvBQAiqV@47lJebZn(fjc3r*6ghh}!A*46T>oNO*1h_c*L@|1kIe}p5 zUPo`@5Kgn`&}6s}Y!4xzH`}X;ZuW(AVL7p49gqPm5{tQ0AHcc5w*>FG~)F}{YKd`gw|+`NJ;^(>EuMnVFc^- z+?a^VzdA8YPMj7of+Ph^$l(=EiCCdnC~_%aMA8&^hn5o|hY{KN0GGLS^(I0NBgjz@ z(%S~<&F%{c$7RGdRM0?&b9ACa+e-ySjw)C}*O_#_+(JPl+l(MdK}dN!34;s&95IDh z3LG){m6vnkgivaV`%}@lp^WPET}flk5ellT5zrjrV2eEw^#TS$1V*UpJtjopnNz)o z-z!2pU<8{yKmcUOs=g)wDo8X7G6Xo5DrP`30J3#>eep_XKoE{uLKLli8F%K4Q-s(SP`Lwf!mU?RNN6&^Dn}Z?2(a9pkePW zAvbvVyY-D)6v-HG2$66rHP{8cXAw#|o*)T9jZ8JYiep;xb`VC8DZm91?|`<14>RIA zJTPj84o5;I+F96=Hx=Q_^x2b`ceRXYea5r^Vtr=8s&AZH#S-XK zp2rV?TY_B!`6GrXdS!zsdW97PQ!>zdd=$eJX)D19)~?lls(us!2NvXQAdEPI0>eKb z2@ApUKNn}Xn63j0J93Q^WIPZqsumBqD6Y;EE-H@bP>iNz<|oJ%;lTi=JRs@Vtr~j8 z$7xIv!UMw|L+C{qZm`W@`hl(m$<$o1ZGz5}u-T~2fT-aaVM}rJ0fv`8U`HJG;VblM zOHMM#hbCE1si6d$9)pW!96^QB@e-JTbC(bH0e$e{b47J{g+v{L6v>N=cMNWrf!>3Q zLJ^R`#XmwHylv3??a0bxw_4b;pcD+g3$HpT9ANq(__T1#TEFo=oPM>8AR~fnT3o=y z7=ZPe)3KHjY-3TBd88Q!F^{l3ob^~7y@J8~3>^u{)@0T%&=4 zsx?)kE9o`j2p{Bevt~FldJB>sxYFU8vyQ>Y03?o;!SIf!lu0;gw79ks{9}^rC`KH6 zqL=~6#afV?pkqZgDCk&`e9Q+UNXO8`Mt%#TlMl?OAR-APj*`KzHo1C<>%_od{(-L6 zFfVyYv3{W%}Xyd^ERRaSNCF?-d zz%zv2V^g75Ru+yjs&u~O%J4-Kiu6};6bs&PLXxN$LH+{Q*tm6sj8c>PLW5dfZI$Sxgu#Vya-zjCESO@3M(hBxEW`-X4Rn-;yInY| z5lQ)B1Q`|r%A>OmEEH!M7e}+;SDvJ|;VbmHO%ay`sshFza!H^pFqmY5-oqZN2>F5$ ze53}~#(XYHHVb9`=Uxxm1cK~l$_U;K%#{y6Rzt@D7&2^&qbe}CRIfWAnUn~UDKwzs z6;6rvzYHb9Q^cJ^d@aj!+mKed> zatQ!L_8n!!Q6Bhbh9u80;wTRcNqNv_$>gHJUZF332+R!H{$ZgwVNM+7fnRTu)x?OS zJTN5XL8CWArg$Z$Jn##rL_2pZ6jyJRC+M?g2>_%-hgzJru#6ykpai3m+$KgGzk%NZ zQZlZwuBA`!BC8PQi6QJjeGq6(I8vZh3HmT#msGuHPp{A`JDte(@OyZs(R*06pdSNP zkvliyj!)D@O-_bDcBW+nn_3jvLgP9+v4s{Sb#P~vZ$*`IP%W-{#ycKyz9ZY9VH=klwaPZ`MhPC0Z(ukWq}@M5SFSNS)w#Ofdrz`$VZYatHr_ zO0*4@lMO@hW%^L08xXOpxgZ0U zp_8@Bi5x*H*pfmw&`I$Dtv1j}1&IPsdXwU6yRI~(gNox?@DE6`aioIO2pxOz9ix!V zU8O8TK*R)qLvmIcl87S}WP@nSJQ{t`gRU`CftUsMyj+A}yqc2UD2}cm#id-`$tAr} zkV?U?DQ>|)YAPxb(-@6A1;kcRDvn1{%z)&=C64;QKOozvn53%V%k<4KuINZ{BN3Le zg?$r69w6mle^9-LNCQw@=sH}8C!@X~X+rJoxOT4t2$HHL6=XMXos)k+ zaw5eM8>F~*g06%_?u)O`XL16vYVnYBVtN7rS;4w^Ni^jkOlU>t!2*ydu-R2}3#$yS zu#s~t$beAKG1;y_lX{S3H>n_1fvY+1fFvbGDvnGcM>!V>(_5$u);Hhdz~g zU=6$xj5Gvm=s_XH{1NmXLLEFv0SnDZsstGlYPe%=h3?#wekqO{A>|#ABqK=$`377Q zleai)KtMLll?swAGZ?e@jzCxdvJRSh_3@Okv@Dw9YfP1s0aBsF0-;+&>FUUMl zBcGRZU`K{gaf|^e|A6FTE{-!$%z)%#&K8~ZnU)JFehEou7e_-VMnZD?BS_rfnS4y$ zkVxSMdns;Yf!?zTK6+*Mn4C!$IxN#nI^R-fM>>@_PJ$Fyl6AZkby6l4J#nN2DXu{m z8IfaDp>s2h5q!q>S68KeDqBBCoBIFFKS|de(ly{2AMPD4>fnQEL zGBOL2W%xkKhGzsuSc`!=AjPdMka^)?qzDf}DtP<08myRgPy`8N01!tTkQOr{$GAeS z#0@FMj7XgFO2sh;q`2T)L~C-s1j!8={PT*3@UN;_uzLj(sDcE75~z;GMh$W}kHN-P z*XIB{@WQPCkd&-9pjUt!l*6UC49<~W$j#KS!W;)(1Cy;4rGjh%t|jKL2K`a(0?Gwiukn)a=o&ih5f5B>#jxpJp)>WM(_eceq z2!g63{NwsE2SGy1ijJUs883S5FQK|PMuL==k$@f!Nsg#mwf@npGZJYLPJ&b%CxL&~ z5u(teBWRjqQlXn1d0ZcuE!YWB&I`>rq_`OedJmxhYLZ|zq!qj3kl!wR4Z!vLq-59)#8a82;;P7_6Z zfj)c`p)za7K~!ce4<{gqV_8Uv2X$yiCX+Y8J{3jE6S#W==>($IX!ZP}(_*beWP&P4 zs^IuN?{K7xiepnqaR&&ZBdIt zX_2-LQgJ*AeznPUNRR-)H6w26prbXJg$dF!1e8W=5N0iqA&cc4=<{E$ko@LE85nsV zpHy522`RoQTt{=xk}gPu5KtY}@UT#1q>+hxKn4GVlcZvqIQoJN_oL9&n^@2B75ZjJ zNN=eG3&qu2T=k28&e2*&bGDuAW%^_-Qqu;Kjuc2b;P+^zV3ETff!@OcjB$cA!sH&X zA9~M{dk9p^#L+%@BP^Ls%f#_B_{Sq6tV|p|Lq^auv?~ftoTA9Y@g8KDC7^>mlHZK4 z&^McE=dY z$(WUiYf(|mfW(R_6Gxkn5nUj(h9t?d_=o5kP^wDOdKhsm;p)XisN3$h#nSN2}w3FzCs@yG_j9YTO~58j1`Il zU2%*Ke!W$^Vkrl@?8Z@_tRzXZNU#98R?7S*cQbMNCtEEoJxctwMUo)fXHAjj{P9R zb!(lOkrMS4e1$#<1!e{n>}9wrP6ir53WN@T`1B_47Qm*~Cni3)lxfX|+KJ+lmkj7D z;GPIb_^=Rz62(uTt;nDkgA5KA!FWhLHc8>Ary4=8G!V0hb{AU7WE}g8%h-4aBwHfN z#L+rryaOsx)lg102$qRsVepSiwnUT(7UH=G;T2QP@eHBc*?15F^bHx%H?ZvxA~20Z zf`#OST0ss5RJCN7G@#>oWHU^eIQB&`1Cme@$G#|LK;nc>CXRhk%z)(bCs^3#PCCC0 z$rWFiUE_eJ0J%7OB*NAdWI*G<_D7QqG!Cq->OC7CNOu?HU}z?tZy_Yv@-o2&4muX# z9gsNPmWg9?$Z&@N9at*J1XmpEqL=~6MOCnSLkUz?qJoY>#+ehLxXI)0z>UC@fMyy~ zPS7jtN-;-*{1&!eUy?wNX%)J5C7~)vv*4P6cR*XtLpRzgw@?q1K z%zjvsfW84WxdpGbN>qQ9VS)i?PDGW3WxPX^T_a`UcoxMBNXAXJ`lwHD9YeC`v%1(J z<0h+vz5#i_j;SWdfcOq588A0wz}%2wzKH~DkDLKYro_%nkaeLBd$>ZZqX5adl!@yv zA>$vA^d@oq4gLX@sPxHcuqYEpw%{L?>>nx`8-fuyZO&025D}*v97#lc~usk3XvKU9-jY$E9)}1BFcI~YDN~|6I#)lZP ze>+VG@b(S%3mhr2?xx#gOeU^7g^cJ{pk4b(+Bd#JpN>V2E-=A?a1cS3hT1_e!9&ME zNN%r8u*CxxDEtGG$)GqQgN%PbGN_5;9msfj2PDf8U!iXTG^+!za7t9Hm0>CZxjKmJ z8Nsi&5*5GgIN63cN&^3=c4XQnSPd-6e3OeeYa@Nuge5}FX+rE88FUT6;6s4{_Xt6+ z;Ka~1Kn7g{WVq5vT3e6`q4oe=%hd4zPJ2(8AZdYXl43?AjVz8=AuDD?GUpPn{joID9HWyrCBqk+vPo~S5iIS=~2T4%1=kdEh?O~F)WA%qXg1t}aw ziHe$W5K$4!Lne`Oam_2_xch~Ugv1o%3-oEMo3LRCT(k+BT(A`bELU<|uhtQU$iwC0 zh!yrIj_`sv`hNbq?DJ}?Vz|k169W<~;%F4b=uKuU?3atgJ`}CxP_zbPAaRShsvt>2jcwfa zLI;WrxfKydq>%FtNU{p#;&=~oTzS`#kZjF|uh3^o?Tiy;2@QIbsKSed;y8_9TMDjE z^XpB{xH#T}91|yW^(K8$knbU+H;VE__zQCOlvbZLOQJU!XRNWjvG-7&Pm4+$6}CUUV)C@B)1Y@@elMyK|zk&Dv){$mdUx(!;5o< z-2RB;F354oT~}|?aRhl40`rZcxg7Up;OH$bi{sauT(iXy805G!gs$GC;|NkLg!HD7 z-bI!MaZkqJ$2rNT$5-fsB~Pw``Fd#OVz(~H9r2Ne9D1oh@Iybxr_FL8*IF^_-C|9~X41&Jg)n1L=8a$GJXU0IMmq0y9uhpwii6N_t7 zAt!n^XpdUR4xe(t{tr5)#0h@kRL|hWLUF+&v>DP7lh=@B8ZM6KAjdr- zbZ3TKRs@+CnrO&7DJpujya*d>cc?8kOK_^PchoV0u2LC9CHwKus{+-@fG@(9Jih-z2!t}4y}7CQTm7J4Wu@1Uf%w`hSsh=V}pDp)?LT? zPY4L?&_RNE2=JlH*om$|UV#!6v$-Fj%heyCF&(-|S_F}BETe$ z7$V0deO)k-L0hojgN_mTRslH~v;~zC)_5^7$U{g#a z!TtlT^k4apAVY-gbrU;~hzD30?Y$J7xW z)F{X;kRUAwEU4~tf}$?wp_I^q)Iaw-K{|mBYWS+_WFv05AkBwIXExZBQ0IRk)(G;i zcu)g8A*>!}vmuUwBgY&JUDS|CkRVA&Z9&*wsZjIZ#u|ke1+Z| z80ROL$)bv{SO%0-l_ms4Td#;kGdv*NfB}eDqYy_7QebA0E^3HXA77zYJl28= zH5W={LCAPYWWj$PYZT&|ohZm=Cz=(CyzK&Cp-*RZLZ-k^N71pL>b46k6zR|kadaO3 zp~>4W6oT9jPK;HkDIlC|p6r#5ulNUgqYVcZilet6XGlj(-lc#z>c*DQCngSL1SFL( z>1bN;Dh}~uMny8f16q;1le*$Qwx}E1o5J%5p|BKz$_A- zAV=Pvgs&)x1vROK0zVw+ze9~U9+rY^yrMx3c>{t%91lyu%fqrGVOU}w7Qb*xlzw8N zIMG*J^BVr4$s27H;&@mJULF>C9}&JnA9ysYE$^f#(WV>=#noFVt>UgayhD?BqAA3+ zeo^r9p=^m`AAH3>(OZu6W)pOBMG(ib;-7C@G67^Qqfbm&ZE@!*)rz200M86SkHo~s zKL7^wo{ahuMF)3JVfj`Qj-Ei=o1?x$5v$pNY6wQ}0zciI^Q}bM^{-1A@)f zPKow}SSU_h z5hRW2h{<>QOFo|hTSlMWMdn(mz|=b=9%}^j%0lx$^dCWD9*_TE*dt+ClaT%+j_ju3 zCA)EMg~OMW#HetDi95)>k^6e}s8-l~35ajiN2{9v(-TtVlcqKC7zp(ugN5NBI z4xdi)&Uu~q zHEy)7hgn%AG~vI18*z+71ux?eJqv}1H-eNy9;N@|cq7<#iw8G&x>2_rHq3HxqgrnD zISM!4@RAqNqiRSKsxYc=L@*(y?6L6%lNJ9%+^Ae#pNKk2!xo>Dm!Z2@Nu`ChW;{y& z$MHt66Br+EAl?+EQwsKh0Sbhcgv7BPVszmB6N5M%Yyd&EX}p7y;}RdL)Zq7U=7|n{ zSn#4(_&G3_@B-|Df+Fn5fFr8F03*RJj<~776w4w(l1#`2S*08#;ha`yK&4coz)hzJ zQ3X@-Zex8W1o#H3x}gQZd8T_0WeQcz=0d4RgaibeUs{DZ~ThCgu+Mm5(=lx z=oPMmyq^LIDG-qY87bhqQqZ4^0)83=jcX{(43bco)`VWMHWWxlfm}2+!l&UzZW`Z? zDxm>gG@y%y`^;#3s3)PJkuD95bZOY{(|DgRp#hCFYzb&Q*Ag01JsKF!G)x2yXr-YE z4h^lDXy63WpbriD(4Y?uv=bU8hQ{<02@PEtXngZHYSxYI%|Pg9aOjsXm=KR%!T;d< z7~mi>&nxbQR_gfT!KAV~`SL*NM!s4F;@ z2R}9db(_E?E!6!R>w$Z8qo3oW3iJv;2m3>FAJnG=80COJxP=_uiw;o+8Xtjk6E3IF zaFdk~L15}&=3v6~c@WqdNa$c;FueMQSZp=B5`<+_2o&*HSAiLTr>Lp9q1b$w$ckbe zd!?E0t{2opSehzj6NK=4m3Xo0#oGHw6MzI@k0%)he zF(^zHN1wxd>Bf8jJB3+p5(=n#6!-!Kv4H~YQm}wg0AdQ(H440d0ym%_W>MfP6gUe7 zsHea)C~yt!_^h0S*QxYU`Ja-4@L6%G&mX!4n?cK2X1Mh!I5ZK4roYnXmCW$IJ!v!H~ZA{n76t-41IYmc z$snU1AE3{`!ok48!N9`7fXgu8GMeiE>I^IZ3_zQKg@J(ui~(0+U?F2*fz$pRd%I>F zSZEk<1qPC523&ywyfd(1FtAW)-@|y|2MqWH1Gr@X&kW!hevfc1RdixqOOs!MYcSv{ zMe|G?&=_z923&yw$TI+W23&yw=riC#46G>(xC{fX!06_!0CfhSj&8p|qPj>JfI0)v zXTVh$a1}e`pf3f8O9ApyfVvc1NeZr{xejt3 zDXa!kfUFcCD~0GTg=jB@RX_?ZBL$a{f~#nM59=X?oJk6JYo5go_=Sfgqa8WUMd6Xp#KV*^9yQkiPT@(aXkUeMQFvY~dh#lKAK(`r zp^JX59tZRRKhQkE748hs?hsV-!@9b53kSan5S8j~9~qDxR$=c0uj4cR(I+4H>Ko@C+Wbg@x|}SAxgjpvLs@JpfVdGbcbEh~AoK zQ-Et|pIxEe8pHVD8t{-9G`@Nq_*}hh1sBmg=|J-yyQ;QHzWVn-UvLp^^L+Sz2-(_J z>M+j`@@1OO!A=meHQlEnbZfg)tH*`!2N#k-DA)EdR*wgA1N60@V;9l3I#s`i>mfAD z0N>iyr|`Y71ZY~MLWq|E?zIgv!Ojr+W#AvsGY}m~;CY5#i0D1Wy}H*RZdj>myvgcK ztir1ItZU*9edvx+@MgF_PJQnjI`GG;K_@=+71)H@erb3Lwf)kt>9l>+ut}hc61r;? zt_SF9yDfn#uo`H(GQlbUO~ka_n;;Q_o<->HQ}}&IY_uJ2;QIiwa)|zLe?Pig6+VZo zLfZ!b#)Cv&4%vyee*lJ}wyyx5T4)bK^dG=e2<=kP&tY6xm7sl$wx5C8-Vp8O0CBi? z9o^Xr@fGkvfFt1$| zRd!Ih9=bo+ElhRCu(MY%I)--(_C+@lqh{skW>3VFhvXJLp8^1Yc#P`A75xhvNpShI7?;u;b>!G|@A%h<7~O3_<$GrHDbPQH^udi#b+Y*FPx zV@0Gki#65l)GDh=umCPZv5j>e&bM{-3i0wEJ5n{yNTe)AgQ0aFui!Bbfg{GM?zY9V zrTz*2%y+m1iRp`mw&u#KsiXAw^PT7$>>TVn!WT`m4;ouDRFRJiRUPVs{Jp|ZZN|+n z#NSslxTZuyDxr!jpoF7u;K-0s6v}99DB94vyPtoEZ?MF=yT4b6Z#Q2bbW1rZ^RU;T zdJ1ZnkM18H9}?{A6<|1N&6{w8skQA>{~SI#rTO^2mxk{i9#`ntJbtlbTvq!hY4tKo z*IeS-b55tz^_;63tjnBb9@uT1$LGuKLk3UGyBq$pdVk{qgC8H<-JzfJ@zU=`EpB15 zzUA{P0t?1LFuSAUrqH{7wV z%HlVr293PF_G-w@<*z(S?W{NBQBt|=+x>liwFoy)vn|zdP*7I=y!)+x1(>AuDPx=a z$i4Tnli^S6l&b7IBj?cOB%eC3o&V})zBT#D>1^YPTRT!!S{VG5wds~|>y!R(4$k{9 z^Sj5)?CCdCw+B4j^>ojka+&2z)p_$K>Q;2i+!`~^mtLHkI`x9@#JVXB(>DDTJ#lDC zaA)Pt=8uEZ?CSTtb?o??WcQuF*3^;smGW$PYngR&N7uM&dmDDD-#_W~fUa!|q@6~m zf85pIdeEl_<}vkV_|{zCuHuXc!^)GY_c}GJl)v@cnSVw3WOTKy_Fx&ksB7u+txPYw zZ@lQ$sZ^Z>(!aic-ediB^3fq}=(_%ULb{#uf16P7*yDMjo4)v-T%x!!x$giv@?P8 z&&IkXFn6EMs+8l3yBI_|&oNAyo9iC6bihLQxchTu`;ON+9kDN^y7B3V+yRkhqcT?gNuB=aUel1C zdE*R>yqXx6_MMxv(KhkM--9n%4f|tgvi{xWU$NOuD>b|D+WJAMOvjvF%VK+H=9;&l zm$!Zkc?wr3)87ywx{E3mwFdJJTcz;6tYoBl7v4j5bR)ymquS z-ZfGlZ+Cu6O8n2cJA7BI_;o&^e1f^9V^ibBxlb~>KCaoI#{2}g*z)wk^X6-x)Y`fv z+TOZDkB$#}(lz$h`cYs~wZ`1-^CKKhI}X3-6%$)=;MUh}D^e~w~L?>mZg)+G5f0B-n;Vl_~u_E zGvC#`^UA@`VP24RVA_eVwZ66dvi_0T)H(Mb%UWf`$GwqAy7_lJ>C$ie`mHs#n+^S| z%EC^StQK~v(CE!PlgYQL{S|$narc4)joUdMXxw~-U&r%3w`RmvZrQ$SCI3R(#JI7u zXPeAxWKmeRVO+v2x5<@v1m*YyHJxqJwLbkKujjl@C%c#TEF4oY=<1$_U7G)@A2{j7 z$;As=eYc+UV9TE_M#=dnADn6N-FkSl<W;Kpz_q|SVY~PbzObqiq3N|dBwqWYl z{N*h=)%fM&e=L7_!KOLW7BpRwv#CY*dhH`?F1fm?h5ZZ@`+D8cKYEz8pJkX;ZGEK4 zsZrN1H`~7Cr{x;^lbvL<3M*ZcbUT?=H+sv^2d$zv4VBCv^xdw@_Du;stzPCouRHI3 z;rJ_uaxV3oXHdvod+h(cUj55GJN`Y%abtqltS1Qz$LDWbF+Z!&RqAc|t9HEUpN5W` z5*~RvZktC9{IKmoyUBH3sVI}PQt6exHiJKmT)%SKtdBOUr+v8Zzou))Q?1T#XuQc_ ze168Lv+cf97kjk)Rd@fD?j6rIJil$;GiWt*7eA#7G!Y!O0^&h`Q>Us zE1wLF+4Z7AN*RxxgT>ZU-`A%v>x|QTbupyNST+qVCdsTg?;YK?07{nZrhVT z<)?2Q`>6Gu-qju)PFZA3C zGhV#<+R|j(9-p@B*6;DDux|4nAM@Xxhi2R_s2yIV+C#tZ^-Y%jJo&gDdOfwjP!{52 z9A0NpuirkCVp1bB_N`8hG%qLQsXG)8CFDgx{ z6<%2Gc*LUoN|PGQLw{9B$T2Qx-C@&|0+SBgrWACDx!nFm?9XaJOWvNE_ciWqck9%R z$HrA3diCztiWx5g8=f9_v_ZJ7Q$j|@i*1)4*K+>eu4=8z&)-`#DGZz1z3%sh9n4#W zR~=eCro+5u-8v`y-EZUOOAmX;b({FGW!4|#E0+gKC?2>Ymig#nN_JlT3539syUcFELHH`X*Fioc`Ih@4agKhx}$H)_oRV z#_nVdi#H8^$#&oLz`DzVW^X&>qD}AUDSde(DSN)4k zDF2SmbA29qtkQ?Sv;G=+=<3@(v->vj|D5F# z?AFxFH^A1RSKdKs!_k!FCSbgV&nuvweA6`FCQTYo6!gj<4U(`nmV$A-_$>Zr79N z$paFrt)BYu{ko$CUt8WApS<={%$C;U4(}hospZt?*S4M-d3WReJ9l2U$ecLg`V=(E zE5i@3mM?6XX|m zv}yawc=Ai*LskuqbB8pjdSS7A#kgtlAu}I3D+iB%?c6HuTOTi{HdhDby^b*oeSBc+ zAj)Ump*AB64<*><)Nh+(o3vv{hy0r3gFam%~Mk|Dng8KEZ$20&3po^eC}3NI zXHz_nZyP_x$LIOZ@BKaMx~@9CIiT&`uuaUJy*upAA3hYnbJo=51OBSNRaS0opzHqQ zZSC*b+wPDj*4`ELSh}zF=`~+o-`IMg&e`bZhj+=NKdxG}_TlB|=Ka=Px%N6b@J!@^ zwV{gw0|R5~tRB!ks(V6=dCj*&QmxBM7}*{bN^<<;`@0GM+`c{ z-2G7RRoJ7IRX(h&bw%0!?W~_Cdsh2#eXr$@^n}>MW_gD_A{JO(%(uVt@!^wO&97%k zdzhWgii|t^{NZ4k*Reg4j_uCnWlixs*52!$aewKAwqsTeK5kKY|D-Qlr_|rJ?*5aV z@7ndNw6TY`hvoIzllz|7mUM#YZniI{+VlhCj(R@uUO2dZ)w^Hso27dFPPTbmg}HaA zkN3yNZ=d{KrN>>TiQBS#LfyZw-gI!){WoE&<~Rh`HR|9vdUJMDm(@liPK=y4@W%@q zhYpWxJEd+;s(d!!)8YnO1FOU~*qU(v!0Za1iu0phdRKJ4-DdZZBc*P%@wqpkZL`o{ zoxgWZi=Q^9XUD)MzlPN??P1gRd!sXf!L=K{X`b48ZK`*LH_g8dzISKEuyH*i0!F7c zJ+LwdPm3Ez{SU1eMxQA=;Xpf=qmfaYP9OR? zm2&B|{6WlGhfDJf_C>EceAC~%`uOF|cIMrg@b(C`VcJy3f)N>W2J{+nEaO_>^R@oZE9t-5Z+i=yJbFQd;ACQ&$)?8Z_ZWY}ePJw@$nnbI)bp z+R%ABfBPPCn|e9kvT(^Z!wq@Ne#=>V;@a;i8?k(-Lmm0?ZbK%UxsM9>&24#}>HFN^ zQoF%+-|KDsHS%u9l?5G=*M@dX>3hK=adPL{UGq+?ci5eIOX_oVRiC`Tqf=ZbU9Gh` zuFInMw5suYL*_Y5QvE-Cp2OE4RpSc^(Epdw|A$vL_j+D+TLHSJ;6aSr!#!TON-L8*gTiBvo35?pfUBX8-wNL#Jjx zD)5#c=oXNe;#0%GdWn;}b?pWbZ5O;Uf0$r%s&RbS>b$WdJQWk}r>C^8I5=(n+Md&0 zZz*rz{1mWxROqPB-)}Y;F@Do@znbY^6aKhlc+6R6x*=frE;ILv%EZ&ZjN*e5&Y0dA zLmB+>x?(u}P~ABl2UebCeaSTOT(fWG%5O;9XguxB&N^j#&wUV@YuLK?!h+@3T0M-p zWHS1eF(dgu2|8WNP&lZP8elubYpF4i)`0ewntg4) zvo_aQcr_!yefih=e^=VJ@Yvg}t8@PDSSR53+`?yXETtDm8OGZjIbxSsGik2rY2&UL z^@nDxijjF8>X4W?c5J1V?I$#P6w~<*HNWlL?|b{*p(elVdBf$t$)QOv+g$%Tbxdoc z$3tJY-Qd^hW%cQe8jWdVI{!zb)TnUdD3kIL4I*QzK6+I!p;OL-_;yWAmRJ4S#%XhM z^L25|j1?XU(u&W%RlR$^Z%VU~&oY;cO^Gu7((M}6du5M-_NUD}Re!>t2bP=iNAkt? zhkWFNS$E>bru=1o?BR&L^HP-EwypUv_UZNXJAKTLOmVs$9v3nsC(I$+J#eY=6O((% zP5H<8twrwdhilu|pZIKg*j_g3TdAvse}X2zY2bXm%NxZai>u>){P8onwtfD@bpz+V z@?F_>>W6lA%V&-bZIftN`c-#&k%8&rPG#V)g26LZJ4{Tr2zeg#d_|oXKK+Lo>@Ra6 zXYBKWhfXh!%6@-yJ=1Y;P@}&lyY=;YGjRhmbHlJUL0=qxjC*_kaPEdbRnkADtg*9< zQU*vS?{V*Ie>LvQpFK^2pSex=wR+gqe5W(Xw$IOe@R@(WbErk?uGl$+i~D5-In$ms zx~wQ;^n2Oh9}`cV9P?-Qqo#+m0zMq=6IjM9_1^k4YFt#6oGE1v77mVZe@__(d1PMj zlZF{BWX48svVGIAM~kE^dCSMafeSyM^E|QbZdwJ$+xZ7?SDrfKrc>j}J9595QuIrz zwmHhR*SFE(X(leO{+f28<-FMAODAv4Q=Z%7T+{68s;#wg){qY z`q_PYX2(mHwhp>^CgP`YxYbsdlY7daT9)cEz4x`Teip4GFWMbw;c4?eX!(uT^3`{r zWL>>0^!U+bb?vwd4^cw@Q`;8ytys3``C4&-cCO|NKC5@`MSl zikN!sr+N3uefGZA9_v;smn>U3#mo2aI{8n`hnubJJ8dM@AUm$v$>3{8J1|kC$NWfs>o<3#%s@Twl@hmsR)~`@r`X)7Ly&)pO*Wg&}dhdv-iB z=Vs3I4c>kKtd4F_+49BhJF;Gt{0r90-JFy5I3;XOSdwZv@L|@~_oe$6CI-dG#&vsY zvMp!8YtuR{Pt!+^IkG1ye`9xA5$y3YI=XS<(2#x)??hega{Nq} zi%nN4hpr9&9Coi^@Ph%(<2NZHrvLh3HBj=%wqjUcztT}_D%9)Z)^q8z)>TG#ol(7< z$L5L_TU*>Pia(O`?p0Ttx>i*`40+O;_V6oRcgdAsce>vBbUE@>T-W~NdtUIJGhpGx z1yRR4TaU9{n)B&k!O+45%kw-uyG=^jG$Fm5i+t**2PToP7fh|4AJwjJqyxQg@y5)6 zw;>Pj)cj-W(Y^mxCZN0Dy+pTDXKMH85ZSKRtn^+DhdVyGk~y!^o(4;wJ6*bP#49WA z(@nd90|%yNcXsY`f5eX?yZS8{8sW0A|B{ORQ#YR*G|0sxIrC_%Pm$MhH%+&GG3QNT z(&8Q?tJ<_)Yw@7!j)SF-U2;C#Bz()16RXD^>rw9Nk+ycH7sTH$-$4@CwfjMN+PGVi zYps{xebB1bqJh^Abs9KOmi>LruGfceygM-O&6>N`wEbqMgd;hNh7H|u|M{323ajS( zPvn^2onSvAG-k&&qn7i7cZ38d@6WCHgrPsgSjr~%2TD;l8^UK$c%^h^XuA@>BHhx^#(i=tt^E-!b zd=zMVrFQaYhx%S_mhFwNnOZcg+PnJbdHq|JTE42H66L#e zSlH)j%Y1)XZL!+&%WT>XD+~L*C(|3ZdSh+z$=G;7gmd7BCCekD_aE(JzSDk(`;Ot~ zhD`hU{Z*DHEer1GBaPnEdv(CY?RSP%`V)U;dgrs)WMwQ$qd-|{3?YTU?`LU3xGxk|tGu%k$T1bszBV+nkxUzYt$BiwwEM;ZFPpvBI}azvXoNx$wZwK2rl0PK;f2{zP!j6XP%R*us;xW9$!^ zx9mOaYN_rATEA{!*eEA9%3_0ILZ{#kHP3hcID#1y+Bjs|&Npj!3{3QrS}tssdusS6 z+n+I6F`uelf8ji)%-Lv@3a_p{d4KHdU&p@pnrbz5Xw;40<$rvR?BiBx-^xt~(;mx@ z^tLSHGGx=Itq0Q2Rvk^>c)5VdJJ|YbknfY4Is4uZJh|oW{opZS4ma1`el|DbMeAy{ z-TQ98`1ZlG^9vgO`L%Uo{Yxvm+}hU1JNK#YnY?vBp8M=?x_|4C_NjY^Il8=#bXn1U zZ(8I|=MlAf*e<>DKvA~J$I30M7|yo8TWM*eYdtSTVcjIl27fkBAKLhS0PWHA#@+jG z#!?=KY8xLplW4irWRXMS`=^0a!j@L6lZV}0`}X*4qx<_0g~|2}y)lS-_w(G*tj*y8 z8Mh7^d)E4PWPtIHQo(f&%_#RrGJZ{qpy;nZ{**p>WAv1By|dhvhx}{Y-fmVZ-ZY|j zm(e}94UcI#e^pzfD$_ef_q#gO)zj^zg|+*nbG;V@-0Tvf>^8%mNgI&W)MB?$xec{1 zj61lw)1@6TI}EmU=)TKo`^%5-!>794otauX!fxG%oK!1i=5{M8I#$|a+Nl9ES3Jvc zp8GweyYyoHn1+45AMJSe_FabuuUfWF&M4e}|Jq5j4^dWKOAH(9*KZB>?mefvYl zO_~hb{$S$!AKyQJUioI}RK=|DV?Snvci8ukS=rKOsYIDr!6DeaV&&1F)6X>usNMU7 zZ}4M}ey8qEe*ATpWy_I247Wx%n({FE`JUC&jwJm|>|}q_`dR2nYWMlY%_lX8%;-M4 z{;spRm$F-~Ze{CoE+?ngnv2q$KnKgC!DkD{oQv#na`M<;x4M+u=Da<|SQ>rl(z;Q> z(G|9YjNCl1Rr{^8WDDM{*!Ru%PJhCm{(kRUHhbCUUk9f%LhdDgzl#8&senfO_M-ki7eclfQB?R%Gtb$Rb zb%&aX3;jLXpSc%V-Nc~J*pRsH*5l?xPfe?l^RmoLYD<%(E{$JCobz|MV*Kp+i;k9c zsy;2;dZ0qjVUAZm8M-75*fs2cp~>S`BN`+OU6Jvn_R~){&Kk7e-E3fcKc8>Qf1G^r z_{*m^@1mBLy&teM(ciB|`>#JzZ?tS&?OYkZ%I19o3~wIrDdW?@Wt5d+g#5lg^`-VR zrD3Iyiu#6Inzrp1df|+H`tz>6qdaH#m0#)~buG_gSH+Yhm#k$+lQ;Hne@oiKD#dM| zdtdjZm#kZ@SsSlte`(^7X*cbRmtR^@Z%1USNdE_vD_}&%NB&yW=;X zyY{~$SKjLvpB!BO!^Qe*hfW%}eng*{C%rxAju~-rg>32A?lCoFRN|y|gU3vH5ch20 zsr`l{qR*Wi_S33Duj64+zqUjrkFV4@NYUOY)G};uW;MH&o4Q4q)O=KXv-y_MOJ|Lkfvx5h|_S`wUqxmfZxA|++*2d0GKWkytZ})Ne{Fb5p`@UOvy!@2RPQz>> z(`U?7*p&aOJhxzvXJnH>)dG!No22gR^*XWi)7OuK=1y8RvwiNc!sO^HZx1t}R7~9+ zo|AprPY?+a_SNi9(rl0K2{BU`D=;jUT@SS7tvv*yZHtE@{ zZ{v@rj~U!)>g{GfjMiQ4)Vg3&aNdO>(>C{7K6${@O&xO5SFUWG@}PV3Rb9gNhXse$ zeAuwQhqYTwholva1NOMMZt#*$_UJ$PT-lhQRp*zO%v+(Dx5NBu())tDzkhk(b9{Er zX#X|a$gs+v9ja_vab-nBJG*T+ubl7YKF`!KSgf{QyChmM6n@}s^>-A%ktnQ53I_re> zr}g{$ zXh6M6_R`0-@ksuk?dMW z?1G=1U9@^c>yYuL3$w%WFDC72U|=z%jnB)P-m59U>!ZDWM*M#8ChO|ROFRA6ILA)6 z$r*p|fZxzRle5>kZE5|%{ne+FQ$DY2V6f}RyV!%{5C06gUZ>HR1s(?;7LHjuZ*Y`n zV#VC^7fmnp%1N)e#VH{BSGsTZOW(C8wGZ`cf8;g-=ZwZkoRP?e5gO;Zr9bSTQKQgW00aX3cKT zs%2)gy@lJl%=Z(G?!>x=yvnWg_I&H#7hd+Pb!6nKkX+_@tsjLBjI~+lh+a>UGMW9O z&pNGkXF_ZGot*S`S;9rnV@)>(Hgb31R^I5)huMUxYwhGonN z?tQ#f*RyFuO#-Sr9cVVT#q@Nyz2^p;IN@@onrqJsY0IBy7SJ8ORyb&PC&_T%(q0SZ znvH%td(@3i5q%2inFDSNcqbq1w`}s^h$a=b(Ix>dHF{fLJ7HPp^_tPn3D;Xq^cytG zybWVM`9Mys*_l@g&CI`y+GUj4e$eL)k0)H|VAbq>fz9o5=E2U!_1)hzox104&*;g! zoU9r;emj4ASO5HI`99yQ*Za17s=aT{1zAkZ1}V!@PlR`f4`J-b7}aiErDvtsgz8&7 z$2n{@^!sW5(qo_R?DDl%z1nu6a`~}_PJjMRSr9w%_a-lU2ZzDQzp@$ye985k9C+c{ zQrEM6d_QLV`t)Jp?Owg7j&%QIl5HN9asLHF#=GFT8$HdU- z*51g{yQ}TIa(_n6%Q24f^5fPvkgqmuQTg)e>K;+vHeXUq2PRzK{wpxWZdfExE&qfgPKA_U#qae&+OM=_$Ky2k*{!hc0%?`ebUl(rbp@?q%^ub}#!> zuhrbEkB&->YP@S=A6VhW_7$6>^K492=Ga+!ceZK#!d&N^9YK*!h4p4P==n~0ByeVp zWhsN)X1{q|DsN%`#}l(B*t@xzrL0RkS;Os0k2|M48@p{fS+DM+9_c;G%uea>`bKD+ zp=HZS10Ij=SLN9{mu9aX2mCxNZDQVaW7zT124OXu${TO!Re!_41!Zg2ExrAZxu?hD z+G}4hXY+&Zmfv6ar+#Xqm+>jPmwsB=aO>6QlhLo-eAaT~#x{O)Mwr#?IjrpZm=2F3 z-IhBvZ?J2_X4k*6n#ET+gJ$ib&yH)(Z5OxtZ5?OpT*14Cb)vAX_*1dN8z3lDjti_J1uUvU+ z-;o{*El+(a`#apGa-Q!i*^)+s);yZ|Eqa{g?p?CWwq6m;ZleniI!uTga($%3{;3Mj zi#dQ5*WqYm`6k z;HU~;c2r0StmVJ$%!I7X8Oo$)9kVm8*Bh1F?7%I_%(C4}9er{6prykE@8Ci5{#_lm z{cTY`<6>Qhzw)cJ9cdLjct+RX)ehV*b9Y$VUGj6^U$_2oxLy4tj~vj0r1mIrO7cR$9Bh z@wo{%Cs)b-bS*XuO=bI9J2Inp*`rh8_GuGF9%6p~7(?}xE>e7L@#UR+zboq(o^01~ zX4%xfHr==FaI?%^U_UZ>U;SQfnk}a2o6g8NUJsJ4Ej`zNxTNiFqsK^6v<1%@fA#Un zt2JjI2$kG>u*v_U&zQ*QqcsKvphLE1r8=|;L8>)hiJT

1W5|2o;|YDt6mnZQyl2 zqSBFZ2McDuNxa_l)%ZFoPEoJo92O}gO{`nbPhuW?Qk!inGF$geE?K7WNqNpWE6+@C z)28X=%1D;)wVPE-EoD+WJ=eSb&%H_ev%cSnczgBx`k2~H?#-BU=j5VscZb&nv)S~R zx4-=O!EaYo&Hj{#Od;7_t?kGF=_$AQt!kgm^)|V)?fUKE;rHlwe&NnNkVn*;8g{y0 zgF5jGn@L;7sx;{xV%vMy)mOf8S!YML$WOGjH~${Jp%IH6S2`|x@stmLUNtO}J*@4R zp0S^vx<6lgy}|_3RT)!y##U(V}|_vQ`e&Mcg}_Q9cBUez{~-Hd-aI zFMRmlR+Ha%|NeAoyR+L@jBd0c_MFPmZ8t4SIliDHHQx&PZG47br(wgBy;Lp{)@q8O zOa1Ddx_Vk&JQXUpbG`0C?Ra#cLvoXa_UTBgkzc-zI<|O30~lv>kH+JWXO(w9G^A@4 z|ME^(7Ig4gQ66dK-}a2bb5l1T7f-AF*~>2vI+2{ZbBFu;ydxQln@#>Uu1vc0oX?Va zRmZMLL)Q5TK7&JFba>C1azb&(vs?)@A;V%22YPa@1 z&-_06`So?zqrT+K8REC5RM@^Gv(dkqt5di2U)8D1{aVBA8;?se+U35kUtGd~d8s+i z$~8wzLn3mwV}>4?-JW?mrr${!lw7`}|2Ct{@Aa3<*Imy}`G1Ie%cwfGZEG~R1-Ibt z?(XjH9w4}T(BSS8+}$lW1a}Yai{KW569Qk6z0W!Oe)qQ9-g|!@fz?t~tHvC2)EKjt z^gjBWwzzrLo9AUtPRVpKjBH&iV69h*$MYR%UaQhfH+wCHc7Eg45wm{5_{}=gwH#YB zra^Z4m`Vzdx@-lelp6&jw%<*LGf6|FfllBfY<@H5wUg_2P6)NA50bW8K9m;_-bYwx_PuFduh5Y$xNaT2l0Ma zM%v96>~G}?h!$eME(FvOjKOkBNAmf{`}CXn^-#7nJ|bt*qQ-HZA~m(h+i?CA}*!?Oc;P~GBYy* zy$@Ic%&V#@rN3!0IB?=05HmcJsELdDtG)j@0BUaU zUH`zO#bm@^pY@8u1)>3LsXIu?h%1;l5;Mt|0a37W_4>10&C3Od!M{Mes^%6}z$Nh_ zrWOJE7MKGqPL!MhMIEcxjwk;*4cMvX=ICf^{@McRfM5e$E=Crhc?PQiF*m^D&|?F(|AZgd z21pQk92}gCEC4g;HEPVj)c`IOK!4`}oZ$hm^}u-lJj@KV@A&uWJOHCk51Ueea+D|K}jDBLW0KMs5y{*Xi@T&X9v0u)y=$ z-h_h-m@AM7?7$I#ssfC~^H*d5G&tv9Q84(Exc_tm$RcsAKYRa~JFg)2zjNn*12F@n z&woOg4H6X`f|%ciUOZs(dP~Z4C!@81&Usg6vCY??CUae%?Ap?i?lFgC*o=Pq9iImMSerZ>>+EFA`1Qf0=BBQW zdw=~`fYWMK+m*p8lh!iZ#pJayrNGY(3WwVX8~c+-L^jKDC^@vv{;!=+#Rf)eIIUp~ zVjDqUlDdc(y{^(Oy^KD|ndKVJGYHt)tbMY&fu+Y14G$g$zW0TL*m#MO6KOb^@ok2l z*XT1PDU!w?B~#4{XDxuG;U*z~D2Sw%0zPHfla|>MAVE+mO%~;a5tk~e5RQZ|gXT5Drw*b>JkAI@Hf;Vq+HM~jMF$CT&BW$>oi%FnTJI?ZSE%xyeWJ_QL!y? z|9y4)$HjUrO8{>U6zFR){av!JJAvipe=l$edjOvPT95xOV+KHi?XRWH%mi3w0God( z1AmwfK-Q=`XxLl5LfU~Pg0&Pq6K z_%3X?U21cP2@^0|>(6Q|L0DiKAP?Xf6jnvmz^6akEyxqYf$ctXUA&kJJ-D~Gw^wvj zJRX(yOsg#{prgD22P6NQ+}PP&JT1Wsh6XmnUw9rp#Nlti1tEYDW#hi?EJ%C=?m5`EC^7!NclzU-Gm3yFm)Z4{>N-(46vS%t*#~Tf^*aAu z%{=)x&LxcYI$t!qMA|VwFTAz==zSU3#^4~Y^FqsjC;R{!MA$#ZOCfq=h7~X|IIdak zYZ}K7HNs8Ayy^BF`QVZi9a}EkfT*l!#OYyI39^d7AufNDHGaUTXPlu`^@bZyxIs^= zN}qyrhc!Tb_Gu9TtEt;#dr~IgNk_OH@eMK*H!N`hETKC69f)wbjyZvFVC9WJ*&RmF z_SYbhERk`1NJ!;)ZS*`H&(`Z-Q5=dHbJ&UAI+4l0yq0cz1O=pwZa7pi=nr~p46bp1 zBtP}>A*BuBvMWkJyrF0vDChTwSb?O$m|H9I3osjOdCG2$UMqQ1nkwU2F5V}@`E}GG zJhwkmaF@OmU4+{-%(2my79=*jfrm*>qOD~KNiRO`R1j$E8ca_`;vZSd_iaJ=na$(8h5$V z#bw`m7*m3Jf_iCN|I?u5;d+RAruSju2^$2u_IA0>S~*4QxkTL;KN9-umv8FvblUEQ z2;V86Z&|j1V1nqc1|Otu@90n4S`QL)wun|7zc=Lck;&a|G5Ra|!4TKJDWWofw*G|@ zk4XFyh6H*U@>kRv6?h`iX`dh$p!St;jvyb9B6$O%0(sXr*QaWuztDYu(+YKGLY31i zCxvi0K(ykH$U74vbVv83YCELa8NhY9H`}D=#P=M)9Kg^@a55t5498jjt;3OtaKQbv zPff_v&Y#38Vp%OKVnFW)4f{Hs8AThIUL6!iSudOKR4Xo?@~225thGyGQm#+6`yZT{ z9G4vFT7~&jI^Jc@4oW?Au)rnFgmiiO#3wOPY$aUO!4m=AP;t3TZ~Lvs zug77w!V3-8Iv#vgGsi229@YAtNb31U>pj+HUv52~2S_$+klc$bz)iUqDXfXd>D2fB ztjDFfA8ge2oWyqM!?{Fv=*joK&a|W-+21A8WgZ2XMA$NBgWWy{=bDmGf@p0A1r7&z z-G90r3d7icHL$Hkv};A&0y&=x%J*4U?fXxrZ8N%p*eA$@NZ|ED;Q8h4hkt#JNVk;W z+oV!5p%PG=ix^xERvgWA&U|v2=uQ6|^lOAHF|4?Dr&K#M_dQptU1n^(WCIc;xK0?c zPMCrjfv0-X!sq@)j%`$Ox-SP-8@vL3!PYyGb6;weHrk-;N>kIhp_#!>B4x-7Bp@h(Z*%sH%w9d&eqF)n^| zt&$$;x9N9%ADJnrFyiz!ffYrY+_nwd4{Jyw#un^$S{0U}HU(bQO}QWAH*LQi0~@Cv3stfS%sg(>o|IZb^& z7ANO1>|H;TscLg+scd%TD2>0TU$oYL}){zhC;5!oFWevr^yP1H-cf^ zAcFuo2ae<|jc_~v&g#h0&IFUol{|fH7d46kRi#3^sH9(5q9gO0Yndy(xqR5d5NWAf zncW_r49g|-g5WR}j4nP*!61?aRk<)r`5P%(Ue8R^sz`M-B1mrOfcw0Vpk!2|u+bfY zN)ka7RmG>7UJ=;{B1&#)xJwV%*lk8u_{@>AytH}$?cV_i-RTKB&toSGv0b%m1bBvI zwO#fe+=f)uwMhrL;~xAk`h#T$5sxeF7H#3nH$T>7#U7HWBf<@OuVeX*+Gci6Vcq4= zEYFr_oGheTO_he9kujC@i{is@$y~W{4doV&ii^%i8z!w8+^_$QG0`E1j-ytm;E0p$jUk^o^~s&fRS~ zp2s(>Fo<0}W|va9VIN*Twu&{rVV81CpWeA@;eIkNj%}Dbik)k49nP8iT?`}l5Pm;+ zOQ5qmP6m2D&x%g^`P;Plgq+lj_ex)M7(S>a&JrKG>Bi_5tB;tg!cC)M#Mf1S zanfs8PXal&Wx%8Vx_$m-A}Du1h8bGFB|-zCC`(Y6%V{k1`|$ZYv-}RUhaCZfCmyaK z5*dxWyvdBN^c;@KJ9Mgr7J9cOCOE34FYH`lcdw6%9vQj znX(2aH5%Ah#OvGk8XEf2H(zUh>glzyHaT7)H0F1@NWNr z6BEl8B3GlwlQ+dwz#tQwuRoFAg8)Ib&nLCfuim9FGqTXn$>sC3^AZOp*4qq2Z4}K# zPQ$%pSvWi!yxIz6JpLW0^Y$ODQ9|-NT#IX|+(+j_F4Ic;q>xw%N~KOeZmpU13G zDy}cjX1+szwAKOR?!?Zla{ZBKF8v5I_$*MXQp6AvTf2tr$5&>Vz%ZRq%QOAy7N|(~`Ys z>$^w@Idx6-bj1~;H1GPD=Q=!6y+P@0c-c32an>UI6i&tJ$j2Ku=WS73l!O!&*FmMK z5NK)a3(FKh4!$JbhkD8N>uY`<&+sNFCR&mtS^p73H`qM8&B;sG*;KB7c0JVHe2X^E ziNvwC{{5J(1!{D`V$yb<=FL=PDVMuraHU<1l{Kx|Z$EI+L(h>GcvN`mfp^Kukt}6- zXr{_y3`2RJG*3UJhVUK^lFf*r#0dw8@Pk7{gHYckkQ0iaO@L588>7a3eOu`XiG`(W zf{xq|-El9FsADM=c`u+EPw5w@UKF)O787#h7`fFCmUb_Y`Xz8hg*skU-}bg^#sQYV<_)a~t&qfX;=#1jB@Ygj51ZFYz!M>R<;!Tt5oL zpP&bSL~P|j`d#APE7STx2#pOoC`US9+9Z<@`^7j%qxDDzgcK^%5Q;)o42ZppnI8}< zGc&;)3J-I{tr!%07nFC8tZrI$j~Q5o+0&)1mA3l(o?WY1JWw9Fo76~GBUwe z#O~?yl*f39h5JlSIB>)*#vdL*pA|d`63O~R0}_!RN(UZ#*)NGpK=hZyQviKlL>lFx9zR+V~u@0y=;-iM{9f zV45TCZOLvsmAXR8JtfCJhQrK+YJk9a2`LD_%IZ@Sex=nXC;V!y zPwyAVLEniYgeuglTZJyvD{)_b563v9G#Gb@UWF|*MA54L^zOtl{hq}ya(pnZo=_{C z$OHm`X>fWV&Jm{uL#S`BRq_c|Ya91c?CCAuAhHuh4W`h527M>)QWOygcb(WTAcnpZ zD{}zZk-#29=L;jt^0$Va8=J_g^^Hwjz7%2s#n@P4`vb2LT(qewPI6K`;Xzj!dhA9*#tPpf%S4DIdH$ z)69EL!msX^cKqQ4pfyVilXu8b4yJg7s9T~i2rZ_i_gL=6Y4=!!?|1q=)qT+8^3;6L zQTa)_BeLn&Ll`d=_#>$XdkT_5;&x~*Yd!Imq8zs2_{q9IF+R!oKq!H9E)kzlrz0G; zY51ACKW*GM-b3dMA%IRiLqq)gU>t>LiXC6gz_ufd+} zT2G}XgiW&@Jx`-(kev^)W^IBnYK^GuGpU~O?fqOgzp1@U|Xo9-2oSH zRNVpn!PrJ$QAtsagpm^>n23i&6GjHH-*EdGmy1(;K(-^{{fW@-7^6>iH&7p9bia!o z@nx$aj&ZKPVO(i+FO6(`gtZ}>{v~SM=p|d?{HXEU2=<6SnZK3^zH1xku;E5LszG6} zoKSrW%ZR;Ev3~Ep0-xS&K0Z`-0lpr9&ZEdXT?6>Xg|y zf-8$Wr#xp<-JaU+;LvoE?k{>SU?942fk;87;5I~|=NNpCH*80#0KeziwD#dJvpY?h z%pjDZ>(`u%a{n=3;EeIG+(pa*GS^6@wtoTt=tFBe*VI<9?m5`!7p_%yaN$j2!iSd* zM6!3iu#(JysL{rd{b1)DK36@OOXHv#k@>X3Oubl%b!Ybao2K44ythAZKD+6A34xvp z_xNLVo=!lFGBK@kn{z$#D|f?7EP*fhBiOA`nc1JE(p&u@+=}l6bEU)l-rf(h$g$!f zN}c?I-L|+ElH{N>%zbb0@(6wYVkBntAT2)(VqLlXnZy~@L5?tJaJ~9);@{ed8ojL! z(};*~lG{CU$VKP;nJ~c#ZD!#Yh>xiuuV~ra5vM^P8vE1REr)mz^Tzw2n(G*86 zN1`ApU1b^T)*IKEs41|G8Wu?I+Tjbt-ZcYNd3|%n^UxgZR@IvCBq*1{RY~4rAHd^I z^CDnK3b{82ocM>)Z&y$biY+v~DAdtS)fmwd-x;DI*rntLQ?q!~4=*vUZ9 zLul*kJ5?=9jI^{#Ir>x(elI#x2GB;LBFC=H#f|owDG!(4x6p1j-)zon^-_80JxPVv+GrZ2`J*0s7sa(@{l*e?a zMmn9#gK_lGCs`Y-nZEwrbqI~o>al+8`e+iRZ|70kg*>nC`|f#`#m6>|Ewe9g@CnG0 zv1E-tAz8K39XdlEX~eOnmPT`N7SV%>QdOZG7*go2_Xk^1@PTO&sPl)1hq)`S5R>|S z35pDsC{ss4-GV(X*RC7M_kbkGS|E=LXK`=M%8@Fji3{I?UQjP^W)P4gM9npCNNy>j zGFu@)>z0wmSlC$Dpco_pkx(i!CbbzrW2fY)7><2E;2apFWO(}BY?mvD(UF#g`P=!i zWX-5O&uuK0uEn+ulx`J;<&xWp5;cLVMeke8%S?59eRB6wSi}ADWA6bIF_%^0m+;T# zYZr{&DC6w7QIWy%w^p4Si4Jh@WD$mN%5o-^vn>RtD<&Bb_7gh!5f`ZvTKn3{OZN6g z6P@Kpuz$8ah2QHaE10KD&(cj_V5<9xKLx_k3Xw_%>3*Dico6JjI^3NrQ~pd@lCFiU zf*t~vp*?4Wo=)1(x~08y!z%LFMG{oshZiZQo{_LpnVJr{EjL+ zKdLxCqNP!UOC_R{&+k?R}@tQVs~!u4DS+DDd5+k0OmLy>&W{?s)t$Va^{A0 zW!7S*-<3*TRi0kb<%#B0$x^1+w^g3`(N}Ubw$Wmm_h8+By=kw~<--I(#=eZxvlmcD z#W(P}16}#El{7qNrWrl!W(Y8Saaa2d7!NxYP%k}=@+pPkF`$`s1-9Kt~_ zm4Cb+TGp#SkbZ;j>n}W&+AU#+b4lJLMpK=1nUzdUj(RE0oM{*esTylot}aolPMfbq zn=(t6B5T==p&n&PND68}W_c!6&#k(qFUaEw&X!`H0jzJeXOo7&$&1pc4{1 zHi5L64QdZ);nbx2l9QwlCyVUT3&_X9mlyIEVfgb45eq#|;)mzYj_fp(#_fK zb~mbZc+`?#AF6FTx>ZT2lD0vD&y85YS>b+T-Bd43qAsQpF)W{Xhk}aS0E2{jDO@{& zbgTON+3@EUwmREX7@2`x%AKr6O`fVbf35P28V2g67o|){UqX)M55An5n(jDjHxD*fNg5!C z2;I`_P=Xwkz+|oz_$i^Zi2U{j!NRWW?9}$YKd@%$#q*Lw&7nrXS7t+@XOD7FFh(Fb z8;>sDuciI*bU9~vE9ZW^RYgF{eDzM1u?kZNG@Vo>!PCa!#Sf^ky=pX+5Aamr#@>uA$xr+el~Cd0H#+Bn^Msmo0) zWz8t(2FpT6u->+|GWV7@YRiyQ$S!;f#Y(d6B-@@2okEIc`l$8B4h^dpzeJ_L(#IMG zkbs0{5~Nk8e$hqoPsNNWK67_5SvYT9`cG&Tq1Wdr$`^r~w`h-pNx}~15uu!wMP};u zk=cH&$rWrcr;;C|H>9u5q6y9W}<`PU!SmCf<=3_G3Wclyj)Ohu_( zG)6-;hy<~Y-&PDcWa6wcTYY>kh?HB@fUNjZq;CF1QBJ**Xo6K$T-@xYS*(zDQ@N){ z$mA@frNZVVE7m1px5&e3!1_E#YDBYu-*}JXk$df4M2t2ei@?h2M7sbMB1(r-m?&q* zsm77KU^M{UoabKQ#eqqv0q!QgPNc5FpjNLSWS+A@|uX%!gIWjS7~4 zf}F^a2Su5vX*B4Ai|{55f-XInj&k|i5wq1ss1SNl_(1C}%}Qk2kbrLGyswTgZ(!`CF-1b*GS0k$mA8d=c0KYgcd4E21(o z<+vO+<$vKsy-}QsO(k>5jE;&2ov9Wii-)&}t$M$2iObeoP}5L7^+b!3Ld_I{ebG{F$xF-K=QlZ|+=>xIKMUhlK`Ji%y0rE+3fUJZN zI{hZeuAlrMkQA;hMavmw=|tLB`^&YQWHkuneO<5|v_Tnq+zq-S$>iyd6|gj3+N)D^ z>M9&vcY!E*Yhmme0lEw~a~6?qnX-{$Bxs~1rXFeHIa}@z3kYfU6iPkrbyp?haRD=z ztPdY+&lI{x&&z|O0;B|zA2G~wl3Fzdh&PS9yk|Nu|JF7x%>Mzr4&0UnnzL_ z^?aQ8p!Iwb$O!*!8om^E)Gy1JvcRk%*`}o4TZ>P^?;3WrU;V{~$7J4dW`8D?5 zV(kqEcC}8XYT*Lj0qmrfD8`^cJteJ7O;%~J@lJ?BTtB*eaW3I;V$m^u4q1ENG|>#y zUZG07+(B24GpZK5EaAfig9v1QWO<+LWg7M6R8192|MABos_xLnYn4$lanla z`}@+)bZhz2CF@6;HW27O3W8H0vIj$=+L5Has?i?a<7oyczxwoATq!)Prs<~GsIbr%8qyCGdOSZ!9F?(>K3Mlsd`KI?7t)ID3@bgEz6TYi)(@GdII z@DHu>I5>bA$_G+1CaCfS#74s7XGP3a zJ5&;~tQE9WFH18SJ~_!YNXR6!HV3!PC78riuu;q&yC&MZA}C$Jxgq>&H#$2`am7RD zz;qF!QET}$c0((Zz;G_)DH|zc)Hb@@D!KAz_GcqHnkttP9qskjIzFBhq0a2bMP1sj zU#{BZip`}YSoikXj+y9wJn&6tDSPhl(>8{N6bBzOq{J6VJ_`JdkF%_!OdlJ`$--~e zQQ*wfP~uoFS{2NwY49HZd6qpdGpi!ejY4h9@G{EK8qX@O^Taop}os5*iiqU9oYiQsz>*lN2Ki;UA!>y(g?p$MjS3 zkW3{8vy`7|Cg8k+yEOVLHGE-%H!!D#_v7V)eRF6h;3o6?^IYdY{4mlmyCOf=Sd?ic zdwz>A#TA^4Dsu{gQa_10p6-n8sIrK2Iljg(IQFG}`ppyCb2MVpS=wl23*zCnQ#82v z%@*rI=&KB^9CO`Lf29lfuqL+RKtZ=NTq<&L0^L_VNj#u!%L=oII~tR zrE9$GK0Y*b`n-IKjUuYtrScw9tY*wSmxeFmQDXAGj;r-$e^`=CdEQi6s@7$0w5*ud zKX=X6a9=rp{}{Q=7?3r!No=Ny-1r_wCE)q(FxO=QG-*O&nM0FQo}VJe-kYG@tZui; z3CP%-@yXQmL+lFbO*ooz*pkCl64;?P7|bIDs*Ak|@E@(dap_(Z7sD#CMGUSKO>8T< zaz8oSyWdO+*>g}*DOHu*2|RV0jqMDBH@aMPW;@k_Mge3r(Y^L?fKC184fFa?~+D3<__$8 z@1}?))frM0pVPaJp$T&)RObS6JIveeVuo43|FlvwZ{ai$STJzuWOrckGh2EnI=e0$ zCJ@)gdE1)pQ`-iqHYs_X*hY=&4|P5TO+Dk1oFBV4;zW~saKokA>ZtWLVXD~5D;1cai*uUl+U;#Tw`|U{i5y9ad}pCL741rGMpKXd z-)FJCWo7xOvQm4>`0MVQmgESF?CkP-%m#X3VNzj|mWtvzXa=ksC1S9WW-T8g5~QO! z4Q1BwG850<_=J+Y`6ydk*0q zEQd!n=QTFg7Sd|a-P16VP-5K^Sy|VwanT5TH*+yA9)D3ZLNMq>268ENT;gH2l~|{4 zq!uN=JeF4m`xG{pG&#TDXuZsy`j$Mas38Jh8rMcw#mV|g7x6tP8KwgX?q%#LBJ!|6(#VJC_N5kS;iO=01>ivp4PAv;R8~cl- z8uhr}wV65yb(qVZ9a6aroqs&NVhd`^9&I!CU`n555o0?SSuGT)O33Y%IlNmK`FfgX zt$~}coOPzWObQISQB&|?Bo7UomVxz(921REqrkb%vO=X8j$CSKIeZeOYeYgHhqh&A z&o{FaH2-Ti7MYT7+RzHODt zd~oP=pCz`mgaR&;@CuXn(EQ0>XYV*LaFsmf(MM_5%pSXWxL$I>_FN)b0`c}t{F5iK z<-!p}XCpuTgj`7oNcpZm8~HvX#JCsq@l8hiiBlk zG-BgVBlZvW>8Ez$lXh#OH5ZB`=k;g5>&mV;F>UZsUCX!R2gcO6;nDH;&3`*d7aQKF zywlXs@|cFhq2t#OZ5fu{$Q6^1bP*-7R*!Sfj}5VfWwqDz#^HOedf2V^9NyF-JmBmx zs>$#*xxwHW#5~ab$|@+idM&uyHiH-fw;I<&3GP2E^;vVE+(V&4#CY z;9$_Oef=X?h2v&VVzc2i`M@+cnviWjy;9HJCZsBC>VAP}^XcST37=`x@8}r#vfR-9 zK_NJbpzXFnao<03i*C`0sQ|;=(~$}vSbaZ|!~^evA**>w>Q;rfY3!L$%{u9H#MsBa zzf}rOhE%X=fKw{Y+?cABA+tJz=iO9*4-^#Z$fJhKBR5LQA}N#_;m1+wAXB>-vB6weXW%Pp*HOZJy??`1z}6 zwyes}H(FB{#+@|>x!c2JJY^|A@WBfWaCFde5R^X%t(wNHfv2NVeny42u2TPPXRRO% zZV;?~%G?YwcK@S=B+piBIk_T06s%G_Kk9e|55-s9CnyH)WD-8=N7J1hFS9NxSE@Q! zWw~k}o5fdZ)!Nt_B1v5q@28Rh?nJZ89lbPzjG5S=r=U(qO$=i%Wd=6g=&VGgeI5dW zNcH280Quq47Cf>d)-tsgI`xOM&b|-x@So)O$!vf_4p9ZvNw*>SRBAVw*CxM3y2NW~ zF>?Gkz76qO4Z%N(7U>-xe-OLNLGevDPrR|aoz=SfIVWN`afN)|*?pN4@RD&=qL|yG zT+;f|J}OlEPQbmAgTnmh;at6DA)v^17X3B-AknJF=X^qD7nhgGs>{toWAJb*D|aP1 z?aZY_*P6|ls$(N(Wt&E8)NDO!M4a)beKusQSyTv^%>J`oSPToimnFUL8KH?6K z%4*GLuKTf*fU6S%hKW-s;fCPK8;Emxl%YT0JtFgG7->= zzp$^idFkrCbvqj)b@(CU0#3u@EUIS-fr5JwatY2e*s3#k=956Wt=rro1vq|&S*P5{BoUxZ9kkxLSPq!Bv*L=>CWL0T_^~Cv!9-$&Ct4EIC+V34b#`|nz z{=4@L_AYTRWu-sOh#aJ+u=`Nd(X=P5eX*|8%BV1w%*y3iG1IAsC`6hk?c>_lDbvQQ z^I7wuH$QNdv>7B`-!`qgBWk45nCs)>C^ z{+I8b>Pn#p{%it%Z~aMXCXoVbb#Bvj76qL>_e+}IDw4kUPDW03Bc&2gT20YqHw<5+ zW*YxvpG#aC1HrBVU+v+SkL^%!<~9T9k@`X-uGG2@FX=;sl1;kive@wRNBk(i6XxE<@CsiIKRTahc! za>((Nb5f<7NhGPzuTT&M2V+2L?4R-8{+rAoyK`8nQwGt!*vd^NWuw?9=CgiM@0`f2^xFWmyw@dx;^t&nRn8m1~J( zh)So)pAmUW-~U3aym<1QRw!yNY(JPSm@V|R2yPI)_#MGNa+Qt0(|X7Iyy~}+Vx;u( zyc?!=B#X1%_UG7sc81R*L`c2lxt<^b=l5;?<9Wijv~Hrrl?IMYdhQ9VBZEO}Zlc19 z(aMzx7fU zp#64ssqWDuEA`k2Vj4o9LTto2O^lIxAg@7PZ&@wC>ZYts2FgF<6(IO3@J^jp<5M#ZdEtg^tqzIbuXsfw<)+3;_mO7G7)2uC zZp9e{usZ}tL^Fv@g#q$uIsKO5U$M1oyH}pUj33W7a*ogA zhtySojxwWVe)e^}_yFB*cOJU==Pb){AN-Bx9EQ06XKzYG=@j$P%nd!Ep7 z-L2<%ok7hNtDWE}tT)-5-+fHvgQ|4d2odXWpA1a`ZKw(qPtKQ58jkvU6siGfbwuo1vn%If^obVJg<1A{su4w8 zXUP!ADViL4M~RSRH^Xrt+La&9DbDvmcV>SM==oDWoz5mAEf$57(ox!Sb)IP2kyhD0 zTJD-^)no7(b%t+m^uoa5c4?tD#?0Yrcq8zirvt-uO9`=R5|IkIL#-7iO ztGDL!Q^)+G)vio?@0?StQECzged!MKOg0y0h)t%+c4Yv78o~c)_+$afzu27XbP)Ok#f63Sz`6ORhBHNJS?e_9Ii%{XiGDlzgGji zX`%xc7oS`nMAr1nzXAY_YF5 zr2PD&Rf8tw(%ZteBL2a9X%`(AE&m}hpv>~cO(ff%%lf{Ru1#7CE%{z%;+1^%BakJNba%0NHm-XY zyG#2(ELFiS6kCbWPaTU|gWKB^M&hKATl(pN@plfANInLTTQpiMTQc0;)scoZ4PQq- z28u!8X@_z4YD8`}sUfMps~!SVNJAdeutwY~^PGwx*oM?94^68KJ^mdF#Q!x`fUVZC zJartvR-AK+2}+IX;IB`0q!BleVWHewM~9k)s#(Ch`Q$8i_L@1>SX@%A^00p!VEO#v zZXEZk#)QYwu!dyu7Z4_71&=hC&TITeQ$R>$eW}*K&{%G8PqalQP;%|nG?+z>i|{;0(3G4~gK})-L6)aFQ?G`kFxRW! zf0r@v(PK`rDh-A&y$3HeF9}i?Nj9PbGP32Kt3CK!oCDqOp7>~+pYdRSWvWS`UX)7Z zi2~nVUhw$tUx{kfpltt4QX~7Bm@JP-(y0j9XqN20c1XsLkR<1|t}o`|DzgGvd;ynl z%9C+5ubk`_@OJ0%cRpnTRRqmh7i-qu{Lbz1wIpTDFf9-*+udi{a#GDcb^-Zy*Ndia z4*nJ>@qFE6)aX1R#h+KOm-Nk&YaZ8umm)%PIacf{H!etO5amk6a}61BHgp7``KFrG zl1N_{^M%KuKdT*at`uQ^1lEnRc%vMlyrV~v19I}xxs#ZKtmRsnbxpc>b|4^$0zMLP z+da5c58t_SiWe-KeXHnqgZwnPrp@vrbo##u^M=V5{wdugj&r?y{E*ihivXUn`6gy_5N)29rcoWJ|vJR#LQd03N5kU$<vG1Eet%9wQ}d(HM=zuX&ENhqwnp>5&1QowPjx&Wb+ornC+t*E)U_ zVE3POUq&z{!a}9yc%JNssc~5Ox19YGih7ebH|5tduLERcVO_u!fMLEyhf_O*zkO1^ zW%V`qhb$jKhhXeBgsz6Id|^QE7alJvdZrylKCoZRk)~x>=6_ZXd9Jujlfquh)A@1g z;jc*ZW$IF1Oc96>uF%5F^%-%$qNxVqKGQ7854;Pg7W)+OfCS~5x!bhzdRKrg6DW26 z%Fu-Pn8VZ(f7=C8+Xil0Xq40l(nLeZCW?671yVnZVv>L%EYcM|xDe6l#e7m6(YBzZ zh?oUJG^QrQ`I?+=W?-DW^(XkzuPe&(a+C|v2G)n-lDm>@XX(CyHgp*I&$2kKKc(|? z4IO;o*CT#sh+D8CJ0k9eF}w9^UBfjeegy-jBviU)HP>^U>k911f zhJBEjU^@LAjcE^G0c2ZGP?G#kw8cu&*{xuzH!HNFZ%&4uBXqzeFb_3WV+B*-7PFWo zN}S!j#ETpQT#G>VTTo|)y%c6!U=<8FBlH~O$e1r)JrY5$6v4G z6n`4X@{a?QgvC-RW;}?V-`ahZcfK(;pUu=FhfW6N3-`V_sw8Q?%sQqtxWq*-; zUmyQ7ae#{ZM`!MzWZ(ZQ0RLtM|NlVbXJcVy1W5dVrkoxi|H}xdPydl21SEJF0i|SC zfO7wb-w$+F0HhiLy<9*Yj}cHG|0AgekoW(^`vzj(j_#0OBi`=fscXyXA< z<$3Kx0oeMtH-9X10(}Txn=AnOql`dMfM@(?Y<6}aDu9OVYs>+SwAUd2JxO2(%j=l` zp1;BW*qY#vOzz*c|G#m`0sY{AGRX~+6k-dRQ9|yr`o_;C5KPg*(L@^}80tS0A#T7B zZ=1zaLi3mT&vVp5Y{-ax|dE(;^SZk)tx%;K z&-2^!&3bnt+9ZD2DEHsw@;G@impRU;zfS9zx?|v9$;?P?^j4Z9GI2J!)w;c1xV?~B z6)`2O$zey#T&Iv(bLfBcZRJT-U6tbE>6u)++O0f$p%KijG!5hq%5+L!_a%_T zhW2-pndCV@tgLSSB9oQ-95s$_bufPK4NIPeSjmIk8IkNX14k~LDotJR1ZypVnGnnx z#wt}Md>Vix0B zKo-Ti0^X~v9AhvUJilZ@xU5_`NnfuJiW+8**mdU6`0zevhZrsqs3^w|@fNrI3L6}c zvu)bd?66Zm4d5)GW#tT;iiO5B;8@w8e8+0jd)&n_-kqYVC4EL0B>Zs+J5I{Et2$?N zgm$1gYi1TAR^HHsGd5ftmA2bFyGsUnz|}6&H0b1SabcKdYq0nkGq(p>=t$&$Sp9(< z`)_Oi|JLo`|5^b5A2&Mqqh9!58y) zIM`rz>bMBw0O-4@7k~u{XdsI;9zXLZ76c$iiADhR?28o_Zcw}Nk=_gQ1UbS|zs<_b zoHtEZ{Vh1<NJ!=R;teQx`u@;LRVbp$no*e4A;S8m` zC%_GUD1$Nxf%_+)TMG-}I%px~3|NwCQ7O1#0L+K^;D+m99?XHcuml#9y=GWUTE7gM zp$SS;TqZ~XH82#$`3)cf3w^;2g&;r~_#p&+p$-PZ5Eu?)U;@m87FY@^U^6@o&v0rk zo6F~lI3E|_25@!UAZ`>llbg@&E=q|T6L%)=PTZT= zoY<0hGO;5OPrR0RGjSwwEb(RH>%_T4mn0Dm7JvXHsdv}FKo|_eU?hx(8E_3;_Y?0f zC+}|HMsd?q?>@twyEyUU%8TnSZY1ySO5B+EP2#S^?-CCs9!Wf!cq*|g@oM5w;+@0? ziQ|dR$~p*=z6s4zOFUhC0+Bn6kQhCE3&6$ z56d2w50FOy*nhiTDOvx~GN@Pi(~JK4+YG7ZlJ8p$FPzja)32m-ikVFlpBh)17b?P>1Ar(#@rEY+Y&`x*IkHIh) z2JLVd_Ws1pH=-ZV53mG2h28Kf`W>|oo`T2VX1EpZf_vdl@DwbBweVYb05)HFE4Pl- zGiNV56o;qb7MKF>!)x#&Y=&E4458#*0I&*_-~bmpJ$3Dq@W=n1-u_Q4%v5S8Y=n)} ze!7Ngqhhp=V(2z>7iVHG&CgzPU>N)sX2Vce3y;F%&<3l>&38a6w8EXRh3qYY zMQ}fS4>wUy!Z270OX&yca@YcG&<|#!Um!WGg-Y0k9)jaA2_``cY=z_K1=x-6WZGaZ ztcNz{J(*4R6-ZD$o?&*eZ^JL)TKFZr2-j{K3j+*j6(HczDiF~sSdUf#4H2{y zrjg%eK!b=DZZ9YaiE*^B)#?nz(W32iU(fmh4zvgml%Yj13N3;bv_9Fk-n}!y_YP1L#sU3}J&BzSxmfF#*R)djLMzjVhQk9&%P#lfeUTtyd{)#5U zX*3xq5YeRVb_ag@r0r%i*|zdLxubErLWvKwq?*@)n}<(CTg}PJ%vO^*RY|>fbh_gSXRK3;g87$ky?bl9lb1RiUy07Bmt>G!jOkkw8NW8VPIANbE|WCg4#MP;d}6 z!7*GzO|30WEwmezA)l!7 zyH1{Ja-HfZbGh44C6u8`pipHiZF3D!qAKJE9$YAl{Gb6B3L<~2$>kfMMt(pjgaV)i z7pg!3Fo6q|qd+U~68E4!upadheU$DK-zSv#UUp9C&^r;=;ddOaHE*N0-*LEH@1T}< z(5tT$xn6yxvd^nX^>W?IR9tG`byT4bjqE^isa?!!E%b-@R(Gq|I;yp$bxrH$)>!Mo z)?=;8_SQ44_-e6kr_~m6)gj$@*LZ5=xM}04KF<}o7Cwg_es1e?RCu@9<$D&j?X|l8 zy4T{``&YAT&+gH#UAqfi&+PWQ;=BE>-N8y%94!|6M*Oboh~L#O($CdD;&IhRGF>&1 z(XIiJ(JnDE+7$`Fez~atDRt(y{G3cyA~! zeJyHtD0HD6jzO-23(&Deq&JFtF-R9F^r5))H&M}IFo(p2MsoN_63gIcp0!OT%J+h@|^jFuS` zKiNHb{bV|hmWa=o{I0pCL9XqlL9P;$-c@YMbqS_QSCPr+`l#S^0rf@!-p~am-t~~X z*5xu~xjgP#m)lhBde|}Afb> zAszCe2wI3%qpj#U^aeVOB&5^<(t!^mun<QmF{Lbd-*!-k{!~B}$?h zwWg9~Dru?`LFFhG#gT+!hM_QYY)#CFhQiP>HQN+^VQ8#rbj{7b`Auf*o}r^B#@1vu z#^o?{?8F#~p*tI6@}Xl=RS*P0SiD47ykzl`7+oLZ>gUdgao)Pcct?wOwBEXywmzoA zb)B~k#Z2{cVgEBk+*I!xf;I!0K|S{tNK><35S9e0e^X!ue79CC@L(-&&$p6 zW_#SOtjr9j!)~)$%qF8j&+D`rwMwaw%Q%*yDFnqRW~-gJ%`Ow19*?K7B(?9jd_P9# z@@G9UFkE(#^NO1?uGr7KVn3^Ae?$ySu?BB#9e(gOX!tA!#uzfi0KWwphsVH>x~abD zYH$6#n7y`X`t(?Xx6aGEV-06~siu%;Z&N92y|vBCl497VRMmQGRVBp`Yw)&gLk;~A zsZb5|eYa5{*OU~;3_^^`t;fGt$Hd#G$EaLyoyX%TDUKO0orz2Bcl165F@un<8Iv^> z<7#6v@?Q76m^dQ_x4X9$x39Y+&cn>NE^td^>e-T^Je_JtJbZnZ}QeR&1kAgKD{<3 zjwKryJ82^6U`(7**O)q#x?s}87(>oXuWR%qi9K}m#9G`i?~FQUlI=Z5rl*b!9X+u= zebS9vFgPYokGW^LV=#K6H%8_5!9{Z)SU0N=0gtB%N5;n(poV z>hdEqQb)L4{#(G+25-akb?X|u?uK>K*UgAaYi4@gym#HUp+ncT)K7QEVARAInh}@w z-0qAu+|d~0r_V)wON!kX0S%)kMw}jfV|sT~dKY4{+8Bo+jFlXd8)I?@r#3U(F&I11 z*jWKFEKGU8)V;+aktVy5gxqZ6VQ&PNx#B=7DJg?_R$6Ji`bNj}S zy z&i_bfU??ev!3gOL42O}@85jXqNoQarjF!&8C>SH1fvaGwbQ(sJatw@@PQzH3Af1MB zq#O^8($_EnCP`nzM3^jfLL*F(I$;t-rB0X(Q>9Lr0#l{0APUo@uV5<7lyHQG%jqyn z`U+-1lk_FbB;_oaEqw`1Fh}|lnqi*wCCr9-(iboXu9m)lxiDY)0_KtOYPeSV02p{B*GZq_%$oE$w7_EN6fA-z(kYxLlTN{6xL!I1OJId`3YNkR(n(lG z%Io15(n(kjtE7{#0)8o-gd5tbsQa%h@q>o@T{7L!< z{s50jAHg5tG3i5i1fGySge~x-^dbC-l#jxnr4QjTcuM*J9*3u;58w&dDt!P?LQFb} zGd|K$cnY>jN8xGME**uxzz*psY$at3c1cHJ8$2Vu53Qu!4!foIVF&Dy-iMu}+y&1{ z@4++BCcOu7cusl`c9U`sv`g>7v+y_RU1%faU*QGmUDyjRNk`y0*e4xcCR zmX5&l@QQQ1}um-k08jx8VcnEjSDx zN^ikC@S*f39D!rfoA54tEWHWuk@9^wE_J|B_(bY}58zX&13rY$qz?E9PD&kc3{FWM z@G&Wm!xvHqd;(uehu{Q!B^`oKp;I~ppTXDCAvg(NO9$Z;oRJQ~=kRywAbbI5rGxM# zDZheq(n08i^U@peHJq0Yz-jnSIsj+jd+7lDos?(cf^-1BfiCF)oFnD8&@CN+^N^7C z!*?J_`{8>8((CX8LelGS0a4QHa1qhc>(GT5>2>Hvob);*NGTzi^f~~zesY};&%^VG zLmbLR15gu%C`8?mTckyQSH;Zg?vOG)W2caYg}w{m>i}<=Khu?)%%9Z_AXQSbs^@JsDk@^L}zYlU0*-D(jT%da~Z>+UDwX3+}0xuO6Fc zfoFl|nCDpbAISPb_NRFDHh2$_^~+0Z&dS^ZvUcVj%)h%}(*Gydh5uyzO~LbIeXrmw zUJKQQd4*!(#9r%y!e19YRQOO)L(v^Y7mF?mdZ9?D749phim75-@r2^N#Yc+Imi)1F zLFsqC`+fKOcK8nXPW}6}Tvook*Lr99mhwHRwWIut^y(kvZ}oo>s18&IKCfu1I3JuH zJQYlYOrea>+ORsT4*#+b)oXQMv2OZ#{U2Gk$$JeRQ)~Y9=o`($e42+LxXyOK@5w!< zU=lSS_R>1(6f>2KA_6lNuDyKJ4$mC9v`}xsH~(^!?-|&{9Do}3TG*66#!i45a&H$z z>2Y{o4FG)+HgQGCYuG2ChM5PO$o4zf#4Ls3Jq<#k%hg#n)P)j`xW@Z^&Mpe2C zxc{UbKQBKo|8Fh-P9A?=eqMfFeqMfFeqMfF{_if-+i?EBdk?b#jbb*SDrQ5{hyG6% zdXTh-dPCYnzXVf~&$x6iwE#M)1u%zN0FC6gpS0&+zF&^dOI^4wTb+EKERx)k=k-gz zE4|Mxg(=+mOXt~!pMF05j3;J3*`yX7^>w4HiO-;?C z{3rawe7^E{?}fb|Mq$3Pi<0O6)jqu!rZ7_he*=JH$p-3E0D3$?daL*gOi@mODYAE@ zJ@Qq_`>)*pKV6s@Okwu^^s_KlF`lu^^gfRFli0ta?LnKmOhN6NX z2*{g9>3KsqVCFa(N106qtHola_I>)7|9UC*=~Wvd)k6pOufBhHqUjBE09Bz1^!6Ld z!OyM!MdHYoKPOJC`Ne0|%9Z=- zm2V_U0m1K~mJKp*g9;{y{pqI=^PK!CrA~%qWg1aK!Jn0D5JE*PuhDQ-?4MAp zQ4GUa@DawQR&!O17Pph}34#!Y$Q6~;cqI3+2ue^~I-aB+o^VScOuHyVI?KyEUVQ*Pefl*1 zLO;{cilQkRkx$zwAAWDdS6;T3Efv^lawZ zIf~=)%^Z*KMcdT`iXL5G4b-Htj^?K(o+^e&VCX;rLouYF0AUU4N zC%RO8I4@7`iDZn-pfVho2pN`KO9t(uP&srr8~>)=;~Ik|KDPCZ#q z9X&MlspzvGFYiCqm6hc*>C5zQxvD<+>x7Mw0c-ltJ}LqQlGQS z*UH;7%-+fZe}%vA;DVZpy1tFn1b(!4bWW4EDQ8KAA(JcK?RIkpi^Z|WK=s+pDa|&_ zU$Z&S!vi8<`RDKviR?nA5B#3Z>UK4y##FPY^m=*OD9AX28)^?NZo^v zfZy-y!3+jVx8J8XRQfs%l^8OFOy2Cg{5&dHVF-tE0)C6t>`juBP||9#$bJ$@&S7Hh zqfO0!iVUAHYRpv#9`}#;D4etV?)lU_@SesAHw_#;>b0^^{;Z0IUyBsguSA#}T>d-s z$>Qe+)DNtyy$b2}J&!7vELyEpJ*Tr@{LUZp2K(>5b;`=)EH|E&%b1iXA4iQh&-313R~8VxXwVl|WMPtz(&@OWtSnh( zg`6&_kjX6^Cb7kACORl7M|y-bQXM_PK~*uQ)3S4I4s9Ha=DH?Q>T;VngJtQ zVF^QA`eFyZC#5V1+s~#EaU!igK1{2RS4BfoeFSwuG5AK1&MG#^;?jj3`0cW|^gVf) zEG{KP6@G|JgKw0@r5AVM`(#ZO{CR?kZhust)*wQsp|VG>7%DpnM#!${kU3wted@eNee#^=C{1Xj)u~PlGiGH@ z?f%2qYxC^d?)Pl@`Lh5)jr1w=6#Eqf;BR83oRR6}Wj4lPF%=1xoT9##Kv9E!kZ%$- ziJ73BpywADR~sp#(NUqMrkAvoP$eaK6`(Yh5 zT@WiU3Ek%FImB6rnsyU4Z6S(ViwVzZG1c)|jvOcY`WT@&H@kvAC-f+7A(|vRMrpto zB}I8zl)WT-dD>RzaTSahHte(Y_kA#I z$j~CUYk>7DYmv!p!4kI#$3^TR&_X6`5j`#u_qvZETsuxHNvdiJfmi+1VMW63XJb}h^oF2RWp##^ojNEa{rcJ-zH+s%o>g4N* z_>_TV?4d|_TH>X5p8BS)tmxuRMU@So6Qq5@e9L|XW$?9F=O=Vf(wsh%c36rmVSPpM zKt0CUc;z+bYpj}rpje0s3)QgNh?0QwVx$!oa>jfU6!Aq=QC#{$RAE{bQMl(tX+jmn zrLPI0isDj-sKI?MD)Q_6qMypdL?KAbG%x)Rk;q?x#PZ9Ln3n%&XTVVD^ASP@d{Jx} zqDhLf**TXPh2F3PeGevyhe$94V!^(26o7h|J@gN6?U8L9o|i7F6^x&wm9!tpDH^LzOa(eFofGw#5;Tt~ zL1$n}&~fRlXD}sbTBFvdRo;Gxbhk&k+sn(GV(8MtF@6JL!{Fm>GYSEHF`Y3fZiMQn7#S{^>Fvzy?d#ey?gIv z{&4TT7pDONh4dx$GO$9|EofTEM zr=U$4k8^2BOQa?3+%ri_VU_tV*YwzZ@+D=)MC^Vrkk&0h%u4Fz)322MA*3o?5t(wW z!>LQWL?Lw3i*M=Gf7NCed=KF=Nk7RK$OXeNl{#H=ax*sSeMM?f;h zgwisOFk?u=kqS2xr9~SxaY>2pByGi5EHCqrxPw05^@n}bee9tNA0^+(ZeS0A8fA#q z@k-Q(>cfVV3#o2Bwe+GIh*Y z<{C!ExN+MUMy68GNG4Y(RWwUsvVSG&3Ees3k5MVopCTCRZMiRCY38axog)2bqLcK! zS*z2I(o(DvKLwXu6>IFR=Xx~X_er?QSbU9)zC!J3Qrb?yUVgfcX=Sa1NOhzd!!K&E zR^r(5IuL~Dbsn!Cc@YlY5c04a_9a#>PpsaJv}g&MgN!WQwTYg0p_@I_^#a`wD5#M> zX6LYHzzT)%lIT$?ShGW6HcwKFGjmxLW-7lzp|lYn#%5klBO3Re2t+cnA(gKP0!bw$ zZAV2D5w|&lPH+hkVYR^M1eDXG$#Trs>yVCUFCEcmI-<{XM3dFk8@Q=;R?e{;{h z=PJd%RgcY`|A+onk@Ii7@hzJ-Gx6b`or$Bf=FXbkJPTz%(}pr;&zf^}_soqah7KP- zbmXwl?n=C$cm;)%mXoPv58)4rVXIi&*VH$2sA*{CDD7BXvu=5Y++G1PUN%xj%VY{$ zg_2gt^+Z+cQ*I%VpL3U@fs|ywo+Ky83Ch5u^B}v-q)4Mk(W4X;gj0&R)J1Mp#HD?N zZi*&%$^!KA{xHdivxjwlkEiFw(CO{?_jq-+_5Fq)eE9KwXxXp#3>-7{KrrN672UFS z`Ns0%0*0UQbiZLEy5D9G6_;0Ty>a+;IW9-{)A|0AtFcE>lUUBqXU{+Z^o28G^;l1{ zXQ_uSD9EXxRau-uq2SDRg~i;c7;ol$6=teWg+iHS|XKT$PR(kX`_+x)Ok& zCF^=?CEqCoe7>Y#5;as-Vr`3cZ4!#v+1@LCJ%gF2WR|os4bVSAC_6uNebr-!lp332 z(>LRLFgkYJ)Z;I*nO5iU+JgBu=bM*9HnCBwZTridS-rqI9fLUWQR3FxK8+4nrd>&I zz6`xDqdqCW&z3^(F>zGAu0E4eB0DNZeW4#3hK86Xp+-|<<~8VQ(+YGw{|l225yeNL z?Z5*sb5u|wCMHF(whE30R<1YeG)SYyRMlV$;KWqckO)>o@*EoS0F8!cdr82{$!He9 zW2a4Q8s=<|31JzM*htg%wzVT63Q>;-U4l9e zYpw`lSX)9$6dd1&)~tDY#E8j{{&se0Sy9WF7ymNu)-s`x8r29G@ z(Cp5zb|1*{6)nN`ak%s;Q-dQEG)!#pX$nn(P)Sv4LK(sQAymC`h`K&wa86zRBx<~} zQ8&ppE@Ns=Gk3LdzInE-Ib(Lg^y0Z?%Q7-<&@L&U3Ix5D0SAdi&A7L&EML~@EIKPI zyTU;&ViAkacoeEID-vR`@(NIryGpHDSt>$bl^KUSqEcZBml9@_rju8t*s5Wkm0}MR zbEGusYmmypq|Z(wqme<1VRA7K%JA7-^JZ(zN?CdQI4g;Jtt9TX5_z%`?pn#)t#Ron zG0WCeX3*;EB|_NoyyT?_0iWK0jT^~wCBq~EA4vyGDVHUg_DTK(D|#m`k`YsisT@az z{`g6gcINksw>&;^di{0dhu`2U4Isz+t3H~h8~EW3>z-_y7THyO|D6NHK|7tb<=;FK`O>1%J1%q$z?Gv7?8qk3AklzC^2iQY6y`A() zcLOVZxI?Wc?#;O!DL?0eXv3YSIKm#9;%J(tT z9*-I+4p<7mbp6mi;}1{D(EXK zqY#N0Dv77DmhX^sVn>`D1| z=sQ)GO0L$0RZ115HUa{%}jr`)o&e0RIN9i=uxbP?b|3rw~o(8VI=@Wm|VI8e}6`k3C%EC)4&(1tYX z)R&^{FT|uPiZn{2s~`Eh9GO@Wc|^X(MaH~HDWe=<49ShkV8{g_qEzt}AvKm;{6{{- zY}}nts83jCS7Uu^C#UR0r0pcjVUJ5k2~O?yKH*CdRjPKdo#2&E=8~dOp;K?D)K`|5 zfgm_{^$|(xGMO46cHxUlSp!@tj!S2@TkJSBb7s3j85ZJF`%bMsjQY87e=^jXu*+pE z9a)5mig0&8YYfSi1B^=2o$tlL`1v76(4o^&RY8(l4s!Ux!G(S7yp|8!d4(!0h`11B zPP+wPXD6oLPOi2a@YVK~aMG+K6Yj+BcMH`d&8QCqdO~LGo0pf7fheI7CBtE}2`ecp z*OOq)3s!`}p`7rgbag6MjTM#NOMPFvKQeUY{JD+mBi7+L{^;2HmA;bD%&Vs&*jSL0 zGdDaSHc?ga{ItanMfz1gi;O75nT^(I}K0WwGk= zbDz`YCVZEf_^Rf(^eTag+3dZbt;e4)7qf;z%iI_3T^iy9HP=~ZbQH{D@J3eD}#!{K$M{UQvHxl33 zsUbYoSW=G1S>jS_;?iM}M0}b>E}P6K#|MoLGYRs{B*-%_$ijTaY)(;Gia#MAAK@upXN{`b?(%tLuH zBcRY_XBQSYI0H%ikjyiead`@oP@Urn3oaMV6os>;FZwjLL!@(m)KD4etlS^PNf2$7 zz1%t6E;<{jaY`VSeO>0|l?X97Fqp@I|_OpY8%Znc_hkX2)1 zkUXbGj=^?bG?Dns>Oee6Y;c$nD4mf(J(?7uf_#sbT%$GNYqaD>En${6tx2`og8Uz8 zQlY(lKZ$IK&hhI5`T*usQ9)8D=zi{s?7n#;-F4Y3GGq=)?j)@okZz^bfqH}jIe@4* zP^QAmuf-gqb{7_SoLa&lExABT7^Ec((zfKMvNM;dzoZ7#rzE1$;|*S^=?JIv0b1XS zQS^znVo@jm#7ByEN%+ zizc0~(WJA{n)LWA(Fn?uu!cfe=N&j zq^eRqN_A(B8rkhG2OjC$!Slp=eug7is!F}6BvOI|EuUl>ZNaB$!cLm7kS6S;32W(= z;`AtxP)%|ton)vyDRn7ZCM224x>WCn&ZncvtBCPXIY7&6-C9~p$gaiXaYT@Cg~$&MUI}MXh5(ABsOBwVQ6VGdG2l=R$dm;FM$xA!1P6h*gTi!j#HCZB z7n8{02v+!IZgS2|w3VBrsN6(nx#QA%1paP!u;LQrU+i)GNZ>{+CjP|)?H?q%sy|mD zuU1z(hjI1t!Rl3LRk=b8ARnGxF*pZ`@n_T2>Ne{_&27IZMflyr?y}Z%U#KgdWz`*nAL!m^9OA2(STSKmw%al6?gh^sA5jx| zYRZJwqH5ds(&#@AK|ZQEJG0}(Jm8Quyq_wu}IF~**tk9UETPiO_2L4%wgz;oh zKQ&lzi8tp&D`AeDcnst&q)suXQxj) z(Z~TRkSlgjDkZIyQ$1~`Mk%K&DW$Rn5a=`rG;*4dn|81`zhT7WWeHJPLQZypnjp4Y zsMZIfzG%P~tt6v4sr1TPzPH%&|-1UD~YR~w?vcUIgu2eFqTMy3*(6E!>oi-bS#As6uAWa8*#08g}VOjidT>WK@|9 z77z;QY^hPHgU=-A_4u4vv*8PzJp<5A0!}BD8k%^WT5?gP^6gnNOHICq$zVa}l$Q{OlsLeWQnOtt zHLLDXvj(DOHz{)*T6xlE)M0`0U{>kKZ92jx+)1iRm*tQrt*dntp$u2dUMLn3`|9*F2RQtfw3q^)hpT@z;AS1|66(*`XoE-9yO8Gci4 zLEgYxz0ck4N$m}*>e|ahR}Q&q(wu2COZD8kJ?)Qe`IUT|ADP#qAQ811qv#qj~N%U5>#MC&NQvf5n8T`C>S*uI7la@ z@W^~xhn-0qiBsue(hkv`6j&Qa+*gjcuN;ZqI9qB~Z8|ezw_PcRsWf|M(!5sKN%qN3 zoYyLC1z%yRvJBPM@pY!57Sb|vgjTAZAScw$PLcXpI+b}r3=nFQ@ex>LciEA_$r0A1 zvmqn{;^;MGiO&-IQ9r~Z8ww>}OPqZ7%{Slec=v6R4NNpK<;+Updu0RlDvmbQXc-0T z6lsNAt?y0uQ$M16&lKkm)5CSHER|K3#<)!0gK;^*xIB$v@4YlL&WS{sfi}qum&z(5_?9`y5Cp-!oZwzg zj?0NMlP8mb1o!?_8bs*b#$+}ynE>p;y@BqX9%RZB-@ZO(O>J9S!=Ibm-+BIq+eZDa zcF4TCyZ=ZHOMIT#nOBe~W`9^38J9Si`0}|o2lnY+o8vf~(m&J@CJF|4NOXdR*SIxh z8b+buVI!$2;G~)Uq-p!QNUz^$CKMXlVKQDe^9vf zS~5YR9OrIuOUrTQgS51qoGHgC91^*fm-h+N1rCNpwPeJ?K{9L((j!M&^c{}gBNi`S zQn4?l!$2|ui4k~1a;TzDa^jzj^Eqs0U!JVc8fKe^jgQlA z&Y^dfr)CM4Cv6LbyLpbN^Ku;ik^%H`GLlLRJ_WqqE%^otim~2Vvkj-Or?gyJ|Gz$<1|EIG!B`Lb8&8( z=IA_B&4Xw_;<*RA zJI75a@Oqt!Cvh11dH!dv} z3Px1YHGqIiG|}goD9GStvCzU*a#OhZ9Al^R5JfXI$8wZRsnc^+m9j?JJQ*!>;Cooy zbO$~hV#l+yS(5p$;a$>m{0eFvn@e@p3MXiba)9qtS^9KCCO@{XHvN4Kjr$*oRDwmVIxFiop`NfX@Z zKPO!J-lj9)$BY00rH!c98%){UUgK&3zoloAVAA)eei7_{?bxxe&Nz3zZhfU#@0gO; zcw1R%$xAB+-)gkk9Ru>jsS76O45ass_-y0q7yCsbZ8_fjd7{qY_mwQ_|Jzd|600(z zW&XbN2UQKn|FV!!;|j{gcW7|0RO&F<8qCS9-AT5h z&g~9^25B@3lgXk`nDqf)@`rq@+gWM%fi2SB-Hwmpp(YRRUAn))U;y_kK)tcF|L;8B zJ8GW2b5hIWi>3^!sjL#)R$jMa#r$6Z1yiJR^j!Ki(19C{i8rxwgnu-Mo}fWgSTj&V zEmb1gs#K}WW|gW#tud+9nmJiWPA{h^N@+#3)vTd6DIZf(uv3YsuyUZ1LgSRp%A1um ztyHR2s;sS>Xhh5VaEQ8B6W350n_3&zfcjo5LT0OyRv9}WTivlqg&?d#cuLcDm9v9< zt*O%BuQfa0>Jyz7?c_hileMSe6o?R=7KI4@7Oy(sn-)#3*1)ty)1p!Qr7DKDjAyH> zVOlgg4NqmB7KP}d78BCiL9*cl_b#lJ#|KKmR%yIh|c_ZI!up z-25BI7feDMUO4(?>$0aN6yBTorfNgteAb8om0N18g@M(R8`ot(k6hmlrEr(%QfV|N zO%{{MWHG5Uv_&sxw=(jrP78n~=h@ssGU{)0gbSbbSYFApDfHn|XYtcnIV`I?Ww2rz zX!tPRie`l>Jkf+`s|isu6SA6A8Ut(bSp%fgg5c})``$X*83+gvIod6}g*{V4W#?O+ z*eYS=loa!nOLV2IV{h{(oKSjDWO2SSg#^rf=-@lY6VX ztINv*-e7?B1_Kv2Zba&hcmWz30Vp$SCQnhpkY_=O%0MBt?^LM53i8k$EYdLYIPz^* zD1h06e85WWJI$7G@0Sp7zmwkwzV`0+4~5Q4Z_D=v1DzW;qJoX7rQb&UJ%3~30m=-J zdhf7jL4nFZDYfs?@?oVC|NEk{9US%Fd2w}jHQp^Rv!>omU-{;Zi3c{Ov<`|>`#}X& zqWT^REtMk$vLi|vN9DWNt+ZU(p-R~Yyxpl*hgB+;=+e&QHzUy~nZw?rM^OCLZ8zOI z`|3y0OK+cPyZffDq^4)&bPaeR2#2>f`;di%#R_GZDNyraM&k-FilVKSIP&kxR%}Hw zxeI@|rsestrhwv8?4rRCwu|_bXtRawqQeoki#a)AyC?`@ zyWLyTVNA81Yz>Cw)@Dm?9ggHSCx>iBwIGCzMupcKRwzO}>QlHpf+Hau-U3#pkxsoe zApA(3hI<8TsgI&9mOvnvXpl>RTPVcHw@lkJbj%|y$4-oxrIMAp+!fE+xhVGXDO|suFQ-b8TL| z-Dzi7J0PixyPcBD3c(0-w^vdK&&I;8suEKXPppJztBlBqT_=U+S&n~}Rz6F|QSmN? z!lbZcf_x>K754B5BcgRiM7fMUW5h@sjY|BLe^Qx%@3T(6^Jw?EXlHWv0?`;aCy|_E zhbckHjl%=^_vLw}SoD+gy}!>BCTqACbou@L+GefLuT+JleG2}oNK_jL(7ZDf+dV7x zZFoLx!G3^RsR=JLqty)ZdbM#>A!ac4t@5IzRm}H#)V+07UCXw(iMzYIySuvt2)=QL z0Kwheo#5^s+?}9-;O-h+gF}GclQ3UA1bjSyQU!UaVE09^vC- z;KzcqsiSB@ItJJ@oh7fUqc7$chSYI-xCU%@D8U>J0=B(Rgq7_#(7CU36cz-2=wE1b zx*Ff4l%~bu(a_OP(D846s75zcxL$_gUsy4>?owgiQZQz&3Y3-9Uw{l`5qdW%m+L5h z?9rv6*K1zWDxAhGxtg`>ZFhw1FJ3^3{^)&c{9tXW-o_EqSRjnD5NWOhp#Sw$izNps3Ii1c zgI#W6$Q%PVUBAbkcYwQq5P~Y)ADL@3I9a=60burnlIn1dZBS*s?0r~|5Ai6N4yOGg zg{}d(%Y(a=F^JGkraE079ZM#JgIrB;zQm}gmD+7`27H}BDrD6mmJ!$M)d_coOK?R4 z>=!vn$)fbf87UGA@U>Rgg!n}MMDLMbiwziWP^wEAed;5&jZRKUX23mc-^jmVK*&{a zCf~o<4k0Su^~5H1n?c^=Vn-)M4y<+N-jf@aB%oWBD(5s>rO83gO!QNcKu7vKK1#Qo z(Ey$74+SaFVQwx=?ZKw zE+d|{2gmS7oWtRStRkB1&J;<l=U6f@^}Oy!6)5)18a`HQM&^(?VmN~yfl}`0d+R@`mnrA zG?8O;@zI_tBoM#qliKf^yKV@9*kif-xtaMUO&6Ej?u$fi75ViPZAN*Gv8c~1TwFMp z3(exuT0e}@J4M9MWZ2?CzQiz&?NL%VaEV?2Km#W;>7|IrC6W~u=Q3RAWU(vo{^9M> z`2p%8kt@Id%}gKT|`MA{KSG+=1pZg|8xNP^+8 zFzaJX7nfNs2NX zyH}ESUl(H1Q=Zb6Qv2c9C%GLR%`@J6!ypI*$=cD0Cee#JLs$1^{aCc90>Bk?<~ zL%!cy+z_;0=CYkUQ@JvdLE$Nrbh*GNYUkaH?bb1LL_ zWvd%bhTBDcIe#$Bx{=<&I581;7TjyX+n&aVyjJh3z-FX$P){kQJk;QjjVo+wpVoHR zspdhAqY(FvJCAaA(w~uUP_fbbMf|0PvfNa&jH?8lFO&0gs#iH%adNDt`I3_0ahi?z z?X#-(5hEh6Am%tnZsp*&eF+KXb+^0h$8UPr^Y8E2l>L z?i?tK5)`2q@dA5%085sPi}bWkUXj{y*{`5TUg3ktF z=W?Px(7&kHxGnOM+C{&xDYud4u?tDJdbqkW`l-Rr4sW=E(8A|laYqqk z^mO}&YJ3R64QlpJA1UIC@t<<*f}U<}O4XT1R8SpmE*BRiz8eZRcb`v5(}>(AqXg&l z1SOQ*l4jEf*Y#^kc^SweOXhY^1&QK*v-W?B=r1VMyqNH?g#G0=w{>WZ5c?|3!83;) zS21kSGOvT2u(@bxXMK3s&gxEM>~G(Um{)nH>%fVWM_-dcB=Q?G)6K5+ZgI)Cr75le zvja+7db&hs;iIdkcgz`4A?lm;+>KGX*P)vz=JbOR&Uky?n%^AC#>8rYJ}7b6jx4*f9~Fv>Li{CwQ5`ZKZ#M-zbD5xyfa%!VGb z=_pe=y|Y3S^@9ZT8bJy!ka9Y*Kx<$)gWw7kQQz?YYx>*RZcAu zlhduzCx7-M_FdNxIMaZur$m+{etnXlY(mCCXs6a_;I(}sHQq6(x*M7eo$r)xjzqoDH=;grd^^)W*Sz;VBJnn z8!zPZTXku5wQ%c*Q1kmgb3?{UMg9D2SCYr?t%v#gzjnN%Pqw;RyzumtsK#oin)$Z@c%su@YR)VKzdx(J#LTt13h`aYIoksRYZ`W^4Zn2}G9ya_?B?}ta-Oe8p;uyLb=^L1U zm$E0a1S#%ZozG7vyXjNDGe(5ANMIXe`JE37d*7aR5C(cG9i><^Z!UV+I)0aoawR;M zr>;IdYo}UScG`i+DVLi&BjXLXa6z*jeS7NhFZ_W@GEI?8MsX;q1mm;A42oibXMK=3 za-T#*0q9M2>9MVLAo+x^JcewcZb~?W2R)nZafng~9&w+zibJ?*0_zj!A48K2?uen!XYbWso^kqR=XBw#?Hjwv$rzwS z-)1{Eq7lkUM?~q&beX>(B!e4hQNhX-nF&gwwLv8G@*`l+v_hL$?B*Em=FHFD-`v3B zc9w=6T6PVa`W)KK-o`8aM!fAB5=2VIR-wq3TM{_CkDtV!WIZ7Rm;c$TjwJ)8>#HTp z>8(?#dvc6#lUvfQfTVJt=0a^uVtk^?qKkHj!MXOy=l3c4i%m|01QE})-5o}Yv`a~} zQcm-cl8sl4b37#S_Us_8eG(W^m-8}pbO& zVyuB3EPhfG-KtA(hgvv&MLMv|YV+uDd6g(!oynGTo`e{SvU75YHzNymb}qMHD$28a z$8_A=DTb}3iyG0DSK1omd{N><<7~s&?7CCf6zpP*02PBQIyJ}THThwEq6|D01NTSX zTbjg~k=YmzxESJS1bk)EdXOP|VjROm%WjX1#O#gv@aKJKIFA)od&(-2Ec+FqmkH*L zC;Jy_emA|>257t2@1DN9UkqBSn_bUJ1TVV7{ZDGVZgU#AHpIQ}`f8A2+nuVM9bWh^ zo(C57OXa|W415+3a&UBRK2P?JV$$b$yuGfpjZN03*2its9yH|QW`u{57443Tkz4iM zfdMyzJ<-oPfe7RoLC-P|s!**Ml@1T6k0#(Mb>u|UZ7ixB&e1UN@UR+@=_1Ir`fSNn ziV79&NfC8qS|}!o|9g|$*XylR*bV-wvLAcS&eweNITi(3_@3cv?dzWfgz?u2?s~!yTNxH5v3Sr}F2?3iz0p@vO`{k+% zh90*}i(Njjjn-RImFFvdB*jImdK3BP4jzxA;su%lzj?OEiW?+r472uCY1C%8t`^=% zm=I4~0kd?FR3>X$F^8YJMU|MjQ?5gV239>m!R{XxREp3h2fw$vgdP3-m{~4+N$R#O zbY-P=^AO410b{SeUhxd0?2V%8jNwVyKCH7=g)&DM^3jSALsSKn+i!1R8k)Cl`(`#q08>NN`w0`KikX83v0c*@Rf9DMgBgQlkaI!=ke#8l&{qZ z_X3hZ>X`wD$Iz$6!Jt8TDZeE@;uIIK$*%=L%vWUvvn^L*FEPW;K%k;+S#MVPe?eAObn=^Q~8Q)=PuZ zdXcMTPH_}gjefV|yeS6`PVBFBP?d6OIpS!S8?q8S&iZUwBoza@Mr={x&pW0J$3R3G zz)33PN$S@Z3Pe5xAuuH4%i69{k|_`OrV$x!o1P$hnI`LiR$Lf+cBb31C%NKZ`wjw~ z{b{(W`y1Z^yy-I~d=p!LH3tnhNBf<3W?L}h`;xK09`5!449?STToAl}&RiqsGFNvZ zv*Ui;l*v|_dFZwX?{8-FQwY!Nnx?DWARpK=%D1|lLegjag+{@Dgr)#xDTLL(Y1Ik1 znZNUbttoW1i4Grp4EE%>PgLe2j})1N9s5)rM9JXQlFGHNE3RJaRdziYnT4tNGESN5 zUBKr1?2(#f#zmcea8aKB`Y(W%V{bT{W2?_SqvLtKdGF?6v zm~NgNR@m)PjT{)V5_mw=r=7p zD|&@lM@fS>88+?{icVtge*T*DJdsqm_lW>VA*nlaf0|fw&<*bFDoCy;KUUMASErLw ztL;j(A+lY0fE&jhbNo1Nq{rgN#y*jvEnKch`w^f1x~-mlOC=@BOhbv3~s?KlgXg7-eY@Z(izu5`}x8shWay2p1+;s*#(GCQr!5j*nSAO`?kjR4p>wLYVlX>qF^ ztSHeTm7!z?;D|)V^U8f5V1kxDmzqrP*79$ZXEO#%56th zr-$a*y)Lb)+8M0~crQ^L^TG7tZ<#(0&pDPS8L3+{Ydykrc{U1Dwur%|gyKGJ8jWj4 z_7A!2VxecrT)UM@dCi}!G}@o4{lWWHRW|Ktn~!sLRcNbt{iu9 ztbAD4uJiSRuNg{b8oAAU?q(PBpY*~)rN4f7sbEuo?4BNs!zDEFkwys}PF?XHF>bXO(xA2$`5uKPo6UM!RHvOrmNJc8`2H4Z+!)mK zz1)S+@_UTLr<&yH50A0$ob{7LQVByu&^UxI$6@=$A^GiBvR(*=Lc*91jt4o`W6TTM zQzkG<{XLLx8Jb3oue_d&9Zavh%-8$%?bfb=xlIq}Mc)BNn2Ce8VqNkhcWrog?h`0O zD0?{k7%{GHn>A&G#fJn`Ug|IC1lwb7``-Zq2{O0$_xpv9$>``~63SQ)>c1l+BGe-! zIMTIq5+xEYE++4P?h!x1y|O zE46UMkLN?`is{=FwD+~UE1f1QmU+5g9n2RjGELhlzvNe;d8W8p^3!mEnY&rmI~=bM zLM1CPn*{u!NLs}zq&R%2N*NXu7^{l%wFHVC>zA0ih$)n_{v)P!A|DQz1>H$Do7qp^ z4)pBia!-%WTD@6Z9^0Fupf8AsI$TVh&l1ni$Li}|)eTy2wxx%wX)*7xQuOZnh=n{k zNf{1K;`Ht|f;4UP*LEK(1#j){lN1Svdzdym=I~dq@~0CNwhbw8;OvMDTwJ7w!>KIB z4nCMCP7}6Gzv6TC+_RrNl>?et;;+uNlZ>J;*})3$RSd#V);=oQ)uW3fs{6^@KZ|3B zL&yegVyTFeYO7UPrK3kx=wnV#D;PHRBal`k5(;N#Z>#xn)-X z+OIXq;3*rN_+f4M-iCBy*?V#Y@e3icF6(2D*B9oS7{9&x@mxjOgJ(vjz3b*-U;=LQ zq%^1AjPTS>fdOsun#7vJumRz8a&xvvf@!P!%!jz&L{E*DP3vf;lO*vv;|k3y!;07w zU5mQk^x^}`D0%mGZf$m0k2>PZ9K?i1~3YkM+zzE&YY_t34<%v5VP&*@Du9*v={ zoq6C9Tl(j5;(IGX(c*%YhmH_m8%UAyS%aQ)&X5k~jDfs-NQE0UasLn%3WoPDdw zONdLyn7+#N_$T89L$`U&ACP>@pBDf~u@pw0``XagAS+-#t*>e?2FGu&PT8%gzo2%$ zXWr?){FDrO2@5A5Ht@O=ULZ2|c--jY)Dhn{G$?Tz2JvwP@A*8*#z#Q{i=ABjqHK&V zK9w(lxinQM0qaezPk$e&k}w!p2S7APR_C-(Hm8)C6)VG19Bn$l1WP5`;uIUD`K|9X z(`UI4SYYUBTOW76*0a<5Vp%$PciKHyV)}A%B3%CVJej@M#pOnl^EhHQ`Eokd(OmuX zxmWMuIalD8*jkwRdV?8T!v;<=h+Dt^vZu>w(u*3uGM|VuZD24iKq9F(RQ^~^dRn8H z)iUN0DczQU_&U^Kt%}*Lfb*!k~`mKDewuL2Buf2h| zA6>Of3txq@y}t9(p{SKSveKc1~?uCc(n3+ee3 zp7~%PrbBt%{j9Gg^;Sr8_8d7qzdD=BCX(Y4v>*4n29@S_SM6Js5?~alWa{& z^t>62EKC~Im885^5fUyY;zVW~Vi&%m&>v5uA1bLI8a(S@STl(Q*LAS?HjfTd zMX2BNvO;*$LeOriDg4mFuNOAywb$b}*!)JxZQ>Rzjey zoIQK9?szB8mcP%|{_0vm2Vd*o(d-${HXiXf;u#6+NGU^mSg@ zZ&IGR?3t*|W>+&R1o=Y0WjnhA=Wau1(YKL9z;~p!x4} zI%dqEc@(=PQMI{(AMPd`$>r@O3e&2^OB5Le@CvA@bH-&`F|0|6RkycpFw<^4sLHV@kI=~OFf&ej z1C^UQ#bniG5xb*P!7vfz64j?8$kJBZ_1szr&(`tER;`)4Ivb|S^VY_A{bQ#|PjJ5D z6+;+xU2I&xMw99iRqccoD+CoK_(Eh5Fb)@+v%Bt|7l|LX+ix+6A8TAG-`gz|Jm?V;4PaRJRKeia?zI+@aB_!waLtD=OdK-fvE(9%m z=&$&&MUKMn(0Cd@zZ0CBW{;kEDp3#j8Ok~x~M%z|U|eSt*Ac z3tD5y($)!%3|i3;R;=x+UwQG8U0bbGQnilO%yyjj!9%VFedz6QT99G5&-4Kf&cPwT%9LE@}`SQ0dUpylrgE}|}OzM32HK{qr}rrx;CvB#XIeZtOIt~**ALud4$ z;GdrziED)@<`g#mI!(Qf5ygxgQT9S8p{5>HsZYR^3T@1#lM?Ss`6j zXR(7Kg_&YBGvq}z!HSD1TEcpCSu)Edq3j%emk^VZ zPsOtRu7LkP@e$FKFTQdwQ_s59bi~{ z&U$*DXg>{;Z<*Z*s#GGQM7zR?9ZW2V`L<2d2tiH@DeW>2@<1^U6o5^WE=*Tr-xGSO2Xxqhyfhxy&ThBmodz&Ou1799NYD`xXz#6U znLIl16JEz1P2`ctwyS{Mhn+qi)Edv)iPH!JVG4T5W~(_q<5Xv5jmRm=N}kNE-(=FA zfojonFn-dbJc$zVk>x8$*;8lPF`gDq%m=0QkDoOJBVBSwV-?ivm+Qc~t;kY7{mj%! z>9&d$8(pF_!t>9w?GS!#!oL@EI-g^ypxeQ~RY_xG>G{BPyHWv|4k#Zsq}zpppU^4U+2)7mC4ntpyU`;9n& z{KZ}9dAj~vlq3v9jBZu377>a46>a>(g#4;cG?If%uh*ziY4svO#`%;UQp_);p>O~u zPHD;vY#5VQ97=J0SmiGusgBl8;O7*t-S{v^Y~D+V2ZYVHawBtz-Na+JZuTVZEG6l3 z)0rf@os>kC@?Sg3x6kf${O6?X>=ZC{IMLh&L82JpeSIkuo%BZZde`%FB~NxjPZM^d z-Vd+p)8S|1xq_lI2f98oi0%`(U3ZUeJzkrLU{`t?+qC$Jzq8V1Lt!&{&5tD!JSAR4;nM}Ivlw!pRtxIYSh$*1e zM&Tv;Xdzc9#30W-8nK&F;9s|V$#26BWnW3SKltz&2C{R2J4$gjTpDA2fUf;2l}cl| zqz=eW92;7~!5?IyV4;XADYm5O+z(?sY;hP5{H-OUqiP~-^BU5Odm+`_WP}OL^JSMF zi|6!p3Irnaj4^(v$P7yZce3zDJ@CFic^j_UX%2-bG-i5x&6{jIsz|J~p z_Hy!*A#O*6deu))Emz>b&W4WYF~TdB7~03p4U@-h8>6}~{ik8gQ7dtfc=*Gn2D!ct zF5z}$H?L0D>^R`%*>F9BX)umhQrRfPyf|yy+cuGFEPrMMQ2HAhLr>~}uZGj$_;RUa zH8vVyuDjv~cb~~rK&ai}2T5LFqFb?p_ z&tCy%k~gldc$Mx0bi29ZclF%0kzk={wa802o*RbAXMbD%3}D7yJ8IsrtPMtiD#4zC z24QVjc>QfDWLd^xh7G+k`XIs_0h(Y~D-sa$QJA~C{J$V03ye91t1DP9Yw=r`YNbH%)9!E> zntVR>zGB6$J!Y0#$;dF%X{R=6FtN)HP)C!&as_~|l8B$~d0J|S?r1h zpa><*sQP1c>7=wHe-u_Ycm846#_|}HDL0NfgLpknRxg>l5L1F}$Mlz=8oK7(Ek?=NMw?)j7#~4`V0**I2$s@NO?f^3a)9KoR{|kiV{|Zq#jZ#^6rtHn z^PqoRK&vjOsURHN1_6eFIh>>4F;LP{%Qeja@Y9}O8$jC;pneC`0eL#mQGx-KBpRFt zSg|I&)rgenq2Ah|$WV{Ibw0tb!j084)hA_o;>fKHv|G81(lUSqrz(gFS#wXBXR zyqG-9zzVN(mUdkv^i;2r`P@_-8+3DodhELrW`NtOSGHVwWTX#+%O*1X!~^`i;&l

xA%If<3{pq4dP;ItK(Tt7MWO`&dZDjT^7&#WV6Z-K3&Dheo^`GwDLeuhywV-g zR8*=8Ez>EAWXpOKvJa|rJiw=SVmah7(E`w0(Szfm1K=F@0^t#5XSX`$&>(dfga0GK zpD^UbXJ}>kp5h1w6Rxh`a}G9_PL`3AD{gI^Ou_CN@1d-MgI?Ln#s9;I>pBS@yxPA~ z%Ne|KspT6T>~~4r%F)Wa@JI?UnejGB6c6RvC^F;Z&~47KENpabT;ok*omnD+=X6)w*E6MvwyE6!vTEj8qgScx6@fr zt3V}Gk!ZjsRXjH=nec4(#XjfsxkYmPJmDGGrvZg2zSHVyDaz- zI-<@KQjuR$xT@;8a}L-R3H%FaQht$zhpPwVt%CoFh|-Nfb-MmQGMUIABM;etAt9+T zB&W1-fuG;}sZTA9F^2=dyNpa=v+^~HO+U=wsbg|{q*PA<*#n9r7f7)}30pTx8-Z3U zKo9iGz%27$&$V(ab&+N|iKe#p=}Q5pAL%uY8WMJjijyxbfWF`ioF3HGUuunVpx>esB#MhxSxqn| zQpli10Vr3TPrPBs<)g0Ta0%9t~B)@aM zM~Jy`@|A#_iA#XEI-_LOj(aS!WT(&{-)vozGXH_&Rnh;xUSwornquO4Rexm?4x?vQ zY+E9YhvA5txX#-}!h2T*)^?F`qDFs(9FskrVi;H+*Q&gRgycR(2x!Dr-q>hV7MsB@ zgzG>xo%pu+=8_>_K1MKVqcDr*{Rw>BUTA$M$>i0jbz;1UC<-rdC(SYqv~eCXklX|7 z+AT$e1~nCtZAkra%HO0w!73t3^lSRK@OytwX(46RR~#`=m*a6LTGX<5AvL6z>H=Vj z#m5Wa!CFPOAm-D==Te4T40rk?)RWh@RP-|7wl0m^+wr6(3rd=lo3gf@|qs+s5p?3&K>DbAP!JuZe0St2HV6$Ai=kdG9 zn_;SnVhl*|;eh^`(I@R{$DvU8^%#)b3wYNVnEA7&c(VK6JsC!^trq1hnk$Maatky8 zn=sVr@DpiPmJC&UZAAsLK!)@pNY?nBnQzoEb zoL|jEODSj$d9DQbefCP>P(tIst2_q*WlPqGB*)3_5NmQod9dI0F#3WI-7;oVz5^0Q z6QNSVjs`uKzQZxify0JFU~<|fkOg8pfAq^Su%L}mZVV_~y=|#3Pq>+hLRBKgW#EW> z#`c93WAGPMH>u+S_=3!!->N2gd_uyi`>M^S)iKHPWGaS1bd|=8S#z0TPzGcN zF+*ypPwznvm5MeKQ@xsioQ|+wO7+ZnSSum8G>9k ziNbo{71Wf(hE4bi!_^N1fyLRa!ulL;Uar9CZo*xEXfKll13|08tGDn|qz2CEDbV3| z*j0nd8~e6!OqfxyFk#xf`^w4=H%K^KQMFxRCZD2bH6;!Sk8D)7GN4%PM>3FW7rIkO znEciBAqW&NDM|Dcdp|bhrA`>tT3LAL;_&cX~eqpo?QFSRy zC6ygl*Cg_1a8CcBRMC$IOf8(f_&pD*KYpdaGrx#FFjMG;E!Yk(wk3WbPz-bWR8&d~ zTa)?hGSA`yQcNa+$BXpJYIP>94b;%h^l6R_G&|G z2ey9XiRE-^?@bMmU_pfi3(g!lNA?W#<;210rFMn{7oA#*H3UCb^h(DLIQ}!yyU@%f z{&2F?-C6 zH40Z|KGrkNAc$ZDl6@Zh%(k-t0*Vcp7t1L;G~sqF3}_uS0bI~*WeC2#+sc@?wrM?y zUt%Mt3&l+0og2T;!?zCubo3%{AQ*X@roQb?zz}v(c>QQ=|Ht0>+-3qbhQAcz6-yXy zLr99eoF^X!L|}osoco}-|AZR9Hnb#fia}izSZ7+gb0e8J1vs-^CKWtkS2R#IbicI+ zKKpBX2$s4Z3%0p(mkwsPl$`;b7YS@r0r9gsD;a#*whr`Q8#I9l@@{PmrQ}E~P=z3h%)MJI zA}v>|pU_;~mw^*evV?drTMqX=$5_V6c@sxsU=sHsVs($mj!CpPA`HRKT-g1HUFOe{Oz^3l|2} z(w&&o{T>gM=Km9fNx1ir$6pqB9q!|>=A>S}wk*f^y8A}(*o{Bb6MPC4Up_kutVDC? z?hhAWzSOS3L1++LXEOk@^D3H3j8*J)EzaaemKhd)BnXfVSS@hWHpP1I9O2+v;{zpzrV9(ADlW+qM3>p-=71NA2n5Nty+BH-Sy; z^tZHay48bf>R0BlT|1HGFPBul4+#du}aY_jQ=z z#NJ_K<^%FgF#7hu#QF(0*zt!W?rGZBw5K#7WRq^N^x ztWsTp+S}d*^J#Qg092hJ8FJeGNDG8R&>$s9A&Qdtg&YoZbSfOkpt;vAW;>iuzE~e* zS2bsdH>h!T0vN~-BlG*Y@0~De*nieZXejNqNN0{HeJL6YI4yQJ=H5r3$QU49Y0A4T z>n~*|%@aTk$9)r%T65oc^!-A^92cvVF)l8Y5?fruSNkXibci6TjB0*ty^2QGJE@e% zLY5SKJiYBWbLbS2l^dceLa|*Z%_vnS-zAS-MO_{e=z>8~EUlv3rUg&0`LRvRoQLv0WZTo%>u?<;-?MZBEK4Fu0@@~)WV?| zY+O&j>~a=^tq)bkZdP|hrB^#>Xdhk2-A$L~j~OyuTp1LhYl-XZ(z0yCb>Q+Ohw@77 z8SV@9AE2E)Z2t+{`EPPDke3N08UKZB78ia0p;t5dWM^XpZ212NviT1RROO#+Q1<^c z&(vbFX-|=@XoBfB*0GpCKR*rz5{^S{{?G)2N`qn@G`M;aspAx zT>o!4(0>raobRw^pcEXuK#ATF(m)c6R^Z9FSl?U!hF1R>0MB1|>wnbpumfqz z9PIC0<-hxIasV03Tz^>0f6MT01Qy_Vfc)$K#Jc`R@4pPo!TVq5(gWHCXn=p%A3 z5Je5-UjIeT=Hcc3L(l#PJV*?HN35OHpx`@JK@ zS%DW_|7#RLy94XE-|hP@^?$DapZoo1PWQhf;y>}a|5F_+8xY3L3k1t^0ORprAa4@h zzxDeMlJ`GR-~aIlApe^is6=4EFaZ?<^eUVF`ujaF2!U|{YytM>dG`SzZ=IL>9pcUnB%-r&Z~()O4XDSzKId=z z_q#rM{>3-{H)))U1=yP%7&SorI~x-#%X{?vn>_v>tOxWZR-oNjfy8r8ZVn(}{XIJV zMI;A$+TRrP|B*z_!uE$M|6eNpe?pV9{ByQ{r?>wvxa3@H{|ES4FKH}tl?5a8-WQJ1 z0xQVudkP~As2*vQ{wF@<_$4{3PXxO>QJp?lkJ$*$327H=E;og1XTNO^EmGdHs?UVR zPv?-=lWZ5C=crsi9QcmF^|g0rYZkoSIpQ<`k9&2c(FIP%e8&W`T6|J^Qw@_@gv$|mGNYTc5J<+|KY`6mZ7vUPA@gIEti-W zco^o@GbPzBCkeu38nbuAt=$B-;;P16cxGY+|rhSQ%1zvzk4w4U8TTztF!Pk;m zG&2!$v%T0dRFy;nz{j8*3v9D?+OEc?C$4;P&xxU@X7v6iZ4EiC?g^= zfNxatAN*yO$jc=9Fd18orz_M~o*KW(`@HDrxWIfxY+PuaFIB=|u=?t+;jluYyjBL+ zcK)}5)`@D|Lc;=WxIgI9INeh)mKVg0>6*ix#gJ6|xWpj`0%8iaj*!L8r|s96mignTeQoY{ zP51qUi<<6s=YXbH_M($L{L-2U}9b#|H_JEys(!h=yEP1@M*j zLRbYjyR~o!MzGp-7}!Rf6N7ZR{vZ`Vc4SgMgjwrp8cRSvi>d;{|Y*nIL zJsxZ>1its!FnI*UG?JWP)G?K)mG-FZW^SYlWghgRASMn(6XGD)+qw+W*b9bD-pv@5 zX1q*&Dh#3#+VFcB5a>LPME)8Bef^BXo58SY!B{^;=vN$SsFcv>lPrg7x)10FjVBd#74-_pi`;z$5$^Kf! z=f=2D`yoKJ0c+LSf;jjrc=c@6*@Hri<;VEy3h2Cul)bvx0mZAiIfq@{=`#z`M9}Ab zuyI%2fHgQ2*(LS>fP&DJuK^)FMr@!!pT=I1l`BLog3;Hp84ROu*t_20; zh|n_5J-t=pv&99}JoMM1FI6O|@z$Y-o=93uUPL7D-{l;SSQ8Zc>@Fc*1KqBsr#K_* zB}gnmHpr25DZ^gT?{*lyBm0A?xpv6&{W8`&F2j$#Vu07fmo0k7e+atAhPT4I<)g;z zshnC=o7Pwyz2Mtp(kVYOC>CI7Hs8tmE=DoL<3xnn^_>``(~6VphH8abm5rL^6l zR#uVD`2L|j{4)hH8>T74(-kZftTe$uoXt{<503~=0l6YuI&jb8Q35J5t^DPY)z-MU zh%;R_RBl5dEroU&Y@}_dABplN=>G7Afyl4<&gAZiRU~Eu15C!AOSGq$r^B5AXP^(+ zmzEIws^+*vgM4cN{8O>6D$$C%QI&Ps!S?Y4q5-IrcAWqR?FwV$2;-I25Q- zIFzVVX+@%GMk!E60qS_cn1LKHpj?(HTua5GOWX0Wy`ZVi5Q?qEcm|JfUg9$QXQ!%e$cj8%gPwKp{PXHzk*T0wWDB zW{wZiJ%SHTVD<^n#gNG*S8}~prtygd!;sA_M4_BH2+xyvf{sK^9nwgA0;N7A3H7-H3n_FeS>i%m z#+l(Xe_CKc)A5Bx;R)sotKJg~Bai$OjH;;{ow*#b5uUj$G^?Jgg<-5SipBQlBNFoh zg(s8-Q#Y=aVq{k6>mua0sF>?wWCyN&dEz|80~z8xLii$N7VPX9=(Mm9U=b%d1L|y8 z=L~XS7~u>8U#rIFugH*!^s!RJlQ_!Mja5VD!UR@B?!u(}l&Rli#+PCEamLp$XC}ts@S*cTd|68yF>Ynn{N16QO_=!F6j$lWu3+o z|H8=mV5`a#K2x9i%%(lA1R>S$(KGeT6`lv)e9x z2)V&J?-QTVY1MSS=w~i&u@@#vx!bXDmsp8{Hof0Wh`-x8SYkLEd=rmrghn=wYJ~3T zttl3Chv`YU+e)o}roWNvxMFDr7BJ1S9am+iRF__s++4d3M+!G1=UBf*J<)rla&M@7 zgG!6IuW)h_UjsLHERUEE2ia!~YB49}{Q9_lD?h>eh8+}fUwR2JcLfZa-6Ok;dV(u$ zNgg5I7#dwQaVM&g zt=Wa_Wa(ySAAbu+HHq0{eMovD04f2He+)N_|! z3DLZ&ZiKHf$^{m?f|l5?(M65$c3XBcxtG{C@_yek>pSo-fUiUZ@ns>7BA$?TM(@Qg z_z~uBOz2|GOC+I0{l1O|z)FPAgTzY2{bhP1y6o}`K9Vf|mD_Lel>lFFrn6pOZ`!lo zXC8*LUQmJrS3?l#qSPx;sN&QsQi>xp0*ZM3A?aWl=3SF-GR)iP-{hFLRlmtHZ?k`s zXWmAc2G$Wv(Ax|j)IUSA%B)^dvdXPGq~R1}I|Qc{VYA9G%tWC?sC%NKx$Isoqn{j0 z&(ALmR8-Q@kB-R|QVvQcd<*>Dxn{3O!;f=mVP;y0U2!pzX>XPabIDk*hUqCB zD_tuZrf=p!b*yur7?{Kgc;~v(IpRp`!z^tpR)2_CFfqw3k;780N!(*bJSb$tQW4-0 zBS1w(p~V)uc@}qPhHZ*{Ba(e2h>WA)h=Ki@WxKT|;cW!Q4atRlYtz}L*;_CXZYlfe z`?p6MSXU!2hiUWL?UL%{MuN=4jb%(3`|T&K*X@27HsV7 z8&3yivCXAs(xud#TJC0>PjX9S5wQ7J5wM!4@)q1&9+QYuvoZMG5NP)~^IX4e=cw>?nXw1b8G7sjTB+^CQe{-jAO!E3T0|QaBVcIO`ME7675N6 zwgdIU^xHzoW^JjFVy@A2JJCVzG$!KV^P~T=)2b;b`c=s8>b(3hxYH_HmUpR>x7SD7 z)0A|Z7!H&CkloqjMIq;6)vg(~hOe|P>>^Gqw5S&!Ah4j((w4F0Shn$w8NT)n4RSjE zgl+z^Q;>oyE&hKochBLGJ^$k1W81cEYhv5Z#J25ZVp|j2PA0aUOl(eUZqMg?x%WQ5 z-Dm&WB+p5o?mmTg)v4}uPQ8l!xef`&<{b`t9vg?t;UXpU3|&t18E$bM&+2lct)3gr zVr#YC(Wth`Pl<{sgu~9xa&2{U5nIwwTqlU`BBfJ3q0DTSCX)PoQexDb{tI z`QVnv#nb3TYLwr@Az|vfsToaZD7qerUYb~vhHg4cv+2FrEsyu2K5n=VGj`Sr8nI0z z57NHufS6g;&#`E0v#RpNRB&PyHQ$wNabciif}@_#SgA~dY9?<5d$7&t8uFax{943c z!SgcrJ)2g%o>r`DJ6UnkSWcNeMEIx*TAy1~OC!3hZR=KT&vRgFsXvwN+%ulnI%5?T z7S0#gjYPvFza_;DRWy{KA{IsQ(J}Dy^%kllH!QWcS1&uPYPSt6i4kcQYX2!Mef~|Q zjJm1Lx=@;2`&7ZN*;Kuv*D{8m6S?QKN80D(Cpt7mwqZZ4eaRVRo023OAxsG*=WpRh zA=@Us@}>UR**mgtc+gy-VC88;Bn^P6P`gsl%PL7kvHskl#$vxG4L6RE+Hb0+9G&(7 z+3J83IqYAw5QH(spa*pf7N+2Omgo@gh4# z0w|}<8I`TlzC~q}y7^w&--a5+7*$8SrTpS%C=EupzjG|W?$S?9-PVr~#@mN@&$Ft= zO;meTaV!}#jpbhCcJVDGr4)O#V8O9vUeHsZ@@;7-TVl2CIJ2};=8uV~D9snTu^AgHrf&0|pFcp`;J0f%t6x)% z)2EJW%57j7YAh~`fRH}Ffn4Iv(5jG_D)Wt6mm{oy@<{Rc!@p2co?8N4L783YwU~TS zp7`{H6;m;i-SJak|H_d|H7--BM>m=*E%#2?Vj-!vOR#~wHoz=wZSQ85 zRO*nWbmUx(U%lYf@jEe}&9#gIieHqL-@*n)mYl%j3H)u6Zbg1u?(1BI`K`%NpPoH#9PdkRr&VS_Sdf&gI9r5_t$qq0;G3B zJfwFnG{M1p>GS<95pk6V;=MaWJf_#s;8p^YJ;)A3KF5GRDX%R))LfO`#lJi@G2P4` zwi5;X8^(|0zQ}n?J9cMGj?dvgSM;A=R(hoMeK(JjPa)-e0YF!9eTqep`q9m`u|Y ztzsW@c}cCul{nI%%w2W+cg~5~mbTbl<;CR5gye#1=gXYJtWTy=$&I_-Mya=lxg z*|+kuxm&o-dtW6}9mylVp*LwmeQK1k?d@_ zWZ80h(B>BJXY2{1nNgO{QKTZw_z%@3Z)Z!^>J$0Lm86&w3?&F*{O%;`wG;_U2}|+D z5u*qND~GTNOCse}FBOuX;Im!g2jA*uS6V)TqAB;snh&&xC(|=-COwY)j{Mx%-jFbN zYL07;1&%rIyxG<=;cp)gTg?}Ki?<}$n&7TYk8+$Qw-WrJ>_`z==h;+nVLU-N7a6g2 zpPS0Z{(al?y>lH4r=|PN-;&i*ZvU`)z$LO{^Lxk8eN>X2M6BJB*`30HZJz1v?6P?d zg}vUgwfqJc3DP(Jjb-1J494~+Ybd11n9J7}BCFE>eU^QS$$mv^_r4vy$fw$L^il>l+S?AMElwy0Jk zlr*qsKiwI|Bm{(^O=9?L>UMF5*L>U5*z8ljXhSektUf4lrXb{Z0|)?dZoE)ryP=ys zOKZr`3*-!(%8HqXESy}1)3SWnQxbVmbjcSJ2iryuTGw< zqMsYWB0Yk0B&>EzOn7W?8b7$q0>qk(-*W#%*ljmcz5 zQ0ET(uIWdqJNga0V{fRsrnqJTuWD0Wbt^-xp`j@wg{P%Wxl+ffdvLrh=kX`RxeKjv zv(*5@tQ2+l76xcVm0Dnh2YQy;!6HLB8UlQ(`zp=P1YmF5rU8B4*}$+sh!>OmmX@lf zU@ms;7z(r|@QF>p)ntc@7J7D^<&#qrQ}CfS=!Z-7XI3NirSGe2sAhuxPdl|(6QDim ze8#UYIk^4|5CfjO#WFr~)8*hpYsZU=CI%fvW1imbdSc1%BNdRw`&Q36mM;nG)7)PL zK6*zOx8JAHvU-2aI3cVY^y$ehWSgmXManqO6j=M=7Q1_5egXe*^yeGJh6|M!@!GYp z*BVx}=PR7@bz45%z}Zg$KT=E+i&*n2K5x+s!CIi@$5=U^Vzz78!kZ`}S1#fl;dAWf z#$boM#ah!cr7&x3JcEIsW6JR)V`NlxWrl(~^1mV9`)xBsy*byzua=giuQE=jZnQ9KZ4=g})fKWK zTGG1B(z4g!VD~yXK3+MT*KFi66*BhV+A5#!BO>*Z9DNaVXO&euN+w8DIZASSCEwXR zPy9K!js=CL!L}&ix+xd8vXq==`m%4i_YupIH32Vdjr64FM;V=t!nmN8a_jgj>FS6( z7g9q^(-+-kc91Z^;|ck*}0(Z0C7wd^_5st5VU>AL1lb5pwGnI$)v3ERzg zwcmETPTuYvy$katj50C?>Rz??l^*=QT8$4!nr>?D5h21}99H_K`sQYZAM_DA;aeiz?!EX}Q)t%GO@I&m3phoGEb zbv_mK)t${UrnBbI0xS;K*1kSD)*kDl%>d`89UInn3;1c6H=I>Bf?W%0iLVaQ^EbJ& zuB~6I?H1>H^z4t6h#gFRnz2t%>15Dfm4_OrBQszejDQzEVVFhrE#E>}Sl1jjWIMsy z$~T3sWqp)Z7@6`SF5xV~#v(OlDjOG#05+@C71P8&KZsLLkQ8fbX(0^?dkypAT_Q0F zo*j9(2~I$$iL*h1Ang#CM}|YG*0q$3zt$P_avUOaByA75pvZ!ROpi7FnY+#1miW@Q zqe_02nf08*v>R2;BpEk&PA?qY6Fr~F(-mm+05Lrx)(*ii?dr6dox0y7&M(u%)7>|E z8;vSMuP(E}GSELi{-D#1wEyQfx!LZlP>~21o8jGJ1$M04Dt~i=q)B6#W%lMHu86J@ zfv;-h_1T=zL~j-62v&uF$649kifQrb4F;|8p^;%J>w;$(4<FJf8F@%RLy=3Qxz*t3`95cX1p04OKL-5yY_&EadytM+=>EA`h>KU1SAg+gAF2t zEt#lA0y4_t2H6&!A6XAn4rAIQ(En)jc!{_sK z69uorTx>+NKZw-`B9VZB^?R)gGwN=x1CW2543 zDrM{1bpNA%zlM&X+T@erQ&LydPdqjL@Ib(^z~lI#r;<=lZCP=}{lnd#)k{{*_G2}? zyy~8w=OxF1DBAKy2;BhPYA;I|^DfQAnxz5`Ld7d3gZ*@R*ojHLxV&{t&Nr8xF?Xa0 zH@k$qT>|$Nfo-(I+px^F6rN8FUmACaa6MfmGJAjcU^;53Cif<~Edks8;i_aZm$9an zu}Yh(a$Lhzo~m*#H*1>U^3_*liz{w~vgC%h5S_heeU)kEXzs^87JQy1;*26W_vBRw zkl^w#u2$uMHgz;67wAm_RXJs0brRs5_i5;_kHy0J9uD;Fys{`I%m`~sr`)LT+=iiY z&E9O%EHEvUeXJj1p$q{=k-I$oQM+QzvRmypb?478h&M^dvNLWPMs~~9eYo_2?=fxH z5I1H=7qs4`Q(VV2m1*MRBGosQ>hipToRotmh?j@%uM;$WPA`lGwnVK<*@rD>gK7x= z!))UnQuE#$z@I=g*SG4=#VzSjO^V!k*r++-4)dYdN zi7|Gh0^-b=y%28>(zLjq8yXsDePuZnJoGi7MbuM=)JmGjvFjTeXksVaH;<$|ung6Ba0EZ-eFOiB|Qhj^CNu$wNVq#5t7<<5FhT!C&N3+ zm`iIHia8d;GF+IuLi~((T)LbZ@HQ%2RYi3zhy!@CbdTZ2(P?aGMbDSJUDRh;p&>H- z<4*#(`?lws?}e`fMHJ{BE|JT0bWg}OG`t@QA$%^iNk82{Lo`F)0dtHA%m8x;dw{vG zLp}0AU7_Nb{5Miw!p7CRy7X?8O08WEq!i|+n`JFmSQj|ll)KeAqsr=H(|KmusXJwZ zxGm)1svSMNtAl;O6uYir1f*Q&I=IRPt6-+Ga}fz%;%_KqAw8lY9-&Z9_Cq1&tN7wZxdIpI@uTn7qA7SB za9O7toOOTfc&d7Z?eg>-!@PdQVKWW@9tNo%?sI;JzNLnM-R*=FbKKuCKOjyNnSaia z@MH1dWsU1Lxdz$?ONeC(a$~WEgk3j2fDZeF2QtMhTPFXeqsaSmNAQPDoCt2|dWVv= zkiB+-e{h(H)e@Bh9bGzBJ%8q6DRFCRGKWX+v34;B@gD8Z;A)BR`>2u zStHj%R72k-XFDYp{&c*ubTGZ#6}%vF(ZQm_D!4XKHnO zcbRW?d-&ZGQUcTAKe-ABCc}5Ldt9vLiWC8A}f+VhN7=C`^})Vu98%l)v&TSGu^Gz&30SG zzH#OEMRO7P99tp19nJeR#Y3>mCK5-L`vgPX|dpe#i zlRN3UsxFNKHX*D*nS{wIv=Ka`Dem)m=Xb2#o{{zt@QvgALyjGaI`_n8AAvA%1rxhn z8C8qbAL8XmIPJ)HFOjO}kLY0VkGe-4mk91A^tJ3Bwo?Y#SL0MGK#%q|TnX4zp}j9K zG6eK#p^#-NT=u9S7{NQ`Fe2Mc(~jW^YGdk*!{M%~8;?q_14smnikFVL{Ej4v#(N~Wpk zcpYA7t*ECiy}#_1G?TkP3A-1yc?v6Cnq;|yRI_W$Dap8j-zo*Pw1uL|3e&|T@>p=^ zpQi}KnQMk>{>n^Hm1Y;clYjl9zE*%U<%-K zas@aX%rT<(CwGryJxjv~F=K1|kM6W-yNK3AsvCEK?{mfcT)X|1rSdu5ize6lItD8g zThvnX9Q8TNFeRDIb6*06q3fRkl6=9Y5#;(x*o}jFr_3_CbwhObcVbb&oMBQ$6%{b# z2@yW3Rv4HIs=aZvd;4scZ0^dnj%N=zAop*L*?#^Qb0=D~4f7jS0Uv&SjQ2XOmu%)8 z`o;$h^B*C>+oR+QIa0>6IQd>ahBE10e~nZ?*5CuvOmI^;2< zto0tpXjrvwAhh{){BHXusi8&X>@!4Yrq5=#{HJ&@zT#@en&trKdeb9n+p3kK@nibJ zriYQfdi&hbtD7KCFl%Dh)smnyWvTSf6U^4T-^tq{Pr=0Fv5EE{vrM}f*(5UtsG=p7^h13|hddQr&M;?MM zZswWoMN~8IexYAN`u^G)(Szb?TfIq2@7lS{w;;pZf9wj%iWDVNF(@|o-^Y5*xyPdG z`>liX5TH6CAK}UliSL!76q#WQLGv!9_3R}*C1v^@)pa&(@$hj!1ngaIyXr?Tn6N_~C{e6nW4ZaOU|nG@8qb zO!W14tNKT?e~y=9YE0RopAQ!AQ84e$A@d%4UKl5eIH||rcwdJ1VEG$0b+Bg(l2g{U zJIKl@jhWWJvJcIjIcfzicg7M5kiE1S9wu43D$xe2 z)wyYRR%f&GpjGNh;f_lqLx(kB7xNOQ!A#ib-1h_TP0P=uYpSvb%95BpK1mG6SKORex}k*`yzr3AwT?xMld>h#1wi<&r-xRaeSHH)-06p!%8 zpKh6Bv+?og@ig)_*-#wPllA7_!qfY4WnBD&D3PDpPpTt^irNg>j^FAU(WGh`TW_RR z#8W`4q#H18b7(X5tAC^*9eMCVEbuXMe0xM3PF|aNqBcb@5L6N$toZv0%PDGVQ(}}W z!c0P|XbKvXow6&Yrc!wK>C`tqhiHH1bv2tjt|}E?>KB}oiMMn-yc9F`$aKKm!W^Ta z&k%~?ocjTXO|Lt;J6fOiq#0`S&CogUlo~dY|B)}o{E0<zDp;Az?;t4M(7QSEQo&{J@!nW6sGEaDSSXQx%R@}4{BXETP1 zhrWBaFE^z!5?@o1FJNO{gX2@{Qp||-lYj183~lC%Z5 z+OT=3>0=4!C-Ztzi~i}3F^4mXU5v~L>LbEs1$O=>%3A+V&@z1d!LivDYh0Y}e0V(}fK=)D{=VQezra@cxnr2yEyhr0%;&akF> zQRDeeSOK^;=hwA+zgB(ob4-_#038)1$=6NrPZQ4^%2RiV&4tbLjn)oNcfW=JKTr#d z@8HPaE|J77D49s$R+pqV>U<4H0%li))?s;K7r%j(J(zgdDtT!S@VDeMkP&#A-|%}D zyX!VSZ*4TZF`%rE2D7H2K3z~(M@5(~CojuHhG!7}qW|UK_OY5_z7m%s+`%AVzJRE+ zQlnLO8g6djD2GWMPY-=6)19Xm<)msT;xqnCm60a%8@@de;;m$SoM%h64i6g73Wu-T{=0vS)|A_`%y%IFkDG4%$+FZ&GSPy5w!u zL_VQ49pkyTX2pbLY1FqdtWO}&&GtZ9f0h_EZ}eR$jJz4}F}Wy%O)LgS&P?TWIaT=l zK!KIj)Jd4L+0@s|VA$oYx&77{U&DJUZ)3+`ups{}>E09SF^uwak|4_7;pW`NdRijn z{K#>vb(W%5LkBUHK9BeBd#$308Q-hX>4_`Mo>jH=a!#E=-X--|>6x~*()b2j@3mm6 zUM#Y`!wmf-Bxfaa)1%+AW|7u0QBsAaFP5Ao%r#!$VOAQ^#bXzp?(;Bcw-k~_p22mh z*lfjdlHBTq2sZP-V(W%z2K&2}?Kg@!2W1xp*yI**zZ*@f+V9~HjpMaYt7qSn>f^6u zeP5Caulct6%bJMf{trO}6{`Vqd*0$Kasq7YfcOOKPgzkhlD?g~GaMDjfTdqQd9rH~ zCRnlLPiu%t;~sInhd}R8jfX$1emx^)oR2OYO;3!)xyoDrUS@u(w^YGgISfrTb>!CH zL$8dYkr`-j9H@3*Iyi3P_N{I_qZ*09*>38R)Uh}-4Q(F+e|brho65+_W1Xp-dj$VM zpNrSH#z$jQF?zp*HJKuP>g;~Kcqye)h4u6J6GBIKhDiO-V|WJEPZ116eC&mdY1KxY zB?7I1D3Qk~&U37{Q0+sYFTEIi%XI9gJI5q$v(g2vjy9!;Vtf{Shezvdac9}WJF=7n zjYcGq4rlA?Ov$fMYH=o2u3+f22tm&b8Gy7wTl z>5w(Ax)Fa}!#E}#&v=&oDtG}+Y`$gBn(j8AejkfkUqv*<_lVD$eC!QI8XkcaH$^h; z`Bqxc2#Let+p{cxK?wP#l>>n~2SGt=`9OipaWI-Qpa+WogQjqSDj^IwLqmS*y1t=c zWk!Ple|w(-(Z;T`wcvAjDC^RO?ij9<@XrM{uV3n_|61~#d37f9CEe|8l0kHAN?KA4 zIDDKxm*+)WshO;{K&js(_dam!QAXbA{9_B$5Y@NiR3#&n`~iG5FJroVI9IGPsO)c9 zjRxlwmvOLf%{U;a%HShj>PpN3zW4T4*szP69Q>dRIk+^)o*qiq-9h4+_r^+4UO{6`z7a7rN z1|~M(Rhsa43k}w0{m~!pJQdm=K5@?FY(#Xbpny?Aqiqxz-{gBFow@YGEVgW)*qdZL z0Lr!^S8U){AVT|)RX%8}gbpP`I!-IT{)bPtQq z^CKey-lljlJo&5)Sn_%4xKc2i<1{?2!+m(=aF8+vkL54p+KcNGogTEy+Rsm(JBR+M zeSm*dgX%m$&=4CCk{A$j32+NTR}sgiJ8JrtrL6K@Eyc~VE_$w`!kLD~WK)6`*1gB5 z@p;_I@nNWOWQxDi`Ivuu`*X+{cPyp5rLSS_SeCAK?G41UU!V z0l)74FGHV^c@7>_2_5&sh7 zd=acw^iwQJdOogY8}+a|Q=>ja!U=z;^DDcry>jkuTRp)k=E((n!TT{^1(c}gFk^Ms zhiq(f^l7q3%puNc938RW+43{dT*h8K$B3e|`koX0YDqK$g)#U3k$`Y}Ju1u+E1308 z-8e6sCP+ePRg!C<1R-UuY@d+|I1AP49Fq#rw8m``B7p<~?O&{LZNyqhR2pXlBXR*0 z2ayuSq&YIy!i)vh71IU*ns&r7S!b%AUZqtCYuRl*| zOOsDdh^Q^cZXmqqI3=i&Ae%{=5RRDd5-ZM9!4@Wm>`-wKwwxwPY3;whzjoFCW*nM< z^!pXlYt*?|>U{*$a5?O)eSU8KLqjtimoP?Lq=-KAa%P_eg43Q<{D%0(ILsbhkV{Ho z10?A=?6<^9Sstj6$C1ZK}f zyOP6LxJk@`H2{f+DsRd9INF?q|E_dQtM^r`-0md16< zM;Oik)@19hL4P++e+jX;>U!Bm$qhZMAXU-(z6_s084|QCK%4!E?*r!8;IR8sR%_$m zibv1L8S`G>mA2-Kca=i1==3+CFb{izY46ZwmMhaO?W*ybey&B7lGhiJE)C(cd!`_m zGTgmxvAS%SD|m<#qkMG%S@A*wi0)hjx@_#NJPJMDAbVyP3C}!|Y*j#{C4nwekA~rbWb`c^a8nH<94ly7iMWnk;)g}Qc+Xk{^j@pRT$ZaCma8d>B;b2e zm4S?gHie`b{JvH!o+;?AmC6f&*e7Wn%50>86_AW49IaZ5SbhBOwN|!J9Dwu)te_tx z;$ZsFL-RR%;b`*pm$Y%9+;%<2e;SLM8QCu;fzzdqMAUGFV4$^v(VT zlD6bd>NEG`ycg*zbhk@mjd#$yQ*TX8GfcbRsA~lg){?0o5}T}fQW1nTn+zM<{H1#Fa9#ri7lcJJSt1XD1q-?r=;A07RzIS(Enpge<6 z19OFbnvxk$szSk;-t-p?Z`3ziV5F0?0Cem-bDs@crkuG|$RD;fP10wKf&3I~N4zUj zJNyan;Q3{41qBW^>Mh=|JAKP(c#$smj%2-1rojkRUK`1BAo8L)FxsZu?|Eof_;w?I zJCYfzY~5&At5!9I)rHO^AlWIvQpA93QNB>KL!QJ_HUXxI9^*a_aab)lAhFz#r{U!0 zT_@VkAE=)8vN!t7V9{n!fM%&!p6@?(%!Rq4PkvIzG7Q|bVjr;L(4sg0@l;xF&kkty zXr2t@B4VdIK7TI^oLd9#@Z4oiOW)GU*LH;T=2D zl1bPciokM!#QP3ib5O467yHA3)w#g>gn3_FK&~gFs{jlBEJ6JmQuM5yJ9DAV%~EwE zobYzd$vzIj`h@{^v&EnNB<(EkS=+QiP}@*hhu9oaQYf)s7jFi8L35Qt>Eck@t`Pd9 zPD_Vm7=kXJH3ic59i!@>;67A~U(<(4F|r0Rh{z#NS2et?&h)Boc`HQhS_2<;ZG=C; z8Q>b@N^9+QbIPr$afN9M-^~?TYDzYuBPaq-QMg(G5#$}Krmn*zKi&7VgSTC{(b}j- z$#JQ43Cj6a0?t*V`H5`q*e90GxWn!a0ggZaR%CCItEnSZM$)O$3z}>ZdZY1Ag1zqV zP?{!N3pZS9)~CC2Pq`>p;F?C^&>U@eZh;7wbCF-HtPOsduj+sMgAd6SJx!;=xc4?R zvkyJip6_pmt@VF`E2H6tUQ>`d>vUg+q{|FO2a?TlMQ?TaAo*09HxBs+f!D^N9}cpM zAuR{zP5vzd$B0+1!wqcvyC!_YpNfT37wUekK)CGdwN^lB&+kn{K2JhpH%;Nb&K9|o z8>EhGQw`SCJi^3rmH%8TV(KbE1>qAY0)xA%^#Et|O{!>_UAgokaMC&SAG`6E)n7T~ zmLs<;6j@6E!fcq#E%J9zkOK@_dA^nB#-OE?xXE`;gaX#jnOskb<7OGHf%y%UlDJmT zwi0pub@3dKUHKrtRjW-!i6|Xty4*W=LNL|a5;9Vcf<{j7J7!cBc<-W%c!KO%Y$!5V zA%f-8NV4(c+g&eys&>vVhsrB(P#v4<2ypqxWwS(BG?rGyD8@Eajw)Ux8nNmo^g}@G!HcSf8 z!xNbFA`k{;VCxro^PO6nSCr=XiYw#yabn9yO_~>u*~lYLgO6_TxH)Ea$k*%uMh+)^ z0`Cd@UmW5E?0OlM*Dd*qYieL&Uijs1t0_3Bin7M7n0|`DdS_?)urM;P3Z@ypW)MEt z1Cg=^5FpssrEl-pSo5r3oumAr=75XNzM%Pn_&CfsGQvMchhaq>`pvfqimPe4`PcFz zb;{@ou>~|rW;1IR9Y?$9sny)jxkZ*GD)1jmFfgcz7g&%$&}YxpJK#X1X=LP0+iiQ} z;7kT$fMA3R=zgD=za91*@PktE{aZ%!7cAzJW{uopT!ctN(0PF`$t^YS|5PZS!pr4Z zH-jZz$oxW&C1-**MNdrNR?Wc5ZYE6`SbFrH?jR5*{Zegs@DEijyN2KhH2$V4at>()h?=3+k5j%gKy6}M*URY*Sbb__v1Lc^ zK`mKwBw_$ByGlb0*c_cF3J(|P3taKRD2l)z89zcVg*V$eR9@=X1rfO;h(KS;=oqHO zjJPNh)08Q{*2{QE%VT%4;Cgh9qBqT2OEJ130==@Yxg~{|bM^rz3Y#M(eoiH!fQEWe z5neLATjn#DsfLok;<@D7cAqMyE^~}XI~6E8(|YaXYv&wS@r|m03pKSk@Hny%+Cxf; z7!@rY6{CO(#?#5aRq1A|rE=obsTiZ@5JZk^ccHUxJ}uYBtKl2!)2x8U!u66ROtteZ zP|xJWm`bvq82*{6K}B9(7Ryk|w}5w&#UPk;Oyv$Zfu^RG>~S_9F>YzaU_8~I^))qc zOU2~%KNdJTZWNdRb7TIVo-iz?#=rO7+Lc41fK(j&Z)b&ztFt@-d2wGAQoJtiRxVg2 z(}^FXW>IWaT+j@EuR5dU$#gBY&!-C6V_^$`!5Y(m`;wj1W;$0Dps9)}05Oh_u`?7vR_4twN@1f35$JNZsi3{iE+nSr9h^u7{6Tn=BkA(D0*FmEc3f_-N8@t(uSSxa%AJ# zAZ4~f-?K%>^hG8wC(_wx_Q*HE0n7Pjz|ddFqi_^0NDHtPxYS|kMxZA|tPko1bR7V} ze7jn6Y4UlwW_BLxt*!x;KFAn?Wl(lQua;bUFV_nDoeUGtOH)P|zLpQNtVZ4F@A>>? zANFGWKj)Woa{WbN{>KdS|I}~(EBhn*U)djeVS5{s|IhXhfS2IlU}FNz92YTikTkV4 zw{QXI698S}uLT3cDCJ^gV`==&&fLb7i0NNN5Ei!gAkv}d08k^G0L+G&h@OR+oq>ym z3E+P)|79)!pawg@b^$;zfNAW1ts=mjbW;|9-t%|X`hT0o&iTJp`CrHUXT<>r{teB4 zV}Jg)0Q~>L{$T-tApbBkdPIMT9u6)J1||U9!2vKiI2qWum;h)FK<@y=3!vcud=4fe zPB!*`6GH&v1{W&>D>oP0-|7Aw089rU>-<9nadERU02UkuI353q^!&ZW^cP(L*eaY1 zEG!%V!v>HVK+s|0WMg3=;$Q`&#RO=szC;Je32WHE{tF;bI1a9k360`aj;%Kdca7 zpX;vz^B=a)-;~(d09kUgFav`259ac>fw=#&Gk|^Wzd-{u=)bU)|HN>ZhyaZZ=n7mc zf4jneR2GodzgO#@lmS!?pmhKZ%>EytVdnslN^AhS1<+(n0A@<(KMvLdoOGfZME?z3 z`7e0I|H+2{1oFS?5PC^dHCaqZp-+6FX-^C_OdQZe#AG%$kz)Rf_X0q`lwwwJPNO9D z2A&9V#SB8Pe8w|3*^U)&HBWcL^H)aL3U> zZ17nd;rL(0g!J!=_G8u2_0l?@Q$D$Jv}! zEz7?t9Q|Gy8Ngd|824+56+3J+r`mVq$dphHAr)b1gm6>?;Utlz1`#5t=jybt>Eg(2 zFpMQkq>;xeM5M^#L|aH{c*oJesnqLA*hJAyGtZ!CzHt?&h*pECaSa7&4{9UPjKYP$ z(9@J7~yqY3?hsmSLqwERW ztflik9K@A8hQF`xR3KOHDya|3btI}(dak>sXi3#hL4X2BViCN$8lEjBttt7euZt3T zQ<(#Ivq3nGWUxGTyLD&>SK7@-SD2qq$j-ZXTr>oK?DNIuIeyIqj{DFRV))ec?Eie7 z`FmFVk5`%hlVkF~4YmKTF_XWe-Txgk`P&))+pEBTDbl}VCLC;Boc~Xl2^RpV`mf#f z!WY^H4X~V6<*yR&>C22<-mys&ies`2vLq8psL2qr4JapyJ)t&Gs_^^`*z-^lTAGWq z4uX?cXFWYvs7@SB#4ZgDRWwJnYd5x0Dw+nEqPLzU)+Eq9{V&42?c233`4v?{wTH8% zO8agvHCA9SAU=?MpkaBaWX5;9o$o-42tf2nxWjDK@8_>#KpPG~ufbZFGn(@aZKk+D zYajtzS*~kNPlOup9Y_JOK#CU|49k-fFz@2GTi4X+A+D| zwn5;ofmP`&a(r*b+eyQ>U>)Y*fjIGOa#)QY4QRHtKm#~{gw(9hv$4a5&M1LqLxFaz zyFFgUr>49pg{C2Z_W3dCbJ+`dj0fQYqDg@4_J&y18FuYTCxNIyfi|MdbooYJl|7Mx zj6j7%(&+SCboC(^LGrcVea6V0Q~7RFY)JyRL3;5ZfKW7v0wBpIgg`JR>~{s~`Zg9n zpkzVvk5FfT;KAba{W=iuscRo>Jiwk%RYazh!kjjh5ipdE;aWr4md7uKE67vq+JQ7y>Knjeg z6HBOqKpc{A6D^agMr0j2`{3sH(!7j>AVP2;g%s3`@l6J&>(>t zcQaM-0x_0L_Q2}u_|Pc}ki<*&&@}T&&`XJ7>B_l4F@VycSo6^_28NhGe;7g0g+&>1 z$cAw{K+^>S79_$5#HA?!lP-Xh&WBTfl-|FTW`#H!plbue7+}$ab2j2`^FNrnH=il( z0q0B>p-vV?Z+n}pB)}LTuM>qf@4ZE{C*KL0>=5#3lF{%4#d{Wgauq^NH-av#e{h@AXax^e`s&sBujv5 zIGzq+#>E=d9^o0}nelRIayN#nu;qX2PHB4Tbt-;}IZr=tU-VWSUX(LWs@!=mVcFv% z`6~1(`l|aX{Hn}Dk&|dzEO*z3NUpeD*~HC6h|y2dcF;6$RwVuQYO*kfn>;TsC3sHr zr*%G9S*N0jvq)tAZC*lImxqo#v3QK|E7*9s9FgbPt>#j9{bgeMi~C_h1(zpEVG2^Q zyR0Lr+IRryUeGgpEAP{?;*9hWF_PqS~VG0W%Oq6!K}|y}+536~+tI zHPEfZEwN=Wn~oKQKRA{gyTlCY`4oNJK89rZT! zvjT$ZfPq2nTN)I>9-QTG;HvL;C}$MP5;os3QD_Z$JEF=cGb2O>Klt-VF~kc)Pf%PI zGAt9o+ZBP9@(V2ZG9x((Y9oj$f)SoEc#uP7N5MOHiF`mL08>rLekPn~e}{8Kk+J}8 zjKnCj_d4Vtgb%OW6>frS{;AG|pfLa@hO-W2h-}dZTvOxpagC-OFdmrLj!y z0JaSNsNW<#>h}(O8_*_DaPIrUE4OSTonfbU||oR_uP~~ zaQV~#6=C)=Q)ssQ1oGJ+^-D1PlLPtL;BS{;pw~{6*F>W^L;*^dV5I2>Duy79J}{zn zqouu7AsBUIrM)n2FJ!C9vj_+;WL&+}d0><#W0b_84F{<4&1@HW5LJ5G4aO68b0sldd zFftfm!<;VDrU3QA+yZUS!&k%*G{%U>Xldnc&SPBn){b6XJwmCzIj~&qY2#%xHG$ zG>QCy8;uIy!6%ja+Z_^ou#WnMZjCHL$#0F)YeQfS%YCpg`pKOjb-^YM5ZbU(C-Cc_ z7*co*nXBS?4WX;z1;dN$lt1wp0+()&>)@70^9G#AyU>=$^ZHEd)~>u?y-_Uj;O;`i$?YLfTs5NZRY4&sbZZBhio&JDBPnQg-ICI|;+ zb}(;89QZfr{K0f~(rzUjKsRXo!8mDl<*sR8NLOK6CkXpF+C)B){1Dv73HuohJKu>n z;M~Uu`{}Q(UqGLWQMO>7MhSascl-|!cR0wq@Gq0L!+CdFZ{Hj!HyC>m*Hf+;JcRrR zFT)@YpLSNRK}r6(7Ma)q2z~4pwUmcL;hmxfuf(f9mZd$@d?XU z9HKBlduyrhD?tnB(qj@Ax9UKhXf z_&`YZ2BJP1O z=#_e5=K?7`SRn!~J{^EaHn^grwY{wo$p(SO5s3zYS4i|d zAS1}6Js|#KG@gt;wIPB;&0U!CL@|I3Nz6M-wy^e#>4~VJaH1#@Rdb` zs&WFPii#?z)U*@`=^s(=q2Mo`?c4(PEhB%%$0r?lE;uTO=rW^0>}SuLaM(vGurf)Y z!NjP{D(!z8X}shR#Fe&lx&Arv#V2)sw&!DJdh{Ce)czU0P{XRwIepe4zzFf(rmXcB zuz43~*s!tT4`ODBHZ&6)R8V6YrTq)=t>G7D?~jYr)Kx|M;57G#$SNN=mL{g^ZY`{Oj78e4?9M4&`@+;t{S%xrLn=PC9le-=mU9aG~_ZO)$@ z{{8+EEbhYgHVepj?wD%SdOX~lwfVDHCGIT zE(2Y({j)3%uSYK9;n$Lw80J1g+#}lvc%6^Pq>V+3tgGR)EDl?jk(Rve#k+FI@TsY( zmOYZE5^;NDYZ(zorTrk{A+b>mDhBCgYVv?JdRT`HSTjQbT+B93Jx+;T*T(+fTPx$r zR>#k1|NG?(%w(8h5>}i{l~!d&-C4B4WJ-GYsobJE zi5UBek!VkIHm; zZQHhO+eXK>ZKLC)qmDXu(sA;we)m2b=bZ1{^WPnJtc+13mCD33pPDnZD)n0iKFRE2 z2eADEIl%RnD?p;2JTv9(7EA4yDvVs=kDctk5R6NEvbZO{=3=Mt$wU4S99&D>&8CZ` z-}D~8%hG0>HBaG~d2MyOFUBbrvC&%EIF1?UD8tTJq`T`M#lUTC;_ho}Datw~;#l{8 zzmYX86St7lkCWCHl2Iivu7{4tjxRwhauO@%Gix_yXqecqjUOm3H8ZDSYJeIeXJh3~ z&eur%amWjrm7T3@G{ls}Tp-Q!P*FY>+;Z3X8*-Y4?sk|hXD6lGZAiu(M+D^K7v_9W z{<$PSqx_&eTXDV%sr`*tRJ326jK>TkTie-OzqU+l4J)I`Gk5&a*ZeV=2if9e3%OB9 z@6Ii`wS7i(Hi}sSQi6UN6g`ECY>Wa|xn#9@!)9Gdc*x4u&Igu(BE7JlJj;zK!w9 z&5;dQyQo8CC2P&}{qjW`NuUHLCJ=6sYDoyPWMncIiD`z9AX@Cx6}qCjAT$_1Gn?FQ)5nvAUlDWcpSYhhCdw%&N}o{KtA)?XkuAV549Q83p5^b`zv@R&QJ zHfL;?)diQr6XCYa(p9zVfVaGywu}%BiFaaLF0L-Bx#*28T(~2#Pq}P}-N_9euhOTr zV{sgad(Rh8R>JdyN9h-AgMr?GP4=%`9@FCQ8ug@l;fi<2W%8e5kx z<-mejLosnsLX?A~=i0{PWKv?Ts3=Eb&&;7D@5Ho}s8S5JyB5t8nsGL5W?jQY_nlJW zs9}YKoE=pX)kH>Gca?0Bq^QdYcZIu-EtxV^lF?F01kxeVlGmh@h>Y+5wNYt?uX-aC>OV35f|-Z+OD{qI*}yU82&C(io{MP6N%*U2^Y6{x#S)V zmAcy~70&|{YQE_1?R%;4>Ib^n~FTH9K;=r;?|p|$CH z)%e%Q3vU&>@*?MiR_TvCP6_)}Y29%}iB)>)*_A%obrPw)FSGlCC5MXDjfDImabJ3 zOITS|ujoY+OL8i#rjpi>_ufY+8rLY)dRiqsry3#e_#VSL8ahl&@XgCRo#CTq)bQbQ zVg(Jkoj!SfI}`h$Wc8Z9qYu6}@$!>w!jQl~S%^;Vw6CP8IlaTGaIrGEzAwQkR{*y` z(dB$>Y&83*9&n@tB0XF{&1Su9pdzh3MU2oZO?(`WhG`HgVx%laIXabLgjN7(c)H*_&so2YM? zoxPl+dy2x5LHwdqX|L(D`mH=-Y@->YD_D%98}+dI$2Xz)`o}oN;68@eYy}x>YuG3~ zaDXGF@MU`%odi@AoiUt}`9z|I!DSg7+g8cZ`6wIrOJb_77&L^BaNsb#bQwOg}W`08KC+gZ(+N4_YY1nMasM*h|t&LS7t)@p&Vd zE1oOPE6Xd3P{2+-hH{T06M!`qW8BK*s9IF1=*rC<($n&J%>NSqQS|u)G1kTJ=g_f9 z*3uhq9NLLTkVTZG&QfQ=w-D5-!7c0kU@1<{=W^E-cjkidvG&dV>`Iy-3=-jJX?Ne8 zV(hebpX`I|W1trvSSehz9+GyjXp#uaoE#x}>fU*t+G|8`kI*s7sr!u{pCEE4-!DTv zTRdAm!#&GgtCEIYgOl34XVfeFLGi4JE!;NQHW}}n|IBf7_0{2ias8WA>liwC;mK8< zK$80q@vR+Z^!~J&M=G zx6kzQ^a7n{+=NYcfr~)s1)yK|&PKr1s+WE6>L@TjtwJ#{bY$^Dp|hm*lBsvCs9?a8 zP&yYMl==Vl>z!M~2Yl|0`=nfc7wm!3!;+fgv*M9_s!Ot~o7-1b`&(e^ph#%AXaXVn z0FMz83-!_E<>Sk=we_R38(}r~JEz=EQ?-Am+|wSSS-c(GTl&3`%h3_!IZO1O1p0QcvY`;v(SDy-0ts6r`XfSZ6Aamm|^ z0pg8tK}-DriBB{};(^HEU<3+BenW1f&|B3cktG{vF5)00T&xnG;MQmrn)$us!3T1; zKOyp-OQaH3s#p5D?fW#sEmd2kifARsya)p#t5#K!pFis0Zllt{#LUbTDHV0hx5~|bd4{H@#FmLdjP*yEGU<~Zxkuo}1C_$9hL zmbGg#N!)65iF(}UKo@pf(s@~7?>N=6WWmw+!aufSRJ5eK-Uj4&35ZqA4XunxMH2x$Zmw4{X4Ie+V^Ytg z&}*6Gvfr)^4v)`R$=3bg)9f+nS|UnR_}i?)_&E#InJN52vjJTbza7G~PNU0O#OzGF z3SD}Pp-etlqEF;Z5cDG=)(2lvzHzP!P-~E;l?x89G;5lW3Zd4kr1=GT!pm?FN+R95T<_=;VUf=N2|5-I1WC%Q8v*$g z3`|ocpm`$JIo#*C)kG1M0Fihz8(xACd53xP`l=$EQRMY({6oecYQ({PSh!+2wD5-} z$GBP=<0$%sMWf7wmMHCNHSKp#?FSzTi}bo-IPvDGco7acj6yM|P6e`Ubj9Hlp%ut7 zm@h7uJVH?DX9;rTYPDvF!p#TYx_R6WO9BLHrpBORa)?s$2^heL15bb#49SktuE&rS zi7>BMg)|grO)z8AQpr|likfl2@Oci=tCNB-s;q0-vlSU*G>0F94plW=gF)1E!=@A5 zB+|c~eP8=^b{0hK?3BAtO|`vh+x)m13(^c~M7{J&>SBBTlitvj^&&%BN z7tSjfCfT(ucWsRfI6N$>>pgnR1l%^#s1MU^*;1iQqZ8KGrTJxULwJpcI1KODM%RxBc?Nu@QbRcfpR!2UuDFvz3c!#*OeF0^t(_vb-6j2A^wgd`t~w9vFvQ8b%8 zL*hA1SvD%BPSNrVH8DL-C+M*2xL>YOg7P|aGtudI@UtAsc`ovA(kXeF2tLwX6Y}|; zb#Jjq!QVsgKh1Jd!QOU>qcNV>nLu6TscE@|a58mFKLEil-bfj%sceg5i9^n!O=dD( zbX2AFVpo9zmkhS^eld0>%#vWUVhR7& zty}Y>OVSfMTHr(_eI`Y(U9?j=EACPV=EK87x7AthB%o$r`x29<9*L14TD9<=&vC2x zZT|Skf-@`Z5|2&drNuL5QCEq(z16qH<<>h$fX`$EoBzVTzAJDW0y{!PBo3OXI183I zZCR%PT{5d3$o^Z@bq<|K5?76&dS&!++A&3_^=E@q@0|u`2=~-b?xB=E9JJm5iAN}5 z$L^9h1bo6e1UzAs50c_Sg))&qROz{jR2P%dS`4CK0!9A5umliXeKf~#s?!T1c|wR* zkr|X$4KdP;)sI7SVy-v@N2{)^?}%2c2DLI$^s87dcDf?sjl~jkE4O3ZQ>l6=uqx`} z?})Wa47FloXY8RV{FF-AZT~DM1@PT9{doxwmG)nGtrEWG>dO zeKEc$m~3>I$*Zb;A1W9~U2btj06|os^B0NzRtrRnI`kt#0yPVozJR`-z5rt_y(!&9 zUu*oZbqeC6;83`n4OE@EcsZ1bN#>bOzrSs}ZAARp=hA^sb-Hqh%#{q4I|>~F7T9E$ zQ+7OIf0D?_Nd@10eS!qaq6_f|%Ov5p?cWLwt<>D(I18ox=3xA5pL%8W?EiCcTpupxOxP4|K>BmZ1 z&EY<{L*vYuM&|dY`BqN(6Z$n3sLeBFc2u@nrJiaH1}ur`IPiElc-5E9a<92Vnar=7 ztkYYo!83ur&GhH8wtb(^xJ`cBrbZ5HE8$uWtIKeNa9{gb5t8uPJFsP+22~dGr^lH6 z)eKX1bnJqkZmXC*kWj^u?9y_=7fuHHy_tE{j@$XGj z8FVU9Hxr?uk|Cud`Pu9%C(c}SS|WX=cy_luVj>LFZ!hfT#?? zB7`Q88e=#EhxY!NR41gkU5@WfG?=h-!uzruB_NmI_XV$~h@r2C$71ViLfGI7$L(fxmXjiWt9muyrgPRHy6Dczy4uMhFBC7yNdP`Cs0z#@Vo-YJ(%qVZn z(WLSbSK7c01j>IQ0@_gc6y?N@MV3}Kp6;8OtvE>9$THB9p%@t48{6JMgme{Rp=N%m z&|(2rU1n8>%#cL{`UCO5_zJ(HuS4bd4t?5e^l__!G_m7*GP=kLgl2VJwOdZ>u zI@Jrf4vR;SH3)5$>lu}L;%yid-m@$;i~~p8(y|R(<93R!6fYRl)WI3M(o@HJ@d#mf z7;n!=J+djgOK-4(8wcr(9vu1;sS@v+Yx z-94)V1FK6nRj`*&1%}IH;4-QePELa_C2&qKOx4HfnVYAs$E6`yZX6CJ($EqtFcfo$ z$U8PS#T{tW{0u3fi&Z@|z%acTa6W26ENW{P`#R~!yz=Vq^yjuK13g#as&Tf$jT z_s?o~qe%OeadDT=Nw~#jZb+z=qp06#NUKQhOU;{ns+Tv`SjmcAiS3_SqhpJeVn04mc9~1d>u^6xVp$%slh)Xa3+s3FgVn>>98E?q|XLH zQ`D_tk?d*^Z$&s{$4^()6%snunzn^Kc%FA&c$ll{Ie!j=-S^}a#ZZsW(`PJsTBuY#6Jvw* zA5}dRelA7sT2@xdOSf0ihWp~K%XnHDWnkM+;#-ISSy(ifuWhoD5$nlbhtpM3-6)2I zaxiot@q+Ilq&!?bU<@~PT3)Xux?y94$&n2X19%XMD3**lhm$SYV2LYyvcDaZU6M)Q z_F(9%BzWO~MST+wqAc8H?jbM_85pQ(i3Vh<2cbgN28oO7U_qu~L=@;_xTd>+hHi6~t!F z5)YUt4H`l+A^SqGMcKnmGTc;A7O-5oM1wa(!i@177#ozp{)75>KluY43VD7$?lTe> zvxtu8u(SmQ9c5M|Lrk+~LT~m0Uo7C=NN+$KVj#9Ha7Dj&?=HXu`~(BSQn7i~Of*8H z3!GWC5VAoZ3tgiLCMqt(q?)Y|iBon&Rbm`NoC>6pW4^eZj|c?L0o#+P5_aqzog;s{ zf-9GUTt-xr`5pU${3+(O)ijCk@+Fzt=}sbk!iX_h@T9)Zhn@<62O`dj|41toUI<-n z?u*-)hY{G4z5?Z@i37t7Q){S>JZFIUZrFC`HeiAX>$vi@v3kP9K(fX`W-2I*)iTC0 zIN(_cCaZmAcZp0>%1LFW4`jD~KI=fT?^5%pHAFzH{PimeRhIrE&1-c{H7aGPCH{(z zpdq@a(uMRE_ujH>Aa#zg__9Q}__E|2q5bY^-}bd!NQoUKLe}$072k90fk?jI-J4P& zhqsV0BVl)(u&?k{u+5X`@K6OSdbA?TC@Z>Ra1r(bA(LD-^`ND2?GSDzZaOOwaz7f{rtS0?CY>CZ$$o{ zWqm6ewD;vbQMEM}m2^YpFXfK6!7rm;SMpQn=w@@yFXBs5cqER?P0^*Wkg-SZSk$_i zo?2cz8}?tSqCs4S8i#pxnJ{^({l*J+TSx>nNeFjJS`whXFwiL-x_|4Y*BPyMj80_n zg6HmAagkA*Q$?{-vvppB)Zav}k8P=GO9aO$gU4#zV_T@BJ9xakx{ytIE#mpjA_Djf zRX?7?IO1r=NT#U1Z&WFOYb5CbCz$o2b25myobFoFm80mAsqc-k^bo@<0xs63o+G{b za?x-Lwa}^XxO@!mSgyOF3l-k%{w!A097|k&EEB3~DY~B9pyXu|%Cmhw8j?4Js+D4H zMg$QCVObJxD4`T91qjDOFDEY}URv}{c>Rs2)<9K!b_`AEA07PzV142YYQi4!M|lEE z8hC&=4ZfD-yTq!fc`@V*CN9WwRnAQ14H#Ty%{t+`xDp5 znwC(Q-sW$6Uo#}w!Bnkh+rc^g8nF#-Pqf!jRWmD`1TR~b;(Zxd*IYlN$L|)H!o&ct zvJP5$JueVCnHzAbiNd;h*wXz`lD$loW!*$Z@2Ygh>7Gh&Pl(8%$S2q%&c6XFIX_6NMm*V#P8nN9U-po@54{ZHYIH2n4aEVA8_q8p`=O$IF z*ffgo5rX9MH ztYdWPIO5$v?B+iad zXBZgpqbFZity9WL#A$j80eTl7rC*Qfz8(j3&DX!RKi-bC?Go)etI!go)9iX|KXgv2 zu#(4A!NG(@pFOInp@8m{*TBDC$0L>@!jUT$+HwB1j35i6BoJwc~oR+z$kd~ zm{b(@P@IHL4v*@Cc;7-00+Z{WCPb#vfbEbfRvUW1Hla6aXNo}PTpqJ#`3)bETu(X# z#V=+PBrnJ#X)P_?rSL0nC9ezvY=|T%ZH96p+i+!NdCe-^tEKCMivLY}XJ<57uh(*j zYTD!gKjB23E?ja^A+B}&Ss41*;xfq60S4n|; znhK7!ma>Q{T~{6v4o5qTL$w*IU#T9lo;Uy*4+pV`I;PZ{3O~n0hZHNh^yYee8k&P%R`h3A z+Urmdb)p=jQOdi$O`(iRmr6RGfbYKRzTkZB%cEwMgel3i!>+($o)ox`ty?dxRYj4Q zgG`2aKToCH9XCN0s7(vMuYT_}l=kN`n;z!WYPpE|kwtcbu9@a8J)=P#L4_>eNc+`) z2<2zINdnk{cn}(q@9;V72}w8N!al*(_iPJU_*-=~HKE9zIv=vT{(UgTP=8vuBgLWv z!Nd@_Bf}xjH3xjQsIUEL73?`_;!4~Vh$vtf)p4+fG;Y#4#v*aXhyvl%(k>m1h(n3s z5uZU=;KIKdd)gS{w&ynKXZHQIa zN{-h1@X8GvdUi$&(uVLGsH<$7X)|u>0L{Gy{(qS^~6{r=9zQ07t$}Q$w z5*6u!=Mm@JBG=dH!nd{!o1)mVmv<*ieORp9u;IR%^V{s(KI8SfQh1G+b@^n17s z{LSkdhTFX9#zqi*-CD~4_Sc~Ah9}-ok!N~K*w616G4|Ljo7@aL92^_WHNOIfM6m)4 zVwYxn1?)m0d9X4WCL_fT3AKm&!)-KVz6lOQgsq5j1+YfB=}Ic*T}Bt2N$7?5u!aZ) zOVyRm3wXiFT3FzRMTo)Zt0;|+NyF@?5!JT8a_jRH(NXpWN79|#c!!*xTA)H8O5kqf z_Y%m7EA2H~fQN$n3R;U~t)Lpd_7zLeP*O@sw@R-o5yy!wV(xe|)r@1lBsosDHx_>c z3H}bvn#?36Rlb8JM|~kjQ;q3pcG@$k5X&O5O*T?P$D%wV*&!sjrN!4^|E$_!qk&VA zot+@Kn8MNG@DNa(m{hp9-4j?&pm{v#7M!ZuNtYvrKf&Nlu4s7MZTv;KJ1~NB7mW_z zwcB%>GuRRMGiNghS->IQ2l~RygYX;0-q$!1^gRlpc?!F_iL%6~)`h!cG?tPz`EkSj zv9DV$EN+VzY49X4q3cdv&K79Sl8Cav+_m5_`lT})%@sc^!^3UJ*vD8mJ3 z10Hx)^7wQ^=x$5mdKNsftrxq8ya$K9T(g!rCPJ(c@r${fNR0m>rjm#y1BXCwEudnBE2X9bMCDEwzq6HHi|mV#6h%L{D0vHWIx#~a!~1tx zw44+0K%zE*1~`CjQCom$zGL2;#E!N@MUtTiQ6=ZG;G!)WGUew$o9ImcdDNL={C58xF|wchhxze{CXPfx~^O`eFmKkf55ZpL2IQ zR^CEb=b)r`6AG&6J7XW%@4Z^CUBUX4j=b9~kr!8{k%$q}u80~drf3q&V~&bju}h}R zGU_=nVRk(S0iQ}fxkw`E6f*FN!e9&;&o;mZjx++Aq#|{?LaxpB6xAkD^7_46k(uKc zBWkB?{#bSpCx$q7cpO%Y7B#OGd2jMn0G2X$U{bI4AVscJlL_QFvacuFU{&Pp+?Oo-1x|r#&r;f@B`t z?plD2MHIU1)*=L2k*%c*R6|=R_<7!i;gpK?+e(LT=I>q9!lm`!?QnNyD0}PpE$KTb z93h@4G0XGCz!9FX(%*GzoAq{?H- z(WF`kQ!Nq-wdP+9nbP)ClioHb)fXM#Ud>kZ&VNhdl+2#>+GO!?XNzFz{66A;-Fo!` zNu9~}_~0?g+D~$gEp@tSJ}pa-NW~xft&{M5#{K0sT@>p~TIK!^&J9DH4;`gz9tH`otw+ z9v*5O3ZJ*!Nh=e`ec6cGzIVvF(msV;AJbCZX4CLJ?ac%DBNYRWfa>}#Y6vnKUV1ho z2wv1Es{48^wnu!zHG8Yb2jqJXlq zViWcmMqQ~yZqXBUT~Rma@LQZ3noB z-cy;Hg}7ojhwAHIfyTgIuwtT7!*wE5qF>&IYKwj+!KX%{Aw_t&;Zq5cI(Fr%2V=5tIPW1YrBkDnze6Ak zJphfMWqP=IHK)m^P)nrVeo}1Y&t6|>DxV1lpq@+79aI4B`c2sY**^tFZ-=WNo6@;`#Ggs_^Kc9Ab9A&`FdXT~bp`=pIr zqgZk1jubVT(b%S^-8>yG5OWK&9{@KX_-&os+6p!4;|lpZ*GF) zQGh8$2y{n)l@ZMWb89fC6tNh_vnF*}zgJ_EqLu2r_7dACJR+2-m40>5@{*7Y9pS+; z8y~%Xvj7C0axC@C8yGx4{1LLFp?e|!tcN_wBIelidQ-jtLqw5UV8QfpmgnZ;nFAQ& zh+=*e9DB?1HP@4n9oR?-@!znzntFQ1jeKoqQEg*KvF=B?=FBemx{i7m&(ULWoCLPI zTU?GWBf-{Oa_HYWesww=%suw}h7U}$$MX`EQ~Vg*tx2|mkhf0wRk$Sy#w!*3b)z3z z&msa9c3L0{o^`67(=~!9H(XWB4_q79X{py_PF5sY08 zcEdPZE$|>F>Qa%^%EU-~@wdYTYg6l%lD=F80^jC>+A4$KsRpN)LL>4c|HDv$f+wd{ zF23JeRidkK9zMHK-XJ?rsAp+-@|qZx(vMoxqki0j(HXS~OQFkRW`)=)feO{$Au5TJHJNhfj z3)YnOpT9<f%(isv?eMTvYE%S)Iw{`#3aLk&^L0Pot`mW>P9^ zSunN`P)D79S>AG9Q}X2sX&_ZxTPNh>OQ3j`IeF?s)Tam@{<>2gQVppI=C523RGgjA z(KWoBxW$%IKaT6lxfx{fKt;cH+~s!GNe4m5b+ambnK-Ezd{u6^s5eACk`IED%4~Z^GTQ3%6X;sD;_TJUJ{FoAhcW2D&SKE? zh1QUQ+vy>NL@4_mX{dO-eTRcHgN8Z&Q}`YJr-j{=i^&sQVVXRYtKaXXkNIU|yQ-?n zPA`|kgeQ{p^B7n=1J~Smoknz1e6-S*l#nUqB8GP`UU()#0y9x?xdxI!GjTTLZQjsYlv4mRh`l|YeAxlnLrwM9ojW1q$x3E$v}u+OL=jJ=~aq$~WB8immu@W!-wL0Okm^@O+7D8S3P9xTgy#BLofp_xiHM zJFB}~Z4DlM%?ipY4=$vKqH_PqWDygW`?(nQiQfrMP#%qM<~{X;257$?LCw9}rKOk}+9P88OQcze7;g zEF0p~e+!79O{LzJ{(c3UstOtpu2dS0Vp@UGds+D6zBHSN+}D1n%3I}q51gBsa;t## zqC3D~GUH|jPhDw~$z=HB3vnPivRP1;|E=fZm)HZGs4-ewf7eAjcW+Iggj4}%#l{8Q}AvcMSnL{HMv{iT zgn5gSQH_zx;D$WgFKPC5bC3$I?j%%ob7z3+w|E1zKZZ`~C{?P|7na3z#})badw;2( z254%ltZRuXBeZI%zF{DW2$6?TD$-an?=;L}B^j&PVXBxU=#^_Nyz2sKKo!IH{ZvsP zY9%0Xg#sfb`3k&DeCiP(HHsTET(bM8%D&%drvNZZXzx#*0V=LgRhsCVF8cnnGQSu8 zlEsF~t~#R&Q${t{IY^bK9Z6xU?PU@q_?FJ5VFT={=I=!+a-5D6Qf*A9_W|kqKgVLa z>|lQ}Jmy4wVHMry%kdI=9DmXwf_5R(bw^=>t>s(B`0?ReZ6x@`M!woefEERtADK2B}%#En09?zjd^Xj>|rgPk953L}gL% zEtfY>G)f4S?5m~raQuJBM}FEXVyFRB7$Jul-_wHjdFQ5sw9 zkqmsSd_d0(FghEeOc_-ceKo_d@z^*uO8_=~p4Cr*8Y#n6u_9o!)p*tSdjjz?W7Rg@ zzz5YL#d@muB<%W&B)#V|FU1qQpp$ck2pIKA46jS_@08?pngiHf6JTx(C4dS~TQOWS zyzxe<#6n*kP9?dnoS~{P8MtD4>L-Ev??Y9!dWh*nZ2qH%yZS4b#(!Dm~6+%W9_xGl!q~H)wue)9t(!>kLxG zV88+O)OWJ0<(4c@HwnRJFv)BU!HpS~o4sU>?1duuLRZbQ@GfeM1cU+CsKHdf==|uM zm;WSP!l?qx3Q&OHY`|2p06_yXr}Aqr*4zUB>BS&b148veF-<9K(-)?GkZWU;_i_B|^=42@FJSqes`Pzy{2%0^nv{>n() z+&I-Ox6rUJ-%n5hr*3iJ0pK5xACwLc=oFF<&l$$cm1MHKFBe%T{zS1-CK`=tAdBhs zg-{luTB*U{`>B9JL&5%{{Mw(9D+Xzr+Kg4TVr8RN^)*b*8(TDPQR-qYo9LvgUSKR( zJPBCLk=AB^Iq3`WLMYHsTud3H5*umc&Efoe_aaxCh72&&G`(&osmVvFM(CV0ZGVDj zPoAFzce*d%Wq9LX;i)#XBb4RkWNVl@YI_-`A_*+7(yS0t>?v8f|6Yp8SCZvTZSOu**-6pIARX~iqD4CS3X4;sA51y!N+->R2du91@I7~{daoD;( z0QZ|19a7mxsj@C&?@JJ zHeH~N1BqFMzXi86OoumNUQpGr7UkRjL~}T3#`eBB#?Bq^cK=(*2}w3ix2B>J2>> z!^>DNd=VS{%!U7`gL!A2chjP!{l!K?`*7{51n8>*HIuhFZepF*Vf(GaLx zx|HF-=m!eA4dw!YAS*(033Im0*5rRrSGWRRvN&(0zLqD!iiK7y;cf&fAXK{AsoYE5 zPTb|=hIKkcO)z;lST4+IH|fSj@N0v3`w48L^&r{(Jy)p)xXAz{TMLxT`lUGfs(OB6v24?>1>(L{xPp57mTbH zs5RMilHlC%cXRBwRmMz;0jEEnGi1E|p#7QTonoadH;~bI3Zh2<)}J1-ubR}NQ9s-#wW>#=QOPHn;+hPS90LTI zAju>U5B7xPJOgNNP&=W_7GS^3Jy*S13tQ1?XHq#OXnYdOIpva6NkfM8l)tnQ&M}lp!{#FPO~vi;N+f@+q5G7&N}Mr?z}qfaiG}0_scXug zXt!qd1X`!@?(2|Mrwl>(@K;xrG8L$-ae!@(-y&wVpRTMTnyzd&Td-pO)>F6u)Q)Qt z@$l?|?)({$vnzqSwGlMF5#jqIy0vQy7k~2X3ZkI~?3hdvc3s710oT&$R48|o&r_Bp zu~+)=JD|!d@Jh=|t<*EITTf2shSE|R7mXIduJQ8n!ey8-fme#`gJL%C{3t8PCVc2s z8ZTe;`)>`R(NhDW4cpTvD)2_fqzkRX^F1?+U9lVmZRZ^uvQ(tp6M~2vQUQ8oHGXXV z)wRi_`G~$Rz+rX?(_t=%yh0)Q;qIWzuEy^<(gB7N)UUlw39KB0?w`@6h0xZRae=>Y z5GN#Uf1{`@jZ2EB9vGwz8K>dam9qeG&b&`)1UdBzkfiljC$*5qUE0R2+ls{O9)91l zW5UuxWCrQgtQ3x|ep!EW(Gw^l++_*fq0Az)yr4_PBtD1iRwY?PMv<|T&WA}OQ1baNQJ#sJ&d+y=b? z+lF4v>ILcI@>aaaB5!R(Pn^V`_b*GXv8=M?T#C?7W<}I=Wp7<3^*#&GWTvi?qNIW~ zS|sZ2(xhR3Ws-iy(#o6}wiPS#&G3s=C34P``9jkLx+=Jd8&X`L+Y~F)-DtHgYt(`w zsovJZ2xzS*P%^F7^aFJ3L5gdcl6iv6Vm)qT(Z-eQrruXgKaI`}*>C-*N`f}lu_2?; zlNUU{C9_p5tP0CBix;hoQ{G1g0lj7H#<8e6B=f1ncHvsEPj zjtgY!$ofRWX@XxaXX{6mzaCmN_xXvnr}d>!%RMh{XfytJFvucPrRon;6xk zy|cr~vQ@_nKC>WY>ssV(p>nT6Ie)1zl603vEzIvKLTUd$#-$x*WSP>e z&}F~#^AgYVMr#xmoqnis!F6HQCnrwR>=L4xs*dyBDI9xu;FxxEJFXAaihK?5BTMiu zrR+wslS_DtE6f+{y1pK_wrsE5@PY@>Ud|qJmTeWD!$<#c7cA5gcf$;092g?45Pq1IsE8-%~c+!k@8%o z{msNLrCjFZWi0Whz*58_PbJr8M&``y!hF?(>sVoi)hLU`AqWXdpWtGX7|*ZD9N?$F zDo;+{&0;STv47`in`!g{PvUP3o?&Z`soNIikB(wEtEPHg^?n*T#?Kzpm0bKeqgY>_H)9dW;M&{! zX)wc3kaGIXpJ}{r+n7`BKOf0(K}iV!(`&b8QJ0dj6EyEh;gjQCDuGX^f2&1i4u|~Q z$V=G~!c*0z!E;apt)q*6*d6+}f@%NZn=+jL3QVzc{xQq`3o-Ri&+l)(pPZqsDIvY+ z|C^BEAF=9R2`d1C_}_H-ze0w8h34NN!~ZS-|JNWxWo1+AUgng2mto}2_3QnU_xf5KN#VkjQHO`hkqUS zAF!b=Ase7LR)8?b#>~X=C&H`%mlD7xvj1@;0gV9glYkllxZywQU}j(h>}3DzU^WIo zH%jqekV1eZ_`gyW0mm`^!4Cg_EkyuG_zz3*Pc;7l4*nk$c6uzR{YoB|0g!d22e^F{z?!)y>uMR0KgPb>i>#Z1az4R z06?+>GKT?B#2(1-#vRCAB zVEpUuzIVjtlJnPIvhA!6G$eP)@I`>(lo@hIe1u!~ff;VGZSA*qSf9-~E1<@N`fr+c z+MWgXsOi1)pn*Tsc4U)12` z?v!5K{MK{dq5j}Q9|Lm?S%keiwv*;>Z+XPPK+SR1YaGFZ8ebK^qzd`V54 zNJVKiP~JHos3n;Lp({6079t%J{eJ&QZAHAB`yn7nNTJ0J-J*{ur4SsEa-2q6~fbK z8wJw1NLW-@)R@v7F~EZYB50tz!8x(>P&tb$DNRcy?UM=2CXc(;#d&Q0@n0$&<9@>= z?JA0(4)7ZOqPP?8>FPjXz8;-L-?h`VqSq!|}r9s_X6LnXKk7)18)2KH&5NY^VRa+%o<- z8UM>_`#*t>|J{=FzXcutTIl{0==fJw|J!Q#=REp*ll}vAy{6Bz>%p4q?|CaaX zz7QTdBg?ySo$I3GNPy>tc)EegE&) zy;VCiT~oDFN1pTa>FJ)+)uR9TIgIUJuozQi?NI6o!gxq`i_rMzzaS|?Nr%QKnqVkx*zQ9_m+iZq^F!8tG zNNqZ0)@ZoQTSE@$^7hxB(&zR7LDV~YW?&mT;dSm_+85~Ib26IlX#H(q|Fzp~oZt=_ zK>+>?pA_3KCKwO5_zOBw!%IFqOrCOAY8m<}c3>fPp^bp2bsXO$j+ZhqzJ+|qH?QJx zZgMy6f@2Z!PqJUH^^97HW&ThYmA&P0e-h2y13CQSfBV~i3&uYL4qhH}4i2R}WzB&; zP$N+=yYHj_D?FI~%AmK8+?nh*h#C`yGT|7ZkuxkqxLq-7mVzRpW0J3rUH_p6U)ZO7 z<_@MW!4+ig?Ka1^t-kBYO=LIqYcYS;>p!i`u7q(0V`wy zN}pm5cw=^;!E4uWSw|x1tj7_Flb=rU)3KN|3yVxR0?SB(9>&gpOOjKx3WhvIk=TLF zU!-!p7>R>nviJ*+gA>FluV4*tx1rZg`G`g& zx@JMxEK=L35kk4jiXK%5N9af<6h?ThvL2Cx0>s3V36{yzvcOvnj_6Z0Y_C<;psvIh zqV^GQm2Z`6mGw~cQ1wuFWH}M+ymENokww?{ebf%K7xo!8WCuegQrWg9bW+=B*^?mR zpu7oYb_{dh3{MWQDyVH@i~W6sXo84Ut^zJuDi>yt<+CiqE0KO6a-LS{XRVNkpEWGH z8cdb18c3VC=vv=3d>;Gy*D&-glefbGfi{`=*@EyWgV7c?YJvx8XGvu4~ z3geCaY<$mkUvidprhA5duXm4s)>8-l+_j;m!Os|@7P+GSD@sZ&qYb?~y*DOic2G(v-%U~HCmtL{u*VfM6R^PVu|4_ia$%OPDJXBK7{+o_9}ZOajYkFTYHJ|s)q zcmk2adszj;)%|ymw6h84O8GyT@z{R?iA1LR#Uym#J5B z4B*WSFtYy!51tJYKO%Mi8NWi)2_xM@x9Kf<h!+^WDN-P7OqhC*I~rjBkH|Tr1U#6ND4DSXI6-TZ3VbJoIsD0L#y*Cnfm={1aYwsbv0UXVMEvescW z!YZylJeaf6bVsYOB9!d9ISIRyzHwx(h+BZiL!G- zmv4I1sg;UHb5~z%E)E?D%?fZh#awaW=4WzS82DI^1b1hGZI&pvEXB10UypLx3Gp-#rGV1OQp%`Kw zq4fyM{0L85zkdPU1LpVWhEUF3$J}w_#Ziy8+EGCC%C2qUU;#C}+tb;=SeBkXSNx>A z9aEi~c|B)_Ly~p`C%@^2Oy$UTHRzKkspu*iBJwRxF*UmyzTwm{XK_+{PX}Mvkeis- z?%Kry)^Xh{_+ABF%}onjjTWauWX}dRh_kBhuwPB^luhu=5;YC#;wSpJ*rM9WOfcoy z5aAW#M9xZR*;<|Va+9z3vq&9=@FO3Tkn zQ6O4=mvi{`6DW<{86F2e+c@_{`kd;#HL|fJ;m@_z4S?$IFkyD__q3a2wrBIV`g6;h z+lEi^BMl~d4g2n~s*=7BQj*V?;ZJLuTWq4B?C0!M-KvIcN@!E{da8U#?QrU7YJO^k zpZj9H89bDAoTYHK-lMY80N%*DJyE^v3NWxPOeKI8DoWQxk=N~Q6jmi(xx#GQ$?G67 zT6KevkLDyIkf4hBUgrwmY%}}BZ*+hpF4h3JZGP&;CKk{`T~^X0+(th7)b5XqE4Rdu zdu;5lqZo0o_9*hi_CpCOx9R9Y=(Sz@D@C+iP<(L;NJtX@>>tXS@{t&W~W5$G>J&u8u4zQEX6lXoXGq$&M0kbT7`s z$JQig)APu>wb;F&OIIaFFGgE8SG76Hd7-&4orI}rH&v1(=~fX|Z+dYy?9kO;%hcaR z5>^{DZ%EZT-H!>Qk-_GP`G=SNLY1MOGURZBwV?P zm3L*2$R=N^vc;;;k~Ytt{?Q>lj}Yq|jMr$S#=3J8x1&iTq(xMS2u5iqhgJ;?pI-ZX z?LjTEz8M%ku8p~J^qux{AX^P_;M>4A=~+3hxT<}o8%)p4PSIca1mEBjj)vfE26a6U zWA}*D6mi-ZjacBv1*Qb1fPY4vfSZ4vi5ccZ{;_6Iiw;m)KDhkK5vDySn`8aGSxD?; zY~L=BA3yGSel&d~dkexd;O_ZXPQ4%U#?HW|umq>*kjo-H*$>cWCO%TG z8Xedre-mk@to3ZrB{`K=rF7c0kkA%_NZ)H#{EQ&0FSFmkT*0~Au+f!c{}A_8xW`#X z(p%eF)>*|^^ml!AGnJAqKvPLi?JIMHIK`lN$WEXl%l3n(pk3;m!x6B2B!T9NXdH7s z&ZXA;Iwmd+5n4XxZS~Ce@6juVHpquLCDeOfd}q&&PQb3`sP%QX;F-E)M@}K;;dU0` zvUi_*_Np*~8x4qZJ@KnQtEege+@>p?t`{v+y^m?wS@K#BGv?z1LVsO~}gm@4s{TF7D6wvIWP4hYTd1ToR^!etxyh zRP9~Nn=N*p_O^0c$B99?*uK;nh*)>vJ{}FjAGvLddUm2NXoRCKQW>9{0nBiKm|{xO>)q(oT8y8|-NrqP zUJIMep3aQjiy&;~0`0G`g`+o*0WFfGn(>0tD7$dYNfoOw2&% zJB^z-A2is7^|k1PpQat21b?3n6-B)F@N}&T<=o#gnDzha$ujC=kfV)gWnWE3r?j%3 zO^!I9^0SAS zyYX!xe@lcQY5v{)dR~6z{d!MXyi2E#GvgrU%~8x^b4cOan-+bF$PdKmYag2QxNqrS zM&c$>acIoo=~UTa(b{=YHxkeZK%WU5fRfw6;{qS?*Fm3KcUWV4VRJm;^d2WEuE-2X z0=5!h;3oSK^Lt+q$z<`q+4u&tL{7vih8uw?a5U=)l)w~XnC!woaah6n;duiu_%e!C zZzzsQ>F+;j$0q&qP;o(FG?GOSGCBs$2rF<->Ce6w>T)IHH*BPN;JX;w$%JTWlL_(3 zq^MN>Ux8pR4q(_ne=0EM*BKjf@_~ zo-9)Yh|PiaJE)naZ)m5Co>W}ve*Ky}YcGV+W6+j_4cFEHl@04wAuAVH&SuCHl(8I$ zGQsVL&cyK^>CEU!!o>C-33iNtsO$Jor~4JnaDvfvf~!D;tpqvTTfN^O10nE2Vaz~# z1Nv^0?ikyY7lCe@?il86deG@3dAAj`xl;kMeq?LTsM(=!UH3st)w*Hxne;)^A4~>S zEZdEaJ0?Tus(ZO7)UP>pY`X*L86mDZ#@&GxSBzj>pZ(?z-{UOej7rbuk z?mzZ>-k|YTye;mBJ-(OT_1Md8FK6NID8k8CjwgeQ;RnaWHd5i<*ojn!uJ87}lOWmF z&%#{#eR7Wx5G>#DoHXWL$<|4q&)j=%VDXpbgqH0RP}C#R1&e-fRqLD&Ov?@v_|tk! zBSawjF>QJe{fs4NIIDHdheWtv5Cm==Pq17!7m6ir-FiWB)4H)cV|x$uu3dXUxoFz| z{n!REUyntY5H?1{NcS6JlD!xr0Z%{jzD>Shlhp1rU(tgVJTQB>a{5J=t%bq_AEO~e zZ)iQ7IsMGn;SeIqP&Bk&9N6;ZvmaN^kji5e1Sfk41*GBu z&kR)9(d@K%`#f#f1Ce||V$Sdz$YF%wcNz&r9A1}0DD`?zL5Uup?b8%?zrQT_U_DUJ4s?L99|_+!d_LOm2!fGb zHWKXjav^Z&+i?({w*XO)u?NCC2WfxD<3AtNce~Mtt9S^(=J01QD^cS<0s$9kKbgsd z@OBQ%r0}-rN3haM6ed{7M~nchL_}2yp^3qq^1Q1#Gr;XV#|t<&uP5_u4D|ry+zpOu{FTzqmUQJKQMDbXE-Q zd5^~<_ZIdO_~5~Aotu6S*O&9zs4-A#!OQN=)~fd4%lmXQKOJPR`D&S~Ioj5E?S0ox z=D>F4{9r~KptMj1hy^uUncrm=tT{)V3}8o4Tgg{>wz(;tYc_3_zKUjPaU0IxSM68s z$0iC_W6UF*=X^D?p+dYSz%=u$#uyE$?03z*^)7C*<}4w+t8bN za~s~<>z-M8e?M?bY5IL^yoSM_bM8|jdlu=^q`8(^&EDRGSH!=%F(cUWyqH7}i2s_?&U0sF~u1gDC9TmJy)IM_S)!O{A#!QW*Js&Jtte- zqF|pR1{vn}gfN+2#tn*_LpLwfs_M&ZI3- z-6ToVtBc;~@_m_&?riwd#6dYkfY)M#CC8+21S!iYYqTe}Y}%bi9Onf0Ik$^P zh;}G@o2kZMM$zvrsQAv~wf@?nfOWQKJrHf@drtR`_q@QsMyz$)FiV6m_L5WTwpKO# z(oGaggnpEDIt76N{twW_-=;G|9eP#m!|DV3e#(o+vrAz|Mt5{GC-*d}t!QRv?@5e7 zE*uBpX7xA+{M=LF$sVM=*1Og}8v*imf_!aiE^O+#BQiNr<0C4|W4}1;4P!Nq>4V91 zBx0STW<)L>)CcCDBVyTUZBh0#r?vwZ4#cd+l6$P%W|h zxOb-N%nfdB%XsBXF{{ZK(2XuzU+@UvML|ews7>KTdBuz6c7MlTVFpopZQf50RNaFI z>=gH~JfvK*YBSK6>eaaW#CqO+>D=#L_+&!b~lbz_bC@_wgvV#b9Nxv7@C*|1O zNqd-mf{S(~?;D1XdZ=$Oz(*&FW?!bBFKdP;C)n11twl5+j@%dA<`B1JzqhtMKf1kM z-mgIauN^wtwv|b|?|YDASXPfCJ7_++G5Z~K5qlmxtnA5^f-LXcg4yy=<-~Vhe;ov} zdkb5+#gI(pZkaBsOY;7=KG}VB{oATyKHD;<&UT}DFaEh8TQjQYg+gP@W$kGPIt>r0 z`f$kU?(E0v4rZ@a#f&+Qy%)LQn>a{ao6dJN>$xAbb?)qz#CpKedd$f?=&{~{8uwkF zhQC?t!v(r#V*Ili5(7%SsV1IJij%pR2+k)F9A5ct=uT0T0TpC@W?= zdm93KYd?xk2kdEx#65NzCCAO#ker+Rk*67R17yIh<=Tuo3JCVz6x7OuG6Woaldb=z z3{J((ZsGL{`5FyKm~irE{5zxL{EMG**fF!1zI+hKU>!#msfLeDhS-SS$>1!oU!XCF zM9v(JHGbWvR5?~IGNQipvh@wGT$DHjfVBW`%NZpU3h=?J7?{y8g%Osli<~yXokqf$ zc@$_Dk*uSObz_sD#(6!|81y5mfU~lU{8c9tkE@gqswwR>aDGD`jOnFINyd#-ZibU| zo@8}JEVVDvC`BrFV4BIf_lRPum{JZD$>gY}&cAz$PB0f?&2?o|_*SNkt> zbtZofs93p6W=88Skw1o2>P(AVPZ?EDBa=`V!J8wUYcLc+epi8WDwJKKVP**EUWaab zhrB-{KXeqFucYyP72Z@f8~X?*WeT?8@ROBg`Oi8T0FKoaQH@pL56zhyb((t(1h{cp zI&oTU<;sLYqB(hqx-kbUKGQJK6yi9tgIXCJOJW9w5G!NcyJ;G=8GVr#0=3^MW;6^g zslG&N*A#Vhgyi|{`B!3fGI<)`m1YZx)FL!1jd%k9RnKr!eRMl>z2yR{qXb!sE*$$2 zv4*(zhRK}MV_79B#bw(b7UguK^%GzqQ+N_U@c+D4E@5up45t7!)*zjhP>HO&A{VMS zYwnMfDkcdL+1yI2PQ)=hwV7og%_wr#$FVjf1hf~A(?!GbqAKNA5<=*4xIb3#$U7@0)qnQ@TE{qR9NZJgk1?edcjSsF zRWTL+OtB;u+aQzr_o>2~j&687l5$R8NzG~IR;T5SL0pB0=l7J7Mv;>3KeYxLe13IP zXw^gju9#-N@t@+6$a0?j^_M9io)+^r$|=JQ>YMt)6^-N?+BImz6lDFGK@#s9Nxg+e zAx@z}FCA>tt;d)arEvk9(OlN=yUOw{prY(|l z>;G~|`suU~P-J&%_dt)vJ=TEfm-y;J7IJm-5?hY6xYf)gL-Ag ziKsk=JQjb39S9m6^sxFl7oQAs@8)a!`BHfCD+$L2r%4%Q8xyit3cop3E=M zC@Ke%TLWpz>P6>pbpfh!u22QP_F>_K?3qs0UL?YaFVg-yWbSmqskq@OhR)oIzqe={ z1kUAZ9B^co%TI0{B;bz8yT2iGeyu>ohX&@)+%}MgGNm=i5k{+E(r=-#egp)LjT0k2 zRY38XOC(z+mzpIXb1AUW3Oy2_#RSSYUCb#OVa8AxVMneY!fA^&1l%uVl$W=cA3=>K zx`eB%0+4epYtu-|A1fy>b#a>D1)%hN0=O+hy?T`Z1Z#l*C)e)CU~IXqVsnz%>hwaq zM$remR@5tUolx1iGDycp{m+$yQT@C}V`9usgVtk00qviKZ6F?eeT-lMy)oaO{;PhN zSDs3zHp)*)v}(K_N(;Dh?pOJhC%pd=?X!(xa!arGFF*pEXfDMcU{OKxEJs-R5xUYW zj;MPny4-@xWEGTt0LO4jMN#`6sjfK;v%LA_hmK=z|G(iVD!**?RDgr<0kskc$W%-V4O@0o^(B(p4Fy<>8`z8R%gLelg{x6v{&=z)lThr9k)Iy|?*wJ zTG!h}Cb#wgs$+Kg|L0*HJ!t0;Eaf~|mrq9*tr^lMBz$1-?-;cYDa_E7l_Csa7hBn9 z+_UFQfK#Emv4bNf?!3RLaL|5dPJ2V&tA9iq0+5efZ(Lv)tCIfby75o->7$ssZm5bg zAICQA%F!-;NG<^1aRXQ3TRI>N(MQ$ zyQJpvXQKB&J<(o$(q4GlS$eJKl~XChkzG{gIY1|A=ax0hY70$D5+r?9cWa?JQ)J;R z?BcM=3K&lfB6us3VNGs2Co!50rC8w&}s zb0=GOXs=`f1@`AD-9vEEguDd<=e{dfvSr#SDHBhvN0!^POdXCzDP)NS@9h|C6wzEM z?}x@0tpnjo=8n0D>{{~qTyxfpJ#<3n&QbZC>uA3AIgNypNws6vUd)+@KAg0t4AW-B zXXu9A-1f5a%}9NLC`A}TyB2_E3Bp+or;U@MEXtFm0N)V?pWyt9=%@A|Cl+X;7eVoc z6F~zJ<#@A|9pP9aIt{h^mj+N~F^ZzbW$;yW-vG`u&ckD-P|Mtb zBW|lx9St8N$59{pC>ea!vrEX6F1lc>3YWSa4$a6M?C_CTfs)2SJ=D;^-CP6-qhy$1 zB~1Uq+;5mp-er(56k_-TMWr|7?=g>^`bn^cPw>+zicr+L@es=cRGK_I4TGI32QFj( zV$>`-A#^&lSr8NDeVqJYifkYx?dXZtGR#bio=V*!Lsk*$(@pj`Fa5KT&g<;dNKVvW z;>w~zGf6vf``Y8hE_J%*n8bVp-vOc1E_PaFf0U8bp$)A?*0Do;g*J49iJK!#=9P*1 z(}R!P`9@n4vS(u=D_@=zG1TU3P--|S7s2?~)07U2PObPS|Krnwj-g22u)<^yk5MZP zz64mE#u83V+DU{pI&uw*4oYI7O=Gfi^yTE=lg7sa^Vr~aFf?=-KEw{^>Sz@5D_IOr z{)W-Kd(0xyS5!l5$*H#Qty6Fj^!(HQ`-GArC7igQPFGTK1}Cc-DBkwqqZhk$=?HlR z_V1xWY%6cPqa=-!PuyhdRp=To*i~ED_g$17rkCb76Tuo$;QxVz5GwdERB%2HYC#sP z3Z2_&AN(*fYZ_#HPuTgFr~-NJgM1CB-J>5>C=;+^{*|^e=0US>n^v?I&_AN&2#ZW7 zPfr@vn16_sH+;1CxcoXaP8t{a%rw@U{^O?@qrkbnl2by5vxb)cnO#Ej_eve}JrQ~^ zK-aZ5qe$m${qiBqCtGpE@Nq*t@1wm)?;*yMaW^zK*>m+21TIn2+R^S7JhXA-@SnM< z{Ga%#U=7*-7we>LifiUjtfOs+L+#!SwL1o;q% zLx@EAnAfN1$->ODPbc2Jzz)v_&wkQ+S9?fF4IW#$Nxpa2(OcfQUElyxaE1h_3u(1F8&br z!}W_Ut0q8AlYlX%RUkskFV4wS_YJuy=FvpWHR-!W;b@A(td8>;21Jx8c6@FVu=b;^ zh?B%#geacI;+7%OVOgQAeii#iGI11L%<+^1hoT12Qr24_qQJc@M5kqH@7KKwHE}dH zVQdw{gNLG3D;h?h=Gb6CWK^UqGK$f65`Cpoay;fglE_g;gH7Tzjnnuz@W?2cAtB`y zg=+YrC_DEZ$fBlsn7YUSU`Zd|fze5mr`I+?mc>2^gw`_tKW*BAZvE#>doTjKIH)34 zWq8(0%PMJOhGo{f+n)^5oaBt5`b0mX%CXZWqH`O_wpv#6q6fnowoIQB3fJX@+_X9# z9PBB^B=GF!f*104za0Rz^bt9QtW6af=XaQxkZ^~`h%XG3kbLe}2cRnb-(VF%i@JqG zyXqL6*LUpt^dCVz-!grTPQk@bN%4A}ad5Ttbo7*V37TmPD zn{VV1c(LnX%Jb{>G*SSUD!2_CH3KD*hFTlSQ@;nyE!rE3BP4(}NElF}%FB=A#K`1nD+;;^z9=t8D*b8M zhKoxp$)WXwcV!!+VADP%Vh=ZzT1$<9zKTsCPm1=V8`1ZQ&}C@E7#XbJIu5@%1Z>4x zL9J48RiJ}fk=9~m%j=5tC8gv0L#WgflZyjvx>D{3!blzk_GJzQuehM^jBh0 zSmNTJJTvPDp`(ChKOzB%cl&+9wpDs!D#o%I!`+Ce2sYu?B3o#CdQ_DxLusT&4tGWlzvnU zNl5rP7R~sVoCAT;y{>chn6Zo@CUuFYYe(Z#AXJ?W0e-#Y9xb1feX;5MY~u<}6y$%~ zXkI9aDMjmEL1U3)t2Z9DwxzY?i#iz$vTCV-c~v*Sw`V>|Jv`c|=dm$Vg$WC0dPdRO zWqmyBKZB9rBlzS)iGb_>Ar6PA&9`_RO2(laF z88TT|wPZYX6rf5GL6w?YTRlhm*en(Y0`5s^7BXlMFl07Z%;Jo(E7(~9BdF%7;^DGt z)Kn#CV+l!_MAVnPw6jYe;OI|f%B{W%{P1 z-1+gjygo_WUQt_RO;vYhS(TuSmT+lVc_E>)+OqV#Y83Jd7jO-&&e;tSemA^392_O{3ee9_tIU^tSuV zgexPO+>X?ibWw5=wfwYx-a19MgX{CK!aeSXW-%Bj@~+E006bVRne|4iHe*qEu79+H z(K(tl*rW00=aZDPmkDVl6$S#gL)G*}mNp}Y4UE5Iw`4TQSOUyou!eainaP$CmzSj;b}&B z#z_t9FS=C7B zw;VPTl9LpKjc=0XSifLX8Ik)(SlCwN33%&y5`$8-?Zng-L>im;SA@E2z5y-L{hj!K zCZn;pC`{-x3c1qtZsWtpRxL6x4@(ag4IWsGC)}|mskJp!v~;zVv{be?IGRl-HPd3R z4$GZrv7w*eLqN^#%_$bz-nq4`EInOKXhltR_d}M^(VQRT$5agY`RXH7{+B(_kdW5R z{g)|M>`HrmQ`S*U(hUoeF!&;$9@NM4bja{k*i+fw@@^LQjxh-z4VvWt{j4ATDe91c z{ts-kSSD0O6Yct{#`eaiK;}w;tQ!y`yP4e(>g}%~lYbB^b92E1@e)w zVP`{)@XStD>oXzb`WFNaga|-OTLD@rK}-a&rH8I60AI)k_J~mf;8Vv|SVLIfnh) zIPuN70Z?DmvCaChUd6w*Oy;aWm&d;br*`$IkyDbH)NqjPw1vYq=-%zqUEPLM($>*d zMbz(T1qYpZqZJ2)$ZM&?=|BY=INHUcrWn%J(q7ZrV4VCV?&&Emo47T(-uY0yWbW&? z9-LSlP5Qp^rRM#N`bz6Go=vgAR>p&$BV#7o%)Ls z3s$%0dniJ?9^ntC+|#C=>ZPwI(i6DugIY$_RO)~1N5fl8#BOS|M|#qjfUWT_^b}fr z9SKB~;s!GbbQM%c#h-|OkruO|{F_EKLcs|&#CMX;{(!8~%ks-9mzMX#;ol)j00NmK z25*`tIKxtzqcZ}+x3G#;9MG2F3Cj?v%$7c2Qv@^#oe*@luI8dj&wZfA_=nXzkVYRy zY6?sb^%lAM_T!>)OgTk4EkrE8Z83oijN2g&U4#`()+h-7?{{vV5S}|{JpYzw7!J&U z4?Fk+ICJ|`)J;chy?Dros^n*!tlicQ!=p6xzv^ha+X029#eVC&KraJ967 z7qh>Gv&Qf{t(M=utT9*9L@?W0^ET8y?dS6Cga8yebpcw< zcI)*$_pH;G+HS+W)A~Ywef3g~j?1Uz?t-|-K0EkP)yLVbwtc`um2Vn`-4oq|JyAF$ zQnt7JV+VBlfKGs&OKcSPQuCkFMDLBc-L9stJ-iXw7uj7Ds}!yY!WJ353k_4$Q2D4F z3VRnDHl}cVNM{veM@mcL{e2o;08sVShtSjM_UY^{tPWOdiRJZnd;GkBt;@cDY77z-u&1L<1Q&F z`ugEZQIY?aa?*5;EYi=;fseR;Dtm|vL)yTv4VFgD)%Mjn%B6BXmUk!C!wM&mo#Op3 zuC9Ae?bPU54cCz4n2Qp5Qa>etscU_^m-BR>bn2#>igI_-CGC}`wSeCrch&DF%=`!W z8)RWRiBdZ4Mt8Q@-qbBA?{#**;&PM6&Y((qCiM0C^S*oDzaNny)?K=)%pNvLmx!w>k!W)v!%A69Y<)t)Hw4M;CCMKQq-C zPhV+HxQDr!bZ09OnDlN-4+-4xuzPxNpHJdvFgIL+pB1AnOTJbVIVxAQP;khdT+5>{ z#)G_5gXL41k{T@$56a>w54?SSG;rB?ogejIUxkx~f^qNR)Pfk%-Lqw37M z+RF#Iqc=zjJ4wzCB7@C^4sZAAS$Tb=7+^Oc-Pz90fH1U%E=$Md{*GEMLmXN@t+eD} z-vmOcixkToS6ajCeqYI1cOxZbI?=(V%~Rc3^{U3AVbDDh0)eIJ=MhLaNkoBo^nRhc z9ptAY%pe@jQspgcbJr1W7~$m>YH?u^(r7PQ(WRU}id%k54aFrB(Jz5l;V~ z8oA6_r?-}oiVQ#AI(r%WX(FSu*YC&6^_CU+LMFlJ?+taFn`}FO$5Zrlu zT-|zLHbkB?RnZA4J;t!YqI?y=yug1V8XZkbdmt4;#d)9j{l({4bqVXCEC7-R5eJup zIMEu>=qg~Lg}%ns7b?q1Z3L_Kj2_xJGfpXRg{s8ePzKLlU4MC$YKS!Ed4iXVJKt3s}wfiFjs+1_k2m*g4c=Q49x zJ>J>2)2;e2Fs<*hc$}{P`WECo_0*@%=65(n{4xfyF0Lwba&7bK=IXROesWWgb6k_N zW|q^>@U)r@t|n#f+J4aedD7lszwv&{7=+P+wC8uf5XOdczlktHzwUo9ms;#nAGZ?{ z(n*=GmWw_w*my&ke)RRC-pZWJGBJuzv8EF|#eZ2#tL*Y9S%~<@6LC9x=RqVm@Rc9# zzZHqP1`|PLhyda`92(lrSq4Nl#Bu334f#+SjC5wLT$*v96@**i^)FB*YmZO{+(&(#4=5>i9T6I6ZvS%{wei}M`>5fgV9r`0os=s}`HrtZGC>>)lch|l@ z4zehCOR`*0_Nn~x;Od^!+9yQtRx4twX#GC3c+f(+Nno=NZ^>hj3|!!Caj_c*$-85j zaWXs>70cSBK~KlnlDx!5$jAz->}FZFC|f9omF!v`@bB3B(RaZ^JURe#0-}^c4=}D? zmf{XGdHs?qIN>RA>7);>Liv_`%7!v>z`gD;duBzKe|bJvwNEKpW7y`KK|~G95@)4@AyueEHrRb)$q}fk-c97FoA!O3+%$gN zGsV0j?Aikv$Z6(zKBQIL+}!ykvSi9CY`3|c46LNxOfx@aq&REi1I|H?ZDm%b% z-aY#V+;{*^K28)|&nH9la*w|vP4d&oi;e+5*L-F7`haGKf5qh65U`p#soNQr411wt z2H2OnI!1n=X8+poa6|Lr;@Q5If!-qs;q{eXL9pn3yJ@axS+A%4uO>@N!ybKS{JBds0XYF|jRuvw#d~-OP*E1uCWa3Fl;Npg3=8cX8+mVpc)Xx7Nt^dxoTcCm(V2QKOzSJf-Z;S@sf7pAO=y+(Y-?`JB$Xx*MwifF>! zs`KKc*U5octknNq7?&p-yPIoq=hjobpv&NFI(?SSo_uk>DV*=h0Bz02wGyz)d*}{vT6xj4)DoF(z9cjY`)*WC7Mqm!H*vLarsnNtXw_EHziV+ zGA8bKwJ%^cTh}Jq#vlDSENn`p)cdU2C!5*|)s6U0e58R#-)!12k zXfZJe2|L4Re{i|18>$F>au^$KCaUtE@W?wT_M)vA-Kl-rx7x`4%olNYBWeZ`^7_-_ zmA3&~1}!I3FwS-Dn-D&zJ3o}6!>W$`FtEAf>HWaweqs8?q4W2Wki@81`5gVj1R9?5 z%ZQq_Ok$fv0AN{yd67TlRCBw|$gX&~28PqxpMXHz=K6NH--uN^Hd(|YLN?n!_O1dXTu3d>g#Iln zZjnYcakT9+vg)rd46L)fYzJYmC6f4D@m&<3R`dJNA)0_MC+ry&j@MN+s6^`1ZQOj~ zuP&%a=_9S!>AeN=$5Hrym>oYx6K!{;Dl-l^n`C7KwZ(r+Bt)ubw}%_pw@?U%FG}Dl z_KuCs{TI{sa-Su6Vs8bgU^>$CG80)Ic>$8`M_~vb%=+%olvS6mvjyq?{ynvrQ?Io* zIX@fM8OM;kBGaauO;wZFLnlm)f=o+ARt!ukCoUAVQ&J$O9{qiyT(8xYrb9`H+K-O) z^CA>kI%X_}3t}e$&@hdXp`@}lpevx^>f~Fi{NmK?_Uz2VKQ_T|e#M(fYKCLdJ$N|t(Scgum{n6s&)V2_a6^;7OicnD8FiSsbNGX>+h`aG zha<`-u#^&TlMlXFCE1tgqyUiB-p%VnA!c@zLMQHiNyG_FHgjF^$l9*@=PX1G#Hcm@ zN)%~bZnER`#%2t`q8;o}0$ma&vCdkzHriC^lvunygR_8dOZlS%{+x2(Va+Mx33&MN z+~axjVCl$M-%h&Hqo${AHpGccP&8!)1ibgP?i*u6A-^Sk2Vz*a$K^qi)s;56|LkBb6|m+tRhoML+=XN{tn^n}5T2cDa5tyABs`w888(G;ZnXS+xC|-D zBQ6FO;mj#5i1<#+lAfm6!wt)(YT=ptZ`4u8RahBpI6D{TXK&R1WR@6JmIo-%o!=l{{K$oJDfR9GQ%uR?UkL|2noF5o8nq6jSFa5x)4|t3^e6)y@y3!BTtF)WU?&y@A-eC(JCd->jFlstMO~>r!@;RJ) z(e@^937)}?E6ym@VOt2)Psl1< zOxK{J%l{acg`y2}79TKN0N%{*zGly9-C69~UuQngX0u;0W~>^Og}3XzeX;&(MaO#> z$v-^J1J7I@HiDYv^sa3X{`+M@fCb$qGLtBEN%x4s|`;@)Fmeu zO;&0#jMOtc{;c0xVrITY8Z{%G+hm|0B!MZdr&B#Z8UOj&S_hMACle;r8J=M3i$NMZ zUgZ*rBpOR+^Y~Gui>tfs`R*U4S*Q6A+6=HnmUa}ASEYu?S)m7ge^`2__Ry}-_|K7D zcrYk|2`g7b(H?H*1&7VFDMvC$unU^w`^|bdFM2bQR zd*dkU{Vi$F&WwXu>)oH4-DmU_F$xKh8Zr*!$qX{hyM*+6`-{#Xr+Y6T=Q%I(8g{si zA$P+Gr&!R;q^L3(h7UFiEO^P~GjyU`o~lkkHB7avTLDm={tvC4y7d5Eck&~8z;A6g z8i<8P`xn_^`j{J?bt|&gJ=|#7HVx+;oke$LBR(|ZZn+e}JtQ%Jfmnv>)a$7!yz+LlaqRtH<9RII`r1UmWpXipE6DBD?F*Rw_*viQ0X_8 z1Q!%8D_t#|?pg0!6I@-mrgC}By83eq3omqRsbne>E(eDKD_ZBHwT%=fi?}{*F8l!H=e9BU49rYY&o7K@7 zCT)>A+uOAgPuNinD|R%ih2gj|+6MLXY?<%IKsvw#ob+)4D>4|cB7*^|KLxDT4OqDc zhRo+J*C&vD#~w(I=t6X2dCX5v&^M9PfuL_p5_TXdL679ZNm-}ae)nYmYiS<&;v^df z&%LT-T7UTJpkw8Sn{U2#@$$a27tXveS=U4g{&LAnry5gUym0&NgQs@i*Y({ir=+Id z8}4cNaOvu=ZtUL}_7?cP6B--7yy)H!8tV>BT=DtSPTg4GO1P5`UcTt|&WV%h8-4Tt z(Do+qZB*yl_&H}5&5ShCXy0sWU+l3gTe3ZNEJqW^j-5S(0C9-olqMlz$x^aXxPd}} z0&Q8sR<;&spam+LttK(lK;Ra7Q>X}G4HsHU(tdTz?MJy>h~@ux&Ws#8fm?d-@6Te% z@{H!p`@ZL0p7%Lt0H7ZyNc2zjs>sN~7L5Bc1~d^I`B=+_K~VSSf2^bkhxJ;4=f>-z z7mmJwJ0+wps?xvC!smI!oNI8Pb0rW2H-R4lx`Mgj=AT{&{zVl@G6HyR9lxDNcW0Vv}xYTfltnmMqP4v&oCMNX66C{WI_SpOMe&)BJ8XQ$k8Y(sg%pw5TaT1YC z8URKHeq1FGmfJujEYQ+SKHzTk@|zw`CNP05)5h5Y>9rvg3Q^WMCl`q7Dj^S|4cn>sbu ze*Fq+=ZS&4-<{jjjcwsm0O;S4F41K8L=Brb+K&4wwOFqUqGxuphC2LHMfMz-9^|NKiC2WO67B=^fk;X#AWqF39CM z%ECd4vQtrtqS6I|@fL8lSs?B2_VJzt{=p6Rs@~6zW{+Lh3Tgz|jVy7rAH|Md`TE zhFXO_)W^>dOd>QQnnn!GnT$R>(Mpz8}#eU z>%uYS826v>KaDj;&SX)Hf)SbQ00NHZXoG`g*jkW)NhZ$D0<&Q^)5CHcWuzFuvjjE5 zg5r6r@h5^JY!{|8#Y2K_Rrcf>1EUJfFLY>21K%LKF$eFY2ge|TGat5k} zq4>eEzvwMR%0SW=O=4R5VlWtKnnPN`%64F4SyqFDvKkwpJwMv6cf+x9ghDO2BkqJD zdd}w`QPrac>Wvd`QBwx#HBapR{1%)?PZ@ik+D&f-CeR9Rt@s(!$u7j7G~N$@qF@hn zLM-`e>|iAb0fZ3R_SUx0JM_WgpX)7VUOI32Oq|xZEbB+3|-`A$+-KUF4C2?Xp7^Xp(;9ljl4wzqP^G`m1< z=Tmhi{{B{9i_c;~9W8`0*23a1ZdupnmCTajm3X5ftN4d(@OxeOI4?>3z2s=G4Ik}Y zuV_gnNkn7~I+b&zJ#Ecq#{qF%jW;!tHoV*qHLzQ2yt~tdjF}1CDr*FN zI{i?7_IVer?!PJTo*T^+7WG_~PPLwQ!7>PTB%{&Q%CsjI8=IdUxa7WkXYK&B!&cU5 zcMqJk{JgmwCfC)+meX=(h`lP8*W)MN@L5pZtQ)!iFE)I zsD_<-wqj#FRF|K2K__Rh3#udlv4Tp7m9$VC9D9qzXt4qUins$o3|tfgH6DX~aO}ih ze1zROp6NJArRp3Udx@Bd(;2AIjpL|1JUkHt9?53&dHl1f@d@K>&741O>-VdT0TnAo zPefqW)M}m$yG%Xiv#VBBZ8GqujUDUWPVynYNOUT?^s+ z?LH^TDx3sHbFQtydd6zjbuUjEVFAi=zs9yOShdta0$9NY3{7H%+NgDxUOV?*wRdgT zO%FX={^VEZUYR#NG1R+r%~Nlo%&%VVQ%0|dRR7!mDgULsD-~~M9ixZUMP<@U>KHpl zeUIJ(640dUr!_6*B_vtgc@rJ34u%>-C{&#Xyd@m2P5M|HY3q>AJXR^4OB}JwfOEN-seNq=Q ziIt>xOvs3`p1n0S7W$c!^QX0veNZCMF{^ilKCX0ybWaLlG7&P9 zW6TbGjG3HhCdx8bJgM27Oq}LP<>BE&1lJ-yCu7Z8vshDgNo`V4ox$etl=%E$cX(Ts z!G}0M8RfLfDBV6dJ}ICN5Elmo9bEd08YOObT{7f169t*c0cN5gGf|LveL}~-C%He( z16uVN(I1Mmoa*U_rmR`YIzds?2SfF$oIJbz+`C(q+??s1j}I)mtY>Iw#=2=YKlCp* z%)0#>u||j8IcMgqH@|!9>kDR{8B4$qK0AS~4*T9b^!#t91EQY~X+U2FYJdz2x_(@8 z&TIy;PL>XOpvU92<^k^wh?Vk7tRUiIrCTpn2%f|v2%0BL)yrlqtQw*ril~bsbBnSO zP_stIIN#A*S*iMQbsd!_xU?5ED=t);$bL1LA|WqkN%@*BJ(m9PUGz_BL(F3m6>p=- zqOgNwpYF9tikvg!nd7tBQHOp<`t6)=f?jXHhk1s3B#A`N4>68~I!qf<`4p7RlBP7u zh5t+uby7ry6j3KdR7*`q_OhG5K!s$6x zee#-B&;6##<(BP-#Dvizq37qUjn~zugGXdwxOp6jdlAOh?(PG&SGf~mVFy=r@f3T94Ts|+24(CTQ zX`G<=)g0$&ccAgBaFecK30Tq=id;&g*!zH4G6&6+nTXwt`(I=t;WuRdXl((cU%4I3>RU;X}TO<-o&d}nD&@0%FJr`MI*FiU@h?BDJfzJ`h4|1EaZCZ zx!OpcNSea+#IozxwPZ2|e~{=QNX$P-%s)uXKS&M=uCGv*MzU!gFqCt-!sJ00FiKD$ zx!eTj1t-#;Td=P~)^SItWUUe+4P7cZV@t>PAG-X;nRk!AV4sz4Ufg`nws6ol=a#j< z{N22+{9{Yc*{&LY{^{7lM`yt_S{be`9o+xyl=4E(ZxW@3+S-eH&g;sbD0j!QT#YR_OT+bO5Zx!FlcqCOlE z*FC!GkPi^Oklq+p4JlMK-a4yCqd%!u5}nq0q{`e9@nrVm6Gf7oiR4%jk)&>|Tj%Y_ zxr$CnrJdK9)%~G}^&~mHlAO7m)F(q0r+Z=VjN5vLh88_^?vMAQ%jSMNSywwdcj9T* zF?wkBf;WEy5ZDd?Jd1f7vsIz2%bI7pSqd?bqcOY{C#5Q25W%B`3FHn!jAUp6+z`^E z-akC4n~zmDQ4U8k23n)7>dGnoLetFzMDUO-zn~F9ULZmzL1p4-KHFoFNdNFNHI8LO z-_IcY2}3%L%yK{^3Vie76L1*50%@q%s)V|NC5&6)IP{?oNMsF(tRanMjmI)yTQ0E6 zXi?1`8#sX%999)S35Q1L zhccwx-5$z5)xj7H#yllD0*15!8Qd%$lnx#f@|pX77nV9PT$jt>plsN(@%Pv?`VD#W zsY<%pHeb;Romd=J{5?)WjB%0%kF(M~auOTFYQU*Ksa3S3z&SYf7Qqvp>->{gQL^mL zY7sJBsBk%Sh8}Y|pCi%|B7%7G(4N5DxY5<(nf*17%kmPu1|B&Q{N=A$_7R4UC8KNS zQ#LP7^UIU>ROgGl740De@ z(id5VVtSd9W=3iiQma5IlbFuWVX zW)&tRhiy4eYHMSU5g|lDA0d_OA$$@{Ceg=^76{7)BnayP1Qrnjk)w!FQa%Rbes-)U zhL|2hjE-4rBElb)bJlEuEF;i*+&b(?Xd-{tSR$j`Feu1SmJ1vDLy-wQk@$a>2WAZo z!TorzRvsU52iN zt1FbCLv$63@m)XHFgSK%A8GjjbLFks;;X#%`3bDA@trtbDujQ{c7Qs)l~K|=7-e5+RUM=$T~_PyfqsHL z`N;u(f;IT>02av-wA^HQ!opZAjp<&4s99?FbXZ$L~ zb0+IVxgS4G?(dDSbg57p0yRdrpV&h!J#yRdwAyb0DkTB?1Qxj3r7T95H zs-6yK+tliFEUPTssbd|V09OCF^LuNY%CCAR(AqWxjBW$5Z-bAt=7`;M#O^urIY*os zr$MH~?lU@ABu^ZphM8(G>A2n7sEHsd-BkYjznyu-^r4{{kFFek{n<;uv*5Prv(|Lq z@&KAs{$qJ>JXx-1{%ceItnw@6|MSaNd)r2@jrxx0{s+B57k~}ir}}{?i9xYZq)og9 zfbI16X+(Dm7KYaepf-gx@bs3Q!(`xeuHR=Q+!=g4zE6@e^5i~w5`K9tMiVwwS<@A7 z(%DrnY3!=C_Hk0ssEjifLKe;EMhj%QCl*5FG{21A5S;1IoZF`l4V`~iUv;&Ox|OB6 zrkyxO7j~XIo1*ajO&|LMbs@bKw8A@e`>SfQ`jT`t2!&!(t7)1xP6d3>N;=`zCY;>j zkv0(~Iv!~g`AZW9x=4NB)YPU>NgqvWf6~|S5ggJ-?#Ne}`aa*pzRtmuF81Jr_2OE# zc&XOVY10-zc-XYh<4ng5wUJgsdt?^s3G}0W&!WJB^cu7>url@a^ft6PaCPA7h|BBn zx@#S^?hZ$Xn{~L|d*lX(EH`YDZ;)?jpyUR(g9g<%h2X?nrGof#D#-e*{w95`S(6r9 z5WdZii92t?AORs6H4$z^Q&U^g?<3tKvs3Ze@G(BpiN!85J3@sg>jRo+NoW+A$<>57dX zJXIJ{g}$FJDO@pUQQi6NmtEejNxaqRKD;qM+*m9g>e?mWH?gM@>sK%;Ql4AyWqr!0_JykF;9*-jr_W_(Pyx*0X9fcEEOkkVvl&ft(vTx){N&E$1LP3(}$e@@bjNdzH+^?=u_6Yc8F1Rq^FU zqu-}bp>bqi9+0zYon66eER&TZp2y68a$dL|F((|vmSf|pL7mcfkoQB^IV zuJc#2SL1l*VkGIPkst#`;=zq;eHFhw4vOhx%M~Ud{E^dnGuABim!Wg+Sw8d1#>SQ* z(e0i;tLL6+Lzm5&-`L!I-v;#B=+*t3>gD9TcB%_NvZc=_bOEs7Zznl2$5pdN`amp0 zF$iaACp3}_LXaC7TJ|0Fc@Fu!lKz4zGQd;e$H;y)fLRYd z0KvlyWN06SC=YN`NsyrKgpWYH`1nsMZbipJ>~e@9{o*8&pjq=0D}`h{J(TmJ|rET*rPq9)nHm6 zkckZoW1kXqg%r)3iFHASWehCO8~xD9It^~#YpjQLC`QMbC?^}6VF%L`6P z-NEM}e>ar&V^vXw6IfmMQ(wdX1BjpRO8uEG0AnR1--k>_iPd0{IDUs1BS*6|))VAt zPT&!1Kma0|;*i6PnX-dp9}~}rL;`p`fAo-4`43E#u3ug7RTw;21ba@hbM|PQ95M{D z7GB^DEW;TYL<50@Pp2Y~tcOHzK}?GS;yRIHZ>KAJiO^~J1AT2TS=W)nbf%oULR`Ug z9Q?vbIqnO75_IAu5r_KovGRYE|M(ibx_rekC_=}P^1r~HboLFDa`!bSUclqJ2HwETq3#Dw;G3!> z&}_AzcZ3($TuN`OHpqix$9G#SN&q{jR?)23B#T91CDEc-Vx}Y}fZ2W-2zYLnf5}=r z7K>sETJ%48o9Y0_dg{DH;6}xpCb<_*yJQ)pM?T1=M?NIKXidopP3FYJ_G6^A_2lyM z35l4(%t7(m+Dp%CKQ|Ilz5Zsee@ZOV<#Jl7M^GY>yn0FQ+(@LO%GK$t?g(Xi><&Ak z?qBuP?3qggUhj;WP`9rt+|r!6an9CJ!z zA~;`_`l)^a(W*&OjQ9sj&{9beu|o=)B!$B51VJ!tWHy=*&42)=WC36@at2%nl6|Z= zy!)7~y*-;I8>h4vK$`rXPHP;aLJYP*CxndlLfgw8c)a`^>?|)VBUmo)g|mMNA3O^_ zFjW3)`LA!nr~Bbk2g`4j-@^6mRp0?St&ew&YCJhqqz@Hi{A$u9DJIUuF%0}B^-TfM z6kGuMAp!sjMlJvYhJKDRz)3^a20L-gCx;AS3;vJMUg&fg!P(`#uw2fSJ+QO<96XMe zm%9$aIE)`GPd}$z7=l)49V#yaaO?!i8D^n(00pf3;4cvX48jb$@!i=Bvp(Hrn1^yd z1Q2`z0Q@~l015P{9)s$1Duy9eb-5H)<#~l=#j_-i9fb9&(ISc#m6sGtN*dI5;gc}1 z-=3B}z>7%nR!DMFqeA+7iq`LTW}T5w`Wg~5I75}o5!*QO_Sd&3!`0MxkkM|PGvydS z;9~&b`}AXg0uHc94I1$Xj6(t*fq+LK2-dS44(cq(QFi1I$Vlu~s~wNTo^t3Taa2Yk z<)j=L&G>*Y%uAg*DER)!$jHQj!D=-9w6OtZ7V2bQC%dn7KLr?=11#(WG|cTX0JtCK z0D_%+0q6B8U=Ze1fuaq7VjyCf^zbXg(&6D(hCzBn{`-jZ?#SP<@R^pFm1&8g&e7+z z|Jf}oRxB^SzGB640O107J2j8G6;NQwF1$=1-zy5@EDP`M2=VW3Dkw<`74&!%Di{nZ zR4|!TD2kY(WYbJiFyf8fgN7?b!yuU%vcX zKYtPL2|#uuTMoS7TdHVa*<)78VP$EnBn^)JZa2>>_*JzVjS453IF>3poxmqZ3h)lX z`d!xFllUWw8d%|wBn1HDag%B?DJGj_U`>pbh)7bbX{{rrp$#8of_KnRjcQw+@<(>JQZi4S6#-$TIQNRKFb(kQZc zAg2t1D4UFfUiIVhcgol9gumWNFZ}#x%yIzG*nR-eU+85Z2-0Bt-r5>TQfdarKG|)S zFgNaZs%R3GkS$eoI{8GgW)P-StFb6>ej6_+btS)QwJQEW*swcfD^bMI1+o$7Zj-3J zS_lJsc(22uP?RyP0TLsbkL8nl*oaw-xMti9j=6$$h`@zn9ekdAA ztWhn#Oggn;>i3V&D_>q!Xv|ExsIOzL6R&;1(s&s~fCH>l-5aft#`2(fv)REHA;;0Q zR1(P+geDRUciWuWtJkjuM@ulM01=8J?{K(y-f1N?X|mP;-*&q<4Lteb(P8`<&ICgE z?ke}&(vrpZl|?UantEF(a%0zl?=4yX$cCkTUF{v}&}HA)w(X*C0R)zgeN3&Uehn-j z2;Nh#VmOHZ6Crhu2$3T8ifEGnDYsxWI-N#i$s{^VCh`0l$WdDIfo+YdlzAaFcAefBCn&KT4q_{Q~qC3Sw5s4m?SrLKhPB(;3w?G-~B@i~1 z?l3~27$IKxu-oV_vHIqWY+4>U+*TMUU>9q3w-4ZkkX#rjfIQwkE|*OY6tsWc*?|oM zg#xx%Jjo3blgojDLSXi25uAHtH?2859UwZ@>*2)k3m`eFFMmlwsCr;w-yu2O+e$p)E-v%lb@7JF z*G^q+W_>H0+NUg}XL;Pu7^@m<=52Z7_Hn;xUVN|JIwN}adLO;rSkoG7nufEiJ^<)h z%xd5U8F0QjO|kXb5Vf@iqMG74Hb~jnge)f-ixib8@?4t|r+2+sP>b@Q$aM*iiDYrpgLIWrH;o&Ss3b5`!0)7pMXL&L_l)>*Ts=H~Ij zg&a4lt79I#^~INZrgU__^y2NXf5&&9fA0G?mmj`;J@jw<<~{d(b5r@l^&2l}34}tS z>sVPF#7h_y|l{>fZxGQ$WHNU!L?nNGxZEfzHRrgTs8y?wr z?T4?L%&$V*#T$g^U2E?+Br^LM>#Dvv=K%;t0DxD59e@I^Cjo+uyC4IeglPcNqc4y| zHH3K}SMJ!c1CW{lAOktt0HQx<(I9Nx%|MEDVAcU3z(&C1;?Jn?vSm- zFQV03(hDFxJUaX?-esn8wM3*PJF;U3OzzPC>D+<$W$Z8Cjqua6wCm0}0C?C4gt6g$ zW=RnQ2I32|=%2Z8ZZwx6``ftnD^sUlc}Mx~9eR|3gXj=2f)3SmJA#`y$U`qg!XRwg z#}p|}C>iz42*2(%nG~auA+d8r+qt7qAgkQR;|3gj>E~Bnx9WnO@cGw18oK4G699t6 z=n#B_ZUP>#Ts1pYr&pmpPOsuY2gqs~tI@1L-gH2;9^hGz;84h?UZX=XSpWpk<+z7Z zV+n;)oj!$PVft_e2X5(T;UKPQM-Pukhh@CD6UZ_QwPe4*60=$ONco9bp1`sNEm?sz zPxsc>FU`+Fhn9&`0_t)Ty^^&?Yf|lc9zt_e7l?osaAfz&G;|R#hZhtYmvOWh$kKdW z(KQG&`@(z?8n^)7s?01&ikOrn#qNy4yJc3sls)n?OPJs$1V=hI%JQSz#qWLdG>`A9_J`PT8^E>G^Z$Rm2G z4{)f@k67JV`LtlKOlYj=n>V{$*=&r&w~2dT(%eG>`)8l|;QIGInD=#~p&<}#{>9DL zKid=x)N!^;lt;?F;q=m3J%yH9`Et`y)@&+H7R=o;taLG4p#4OM-Z$IZI+{#=O!jcQzQYA6^Q8VLKWEGFZ8{8fRs0 zT!ToG;=);~Nf4FIPPmf@NE>XRF-Uuz9P$OSISqq~+IO zkBaXxPj!JT=mEdjy)6XYgJW;tdyM0`AXetWC2>ek$2zi_Ez0!jDPG!RG%0C1&Y1^c z)9#G7NMKvuBr1^OrWE6YFrzv)Ho)!Guo`pqtLf z7bV>^{vBpJW5nNK#@}Ja-(kkzVg91;z?BGUr3~74fbs8eSMhfI9d7&`Zu}i?{2lJe z-+}F^yx35U{uR`M7I5#Az%}*>Hl`M=J{_+NV$`;Ms;@Ao_Hp2}YF(1)$BHjk#ReN_;U? zUv9hR8|%x5pTX14f*z!dWZJ=tVXI@x89(moTKN3dTYh8t_UPv=7pKkV=ljl@bM?dN zR0z_4TQMLQZQsa{X}Iq4P$F^V@|!;_zxuA%BHXiUeRuOz>~qFG#?}Br;Or+s4W1dI zs33E4c)n1v+TsIUv4+W&J^RCJHf|_CPxLCm2N|B+FI)->w&it?96Y$~C%<`;6!u5KwIl zO@R*8NXHnOQairifjTCiIkvxiHv_0NYNa03&P1010KBS*uj}$lWLCRC!zq4%*+9;D zNWWS}^GGy>KqSfdV>E>#*b_vsCx~EA5INPG08n0xZlwoMEA=?}77MrteFkExn?kVB z34zmRwc(3fTxnO{MY$>iFPuD|I-8vTGyVKC(Pto}I`BP5k{&O z9F?&Y&60UPoB|RsyC+i&aUL8Jvrd=d_CVC@fr{r25AryDb~gtphNC3WY%d8y)MBPe zzUFx8S4jwx^`&T5Ix>2AU;~CG4(GJ}>)UPZ8b;Ax*f3DgEVvDaI*lhIU#p=L*P1b) zX1@F&keeD~S_%-;RtZ8U)C`*$N3c$x%5?eCm1k}(|D~blj2qFnMqddoSd@Um@`Vkl z>}|`-+43#rJM8E&*4f@Y{pAOS`lqF~ocDg>jK zaDbb4p(wb%k?h@6pK8*xho_m(KnKWy_ta1=mUKE04wN!!M<$c5tF0|16OLpuK_@$k zfb(iAr9@?BrayyR8CaLjP+TpW*qlJy!Y~mIP~O^NQc7Z}n_*TJy+w|rtHIPjxH_7_ z;;;LE;=dyUGwJ43T_Tw(MWe>%+N3HNl_YJfE;Z-OrI#%b=MkM)>KjJV?~K^m4{JnM zEO;Vwl0Znq7^fZ=ye9dAEWI*fYyTqoe|dTq`foJj(9$)-)0^ktp2^;^vfMOT>hREp zwHJS*{Fik11(%>JM}HeUV|F7HE?#osqDz;wH@CH%BJ?wR<;$009jR-SijJhN#M}Kh z%%u$Uw7ElCtw$|mJ_BL!xS9i4ClG*AIOGV2L#(&TTjE&<&$Bc6ejd5_Rvz&zq=1bL zZ4RMrRWMZLrR<;>mcoN@iptxIHqKk+t|9!s8mCL?<>4JX{c7K-;4J(34YO60MzilE+N6CGhJ zt$Hl2FuWrS!+~hE&+YIAJZTT|(41IW5PdKDQIv|-RhRM(=-^}`yYxHwDKdu(r^v5j zs*_}>8R{1aa@OA6kTUuM&v(1idwz)OSWZ|YPq>p7G)zZAU zd37@~G`pJ-x}zCV%~=m`V>_~Dv-rPs)~XSE>*f|A+iL!&e0JMOpRN2upDi89Nr!X) z&~qDT>0KEk%mbM5fL}IPv$#*9>*{~t)k!D#pYiO}Ehk>1~o+4FPvxbi&SDkaw^h zxnwf&k0L{`5*hwcSg_-;&^a}l%VRQc*H;b>>UeON_Emy|bOiTqPYV%W93gZ)6JE<{ zO8(!34hptF`+pNY)~(mV$J%wLgpdEiO27XLV#xSB?pf9HMKMHI{C{<7o|0O|J_o`e z4rz5Ai6*glygd?iL?Y36gpI(~$g0TZ2z4w1W6{M?$VOe!BT?#(D2zsect%IU;n4pg z09kMVip0awSj|5OLrFahC8XyMe_n9N}=_p9yq! z9OzOFj{3BR7D48p3U`fqxNG`H;qK(*`wBttzf;&(3WgKs+N6;9WwUGBr&xdAog5ec z;e666lLzQaKowegaPbC^tA zBPNUSg5n*-E-+cUxOR0dh2piLMiXbJvqkbiGm{5DMT#yq(xIY#aO|VKCQ-3-68=-l zY*FI1(Nw|&k%^NGB~NNAe&k6_Hd>NeTx4^n(yx3F_-ab7WOZBh1;czsj3svIsqQ+~sa9-};mK7lvG@4U9C_2SyP?D1WfKGjXllcz1nw=4?S zmFnQEmIKee_J?`**aXW@+HTyqa!JF2ptCg?IMK9>u+6ilwyA;%^G%`k@Iu-I4ggbQb@JN1K+WHI5H86X$5`vl<@eBx^Lh{qZ{^c^)1wPLH z%Njo8X0A^+&IAae=gz-4(L1MndHKTf!SZWF0WbX*RABpx^qJEJukTt@msn8k zpI4KZ-)3q^)~;xhF9{{mQ-d|<20PBr9D4Khx+SM43 ztZk^<7_3c=jg9>c09ZDdP%HIaf;;*?`8}|zJOm<*s2CJM1Fq~bCa?tnw9N1~)JlDi zU;-Wxpq|0UB4CvmHioSFu>b;$UCbhDl%u$_*uDpHpajYOmxLcrGq8mF!??ed*B>`J zYv)cDKNImGd>z&S%)kN~p9BnSSN-N9kR+P|IACQ<3|Y9qNEW7)nPN%fZ;_rS^Fnht zCyD-916r1U^4Xm>uXlFW^!1;=M9=^HDP>u*Ha(=Fzt2oUe?>%>p8-zQ1VJ=rcEliv zLV|AS5?ES(2KOXTD^;Z*|EbY2a1O2!cr5?1&+lj=%`PXD~cN z&cS^Za!!?g&g}7XQjNw0J|_iJ6VK85m-Mpn`!J8)OTDC@W0eGB3_(f6rZV{)^fI}Q zb}weYQEA{*jR>PpW=l**z9t<{pFe)Q1po?xYE_~tClI&_x=u&uCSiLb+yuHb3r@rh zAO;gBE%;OeoF*82+WLoTt*Y_$o+Y*ASi)ikRY(4 zqy!CqNCo}iX$;WsvJ}aNOAg$*F$+bT=$HJZs1&ut5&;d87L)+z^f(a;7)s8#bYvtu zP)N(ty9bZ9jkIZ8QT%8e@+kr5Wf>G;ButMpZFDjU&^G|oACpSEv-I+se=EQGjSDyb zVb67MogplK_D5sor`LaTB?OmWdi;SoiywgZ?3sz0n%bI+o9}xj-~3{#1;#$!_}aqm znTB?G|CQ@;RioQ@Aer@%DDz;#yty*ASkUx;Ne9DaPeY-+?XgS z2`2S48-{W?LDgen*o!u@U8Eo?Hk-NF=YlQ~Oi48`N>~b)WJ$IwZ3%5FPeDoWRo*I; zFqW#?u}}(WIsFciiS*jhL((v&ED_PTv-%rNIt3^LK-_yv3wxT3ChU`VQcTWBr4a`0 z%n!c);EnsrZ>?B$(YVkqEzO(Pe~Q?C+Gcdv^X=!CU)X-jPd=gATUsBvL>FOiPtUKe z*jzfZBE);{ZkrLT4j}7jR_7RCdj)pTtvWn7w=qJvRe-#W0Y$r5G!McS)r=WVyu_Dm zz)6b^>EQFyyGMyRyBbQKE7vPSYt~de4*lzs z9tSyTMf1{$I1OS@O<<=}nB;Znk0*GYBE7ugbrSWHy$;qQz2T(Sffnj0aI0nroDQ=s z1})CC6FDcw3@mGM#zT|OKp-44+hWKPPKQyra)y=#Q%k5J;Kf}o*Vu8@* zCAkKnvIi4BDV()B+#Y3#1yX$$XelKrh$z1cFTUBsn-#$)9gz=8?+z433Ob73HZp*7 zPk4HDraj5sg}%LFLqn>0$MW*G;kGrK=X7>{Z>iAzM|9WHN9RqsRBmb>n%P-*eyg&& z^Ytd&cmOj1pqEjHKme>&y_@}Tvmd%Wo|4Vwu-RPSv_abDp?ExIwI*OJs(|M_+icJ# z@q%It(jeqs?pp7nN^tK)0Pm?K4FH=_B(V*V8@a9hh_?_HRpU2J{?s@K7sm|u^YVMfb-P<=5tnyC@ z3iWa&6lM^b!-ZM4OgPYpb`&@SEdR3;U`xAEY`9h>Uwr`t`&i{!=g(EdvIjJkTf zdnSUfqmO`Bl^}3At#}qrXq)M}A`&3y{7gURV(lCXL?a$=Y*M$ZG84;-`5EXC=m3LH z0vS`8%ob&f3_(u}_DT?KYK6yn$Y)pwFDk7?mbG{s9>r3(DFvxi(Zu-&VcV{{Ob2Fx zEVjW2A?OGuB~t9QL1Yto2|pyeBU5V9cV|?ZnmXbZPhF`t9`{Tsb#(emRl$-chpQlr z+1T3CFN|2*t@>jpWRM@gPYTH&#+!i*6m0EsP8yMI?S%rVFb4|q$p=B}Kf%dpSSfZi z7m(H7X`JT_-nO2f(Ui`7@7%e!MccPtF#EcgrM&lV8xMAO&+?*u#^BF)zx=v@c75~m zwd-~+nX&%sJK@u-eln$VYHLHfCGLNzd~C|psV`i&;-DzS_RhWg(YM}QbS!Y_zAHCv zx_$ME+Kj4Qe85!I)OkU5$P!9x~fXmp^|qx5j}xI(*uP`!k3=MeFseeM^00K^f8$Y zL<<9jar)B#ND)@jjS!Y?$=?xa{O+o+s*p7+XFV~qt*hs&YNUMPhHnj#TxIMt!)kO0 z$l$goK>)A9lN3LGWQWl#DJ&H-@!qhEc_@y}aY$i##*5VEYFJ(5@NRCfUTw4%CC+1l zCU2O?Co9Dj}fdblDkJ|Lg9#x5e7FUa;-4^pqRFR$dJcO`jo(aOb9L z*RI>y-+S?@AH%2L_%jyLf*y7LP+d4Yzw^SUAIEZ8x&B+XVoSvm;VwsqKr?6oKbtHQ zyw-D*6#}`j*d(!3$ZLZCSUwpnpC$z~)qMpCwO|RgVhL$>cv3=ETRZl>lcj_alzjzQ zR9*Kk-AJdzARr|}P0-RQ-QC?C(j6k8bSPa?B1pG%r=UoOAl=P9ykETEeeQSv&%K<7 zJ^Solzx7*dubDY>4q*riX;ii8t`-?77bGjF5m)tm(_+mvGeA7h2ijue+umoj)Of`I ztR8oW*08?ZN$vhTvT|1gx;?T(QHI?&I-q|!Jyv}>YV`VS1V1_6&~IaWI9=DjE$ySI zQK)X|M>{BkkVrVIU#wysEYz-lRMroBL^{FEb9*bRI6L%;^jE8g&T##2;qd(6J>Y3k zb*jyEZY5-;ZG#@Fak-priaWJ#4MPKbuTR|qO0z?&@?T+wA9*BUZdu>QzWykOOyM4M zS2nJO$`ZL#+J)`0sO&I9SC|^=>_H@f7H_Sx>frsJs(zxNmIPP7D6J&CLm6AeX)i@r z{f5{c(FD{9Tkd2deUiMEN|WqxiF94v5~J4CD6F2%2>j?O`A~o3*XJ? z5Hd+cPmq;=JqNzK1S?)~cFK-!9e*Q=TbEvcQ_j9c&WxZ#LmsFOm6z_C7`T_gTbvVH zsPG;$)XqgStu%&B4Ysyl4~I*`-Uu6M+#mjK-WU^_7FJt6I>`3$H*=E`Y0lFSSoS)2 z7dTi0oP(C-#opect-zPpofouU4pe4;nq)?RSH=5A0$4pHr=DB(|g`m4>uDtf| z#hrv2ORSb=f>HahT4@MdoV$S=0Yj0=yg=M@3*E#u{+Lew{@KpOCU%dKC-TBb<<+>} zT%V>GSf!9INaIZD2Vx|OUE#P-x7CeI?T{4M7^`UnqcN=Bh9MWqd|S|zI}0+j8`xw` zx3bnHpDkjYK%Pa?X=Sy`ARi^e%Ah%X(SxG+ZfX^&o%6#Sp+gYI_XynIStGJx3I=RA z?Z#@pYOE*2ZzkA24fZ|Ud2Mjv)s2x)lXR;A7(`3Eb;Z{=%Ng?{f zFA@$6XSKK@A6m(1dIK*p&9QCE2mRlmq0Lr zpF3Aj>b?)k6iS$DUtakYW9-Zj+lgR~Tn(>g3_@zi!SQ_>gC;*Qe1^o#Ys#3Ay&W#= zl<8IH^(obn*=IoNt)Z1o$LG;ts+8fT7sKz0Ul2RP?TCl!MQ~_;UbE3tJX;Q(_0%zg zYU141)*0*%Uhk`oWX$t;?JwXRuvckul*Co-v-&ys4$*_14%WimB7vLV+#HLujWz7F zV9vJuhDoNUB>Z_N{^=T3RA+bbb~BGhtHr2|-Fb65#=RCy@-%lc=gBKX>SY^fT@iuf zyA?|gi0M2z-}hfGygZc@VCt=mm`;#+Xf?cnF)Bn`HKplU^<~mCCKC3n35vdRim6>5 z#T!bwL=Wm}Wceaw_vii&ZNf{oOPaL#Nq!Px!df;Rjib?7DGN=Cb3QQ`g)6oC-jPAO zH5CT$3r+_y2#PF=wF`9bP?acmQsvL&N8ZQT+y2NHZkyL~%wSk<>%+Q3AG@765{au9 z9@kRLYV};O8NSC)U)N?90&lR$8|W?ZzFyg>kA#SyzQ|HJBIU4i34Q%hI7qjYsm|}C zjx-Yl(!xWA&Na=~Shgg-rNZVRQiZ#iLxUPH6CnayL*OzWDEfjtzo)}5U`TD9^7iBF z6*NreINiElY+BE|uEMKpX$N%s_OB|L4&(MbyoI83l^J(EIYc$1BZ||p7YX6)WyGs7 zX!fTc2utcx4!IXsS}mIiSreryWm%6Cv=lVM;S7aa@q^)HKZFw)cUQ633HrXpC190v z&X4OU3j4+gO8n$hxxU17S2)=m74kfYZjV27oLF6Gq|kI&yo${JPS$JCamu6h+fvA! zM_!l*Svc_MHTuI{X_Y}~^xTS0$%R2Wx;8pSh}1YrKtb>9x0UXFqlSt6bL4ZQpQRM% zBcA4|W!4#PF5X*c`mVasyo5bD*fo_S;ORdhm7;UUvBZwqA@(Fb;FjO&zgg*Yk74t2 znxDXF>e2QrrZuFf%*LG<`j#QaQDY9NE^_LLZ%+rK&fmaS&6FtjN(rx3)huY{CjDe< z)_P}{+HT;!`r3H6pJ!Jp@P=98723R9mDH6AVNajS?f$;M?Qxt)!pKU_G|nbzq$4|f zfj|`C)YDGHutJtmrwYpa>}#g}HH0CdJTxHWn*BEd&x+5^Sw-t7UxENvTCB7wOGs$s z;wuNV@UBFe^3qZ|qT$ogG_GIf+)u-WbFF&cw8=1#$9eQgyqC4M9fTB(*d#>$`mD|3 z;8`%W+4hQ4jqsW(WV}8tL@)I47KCF-oGKrM%@a0)#D1#s>$4xXgC}Eboqii{4|FJ8 zKW8pisC%%$omo=OzPHjQMpj)n)L9BQ$uukEF)YP_MQ6AvwVfj!gYHVvf*6) zQmo-8?uE_DTC%p!?X)h)6|e;4`Jcs8(XhxItkQNj{IQw|l2oYREqP0mB0Y&(zm4()wGyYV`k;d z=oC@LEnd`n^?QZC%zWjN6$>6Pyj`QS(Qf3LcWYlp=c8h`n}>SBuX~X7uAS>UpOdZs z9ja?d>4CJ}`QDh5{g`9FV*B#W_lrPe%I5InVC;K~KTV9(EEmu}LrTRz`kp8?L#Dl#d;b(v7Z>iQJS+d+6N%q+V>zR7WcEXH1a@+AQUmSNd>j3@ zeiH$n6)NG2%J&z?8cXa>VNM@Bdgcyizc+_fl6}?pSQ^P34aZIuhBE*9Z6J#kHV7oA+KrkN`CM z%Ip=vv+is&*Lm4PdegNr&dNQT*P}9`UsMSdPEhx>bnx|Zm*VIHZOM!DeyjOX5dMpb;`@h^vR_YQ4?d{=Y61z`MCjg#%ybSf zS`!(mHjUT0Y%SFEt3wUGlShw|GjlStvOUx5QhPTlW*7Wx0>*xA?PK2}M$n+GZw1CF zQ9u4o?)F}wL*@9hDuWbNO3!EJ`>(a~0ZOMa0=9SVz59JTC!Xr2j4mXxs|V(6juM~J zC_MN`p1gG5i0gUD^r1KJl5e}w|1#=w!Mm_?qi@xVyHe~S3}u+QdwSd?FOYeP}}tzAs6K`=l_( z>g3FpMub!2Da)_iM5td%5XP_Q;W@(n>(-U=xP@(FBZab?g__?y2f|I*cbGHo#UXr_1RQhBalYo3p z2ezNFOO~M?1`z`T)go~Y)g9+^2`94|h*EB}3PuD}lFJvlD%V;$4DwcSsic=6PUR|m z0JvGEyDj~1lQHuX2BhU=Hes}0i!HZcW1@BXUt4fthv zA)U$Mw?751gztZ6W=HSxz?9Cto0us$?h|5z$=kZmaaglF&q9E_L8QRCr=aGLCn%Xm zFYShexo5xstyE%y!S1G;m-%#)wTH(4Ae#txH*o9F=|dXlPjl|`j;V(heteuuelv-> zb+GG)X=GAzQSM4u{wb@iCPPpWUaz5r8UnL~ydia!jEXfDG3qc%p{)GkAAkCfQI&tB z2ES#2o1>cS%<@{0H-y=Gh=1LFS3T>9i&z)I?w?baX^pl~Q??bf9+XRO{;1n7cJ!eJjgwN9#b?Dc<-JoIux}iS@t>+wk@}8|2b| zqmy-Y+q!klgksyUyuYC!P-~f4;c`3tdUk1Q`t9DER_8Gut5}0doBiRs;SHCqJ0@Bs zzxUc-eZ?$eIaE}MchcG}FZK|Am1Wo@J9J{=C+wV|ua?b^&M59ad5y^4eWP5un0c%M zDYd{I?_i1bdCop;t-Q`lQIgC>f`FSa=bHyH^mv}r7KI!X97I{mJ_k&oVS zn6r8ZRLGc&Y*4fRGLeqL-rwG#eDgy$OL9b?SMYp)*dlmj^9s)cHuk5ns;PQV;D#%4 zl!N=_x6zyP_Owg>&+cbsue)FO9jc+lG&6teZA9HUhK3WP zDMH;11Ljl1bYeG8B(>oj?-&v{v3#5T-Bi7G$q%p7UwaC5;ZYwr`lsVL{jtqxb;guE z+qYhzcxp65&#A0pG5crFvk#}uhsNG#)80+oVWgv%IZ|}>%_Mpab$Fg%`xNoaI zItbr(l_v|qu%H-Kw3^;A+rhx0M<#!Oxyy@FE~CDfN-h|^E@cH;(4(*%oaB-e)2zz7 zA+DbEj8zcQEzm^@kGAvQJ~rNdI3aj}HP2%NFFQ;9DpJ-$|L)w@L9+XYkK3lxj`i=~ zI)p5zOl$A0p1fF6Z~vMn_|@aj`LxJtK;Tr|%{imh3-7X46>lDqoBoS$P*VbDhpJJ8~^HjUzNxh?f4e@}wMH^GJa8@oQCEedOseh=?$o*ZrH=@S4x zz06U_pL>AD##_zRHeqbwH=1k`UXO5fedT|t!*?`Uy$tDm=cBc{p%t&3Na9OxvZ~uR z+QhY1vSv?JlRV{|*Umg)klj38f$ z5?Y?$Yu<51u=y;5SuE`=_bA*(YhiEA;A?Frv&kesCPdCCH6~{W9KkRxW)Ui{`NU9c z2we=UIibCLFS#4ihsrHUHvqkd8EwAWyovibl9rBN^HJ+M-mren$-%m`*~8;IxV)m{ zT6WXZesw)}U$W2s&B5iV)oZ*1oin8TkM)R-KRg5k%gXJ>R502?KPH~0+BULk1+uX0 z7>=o+4<0o$NM@)Mj%XBE2<3AUDI%VN>kveCyW5dHm60ra=;e3E-@b*824&$Eln)Xz z7P%MIy)Y+n4>6QmbG6Da!JbznwL;Wd3l$E2Nw+GqNb@SBX8kad*@mh7tE}vnfjZ>J zN^BdCpJrIPSKW)x9E-EmIN@xDx~J+!eOWs2CL_}6bTyuti+i8d0vop-t6LA-q$N%2 ztm~ckv0mXJDvhh>sUem`;7G*uDN7?|A}70k%YKEUZaKjz6BSXMVGb zGC|~n7gs!pMw9Z%d(w$k1MakmZFI3-VRD={Wfws(__BRfSCs4;YeO(>#VzQ#S_p}y zK69HK5x+y~-+W!|dO2AQW<#W@b2J@T5FzT(SSdf0)Rw>}sL5g!m-^tnpMr))uJ!QY z7Zc8IN`!@5s_MdlIX9o_=va6MCnz zO@ca{>GT$xD>dvrxmL!6Z}T_V9gSS4bgxhJ6g9g&L}B+=8UJwRad)B(rC9w z_>-%>D#O006b&J5?;+_rTwK;m(!xS>?!z^Pu~NQEX$0{vDb&w&mJtoBx%P*%;f~S3 zb2d@?1raPU5gd;$CGiW~uP!tQV&K?4OdjX-|bWx7nN^0z~ zC)i>u0_H#V-+Eb*=E|o?_RkHozo+4UIPA8gAy8&LQc0@UKeKx-S%i!6@t~Ayrh)4F zn^Q|gSz9|?iFO;b=)MUi-IO;H9S;a^VpsoQSvZ=zxw~4JI6UT@Ol`5?d=L;P=rJcK z$f54zYysksa&mN+uy8YTwQ+WLa>e@VT+zhgu}1Xqk4@dg+R4ELsIWJ&aszR3V{wSO znOQixgSa?3Il&NKUT$72j>i!m%eXmVU?_x}501s5=5FDj2{eL$A$;6iyg(yy6K5F< z8!Kyf5S$wh=7e&>fkIh#6MGvoQAaC#3lQf&rWX@)@&@U#L;3i?Fit)mUJyGs7Zl9P z&B?{QApp31ybuV8n-8SN1LXk&7{hoBfZiazzm)*x zsC)=H~qez92v`fSLgF0mSD0Cn$f{^78P3ftACd zz$pLf3s@GI561gf*}v8TPy)sY%m=6p1cAT+b^k>Hz&IfAzZCQD-ha;m2gc!g90tM% z1#|titiLNE5D1tDSpTCrJRbk%6~Hpv!;g=#hhbU^wq1cK|WKoWSY;ZUPNJdN592 zFfT6*&>9@z+@tCNJ^=dp%kBa70A=t;HShom;pKYN-y^H|0Dl8$mzxLh8N6IPAU=SH zk26C+5MU<0zZePS;Rcus13>$KZO04e2G;iIgZKcJ{*?n(2~b*(8v?MAi}Rm|aRZqC zWljbF!2n7BuKjn-qj3L}o{t9@3s3>~e?#Ff{QrO20%Z6Xf`FC&zhU=xJI?=#?ceqP zvkoBtM)_}t^54q-o38#_85cJ|D?VTYfB>$J4=}>VO$5aEZw+#B{@dmJe>CL;Tqg|R z=HnIuQ0E`+6Amn$^HFC&B|zMNa1mhszv=;C0LK4CCP1k$Zr~rFIk2;FaXy+IA7B!C zfY$;8$^^`n4=~Mtunq9Ed_WTzpvV8Qnip_(|1d*7&VRWn2p3QWeOxBsBmocfnBxTT z0#29<_BiB!sR!1^3lI(lcmck@Y#$Do02c%TxJhonRQ}=q{w4^NkMp0KD&SK7NkS-q zIUKMG9)Lc;D35*saKM1K_-h}A10E3Y;(!qI^AY#f zC)z28xjw#^2EA*;`t1{*0xg|U_Ncv z6l1AsDE_2YcD8_Vw5Z@4_k4 zZdi-(yhWhbrEU0Nj3eM-`{>UdjT?IdD09X0P5g~X)Rc)}pM8Fk>QBF^3;*_!Rqxw_ z)-sIOLb&aC?;A25+EnB|?S;fBCVURTDM6YE&+))F*Atx$E(f8z`er)SvJRqdFQ?Y) z`Xo5dhI{WA^8QgxVDS}YCT|6vSPWt+aS*|_I-p#IWPK$uU#=A@AzT+KNoi;!nqDr2 zmgp~vsf8?0+bJ){4O1RZuTSRXu*+~`v->)jBF+~?HYvC`PmbxnuRHyMN2(~lCl#z* z9ZiI)7SuoWT=5t(y=Bf7655j(%LQ`fn%*a5%Jvd+G2c|9@;}mD6vX=Q#8=x7IZxgo z1LEV!oa7yGsu|p0C?PbSO4ur5gyfHT+N0v|qZ~M4LUQsSSSXB`gyn#k-Z_faKPax9 zB!wBVj74L7Ruxsw<*GT7j7~&|$c0FSCJt&ue_q|SeWMYc_*&1-QJnR=kmT$eg-SgN z$v)erycbeWDvC>W_1~mmHnRRw2vzy#}=q^POKr^!?_#HfJb&nJjQ?J3N;SZw0>j!Nu;s$Y#osMQf}x%qmGp(4n4Y2zZ`;o-vn z<=AJd55WC)hreWR32yN*VPGJ5qlh3vUSTAS37mZsLzqTIV1IAh2dTIW_=SY9FNtv7 zO>v?pa6f*T_%z5K5mYp){dM~6+pz#P;%`TUnCy8jyL)LnO~jym8iZc%Pad-9+QG;o zwztVTWK~&Lxg3WCA~OaEZe>>WBSuH#<&~chJp(YD_t3xm)cd)J*;-GDP=OvaI1ik9 zU?&Hni>M)V2)w?ZB*CudeZWe*&QDX-L zRQ)g?5f_AUG_7J26kF~05tzm%Hk8kE@uiAPuwE!9M%eVD$x~W(AR)w!V|_898;;au zj>xUX2S>otQ1+_HQ#z=$F|a#O>6n96nL`PgLrJz;)^-A|aE5~^s>w;a2(>YIOjsHM z)&|=C%vW55U=QXJ%jFX4xBt+>eG%3(PM2Cy4T;mkY%s>q#~};FChew5L;YgH@*?rz zhe`CSK5pws7_+QhaJg&_S`0ZJLZ-10XAba)kRaMY5K0{<(({h+aUAMuD$TAZ-U%u^ zPo75(SYf3jsEw*~Ccg@!=)$q;<@%Byn~poIFx)!aJ-n2zmG*uZ4?LB=$zwNa&+pcq zelsfQEJescR3Gy_8Xhh1O`xYe<|5kVv;1x5CEk(Eky4mp+y z@yK`}{}`+O)XU~AtWR3f}54uG9ptxo_wA-WWGgS3{_gr zyywSMiIS#3IAr1O(!JV)uW(WYzjsEPkcr>zKcRj1nVLpU99@MhH-V1Pp3X6nECJ)E ziNLf7uI`h*Xz02IgX0o{T`m&qz1@P`$t!VtNwuhrYUDiz+o%V`juzcH7J$5ScsLBL|`8pb7BawEu}L9@kfom|l0!drzfM z`K-M#*_;WXm39|LDT~3ftGoJ1W#pe+iVPL5+71$Dik29`L+DO8QhI!S7k?W1$ub5s zR9Chwh~MP-tB~{(qQ$V-@j6cAN>M}zng#v?3eoO!;qdH3aE92E;6DS&zirGV_`_O* z95^wBgML*f|9DtR>^#^GIVuJ49W;J?QfDR{UUv4+`7b zqSeOKUS=TRZB!3YB@{(vl|_M;6f#(g&@+l+^Rr;`FBdW#=A^#MK@UwrK_)LWHBQ(3C?Jg&b1|ioJ@+4r+Pbwv#DsD6Nqotk_@piJ` zp@91n1CZd}1+;YNEtB!fh#ybsz3H^B3H%Z`?l2maL3ao~uIYH;lX%Jql0bsNIEl9$ zNx1!*IV9**h*-SzROlSH;D8QcWAZ{7OLQBOBpU8cIY`Hl9RUg}oKi;An6$wVmEcEB zcBP0woMb5pvE3$?Sa(IhNm1+)iIQW=r9|0gzzLz7lqJUzFMN+5PPr|BpQCyGBtj(n zl==gG;Xw{sg#3~yJ5Fj_I6pa^KlT}?+%;)Lr}efTIXIQxxcn6NO&HqtQ*u-?nWYZv zMk?gBLMP)FuM*lK&>E@Gi3+P$kQ$V)DS^w>Pf1=S3`tq1w1q@->Rj(ENv{!avo+us z$ryAKLUUU_4QZ;72tSl!5;G0U0Z7pHCNZspbtW-)?_`WR z+#9nH-3mE!a5rU*M4I(S!(t29II%b7`%GZ&GJPg*^+&@cCtcFeV++lkxn3l`?ex=T zr4B`Pj&{c(?Q-2FKi4l9Ga)EX5RUArmb<1(>9E~)(`HRg?V#M|(MGc?95d!CPZbU& zacv|7zZVYGaBiGN{EqSQ(RRWb6s|EGgMJpqFOOA= z1KF_bNoi-I1r~n{_|S%DgZwqVV{qVm;js4pjSA;@cNE9a*M;=X0t-yk!PKseMEj|4 zJE#wH+{yRDA`7~mQ}yqkek<&DhVQ<6(ruKh#*cE*;kb>zfDPQqV_Dc+9XKD_KptoG zN5Jc%*+%xpIG3aC=ys)uWGcinCc9PerRCpnh2I+Z($WMwZu@%^e@I!3>|3_k7V@V0 zkhmBM_SW>pJMVCtd|gfUA$2j7X&H6fz?;zU-C}tAz{b*w#kRpEddss`>|cjvr%wbK zQgN&aS;{1&tqx_0=!7OyRc zXHu9Kv2{{dMx^SL-w#uE+v5_80Z1r=fz!|gAc@7;hF(5NoV0T$Q~?sK2_T{5nzTC< zQkALd!gIZ%SCy;kQj%e8#j;9}^F{Sd)b&MuC&Si?^lVbLDkw}|)S5~-$9FkMMx%jS zo!|ur8*UOY*%dbS@rkRyq0-x*8^fc6?sGi$nz8fdkl#Mpn)#Y6e43r(#Ihh}F&90B zsY;yir4`TS%-E@mbGL)Fg7}A7YV>YCJigJ_EGI2DPYV}< zUDYyML1@|Pv#9T{`8{*5J7v(KuA2~>F^r)-k}jby6)$%VmlhhT8yOWZ1?wE^n;V!N zZyO8ccNSeeiN4|93-_mmC})*Mv#lRAZGH=%<+bqdGc+jWifiMMPs zU`@@`F5(JPy-f{^w6vS0ad>(j)1r=LUq)P3ekx?bnpOqC*8I&gaYLJ%mU=FUc52zh zj-K)kE-Jqka(g8ENtwZN6G@gLdPDlRBaId0V@-{f3YkAG^y~Y1`{QRv&ZkFg?&3fE zs4cA=QTsmo0|uFSt)Oq6#4+xz^8S^vzIBCzqE;boLkWxG06Tl#$UgBG={Tpd&L|&U zJ8G8C7vF0O?4*E#KX&dWI8xujQyT0d*q*Fq3c7S@g~y(=&O!3!G-cqrcnIUPGH+6r1fTj^>KW8W-7W zl9R;s6PV?= z(n#mQB0z#j=2bk zvQ{dZt?i_Us3_Ll-bjy8wY#NxKalFauo0i;EoWBWA0Bn4q)O@ohJIesAK{59^Za;@fP5}gW zounw3%89c2qSbH`!onD`ZM*HGpeJr3@p;ToQ@zq*2Gaf=4We0N7bB>ZA&NnNXW(-wN(PVGVC_j6zW zfzXogYxK|Z1H@{t%QmoP2Cz&$r$tyonYlSY->BL`GAm(FqHa%=+wVC*2atTZ4~@4an$^yEY*6Li;K`0c?_hLJ@M2KC9#ceyusVRj!lL(|1Q326p}C3cQ5PCk1FB z+u3+&>Z3Obx?BFwE2fM}ygZ5X@=Pnf%oVsSZkD<5E6gj_f9l=l))1im{5re-=g;gX zuN&R6{Xb<_XS!x_kVguP6a`ILyYk87hL~in=pxUUXgkeWfpDK^syWs0b%yn(>%O9X zUIYBxU6en}ojEAanoc*8M(RPND!(crEQucoG(?C=yZF7y?-6U<@Z=cDJa;gtWm^;B+U`+NULP zdiwIT?By*T+r5?GR@Ip8zNv}2nHp8k1a0Qpiw{t9{M+qX^U}b9Nk;iKbAznX%wLpN zf_3tTaimMuM{!@lmECZDCH9!i9m_t24?h^?@9qmzmoGSuE-p@aYEd58b!yzBvkvbF z9~kfDoe`~sE{1B&F^@TpI1n4r+x0>E99oyDr-2sR?K|u3iJX-Id>EToGc8nx!CEA`0%WSboA% zrDj;1H#U^1EZF#cj%!>;Bf0 zhyL4(rGDBiUbim3l`I!Nw{<_A(Gk98S3^Fwz^s!{=|8}GxdW5~N51xT{{gGhk+YGr zii_3+y)3=dr$bX)t?yn!0*&sEkjg;KIc3WqQ6F9qV;Qjus(Ke35|7>&0ta;4&mgh2 z->|m)a!XxpLBFN?vTXHJj1zl6?d43H{wlb!Xg%>=74jOgQ!<*|0n1d9150!MZgh>+ zpxe-c{{2=CzxRpGRpJ-7@V3>MrIUvgzo)NfMbY!NjSfWKd37KiKe6uM{DS%+q@9U?KixDZ|JWIr%zCpg?5pZVDE;M~5fA*K{qMHL)b0QYqBCbejz|~iU zo=D6!qD&Wou}Qa8$7>g-v`mt%(>D+BC+)>cQwQ4@r?0Pt?mh%4JqQpUQ3!@U+%w%2 zRFoQ_Wo4JhP8xEH|ByF8QP{S!y>Zb-z%)f1enu+t<+fhy$4%9Bab@2g%flOL47-g# z`r6-@mKLY$*RSP`>r939d(n)ccTCzNZz5-K5^cM;j5n-mjMw`2TBqeF7f%jI zI6CCcQMK=0_UAnm6MaUBd59fblH3g!sLomjZsf`^ERE4;8s&&2;pI=PIG;3>x@^x;Z8)})&hJ+PY~{#^V+&ei#ztEY9%hSK z8V-Cth5Uv3t=6~0Ck@N~q@EfZ^jyE`dnNZ>&DZbzj?e$v1p0+SbLe*9-T9;&;_oqp zlC6SP_tx4}%x>GwQuo)sIFoB81VpkYW*`KQnW~}9`b+k;b}G^dr_Q`3+Xn!@dnNhf z1u@XomNm}3{|BTR6l!th4t#YM49+Jq7~c$qPFYca#8#(N?_l<3!55B%7ND!tP2m@Q zcFf^R8s)I=-HY<1ghZ|mqm+pyB@;7FekhUv?5-p8Nwh$W>EdT_AgWbBf>r}@A(kpZ z6k|wmS|?@n)g4!q)R0`F036PdilIT?m7Hu+pKd~oV*;ZnCyh;};<&e2Ejkuq)>m)3 z601E7`IA4BFNIUt=8`ox8{m9KXx@CZl;FEESZ`$&H}}P|(fhLUrf1DAMF@(sHV^um zC`vJQEj=ug`_)~Rc7OKRq)7f+S%$tzEVdSp+fp9u=aG$ll?F%>MEZ&@BdX{2p1A0_ zSSKfzS4j=Gz72*3z7O!z0=oJh(>n5gV`k+xo4m_n7dS4JpsBjY={5Fv7dAYVG(39e zX^)w_7OnjlIQB z64LFcNFs$8h?EyyqYBr2@AU2GxVt+pNc*|4>^bLiKYO`qDH#bQF%1-(XNQX1NKgAB zqruxJ(tN^My}?rj&K>$sl9C+@Xe5|_@27nmMLv`k&>%g5BGRF~O}}Gt z%~l`Tr?Wl2$fwKQ| z1Po0XS{0@7n_Lu4EVCs0#L6sI_>5{@QJSYI*C(6lV-r~DTDPOu?1vDf-g>4;;v|{U z=>v--nvXov$~N76nm`%ya%Xs8yIvWaM+#xq3kBg})Z4UnE^&r;7?(tdQL9|E3gpj% z&3$sprw`)kh(akoWM&rQtsaF^P*s-WXsmB;n5M7^rZKzeJ|W$Sj)?f|wB&oCW#-2H zhd%cTiVPz*-?|j)v}5aCB6LYSV1!#LRgbF6XZ$whYCXRwi$WT{5aQmLV%ay~)!3Jz}_% z)~|9ka7?381d~#?undqek}~{mLxQKmPfn1Vg7DKwL3@_2mY9LuP?1kZ2t8%=sh`P# z%Y;p9u}=jH=B#GSpmH|2-DwIh?bLWn`rc!TTgEhhv#{o|wZ30VSjIA{H1m_}`SAlA z_N0swR^!_-YeElCN{SyHW@+G6ZD+;)6-1i1{d?&ZnnAcq%Z%olD+zjjVsX{NZ9SsK zhQ+)hop?*Pb`e}JC;|GL#P_8&9d=$w`#k3W*ZZ{mTyoX?=P&3|Y9okWd=lqBaTVbl zV&U!M!~7&Wkuvg8@!RG5&$X`K%yshX}q@Kfd|um2YdvCS90Z}Y-G0v{ZIXXU?4j(CEuWY%c>nYCJ*+F%k{3`%N8 zUyQjG#xK~vw$dMDnQNIHfm0ARM4&@bpt^R%Nm)+-((0EiY!m~Vz@X1 z9%~7ypLt0V$sFm|&_eALO;#C+FH|NWo^guw))*;}2?tvr^EITrnTZ|9`Tm#U6}E2* z&sZGAhLj1}>Ax~^6kRaI<2#CYx5w#o1S;Uaq9QSuVWg0F@u^A1=9$gvGrus^Wj=*8 z2B>XzkvhFKKO||G{$apX*r!Gxb~WI76)P$POQ7*j&|i9exh%@PwJa);$U!H!yN6qB z^OHSrPcFBbf-VH4BHS05NTc2XUdGoNwCT?Jz>vhsC|`i7=^;CYL22KaOd%&G$((FM zAGu{4r?@tWL3#W_bD4`0N8%Y*U$w)PzW+vTu<-U1y>{1(}?${DScm#D<6`Js?vQcVsd*EE> zU3_@)8Fq#ltUHw%mqI``bu;!xPCjk^)d4#6wgEk9Qe3Pq1#gP2dZi1sfYD91Z%t-DrqqG&~l>6Yu>UCb%&^iN19 z<bFU`1KM_vlcHTdMB|yNFR>VVc)LqQfx%r~9FnnA{aBR)CwPAIj;4@5+0{9Jrq;qr0uW;ta#!wuEsG87WYf-AA#rLe+bGC_&kYZ+Rr zXDlhZh?3W$3%CW=qvLeO7^L$Fv>G5pTqY@=vzVDEGxMIQSxKeJj2J^ej)MI z>FMc)^F<#OPjyb)LQ|vAy#{L=5j1YE^(8N&Qe#|>Ok9L?6a@F78(H|e>D z-u_r6j^QU0vgZ>A?Z#wM=acI?mo4*HYUY&uyk4%ne)A1hJqq8(hdz-1;+51lG9b6j z#;=Ji@~yGMX?$`#yvvmDBVM7|oF;mJhw|ye9C3DeJ?pnz6mcsGzqRV=ZB@)~ zSS@4fCbn>Pjf&awX$~mM8-qzRZS^uR3s_zyzOaB`@*cwx%Xl>c*hJ93lP-6~d=Gq; zn}TO$`D5^@jD+p5zN3tEQY3Go&Qb^66Mf6jcRYxNu;H?44Xk8|L>(a>hM~)nMamVT zV38Mzo_l$p&3!r^zVzR5B^fHMZ~9E-eLo3%#<&mS|2ZQhPw+c`hK)n-+-b!*jqoFh z({Hu#3nK>8RN{I)KT}Ja9*$Zu{i3NS!;FyAvf+NZtj%#||2^>H4$ zlAA21v-*A45lWs|st+Y!QWb%B8@X5=PZ&IN zM`yw5^&-P9Mz6rb+afHjT(d#fB8k!)>La>zC_ewyj?Pa zTfe}(xJ|AbQKXbvIs1r2H0BI?b3xQH&(#pS)Fj>P|tx+(dQlnrbXw&P|5sIMF zOGgTm9_w43C!*D{vQq5q>)m=29v~sTTh9Y=Qam934K2Im%F9-wW#V&Al!L6~CjHcCSWrDol1GSr)cf24CB+ds4wMa? zpxjEVr?ztImAL5$w@<0zSVml5XTS8r?=C>kv1JGr#&=oGETZSHAiaT9SANGnl-& zcNV?BH-r2P{{bL;u8r=X_X7*Ng%&_S@rb8NQ7I)Lfw~a2pjN5_Euu7_pb$mUB+2TO zrOJg$QUw%k0tf8{KOKUB^k}G|CqXkkAEwi5VKL3);Hin`DHl~r#VLxSl$^W*3MR_~ zff&{z92PyxS^8OvGD;QoiiwKpiscGg!6;Y*AW$$ynxcQ9Nhv2A(H%)q+>|_p?n7Rb zLGSOqHF+H|nOv4UL70Gma_M7g40Q#lp%~T(CZf<-coBV3#$0-C#!_~1&LxFhZkKfM zQ8gb%)tOlN!)i@9Gt-#w^ai}^yj#7L*XxSrQ^l+@YE))HvAt+vKo>w3o>PV8c$?3s zuvW`Q=qpIUdonHIr90c;y#EAtr63g5{Oglvk0H8>*-bS9-&tigYZfvNGtR z%z8KRP4j|KNpRwXl9oJgq%_bx?uO^urcOibP1jsA`0j@6iqDs!9K?xtA>VOMW8{Za zHViDhI=j?nwY_-L+U><9UL_M7R_sAKUBOe&Cn+FcgLIsFlQDrBvYJR@&u@MrG$Hg|l9s&wV^# zCqRK$Cpea6Am`7V8;BKqhI!VlMKi^(Uus-auM>BnsbhA`dG;-0z&+PyXD46LD(`t% zJSje!n_tD4lJ5;2FTMr{Dx{Ao8+ADpLc3rsFnZksjX`%U*}#p;sB^G+g&IDr#WRsk z!-sk79`n3aP*YwkK9!aad%al#aJtkz=L&nhMRo_%;pa~#y;9D? zF>a(kix%n%@s6Ae-G$>L-5-vw8T3$V)1pyms(6RJG5gYW?TbpLUXm&BCKDQrl-IvA zZp1`i9y)v`hse|DH zf}Ft&wZn#N=|v&F3K^U#Y_sVF;4(6tcIBcxT^{nO>|{sE1?NxpIRbuvtj`bc4}-Zt zZ+2UL8TT@#JOJW3@oDivRE;tbfv6;+t4fMj3>&np%s;~F^A8&wS(!z)w#{6^WTR4) zfsAOlcw9_gI(ClJ>2O$#MSAgwK1-+56CW;Ix@r!d;mrVKBa;m_XcnpgD8URWZIsp1 z8de87yAxboDJO6Y$AOk*L8o)-0y9LcvglEIG2#VVambcMP(Kr^P$AYR0e~m%Y^9Vc%z`?RNJNizW>Dc7EYNAp>P(C>$jn15(DV&$P*2T3YF_ zht{uOY2V(IHg4yIyhG<{X4p;o?4xrpU1zjsTo4URMjJ00JNCV+I$j!CU?1l7kM}n( z&(5|Dyk-1*i4j8v$EQuYx=Mp4py?>7=I^|2M0;Ltp6i*vyf&x*> z8*Z+MqD+)?xaBpze@IT14;?CZnlBS*PqU)4YbQHBwrHG%OxkwiX-W zRd&q+YdQcXd-74)i;ub8`u>%SzR%8C%+wv?4wcSO8}T=li+7-@lkS}Hz+*&l?M7Fw z+vVv!%9xUGju`*$Ye2vn0Q9B2W2AtAf{76{e3%(Z(*%uJ>UMe>*imYQt_>r^u~@Ul zab$W9Ihnvp6G-?|F(AAFf{x-3D#bsd#ng-)NDn|r4~We~D`Nr!SYUz>)}bs!nDK|q zLro?=s;3nS)euqx`UX9r4=AFF1_h}w-EK50^rRNEKxY$d1SZ`Y3?u@v)CNod3mtzt zMw|L2D|@M)kUQkoyNTA1cenh->&Tgq(LUm3B46B$E;0T6Ej@<_m1fN!N%o|SUrG|k z855}BUcrsyakK&=LC_9wD2+BEAK{-3U;OwAWO!iBwqtUY* zQ&#%$XP2U|KO|_!AwfHbxO~nV<{ptlf{er-LUdG?a^7PELhLzGrKwU{pvTdDQkAz`-Tks; zOz9pJUg~o2!*n6QxR^Y%%bZ6kjI|E87u2*Zo64YVtlti;ZmY_!c3ADU*7fSRdY_t9 z_XStg>f$24I8ZDUw-l#?tFe>Gaz3Gt$eM&*FdSlyovYR7Xn1;8s&8+~tf!x4bh>CH ze{6}kL!r|}tNi2hWSzfh+N{46wC|oWc8xy}Aj&2z_IlkOSMO1xZ2S_h*PZX^Jxa}3 zGh+Po)~W5~<&`(ANc#FlC3X34jY|5zGb+vhxlt)&^X_XP&u53IC#el`tjs5v4FX4H z3#3x*fJMHj57`_{M=IWRG8F?UEB()exu_?_{o)s5LVO*SAtN%OQt@VYmUC=*V6-dS zo8!oA3>IE!ce;o&@eA=O6h&raLxaSp#LwHV$}ed_?CgSU{$!soCp!;vG#lITln>X>MZ-BB9`&a9)r*a5>M=LR?3nY+L2-Ngyg;lt zv!-PI8e)j}o%mR8zF0&{mkb>*zAS!u*S75JsTtUqOGt78Y%^3iwzI7&5 zYoL`h$DS3Jv+Ar)E1_1{xdoQ~fsR!+>6fR^zEQ3|YvHg5w0O*`aHO<3J)`#NLwdR) zS7ez+mxNbFFa@@Wp4|3geW9MTDHJYClBZ_uwA7faZi2x04wOD3lhox9fm;Q;&J5*F zGhaTy94xObZ#U1iud*+$y4$ICVkgw2HiQY6S|4@`oGNVfYbz+oTIgQqCfx4)Xco1D zLR8uTl8M%FsxU>VqQJRaR;{A~*O@p=xT0bpN%|cf)@!XhtZ^%44fKab43V>3B6(3^ zjr{Dn{yfQ^6&d+eSGF%Wf^`*ru;$S4tPCMiwtD!;MHQp+R|T%Ts;;Kyxi!n4ugx@! z^%c#JjGj|*VeuuEE7y*!9sZ27(u?wqB^g$CNqJtrL21<$?7U*sRTaVNfLlDO4d_kw z3V&{qS!Fil-MF@4YguT3>_JJ#$&vJ3;D9EGIDC>G zgh4aI5ETcX*{}gkMHh$<6FTj%+RVv@?8tgc{6#`@5)BdmaZ+47u*u^ouqnUS=>fr7 z=_Bf9Y6e(fpx{$6S{Q@yqV@`#OiBlrK!&wXfZermh*?@a6&H{p6b$F39zGy-qSS{jUjAJE2uT|*o|?-LY*IKqN5h}i^< zgEe-19)q%z1PS>XK1_B=?+7N19AY6IU?K~_M-GG#DMuju{{BTciY-dTbBso6r#N4{ zTahXqKXIiP`9r5-EWsn$#y%tcNcf05frmQ5rVz~Lu;NKZ zuvo&3z;R)Q(M;r7rVAAbDxF57!&_;(Vm=)><^%qOKN8@NLNrkx@cXfA8kUV-*qjQp z1oiG`CQquGX*3qNyj5M|Z9SKC4azSdw$mC*#@ONF3qar#N<=PQ1|*oEQE;g+4yq@W z7zZWBL8;VFG?~bWh$W4LNh#xE)a#8H7h|z0jf<%d7rvUWmhtIFNp|ecM~m>`BV^+3 z=U|7fL0_DYAhrVHeZ(MXA&}tdB$LpEK+q6L#AevT2uMUw{J!o(-4O66P9@4pX*%R1 z1|^>%27TMs){5U}0cf6n6&Scf;0YR4(+o{9J%F&EiM7Gk!x<~F6`*=Z!bFk;L8E~J zt^|f6SsD>15kcd5&Y&HMrkvz%ruT_=v&sUY}BZC6dV$)*& zU=VE4>R`0Gy4vsGNSD~g_}R7mvoe^1QbYO?I<-mKTvi%P1&3L561mZL;vVr?@hSS% zpMsR<%ps7^c9ZYZIC@n8_slVu@(tKuiv1Sq$c%4=YuM`}VX_OL6k7 z017#9j!ao=IAi;O*4H1ZTC%f^Pt5{j78>1@kQDPq;agNit)`+#T-1 zn`hp$x_L3OUffbS?V?C+1Dd^h$?Dbft`Kj+zHgIsf>Kj%&_NblE%=m5)?~664Jw1f zq$SmAl}YJ1MzX`5NNF;Vyh;n|9#xn0wO~=@tJL8YDx_lg9&p-v!VCf?n?E=$mf#O3 z_yaM2RYD)pr!08lz^jL<^@fPv5J98jb>o@@rOIIF(W*^at$L%j#80o~pKUHHRcN1O zpJks#Ch+6{_F=+#?(~jpUVrG6Qj6cbvQPZ}#tps=Xy_xCuNO~Fn#B7iO}Y4@rc2Os zZ-_r4`oMJ`kGHP7t>f+C!pc!vOU25DXlpwj548Z)3ycFw;7Xy`X0n)iG&Yk)WAlo> z0#WBiZU-D^>Ejg7QZ|!@)LTyK*=!qU49l@ZgTWZ~7A3Q@tu(K(@zx%b1v5iw;d}If zQhg6Uz|SWRS49%~NQ8e)9|AteG*}WUkJhw8%lN<6M>Ayn%oq0e~)p zZ9oG57}1MpID-Po=Vhbq_Jo4yvTfV8VO^O8Kq%zr6kr$B4ADy?jyjWi{xE){I$2#- z8uWyMv?mlivuztvZ^H>lXa*q6s9kKY4!t z+r(S80m7q{$RJmP3Y>yo`4y$cJF3ShR@tM{P-^};{xDt_`th-8o=6Wuy_?AR=J|V8 z$2yRem?S2|zoFTeb|4?5Mm=>sc^V2J1Y-ncuvn)sct{guVqJ1_%5d=)nO_$i1Klw# z3t^)om_s>Bdn|=L&ak$Uf0h3raUfx^M)+=sCiW$w2|pfXdb}8Rr}yFejx1oO^Q^@H zHgKt>LeN*ywtzyx`d`^n(ZyFA6Aq*Qg0h3 z`vlBpf>jiN5iZyR2I)hArdNkoe4>#zGNOi8iX5)vIbI||P-?3m z{@}CIVtxoD4pt>F`Be$*@F9QdT1h#*u=G)Ny~a ziE@{hrx$AYIDN`evjcA*yTg}%=S;CloF`6q(MZXdQ6{4Qw9htqjgmlGMn7rLA6~mK zDojEFM_wJD{cA{L7&d6*EMH*aIL#0Mv77PS}C=cb$*v+t(yGJj&EcY)X zZaZ@Lp0T%CF3t{l28|v&Ff;0~6q0nlBuRV4W;~LKN)m@6EhK;K-@z|rk_6HU!HTd} zFgwCpf!Bw%_#eR58flde`9q-)l=%JfHPr{6vx!Rle;mPm43PVvNWzBuu;D&zxDOje z5@5^H>dxx}2v{vPv6ql<0t*%JkdQwoZ)F})uSTLEQ` zLFutFwg8U}jY=IRkB=)r1?H?{x-RK+99PicmO@S$HlNUGa!s5vTvq9^1IKIlFlQ)Y zde-HlT==XgXzsC`viBI$0yTuv8hi)~RBwp*Wr_MBdera7b9llp%T+$V8o|dgVlmV= z7a?q9*Ho}66fZcTedcw`*Si#~Sg3h||v#ra~3n?OoR zR`b0`-FQ(A^ON{};;*|AVNvUbewp+GbAp@>4j3ZnGq@AWNu)nUvKc+LQ>LEOGE>8c zA&cu#p3?R(X+|)aY0W;2ONb_7(dzyv-&y3jynO?gIU&v!r;5|W1(_r=Fea3NXhfha z3yt&&J5euwCjJu*K&F5v1bhv? zbw1MP{|u@$J=#--9%Y(1W#6zpoH&${GDQ7;9QVTlHl$__d^#z0+Kuv@C)4y!Th7lL z*1>Y1&vJ+rkKDF)z4*kgVo?z5>0Yl`yxw9qh4U$bpkBVC^Q3syRcTLIlJJP3t<*I{CAkv- zSxKHyq74ub+<2ho7c0O=63Ce!$bUVb4CF`ix8}c|er#q(An6z4rnP%x8o7rTunR&B~%+_gGnhfZUYk6(niNLXJHs6bEX1kGQ zNhNR8X_Pvx-Poffd+cR-J^81Kdc39l!L;NP{DErhkK_O3KlwC%pekj@t&u*TbR$(_ z^J~}g*g2gRL#e6CmvSPIo=(NMX*r1Kap!o`vBo}Kp%2wf7&1!iS=)H+<;4EvL!rR1 z3IucKBpy#5dslp>y=}UaK}v-QsgWDaF7ss72LC>OsnEQo6e-2Gp4NM`hN_%<>TjRF z_vi(qN58kZ^>mfaBFJXdE1rfMQh_w|gEg*i2S#}SdC$prg{xz<7ipd^(q_n5(RH&zTfXuhRp@boVIoEAChjX&$g?SfE=>G-#{uTH7 zETcS4{(_C@xA>Smy;xKx-4f)iE= zLm5kkrH5lo9LFGlW6o@o)0vGJj?_VRhK1zexRZDC$8@YE!{p!?6_xF9A_|-;s>fmK zQMq+4yvCL_uKOH~gJ|L`eJhdKv{74P>st$*r(Ad$em484@HzUll~~!wqd#6r?5Ejt zw5OjFi-%6Qt`sTz{O*^SpzQTsBaCwD47#2irzt1LY0tbx+SQwv&84-}ity9~YHcRo(ZTy7#Yh z{&Vgve<+@!GYlW}n;Inqj|TKWTNQ*V@dNo(JQLMS#R^4(ek&NrL@Sn3$+Ytw`V&3R z&Axe{7url6Bp{caJW$P<9BTNKmp(S1-j24A3HlUZEXwnf73{C<`ef5_BixF zKRi^vv`?1g(yk(+92hG0_w}mEZv&&=`DjEnPb{_Xh9ZKZ*xTR0r9l^$2Ik??)8qP) zy))zUY`3?LPfz~38OG82_;cd~reN<(;lB%22o3()X|@<#(Y_N^%KzU2EvCKp{CW6A zca)1=$MIXFU;>^fUpY#wJ%$L1jg60v{CZn2z_O8MFq>KkX6oqmV`xkq8E-E8m8 zc{KQ2U{b|)`z=_xr#@<3#5@jd&3}{@;$E?eieCX90u;I<7CE zg4N#Hz|t*Hv>522EQ;6ei-^K?}&nPo{X z1z*awCwpbVOZQiSuJ$IYUS1Y-F4vynDqa1Viar>ugkFnP>;{BBjOTM>964MB`Rf$g z^cC+J(`6|q$U>z(XSKKI>NEK)OrI10Gxd9CW>9)zh8U8G%v=y)_dAh~n_JFcR+3vS z4E^Go3$8eFsB~>_*T~7r$y2pY*FGQhAf5Jq;S_p5`p_qCTX9QQ*C?`{z5ZwGsJo(l zeeIbQIqz^fb$01!?UyxVz5j99)Q_CKZF*vTdaTWyOeOb>e&J|m1P~mq-N!#h-3n#+ zyYlu;VYDfW0%c7v1+bY)6a8h0QOZ@|O)IE$cjlEB`{OU=tHFaMRGR4h0|t#ThZ&E= z=&;f*)^~B-F*OanI~8j?_P&svo%Kw55sC z#EXn%F^tq{T4trY)9UW-Y+z)47`i5&nW?jK^AqvJ#;}-;UE0@n|LgDP9~&Lq-`%}C z-?6=;u%mNe%awy??yQa0&ad58`--1OChh;mt0;pqmyNBs=#{@*eMQgE&TQM}Onz<0 zQs4F6sAT{3d(YZ6HQ9A$TjwR~KKgty+i^ip z|M|K0?o+3r>(mL)2-ofzqCP@+j!+*!vTP$zXb+V|c+ZBiAUy)!cf6bk5DoFXU&=gRJ{9Yr>c ztDrpaS-hJEYvQ*~!o)X$I`!}6l&qAP<7yRn)nF@3S=X3KaI{B5py}E^?Q)IQdK%yA zJjwcc0mFdDXk_C1DbM};zxNWdQ{l+^&cStP15&65POP3hD;kN_Za;Mjey{dD%p!P( zs4R)=6wh_F?}4u@BZ$RW$RdcL81&2#jY%B%<0f9oPbsJVs`g{>msJF+hDjt`3F=Yj zU(v)O`6&f7=L!!5f`XRyKtK<80&`b5%m#g1qfe^mD!hS5S@Ix=CRoUNP!xKc;1r)v zI8JhY#6AB>=X}Zog>EU6r96eE^BtC(p&B}Q%7XK$Aib-hfeM4v zcOhKXDG&HVs+OfZn!m^I@nanty;>N3E9m=hCm3acg0WFEhzkvR8a6uh^?A{?LS1_5 z>-by*p|Ap36l^-T_6+MG=icRh@42;SoO2UN#+yZP_gsQ3giC-doKf!P!-xuJ%xwH< z0$vPckth4IR#nBSsRY6Z!Wli2)$$3BLmsZ;%jr+g?wxs9fAZ<4hM&M5>d7OsQ+sD- z9n9T3gKK$HbR$#cq;{N1K9nitGK7++{r!yp_aCY~vwz>7$G-l7H}(j7PTW{~srI3Z z_U%Q+-4nH&KeOu0pL(<>MmVU6556AD<0c=+IgyX5DngQEA}g0 zdqRms#TS;xEgdmcmghTA$6$20_h{d+RC+NujD}whRfE22RAI-4KpKwYf7b_xuMeTn zNFQq=vyvXwpPrq0xaY~4r=A*qVn%=NxkvB}!Ya<3d{B3EoN+4u32$wX`j$5`-uMDwVtV~dwOfPf9g&|4;8!rX!^?A5}DfP3)!99&iJ=m9DA_3s`%01 zXFpZ@52T#*eCTrMCFh4T{dqOHi3;5Ux)CISpimlr+#rq*>@B#ga zhxO;j|KBs4$uf7;M%H}h@Vg(|Q+oz=eF@o5 zUtas}XTJZ`m(cY{M{&>kS}nJKe0U=|IQ^dfm1yvNcYXq#UgA+pD*F0-fGFB}(G+-! z@hV3(6{&Qv8p4^Tf`9~5H@%^_DfobIHYBgBPER z`+fZ#pQs&sOp$B1|E`Mx%pDAxo1vZ}eQAA$n*O;N3J6XIpia;igAHrS8Pzl^USxT_ z2ytGOH;;OCFJI+&)s&Ixl`B+$^XrddpLu+QB)hS{N5}-%+3V{?3w&T_gB6;Bi$C`I zKi1;th4ao_F*f$8i}`&>qK0-pe*O=Bh7PvpJ9cz-oim13js7Kg@D6eGC+WlZeZ+8D zIj`t6Z|R0nF)hn9EsHmG!8Agy_Zqd#if)AYi2i8LbH&+W-4S(Se!Q0t?ij+(YX2f( zJ(cvjk8_ItrUl7S?VqSP=#@#i@Xso})95^-c(Z9Ok zKR>YHTRRWzKPQW_z5}RmU+vlRwyZ^$q|@#1-+k$8qjKmN-ZiH-O`D#3ApuE9!F0J} zw~jV&hz^K}D)50>3HL{;vf_~F5tT$H8eC5!T1*8i0o=EMQAs7mwEkevbGUDYIWps9 zo^T&=lU9BNX3v?7KeYvr|N^!KK(jLgQ<<{p8 z=C01&m}4O~S{bWnM8+n0jrD z?>;boX(C;_x%S$JU!LB1UXn%;I{(z2U;P&ip}vdGJmW%SpSgM0yOHhL`MW>ax_$Sq z@lET8I*-;WJGO26)u(U!QHYgmzufl+pZkZqKa42xOMiEG&uxFQ>)Q7mMp-8ZCt*9U zfR*q!<^8({FB$ZdJ5ks~(;V8ZU!ot@se>Y-MV(Um$6qOStf&HCs&d`c4u$t+P^Nm7 z7e%}Ztfs~B;?+9RgN6P|F+X0c6h{k{j(EN&zcqhZ{<{1vc{V>*8Lyjqd}X8(eJxoD zx~2}Us^^glvqu`1o^e8whOKd;Uw?3l6x85M9GRHOCKE4aXtr+X+#-fHNbXsBVE+|& z#PSbZap2Be>F)E!FHK}>H`GoNgQqAI5mNvDkuScWdgcti^9p2Nv#s=@9iuC^?;78@ zVW{)f+RMb^fBdhioOtoTEq8qHo)7;=sBZ5M97OH4r!K$RVVY05kk9^?=guZ9%fSG9OO6QaegGz?}c z=}=d?QXCCe;-Q|<))0P)9TV!Tbh+Ah^;-k_6VAXPRV@#?vA=79nOXgZbE5{QZ{Iv( zev>JjAtJ;NTyaM%_3c9kK9leG{FMjp%njXj@tcft1@d3LZFqXe=;W?lD=!Wy6txSJB2r0BgDxyUw5zhySd*P91eBi-x+MW z#z7PDXEcBYV{ous>h#Oi5uQETbF9tgVklPi>&Rp4N?RqL8SX^1T7x2tFsnn`CL>jPm5n_WhsRu;$YIw{QB#*MCuas&;4EgCa`opFVr~NBhSn zWEw?Tf%BsXI`p0IBIbg--Q^503OxU1hd4b+5;1%5-L?fA)S`e zzKR{{uB6AbaXh&lrHR)1gGBL}AG&JmKb&opfBr_zoyxEN{_MgL!~Oj7n?4!4xc2I& z7aHOG`}Ur9^u})?QCIAq+^0_7P#&AvzVGwv)?U)}$jv|Stk}MF>-Kq;xaB8jPG3|z z@sre9aYY`9tZu8Dqjrrud&QqINzV}VO~(&ncGDkmyw{2G43+gvpt)!sj}vn4-@2Zz zQ9rl8Xl1jW{<^12EY0H8b{Z6*6D}^V97&+a@!r_!v3;@2W3(p`E5|&76|_9Dutf#f zqse21sveMo)c~(|%h=>W3t5?9tP&1Ul}u-#t6_CK_eiba)X4{R`~)UAx_5>+#&4Q^ z_1j=1@eXc7nI^ka^r4BVJ=@Ru{Q9ZOi;vv&BhQyEy!rOn!P>D;pZEld$B}h0>iE+~ z5ehJNPwr#qqr1YZb1SFU4RpU}Dp|Yg%+t1RM+f8dM^60cn$Ol=KQgjUP*f5V)V256 z)OVoy??d?S#Bwjuw>bPtcUL5nrMl;0gsW=P^b7P8&;d7=y-`7km8*g{l!(WxF~N$( z1mdvZkD@|6LC5r%vqh9xifLxm>#u51?Z{Or#Z08*aUmc`B_YbjpfgmF(-pR$KRtV7 zR)7A8F)~uX#lMddE&&tN`oo1`f3bkGNn1$JRN!d+{SOClyjtf=8a%Tx$4zHl*1o@( zDfQDYobl5;YTvKjv2VJ4GgANM&^?-%z5BFt-k%D4;+k)DPqY%h0SW1+%Fz&01rN<@9vVV^h{pXD0SDq(Q6~<36sG$7XNyIwCyohe60E|^OqM1? zgX~(fK(C=pi4kjGu1#H0>qi?Cqp1DG_tZw@4R4(B-dlH%{U!SF2kzT@7CIRF_|yw1 zJ`%2dKiq$K+xja)*6vt6i*+A$4BBB5zEUnm19;Z}{J%@0hoheJe209VApUDx@W!CW zH;&}&IMpxFXoN%|pCFcG%)ZO( z1+kQ+<#Czub~$@m%B8MK9LhswK`sqdy!;e4M?rt^+1Y}#Ph7z%5u7QI2{nUP^WgeO zylq>d(5Mw8Rrd=PuhrM8;W^|M49;82HZ(-vS9|H4uS~A|;yJZ@(AnM`m)o6bEA2aN z&HXcHf8(?*2S)Sh3u1I8fC9Cl&Vk-+?#%4YPY$yr1w=`tOv!2piA4shsbCzd_$~ZhNeTaU<<$;Gra=376Xd$or4EwQ0 zKU6rx)Ec~TAhrl^OyA0^ZS+M&6n(FIereQ>XDQMf=X@OB0x*Bbnh=C-))#=iT4?M`WYN_uMl+wf1EcyX0wux1ZZ_&c*}N)GLlh$9|YK zAr1*Rzue))ofL2AmBKvmf3+dY0y`eY*BttOu@-#d-{3x5dgf)U$O}C0 z``7KeH}0QiK5=68#lMeNdbm~WB%2Z~Ys&Fy8ewKpsi-=w)oa*inrqmx^N7pXt54p2 zxMNc|7AKYLuh+hc4o0Ji?JKAU+!ZWQ)+8%fy2}BY*N#u~$Z{z&Kg~C-TQTdo+nK3G zv3kQ_sq08ZFPy(>W%NLO)e6qh)Yq+GR^=FVFN7fvtIKU;38Yv38qyS>EX0onU(Hqp zUe;{PE2S!Sq|!?vs$FZt^cJS;3&x1Mo!yADEA|YkV~ukYIAh4&+z+q*8ED!*<*YT^ zc1}%wWNQ0gtlU`p=@-|&t5{s^^*{2>bH6lx)oJ5rTz2}(GpNp$e{;A|&E7mK4<=@0&RTYg(2 ze_B90d91EIy7k(FXYRdpp6WmR7Y{mg>)H=yJKHXCEl|4v-S0%n$LNjJy=0g3)#diF z2$HHI&*J^%SSoy!Gm-gfvP$tRtcPjv#41{#0$I^1sW>t_;{>~SqrJkEb0;mfd1N&n zB~0^O+&KHy={u*!Z#;0@?W>Bk`0SVW-MeD-_FDDo%@d9i_dN9aM5$Or2lq~W>y?cw zR(|L74Hwq$9PTr-p2B=)C%Jd~zBZOc34GfI1*w#9mTrhx1zO}XM>!kWuXf;D*E<|S zA|0Z-*LP2Md-4@;veMn9_BambEO}XgbHipxJw2W*lRB2xLedUG1T{E3{NsRB=HTtaW-)^nafRX@fl(3yQBr*!1rD2~!fN1_rBS32rScC_mi z<27GJag|gG^_u#EQ;LCUnMYvif8*%QFh{0vR6obW(7%qa8^nZNKh_%M+3!4Hb%Z)= zch=tc<6oY=8}o7@CHCY4_djJxXmDl!Ehp}}^;XZ14l~?8{QIg+n~#0r#^+_e_|xg% zyW#h4_}#zH@$Wt#J$~DXv6G8rc@>D#mpK_5MHZzj$?-OMB+VIK1x}~F>*T2YwyI^D z^1H6D;jSk1*c5(0ln1{#hS^5n2|-B1_OcX?B0)wOW5B>W>6X(+C3z?mPI6ZAs2DzK z1*^c9@n?|b!WAnUt0)=$XX#W*oERIf6G^bBxJs+jq`Q z{rxL^V}E_xw|;uTzP(8M3q&8*>cBoJh_@zPJZLv z??cgJ#-4rMQ$^RmX1>jw4iRXB1Ld(@LF5SpQGh~dR|voS19{+RBz^Qk?JCVfMYLC= z7Jm2zDs)FgPE>1;PqW&;>-kuyEpL!Dib#{(C$Lx830t_d$)mSjY zkzoPpw6{37YzEyqi&RhgSI@lhBgFZC^0)W@%(}97sP?D#y>j>DhRqXG8#XehpZwd} z!#_Tb4)&~n{Mi5b`-#y~_b<@?lWSHCjcnUGKDg3-mQoK<{{#WZz?J1Q3L&&9fCeNq z~O0sE5A!w;y}`J3H>1KKSX3 zxLbJQ_<#J_LrAUt*t>mbXvgVibZkTBKh%DF#)?5SZCU=)%O3f~d%umi+WT%k_p%KG z*$K&c=F%S`D{t~-{@+yIb6pG-BIq!O-iHvRseE;32!)h{z+V?aA*^?ZI-1ZEiG;_h zvR>OeaQLs1V~iS|UdMz4!fA*^`_Dp4if*6!_?YDeuoSo>#-#fM$} z>y*Ae`)~pKK_fGm+`)j*ytZaxB5VGV8qVwahH~EE(5A0=*3zYq>Ql#VzWEoIeC+kD zyH{|(s{L~I^YluKn1p=LkdvT+cCAlhW|!uQ>H8{S@)$_m*Q<(>SGDF%8Q!4X=5Xg*=9( z2966=QLVE0@u=c0JbA+s0+nFcYu}5qS8-Yb?*M-AGrMoDzE&_7Ze{=%cDN@#rAc`@9DqonYHWISRQnV z$LFChdgD>=*=wkOBG`25Rpy^r6H<_d%gX6%n){xtrg*POvG29e0tHN>Kr%}O9VOCr zB{D>bo@cQV<_Jdox*{{?$kPu_%{J?jbeN(=bg7pp8ENLqi$6Vner@(o7Sk&gj;`w* z+_!7{p0gL~M(UVi5zMmVs+f6oN$RO*YR}+%T3_AfX{tDO z1VGUs2_nbyz45yLR5CP}1$AgXvW5JUxm z1CFR7N(>9Qk;^F>qaFT};mCIoJ2=0_y|u7P(xrJ=B>?klv5F^HC1R}NIp8^|aSj20 z8iP1TN%%3quuQYx)mmxcGi_PkQL-pjkwMDREIzLXkU%CE$)dyy7^@_i<($^S zPR;9}NIEG>ZdV1K=LH9=Sd3LH!)m&$;HHBp>3A}@SjD?oCCMxcxM8dkXqw|WP7r9C z2Oxt2Kr^67GB4mTK~^}Qmp0RYZq*dN}5iI1qVR zk_1TY) zk!4YqM36+S5seW?mBk4NfgxDM@vP9~cePep_)J@tca$uORb-&^ERPSGu}Z+py>!5Q zw61ERj42IG=LM&=u2b`|N+tZas<|wp5VwC_SujR9KgEUYzAD#>fd3q}%h-ALu z{(b07(-EK9O1 zf-LC`tP(|D6j@PZSdr1N561DFD5y(>RV{p`Ez3Jf*u@glWS|SIun1P^U=XY_G+k0K zJ*yjnNSfKYPR++EjbN3gyI7^jvZTm(^5KY$V3qARP2BvT>3%#JRPyvzB^(9<;64gq zIPo3EC`lG2S!Gy3lIskWYlc>42$ozJFoMP$R`EQjz%kg}UBF<=k^-P8JjkM^D3T&e zph(6XR^hOalO&8)I(At(fx~{+()gImShXzwGg%a?$RHLt0Uuzfs-GhQ7+{hrBvUtJ z1=lherXZ5$ZC$6DeLaTNN!62XxV2A;qR5JZCm#qn$P;)W5cGOU1KkK<$4?_OLX&YA z2tfJ>rc(i?s4OQdqJ;kxB=Q|Fm*bb;N?65$88YW#l?FV!7*@&H*Hi^iL|s*7RgplE zy$zO|1l61*Gn~X2*k$DfP8Rhg!m1Yf-^=ojl0^%O$Uql4aS^QYf|p>G7w6qn;KB5Y z5@}ZJI@OGyENc*~GQ2KUsj8}|s*0T_(Z#A@$nxP90>cbp$4@6SLRUa0s+e9;1h2zI zsT#&ANydMQvRY@T>R9zwVt!fhM!a*@5(LnJXC;ES^DtMzjJ*X=9NQYGiv>@x;2zwa z!QI{6CHUZO0fM``1b3I}A#X0bYc2n~x;J+?;H%+{U0K+GmAqvReFcX#1td3AOOc?{uXIeArfc}V#i zj;VM@tr0rJ@zF}n__~@=y82eJ`Bw}(h$*{-6p<#FxC7`+-=U+_Z=A^?h<`d**}CB2!bSGnb(o`|`&=^;JSd*7IoYj4i!JQ@sbxC*jhP=tVlI$>CdRO8re9skRA;p(5_3RYoteTqW*dfK6cvArwsH>y)1yq|Id* zHRA2y9->y)R9oBeke)xXebB|IIR40wQXz-RMUcT%iagCufQ^rC8DWl7{;49*<{5Up z$_O!1(=ndlNB{RGYJ^D^6QI!R_11S3m?>AjgVx6~^VwwVBFl&P*{fLI{*+?KhK$;K zJB|qM5&~lF_{gzC+{ra%V{!>M&Qu7&foc1;!!9 zqu8~;grULt#^?D{p|#_*73jC^DMQ1yfA1~!^U+zSIh6b1_`ZO!uB{=^!ZvTZQC~}R z2q2lkOL7plC{?;^mFs!N);md zF{PPxiNDK$Ro=4y@CA!}ZdN|m5S3$2^H1*9Uz&`Hf3PIwX}MNZCV_}BO4|dZ4aOjD zt_n2&XuD$bOYU5|4tCsbvsQ_f}Fy4!7LR5-z3y&vA<=M_>9?{*5UN5>jns?-0=*|nX z?C=4!87WP1z0EA?IPjK6nKMSy*w?zKwTZ9yMr?3XG@I#_ZzCMX>xJeq$MLoE4+HlT ze2(fnp3G-N>{zE?FZaOZhqb^t4!~S`sb=xY4koyv&NYN59Skz$fd8x?$`SE;`t%3y zj{^ff5F&4m!i69d7u$GgD)NZp4WR3zI(G_&Eg4~KkfyWB^}IGDN-*Qyym{51VEZ$0 z(?{pamlcMI_l&JlamVsg+Ol&b$mRPt_gJh;esYwsK|DV@0eRoR`k>aT0`gws3dmmE zi!GyX3yj0j?ltAhs_9RQ{PzBPn;8J6r4+CnqGgJU{vxbGLa2BzDm+b+&dIQfp1$B} zEub-YaVE@1!A&85nTa~Bofjl1O5Iqx z4myP}LMp;4v_~I*XRqV&Z&3-yrWi_Cki!BPe=*1O^YI7v!oT?_4}YI9BSnIKE6SB- z!z|gnak)}1NJ7(`^ycSNo~Z#{s~B$k*S~Blu1(J(58GCx^HazPYXj{9a)$&cOjl)k z)Oe84PM*2Zy2YJ9+{VaKP>BCLQ=2mHEr^$*#_LT)k+c=)xAA}r`Nr#j|542iRWSr+ zGmITAkmQ2U$#Wdeld3Cn=q(3MGxW^oe4SoP{zGSHgI^Vl4HW>1_n0`~baoXwn1ysIhP1bD)*Ky?J$`q%{*Lqs#RcYL2rnfS&#}_(#f) zZtCs{(>IgBShKueu7UzRNWn*=M-Xv~s6pkT7G0?Fl)xDUf7F)SQy_+M1u;2d{ZTGo z%^RmAgp0AKOBo4W!jWv=c>07$K`ak+1V`2jC*|sc&{% z;q&0Uq~CYfOec;M_QLV|eu4+<4+6rs)=JJJ6(;0OZ!ch&%^rwRX2VK}GQ*JOZv0@X zp?$O2427sqmj@8B>>#F7K5zxB<*XaQKLnbc7CE=!Dh8XTcYtBHapAexW_RPa?Tg^W zL$^Y<^k|YdP)>}FW?(L8G8N@0oFSI=AZBN(eFW0KkdtH{+KZXiiBS_>4Pj*9&Us;6 za4nG?U1?vEY`FkIlalSuT6Tf=8-FXHQ<)cr zd|k>Efx@gYe?k9-8JP^C7)2tYNbeJ2pb}mkp zJ)Gq*nl2yCn-GvaVdj7x(p$I|AvAA){-x|O;glY*6**og8dcZPKD+O7vsiJ#%C$Hf z;Z5n6-S~$41*^IplfM0!8E%_kk!fgQw)9C8@eD=VarZ4DE-1Uvfa+>JO-U?7Pjq?= zX^ZXUr0mIzu14*#_w}ezuj7zF@JD8jM_H=5h`E*L*gMt(}DQ--#sl_pM&43*~ z@Ge;p>4$f)^wiD5IX$OKVtfdnzoQloU;aQk(9_H{r@GXCDUTa1oykjYku**WIW%Q3 zBZU$2yL}|h);~OPq2UV!DKN*f_-5j{?ro5^=+k4raEJzyAV63 z)lmJy*uY;dps4+m0&>(rC_X7)G?{qDAS(l!Af8bxGxg@!v_5iG8l>9w+YJu=Nnzh- zX+Ve8A^kx%E&5wztVoYRl*Ee0E6n_qBINA-}pFeSY*}iB;RPG zxA#Y2N-3m*?&SX#gYww?MN<(T6Qa3CZw)X`Lc)be@-c*|oiP|n|y z;hA5nonptK)3m=I_|B}`BEl~Yr>FwBfD&5NGEK1+N`5PR!->qT#RRcXn%o@mG!einOa-&$Jqb-c% zD~2YK$!LiI>&g3laG#8*)tCkjAeSK9A>JXRt=F5n?2_+nP8^)R16v%wEW4>Kuw=UD zXT2$er?ffAiHQYtR8)|ZpDM7uoC#ZFL>EujBUikQ$D8l^us+<>57z=y4lC$v5Xwue1lefvxJ5_p9f1i<=vbqS;6!|B0m0Vv)IIJ`rogl zc1&PsarX!1(1^tfW+Y~2NO}yout{i&Hv{!}-73rgSA8u7<$2;I6nINfn(?vG5%&R|DW z2j~y3nOo_j^F`sVC4+2(;WyJDp|1JKaQQ0hY%EzuN3OGlt38>rwl*XDw_63~69iMH zD2tWfkZY}+nJNjnZN^iQbvfmFht#n}^cOG6dduoo)wok|V^0_q6@))^7w~yug%{{D z{rz&EHIm|BRET&C<$I6^Jzf=6k0b=+$4Cej&(9uvflay~VNjO;#W@%FM^^=~h%2NF ztFryPGurD*>c0W6HCml4$Y(k*v#=u;tUawgfz}?X-M8J?CI~*`zK3u#=?ToXG3E$Z zLt@0k3y2JrIKN1-D|XEI9h>Nj@#K;Qo$k%G9n z{)K=3iy{>>cQ$pha&WPC0vMnGOH{$w&YXlvQuyVMPR-cT-p-go#NO5nxN2)`;S4aK z5t)RYP0hhB05uqZfwOY~Jm!CZq%Hu;9-zsy1B7`1aq41jr$NF35RU<(J2NLDlc=$S zl)06KrOO|dIr9rv9+64f#n{%$R2XbwYfb`iP+vq45wZ6m(PiKS@ZKPf7ce~oD+|E# z22k)U0Qwx@F9XPF4gmZP0FVLR=}Qj)UTO}o$pHxX|BH9Z{l8THtIxlR107#Xqw?Pr z*8gRI|1eucrNv%meK8_n4d9uYy`;35ys^W-kk&t^RXv>n8%TrS+mrC|F{zkaSOKtO zPZDZjGkX(r8bl^VCo^-W7q%<4v>9MfD;H1Tq^hffgRQyUi-DO11Q3}tNpu0M^^2_m z8G(0!0j%{O_WTQp8Sn-PJ3z|*i+|0+4)EOn1HA`O&ujn)nuC>%orH~rl@S0~g8xF zn0gSv?*5y9&BeyZ1|Yj%F!h|=|8Va)07&}_)twC>v$Fw|?-wLI2%u}f`24RU05A>k zusH#=_h0z;m#crB1CZH&!RG<_fU%i@F2L9zHZFjO{ihE=wFZO(=+`XFtN=Nj4G4^v z$$l;T+A#0>>E(-uasf~ zsN<~6AZ9jw;L1xJa0AKq$Lb(}OwRmAMGyxwz%mCM3K*Il;JpKQaX@hZ;trIpzXO4V znS=B1aO3#*2=KyR09fiEu9tB6U*`ZzvU38-2xKp6vI4>M z5($7vpqGezNiQJAxVeE!zyU-e*Gm=w{Xl>X08;`biT&jg5Ci`t3I{t7CETn)q2U17 z^Z$$jGypux$^{hl|Iq*#kPDdhr4(=j$Z2j8E+BWfKtLG$A5H=U;RNy=5D5_PPga81 z{?U*F5SaytP9W_${!R)Y`Z@nL9FS%}4gd?*|1sX*;SWgkk25)e_2uut2WsX&6^7$K zA;STbKW=tl79atcff}s)UpWMvcrhJN76D~nN+^)_%)nqTZCSWq+WwPy`oQWXrunDJ zn}dNR!5R@rJHXU0N5FdVeF>C#_Lm@DQ)CqP`Nuq*Z zUHpZxvfh@FhSi2X-Cz9R!c@5>b59MInKh_DndCCI&$=%(){c$~uvi6qvuB9yc zz^3C?G~uA}XvLG_-roKP%kocCUoZcO3pWDBJF{-ZxydgcC&X9RIev9(PaFainP;ll zC4Qfa4GkU-XqL0qhb|O528|Weowy41JIL{W-|+eIZkV*iH~uii4sFfd$TfUCa2~F? zV=30*D#*ND9~|}oZ@UmxvnHpJJ}ILj(n4Ah0;|K zg;J9ei8&cpQ>D7sYVe*Y3^#a-UqvW*n?jUkcrCfTF~%S`DwS(QRCP&h%t|<>--rxq z92a8SXzO0_iCLr^QU;ZR%YeuJ+^AI0_7p)U4tTVRb~V9fN!qn0n2%Nquuj_Er9PiIzT*L79;!Xbh$Vh==n6D6&LMeosg+2(l z7V;9k2|0|`wF_iQfphRI6*O49waeII9=Bm+I=oD|6@BzLAO7{(jG?=mBiG&l5eR2s zr(*$@WtZ}1o04;UZ9AXJvPj>Dtx%qg}7lm^)yK-Pj`l3hnDjFnS-mECnACY;kqy(No=DV#tXTX zvfJRtX1;KDMl=E$l?xg2_DTM}>-dU40u$E*mG&){@@woKDjby_VWAt)%q=-($i^u= z4D$r4G=$CPjt+*1#ithckVEbW((M~n|6G@rg5R$Sac%;iVh49+9%H|>eQE9JNIvJ; zCWMI6^pn)!r34~#0-r+sUDcwu`m805Ox~t@W+flg) zgOG(F3RrGs4*2ct>`MF@9-&Tn0tE@c!)NEbA~$hTr&KF94j+9Kz9n((GVIdf<@x93 z)*?55JlT|fbbN*qoQl1QwC=#G@w56o@34%+JjDU#AY!|hrzl3`@36IIL1F&A@Yr_m zDjQhzO~uBlVNdr?_iiPMvw&eG#V*q_)6#qCw)Uivzh7YPIV6Y&mM6#AXGhK<#+_&` ztXZvMjx?Q3-@=N;0F<4i>XzR8w}lgVBktX!l^2*-%7aV4aE$egR@ z_}gTTIHv`n)3yvri3V6X^VepM@Sc(jYxB)Jiz5%iM(C|gkL^8}tNY#Kugcqc*GkCy zdi(FJ{+|U~zYDlTU-L&{Z`&P<(#YI+5Kn!=lIM!WT?*YT?RUC_z^jA*r37WfUi=-^ zI;o56B1CvMiWX)~C7UC{Urbtlv$^jpwD|f2SWvc@=XDtej;d*9CZ4SgzUl}%@Ix_* zm7n}fUtj%Fc+&Y^HUTaedRq(|Zm}Qd&?HKQGJ4Eut!Es)m0CtBZeahu<&)?&>-{|5 zY0dX%v_u<~v&@vXzSh1)_h4t4jR}&dHHlZo1gbwRL^QBpNv~{?gpi*EqFtX#rwgaG z6&1h97AOpnn?mkeWq_Z1y*@!gzors8uk^dNF1RcID`pv;L{g;`VG7SlP6FQE;>D84 zMiQpDHXyJj7k?9M3$~XHlsJMWQOR4((HdQIez_V8apl9>uW)@Fe0htt`P3C0Z1B1* z%4HmlS807Kh&E#(Sz-copUmWkTjM$uF*vyu1eswLrme3Fq>RSliWCBg?c6zHPf z;VJIy0UbbJB{wZh-qDovTZbIZf}m5vQ{I}MT|V>AhypG2}{_UcEysFR;mi7XOFIkVK)Lc9-R zsnt7{V&%PJ0|>{o{E)iEo$ze?DEX`52g@Wp1bX_dw#Ordk&reCo^ zP8522K(Hx~X$C_z8{@Oy)9REZg_H_AB||P1%2CiMf6^QHl_njU2P%FSp4)FyFX{>% za_^xg!wYf<$|4zx(wD&vU+?NlLs1}~DTSe!p@t>{!z-Z|4Kscg3NIGqy;%x~&Ksx} zTxs@a%R;%uz~2i(r+dsB2qaK4SvX=lu*Wrqqkw(Vp5OTXz&3YPmQ%3LO|C>Uv|t;=M^(I1lY4E^GJy5C+$ z!aTI*Sr8SLyn6Zp_Bw3f@;gL-qL1;@k^W7(PZgNMhc>B*==CncPfuF#!JGZJ)O2@E z{du?K-1gN$sIS#FEt37QY%GG;H8h(b?LOT5ZzTI@!YDPbhz|RJm3#h zz`6ztQ^_};OS|dliJb8Won={mcW*loLt;R0oR+78=P{V(Mc?{K$bIc5JdD7jF}<=k z#}c@HSlj~d#WuPFLgMiP5zZ$Au)T4~+hQ`X%LQ89b@8XVM@r$)js2~D+5Os2bv{dA z9`=+stuuq6*To<#%SQ?9PL@&_C`t|HrCua<9aJ?`=DJ=;UgG1tpdd5Fo&l(!N?aFqI4VPSOmq}qVMHzeskJrv&zr~UM;427?npmGdFeqD$C@RgWZ54;_7 zxiSdci_Yh7*!KLQ%nsts`GJ^GfO$?J>}}TO8S9ZjsMx@|#z&Xp1{yp+ZMS;gLY;F^ zoGvWx*PN-ky_RnYT3(e&aSY0}fcFZb3E9{zIgl~TVYJi~ZioN2Ed0dsYE>}F%}FUY z&k8`2VSLTZ811~WvZj>B6;A|2p|?!P3rBF|Q=W$5@)RM6#%5QT))M9ida*?fiCJ8x zLE)XaUGB>ry3JB^c_wAHHA0=DtC6Ma(d8+G2JvznBF{`2w>X6c@rX1wtW2zei*8X* zY&9@i(#sg6pm@{f#j2dZH}A28CyQ~wSYO|YMk`pOs!^otVJ5ZHs6RM)h0~HJ()rQ^ zBqqusQc(!2)O%o#{`?Jh{<{tGx2#kGZ9AM$xO6fl)em?wTvrH(H^~+8Z{MYpzC~Ds zPJ2z8S5Dg}B|!*Lj@Uo{&uJ}WSm4tUk|?vUka>ohDq&_JO5}PdMjX#5UB-_azWwi6 zp(F~>jL8e$Kop=ASI~&Dic+Ij!#SkMe;73r!5J-u?lB9zw?>SgK#ll#1et}!X2h9^ zCc%0LG0h}P9xU}9UcB!H;@j^X{FL8op!`a#9+SF1vl-R(Sl3DXGa;&710-kW>p$~6 zwIzsgJh_nK1LSo`y#`v%HyEjk< zHQ0Y-slhH=|N5T^9^5V=1#(gTlJfBFKl1cMALs{E>$m@zVY-QKw!Qd28ZgtcVT?>n zh`l&MsJprlbrkuN)Mp|H#Nu!n&ggCkL}-=uufEV9WL9~@JMW%Za!RT&D9kj zQ%_dhcV@WbiH;Yrf7*3cwyOR8^U40Ji_qP;Dv~`+D8j64nlci!nii||%_LmV-WUp$ zws2fx97_EXysU&9N61${+SL$55 z-KI3b8BZYg6)s|bbYg`sV1vJrLbcXF)$*_+$X6~qV?P-Upf zOhWe@*AL)vOp;eyQ!*CCl7YG(s?Ii&iZ%ChPnp9If28BBQaI3wKQ_Q^%1~SC991ZG zV|ClEEk#i7uqn^(i#v8y5^cUpEAdaHDk<_$Y}lP>B^vRfDtTTVbe?iP)8i{?y=?NC z+#OxiyI&T=m%8+FJI!B~ZsesLlWb39^c)>1aHaC(_r94Lzahs^ba9^*m!+Hm&*vR` z{A_7>X0f}CGNmv(?Ddv?v*y0pSzwdT6!T-~@#M^6LoFmsci)s^@D3EW(V(3; zl&ZHb%IbM{O$d$j_R?T%YHZ`I+5hUbV$5$<2`r43qT{vC7xt7NsA+wb??s{^gQ?h^ zU=ZnX;Zd~G+{UnT%UG&xlG8~FJM8$f^QHn`4}LOKN;4kC$%4?9wNSc-8e~?Af*Vv* znt&WcS32PAOS!$wKG{8MO!bFH)V(l@`6BEH`BsKSBnbXqUp?Hdov&+3#z0Zip2zqi zUpx||S!HNgG{sH!;Cs=!|7(!D{;WY!+7|=9wZe|?#L2EA?JGYkth3K+B*tE-A`b_s zEhAwb`jOCAWJbbh^l5L9_WA;MYxCMVeQdn%t8XuZPIifVp)zfgNoUJfaWorsS$=x8 zUV72)Fm{S(Vl(L!PFbNp3G!1sfPBT*xqU-Apixws3GMy_>dLN*)Qh>s;vGBg<1$y#!BtJJXv`s zXyy^0MUjsp2mNXU{gT`>fxfjdz=f*w`Sa)bD@ivYACh%l^>Uw992iLm&M4vl+%EWG z@=xi#4c5jV5mKU1P5a}IiTrAYt{z)6K;LAK3v09__BtfJnh2K0iaWd9e-*_x2O2^5 z_&oBVJL-}UhWk}tE`9bmls>PZWpc1)`c}T0jI55B^*%T)k>5(;8_npl%EoM?MeN?# zSwLlR)?V>m_xo5pO-qMMdk)1ev7D@Y_HNL(%tfS5*XCVW)z_ZT(%?6V9is{(w!8Bw z%k9#(2BWpE=I%skuD{#wvPe?$69l4Hn(bB;+fcGJU5_5;UxA*RB&U|U<<_s3Hko#i z_14`oZuWl6VD>{Fgq25d*09^vsh82vskV@7+|$k>SlAbH**h{89qAV}K+YH({#+`J2oSYxiXNvOZSbCSzSz`4Fn~w7E?==@e1D zjUB#4=qWafzOU#v)sMELlI+qLV(kD2FF_|Xd2it%3Sq75or2>-=k9husomp z`(!w&l{HYFzbZW(N@?W6cjofS{1|Kal>DAlfZYUp}y%WGR zOyQ&U`+0Y_^)6k0A;Hs3UFJx%iK#>B*AKx?ow=@a4JaOvT0C~xwqG#q*DqXjo9bE; z+!RrTq=TEbR^^MAyEvR6ajfu;$l3gknE}ZTx&yi@Lt^!)D$fqb$D&|9(L?OWZwd{% zKRvZJMX|uhKTWGj5A!uBEHqka6D!(nCu5r2+DN(ERO|bb2(sfO5((FnY%OGc69udj zu;R-YRqyYY7twoaDv(>bS)3R`e)yMk$i;rt`>hjyleeOe6I(e~d?JbwOj6xT^H-Vq<;P!R6 z5_USnP!zZy=%^2NI%TrtoeVkPKm6zy&`6D`&ceiNM^{ymt!`zlZep&}Bq&ovPBEEe zdqO}J`cqC-R@Ecy=H6YH_`4Kz!5WKNt~cW(<1k-M2_0yjLXx+t!Rc3@>k$^CcJp-- z!NHTZQ{nF?l)f(P+%S8IN=;g_{-w5#g^gejX}!^)fz?MQ6Lh)(xjHqz3@GXyMHPwq zc2@@0BOMxCm@MI1GGSB;)JJ&h-u{G5odX)^dyE6JHzS#A4CMm^bp{K3!jG!=Icrrm z-K~q(-NL0jzuq8t2eHAw>rMzJ*>B>h)hy_urah{m@V)i-$d&&cq4p~0o#HH~GKH0a zltf+Yb=qh)H{LQq@>*tJ>)}>`#VRuG8WtDg6~dNlbG#rTlGoP8r!KD< zefy8)k@7SWc3vBHnh(|E)|lC!^0C#BgCZ`JSTJe#ga*+|OKq7pW(~R;Iv=xdFHwp( zi4x{%8AqUH2S4F$$L=?I&fpqc2V(bc4tjX7U>ZyWV9Rcbn|t5?*J^HG$jQXmQ?dBo z6?W!`+{9+T|E>zD}Q(FoUie>q4a=!OI&3J-xb>yEEU%)2ZI=4TE5;K;kFv zUQ;J8$rb5Th3VyFx{T#*4ka9iWe^>p&N7j|WZ8>sh-*x{~3mL>YA0BLy>LEHoCea*%fk8nW z=T!gkQC{y+ZVLE$euJ6acwBs_pzl;hSu}oA$bM65fOjId z<6*s}Is!MfmQ;H?rj2<6zuL}6vL#o-#Qqo@{ox&LuWW+U9x_*_!XPqY0+eKfuAdv7medyMJcyt^p;%Azmz^+-PPN?tf(9U--PdtL^mI%XRp zO|(G{sR4}VdG1|fe17ne?^0iWw}Hv`ZqlD#=d(c+c~kE1&z3W7*n{F1i8#fN6*o7t z>P_Rx0Vao)yhJq1ms3Y~d`B_dKAY3VGzUOcB6{T8n&w9(zQb!CR#RTFSvZ-ZRi&tk zi5;uU56vJvv$Yy)rA_#v&zE}Af?;TFDKqTRm%n^4HhD0gdGq=Ho_CO@E*ANnw7Y=+ z)rj5p-QwaI(<;w9CjM--1nnJTmFyfp4kCdggY44uM1~Sc zx8&>O*=<|p!0!qe4gIn#!f*2BfL+X9G|6Qlc6%_W4?P}&ogT8V$|NppqcF3+drEt^ zGk40WQ(t@S0e`nDWw?m)`n-8zZKcq_4a+FF1C7$`(d22?(J{Nt*WzGep*@MF>I*@_ z{M5IFLwIfLl4Tq}BI$kN^PxeqkReS9H=L%OkDtxxyu^1)Jx3EvZWwMfLlg**T8XOJ z{4$unj1R54S;laY9ZG*7m-jNCuvh*q9@Xr~&5gQHE=1SDq=n=t#ig<#oT;#y)q-%I-y}5Y01&5Ja zkcD(#u$ie^_1?Ub=v3xzXwtQ4s7@^LR&>>k%x_uTG50FGFus*xwWp(Jp(JhV9tn*i zr`b$O{OJ`lS0bA+E2}YtOc3`nn%56cjx%8%p$^$g-P?Gd3r71@H_zT?JKIl%4~`%B z%YQYRKx=5zkc~FNd6g^Z$B**NB@q^_m=2@Ryk?;2xKJq&fa8I*Q!OtDY0Chx%}oCCraID>^~d|25ATC51w^YY0*w9k-eg z`Xkyf*s)GWuXI;ZXL4*%c6oKtDTjy27wR4VueM#QzRyZXlMfbw%!YF!vL1y=4(-T- z_Nf=xD-^n-je3)^?1pb0=|F_o@>RXtk0ZP5$D2jAZs$(+)M;yO8EZvHu|z+il#6NM zsv@dg)P~;;f0Lbb+#NhT63AzKpBRRllerzywwKOm*jDFtFnbs+8~Z%{&FdmGWq~-U z!3-uG+b|3yrsN~MH3H9~m=sSn%37b46wXS`gPm4&l}&91V011TF@h4zEcD91#ac)K?2j8Ea#(dCA{4h68rr-r@z#_0b<^u?OL!|!6t zRaYVR=T2@Flk~D4>6fre2{BSM6qtv!C|GNarzGW5izuv8 zNj1^@?2K)vw1s{}!#;QmhyIU4zJLPi4AY znmnH#J;wV@QG6Sp_GW*r3y5(Vk{>!(NeXDUw^>JDHhb@3u|!Y!R)2QowtpOkJ94KJ zulkXrQ}5uZXOXVX(3QB{lr?o7RR(zrcFnL(Lrjhgs#R?7kymJ^Q*H(CIFK74 zVNH}ug-KNkrn%Fb_@`0q&pPvmIyhoqf1R7= zWgB9iOUgQ5o~{_d-+tZCk;JX`8BEQ?a5W&mlypy@fwQ8K6jhT2_>By zDq-WWs1IuJj@Fw=)-Q-kOSoMo1HVE>Kg`1`Gd{n{Le!}^3rUtZd-BfvTXrR{nQP$` zq2PCfn*Ila=h{F!6uAwz?dh&Dh_+9IJOy0UhQFVmwobjm3~bYH?^l8zZcbM0H~p3q zd_LGsGpx@9xV&R9_^GZG?lP&oh=bHK`lg&!rYD&`hk?OJaYWf=(uK_rU-@<9?4wFI zS?d_PlY0=3t8kR{(hwV?`AB~_xr;^ys4qe_fK+dnvLQTO{;MquomidLMEQmhyMy4i zM1k;y>L4QBMZb>G9wLVdCNI52#h@)~NQ|5=^2mdYl%(B(A#pl8A--%qJT9!7T9xtq@OC!hwf0Dq4)zLDPfERiUS2*!B4|<4av0-K(T7_Sl=< zrzl@}eumDGT^Vg^p$ z{Vm-lE5J9OZ~Y$@KHUK?xKl%?8&eW1PgP)jJH)HY!cM_r%6aXl9!ig5-_ex^;mIzb zhoFK@Ld$WeEe0SKa;XZsdkeqt(roIJ`khxmVt|gSXK+w$x1CJp z<*U7TO8>F2>np@t-$mG2b;Z6MO`>wutG!+8F23C`YCeAh->qSPwbDwFTwxPv-f0bo zXhnht`v&iGOM4<-hgQY4W;TWv>=RVtlbIi%@DzVN_xA_I-)n(EEDr zW!rUIPl5Lh7S6|epC>PFO`HaWj*bs;`s090D5hZ)t78lckJp_!93^kgkK|-Gz~Ee#j#-h&qX_@Ex8+=Agj?;1O6cW zwkX2P28FSdmKB8tk@?q1=<$SMu}vA4t^I05`X{Bb9+Ynw7tggS%7;*7oj5_eJNtaJ zJJPd-WCwW8Df@n$YujtBkp!wbt+p2lm~<)h1`A$oD{QJ8zYdQ-B^;ji<|W*GAEv^K z&3@GKG_n1}Mcsw55%z>JRFwc1k8lelq@P9vm)WbWDmpvwEG^s?s^To3ae3F-9tZ7D zZzA0}7bTC8N-R^J^9+@g$Xv{xh zD1E{Ex3`;JxrE$4jf_hKqDMF|Gp4OA1`|4~jwvy?vnG7WGlkokmbvVFK2a@^W z5?+d^DJX&ho|P8^!|}Hx7`ro4Q6Awt#KxmHB#XjZXswBXI=dDHH10y_< zw-OaR9QkA}W0Q`!BB~McU~W5~qxn95w&JsPRXr@?#IIMx&q}8H?Y0+m5=B1w5BC8A z_x5IHaX#qcmE@mGh1JISIrH# z-@cd(+Kr#KQZ@k*2jnL(4RS%R+6J@}G1}5fZOql8%J{g8SB1xU=P7t^UDv!tQ@KhP zzL$^hb{@c5IQx23W?4*q4aF*MQ2qwG7tEbh2Jec zr{goaLu>8#LnW!ME6fGYM>R*p)|5bdK}E8w$SqGO49OHXmnpV9jVN(J@oDTQjjjshSo|y{D8n2 zb-3S8-WEgjk=s^XJLcnFvihYaEl8Bj+Bkq?qCHx30=QXjq#L(BR0`Y}drdVw8kG_{ zitHFs2orQ%ow6E%$fHE_6A{sR(Bn{TtyWkxR|p-BB^7^E;jpqrQ1lM(u9aS5u#hXw zI&_@Io#*gKpY-NbPIe$OdvaH8X4Tr#T>|V)9c)6}dj5@ga1nzFHVFLSIky?}IK8;fg4;Uwnn-<@5=A|VC@7wGwZJKuc0#mF#b^hy$onEno5M*$ z9YcAZ6vx_?6U4FmQSISb-hz)%yy!v~I+FVJ>?d<}DS6|{a-3)u8Twd`JsN&brfp{P za#r2+{whjq_911I$X!S44$_6CWVi47ub(Uh7czg~d*MsYqJ_L`8aXG)o+_@Gd+mIH z;Zx1C7^fArRjL=glMxn+!)~Zb$o-U#qcA|@cy`G%zosICH!!eZEYmB(0uS>8l2bW4 z^tHZ-DR?fF#Y{Ov;qWr?XxnEtXcK%3wXl^~9CtDXon0}-kAlaLtmY&cv+q^bYgwKQ#)!G!{T{^bynBtuADq)} zWf}Cd@y5~odrNGJ-+G&nwufKJfbHeZb5MxB_ITxWchzoZcSp*X$1R=EfE)X$+g*PnI_b z>ODRj4@O?k#R}T;Ip$yV$AZF91_TFb9Fxq@t?*q_2?`aWELs{=^lA5C(QBjzdAvE6 zCGRM$7{?v-ISmS!xV~hyzQcLa^uk?u%VUWed-jdu)pn|3C#WeS%MfptD5ct(zchnx zNFMbptY*-CYZJzav{GmUivinI8_`>D(a;GxT=P1WN7A*g>vN}c+bt^r<6=)m!oxQ# zeY$jMNzR)4k^Q1bY$n4lj|&wFfd;R!-Uh=?O_ep@MMetI<~*RjR?!V%#;=Q!u@s(t zbi@?Yhap9QutlQ_4$*~HF7FljG(+p1ZB^4-S4QYow6vr~fl2MWNF)5>hZLlBfCKI%koZar`>Tfd>$KAQV zdJ1}cs;QD(-oc1vk~$*)VQ#u&Ra~#)(t9i<)Y8Il?7Whgm7o`wL{Oo-I-@0h%X~Ms z&tJETM|ZbqHLss2LU~t(Q11It*tO*K2_AYLp&Yp6Gf7j;d2gZ3#Az7DcMI5Nud0CT zA~84OgI_IRB2f2rHAsTdaQ?dYb2iHA@4b_&6g}oQPyPNrkLv>r^!}1}r!CR=9J3a& z8|?n$;M^KUrn9d6dZGw^^R>Vq&*Mme;t*_?7HGjl0b0Buo_z&Nvl_>eWlI(sT~GNf?-`N+*HIZ}yW7pu!w4NyeCkep zYNSEB_x*k9DGfKpa6OC>+mcyn8oE>ok<|}R5x1OBT3WR&NHwb;C%aCuF%FziqOTvS zvz~7ZSx%bWmEj|x-41YARLD&AF6d-fXx=RQbS1QmD6t+hTx8lW@1(38(4~STZ5=sr zHS%AD=)5gYlYcLR*)TZ4#pXDr7Rx@wPine~!8*jhh_mhOKZ#@i>}twq7AzTc3A}&w zroq;;b#Q>5Mv7EghELwik#8o+D5j-Qi?@JHT9fC>g=1}#&8=aNuG6C{!+wP0w&e{+ z3~=LVRAUSXD}z!iqEa>az=bw*ikMlxZbEtQf-0(;<7p`2fO(i^b}4q7X78Jn)p>KyG~j%Oy9RpIZJ#=tI_aB33~AgvDYmkXN{jUl5uO1u@}j}W0JLQUhRWQ!j$ z;lz1O89QT1&5Vy@EOERm^HUnvSgoj7WkZE{L2YWs!_RTY-Xt)dzK( zxDEzb8W2t0mhffc@c}za4ue_Z_rARzrOaV);@nVvTBXX5Sw6NJpP1vMc4e5o-j->e4j8m0 z;?v;ZLb{e}JAJKf$UFEED$;z%G8Q6nm+u=TdLXq8qhpyRLd-l>d=<54;%O#ky*A*T z@xFOV6RYW11FK13^3=C%*KC*b5KdTr1>(Kh`0nr0{mqloVEmcyleg1ECg^gthtKYe zxSJ48yE}=L{3tVROEY&UiG@98Lp_SRve;9ZE!KMrxlsLMXIhR%XBoU>&Wn}5cY2~M z=u-H^m#B_tDJ!uiIh-;0A`QT$I80w6ZZW0Du&rmAvM?#)=l&n+-ZCtXZQJ(_!686! zm*DR1PH=a3_u%fqA-EIVJ-E9En&9plk#J*u?)Od%YHsMiW}R~~{>QZQe=7g}R094fdHnpRb& zq}6k=F?0gG|EK7Y9 zHa3u8>JQxr9Scb3#>T`1lAm#aWH7&FIIJMG2}oAM_J^9}&$0iheEhBG`roqKe@o*2 z%5MMHg!|tq^8d6SlotQnL;t3H{Lg;)J5m4tz4nm{r1Sx4?f%e<{s+a(Z*kIJhW>YX z`z*}N|C+V0`)$lFi1DM?-D`vEy(1>=M_VNxro40_T zk$bzvd5GYujEi*+Vk5^_i4N)@dX``xc#Pf)3vH}PEQE_*g{IEt8go&`)sVuqv9RE4 zBnH;q<-q8Cj;V2S^QD(y(4-XMd@W;I1ysD#C6N{U!mrRvny_YZiW@l2A z!Eu@`3R+^LIpKQYdc>xI2ZE&G!@F8%K(&`?8JrnWIeKENf$TsNayPUJ60#kta;(H1 zs)T##5v`&owA@CcxE-n!Q&Ra6cCs!VrG3o;x}-Jod2ofXv1q!4j#!bb4hh_?`)!!y zF%FC~hJIN)MY?sufZBrppf6_nKSbQ$k@H_99{)Mg{)ZwD&Obz-|GUWJcQE{$$b;>l z(eV$F2RjoN3rLOp-v!HWeHqKYhRcZ$yaC#5$KBcZc&C1S{n>=ETfK+>|!_@^TpRU_vpD)pAjY;CJ!%cN^C4&c#m4)@{3D&X$*aET%?O5xHT zPSet41HaFCEnoYtFm#QKXQpvE>s`IQ?fJd#k3&JclYfVp%1@)!aagZd4pxUQxSu&^ zd4lXv6CrgT_mGXgK5A6=JfL${5+(e*Yr6DprQOzE$L|{J53%KI^9nsiC)cf3o5LET$u9N_iL^O@ukz?17x!sf z-J>@tGU169-v#c|bDO8_FCH@cU%WSYPM@`&m$bh4Aeeq|!?w?$eRVLjw&))T-T?is zW-15_iCEG=C}Wp@1)d?fd>@q)d}Gom+aKzK#&F!+6s!FE_Xpt%U>dERIybwtk=|yE z%}+ZlID3N>MIAXEQ*+nYb=Q|kVS2t#My-C&;n1gVPDz|=?_Xw#82uIPO}SXEcr0?~ zSH6Rab_CR!pAE8%oAF^KWE->L4LJzLt>&(fQ1IERF@7Zq+;X(7S_n||6n^FV{1uHB zs+lZZx52c8RWQt>0f7_Tzpz;i0X5Yz4aY2Zh?{LZ4=rJq5t_qlwg#%jq1=#e<(-)^ za%&CFz5((m6Jog|d$JK?@}_sifTl*YGqUIh^b%2YYK4$U`DTg*zn`p`A!M9{<+T{) zwRlG#K^ZYDPd(~qA|Gc*)qX+&Suim%c4a{sVFhcTj^99)Fi^3DuFKj`JI zNQzB1tkj8xul*pc_+2W1R+2G@qzRl86}Ck+kWvU;iwTy+p{emR?yQ=%_7Bt_NY-z; zKBpQ(wHYz1PBt^OPq8nDsibQ}Ys9Y#?YiyK?Go+!GU2gbwFia~25EV0aDn?&@AfOZ`q3}Ex#O*>z2urMFr?3CQcwMn1BFJ= zPb3PbsUqAXQcj;*%JhHyB&FHu<}*Ro6r+EX()5cK*g|LzlYjgv8BD@^0e0`t>CobR zN3#&y(~@`#aU}qwAuiIvza{F0UbCKY{tRkc(p4NcAcXg*&27YGp*!YytJL`Zcse*VvK|6F!dWBRluOL2(f@Q?0 zF5Y|mS6uBukN%Vd=N1u|&5&MutVr-D&n?n~k-JpOaVFw#NlVcZl)u*Bx_?$PNtSM6q@%k;Ns0VaX+fvCT@6 zFfX3;gYW}?L4zRzIq@7Mhc<(?xe-&^UHWM7!=U+rSnZc{&|P0X z_?k!##f9hqvFhS!e8TY{3K)Q1!jC9m!37M+qS@DE!Z6bML`V++OqhJZ$M+AEiJgLA z5rqs2Ur~V0Xd}x;iBdWF5T<~AO0ke@F5;57*9d0O!md2LB!E8fN%fH?uO#_3Lh9FN z3B?dCAY~bHLQEc%BaqTckpW-q0|LOjB%#TXRVwE@fh@q>BCaVKiv+)il2uw_DmeHH zL_U)mcOVlM1-=lARzVk0fEthiE0P`5fbvlFrvk%4O++}L*rJaVs2nwf22_^mRsl7M zO$iR^*RbTqt=F(c8()B^GD%G{jLMG?#fHgWux55oK4gBv9|We-;19|)ND~?-w`WBo z0*x~R9somcrP1&Ou}qktW-5lIFm@^+1R#+bCI=)LA0h^l&~_UEKYr67#_vtjpvLbN zrcFTnj!IJ?@+&Y7(WtgvH!ukxL_0tYBuXAx-sRNh_DAQG;P$7lh)3L@^=L!cmxQi? z+n46!#I-IY$P98(LYf?aR6FnHyP#cSyVb zMBl#)2y2GHEkJUtZSR0kmm=I!(jhCXC0I`eG=Er+2Q=fYCjss;sz6)s=RXq~fOk^( z_|w!XKE6}m1lS5~^>D1ATOYG79b->Fko%hVC39uL;<<)PQ_C zVX^?PW&(k@wZ6Lx5g^}+|8jwwBgw0*Cnm=xB2agQ*51A+7PO1Tm$)NHwprN|TA*7O zz;plUG+9R6W(pp7f824 zXYUsQUC?yS!b^EF^z$OFf$(m4S)YjPWQd_wb1BwOqNxGt&L|SpY$96{o(@$P`nU_O zy~1f1cDR3BF6eNFSq5!stQDQ}STeA0-vKfXlAE;8p@Fizf%t%odp3fUwf=l}9Z&GC z9#jDCLPQp*38g6HsKJ+)wBKlQ*Yd>g>SF;M-FLLVfM+M^4G~^sUUR!{cmUrJdj5F5 zL9ct2rI5P|OiN7uZgT*A!R|>IC-q5h&V@RV?0Ul)NQT#)0+8nXY7ANNwwCM1Gh%hZ-xVZU zJdXt~I0rD!b3&Cq%6(0|Ndo0`IAvZXZLhp;&nP(*4TfoJS@vcy@n;2+h-Feu>3lWT zf(!IiUIrv>Hr6Y3CKqO9m0oPlF%rr|3*f0n5*K$Yn%KZL(Xu3`7}ih!La}2?O8Lab z-)_c&Ef6{kX6CKEZ3g8j?V&04wIDT+y|qe)tm;UH3378zfl@^;+3ADMBK1kL?>SjP z@MrQJ1!2-cTS?%KcaV7@N~BUVeRGS=(BM(irN#<269WQ+ZbL5L0R15)!}5p5+)8V9 z+adN=S64`79ie8`HIu@HKm%=c2jf(B32k-r2s)T1*Tj^pYi|$D(J6qhtc8S{l!awi z5uIsl0eP^tGAg}~K;-Je9v{tTBo4cF^3>L+>)Rf6kuS^B)0=b*@OC!vO)f5t$tgc7 z@znLi+ovy?3+1f9X-{+{uXI|zCfD1VI&8a)?bPWpqzJ_<&7s@zSWmA<{%|y21~@E( zve$>GN}CE$>UZ(Yep;u{q)O3RohZncBJceu=6sMcx0d*9c}&H!tZmWL8>v;FIJD+h zq|C=+YUZWAXB3xgphP{L21gHD#8}UlX#{r&G}Gi`r!P$jq4*^U1`7WTVt_^_Wz2S0~AUn``{wWgStH{{_o(M!oQD%=l*1_$Q^-AJ_)Jxj%)?;n#U zj~dBqn%ij;mmECXobdX$D(pz};$zTeB&&~ybsV#Vs%?gKfL$Fdc+?DDqNi)EN{_v- z0=Q?c4K8d&U!Oo4eC4WQJh~GbjG6DjskF(VQSpp~Hc}qMyQZaE!{YpS-c*@eBvi6b zF%?BIME3GRx$^dE?xVn~r660}r$Rxl{JF$YA?;?m_7dy-GAWe4s4vrsdBKndA5`0gnMHunyv-GC<=Lk+Ehf68|se0sa zX0j1Hd7*~9PXy=dGFPTX;lzhQTf_uw`Snbq`N{P8&S>bs`ANGoGng3wS5RtPh>R% z-ScFP-?r?!^!XLr2aLW`z%@n@GMu%63q1%6xv4ecIJC_APgFrgnQ_I<>#7t$IXNeF zW8}w^Co~P%6g^F0#KxX3+Qmx5bh|!T4923WyV(gMU}ZUVPusH2*(MN>oStf$mjuid zl?ODkvrPf=proV{yOW=V(}UWU_Y<5Q+}c*Hy6x1#p$hrDS5;M|45T%&J4WX5VM^7V zoM!#C6bvJmW%FTH^~iL;KQ+w!5?qF0Q&1>@`jr_hGX~u%OU`C6lBu{2(Tb1AmW#nk z#6dzuT+qfHw;ESvFVTpWG}X(f%}dZqk*2ig=G9@>to#GsFG&*H|V>(srNl~WA`7n#_NQJ+7_J||^0C1X_+_sd_*e?bUF-lhcf z+S-B*rbSEop4>j`7b*-M=K+!m0V$uH-^R9x_eQ>m_d5wxiL^>UXz*Rhh>lz0J$Of$ z4A|>=bXZ#dceb!s_phteCkvNkXB8mCxlrq z>IZOKJ+kV~J)|$4jeBf+i{?_`Klx`A-e{R#B#!(TOFK-;8@8uApgQd)##mGDPshY1 zbYE}ktM%Pp+^V|vAAjBORL_%T6J6Nec55yl2MjeXg8QwQAw3&r@SQ(_cAkNDbY&SM z*{mm{-`}u%ZA{y~cH1{TfNM8BfG-=p3M$w0F}5xeV9nSdHLx#((@orVH=C&&*woX+ zSu{eY8{Z1LM6)QMIzt)#m=v@f$8P2!Pa#EPxo`=;!ofXS zss=yVnlhPW-14)~RNr^z@|P2PZLdLjn>Ds1T@pjImXzeC9m=!NA+*dDoylgj`pPgw zSJ(EFjDxBXdJsMF+r+`)u~D^#DD^(Yo<5UbpLf;k(Cxd9HwFRrE0=HzNhA2h<7;Ro zIrR<0V!rdZ83OkyOQh#fOE}(*2B=dvE%10HF9TUBDw-y!(X8a|te#}S?lfE&NbUDR z=E@t$e!d&@Z*_vnUEB^KZ{h;mZhMf~t}-8*GpGIgus^84!5W4-By?9Iv%@gJR%~2D z9n0CjlVk2%1nO*?&{RTb8Ikmh=M|(?kgLaqjS!AR?idpz=OgroQl{79fQIypT<3_C zKm8GD0SkAmeYkyCDyrY{?)^%gEm-@0m#r)2@M07-61zx-Emk@(F%WlN^?DAZlk_zNYMmI3$3iyqWu zio}5(cHM=pV@9u?U70JnE4?e^Rt+^R{^Cg0n&d1KkM2i*m$7c6wgp!vLOw&w?ftQ_ zC84pI+Y&x2eKH1lfS3jP9;|i9O|R!>uESLA@WpiZ7OS_Zra;|?u}hJcTv&LSr}5El?ZMhsQP>}A2`miIaps|#2EvTqYNNO zuzG%Pb|KltG-TaKF1L7K0cZ>+#TcCF)fo5lMD&`Zm%G?2wV|tyBsMQU0d3VUAFC7P zyD(Cmt&_a2xunw5j8|uxMc-KG-qZoOJsi;W7%> zmu(@RE-7@i9-}i5xnMJrH*-y!rAQgUY4b+*@AzU5uAZ{1%RVfM+y=(+iRKQZ;IeXx z-z^aNr8!qMCuWtp5W`o7OVNDV<7$o6i6XPu-43?1;p(^gR53|j+!pF9x#t--59yM` zt%fOkJ13eDSo{e)oOF6cM^#cdBt$Gkrb6xO{Oo`TeWhBlsg7LFwyW1d+SY`*O-0P$ zmTA)b1X;O!*N+y<(E=vfriqU!^vm_WbQf!&+hRSR5o=wpghpN0_~y54=zj1N-TMUy ztx4dPQlmKi{=I~bsJW|s(%PEQ?)Fk8U>WvDE~a+)jZo!SWuj}5)>6h7QB2u6O2`vg zauVdi0y**G*+b#l)>ikdkL9KjaPW~vpPH7pF&5G_8a%hLwL}**BbGzV$az9<6wU%Kq;oy*sfr4NOxXd%-w8ATR(bwcBY}20^(i1u+M%HAkgRh_n^kms=VG;DPCTEsB z++(%KrYOH487wt1SzU7HYC(~-m zyywX#UNe=(pwn*E@P2CQf0)zW0=qc(Itx(Hr@SMXD6cW}CeHIvDQBx!bETQ~`|utC zHjXGSIAjrrRLB)-Y~dJ;r?1X+jhgCOM5D7g!~U(Z6)mOVRt1F&bbTB9w1)_P~%!adWyKC*wK<-n9DaA#!=k3bS71m zdr3QqZz5yZ&h6(!N3gbeYehfGY2BsclNyjmWh%82kPjQwgR)PKOM8L0)M^SPy^_GF zv85FVuG$JCf7GBVx0c{2sB8W~;2k(a%>U4sG zd^`}JBdW5cIhWDe%=TorvRI#`)wg%2Xb_h|rn5EA)5=wEWD#MTVd9ejQTd3`C48|# zxyx69i$;i`UA75?Sik+&b&ff8^<5|w{tNRM>b5b2(8qYTpO&sY5qPW=px;L_D|W{< zG33Uy#-n(O%=wtI^z2F4=f#LLkUvA1L14uyH?gJ&EM@G(l6oaroZ6nss;QEjI&SxV z@+wKsS5l*yr?ye2&i6Q-KAHqpNhv_RE9BJR7zfyBtQTo;?SNt>=R7`hZ!tX8l zEc=xB)lB`Ofn{Jwuft`&MCs)MGPG=PU$BHBb2WDn5)}ouIG}vG4kSl8xGC}wrzkhv zn+D3lt{H^aR}9$Ht7xGlvtiIn4$wFiq@VF9(M781|De@myRG3fL0Yz6Kp46^TkP9A zSrp;C#qJFsiXEC&bF^~p33#8G3^T}D>yS}I=N+)u%WLa8Zoz6kKwF_HfJxKMsavPC zgmBBLU_v8>#-u4;>|y!Iuc2B?@$+b2hcGz$5ve19LTdIKhETkJbpx6$?S3;;$kp}* zjrlPe2lIB-^U1G8_); zlW5Okuj=zU_Uaz2rrn|rn|_T^fuCo7olV@|b}UuDbxd@$B{=D>1WH%MJdYo3QO~0d zT2W2c)O~KtR7qV_OxV|1YuWi(J^(+mWSt$!-Ih0^Z9My$MgxaODv&`G>+Lo=And)=}MSiW~x#6MXKva5G^2;TzUZCw< z$&ba>sn(^kT5bfssUxlq1On@&JwV$7MZTBU1HS8bunC?=SJ4Dvkvh;UjieR`Nk44W zpcNg{?5pb}qd7xNdR@Nsns>{T=QVemqsyG1;?DX~so&=NQf437q+Df}g6L>Q4}t6C zV4K%%@rx(RF3bR>E>msJhMg-~ldr4Hg&z6~)sU5+>di^ zvCTz%-E;CamryO7Y*p@3?n>Z9o)oEQq$gm2D`i$KIEOX$sJ|a^W(fD|I5`X~A8D4T zS|TDyV+mtOt=~BIIKhmg(8uT^N?&bi=b3RH$g{cv$fI_&5_pxW{$IlDVgwAF(g!9)9p2kZio8!+3&#!u{cj6@;y)0$jiy~2H3B{+n zIXWV|<9M~|&wRkgIUJ>TV!bWei}{p7A7)Sn)15NpVCv;els~~?MO|D5Erv;Z8XVOg zBD)<}T{a7vF6pKt_E)+*Up=p_t#$+!2doF|t)+Noc#){&9Sa9hUx%oix?oSojvbbz zLaC&!ULF+}<*KRb$?M{~9l4-Z*;u3!QspqY9xvAGKc7}euhcSKv@Q{5wQNr~{+K%! z?71(iHJV8rbhL$+wl0Ql?_4@QKCGy)0qp)#)noUXYqBmse7iPSeuP6Po<4|8iOo?^ z<6Jz=5-i+vS_(5$&%+|Kkk{Z^8z1sfDHWL$d8`ENhuh#1GPo)iiU+goh5>qxzxal; z**=X6-B=BgKsm3djGl#G;{&5lPnK7$F?U+e4wjKE*DUQni$gW=p#^uSF&XO0(W~gn z=7?1q5mSH2C3Km$bi+h@^KMz7uR_6uX6r9;iMPpXc_2G`9*g==Yjxr*?)MhbG7-*2 zD)l}KN%N-Diz3r)x52VLB`tc*o`bD1vPt?|gYI#zQqQki*T(^tvGr?cQIthmk&5v2 zrDnFx(FkqSc0Rj8LOM-+9A&WZej_FoQQ}KI$si|uWr;47VoTOhO9$HRLQ8i`9KH0= z6k}|6=ZePag71xITDAy>)Bav4BZXu~QdnzdN3~j+YUKlk)7AjJl$j%!3;Jq-3aUEI zYn;Zl^kvkkWkd}R>)r2<*~C5uDvn$it503wTw3ePE9Ec+#Q}f?wz)Aky*^cUlDV;mEY&1Z z$wUdITp03NYqb2&+pcXTCQB@_%nbS-v)2N$d5iPx*2ic}J`cwkRYtBid}xt!C1e^N zYI4#e9s=2prjjqF^-rF9m^m7aUmOLcqqr%U5*;$%_bF;d}@JKw+0DhF$4nXOl4E{v~A~d*fwH!#kfziY^q6WW9#22 z*wvGZEDC8Q#lrIVS{}E&dl4~6t!LS0A!Y|ham4Z#8U)l5>0c{ov-D}k5|*fCz0&Vj z;S)f$hGu&UsFfL>>B0ey-ZPy=Wm0vF;UOc#^Drq_11 zpTRVj>x0&_RtzyiBUa%3ME02JB20G9VD*v1U+}>i0DF%-O72BV{17Rh^$*P~N=Vl- zEwZqPP<4a*PatTHgJD=IymdaOjytY@@BOAwxVW)l<=K6s66%TPSsyTup1gAe*kyaL zlq=j5f!0z@;*@l_Z5$!2YiFzA$MQ!3W4M*{=L- ziYVefdF3xkPqE%%JF|d0HijGSd=`2~UURpjqMEc5OZVkh*wx+u($}2Gevbzhvw(Fz zd$Xs5@J+>m-qiM~v}hze$}y-OPsj3C%Ud)MPY4R2%pCbyO=vIu!^OvqsY`%@ z^K9*81SC*Ke0Sk^2~YEwuH&`@%KUB3HnYJQx9XUQ^s#faG_?sVM>dvdM*!u_fJ@6B?kG4zXWLo zyv9I^+k?}*aK)Q9_OnS;@=a-|(VkRGoE{bb)vCUof7tAz24f+S2g}_DN*fHE;mouh+>H{0s42YExPF(XVjTY0 zKni2+2V(T1=l99sL^%1=Nq81WaT&`wLFi1kuqI^g$pJ(5KhQzwU(_2$g7hKKlRc7j z!uqFF?%RE-mIdbm2?Kd7m}gB4w2y>&PT`3hJkjt-MdZZ6hNZI+L3WX8Z0^U<*Y5)i z4yKPH>4G&@+Ly-<`OX(_^j;sbo?naJ5?_68I`$*KIlHs4hs=QJO^wLgAb6m&;1AdfKG+$wq;DJb`` zo!~@iJ3d|hrDTQeO6bH$p}70rkZWJ4C)b<}aSSXYNrOv2Hs@A^7|Q6Qd(S2k3=}5k zBoIcUSx0NGgJ;M0eOLACNWqHchFgw;-fRX-bA@a4vMy>}*d^RzB*8~N2XwlN1X90x zr7isgny>K?{W+~PaKrB}2K1jJG*Z^3Th-|EcGu3gm;3wVwa2$k*7I!m!3|=aX*R{R ziPx!NETJ8_@q!QV)H)oxdw8z-xI0uCQF{#K>h-e1{31Gkq~q*V@%o63gnoPUEh{6{ z_kc&oz*w|i!HZ04^1i6UBC@B;;&c5~LRk6B4Fy%3V?WK5p;Mw&DnjZfC5=8~L<8#@ zbZAV6qnpDKkfCFvK{P&O|5|R%P99#an20mWGk^oe)eq@lTOvmJ^VKb_5BrbT0rI`G$2xRU`Ew8Bjp#=~8*>SkRfjw_C9=hx2Fo4R6+(-iV}8S;d(t_6Cp@64;juGg|i zdRO!1X2$6od>FPSxZlj3yH3N;}!A>9aJg8Y5`vksd zh=<{PscKaTH?oAyqU966XT+jT&8IHMKzE+-DSfkiy0KXnm*XJnNT1O)aP?l94cEnx z_eRdBcSMK@NAyE}2mSZ7mwkHrVAf+44{7%*ZiNgthOT^44t9B#z68LjZ?4G}6Hsm= zLqcx8NEx%+><1@&?Q3{ntfd2i{%xdjRd&|};HM>Ctxs@w(RR^ooVvnYLk;H_-(VQp zfW*JpFgLv*RVCANinTiOHBSRZ`M6Y(AGsqpdVHmr1eyzVVOi7&%$Xtgd@2MYYP3^N{9tkDHk{M;M@&73u0j zVzN3_8XrrTnj^(%9G!&JB^ z{i~PETHYuPU+!ZDq~dbidH|T;FM(BgfrwxCV}M<&lQHYeAJ13Q9cfErYn?)LamTx} zYUh-damac03uvy3Yz8p2TxUZq9)224J5_lAeZ88U)81AuHAKtAtFQZ8X*Vb4tTAak zO_t->&KcxUgiEwr<<|6SYt<@|`=P@(Z4W~watz|5V&=y0<)u|5PR3(rNzHsF)nnDAp<8Zwtj%^`uF~o; zo}G-Y(hn)B*9UkTwnCSk+xq+52)d#n-uJhBOMKm-5^l$FYdB9f-N~+)>vcRqwW670 z59^OK%VGDy0Kz$}3v?cR%L&&%wmV?esMtCw8xXYq*}OOeRG^uQJV~dg4^>PJhDIGg z_ymZPer}ijiWx<*eU%Z(r)E@xVx830Tr7Hahcj`p7xb$5rC=flJ>^cWsa+ldVeh_# zKtrmiAtr|}yH<`hb$jljwTMdfsywyqd5Iin#F%j?gTFtW2v!a|g)wz|oBzvPkxRk@ zUq_o4$@ALI10JWsxb9M7>o{(D zosr^OoD#zF+00e{(c!|pe*5W6%ZYSeh=;TE&EkZZhidp-KWd6jow3bWuwR#z>&9_- zfwZ?*@2)DFxf#RFWHnv62AAu6f5tvt`(xm9;KFlx|81iqoxI*+JFn8h&fbdb57RN4 z!=GhkShFgMwd9Q(o$lEm(d>WTnaf^IHdatp+c|SqR`BplAJS>>-*N_8GxM!g9vdba zd{7EM#H&t=Z96bioqWVIE49vwyqNAYTK~a6I4e31DQgoQPJ)28;`cZXX&R#O zVeyVSa|l0vDF%rJ2L^}W8>O^S>4TW;iaH3cM1z?0y3;boaU@!poy=oWiorksnl@KT zaXrX**aw0y);4hvyW{N0`<4>ca$(00@LCps!~gD-B}$3{hd>;YAOK%UyZAkDT-z!) zOhbLh79EN1b2Ro{Ipo%hNp`-1I;TJv=bDGs^6gH$gnUM0K`muFZ_3ymO0|Z%nzp6_ zXPdz!{>{L{iQHSJW^|&htk#>QSp4mIL!>nPm~=!WLw2%2HK$*vdigRkZIkCE9mZJ6 zduQ30t~HBNRl0qS9~>+$6>2`xx<$ZzcE+I3r5sIyjrA<9yf?zuCA(+JTPWan-%&X9 z_7Tw-FHpDY?5*s9^79ccIJN76cYE0eJu|*kxewWD{zxpSV6we7;qb7a9|tI8yjR?Q zc@Hn&FuyK*NfUkwN^ZDe`DCjC zZ~MXY`B%K&K(j|qvJR8o!O;1rS2%oD!S$&0XRp<*LBtG^c4hCtIg%908}~i*E&gJb zIUsw-19IiCwr9uDiRKjrgWdd!YrD&?+fF>q1J|wvq{ndu@#gMzWB!Tfmdb=CQZXT` z5xEJ}$8QraBG50}rfGm0E*d#J25X&f*bMnEpI!Q8mgw>K$m>@xk3;7~M@npr4w(*r z2n8`!nbsp7KoEQ592(we$%yi#t~5+!hzBS-DxaLnQZrndR8M`hJWX1smOI(Fye`iy zay1x_&sb?|c{$xHvQDvS?I=uVIrV9-_p&*nt=-=$&6n5bGhPAiybg6@;a69Y>%wRF zy9g( z1=F|bMYTA*e{goi{W)9P-qf{n{vi9wc`UIsjIi0YOH=>F?kQNH^>aq!#V<4MWR@E!-#|$hHb-xHH(e%&Y3C(9hOzB z%Xl(J!7cB>zcbp-r6{}>W!d=_^B&I4|N8a(p6^&Ub^P8x0Y0IC9HtGRs=_BmUo2;* z5Zo7IHEZO^wYqbvXJp!|-|1n4Iqk9baB_Q>iK#LdF0lA`(P2z3i@(ZiS!X7`Za*mUpo8J;SA=(AM?W9GQ!6TBxO0 zaW1ak+|J>$&tZeuCd~;Qu1<3^rf)2M*x6>e#<;eqZO>^%A$A497T_xmeHlVWoD-JV zr|WD04_OM_6aH3!-x`4Ym^HFSy57b^{1Z%S)Ta%#)pbz^2K=B*J}(j zHmCMS$I+zsws+r^#M|Y}&{tLORi^T)(U~)w}6BG zF+0%1FeTd_3)HL(vPS21Q~dw}vAR8-9L}o<9h4FBBbOa!;g4pKo_qJ<>nN-T)#s=J zl}$N(A9sR!h_;WXxmRMihwz4+!pIM5(8Qv+Uh%{;Cd2n+ezp*Y-OQvv#ZT4-M@r)r zPLLIlN?CU`wR#q7`AKO@!5>E{oLYUpUBbmFp(aZpRo1?ol{0B))VKTy&6e~9aZhm! zps|{W#*!Vm-p@hUxfx1QZuemH_*~n{F!0mMuO+M7uxq$_-Gi!SYpHFIm2Coz;Ro|P zKVEdGq!%lVLci&q?V6m-*2`9Llv&LEv?Q;wTI#^OYF84+kl>3Ncqh}1?#Ko&PU{;lkgw{g?48gkV`h?wy<@JT4Rt+V(4vnW1!dy9 zO^6d+Hv{1xQ4+CGUKwV@A&Kr_ZJ9nS^%XwP@hB6JajGxc_7z=$Z%grzp| zGfq)z)&J7+om`*gyL?4W)q^BIPcU0?0ZSROl9_4s2&k$03aHkoP;;v<)Ktvy`6Hac z`}|T?&DcishnL$ zTw0cfD&e!GC8~x9ZK~|jM8Bqvu6m}&Bs-xO2k3RaHv#Y$qNRn57~yP|AwT;e(NS14 z?UqFmTZ5$`HTUS4&hm_{ZtOaXO6k=LD z=R~eM2LAXIf?0$gwMi9V_)SKBBIz(oNR8i0$l^z$Ev=fVs-eoUzyXUmM064Vz@JDkV*IfnHUT`?n9=(Evxz_;M3*?ttnsbkC5)x$A5;_yWkv#V=*94S;Wf2a&v z(i!b3RT%)CM_fUHZdGitIdHWP*X(Eu*EPZdsZ_6}xx8fi<`d_zg`WmE#OPo`%5B=~ z${1;G*Wam6r%FvFZc?*kt=TE*a=qxV$&f(wy+^QdT_#f39(2Idf*wdKahmQVkIU4S5_3nYBn*%8W&J z=$!_u7TAK>d#A@~ zN&Uw7*b?#cJY3TdZ`QidA&H2x9Io8xtp_w}9A(&ax5y6jRe#eW>whE zlihsh)@!IycEqr{vKp#eLYXu~r0CMPZv)gkb719a=Kf?X47AGm%Hib=A9Z>;wy5i6 zR;lfCq*U>GU^>B~xHTiYdR3VTGrLt0Ng%L3afTIREqzeuV)hoknWU!4sm&?Rq&HRV zp}8d-$0iZZrYT#o%?GBw%TO~9W@8CrRn9lWnZU!a*mU55k`bOQwqr&jBN>BHkKXWO zlIv9^7c?tCZ637@1Sf~7N+`NS$_ITP#U?fCkJJ*=9VeRdh!5(Tk1eAh-{4j|q06_7 zCa3kKmrgTAn<2tCICy4?G>_U~k2YZ}j;q*?D~HEOFb3McOVFD$8U=+7I~X$w5K4a9 zF>NCl7V%C}`5{cmLu&G8(nXU(NBzjCgzg}mdJErZS#Q;~s^J6{@@2d0uALqM1jI3S z2duYQqgRo5A8R@n?NraM3xY$E)u-&J@(N~`avc}y=?fU~bA(){B(>ssB&WUMs4wr; zTP7TOJ{0d4ls@+z>{Gji%9pHN)Lkl8F{gu!xu7nG1B=M<@h>08uG%_b#fK*<(JAcb zWNR5caYovtj&z~KUNkvWvmHmS!s&TqPDQB0IC23;{Z8jU{`s+I2A0pP*)Vj>3!w69 zsF3~b>dNGqPUY;l?sB^25>DO22V}+f`&omrFYY_`4vB4$9=-23rf|n=%1I(#P)Wlq z_XcVNkz&_brhFzGl58p0v%6J`QSf68AlBcR1u;+g5UY>;w!wq$y!(CA*(&FVdsB<9oG&U}BSd;v)UUYWH&S7VHOzF|_*OZz^a|$b{ zbt3d_{mXaGAa~>XN}ZAtoRUbcY!!Z=0%;qKTnRNI>)fX@t*`Aqlk8_4YlgE95XsU5&p@(3WPDebd`iHNFzm znRyfpj`DZ9oSdTVcXcvmJl>Z{LcfE6)mg(pxP+0_0HsW4*8#MefEL50)IQc06Obcw z0uADSxHGDA`SV`;=@k*v5C{qTFu z`b4IWAv*+Xl0p0a`7fiU#|;BuAqyeF$-f^KJ9&i7^-yqxEphPvbtU4gbkgrNX}p%F z{{jYDTh6sr#puy`L!Q5ZPT4LMS6AOq46J6(U*c}KM1%M?eybu5~*cuR}pmk4h&EDW6L07(K_>Ilo*SG;2 zU@yP}1e-6|CG$zQgzyN+*IASdZ0ENY`J{rMQf+=!69CJwzVV zfVt%?8=;mT|A-ag80k}Kg{o03e6ZZ?Q~7(@kWiie_{fD4kd^qw30LJmKQt(P7d zlC>nzx^Avwx@)%kKHPD%EDuL2wt;e{7(SS}s^pT=MJFVHx|rvy(;s~*WpX>@nOco? zU(f0cgwjTchW68@Oc*9LjlG75fi8frki&OlFP~Coc1LBnsUemQUW3mp$+K>`KbxbEwU$l)kfY;zs#Z-0rtGI`L)njEM7lKm>yW9*F6b|pJ_b;V4bf`y( z5}a(U5AeLH%;ux-Cdz!eKAY~wrOrCtX`HNQ|8?1wa@z4t9y)O{MdNxelkfGm5nlu> zOpQ>IN#_EcP|(c}hwMrJ$m*153xgKQ_fDr#c9wn0@`ze2nlo$W-?wWrWfpj{GgQ#& zFI;36qP0enrl4C1y(Yr|!g`EJoK%Lw`*Q{CNQ7`~`%vDkT)gMM=7|CUZ81;R&$FN7 z!{P7EbNyOlB5vWv#UbT#1VH`HVC_Cn4uj^3wpG+|26Z(AoHV?2zyo}e>VT+B;@u54 zNgglt6{^1|4G12Cm4i<^q>)IKT`3bSjwDYP3c0A^Fc%m=WmpJzG$@X6gBL>(8!)Lq z(^c~obeZUSH~Ziu0HU8$P_O_gp@zLbV)1Gq=C(rT(%fCHA`J3 zZ04h~8MlcQtL5*z@!`HV$X1b#>Hk#b;y3g5zZ84;XPJk8Eps9H|3|VnC*yA&|Gx?Q zf1|$ti^+c)`cK0_^(}t8=HKdD{3i$eU8CUtb#)6CBJKYU=>3~x4Fb^q0qy<+;QS}8 z`)_WyE{IzVV$J?nT(>R}s91p$4sx**In=(vBtwts`3|Bndh-xP6B1riWO_YZXV zKUvQIMtcT9j{iOc#8v)}$mZX;X6=9Sn*ZkBg3ieQ8PWW^m%ll?pu_$Nr~VVc4626m zKS416E2jGYhF<>N_9ZSQhyDIbn*Ezsi# zm?L?qgxCA972EnXVcH@p4e#oid|5#AgKUyN!bViit_G{unf15an&VPj0nPrKhg&@v zoeyW@>~#DonMt15T^^^86@Uxus3eE6kIL?69?`yR&-ueR!(;KU&h=lKELBGv*GEOV zkay4{eVquThb*%NTqp8ewRSSV&HB7yr&>rqQ=4r<+b8czh$_nE6~lgk`OJ?8!KjEdUo@^uc%Sl=V2w7(aD{&`V^x#nwO)Yui6@_8!omcE9L_R0KKaqDJ~y%B<%trsa z3M&QEBYM-pgFCl(;TOc@kZkfpJBq!vXXU|H%$q#SxPLJ{wKUJSe@E;0;AVXD=o?hD zrdl!D!^f%@;)80A?f0N>WW6<<3%~3FLVSa*{I4y=24ube&&u^r0c?~;_+f$N5}Ndo zp~w70lK7+VDX_uxtQ}oA_;j_yL^C_X_WfuHs`Bzg0d8{;I$UXhez4!}G+Q6%5SU=f zg@JL|riJ;6cD$er`!*OIGgjWeE7@<1%KuYz9R>!L-&d+ey7m$8s`EH_*{p{Ok0j(3Ai0`=$xUbV14r#d`;Gc;hi|M=f2T3)%lBl}8 zqsf0dt+$MF7OjktF_b-qtj2{p$QtMhfSk*wJV#AK1ysX<<9%o+4;Y+=2nd)G?YqI= zVjUCvfK5iqhVwz?jBIrzw4?ZVhBN2*%E|51OaJ{Mk${Tb(@`H(fFu(}bS6xqDK^Ze zTP*zG%hE}(UX5&Y0-N)Dp}984*k;*~A`J{;w&(Kwlze_6`6+Q$2eEV1cCc{(4Q%!tkdo0RbDJBnC!C0w8ZUF#H+;H9b%s z@H*fcPSHkGTu55a_8&yy|HK7O|3|d6KgImMSp0*f9Jp9iF*5|10*S=wnHhgAAOH9F z|62<%0G~DRx%_=}=HmK;?w#Q8DZo&AX2!-w_D0qQM!;sLBcL_;-{XJV;QtRgz{oD5 z1sv;We~m+5>}?Dj2!PrP)J`_wcbh-HSegKH5d1kQ16L3K7#7)nkB9fS&~uE;EWbwA z|7mh$VrBsjuK!pQpQO42Q0G$n+uJ##s28HBhvTRjp~eY>a74OvS$n$7i8L@=O+Sth zm&WA7JL%(SBao10Qp?EOuzi;%T(8MD8HYKkOSWoW&>ptpkP)-1mKLZVq%ljbYnJ{b z$=!yi4*%{z&wr{OX*YpSO3JdRClkt)wP$^084y)aIi1_o?-)3IK^atGKi;IuNGZ-V=-N^`DzYzJJZSMT7cwd)va3GRo!SRnyP7V%vUVQfFKpwfgNr}2|k~fk0=D-r> zn)QzA7$)Qh8cvY-Z_3wd>MT~O9AVc@L4ucnH(olvuV+9fcn4x+Fz1i=Jajt_vI;_H zag|4{aaFnxRlpx()7ETqRc-a^{_XO{d&>j_T3d`D85KS&%*nFLO%5DF>|F)uj%v6* zGMB3Fi8u9;H3l<5C&8M&OFKSTFjCqPntr%rcx_WeP|Bd#J6LTINt*c#kcv*S%NfzD zhpwxJr4rG;mx*WYx5nq&%YBL{T#;K=XKrVa`Rsm-ygrU`OJ{tCRfz8nS**9GlvI$M zmbbL?$)LxSs&!Z^QJcCYHCQa11|!Cglv@|IQR9PG9ZYucL?mltA6Gx-~3#=gY*vZTV;EB?3c7OVJmce%Ep2(X9tAuogu zXluyJx$QS=H|Hj(_H3lckg^V>LiGF|yvzqSDFXW4Y7j&)#bEpdbdhsj7q9?YJcySU z_tnJ$P+}L*WV&0PIr7mbE+owZ`DmGs|_|1LDRbs4e&*R?d?H(J7 z0jA?DAH^V47s#c`5D$$&(yCNSnyB(x%3nbfpM&hRH70W!CZy^`1hz1NwHIah_~<`dmW{A?Jq_UKL9B8NGk> zJz^W(sjw9YlR<2I(i=9U-3RYIV08CePT$k5rv=cc^Pva3c^&3ESG|CCZtbIG8-y#g zw63>JEKeUVQlzx36|C+n7sT#)X@vPOT3ybc>(p5eKhnD4_J#I@4GOF4TWK9QZxwth zHCQq+L=TKErPN$`Z*>~_LO2d);hf%|Yk}d?@!XAFMJUvgTXiorymGseSKyXc*cf2m zZt`0)U9v17&WoIrU?gBx%run@|MmAeKncEo-;lQ@Bi&z`bl+BJMH5#ofoyFOJKyuU z(no2ty5xOCwZsJi##L8$%a;VqkXKuWUB6UUHPRx&k991(66L+8c5xz}b|p=Dz#v-6 zb2C%|6xY~SYnvM|omwA!1r z&pIoq9!hOZoIjNw4&B^}x20P--FTNMGyA8rug}vAxNj%%4i9ke?@Jb`XYcwgW}h}| zKN}Dq)3e-{{(N+B%{%Dvzmf_$@XeQyAR>PkQd`IHb0{tg{YFaf=F|7S-Dx$kiOXzD zr?M=fORD72!;#P?&8@6~z{rABBeEgxNE5Xcp;WcTA&G25|02g2QLhKu+Kw>ta6llUHMH38C3V66$&X1P`} zZg{@(ZYWg+k-P$H!_+}SBA2|GXFG>VjB-i}V(^^9L_6;eKxd}Gb#r#`qM1@@#!t|9 zt2SB@3Ds=p)!qjkpkdKCeqh<73fd>xgPL_qompOebW44PG;lYsP)!!GVey<&DxC? zH9L1BBGVjVg+)vg{OzSEJ^cOo9*BGTO=2U)KjWt>r8&e2r{xCh^DAcX@Jb*BV<5`Y zmI|UC7LwIxi%pyHTk|FOFUut7O+PQ2_$IPGgG`?@jY3W?Z+s&n6tOCE3jY#h?%>&; z_^CK>UlIR`E9YlAH?dizv&D0y^Ajn3e|Nq|Tc93gOIM#}{>te9XFkb^Vtco(OL5_X zt&7DfS9j)h>7iPruwZ587~x<6o#IABkBsj};xtqe9Ea*%tZ+&Dv|#8rf32rJZ>_86 zs;hW+Pl^*fThq#|+FF#I``Lbc-q*0LfN*?H?dw8ZMC#jv%gcfMQJ$yxI&6>IWRE*q zX!H`Ud|hxq)uPAZ=g3#kypsw- zut^eG-O&dvB;e9uXHQ?dH|$$+2%mNMxsiFYsK^>LB=t>*8~jjZPt`}aWhAezly6W& zNi{}nP-D*;3Hn4S5F;Kqg_U*9l6B37n?c(p{A9|D&?W3{O3Mw4>5+On6>U4MvJb)y z%IXnxI~~7~S57RU!ynT|a!4fcgq+JAC=XYg1%ERmA@@k9IU^QgO0S8QMMv!$QkF@l z^^UbxE@snqTB#;b-*%cgQ2t{U06l9a_!BdcMCN>3!PngJ_MW}wuDzEZz)GodV#g6;M&V+{3uzS&hljb*X-vcmiwovAqAZ;-0~-lk zkB)M8rU)r$7Y>KZY0jwP4Ue)?Rts@0dF4wo>cP}h)_ng!Jn<{zuC7JSCrAI z6dSQWjjv9(i_p%}Q+K6_DovcQ#oGXo@!%(mik1ru4^{ug)N^@FB0RzPm~%2-FD5&^M}-TFTn@50N+nR!2-A7o(rghe&j$Tp67q7Y!<7 z=d?arXBAKg_ zi8*v-924yn4WRLxB>(uCBI22-A!(--zcn0G+1cSP0;Pg^#}%q;7FQ}_zN7PXl?=Umq3wCo3Xtqz+oH74 zS5QN4{+hKHh~Y(q4dD*=zDt7`wnDBxeQFckD9q}D#Q8;Qc17FUPtXB(ty+QR{pxuW z1d}V6Vu13k+KbZ>S7})cG^N;*D6PaI(>SGBXpTXoaxsM1xE}C}v?u@@1TnZEY3>DI zi=v^eitZJ1V;K=~r)E&DAUvWoTTd&|vTwtai=~QxwQh!>(yVQuN*e9tRdzEu<~R4+ z3q>PBCFt*%y{>kI&_=isj&{fL{sP$xD^2e#7?O%PCsrIKgC(^NY)zr%=_Z?#=14hh zBke%~XP^MS(xO%oJg0x9L?jswuexQK_$JL)&i(Xqz_ZkabnxdJ}gJ`>rhJHjS zJ!)mv0N$~lo=GXTcNe|Bpn!zUdi(WWx=_f5z^+XYSW@{0w3dppqDysY#SvpvqMowT zTiXI;hVt35e%f2#5)_k8wun*|c;$}{86++x#l%*|iHNL0=2;}ukut@8uJPYtX%TrW zb+EovW%X_nluvOT-p$yWGT}%7V<>VM8eFIJD&Tug)3zrf21KQzE^Nz|Nf~`yi-E%? zRe_A6ge}f+evVYrn(wgG-&JC^di+@0b{wxG6CgMYHq5YaT;*Lwl|1Nl@bJnE+KW1X z*r>TM8WgI3{Pl3^O7<-3p=!nKbntp=%2o{J$ZBDTq+HXkIw8$S!bWAIqJbl$ZopjF zZ8_heN#7y;Vv~)Q;a2VEl?Ypp)DP_{eegvJ9~F{NI-#oYc1G8}nVAe-=pRwp)lfNFN!E)}ta zNWbJ)*5Ihui>RQ(L~m@whu(k-;koPWo0L*$msqjv%yzqFr>n_X3U03^KTh?LgR;^{m2N8kEV2-@EWnHwD{T)rGsgezs{AexwLP_m|8f^%>fM|8j( z+%9!-CrP~75f^m!s$ zn)S23U6}%#a=s#-d}dBp?n7!J zI>qW5;gxCt)+Rn;^yC;CJ~}Z5xVWrP(WfBU(yL_pHMFTnZRDSJ4w>i|=s6<>2bOi` z7+)StStw1w4=$WjZS_P(Mke%_2p~~8Zk(}&UqH8xe1!NApm(Nm^>FKbqqdUiO16H6 zP<~x9^s_-aXc+y%b_GhY>_`Dy-bms%CsS(cEY`1{X*)gx>%j6xge@D#r2K=4#V^OX zMRbajmd5PNP<+l0aE%>x>%kYb>PQh))=nbMDn)$&Y!U>4DQT}Z;pPW!Sg+?z*fnmo z4QfJM=Z~;9FWzWvd-7;)cTs4sJu|R2a0mCkU+&v@)4T3u<*|e@U)_HA{r=PYE)=}a z2JH#-U^q87T(Uq^UEI@hs;a6|qL5$I)L8y{O}c(1{X(uoMMd=rb`I*pI zMqFSidI19+#|yK6y&i!=Gg{<@vjB>m{^VWsYW9{=1I+UVjt&iVWc=rCp~2uVKEa&2q)7Wj2ix{psHY0TG&YkpMa&vGoV0@U>*Il zGAQq&2ICULySlpcIifm>lvMsouY_&biR<~RX7wN|D}#S!YWnsD&Ml?q`|qp)zfoBH z13~`>F#RV`{U?|&`LAF;wSbMKAu!Kh$oxN;N?2HESlLtL zw0RF5yx$#0qtXu5DKtd;i1m7EUk+lzNswb-ieXMoYtJJY(+qNq#E}`wLldHjmDVc5556_ym+1j?SQshN%GAGECR=FC_Iuu)ym4nNCo#$w z5-R?{B}C@2m!Ll>YxM+?9vGQQf0YN)hFO7 z!4hW`+JQWofV{~yLL4M)sqbUi)7^;$%Qn+XLI{egU$x@+zAh7yl+QHj6Iy}1X`E$B z^{BPIs*IjP6i*dIQrgNaAv?8Ib=ilr4`H2!EQ{6UDJxHECeI#gBl&NujSl1cV4xsy z@E`(D+>%8CtzLiRMbsTW|KBBb-4389@P}TdBD8w|C&r{qC04%FK1rh5=wK)eHk*QS>i1Tk^d5HpG)Gqw&GW3OU)>g~u1Kz7^^T>`xk zKy-4qI=n&h#(jfqx~c}N-)c}mtm-vE_{Jm3e?Ga~v7J@Y_no z_j^9FwwmTmk@w77zfvD`eh%U-=VmA2kt`A}z z%$or|=NK2!=4^aLfk-^y;fj$VJ$zjiU~4|Twy=m@gf7V->_SWxkzmcxHl3vR6q-hu z3)QwivhIzckV#P)7S^s| zKk~E087diJ!XV3i5YIbzI!X4ywa=bs!(gWHq`njWfbeKUllo4--b(%LbqS;XK5Lc# zY`tqfRQBpp ziv=0#&m2^!Y^6CLak_U&EihEp)@n0RSf&7SqAFxn;Zcd! z)rXg2t!QQqaPf+mchMci6q4%iQJlfDe0)A%i3fe_@AF)I}EFSfzBP`dEpw=R&|FuHr_+8%>bTbnR+Dz}E6p3_46wHc}n4DO>oMBuqb z;l^IK-vFV4L%5 zegN!wjEn31Yap&_$aui{62t2b)+v7Xp5fP@{^R#{^L}el7vqLs)%!dc5FIZq3-Hqy zDXa2R8t%l?PS7vJ@$LYw^J&P4Nw#-o)0Xw2&1t3v%`#hMppQLUp0`3Dc~CMVp6vDv zlq$gW=k4x2JhBs+=reApDErxa zm{d_GVpvO4f0pgdE^J!1|pWg$>HCCEKX$32bp zC7L7>HRd3a6h_FN0#{^v1fF{!G15B>1?fA2cvO$mDJl9}`NwB`PiIhDTO}EYlMjwet;#UXr}qKKoRymTp?WHffN7x=O}_+iclM&yZiEOz0#elwQKVsZ-v#X z2?0rKLt71R>q$EOjEpT>rW}FUFS&-u;$l86;-^tw8DC9M$<$B|@vNHV5X9qR@K)wk9emx*H@^dScJ)8p-Ss04)j;S(?pWT`6>*qD=OjeV8 zn6D4)fi%^tW+hZY`Krnz5&5gQvVH;rGO7pNv6OT2bMnthujZ;1(y!7H5Ek6iaQW^Q zx#bS6;o5sN^TJLaoY`U4$EOnP=KiG{HP_Df(!69k{XE1D3t zWo z@D}Djg|I2 zUfRDWvDPPc*g@L1(7rK7F? z=U%7={ppfc)%YWTU(Jq&O`0&d?|_2^B(lDkhQ+eaS1vu-ve9;SgU_cf^4y zj|>i$^ab=f1lqr`f2*JMO<{L@|LC@)&T46Dbwv8QHk4kMW~y;{3BES3d7(JB5W(x2 z?a}c4zOfu3o$F$xnp|dxcCh_rCRKJM_FK7EcKL(#djemqff1vS01)U4(20|SNZOD< z43rxvWO|xomJ`N=8yPx|d${Za^o=24r;sD$jt}DS#Mu0TC)lrU^H>I)dH$ZmpkO0Vkvv)vb)P^z#%@z;oLi{^RT1S*zo^1iA>~0Q zoR^&&Ec*e+G+dyt8dnX5BMiItXB5*AMVC&=JUgQ%29Lzd5mn>QjPYt?HhvF)~D>Qsf*C@%8ZNlgn_wJMG7pd%FjNO}%&9?QMV;^XV2z+OGqXl7=+M z3DoryCT5(fdXk1|ou(kgBE^Dv?<&+wbeWS!K$NR{E$OhOM6x?=SPot3stCU>>M!?e zjF9FT3{6%sfyXMU(hCPHcM zc^N&%WPLLpt^$7Hc;JzjTzAf?JL{OnA5~l9+tZ@mido6QF^c#y9#dLW!uwFoP8D60 z@E%LS(MJS4+Wo7}&nosLsUm%e2~)+Df>|)r>Rl6unncCkn2gz?rUS|`)+4~V-%9sL z+~}l{Ji5YnnfycLRy@9RX5QrN@tn_j5Utv?u0Eh0KY$4a1HTe{_-Fmf+19L?z_5M^ z3;iWbm#L_!+Y*Y-WORHtfw3@{qSI2@F}^qJ_|3#(0Y{QdiJnxIs_1mr6@(%+0&-jOuz=(K>x%i4BAOt5w=HVP;oZPrcOeKaI(6m% zO;Nk5W*_@?i7kgxD;Fy!0V_}_IqR>A+dC+=GVtOP@cv5S{grZgrNq5`0pjxOK3u<~ ztX~v7!i&q_s#n)qhR292Nx?+r99Dnj0nz#SY#C1b>zc1TO6|2ZXce-FPgF##!u>F8M52&fs^nP}*k7}!|}SQ+VQfFCdd zfjBy5dKy+{;8v9Xg1`OY`2IVX@tc$T?%GX75>4FlUh`OIrgjt2YCA%m>Dr|E~EWRjSlAO(Iw>+F85u*UBo z%l@cp5O}e^L@TpQb4>UxB9SoqgZ7M(Rn5ECQ;~&?0GW?Fw%8`*D0uT0-C0;Mah|9v z_NLTZ!dfiD+ja%gtW{Lt>-h6WgUygmtjs*N_lHAxJHs8Nao^ zUh1zt0k|LIRBcgn_RK|;fP2Y^kBwzs)SxGgaG+Y9H?naq)|zwkN>4?U|7-tY_zm3r zU61~*62B7v39b7#`tu*dbbq*B0ySIFM#FvwRb$EiaaD zFL&irIvzKNvrlbzhpjqFb5BL!V0{0mOYMs@>Up5hor=ydAVOK2E{@I`);LY(GQ!$2aeoo zX|zGALTy^K>JC4U1c8NFUr*gywf^`R&F4XEdsKG&P-=A+*Xo79_Y>@sjZONRxn|i5 zBi}B_fVoc7F{3d>-|MM1JaX@VUWVOyYdFh*WmoNs&R-CbH4d^-hSBdLdX)G0@iT>7s$t| z$l`Yf7#-C5+8{_!RA>|E_K9|UAj~k#p#Ha8lHucYpD4gPxl3}+N2pDf;yjqB#JiPkK_as-a$A+_HMhf>zTV}o01?=xZ2fs8%D-H*r76` zm0bPwIO|=D=kyYj3dRkZwlM6Ow0@yqQ2UBt9!f)y53ylvukKImhX-0gNtVGTe7aX8 zilS(?p&h$O^wKKC*r;$gQD9ea9)GiKx&JVMW{Gv*V}QAU&M# zUit@h(w=w35i&e|`#W~rtqfn5jE!nb!e^^>VJPeUVKE2N?6E|{x$V35%EHNSgybl$^08kbX#>rqOs($$zB7D?JocIicYI^+mOz@BjoZ)qW4~B6a)eqh%rQ*!EHvLk-(~Q2tbf zPD$Jr<`0Kz3q$h_E?y4>BMX;uGShNYG(bLrk`CVr3jzAW_gLNmn?292QG+)Sl#KL= zl}t)Ot4y+6l?re}7Bj+-LG*3Wmz5`?sxNYQ1f_;BYUmD8W1VLgRIB~@_f5QE@52L0 z&^xsCJI#0Y>(2>q*P&&Q(-H!GTsFgVzBRx_h}JlZv1ZFNQU7GT+a!J8rPTn(TtHY{ z5z!de`YrbE4Wd{scAnovKkfOlI--0 zlv&6C$uoq5*gG@w6Sm-VUH>U;XAo5=Zk!*FCvF!wYEr?0X{`wd`r~@##z+_Hb#UwT z-GqT7D8`^^9oWm^OOw82Q4tuL5l0oVF19~XgvzGE?{pF3bYX67uNsQy&3@t_mP@j%wg@}TRKdeZgxOY?;sye28Bn2Z)v&OvZ;dru*jORlS8 z_!;LK0>^d=)t1O7jSrh)-!4Zmt3MJHlN%An+6{-saB9o8TQGtURqYy!YakCu&5Lr4 zRYf0#*A2Zrb@n5BAQDTsU6AH--xlOXyF)J||o2i7CPXF2J1{#c@ZT`unLmFJVh1^(%H zoorGWqI7tu2PpSn1d+NU)ZMU9zlb6k5RGL8$oYt0)0xu;;R)c5M8R$p)R?pJNk^RG>@s40E9O!Rv7iP6<}KCdk|P>V`Z$Dc0TvFFYoM z`T|>ROZakFo^6Nt0=s8``0_riA<#33Y$R7_6Rc)}){ap9AU?q-Q&{8T`+1KPL%(>f)}1)ZSC9gd9zX z-))LE1fBNY1V7XNoF2CY_Y~pnmbHa@fj_dPe1T`()HyJ@r?AdW&qA%AmbF7Y%}Ceh z_LSl69leKqLF4Uip9E(16zATX?|OWbS13vTw(ggtv@T38lTycT^cwo&HN)9}IUz=WWQH*RCWL@#=uLwHr z+Hy%c?LrNPUzY`7vaEl21Z_fVMDfbQUl+Fop6*=IJiYVJxjgW_I^eE700wX#_{ry( zk34C8A=e(yD72Gz=j1I?Yu${&&y3ocXB%oQB5U2ombqt}?7}<|a|g{XpiBGB^WRwy zn(yIOb2u-ERwlAf->&pYyow zYuxiiHdmy#`K_?=?Qq6`UrQhJ(|VxdZ%96p@$~TnPn~nu(qDrG0h~PDdQG_Q7YHli z8AQDOa!t58>l#f6-1p(u-8?w?OBML!`z$ye;!OyxY~pJj@%z~}>y%#}p%C9su%0s$ zUL)WT!F%8az)0%!qjXKqT_9{69I;72JaV*=er`fvqqxMHoGN9L0RSjvka0Z_$b-__ z{CT2;bOZyj4lFXNuAfX~uobwSq?C+=UDeFQ?d0@2_n*1W3YH_;iL0u)60PH7BGYyO zIg1LGgFqf#66|Prw^Ze9Wf(3XnXW5|Zn@d=kD^PC6>Db&Ps1gYW?bFsUcpn45&FcG zHO@~b?n?)2OJnqAlU^u!-n37!3OcOD;NZ1{ zytKNvPzlEz57lhz?S-TrhVJIT;&^Iz*qh6;uCxGY4*7T3Q|32q`;-2q}&?CNg!^V{HSZ zHcn1u4v?qGKiO={HR7bDrEj0PR7_Iy%UW|;c+0IXdY-xTX$QvP<|w8>tf)hbl0!6% zc#kleIc$>3@0+wQ_y^VJP75_=9@lmbk4r4BW@>Beq~YVWQP0N}l@na~v*kOz+iUA6 zqR<9y`3<}kZfj;(8oR?*=vpg9oJOYAkKog9Ut{9LB)YHrn)YSlk2@DYy(BxB$I&~k zMP$7P%VK(;B>-c^iX0KmLEC%GQrwP;-s2eDWn(tT1k`D(;FT1mk!XY(&rntBXN5Kg z$0c_ShnyN58qEibA>1^R(~X&#nGV8QA(Y;QVW3ls(&=r39}0BV(2zA22NRyT;+Me> z^I0ELtQ8ah4(0$-Od2PrPMvitSUT#< zw9!fK!Rl>pi){BtB%6zdtE)7{wr%ovGsKT?p;@Eyb39~xmT$Uu&*!h+xp|R}4AOdG zc2!k*H*P{Ijs(5Q>6lmtCurPdR%zb*2BxXS^0H z9h@?`j+7OB&$BJZsCU&S1J#G=_zWdhD^HzetL{C@!`YFi2o<>VP9v~%3JMC9)ny=T zn&o~ljFHB4nFOrtqX~->y>Fpe;`~eul4SzBvj8md+N00)H$D=N^Jt!BiN}sE(Y8*_ z9S5|f0XbU$PNYf(~2rN;<%^g!2y~8L2<0cN!=f)RjfbfD353c zbB_m3?em0V3^j!4lZb+q%ReP*v9qI)5)+qL&Y17$Gq4`sTrRfKlPYOfYf(^0K-Ezy zu{?8$pAP*X3jSI2DUPPJ?klQC_)MlDnqEl3E;ELi9okM6NkBA*5MCII%ItB<#Kbbjeq1B0%Ek5MrK%;r(-D z7<*$C<49i&nyYk{wz1>F2}=?#TE2hL4$9R$A?d>dtUPq;f zkWbO%0kL6wtb?v_O-vlQcC%X80a>O^&&(*o>|)9ym?AMLaww#Xlv!+9qH59Ty^FE2 z=yD?4zOi9$l8tr(?r(?p>rmxXycK#UfL_| zPzv3p*S-xxI7M1FJrrtTSAOkHt~zYrkS@*TvUnM}p`3ejc>zBHKdLWLdC(z1PnMd> zk|sN71Re{(V;uON5qPAtBu+Ry<-9XXc4!nvAECJ|~s*Oc%e5P0ZPRU#uu^96KaYDsPsk)Ko5Y(W8}zI{XqJ z{y_ogez@7LXh6|!B&-WrZNh#*%c?P4Ppx3T5I5LH+u+Er3woqeKp(fpnGUfSqh+vU zDBlp#;E8w7zSSHy(rPqH9eT7o%F0+q`brAF7<9>6+nSrIH7x;4d#%&3+f|EcAM2b< z@^IhneAH|IrW5EzyrAnS25M1t?K?}tZOtqmp(Z_bR>E;0XAyNB$Si&t&LB=H64~-h zLa}E4!}FvllElJ)+i6ye#-jE*hgh;{j$X%}BRH2iTmvVxJBgyzFp-2*1CAtQmJ~hd zWw&rwV{{qzO8tt3(V}jVGcJ&ow1tt`wZzYz zt~JyiB48gte|X${M(X4WBCRBe>xoi*E5r5ZwDi+_le>jn=F&EcQM0r)7-;|8B^zORrE<{AeK^|!xaND+ z&|7jJP{B4M7)jNy!}siI4|Oh~@odMNF{?#Ic94)2n7*-D0tmx+&?{(7;hNpgL;!F# zLFMnywTcy_&rbVv7Co>xVyR^UshN|C1&pD6u;}Er{9!{ylLbsTaxkKS>flO0 zFq;ne+vU8!LVpK3z4P0JsvO^Vs;bRe6n!68u_`N4k7rtJ4%cZFZgi*q3}}|lCAL?s z?=_Y*mK1oZ3a*dJI;Cgndr+LqHFP!|3tO=na=Zpv_IY;7(Qj<$Lw1Q(x6*TulHM1W zMpux=$R5N})F?5p?bTPTcAG|N#pb9LHbKOeuF_GDrYUe{yU~m9!cz(=@7`m~_w|ra zZ-vx%Bn9$IH)N~yh7{Kq)X#^&1`)V0h;HZ1TMm--s%_RSNCXd4N;JG1_sp(dcQf(m zPMB!(hsneYG?|%eS9@yH8C_A+jH04xKdfH!Pcb|TqtzbTHW@1zefJG@9r0o*GDzos z1-o?2m{DVtKXtSyS_#*p23Dqddv2dH#{>J}W7S&Lq*8_Vv|CRB#=2&#$Pcpo3e#)D4+ck##dC^E>#0jE%wMsKdRJCAd2DJMhnO;MKeshC zwKsjw2v*oS59W-yTz3?f6fvR!xQ&+~-blq2rzJXj8ubF~q8p_o#wV|q)|S-dV^uol zoUVB$)}Xe=ixSza^0G8FEt3)J=+J)bCNJ!WT3yR?tI~BTHvKAsEoP7p|$0Y ztE{5xJ~u4Uk@4-Q6m{t;faz|&*7u8ZK8NRwB8MNZ1`{e^uS>>T?kMWPqzvhuqYRN& z+JfC>8MnHPi|IHmrnyj3QE_iQx}UtI(7rLZ3(4}YMF3M^thPgSX=;=_?O)By5)fGK zk%p%AhtK3DC7vomBl8J-}?}8V5L>3>2 zRAjwz*h9b2(2NyI&v{Davwie#ttmL2S|GLM9OZ4&h?3IkC&~fej&LkF<-F^zs;SH1 z%gZSJ6I8o(fz?(lT@#T zOPE;6!EMNPoJ{6sS+41e^ZUxJ;(kTm2@JP3cDk_2vDW=2%A~xy9T0dAY+c^<8!=Iu zyvO0z_k#wsozdgchF4xK-Hz{`u^9b@PDyoQ{bQ$laK`v88GWh26#&P*uYq|;i z)ne)3?1ySvM|%sVvdfZ`N?p2WyglvBH38UQyV|u#12Rh}W_*<6tXzQ!7YA61WI?qs z^)>1CEAkJ$u&o7?%6wL6l-nr#cR(GxUH^={Z3J09gWCGz&aXaa=1KS>;Yv~^mVsCj zW|xK4y?3<;ceHggd{=wtA+)?ngl^YM zW>yx7FrnsYZvSbaPHMW>=)xUWlm2a}it(76(a)0bFA=m}*Uu=~7@FBPl!ua?N=~Rt-V=6kWS7Q@aclD@F3%T`ct)~3@#XlDmhGC`prZx{Q zV;yNbcCPR{{BmC}==K4=8x0;D`m=T|;ks?%1ptaBSlr=#Hf3oca6ps zA_udeW#R{ZIdnzL>ml&xRC9&c|HIo`$JNngd)|-)4-(uVxXZykxI=JvcMlE;!QI_G z!QI{6-GXay*D3Nm-P7Io&b;^iXFfn3tJ<~pZ?ChfC-&#g zPRZePPRlZ|Is{|H!AbiCVfbzt)-A)LJ*~7m{=|v(#NfQ$L(59f;MJ`2&^keZ!?>MZ z6!>2qIwkal|stf1XmUV|Pzd z;d8P+y_h-LzTeXpNO0XbcX)+{h8%U~B1PJ>ZA8WvbAJ_rs5RB>lgm&g*pg);_Rv;L zxKK%71b(IZg;9hEBkY+(W;z7z2k!dxaqe65hPphgp5Ulj{KA=2`$ih)@i_G1L1|d* zwu*O7D~=a2*q(mYvA5xMCt@}orM6YUDeFtibqj|}`ajL%ZE}fP27mkfsBZ2|qk$d( z*lbN2cNI>ef?=zwfv2CoN)atL%MX>J&&K?PjMXHL6(Qv6ACq!FJnPgg&^Id&MUr$d zyDyKFWt?1$kwFNVyK;-C-H)p!XtNm&4&S+zJl!8KhGlW_pMvp-r=ts%_g`=&AprGltB*)mV>f?-|nSg-fTdG$BnQB%OzAv!}TjOK%r_(|() z?|wCG3pe_OS28qcj71MSI2c%0EM+|g0p^V19ESVg@9@L{8Ks~h97AbZt3F~}q-2H8 zVjPnLm5g75y|3)>nVDn8Y$F7sDa1Ww`^(|VtVX$7>)j9azKCnG67)Kxn1(?`P$rsD zDP72F(qLD)@Ak1~-a`=G2S1C(B!(7>`_Z0xkQ z+DVF#K<-FR0k5OwyOe0J3_XWHefQb>_Yqb_O~@s0M<~f1&bM__V$rQu6KJxaRK~@d zWT@+w;B(uXL)WJ)3lSD}CXzfTd^jt@-~0QeSSa&+10PPaM2-oUAjs`M9};lP3U{ZY z5;I~)Utyc+9Aq8ghpF3C2Z<;M8;{4M(-^{}Sm07xqw`rFuS^`qK{=L{&1oua$Ix5k zCiSm~H*Uv_uCrFn>sFZQm zxhqAtT|G%k5Xp95wu8Aw+b~B95lN$eUv7uz;x)K{)SguUA|C>6euczlEJD zCZ#E5d8&z;fgb$%)lwvOwXSdEZyf&Q?Zxy)e;c!7D|B~fm#!--5eEmR3Xpx6v_y`I z5Jz(^=41QetwYMQ%TmRzi4$Ax3#X)emhs_4*A1U&w7`j)acsg^GN;wFqrMmIH0-<)SQL zz6FfzC;cWw_%4~Fy-A4Bu!`^{L(;K9Gqgsue4b`Iejy$LebtC@%cW*N+))HNA0sy- zp_MGnsh`D}Fwy!nbhsw;d^>viO~Z9la7dld0ewV_Eu7^U>jQ9cA35J{Ei6~F_BXW> z-zw_-!HN}yW{niWZK#O8Ln)}C<|_F0$1asMnkSU^brrFsHP@F_KNQ72!L=*iTjp;) z)q`29_AF`+n=MsEMFjz+GWymY*_rKv*l?-x2nn-w>t@+-Jjk5gr&Zg|qvmn?&b594j3kt*g&A2(jg4FNxove(b3_N4du zZxJ7zU7r_rlbR&G8l8;S9}M;$0>g2<&IpI_;B46*+wS5&y)k)1WFYjWe$9+IdHoCd zw|D+;eF&k~<2+gdO=+^*ut^O1`$R71zHg}SKq<&GGf@Y4q)Ff@VilmNn$|3?e<6B3 z!mc4WV*Tew0-PsJw6RJg^4}*Ls471o^AUcAPQlVRa<15NMSXhH1s*4n2U|wOohe_m z^>H+95VU2Gv-X91==(;JsWF{@kuOt15&PcN{ok!p#YsF*DAlN!G~=Pn>Nl7JhP3^5 ziC40le5T7zYB;4I5>bFs>NSHvfc&mkpC(55ZT&0fu>4D4k-vxK3x(XlAgBog1Yz$kb!laVat)%qu-xt(MB7m<<@}yQ387jn=*5dH z_RH}rFK!u1{9+*bkdoaq<>3q6y;WZoccE#Sf+mZtgq-`1hyrXm8RFd?Al4MPTZ=Y+ z?|LDgme8hwz}x76#R9L=V8Db4Z8>@Q9x2N7j;J*%k|qB~55VAkFu}w;{RRoNE~Q$) zgx9V;o7m1I;oOz@&>HoRiN1kJMPR^uf6jm)ig`esOhpz-X;kQWusZ;#g_eX(O6nwa z+lBQQAB;_T{gICVhQyYWkdjgyumD=nQz&Gc>f!Ul^qA5a&C91!Y|Q6OK8H1Vxvq51 ztzF`8?_{7byJ*nj{p&U3IH!59UPtVEaCStrJdFz0=yILnv~51e3)CH;!3(a``g~_O zvk0@3RKL;UYnw4=G>B9nmirOe@89^v#Z8VisIYTQauNUX2!@oAol~mLx0U(fFb0E| z<{9^{p!98i;e3%GX&jViU2edRiO%gWe>+gQddcixyNAIT*dx}+K5=7>oo8m(=5*(W zB*`cHH*gXIi$d;|^tFVj|`yw0J#e@NeVdH*%k}gn~vw zzai#M{h7r~y^0Z7*5t@yy6nHsT(ykSOz}_4-Cwd;~C9vxA;R-e{iuvBp=}n(-u?83v+I! zoV}k7*1p{_)G3j z;^N|1Un@asJEGOoi7IEtuV7MSTF~A z`#kWjJKS`iF`|4I;V;LrU(l5ZmN}8cj(-md{1YklZ=qAKl( zdx~-(12+)p2q3)wNI(ue@tR5euUs0rH=_Euzrvei8zn!(hoJpE1WPZHM%wvS_+j&GwnGg~r| zUWv#ygusngEr$?)cR(@@7rGd!B@h??EaEcsqs~{!YNaG3;0H^W)Oo7>HB63nv&O!{ zj~zHmXm@AWR90e7PL&n^@?#)KQc9P5JC~x+=Ue$fO`XltyV&*lS|#1)>%(hJCvQBR zB8X(dlWZIgYE?`6jw|DL(GH`o{0<*|DWEa0PIy8T68ODt!nZuUf5IhS2-?%UF$!Q< zGK9*l6rz&My$@9E8&wxV78^lOn%otWW8x7uFRwCtlebMd9%4GaGSc^aeDEA3WXMnS z0gVceooapTHl4fzGwc+EZcrd*(*udp72W1iOwjyU=rwz~J#4@4BBj>PE@^ zk=|ab84A-S8cKE6Y3|2&#~e4y2VieL5q^E2$V~g$wF8Jj0Sf|8WXsu4ot-HwNdr$r zd~@!(OKWz*ZK75Id|Vgz^Yn`)6~|5l zmQRjHw7WX>#_L=Q8MR>BDPT8vA)N7=#VMS1NQs&VkBX|*nJt?$;t9XO`#Hf_LeObV z*=m2j1xtb_w3&#G!*F&UYJJDu^3`t4Z)nt_tQ_%$=hysO3O=<-FS~+4lBzc(a9>T# z9-<-Q@3yJFI)LS7IxGaoRl}<=tS&r_hM+sPR=r%od_AZ7)QSa$h{g_080!NCeoKF< z!CuZ_#}n!R{w#7#+28RkK+F-c=;ECa7+O#rc%$E%B3`S|uAYi~(2GDb(XLm+En<;3 z=bY*bgeKM5s9+11D$Z%_Ik~f^0-DW)t0FDf^snfMbGFE^NI}w<8gA%FkNy6&FzWgd zDqD5G(1anwR7*VWN6vk$iUWsyS8nJo6u!d#pt>v>;eJnifx-glNZ~Dd&;WzgS7fH7 z_lD+Rc&f4eD0>WCQMvE5Z{ch|DC;uD>rrwSieL@O zeFG_O=%;?~VMoN!!&278Lf0qdP7MD-(+Q&C=i*KbhF~Wsvi-PdH{m=gFZqmsub7Ry zUHEO+_li{?i%y^*vDU!ICkN!hfQ_*s;9~m$eba-cQ=%AY3|-f+|H+)@6VxDb3ynFf zV`P*)n863*C!$$Cbi8_`p>kN~%@4v^M45%36in__DQ>9^PyuC?%Zo#eMDXwKiBV;Ch25_u53`|2gM7NmoF z5?FWE#{coFh8s0jgkuo>$)~~z}y*-47_er<`G{GtZrSV zm=taxZ=OQC1*)w}aNY;c$_&5g@-eng2}}W!Uf!8Jv zPgAXQyj#lBEoPG6SU-&|bWD;4DDCJsQ=MwbMui4aH~C_1Ap;7rN0yJ_=ynjS9yw~F2PV#`0*&hSr6?BRy;PPK@};;JS-7w%1S?b zMZDM1;{8UP+{%}LM687pr)f*_Z4#te#~B0-xbqMC-yQ< zTv{d#TBB}tQ}X+@>gIrtOjuX-?6az+9?QVNXo#@Qe&T8?0M4w7MJI@n-VS&B9u+%`bBonpB;Ea+`?mtzZ6i+45>4eHhnjaaG20{}6hX;- z29KAF6YoLzn+0JD#if@K5nrV6E-nts#==)Qz055fwZ3Rb`JGK^O3>P*3Y(bpx1)KQoo$7QK(o|qHLj^7cM3n?3D>&%d# zRwiSDIj{1jtScY%CQ_$t)!J&8Rlcv#_v?;OMUi|Q*qRSfPOm51FP$g!4yCctMkOBd zl&0>UJc0o0Brcc>E5yV9`J)t8SveMJP(t#trz;p1?)<`|K|6wroNH)%*m2%US<-)j z1QY&G93v%~J=WNV*vP`13MYsB$&+)p7OT7I@pHRi_f#pqg8DV z=?+I8`l#`yA!QXoO3hfb1R_x%NaHEyHN<*3D} zL*G96pj8}?s7vRR*?r5eewV_sD58^7Tm5bVU1!&L5)>SWRhsA8ZHz$WX(m2>6k&fi zV%bKB-D<5JJ9NY1+Oj$sZQ6Ur#kkn?@d`cL*c+7aaa&Ar0#hmY)pj;BZ7|T!_h3o+N!d8C6m2Swg(fOjwDEyStX0gTdjZQ z;gKGHbunAlx7T>b8}Un{&Gqs2DsAMoN$araYs?k9H0s65ELy5z4Iz=(c$Y-TYAGu# zTUTkTfvJH31);bwIS2(k*8YL>oXW>4lz=a?s8bp(>Ae)AR#pzeT~iurUTzjClAIvn z;EC0BD=RCQ3PvniOfMs?s1@$}!#GR(b4+ytQ*~{Et3fNrx z+~}IB=v;T_H!nN@Hz?VV8pc*a!+kJ8wZs|;5fKWa9r+D0`D7|$Kz*{Yn2Wo+p(e9= z1CxGVxT2$uOnY92;gP=5TKFqsP{Up8$_l>&)i3F4m3i%!l1YPS}R1ifviOItVFLMz0)arxlrVL(npFso$Pl;mq-#l z)$g}2k=_($27C^eaDzsJo)jz;LgVozG~fHEm<0sxoBQrz6n5C&LkQ?|;yPUtn#;)| z>c112{9cGMIDZLQ<_f{GwjkK6n%=(G&>{b1MQ^)^E%BvM{-X! zgu(tUJKW~pE@sHtWt%N4+4x(`0Ea#BP>d;Zt zr3y0iekRIvy}!rG-xR%MN~O+c8W-W~iC~EF3RWJ@elqQ{T#QHx9QPXT#9fS7NEGDb zf9J4Ip7{MPc4SY_aI86J;4;~5^VJQ3JbBzlEj*nvM`h5(!d zW=PNF8=5SF%|2C$mteRaN0|7jHhoIEY;U78oO=~VeTbEaZB_A`Dpll_2((+6m54Z1 z!5afKx-0^{n#AXKe0c=x`o(z!dhh_4&`ElW^@PONV{|E~89cNfpc+EG7jTPJnoF<& z(iG-v=VWZbTy&OT|`CZs! z561;8Wgo`{NdveCt+9`@hdf15JHb|t6*1nHsUcOT%k>0Ts}J=AhmSsG%`xxB#FC-BFhN8Ta*M ziwk(!ctVASlumcirP1X&%RNZD^YGH>s6%HDzd4j`AOEOF!x2%tL*tUPIoK1}_&e`O zx@bwg_&A#pthT#XBid}dT?_z7uisruw}IDs1lLo%&BXa98wQI3H>Yg(M5}$|m%<)j zz3z#4y9G61(>#FwtPs8jT|vAhw2fm2$y$g#(rD(@~iJfxvL5dvUDGrHJ(3&z%= zqb7E3V=49=0z)WCCGK&-Zp@ao#jLkX3To|8+NyX4S-s+_2dtyHwrx_sE$zP0+rVy* zvsS}f38rj7_O!W;wD7mtm0qpcFr~)8+5+8VGqH65 zOF&Ib%~H>A>J?4k2m6WPiNUk3RpVuuue`;5W8{~VWgSWF8csShw;7GNJiKVRI% z7X%_6t6bR?f|kC&JeyoF2njO({rlpv(Km^=4CmMvbZjR%F-F^Sw=7>Bv`Z|jXeGvb`@5K8OXRR0K1H)|&A}_B} zUEo6qJy)TQf%fb6Qa0xxap8u!EtYL}Y3(-<2xT#wIA6Ls8|rJh{JjX>KRv_vxPUZ^ zqA8_++6N4qRiQpux%LoSc5I+e&9_Tg=O2kC6D0ZOVI>&ZVquL^;jxufSH)7((iZxV z<!&KJ*69Sjcs44dgz{au=_G2*U&XR?{W&qYhFAaKHSN!2OXPpPwGYfdD}%bP?W7V`1gjrkJl z?FYsm6cPOnaIkRo2f$ZO`DChQRZEs%ipnxG%QM>(iat}JD#JCQ7Z=0KO#AvMLp1fa zxK3eMSzYXonsd1<-BJf6&=(@5Djb@7PjKEm+X@cVpBAo-CfO-inO3f-p5@5pBsA>Q z)x|8Qiw6CE4DVr$shGY>${9GMOJ>*W48ZZe+VQp94B$b!JDl9C))BwibZ3tCpf96qrTk+Hn^bm-3dt!He|a!BD}2IJV?i2turO$t9WK`x z7bJ|ALlB}b_njmp2Gdi8Mn?a#Ts5_PSJe(Vhrqw4nERt!T5tk+MpWOPiXN@I7@Yyc zHe=((2W1($xkIU)MX;Fbl$L>1o%uNS%&fYs1g+m?6wZDn9#<8Rg&c5>#c_cX-}NeT z^mMgCA>49!i+2^9h4FLFtd5eK1<7&G2}(kt10KCzCM1}r*``x0-aesiTXmmYB{h>} zP@0}6`&6!s;ig>=GcA7~vn|7;jcs#b?hS)rpPq2Oy+K1a%>Ifh?gOup>Y6%eUB@2D z)JsJCvzd_!UjOp*d}r>PrWok#c2pQMJShna9d;FDdAY1P!gxkiRjlu~nf%jYf}yP+ zgZp(UsJO`zwFWe8PU!fs?{R=%sSXK+aa6-MRCLH(UhnuO72>$Eb)yR?ou%U^2YzT0 zCFI0tE~fcji9yC-+X=jbX3l=UP>}4Jjhlexss))c(LaOL{RWbpTGzHR#eY;lF80|= zJp1|n-eCEibce36tMBbnbE~=BmFIgph#4gonEFJYW^v!(na)q0ZEFh1*BB#(X;&%9 zpPzhtqYy{ayv!VAWrr#!*j6v94trPJyffei6u>G3*Zt9%~5O;mGt zi=_oRmWGRM;DiQf=%+672Wcg^AE~nan9AO=zhhI&O?{s#XL894EjJ^4}Op%Z_WxjhR>N#^6v)J_@%Zv;Dv&a*a?(BHPW&q3M;1)9p=psT6Il;_O8FkPdRBk+`&277~CM}b&iS>B1Di!d2x8m znbUULuW6mrHeQ?-9sWM>Y$4CLY4ze`21$o#}HtddiF6}FkYVx^g4(CBWj%UIXml6l$O zj%{7@Ky{!b&vFi^fu2C z=ZPGb#);bX2I z@Fg?jyi|FI2Vub>kg>ZJ;3FL$W;gn0{6TGcCFLkXJy|_#y;8PL<6o9Xl~pSEMX*ig z3A^4`=Dzz<5j)fAPgGfQ@uR$#u`%DHr*P6QiT7q9syPnrP`UL*HJ7{?tI5^aaK(4O zXLnA!v6fZQJEe&CCNH#7N~2V%Tt$h6bzufU5C8#IuU(Q(WNQq_WX#QK%}O9|dL{d` zwUyO+nENF7c8(L)U@-L8ACJn}(Osq&BPH?@!X_!U5d!kS5Bx1RzABHqRX4MmVN?&( znI@u})+}JR5Q*FbR0*pd^n4IKQN%{G_6yiW^8hbOh?uCS_`=wgJJ`3k3*=Mh%-1^d zH*LLJs*2W@Hp*>XBRGY-%C0BA`oN4^@}7B`pac<;YH*$0%hj5{W66-VY-beIgxu7 zSo36!J{U$zz=$t>6Z+1yAZBUI5uiym>0Q^cxu26O3M_ykXaOroN; zlGCpJ@pg_%O$pT*hIH|E-`8#5nVl$c-+-hp+CMYuOgb@oIm z-fYm%DTp!7MjuWYtciiz$2x-$^9&ktGKorVH&tV|3=a!VZaBT8 zw(?NwU?w~ zBk6WMf9OlNrRmR0 z`Qk&lI*xouoOK*Zixe91H(GNRLoP8ed*JyQbP3NOQ@ndW6Qn(!#Rv2hc#<_`pt)C|@DH+Ec{rsZQq24_&d@#i=D$6c?gS z=7xs09R(ePG-L-8GhdZb)@-~EaT8sj!#nOU6(^cocj~Ye5ETN|b%j)1iv)ge-x9y9 zSw_S}T_symUN{q6HDhHynqMmCU=Fdar|hEQ-oT>Pof0=-l1Z)79~SjN&q*XfEiTDu zOs;S5n{D_EI;W(xwE{|w05$O{?$+W%wQFUoKW?@_$8dp)yY1~+-&l2O-RA&wE1SVc(p{8X*` zn7b2H8A=;9RpFl~Iq&#H&rt2U{$#;dqHS|g)pLI++=b1Sh@KQBXyBYNuh>QYJfwit@{rI}5U zD+E7g{9v0vj5`}qhN^oiAFT}wHA{u(K?|K~Qmk2}p|?Z9ey!){%^SQ+i8LLAzRRBU zE`j7_^cL0SH+$*ZPhRWrc7!G%bcm66+8Q`P&@IrH56;K?HhXNOoFDB@b+mTc+jmwsXq#^d^L5JHH-Q+(B|AmD%&Uk@P|{jhN`G#K4MS5-J-Kg3AKgqfh$aLpT7tj-N;QJKGU z{1wLb5YR^Rg+E%$j{65jq5%sVNM$3{>JE0{rrrcxD1~;f=H4D|tNAJHHV(t1j8YnL zxS~#mjJPO+@QpD?h*g+L# zf#kR+m1g?Y&P9tzlk9#i&uN_+4#ZDU4IkQ=eMiH}&rTmUb-yuJZF;R%G#6I`853{E z29kS<$U|{myDX)rbQ*Lnl>`E%q zGOP)i#|!7J8qsH6@-im_Ree`sO7G&Zwo<_~k245MN`$B(GraEqRWnxQv^cxqq7jO} zS8K-Ixti8R94BD2lQOFHEJo_ko+|2&6Fnt&r>;k?z=`Y7YwzG$3?DQ=K`<;9N?Xa1 z6}~jsStr$U7U{34!MC*E|C##4<-j~^-jYB;iLG_NxPm90W)3m50rDNIpVA)MYOqlFanL1s zc{+X1+Rq3^QrK(b%FFu@=TU6W-3GiEY*B7CcDFu*3@I3lD?@apwK^A(oA)bfh!qrk z8zqnLWqQ5td^Lw*ER>B=lDeiK;)q4e8*8+%jUlD5Kjuyl+awk%_Zkf_YtuKG&+Z8% zc^2Vy>m6uplCElo$?m~7e>^8!lxOW^!cQU|%T0`2uWO)X=j}tJ`u!LRa}2|A_fly( zAssaVsO4DNr%yS__k)5BV;E=K%iP?*{H$V7p3S1SjsAy*lKf<1)|B?1OfuYbuxsQ7(4^|b!A1M@y8 zBa_Ea#xCC*=krGuTpyvY!y^M;~!z@8&J2+~6r6*&4T~e?AmEXdG;XJtIHa3H>ZAto3a|Zkeq(vz;-!%sRJ-erRg9&}25+ zm1JkJo#`oG-Hvt1rt~UDk4_`kbY5X?e4LL>zw~Mj?#au+ibP(Hm#s5Xgk}KRlBnm* z)N+zSq8v_w!;DT&O~DHGrv}xVIJRNV-T3Ck5PD2+=~l+)(_Lz3R+g}#ty(jll)C2A zVE@DC{l&bY=+{4$^%xEs-y$9Bp?`hh@?) z{Vj*1Ln5fQoTI6iXoHJB* zt~i4q9`i<9rLu%vI4S%4&qETSrFRygvF{N@?una(<*tLzjiw~Xno$Qx{VDQOX4Wv` z?I*mgId3@H?fH*DcAE2~zfr}MXX+GdV zW<_`N;}N~oUxkD@r5{F0k0l`3K7k{n=#!d2j_^aX;3e(>zF+#ZfDEEw=2~$5Z9FmPV9Fos41vhOo>1gQVlNUNiMecC5G=Jsh*hEHIS&DU)+6k#_+qJl? zu-D z5zgAtN&}k;%4xb;;ohbXS0!!m5M@tiTl>4TiiP;|3}*>7#3E}J&JASYh1gbXDOHt~ zV=&T5!uih!vI=CWGm{QxyT7>(Ekqv={amfOV5xZ2YI~QngLpH%M;Yl&zXZ=+nmk7o zi$Y&==CNjQe4@lhxq0(KqS8f;(o(P`nODhLG@B@EIk373ylXr9@~mK}hneqJgkc<2 zOjlUx02>$ISt>Qc0ADb$&!iqGTw*gtKYKBl%GzKA-{xK3xTZM95Jb2-+ooar|Z+iUgjrm(&Bu@)TosMk6*b zW2uqVOmi1^wiP;~8-rHXDhc3J?!YIG`V9U@!*!pFSh7MK#H|xNp?>%WdtH@xV9GmD zF7VXJ+e~1EeniE2mxBa?#`f(0^~Ln+qcedW`Zg2Kr1@3WW2-B=z8pf6qDyZD+vEvi zv)aN*+2w&Lov6r`Cd0X_r#~Eb5@PN3HeBIU&V_6wSXm3yE8)uc``^{>` z8>vUTf{Pu|fVubX2hr|2&D(ovu5(%>ZMyajMSdx6zu$s;8%OBg4D(5{Ib0UP*VMJ5 zw6Q(wU~J7%zuX?5_a51u(MFd&(S&@&n|k-Q$!FO*yuKSLN(94)dPsM87y76>{WMFM z9NYTqi$sYM@!ieIWYoFpxwBZh=50bHNIO0MluzX6l*S5}I-k(Xk~oI7?b#1!e$sKx|-SsL|Sl3(jQPN2W*n5++_5#a8B${NfrdNyn z9W9!yF7(B>v~Ob{VU`(m`kYwq;;dclJEh;~BAi1}tRdN$o9;l0aT%Ho_5% zj~SyKGT|_(v2jCF@W?NiX~s#2>t2E3o7!H;%hb6%Egd{Y8%E@vEfPKD1)nAA*#w{r z7S*2-j^iln=iu(a&01mKcJt61p;dNtJ(o#C&(rUeYgz2ri?lAt)DZ+ypFDC>w49~*{Z>Q!=oX@keiFjLg4Q2N{Q37H_|{o>`pMy{7VG0gxmE|YUd#1^UJ zb){2%bElRn&o9|-syF!@OD*(XQoH_*af<7wWO@N!txvQZP?6$lar%+U>~@QNiyxN} zC)(}G;;&zOF%@K2HpO`KgBj@mAS9EenFSWuG1C#&+dj|6uj-Q+?EVk@>g(NR1Qt*> z>w1BsnDV4#t#5lg-9#T^gGwg632xkY02=6n6LJ2Gg9B;c0Cb#E_|KhokajVDpTfNbL`-^b>63U53&2* z^=z1!*UakW9Ic!v%7*zRq%76>V)KT6^1i0XH>?R(zv^W%K1+7+44DJxDsU~vYli51 z9S&NvZwI;ca}w_NFxQkQ4}(_^^=#N!*UX$SH+1vABs7L#?8=ZCX@pl4%S{%beBCFe z3*ZV#cb~zChmyI^y2^{M!_9JN;$2_c!W1s+O`b5L}g$v*%jNhpiFr zqO}H}l3xUE$0`^3yZ<4@IBuV(8+BpK^(D%Y0+}Dk%uk^K>7751zk3a2vL$u^*oAQaA0xnEDT@>kCo!bk#mU(C5+Q9m?0`XqD3y}PeKBBvV=pKOA z!k7F&W>w4kz)0h|j_m@=^LEmM(LWtn4DR1Zn1wQ!LhTPhrhZ96T=m&RN@L7TsQ#2A zm(~r7owvfoZdo`jPMtak@vs(EtowrVO$O2P<1&SmhU5XNMq2rRc?q; zZ+Iu=JEp(xqPjev;T$P?tCNTwyukg=NMPl<(c63eCTLgZQ5^Bb77ld)g&L^(1z{NbK{JkGJn67YE=>^hk_CChyfqX_2Hl#fviq7pZ}( zP8#E@gP!hGe)U;_NGvH27lIH0=9*Km6)@MntAz$ge&l2_7nys%2W74Q2%?w%W>_vw z-m`?a^T*&|;A%$rRBawON>VAV-fo-WwYnR74P1r<=}C6eerRrOGaUhmb?@%4CklJ_ zs%vt4*TP^@EQgU+oc7&w-iMlEF%pK5(_!Xo@QBe_?&TFT832nbm}1820VF+8AsB+F4ma1KJO*4QXA6C69+GkcCbfIuu7g=DLla>lNr+Ayt{DAL{E;uBnhgN~ z$A@&^JKtVT9x}E)4UxSvk_Ezmfz*oj2LXyP$<8ho0wX@1>pDVc28-7xJ`mE!47y-; z8arljHJcqmVQz5dG{EHjn4yoHG5xey0!<@s$kd7Czc&Gmt7u;}E`+WeD6zr(W#7dE zx5+>aG<@c4p$|TM;7c%Yf{>ax%V_n9pf%Vo)KRY6@GFxbHt~5CU~j&Jc-FToaPmDMv%2%P}j(RfB8CeRFR!>3kwV66+WbsyzaL1uLSa`eSurYa zb5Gj)x4%uk^y&pZw`J-@qt`oB%APM>+ndSS*vcm1atxrIDbn>agBu`0gS+-nXn(&` zjj09rfxBW~>Cy8nRz^$4xEDUcUtK@{D`~_2-)9m3-+Ac&swMdsYbW!stR3*ee^$Tz zpJ8?k%yi5gAja1kng2gII~EQOI#z~PJnUaNI~D-B`-`;$Dq%7L)i?i>wR^31`ERV< zzf=?a*Tm`nnSlR~H2=oh{f7Ym#F74AV|D-!2UHNHW6>gfMderlxRHgOg@aj(kQsP| z6(HvT>WG6)i;xWf>LcNgiL^#%xoZ5RzemQHab>DHbxErp#zE^XaM})-!c<|Sb*xAOe{=)wWtNu zEMNitI0%{9{;sD0B#8%@I}U)4WMBiJNPy*Ic(s9vkw#NKiETN zMpilwz|~%J*Z=E%pmxDO4P+o>1OXl50Q~CJh*yc2fr_nwLLfk1j#tzW1egGTjcl(} z>L1a7{n>zOt$#_#@GllK0VN<9*qHx7nk;P0KqvoIS|&PnW;RwI< zHK%@m@!554Z?<6w8O;yr46$+aW`wUtgN4?_En>sQi-@nO$mPNE{)Xb1;P5`hdW-dA zP9-XeR(Aazs8}ZeCn(X1U`5C}rEa&+Wa_2p_D)lrV&`1}RzlG6Awx95GLZL8f>KgW z)xruZD&={q#`m`emN$I*5Kw=aGLGi$4`PL%{45kE5={99u$7tq`^(DU z*5$V@v){W9e(K%t|N0|e|G>|Gb@z{tee?Z4e(r7lz4_dmUwZ9BAN~8MKl!h3z46x9 zp4xu#*+2dCm4E)>^*?{&3%`5i;rpKd>JNVT_FLck_)~xT(O7hT-|YZ#Lfl_DI}py#HB||J`i-_M;bH zda$9W=L2p@|M%~XkG+<5_tM>awDmi!>+kN>|Iqe*VpMKv{O@w2Ay1Cuzbn7d8#J^U zrF?SSu^qLcVK=n>M4N)v_Pxa%$Fg8->*m>cjxF9-3SK8^z1EH2y?yf8GRD2OKS|?< zU-p~>9$;*5pWXIS&Y(fqTkwh>r{(vY^(-d$mc|^*2;Ho&a#^|sZ7XHg;OV;YoNL(% z_Q~ULtvSb-GiY0_bzP8tk}u4$euMdjw)N8TS*+|KdBJJveaF^X%v+bN1+Qv35RTh< zxozKbAIv*vjyt!b#2ie)?~ug%a8f~r3kGS0KZyk8E>&vJb~-p9pfukVz89566+VL#9}@$}%HzUDZ( z)&g=c4TaOQb!vSmzxUCr)&~H&pk2&6z2>r6hK{pJUJBnk$Tb(rm!e(BMycyA`mJ1n z*2I@m0a!WiOxw91rKfFzHaE#y85i?TuNln@UFFQz7cpWjX!Qln^Riu?TkY%8{w)tG zFiOS^O>zlh6aCH-$_Un ze3>{x(Kg!yfD!UU4Ef}oWv{C&Cg!g=kx4d^PI`6!WVMVCgQe;zHiUGfMj!C#tC2XO&{q| z*^2b2fb0A|B74m{rN4Y_M?Sf1r+hNBGi4lCYaZ;ocvS%IS|4H{UV+U#2Zl2Du~+kI z@8;D$)OY)%1SrTmozGIK3teRRIKBhktjaI2;{h9Ifty;X7o$%8sC);#39fuf4lNPk&p8Y$$QgK$uM zX&?!WyU)Abo>2oVYaE|*6HReEN;KqW@Z9D-AK;aKuw=;}qT#)rwhD^O;KI);3FIgwuN!`QKm)3Pzi)a0{ZDZLMjq&0)3Bro75$;N%IJor4TuoR}< zHfIjS5R9`uofo!vo|Z`xD_S$(_md8u7xwSOfh4bh-r@_X`lMAv$a5gPdf%|!z;sGi zDM_SG*KHH_CK{A4W1Q?2rd`;ZyuElum@J;MH`(k;GBV`=nlNxB%ai9(Hx51cLPkb7 z3>Re{?1b|9?2c15O=r$-6<5@qKwi&cWJPNiYvE_SHO~16GDbcRzATxthW8NWuO#BM z7HUv>795eX#``3b70i`(lJth+H(b18zj|EA{jol_d8_-3yWqWZ!xezG)2ey^LPdEc=dE4&;#w&>Xq@9(###64Y1wE^_`WvEt2sw=&W)&>s%6Mpqog8Q zRlHS6N&8t8NzYQx70uY9Ha;$^aa8`3M);vK@4MIBiCFb4!IfyLZ(+fVktj_IOI5xS z?DbLH$vH?>X>Y^%hzhn4ugEuD(0m@`pk%|yH+e05$XDxYwh?q-L%B8Z^~s8d0bAc2OT} zc#yQGbj(O5DKAJS87G;<1xSww&y}wOjZ0S>DNwBi^+>(MY6zFKRUk}Wq2}T}xJi5g zH;K>SrnEVv3Y1f5;3lmZ+@v*=HcNX7T9Z$PCjZ;W1!>JB=Pqc%Dx@teYb27iX7Wu2jr#nENNS<5Az~Eqi*6eL4?i{@GYJ$z?f|SnJ)mXXRB=@M=2fvF8r)>X}vjbhGkcrLZ(W&G$4rZWJjR2UsA7H%itDE z?u|xd@vpCY5Vx^pXE8cA%9r9_rPL`i6N*InQf$t%G)S+nn_?UOC64pduQL$fChi(}bmnTQHgH);Y_q_1? z=Y{_T-sj#BXW=U%V#y>o1{4pj@^s2BDcTFZ*w0X*lCK?G7oR~=!o`>f`EDxK(hufP zY`@wGwdUP<{*_iv*(H*c?hm?HQtlb2IEt#IY&KR`wjJ#i_Thq=;POFB`{G#6s%~v1ZYJ1SUD;b{ilKzuC zuVSFaxjutLT88KWk}W1VES{3&)|r#a5D#dZ^ZT3^zzr(iq8rJwLBQa7FYLxekJ57I z;wizJ&JrhO8r7%d=|zr*?h;{Oki2atSU_iaQ3qRPCh?DuL~B9$b>_7Adz^E4cwgJP zi~by1g)A@Bx4b^z|KL`FZ4UK3@!+BdXy69ZpN~mYoaaHyj?R}-hvyiQXW)=x5>jc3 zkDzHE+%%Fu2brms&n+9pjkHiH|H3%omvQR8-@5p1*j|vQ2f3r8^$=Zio^*(O@OI~Qd z&$XNz$-Y?MYX8eQxN;%gKdm!l$aI$intBqLKz-6s6ccm3QMpZMBl&_tt0s(;Qs}(h z8(FiS`88;&^#Z7MPYKDDk3u53+8cAwHj+4Ob#o==hXz;US;EGQQz$&qWSHZy*>UD% z>yW_Paa`gRE&)x#Cg+pdsev>Q~lv$q4FhGc?&^XdHUH zkL+6JSy!8H+8k(PTjE)+AEgZ=@v3_<(3IbRruz`kbe43v0n%^hC7ZaVY(V2PAQ~rL zfvCBepW_H@1J?mWxy_?-RHKuIxk?&dKlw*eK>cQ`9aHn}i64XTI|A;p4meYbSSn&*h7+zVz@8_TGOK=Jm%f YJbHZp4Fu}uTb&P{e)@A?{``Y~0}hjQ+5i9m literal 0 HcmV?d00001 diff --git a/backend/helm/cloudhost-app/templates/_helpers.tpl b/backend/helm/cloudhost-app/templates/_helpers.tpl index bd9d650..cb982a0 100644 --- a/backend/helm/cloudhost-app/templates/_helpers.tpl +++ b/backend/helm/cloudhost-app/templates/_helpers.tpl @@ -46,6 +46,16 @@ Database deployment name {{- printf "%s-db" .Values.app.name }} {{- end }} +{{/* +Optional mirror registry prefix for Docker Hub images. +Usage: {{ include "cloudhost-app.baseImage" (dict "root" $ "image" "redis:7.2-alpine") }} +*/}} +{{- define "cloudhost-app.baseImage" -}} +{{- $reg := "" -}} +{{- with .root.Values.images -}}{{- $reg = .baseRegistry | default "" -}}{{- end -}} +{{- if $reg -}}{{ printf "%s/%s" $reg .image }}{{- else -}}{{ .image }}{{- end -}} +{{- end }} + {{/* Database image — auto-computed from type + version if not explicitly set */}} @@ -53,13 +63,13 @@ Database image — auto-computed from type + version if not explicitly set {{- if .Values.database.image }} {{- .Values.database.image }} {{- else if eq .Values.database.type "postgresql" }} - {{- printf "postgres:%s-alpine" .Values.database.version }} + {{- include "cloudhost-app.baseImage" (dict "root" $ "image" (printf "postgres:%s-alpine" .Values.database.version)) }} {{- else if eq .Values.database.type "mariadb" }} - {{- printf "mariadb:%s" .Values.database.version }} + {{- include "cloudhost-app.baseImage" (dict "root" $ "image" (printf "mariadb:%s" .Values.database.version)) }} {{- else if eq .Values.database.type "mongodb" }} - {{- printf "mongo:%s" .Values.database.version }} + {{- include "cloudhost-app.baseImage" (dict "root" $ "image" (printf "mongo:%s" .Values.database.version)) }} {{- else }} - {{- printf "mysql:%s" .Values.database.version }} + {{- include "cloudhost-app.baseImage" (dict "root" $ "image" (printf "mysql:%s" .Values.database.version)) }} {{- end }} {{- end }} diff --git a/backend/helm/cloudhost-app/templates/_log-shipper.tpl b/backend/helm/cloudhost-app/templates/_log-shipper.tpl index ddd2d65..c7496b9 100644 --- a/backend/helm/cloudhost-app/templates/_log-shipper.tpl +++ b/backend/helm/cloudhost-app/templates/_log-shipper.tpl @@ -5,7 +5,7 @@ {{- define "cloudhost-app.logShipperContainers" -}} {{- if .root.Values.elasticsearch.enabled }} - name: log-shipper - image: fluent/fluent-bit:2.2 + image: {{ include "cloudhost-app.baseImage" (dict "root" .root "image" "fluent/fluent-bit:2.2") }} resources: requests: cpu: "10m" diff --git a/backend/helm/cloudhost-app/templates/db-deployment.yaml b/backend/helm/cloudhost-app/templates/db-deployment.yaml index 1d3cd05..2479474 100644 --- a/backend/helm/cloudhost-app/templates/db-deployment.yaml +++ b/backend/helm/cloudhost-app/templates/db-deployment.yaml @@ -16,6 +16,10 @@ metadata: {{- include "cloudhost-app.labels" . | nindent 4 }} spec: replicas: 1 + # RWO volume + single replica: recreate the old pod before starting the new + # one — a rolling update would deadlock on the attached PVC. + strategy: + type: Recreate selector: matchLabels: app: {{ $dbName }} @@ -114,7 +118,7 @@ spec: command: ["healthcheck.sh", "--connect", "--innodb_initialized"] {{- else if eq .Values.database.type "mongodb" }} exec: - command: ["mongosh", "--eval", "db.adminCommand('ping')"] + command: ["sh", "-c", "mongosh --quiet -u \"$MONGO_INITDB_ROOT_USERNAME\" -p \"$MONGO_INITDB_ROOT_PASSWORD\" --eval \"db.adminCommand('ping')\""] {{- else }} exec: command: ["mysqladmin", "ping", "-h", "127.0.0.1"] @@ -131,7 +135,7 @@ spec: command: ["healthcheck.sh", "--connect", "--innodb_initialized"] {{- else if eq .Values.database.type "mongodb" }} exec: - command: ["mongosh", "--eval", "db.adminCommand('ping')"] + command: ["sh", "-c", "mongosh --quiet -u \"$MONGO_INITDB_ROOT_USERNAME\" -p \"$MONGO_INITDB_ROOT_PASSWORD\" --eval \"db.adminCommand('ping')\""] {{- else }} exec: command: ["mysqladmin", "ping", "-h", "127.0.0.1"] diff --git a/backend/helm/cloudhost-app/templates/deployment.yaml b/backend/helm/cloudhost-app/templates/deployment.yaml index 20f39b9..1c4aec3 100644 --- a/backend/helm/cloudhost-app/templates/deployment.yaml +++ b/backend/helm/cloudhost-app/templates/deployment.yaml @@ -217,7 +217,7 @@ spec: {{- end }} {{- if .Values.elasticsearch.enabled }} - name: fluent-bit - image: fluent/fluent-bit:2.2 + image: {{ include "cloudhost-app.baseImage" (dict "root" $ "image" "fluent/fluent-bit:2.2") }} resources: requests: cpu: "10m" diff --git a/backend/helm/cloudhost-app/templates/elasticsearch-credentials-secret.yaml b/backend/helm/cloudhost-app/templates/elasticsearch-credentials-secret.yaml index 8d12452..a4f7b9c 100644 --- a/backend/helm/cloudhost-app/templates/elasticsearch-credentials-secret.yaml +++ b/backend/helm/cloudhost-app/templates/elasticsearch-credentials-secret.yaml @@ -1,4 +1,7 @@ {{- if .Values.elasticsearch.enabled }} +{{- if not .Values.elasticsearch.fluentbitPassword }} +{{- fail "elasticsearch.fluentbitPassword is required when elasticsearch.enabled=true — no hardcoded default is shipped" }} +{{- end }} apiVersion: v1 kind: Secret metadata: @@ -8,7 +11,7 @@ metadata: {{- include "cloudhost-app.labels" . | nindent 4 }} type: Opaque stringData: - ELASTIC_PASSWORD: {{ .Values.elasticsearch.elasticPassword | default "CloudHost2024!Secure" | quote }} - FLUENTBIT_PASSWORD: {{ .Values.elasticsearch.fluentbitPassword | default "FluentBit2024!Writer" | quote }} - KIBANA_SYSTEM_PASSWORD: {{ .Values.elasticsearch.kibanaPassword | default "Kibana2024!System" | quote }} + ELASTIC_PASSWORD: {{ .Values.elasticsearch.elasticPassword | quote }} + FLUENTBIT_PASSWORD: {{ .Values.elasticsearch.fluentbitPassword | quote }} + KIBANA_SYSTEM_PASSWORD: {{ .Values.elasticsearch.kibanaPassword | quote }} {{- end }} diff --git a/backend/helm/cloudhost-app/templates/rabbitmq-deployment.yaml b/backend/helm/cloudhost-app/templates/rabbitmq-deployment.yaml index c169171..355ea1a 100644 --- a/backend/helm/cloudhost-app/templates/rabbitmq-deployment.yaml +++ b/backend/helm/cloudhost-app/templates/rabbitmq-deployment.yaml @@ -2,11 +2,22 @@ {{- $name := include "cloudhost-app.name" . -}} {{- $ns := include "cloudhost-app.namespace" . -}} {{- $rabbitName := printf "%s-rabbitmq" $name -}} +{{- /* Preserve the existing password across upgrades — RabbitMQ only applies + RABBITMQ_DEFAULT_PASS on first boot, so a regenerated secret would + diverge from the credentials stored in the persisted volume. */ -}} +{{- $rabbitSecretName := printf "%s-secret" $rabbitName -}} +{{- $existingRabbit := lookup "v1" "Secret" $ns $rabbitSecretName -}} +{{- $rabbitPass := "" -}} +{{- if and $existingRabbit $existingRabbit.data (index $existingRabbit.data "password") -}} +{{- $rabbitPass = index $existingRabbit.data "password" | b64dec -}} +{{- else -}} +{{- $rabbitPass = randAlphaNum 16 -}} +{{- end -}} --- apiVersion: v1 kind: Secret metadata: - name: {{ $rabbitName }}-secret + name: {{ $rabbitSecretName }} namespace: {{ $ns }} labels: app: {{ $rabbitName }} @@ -16,7 +27,7 @@ metadata: type: Opaque data: username: {{ "appuser" | b64enc | quote }} - password: {{ randAlphaNum 16 | b64enc | quote }} + password: {{ $rabbitPass | b64enc | quote }} --- apiVersion: v1 kind: PersistentVolumeClaim @@ -48,6 +59,9 @@ metadata: {{- include "cloudhost-app.labels" . | nindent 4 }} spec: replicas: 1 + # RWO volume + single replica: recreate instead of rolling update. + strategy: + type: Recreate selector: matchLabels: app: {{ $rabbitName }} @@ -58,7 +72,7 @@ spec: spec: containers: - name: rabbitmq - image: {{ printf "rabbitmq:%s-management-alpine" (.Values.rabbitmq.version | default "3.13") }} + image: {{ include "cloudhost-app.baseImage" (dict "root" $ "image" (printf "rabbitmq:%s-management-alpine" (.Values.rabbitmq.version | default "3.13"))) }} ports: - containerPort: 5672 name: amqp @@ -68,12 +82,12 @@ spec: - name: RABBITMQ_DEFAULT_USER valueFrom: secretKeyRef: - name: {{ $rabbitName }}-secret + name: {{ $rabbitSecretName }} key: username - name: RABBITMQ_DEFAULT_PASS valueFrom: secretKeyRef: - name: {{ $rabbitName }}-secret + name: {{ $rabbitSecretName }} key: password volumeMounts: - name: rabbitmq-data diff --git a/backend/helm/cloudhost-app/templates/redis-deployment.yaml b/backend/helm/cloudhost-app/templates/redis-deployment.yaml index a835acf..f4d32fb 100644 --- a/backend/helm/cloudhost-app/templates/redis-deployment.yaml +++ b/backend/helm/cloudhost-app/templates/redis-deployment.yaml @@ -2,11 +2,21 @@ {{- $name := include "cloudhost-app.name" . -}} {{- $ns := include "cloudhost-app.namespace" . -}} {{- $redisName := printf "%s-redis" $name -}} +{{- /* Preserve the existing password across upgrades — regenerating it every + upgrade would break app↔Redis auth against the persisted volume. */ -}} +{{- $redisSecretName := printf "%s-secret" $redisName -}} +{{- $existingRedis := lookup "v1" "Secret" $ns $redisSecretName -}} +{{- $redisPass := "" -}} +{{- if and $existingRedis $existingRedis.data (index $existingRedis.data "password") -}} +{{- $redisPass = index $existingRedis.data "password" | b64dec -}} +{{- else -}} +{{- $redisPass = randAlphaNum 16 -}} +{{- end -}} --- apiVersion: v1 kind: Secret metadata: - name: {{ $redisName }}-secret + name: {{ $redisSecretName }} namespace: {{ $ns }} labels: app: {{ $redisName }} @@ -15,7 +25,7 @@ metadata: "helm.sh/resource-policy": keep type: Opaque data: - password: {{ randAlphaNum 16 | b64enc | quote }} + password: {{ $redisPass | b64enc | quote }} --- apiVersion: v1 kind: PersistentVolumeClaim @@ -47,6 +57,10 @@ metadata: {{- include "cloudhost-app.labels" . | nindent 4 }} spec: replicas: 1 + # RWO volume + single replica: recreate the old pod before starting the new + # one, otherwise a rolling update deadlocks on the attached PVC. + strategy: + type: Recreate selector: matchLabels: app: {{ $redisName }} @@ -57,7 +71,7 @@ spec: spec: containers: - name: redis - image: {{ printf "redis:%s-alpine" (.Values.redis.version | default "7.2") }} + image: {{ include "cloudhost-app.baseImage" (dict "root" $ "image" (printf "redis:%s-alpine" (.Values.redis.version | default "7.2"))) }} args: ["--requirepass", "$(REDIS_PASSWORD)"] ports: - containerPort: 6379 @@ -65,7 +79,14 @@ spec: - name: REDIS_PASSWORD valueFrom: secretKeyRef: - name: {{ $redisName }}-secret + name: {{ $redisSecretName }} + key: password + # redis-cli in the probes auto-authenticates from REDISCLI_AUTH, + # so `redis-cli ping` works even with --requirepass set. + - name: REDISCLI_AUTH + valueFrom: + secretKeyRef: + name: {{ $redisSecretName }} key: password volumeMounts: - name: redis-data diff --git a/backend/helm/cloudhost-app/values.yaml b/backend/helm/cloudhost-app/values.yaml index dfab77a..9084ad8 100644 --- a/backend/helm/cloudhost-app/values.yaml +++ b/backend/helm/cloudhost-app/values.yaml @@ -101,3 +101,9 @@ changeCause: "" # ── Registry (for imagePullSecret) ────────────────────── registry: url: "localhost:30500" + +# ── Base images ────────────────────────────────────────── +images: + # Optional mirror registry prefix for Docker Hub images (postgres, mysql, + # redis, rabbitmq, fluent-bit, …), e.g. "mirror.example.com". + baseRegistry: "" diff --git a/backend/helm/cloudhost-platform/migrations/000_base_schema.sql b/backend/helm/cloudhost-platform/migrations/000_base_schema.sql new file mode 100644 index 0000000..90ce330 --- /dev/null +++ b/backend/helm/cloudhost-platform/migrations/000_base_schema.sql @@ -0,0 +1,1638 @@ +-- 000_base_schema.sql — greenfield base schema (generated from TypeORM entities). +-- Auto-generated by scripts/generate-base-schema.mjs. Do not edit by hand. +-- Incremental migrations (001+) run afterwards on top of this schema. + +-- +-- PostgreSQL database dump +-- + + +-- Dumped from database version 16.13 +-- Dumped by pg_dump version 16.13 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: public; Type: SCHEMA; Schema: -; Owner: - +-- + +-- *not* creating schema, since initdb creates it + + +-- +-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON SCHEMA public IS ''; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: addon_rates_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.addon_rates_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: applications_billingcycle_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_billingcycle_enum AS ENUM ( + 'hourly', + 'monthly', + 'yearly' +); + + +-- +-- Name: applications_customdomainstatus_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_customdomainstatus_enum AS ENUM ( + 'none', + 'pending_dns', + 'verified' +); + + +-- +-- Name: applications_databasetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_databasetype_enum AS ENUM ( + 'mysql', + 'postgresql', + 'mongodb', + 'mariadb', + 'none' +); + + +-- +-- Name: applications_lifecyclestatus_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_lifecyclestatus_enum AS ENUM ( + 'active', + 'suspended', + 'pending_deletion', + 'docked', + 'deleted' +); + + +-- +-- Name: applications_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: clusters_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.clusters_status_enum AS ENUM ( + 'active', + 'inactive', + 'maintenance' +); + + +-- +-- Name: deployments_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.deployments_status_enum AS ENUM ( + 'pending', + 'building', + 'build_failed', + 'deploying', + 'running', + 'failed', + 'cancelled', + 'stopped', + 'deleting' +); + + +-- +-- Name: invoices_paymentmethod_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.invoices_paymentmethod_enum AS ENUM ( + 'wallet', + 'gateway', + 'mixed' +); + + +-- +-- Name: invoices_reason_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.invoices_reason_enum AS ENUM ( + 'deploy', + 'renewal', + 'upgrade', + 'wallet_topup', + 'manual' +); + + +-- +-- Name: invoices_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.invoices_status_enum AS ENUM ( + 'draft', + 'issued', + 'partially_paid', + 'paid', + 'void', + 'failed' +); + + +-- +-- Name: optional_service_profiles_service_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.optional_service_profiles_service_enum AS ENUM ( + 'redis', + 'rabbitmq', + 'elasticsearch' +); + + +-- +-- Name: optional_service_rates_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.optional_service_rates_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: optional_service_rates_service_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.optional_service_rates_service_enum AS ENUM ( + 'redis', + 'rabbitmq', + 'elasticsearch' +); + + +-- +-- Name: pricing_rates_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.pricing_rates_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: pricing_rates_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.pricing_rates_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: pricing_rules_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.pricing_rules_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: resource_credits_billingcycle_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.resource_credits_billingcycle_enum AS ENUM ( + 'hourly', + 'monthly', + 'yearly' +); + + +-- +-- Name: resource_credits_databasetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.resource_credits_databasetype_enum AS ENUM ( + 'mysql', + 'postgresql', + 'mongodb', + 'mariadb', + 'none' +); + + +-- +-- Name: resource_credits_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.resource_credits_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: service_access_grants_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_access_grants_status_enum AS ENUM ( + 'active', + 'expired', + 'revoked' +); + + +-- +-- Name: service_access_grants_target_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_access_grants_target_enum AS ENUM ( + 'database', + 'redis', + 'rabbitmq_amqp', + 'rabbitmq_management' +); + + +-- +-- Name: service_plans_billingcycle_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_plans_billingcycle_enum AS ENUM ( + 'hourly', + 'monthly', + 'yearly' +); + + +-- +-- Name: service_plans_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_plans_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: snapshots_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.snapshots_status_enum AS ENUM ( + 'in_progress', + 'completed', + 'failed' +); + + +-- +-- Name: snapshots_type_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.snapshots_type_enum AS ENUM ( + 'pre_deploy', + 'manual' +); + + +-- +-- Name: ticket_messages_senderrole_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.ticket_messages_senderrole_enum AS ENUM ( + 'user', + 'admin', + 'technical', + 'sales' +); + + +-- +-- Name: tickets_department_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tickets_department_enum AS ENUM ( + 'technical', + 'sales' +); + + +-- +-- Name: tickets_priority_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tickets_priority_enum AS ENUM ( + 'low', + 'medium', + 'high' +); + + +-- +-- Name: tickets_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tickets_status_enum AS ENUM ( + 'open', + 'answered', + 'waiting', + 'closed' +); + + +-- +-- Name: users_role_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.users_role_enum AS ENUM ( + 'user', + 'admin', + 'technical', + 'sales' +); + + +-- +-- Name: verification_codes_purpose_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.verification_codes_purpose_enum AS ENUM ( + 'login', + 'change_phone' +); + + +-- +-- Name: wallet_transactions_type_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.wallet_transactions_type_enum AS ENUM ( + 'charge', + 'deduction', + 'refund', + 'gateway_payment' +); + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: addon_rates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_rates ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "resourceType" public.addon_rates_resourcetype_enum NOT NULL, + "hourlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "monthlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "yearlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: application_migration_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.application_migration_events ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "migrationId" uuid NOT NULL, + step character varying NOT NULL, + level character varying DEFAULT 'info'::character varying NOT NULL, + message character varying NOT NULL, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: application_migration_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.application_migration_jobs ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "applicationId" uuid NOT NULL, + "requestedBy" character varying NOT NULL, + "sourceClusterId" uuid NOT NULL, + "targetClusterId" uuid NOT NULL, + status character varying DEFAULT 'queued'::character varying NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + "maxAttempts" integer DEFAULT 3 NOT NULL, + "currentStep" character varying, + "errorMessage" character varying, + metadata jsonb, + "startedAt" timestamp with time zone, + "completedAt" timestamp with time zone, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: applications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.applications ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + "productType" character varying DEFAULT 'application'::character varying NOT NULL, + description character varying, + runtime public.applications_runtime_enum NOT NULL, + "databaseType" public.applications_databasetype_enum DEFAULT 'none'::public.applications_databasetype_enum NOT NULL, + "runtimeVersion" character varying, + "phpVersion" character varying, + "dbVersion" character varying, + "dbUsername" character varying, + "dbPassword" character varying, + "dbStorageSize" character varying DEFAULT '1Gi'::character varying, + "appStorageSize" character varying DEFAULT '2Gi'::character varying, + "enableRedis" boolean DEFAULT false NOT NULL, + "redisVersion" character varying DEFAULT '7.2'::character varying, + "enableRabbitmq" boolean DEFAULT false NOT NULL, + "rabbitmqVersion" character varying DEFAULT '3.13'::character varying, + "enableElasticsearch" boolean DEFAULT false NOT NULL, + "elasticsearchVersion" character varying DEFAULT '8.12'::character varying, + "logPaths" jsonb, + "optionalServiceResources" jsonb, + "gitUrl" character varying, + "gitToken" character varying, + "gitBranch" character varying, + "codePath" character varying, + "dbDumpPath" character varying, + "envVars" jsonb, + "cpuRequest" character varying DEFAULT '100m'::character varying NOT NULL, + "cpuLimit" character varying DEFAULT '500m'::character varying NOT NULL, + "memoryRequest" character varying DEFAULT '128Mi'::character varying NOT NULL, + "memoryLimit" character varying DEFAULT '512Mi'::character varying NOT NULL, + replicas integer DEFAULT 1 NOT NULL, + port integer DEFAULT 3000 NOT NULL, + "userId" uuid NOT NULL, + "clusterId" uuid, + "poolId" uuid, + "latestImageTag" character varying, + subdomain character varying, + "customDomain" character varying, + "customDomainStatus" public.applications_customdomainstatus_enum DEFAULT 'none'::public.applications_customdomainstatus_enum NOT NULL, + "customDomainVerifiedAt" timestamp with time zone, + "planId" character varying, + "billingCycle" public.applications_billingcycle_enum, + "lifecycleStatus" public.applications_lifecyclestatus_enum DEFAULT 'active'::public.applications_lifecyclestatus_enum NOT NULL, + "planExpiresAt" timestamp with time zone, + "suspendedAt" timestamp with time zone, + "suspendedReplicas" jsonb, + "scheduledDeletionAt" timestamp with time zone, + "dockedAt" timestamp with time zone, + "dockSnapshotId" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: audit_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.audit_logs ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + action character varying NOT NULL, + "actorUserId" uuid NOT NULL, + "targetUserId" uuid, + metadata jsonb, + ip character varying, + "userAgent" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: cluster_allocation_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.cluster_allocation_logs ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "applicationId" uuid, + "userId" character varying NOT NULL, + "poolId" uuid, + "selectedClusterId" uuid, + strategy character varying DEFAULT 'weighted-resource'::character varying NOT NULL, + "estimatedRequest" jsonb, + "candidateScores" jsonb, + "rejectionReasons" jsonb, + status character varying DEFAULT 'success'::character varying NOT NULL, + message character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: cluster_health; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.cluster_health ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "clusterId" uuid NOT NULL, + status character varying DEFAULT 'unknown'::character varying NOT NULL, + "readyNodes" integer DEFAULT 0 NOT NULL, + "nodeCount" integer DEFAULT 0 NOT NULL, + "cpuAllocatable" character varying, + "memoryAllocatable" character varying, + "podCount" integer DEFAULT 0 NOT NULL, + "appCount" integer DEFAULT 0 NOT NULL, + message character varying, + resources jsonb, + "checkedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: cluster_pools; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.cluster_pools ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + description character varying, + strategy character varying DEFAULT 'weighted-resource'::character varying NOT NULL, + "clusterIds" jsonb DEFAULT '[]'::jsonb NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "isDefault" boolean DEFAULT false NOT NULL, + priority integer DEFAULT 100 NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: clusters; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.clusters ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + description character varying, + status public.clusters_status_enum DEFAULT 'active'::public.clusters_status_enum NOT NULL, + kubeconfig text NOT NULL, + "apiServer" character varying NOT NULL, + region character varying, + weight integer DEFAULT 1 NOT NULL, + tags jsonb DEFAULT '[]'::jsonb NOT NULL, + "healthStatus" character varying DEFAULT 'unknown'::character varying NOT NULL, + "lastHealthCheckedAt" timestamp with time zone, + "healthMessage" character varying, + "availableResources" jsonb, + provider character varying, + "isDefault" boolean DEFAULT false NOT NULL, + "defaultCpuLimit" character varying DEFAULT '4'::character varying NOT NULL, + "defaultMemoryLimit" character varying DEFAULT '8Gi'::character varying NOT NULL, + "maxAppsPerUser" integer DEFAULT 10 NOT NULL, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: deployments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.deployments ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + status public.deployments_status_enum DEFAULT 'pending'::public.deployments_status_enum NOT NULL, + "imageTag" character varying NOT NULL, + version character varying, + "k8sResources" jsonb, + "buildLog" text, + "deployLog" text, + "previewSubdomain" character varying(63), + "errorMessage" character varying, + "applicationId" uuid NOT NULL, + "triggeredBy" character varying NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL, + "finishedAt" timestamp without time zone +); + + +-- +-- Name: discount_redemptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.discount_redemptions ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "discountId" uuid NOT NULL, + "userId" character varying NOT NULL, + "invoiceId" character varying, + amount numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: discounts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.discounts ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + code character varying NOT NULL, + name character varying NOT NULL, + description character varying, + "percentOff" integer NOT NULL, + services jsonb DEFAULT '[]'::jsonb NOT NULL, + "isPublic" boolean DEFAULT true NOT NULL, + "allowedUserIds" jsonb DEFAULT '[]'::jsonb NOT NULL, + "maxUses" integer, + "maxUsesPerUser" integer, + "usedCount" integer DEFAULT 0 NOT NULL, + "startsAt" timestamp with time zone, + "endsAt" timestamp with time zone, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: invoice_lines; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.invoice_lines ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "invoiceId" uuid NOT NULL, + label character varying NOT NULL, + description character varying, + quantity integer DEFAULT 1 NOT NULL, + "unitAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + amount numeric(14,2) DEFAULT '0'::numeric NOT NULL, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: invoices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.invoices ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "invoiceNumber" character varying NOT NULL, + "userId" uuid NOT NULL, + "applicationId" uuid, + reason public.invoices_reason_enum DEFAULT 'manual'::public.invoices_reason_enum NOT NULL, + status public.invoices_status_enum DEFAULT 'issued'::public.invoices_status_enum NOT NULL, + "paymentMethod" public.invoices_paymentmethod_enum, + subtotal numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "discountAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "discountCode" character varying, + total numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "paidAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "dueAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "dueDate" timestamp with time zone, + "paidAt" timestamp with time zone, + "gatewayTrackingCode" character varying, + "gatewayReference" character varying, + "adminNote" character varying, + "statusReason" character varying, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: optional_service_profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.optional_service_profiles ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + service public.optional_service_profiles_service_enum NOT NULL, + "cpuRequest" character varying, + "memoryRequest" character varying, + "cpuLimit" character varying DEFAULT '200m'::character varying NOT NULL, + "memoryLimit" character varying DEFAULT '256Mi'::character varying NOT NULL, + "storageGi" numeric(10,2) DEFAULT '1'::numeric NOT NULL, + "logShipperCpuLimit" character varying, + "logShipperMemoryLimit" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: optional_service_rates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.optional_service_rates ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + service public.optional_service_rates_service_enum NOT NULL, + "resourceType" public.optional_service_rates_resourcetype_enum NOT NULL, + "hourlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "monthlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "yearlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: platform_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.platform_settings ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + key character varying NOT NULL, + value text NOT NULL, + description character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: pricing_rates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.pricing_rates ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + runtime public.pricing_rates_runtime_enum NOT NULL, + "resourceType" public.pricing_rates_resourcetype_enum NOT NULL, + "hourlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "monthlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "yearlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: pricing_rules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.pricing_rules ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "resourceType" public.pricing_rules_resourcetype_enum NOT NULL, + "unitPrice" numeric(12,2) NOT NULL, + description character varying, + "planId" uuid NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: resource_credits; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.resource_credits ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "userId" uuid NOT NULL, + "sourceAppName" character varying, + "productType" character varying DEFAULT 'application'::character varying NOT NULL, + runtime public.resource_credits_runtime_enum NOT NULL, + "databaseType" public.resource_credits_databasetype_enum NOT NULL, + "cpuLimit" character varying NOT NULL, + "memoryLimit" character varying NOT NULL, + replicas integer DEFAULT 1 NOT NULL, + "dbStorageSize" character varying, + "appStorageSize" character varying, + "enableRedis" boolean DEFAULT false NOT NULL, + "enableRabbitmq" boolean DEFAULT false NOT NULL, + "enableElasticsearch" boolean DEFAULT false NOT NULL, + "billingCycle" public.resource_credits_billingcycle_enum, + "expiresAt" timestamp with time zone NOT NULL, + "consumedAt" timestamp with time zone, + "appliedApplicationId" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: service_access_grants; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.service_access_grants ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "applicationId" uuid NOT NULL, + "userId" character varying NOT NULL, + "clusterId" character varying NOT NULL, + namespace character varying NOT NULL, + target public.service_access_grants_target_enum NOT NULL, + "nodePort" integer NOT NULL, + "targetPort" integer NOT NULL, + host character varying NOT NULL, + "k8sServiceName" character varying NOT NULL, + status public.service_access_grants_status_enum DEFAULT 'active'::public.service_access_grants_status_enum NOT NULL, + persistent boolean DEFAULT false NOT NULL, + "expiresAt" timestamp with time zone NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: service_plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.service_plans ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + runtime public.service_plans_runtime_enum NOT NULL, + description character varying, + "billingCycle" public.service_plans_billingcycle_enum NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: snapshots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.snapshots ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + type public.snapshots_type_enum DEFAULT 'manual'::public.snapshots_type_enum NOT NULL, + status public.snapshots_status_enum DEFAULT 'in_progress'::public.snapshots_status_enum NOT NULL, + label character varying, + "appArchivePath" character varying, + "wpContentArchivePath" character varying, + "imageTag" character varying, + "dbDumpPath" character varying, + "hasDatabase" boolean DEFAULT false NOT NULL, + "appArchiveSize" bigint, + "dbDumpSize" bigint, + "wpContentSize" bigint, + "errorMessage" character varying, + progress integer DEFAULT 0 NOT NULL, + "applicationId" uuid NOT NULL, + "createdBy" character varying NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: ticket_messages; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ticket_messages ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + message text NOT NULL, + "ticketId" uuid NOT NULL, + "senderId" uuid NOT NULL, + "senderRole" public.ticket_messages_senderrole_enum NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tickets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tickets ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + subject character varying NOT NULL, + department public.tickets_department_enum NOT NULL, + status public.tickets_status_enum DEFAULT 'open'::public.tickets_status_enum NOT NULL, + priority public.tickets_priority_enum DEFAULT 'medium'::public.tickets_priority_enum NOT NULL, + "userId" uuid NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL, + "closedAt" timestamp without time zone +); + + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + phone character varying, + email character varying, + "phoneVerified" boolean DEFAULT false NOT NULL, + password character varying NOT NULL, + "firstName" character varying NOT NULL, + "lastName" character varying NOT NULL, + role public.users_role_enum DEFAULT 'user'::public.users_role_enum NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + namespace character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: verification_codes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.verification_codes ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "userId" uuid NOT NULL, + purpose public.verification_codes_purpose_enum NOT NULL, + destination character varying NOT NULL, + "codeHash" character varying NOT NULL, + "expiresAt" timestamp with time zone NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + "consumedAt" timestamp with time zone, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: wallet_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.wallet_transactions ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + type public.wallet_transactions_type_enum NOT NULL, + amount numeric(14,2) NOT NULL, + "balanceAfter" numeric(14,2) NOT NULL, + description character varying, + "applicationId" character varying, + "invoiceId" uuid, + "walletId" uuid NOT NULL, + "gatewayTrackingCode" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: wallets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.wallets ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + balance numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "userId" uuid NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: application_migration_jobs PK_130c380307e6aec30012c92e932; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "PK_130c380307e6aec30012c92e932" PRIMARY KEY (id); + + +-- +-- Name: verification_codes PK_18741b6b8bf1680dbf5057421d7; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.verification_codes + ADD CONSTRAINT "PK_18741b6b8bf1680dbf5057421d7" PRIMARY KEY (id); + + +-- +-- Name: audit_logs PK_1bb179d048bbc581caa3b013439; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.audit_logs + ADD CONSTRAINT "PK_1bb179d048bbc581caa3b013439" PRIMARY KEY (id); + + +-- +-- Name: deployments PK_1e5627acb3c950deb83fe98fc48; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.deployments + ADD CONSTRAINT "PK_1e5627acb3c950deb83fe98fc48" PRIMARY KEY (id); + + +-- +-- Name: optional_service_profiles PK_2625f60eb9be17174299de23f47; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_profiles + ADD CONSTRAINT "PK_2625f60eb9be17174299de23f47" PRIMARY KEY (id); + + +-- +-- Name: platform_settings PK_2934aeb70ec285196dcab4a2e96; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.platform_settings + ADD CONSTRAINT "PK_2934aeb70ec285196dcab4a2e96" PRIMARY KEY (id); + + +-- +-- Name: discount_redemptions PK_2efe685154f7c7deacaf6f0ef63; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discount_redemptions + ADD CONSTRAINT "PK_2efe685154f7c7deacaf6f0ef63" PRIMARY KEY (id); + + +-- +-- Name: tickets PK_343bc942ae261cf7a1377f48fd0; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tickets + ADD CONSTRAINT "PK_343bc942ae261cf7a1377f48fd0" PRIMARY KEY (id); + + +-- +-- Name: cluster_allocation_logs PK_34d1148bb1df92decfcdc7cab87; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "PK_34d1148bb1df92decfcdc7cab87" PRIMARY KEY (id); + + +-- +-- Name: ticket_messages PK_37beb692dedf7eccb4e519ccec1; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ticket_messages + ADD CONSTRAINT "PK_37beb692dedf7eccb4e519ccec1" PRIMARY KEY (id); + + +-- +-- Name: invoice_lines PK_3d18eb48142b916f581f0c21a65; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoice_lines + ADD CONSTRAINT "PK_3d18eb48142b916f581f0c21a65" PRIMARY KEY (id); + + +-- +-- Name: wallet_transactions PK_5120f131bde2cda940ec1a621db; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallet_transactions + ADD CONSTRAINT "PK_5120f131bde2cda940ec1a621db" PRIMARY KEY (id); + + +-- +-- Name: clusters PK_56c8e201f375e1e961dcdd6831c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.clusters + ADD CONSTRAINT "PK_56c8e201f375e1e961dcdd6831c" PRIMARY KEY (id); + + +-- +-- Name: invoices PK_668cef7c22a427fd822cc1be3ce; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "PK_668cef7c22a427fd822cc1be3ce" PRIMARY KEY (id); + + +-- +-- Name: discounts PK_66c522004212dc814d6e2f14ecc; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discounts + ADD CONSTRAINT "PK_66c522004212dc814d6e2f14ecc" PRIMARY KEY (id); + + +-- +-- Name: service_plans PK_679a9e435f1af95a94d9749a087; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.service_plans + ADD CONSTRAINT "PK_679a9e435f1af95a94d9749a087" PRIMARY KEY (id); + + +-- +-- Name: resource_credits PK_6a1f7bee5bd45667c08a63d9e3c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.resource_credits + ADD CONSTRAINT "PK_6a1f7bee5bd45667c08a63d9e3c" PRIMARY KEY (id); + + +-- +-- Name: pricing_rates PK_6b3ac518f3abafc7d0e1b6bb449; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rates + ADD CONSTRAINT "PK_6b3ac518f3abafc7d0e1b6bb449" PRIMARY KEY (id); + + +-- +-- Name: addon_rates PK_817008cdfd7d2040209665de3cf; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_rates + ADD CONSTRAINT "PK_817008cdfd7d2040209665de3cf" PRIMARY KEY (id); + + +-- +-- Name: wallets PK_8402e5df5a30a229380e83e4f7e; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "PK_8402e5df5a30a229380e83e4f7e" PRIMARY KEY (id); + + +-- +-- Name: applications PK_938c0a27255637bde919591888f; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.applications + ADD CONSTRAINT "PK_938c0a27255637bde919591888f" PRIMARY KEY (id); + + +-- +-- Name: users PK_a3ffb1c0c8416b9fc6f907b7433; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY (id); + + +-- +-- Name: cluster_health PK_a619d68317e11de0cdc7769936c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_health + ADD CONSTRAINT "PK_a619d68317e11de0cdc7769936c" PRIMARY KEY (id); + + +-- +-- Name: service_access_grants PK_ba880e7036fb12629d2bb20813b; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.service_access_grants + ADD CONSTRAINT "PK_ba880e7036fb12629d2bb20813b" PRIMARY KEY (id); + + +-- +-- Name: application_migration_events PK_cbbcd35db024c6ce5282dc4b826; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_events + ADD CONSTRAINT "PK_cbbcd35db024c6ce5282dc4b826" PRIMARY KEY (id); + + +-- +-- Name: cluster_pools PK_cd7c2f36783be8d628956f41734; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_pools + ADD CONSTRAINT "PK_cd7c2f36783be8d628956f41734" PRIMARY KEY (id); + + +-- +-- Name: optional_service_rates PK_dde26d2c02f198afe455543e16c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_rates + ADD CONSTRAINT "PK_dde26d2c02f198afe455543e16c" PRIMARY KEY (id); + + +-- +-- Name: snapshots PK_f5661b5fd4224d23e26a631986b; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.snapshots + ADD CONSTRAINT "PK_f5661b5fd4224d23e26a631986b" PRIMARY KEY (id); + + +-- +-- Name: pricing_rules PK_fda27bb8db4630894decda61ff6; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rules + ADD CONSTRAINT "PK_fda27bb8db4630894decda61ff6" PRIMARY KEY (id); + + +-- +-- Name: wallets UQ_2ecdb33f23e9a6fc392025c0b97; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "UQ_2ecdb33f23e9a6fc392025c0b97" UNIQUE ("userId"); + + +-- +-- Name: platform_settings UQ_5d9031e30fac3ec3ec8b9602e17; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.platform_settings + ADD CONSTRAINT "UQ_5d9031e30fac3ec3ec8b9602e17" UNIQUE (key); + + +-- +-- Name: optional_service_profiles UQ_63c79ca1dc0b57933b800469504; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_profiles + ADD CONSTRAINT "UQ_63c79ca1dc0b57933b800469504" UNIQUE (service); + + +-- +-- Name: pricing_rates UQ_8323897b5a47d20ef92896dc5a8; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rates + ADD CONSTRAINT "UQ_8323897b5a47d20ef92896dc5a8" UNIQUE (runtime, "resourceType"); + + +-- +-- Name: discounts UQ_8c7cc2340e9ea0fc5a246e63749; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discounts + ADD CONSTRAINT "UQ_8c7cc2340e9ea0fc5a246e63749" UNIQUE (code); + + +-- +-- Name: users UQ_97672ac88f789774dd47f7c8be3; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE (email); + + +-- +-- Name: users UQ_a000cca60bcf04454e727699490; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "UQ_a000cca60bcf04454e727699490" UNIQUE (phone); + + +-- +-- Name: invoices UQ_bf8e0f9dd4558ef209ec111782d; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "UQ_bf8e0f9dd4558ef209ec111782d" UNIQUE ("invoiceNumber"); + + +-- +-- Name: optional_service_rates UQ_da71e6eb43a191e589c4e1d32f6; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_rates + ADD CONSTRAINT "UQ_da71e6eb43a191e589c4e1d32f6" UNIQUE (service, "resourceType"); + + +-- +-- Name: addon_rates UQ_ef90e5f423c8ff2fd3b321763e8; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_rates + ADD CONSTRAINT "UQ_ef90e5f423c8ff2fd3b321763e8" UNIQUE ("resourceType"); + + +-- +-- Name: IDX_43facb440ecfb2acadcf2922b5; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_43facb440ecfb2acadcf2922b5" ON public.service_access_grants USING btree ("applicationId", target, status); + + +-- +-- Name: IDX_5b7fbc8045a0654e5f8db27dc5; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_5b7fbc8045a0654e5f8db27dc5" ON public.audit_logs USING btree ("targetUserId"); + + +-- +-- Name: IDX_b455a8c210f79a873760a92819; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_b455a8c210f79a873760a92819" ON public.verification_codes USING btree ("userId", purpose); + + +-- +-- Name: IDX_c1e88bb93860db181f5b21a306; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_c1e88bb93860db181f5b21a306" ON public.discount_redemptions USING btree ("discountId", "userId"); + + +-- +-- Name: IDX_e36d23e1e7cf81ea77758bef79; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_e36d23e1e7cf81ea77758bef79" ON public.audit_logs USING btree ("actorUserId"); + + +-- +-- Name: application_migration_jobs FK_12568b7d01ef97d05da755833f3; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "FK_12568b7d01ef97d05da755833f3" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: wallets FK_2ecdb33f23e9a6fc392025c0b97; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_2ecdb33f23e9a6fc392025c0b97" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: cluster_allocation_logs FK_3b1bba8052f0504c2ff400f70a4; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "FK_3b1bba8052f0504c2ff400f70a4" FOREIGN KEY ("selectedClusterId") REFERENCES public.clusters(id) ON DELETE SET NULL; + + +-- +-- Name: cluster_allocation_logs FK_3ef9df9c9317fab60fdcb5f581b; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "FK_3ef9df9c9317fab60fdcb5f581b" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE SET NULL; + + +-- +-- Name: cluster_allocation_logs FK_44764afc850c4b3a8c62547c2a9; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "FK_44764afc850c4b3a8c62547c2a9" FOREIGN KEY ("poolId") REFERENCES public.cluster_pools(id) ON DELETE SET NULL; + + +-- +-- Name: service_access_grants FK_48b102a7593a5e76d9fda39dfb0; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.service_access_grants + ADD CONSTRAINT "FK_48b102a7593a5e76d9fda39dfb0" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: tickets FK_4bb45e096f521845765f657f5c8; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tickets + ADD CONSTRAINT "FK_4bb45e096f521845765f657f5c8" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: snapshots FK_5652b27d83628dc5612245d62ce; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.snapshots + ADD CONSTRAINT "FK_5652b27d83628dc5612245d62ce" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: wallet_transactions FK_581a824e911b9baa6be19cd92fc; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallet_transactions + ADD CONSTRAINT "FK_581a824e911b9baa6be19cd92fc" FOREIGN KEY ("invoiceId") REFERENCES public.invoices(id) ON DELETE SET NULL; + + +-- +-- Name: invoices FK_875034bdd7f0ac4a726ceffe71e; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "FK_875034bdd7f0ac4a726ceffe71e" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE SET NULL; + + +-- +-- Name: wallet_transactions FK_8a94d9d61a2b05123710b325fbf; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallet_transactions + ADD CONSTRAINT "FK_8a94d9d61a2b05123710b325fbf" FOREIGN KEY ("walletId") REFERENCES public.wallets(id) ON DELETE CASCADE; + + +-- +-- Name: discount_redemptions FK_8b48303d6ae52e9aa69e2df2259; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discount_redemptions + ADD CONSTRAINT "FK_8b48303d6ae52e9aa69e2df2259" FOREIGN KEY ("discountId") REFERENCES public.discounts(id) ON DELETE CASCADE; + + +-- +-- Name: applications FK_90ad8bec24861de0180f638b9cc; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.applications + ADD CONSTRAINT "FK_90ad8bec24861de0180f638b9cc" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: application_migration_jobs FK_932030f502543c1116155e7ef14; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "FK_932030f502543c1116155e7ef14" FOREIGN KEY ("targetClusterId") REFERENCES public.clusters(id) ON DELETE RESTRICT; + + +-- +-- Name: application_migration_events FK_93b1269e814ba5dfb106d408440; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_events + ADD CONSTRAINT "FK_93b1269e814ba5dfb106d408440" FOREIGN KEY ("migrationId") REFERENCES public.application_migration_jobs(id) ON DELETE CASCADE; + + +-- +-- Name: verification_codes FK_9a854eeb4598a22d554ecfe6e81; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.verification_codes + ADD CONSTRAINT "FK_9a854eeb4598a22d554ecfe6e81" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: invoice_lines FK_9f57f31e620fe759b452feb776e; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoice_lines + ADD CONSTRAINT "FK_9f57f31e620fe759b452feb776e" FOREIGN KEY ("invoiceId") REFERENCES public.invoices(id) ON DELETE CASCADE; + + +-- +-- Name: ticket_messages FK_b01e2a35417efbe04c10828266f; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ticket_messages + ADD CONSTRAINT "FK_b01e2a35417efbe04c10828266f" FOREIGN KEY ("ticketId") REFERENCES public.tickets(id) ON DELETE CASCADE; + + +-- +-- Name: resource_credits FK_c546fd3ecce0091b717b32b0c5b; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.resource_credits + ADD CONSTRAINT "FK_c546fd3ecce0091b717b32b0c5b" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: cluster_health FK_d38286a989998e278202b43f6c9; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_health + ADD CONSTRAINT "FK_d38286a989998e278202b43f6c9" FOREIGN KEY ("clusterId") REFERENCES public.clusters(id) ON DELETE CASCADE; + + +-- +-- Name: deployments FK_dca3b4d49c7df1e3a6a93b74fd7; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.deployments + ADD CONSTRAINT "FK_dca3b4d49c7df1e3a6a93b74fd7" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: ticket_messages FK_ddea80824c24d270ef2cb4cb0ba; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ticket_messages + ADD CONSTRAINT "FK_ddea80824c24d270ef2cb4cb0ba" FOREIGN KEY ("senderId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: pricing_rules FK_f44e102039feebaf8436a606652; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rules + ADD CONSTRAINT "FK_f44e102039feebaf8436a606652" FOREIGN KEY ("planId") REFERENCES public.service_plans(id) ON DELETE CASCADE; + + +-- +-- Name: application_migration_jobs FK_f68e403cd35eb2b95b1f77355e5; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "FK_f68e403cd35eb2b95b1f77355e5" FOREIGN KEY ("sourceClusterId") REFERENCES public.clusters(id) ON DELETE RESTRICT; + + +-- +-- Name: invoices FK_fcbe490dc37a1abf68f19c5ccb9; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "FK_fcbe490dc37a1abf68f19c5ccb9" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- PostgreSQL database dump complete +-- + + + + +-- Mark superseded legacy pricing migrations as applied (see generator note). +CREATE TABLE IF NOT EXISTS schema_migrations (filename TEXT PRIMARY KEY, applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()); +INSERT INTO schema_migrations (filename) VALUES + ('004_pricing_catalog.sql'), + ('005_pricing_catalog_all_runtimes.sql'), + ('006_addon_rate_resources.sql'), + ('007_optional_service_pricing_matrix.sql'), + ('008_application_optional_service_resources.sql'), + ('009_optional_service_deploy_defaults.sql') +ON CONFLICT (filename) DO NOTHING; diff --git a/backend/helm/cloudhost-platform/migrations/001_service_access_grants.sql b/backend/helm/cloudhost-platform/migrations/001_service_access_grants.sql index d2c6585..1e7c207 100644 --- a/backend/helm/cloudhost-platform/migrations/001_service_access_grants.sql +++ b/backend/helm/cloudhost-platform/migrations/001_service_access_grants.sql @@ -1,16 +1,20 @@ -- Temporary external access grants (Redis, RabbitMQ, database) -CREATE TYPE service_access_target AS ENUM ( - 'database', - 'redis', - 'rabbitmq_amqp', - 'rabbitmq_management' -); +DO $$ BEGIN + CREATE TYPE service_access_target AS ENUM ( + 'database', + 'redis', + 'rabbitmq_amqp', + 'rabbitmq_management' + ); +EXCEPTION WHEN duplicate_object THEN null; END $$; -CREATE TYPE service_access_grant_status AS ENUM ( - 'active', - 'expired', - 'revoked' -); +DO $$ BEGIN + CREATE TYPE service_access_grant_status AS ENUM ( + 'active', + 'expired', + 'revoked' + ); +EXCEPTION WHEN duplicate_object THEN null; END $$; CREATE TABLE IF NOT EXISTS service_access_grants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/backend/helm/cloudhost-platform/migrations/015_application_product_type.sql b/backend/helm/cloudhost-platform/migrations/015_application_product_type.sql index a59885d..a3eaba0 100644 --- a/backend/helm/cloudhost-platform/migrations/015_application_product_type.sql +++ b/backend/helm/cloudhost-platform/migrations/015_application_product_type.sql @@ -3,7 +3,7 @@ ALTER TABLE applications ADD COLUMN IF NOT EXISTS product_type VARCHAR(32) NOT NULL DEFAULT 'application'; CREATE INDEX IF NOT EXISTS idx_applications_user_product_type - ON applications (user_id, product_type); + ON applications ("userId", product_type); ALTER TABLE resource_credits ADD COLUMN IF NOT EXISTS product_type VARCHAR(32) NOT NULL DEFAULT 'application'; diff --git a/backend/helm/cloudhost-platform/templates/backend-deployment.yaml b/backend/helm/cloudhost-platform/templates/backend-deployment.yaml index 78aa7cd..802d35c 100644 --- a/backend/helm/cloudhost-platform/templates/backend-deployment.yaml +++ b/backend/helm/cloudhost-platform/templates/backend-deployment.yaml @@ -9,8 +9,14 @@ metadata: {{- include "cloudhost-platform.labels" . | nindent 4 }} spec: replicas: {{ .Values.backend.replicas }} + # Zero-downtime rollouts: DB migrations run in a pre-upgrade hook Job, so the + # new pod only starts against a ready schema. The uploads PVC is RWO but + # local-path volumes pin pods to the same node, so surge pods can attach. strategy: - type: Recreate + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 0 selector: matchLabels: app: {{ include "cloudhost-platform.backend.fullname" . }} @@ -72,6 +78,11 @@ spec: value: {{ include "cloudhost-platform.redis.fullname" . }} - name: REDIS_PORT value: "6379" + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cloudhost-platform.secretName" . }} + key: redis-password - name: JWT_SECRET valueFrom: secretKeyRef: diff --git a/backend/helm/cloudhost-platform/templates/migrations-job.yaml b/backend/helm/cloudhost-platform/templates/migrations-job.yaml index 0baddb0..89599b4 100644 --- a/backend/helm/cloudhost-platform/templates/migrations-job.yaml +++ b/backend/helm/cloudhost-platform/templates/migrations-job.yaml @@ -7,7 +7,9 @@ metadata: labels: {{- include "cloudhost-platform.labels" . | nindent 4 }} annotations: - helm.sh/hook: post-install,post-upgrade + # Run BEFORE the backend rolls out so schema-dependent code never starts + # against an unmigrated database. + helm.sh/hook: pre-install,pre-upgrade helm.sh/hook-weight: "5" helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded spec: @@ -47,9 +49,20 @@ spec: - -c - | set -e + # Track applied migrations so each file runs exactly once — the + # loop is idempotent across every helm upgrade. + psql -v ON_ERROR_STOP=1 -c "CREATE TABLE IF NOT EXISTS schema_migrations (filename TEXT PRIMARY KEY, applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW());" for f in $(ls /migrations/*.sql | sort); do - echo ">>> Applying $f" - psql -v ON_ERROR_STOP=1 -f "$f" + name=$(basename "$f") + applied=$(psql -tA -c "SELECT 1 FROM schema_migrations WHERE filename = '$name';") + if [ "$applied" = "1" ]; then + echo ">>> Skipping $name (already applied)" + continue + fi + echo ">>> Applying $name" + psql -v ON_ERROR_STOP=1 --single-transaction \ + -f "$f" \ + -c "INSERT INTO schema_migrations (filename) VALUES ('$name');" done echo ">>> All migrations applied" volumeMounts: diff --git a/backend/helm/cloudhost-platform/templates/postgres-backup.yaml b/backend/helm/cloudhost-platform/templates/postgres-backup.yaml index be16986..e1151c9 100644 --- a/backend/helm/cloudhost-platform/templates/postgres-backup.yaml +++ b/backend/helm/cloudhost-platform/templates/postgres-backup.yaml @@ -41,6 +41,8 @@ spec: FILE="/backup/cloudhost-${STAMP}.sql.gz" pg_dump | gzip > "$FILE" echo "Backup written to $FILE" + # Retention: keep the last {{ .Values.backups.postgres.retentionDays | default 7 }} days + find /backup -name 'cloudhost-*.sql.gz' -mtime +{{ .Values.backups.postgres.retentionDays | default 7 }} -delete volumeMounts: - name: backup mountPath: /backup diff --git a/backend/helm/cloudhost-platform/templates/postgres-deployment.yaml b/backend/helm/cloudhost-platform/templates/postgres-deployment.yaml index cf45a5e..b4baba8 100644 --- a/backend/helm/cloudhost-platform/templates/postgres-deployment.yaml +++ b/backend/helm/cloudhost-platform/templates/postgres-deployment.yaml @@ -19,6 +19,10 @@ spec: labels: app: {{ include "cloudhost-platform.postgres.fullname" . }} spec: + {{- with .Values.postgres.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: postgres image: {{ .Values.images.postgres | quote }} diff --git a/backend/helm/cloudhost-platform/templates/redis-deployment.yaml b/backend/helm/cloudhost-platform/templates/redis-deployment.yaml index d8a9d17..5e13613 100644 --- a/backend/helm/cloudhost-platform/templates/redis-deployment.yaml +++ b/backend/helm/cloudhost-platform/templates/redis-deployment.yaml @@ -19,9 +19,26 @@ spec: labels: app: {{ include "cloudhost-platform.redis.fullname" . }} spec: + {{- with .Values.redis.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: redis image: {{ .Values.images.redis | quote }} + args: ["--requirepass", "$(REDIS_PASSWORD)"] + env: + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "cloudhost-platform.secretName" . }} + key: redis-password + # redis-cli reads REDISCLI_AUTH so authenticated probes need no -a flag + - name: REDISCLI_AUTH + valueFrom: + secretKeyRef: + name: {{ include "cloudhost-platform.secretName" . }} + key: redis-password ports: - containerPort: 6379 volumeMounts: diff --git a/backend/helm/cloudhost-platform/templates/secret.yaml b/backend/helm/cloudhost-platform/templates/secret.yaml index c760c6d..5d0f8a2 100644 --- a/backend/helm/cloudhost-platform/templates/secret.yaml +++ b/backend/helm/cloudhost-platform/templates/secret.yaml @@ -22,6 +22,10 @@ Secret out-of-band (e.g. SealedSecret in the gitops repo). {{- if not $kubeconfigKey }} {{- if and $existing (hasKey $existing.data "cluster-kubeconfig-key") }}{{- $kubeconfigKey = index $existing.data "cluster-kubeconfig-key" | b64dec }}{{- else }}{{- $kubeconfigKey = randAlphaNum 32 }}{{- end }} {{- end }} +{{- $redisPass := .Values.redis.password }} +{{- if not $redisPass }} +{{- if and $existing (hasKey $existing.data "redis-password") }}{{- $redisPass = index $existing.data "redis-password" | b64dec }}{{- else }}{{- $redisPass = randAlphaNum 24 }}{{- end }} +{{- end }} apiVersion: v1 kind: Secret metadata: @@ -35,4 +39,5 @@ stringData: jwt-secret: {{ $jwt | quote }} jwt-refresh-secret: {{ $jwtRefresh | quote }} cluster-kubeconfig-key: {{ $kubeconfigKey | quote }} + redis-password: {{ $redisPass | quote }} {{- end }} diff --git a/backend/helm/cloudhost-platform/values-production.example.yaml b/backend/helm/cloudhost-platform/values-production.example.yaml index 0abe039..764641c 100644 --- a/backend/helm/cloudhost-platform/values-production.example.yaml +++ b/backend/helm/cloudhost-platform/values-production.example.yaml @@ -8,6 +8,11 @@ global: storageClass: local-path # k3s example images: + # Mirror Docker Hub images through your private registry so cluster nodes + # never pull from docker.io directly (matches the kaniko/Harbor setup). + postgres: registry.example.com/mirror/postgres:16-alpine + redis: registry.example.com/mirror/redis:7-alpine + busybox: registry.example.com/mirror/busybox:1.36 backend: repository: registry.example.com/cloudhost-backend tag: "1.0.0" @@ -19,6 +24,15 @@ images: postgres: password: "CHANGE_ME_STRONG_POSTGRES_PASSWORD" + # Pull secret for the mirrored postgres image + imagePullSecrets: + - name: registry-pull-secret + +redis: + # Auto-generated and persisted in the platform Secret when left empty. + password: "" + imagePullSecrets: + - name: registry-pull-secret secrets: jwtSecret: "CHANGE_ME_LONG_JWT_SECRET" @@ -44,6 +58,20 @@ backend: PLATFORM_DOMAIN: apps.example.com REGISTRY_URL: registry.cloudhost-builds.svc.cluster.local:5000 REGISTRY_PULL_URL: registry.cloudhost-builds.svc.cluster.local:5000 + # Mirror prefix for base images in generated Dockerfiles + managed services + BASE_IMAGE_REGISTRY: registry.example.com/mirror + # Elastic log-stack credentials (must match the logging namespace Secret) + ELASTIC_PASSWORD: "CHANGE_ME_ELASTIC_PASSWORD" + FLUENTBIT_PASSWORD: "CHANGE_ME_FLUENTBIT_PASSWORD" + KIBANA_SYSTEM_PASSWORD: "CHANGE_ME_KIBANA_PASSWORD" + # Swagger stays off in production; set SWAGGER_ENABLED: "true" to expose it migrations: enabled: true + +backups: + postgres: + enabled: true + schedule: "0 3 * * *" + storageSize: 10Gi + retentionDays: 7 diff --git a/backend/helm/cloudhost-platform/values.yaml b/backend/helm/cloudhost-platform/values.yaml index 2862bc2..7ae047a 100644 --- a/backend/helm/cloudhost-platform/values.yaml +++ b/backend/helm/cloudhost-platform/values.yaml @@ -12,6 +12,9 @@ createNamespace: true global: storageClass: "" +# For clusters without direct docker.io access, point these at your mirror, +# e.g. registry.abrban.com/abrban/postgres:16-alpine, and set +# postgres.imagePullSecrets / redis.imagePullSecrets accordingly. images: postgres: postgres:16-alpine redis: redis:7-alpine @@ -32,12 +35,31 @@ postgres: # Leave empty to auto-generate on first install (stored in Secret) password: "" storage: 10Gi - resources: {} + # Needed when images.postgres points at a private mirror + imagePullSecrets: [] + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "2" + memory: 2Gi redis: enabled: true storage: 1Gi - resources: {} + # Leave empty to auto-generate on first install (stored in Secret as redis-password). + # With secrets.existingSecret, that Secret must also contain a redis-password key. + password: "" + # Needed when images.redis points at a private mirror + imagePullSecrets: [] + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 500m + memory: 512Mi backend: enabled: true @@ -49,7 +71,13 @@ backend: sourceStorage: enabled: false existingSecret: ceph-app-sources-credentials - resources: {} + resources: + requests: + cpu: 250m + memory: 512Mi + limits: + cpu: "2" + memory: 2Gi extraEnv: {} env: NODE_ENV: production @@ -73,7 +101,13 @@ frontend: replicas: 1 imagePullSecrets: - name: registry-pull-secret - resources: {} + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: "1" + memory: 1Gi # JWT secrets — set in production (values-production.example.yaml) secrets: @@ -117,6 +151,7 @@ monitoring: backups: postgres: - enabled: false + enabled: true schedule: "0 3 * * *" storageSize: 10Gi + retentionDays: 7 diff --git a/backend/k8s/logging/elasticsearch-stack.yaml b/backend/k8s/logging/elasticsearch-stack.yaml index a25c18d..cb8c3d8 100644 --- a/backend/k8s/logging/elasticsearch-stack.yaml +++ b/backend/k8s/logging/elasticsearch-stack.yaml @@ -9,18 +9,16 @@ metadata: labels: app.kubernetes.io/managed-by: cloudhost --- -# Elasticsearch credentials secret -apiVersion: v1 -kind: Secret -metadata: - name: elasticsearch-credentials - namespace: logging -type: Opaque -stringData: - # Admin credentials - change in production! - ELASTIC_PASSWORD: "CloudHost2024!Secure" - # For Fluent Bit to send logs - FLUENTBIT_PASSWORD: "FluentBit2024!Writer" +# Elasticsearch credentials — managed OUT-OF-BAND, never committed to git. +# Create the Secret before applying this manifest (or use a SealedSecret in +# the GitOps repo): +# +# kubectl -n logging create secret generic elasticsearch-credentials \ +# --from-literal=ELASTIC_PASSWORD="$(openssl rand -base64 24)" \ +# --from-literal=FLUENTBIT_PASSWORD="$(openssl rand -base64 24)" +# +# The backend reads the same values from ELASTIC_PASSWORD / FLUENTBIT_PASSWORD +# env vars (see cloudhost-platform values: backend.extraEnv or an extra Secret). --- # ConfigMap for Elasticsearch configuration apiVersion: v1 diff --git a/backend/migrations/000_base_schema.sql b/backend/migrations/000_base_schema.sql new file mode 100644 index 0000000..90ce330 --- /dev/null +++ b/backend/migrations/000_base_schema.sql @@ -0,0 +1,1638 @@ +-- 000_base_schema.sql — greenfield base schema (generated from TypeORM entities). +-- Auto-generated by scripts/generate-base-schema.mjs. Do not edit by hand. +-- Incremental migrations (001+) run afterwards on top of this schema. + +-- +-- PostgreSQL database dump +-- + + +-- Dumped from database version 16.13 +-- Dumped by pg_dump version 16.13 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: public; Type: SCHEMA; Schema: -; Owner: - +-- + +-- *not* creating schema, since initdb creates it + + +-- +-- Name: SCHEMA public; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON SCHEMA public IS ''; + + +-- +-- Name: uuid-ossp; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS "uuid-ossp" WITH SCHEMA public; + + +-- +-- Name: EXTENSION "uuid-ossp"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON EXTENSION "uuid-ossp" IS 'generate universally unique identifiers (UUIDs)'; + + +-- +-- Name: addon_rates_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.addon_rates_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: applications_billingcycle_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_billingcycle_enum AS ENUM ( + 'hourly', + 'monthly', + 'yearly' +); + + +-- +-- Name: applications_customdomainstatus_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_customdomainstatus_enum AS ENUM ( + 'none', + 'pending_dns', + 'verified' +); + + +-- +-- Name: applications_databasetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_databasetype_enum AS ENUM ( + 'mysql', + 'postgresql', + 'mongodb', + 'mariadb', + 'none' +); + + +-- +-- Name: applications_lifecyclestatus_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_lifecyclestatus_enum AS ENUM ( + 'active', + 'suspended', + 'pending_deletion', + 'docked', + 'deleted' +); + + +-- +-- Name: applications_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.applications_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: clusters_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.clusters_status_enum AS ENUM ( + 'active', + 'inactive', + 'maintenance' +); + + +-- +-- Name: deployments_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.deployments_status_enum AS ENUM ( + 'pending', + 'building', + 'build_failed', + 'deploying', + 'running', + 'failed', + 'cancelled', + 'stopped', + 'deleting' +); + + +-- +-- Name: invoices_paymentmethod_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.invoices_paymentmethod_enum AS ENUM ( + 'wallet', + 'gateway', + 'mixed' +); + + +-- +-- Name: invoices_reason_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.invoices_reason_enum AS ENUM ( + 'deploy', + 'renewal', + 'upgrade', + 'wallet_topup', + 'manual' +); + + +-- +-- Name: invoices_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.invoices_status_enum AS ENUM ( + 'draft', + 'issued', + 'partially_paid', + 'paid', + 'void', + 'failed' +); + + +-- +-- Name: optional_service_profiles_service_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.optional_service_profiles_service_enum AS ENUM ( + 'redis', + 'rabbitmq', + 'elasticsearch' +); + + +-- +-- Name: optional_service_rates_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.optional_service_rates_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: optional_service_rates_service_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.optional_service_rates_service_enum AS ENUM ( + 'redis', + 'rabbitmq', + 'elasticsearch' +); + + +-- +-- Name: pricing_rates_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.pricing_rates_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: pricing_rates_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.pricing_rates_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: pricing_rules_resourcetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.pricing_rules_resourcetype_enum AS ENUM ( + 'base_fee', + 'cpu_per_core', + 'memory_per_gb', + 'storage_per_gb', + 'database_addon', + 'redis_addon', + 'rabbitmq_addon', + 'elasticsearch_addon', + 'custom_domain_addon' +); + + +-- +-- Name: resource_credits_billingcycle_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.resource_credits_billingcycle_enum AS ENUM ( + 'hourly', + 'monthly', + 'yearly' +); + + +-- +-- Name: resource_credits_databasetype_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.resource_credits_databasetype_enum AS ENUM ( + 'mysql', + 'postgresql', + 'mongodb', + 'mariadb', + 'none' +); + + +-- +-- Name: resource_credits_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.resource_credits_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: service_access_grants_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_access_grants_status_enum AS ENUM ( + 'active', + 'expired', + 'revoked' +); + + +-- +-- Name: service_access_grants_target_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_access_grants_target_enum AS ENUM ( + 'database', + 'redis', + 'rabbitmq_amqp', + 'rabbitmq_management' +); + + +-- +-- Name: service_plans_billingcycle_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_plans_billingcycle_enum AS ENUM ( + 'hourly', + 'monthly', + 'yearly' +); + + +-- +-- Name: service_plans_runtime_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.service_plans_runtime_enum AS ENUM ( + 'nodejs', + 'laravel', + 'wordpress', + 'go', + 'php', + 'python', + 'django', + 'dotnet' +); + + +-- +-- Name: snapshots_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.snapshots_status_enum AS ENUM ( + 'in_progress', + 'completed', + 'failed' +); + + +-- +-- Name: snapshots_type_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.snapshots_type_enum AS ENUM ( + 'pre_deploy', + 'manual' +); + + +-- +-- Name: ticket_messages_senderrole_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.ticket_messages_senderrole_enum AS ENUM ( + 'user', + 'admin', + 'technical', + 'sales' +); + + +-- +-- Name: tickets_department_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tickets_department_enum AS ENUM ( + 'technical', + 'sales' +); + + +-- +-- Name: tickets_priority_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tickets_priority_enum AS ENUM ( + 'low', + 'medium', + 'high' +); + + +-- +-- Name: tickets_status_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.tickets_status_enum AS ENUM ( + 'open', + 'answered', + 'waiting', + 'closed' +); + + +-- +-- Name: users_role_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.users_role_enum AS ENUM ( + 'user', + 'admin', + 'technical', + 'sales' +); + + +-- +-- Name: verification_codes_purpose_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.verification_codes_purpose_enum AS ENUM ( + 'login', + 'change_phone' +); + + +-- +-- Name: wallet_transactions_type_enum; Type: TYPE; Schema: public; Owner: - +-- + +CREATE TYPE public.wallet_transactions_type_enum AS ENUM ( + 'charge', + 'deduction', + 'refund', + 'gateway_payment' +); + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: addon_rates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.addon_rates ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "resourceType" public.addon_rates_resourcetype_enum NOT NULL, + "hourlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "monthlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "yearlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: application_migration_events; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.application_migration_events ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "migrationId" uuid NOT NULL, + step character varying NOT NULL, + level character varying DEFAULT 'info'::character varying NOT NULL, + message character varying NOT NULL, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: application_migration_jobs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.application_migration_jobs ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "applicationId" uuid NOT NULL, + "requestedBy" character varying NOT NULL, + "sourceClusterId" uuid NOT NULL, + "targetClusterId" uuid NOT NULL, + status character varying DEFAULT 'queued'::character varying NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + "maxAttempts" integer DEFAULT 3 NOT NULL, + "currentStep" character varying, + "errorMessage" character varying, + metadata jsonb, + "startedAt" timestamp with time zone, + "completedAt" timestamp with time zone, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: applications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.applications ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + "productType" character varying DEFAULT 'application'::character varying NOT NULL, + description character varying, + runtime public.applications_runtime_enum NOT NULL, + "databaseType" public.applications_databasetype_enum DEFAULT 'none'::public.applications_databasetype_enum NOT NULL, + "runtimeVersion" character varying, + "phpVersion" character varying, + "dbVersion" character varying, + "dbUsername" character varying, + "dbPassword" character varying, + "dbStorageSize" character varying DEFAULT '1Gi'::character varying, + "appStorageSize" character varying DEFAULT '2Gi'::character varying, + "enableRedis" boolean DEFAULT false NOT NULL, + "redisVersion" character varying DEFAULT '7.2'::character varying, + "enableRabbitmq" boolean DEFAULT false NOT NULL, + "rabbitmqVersion" character varying DEFAULT '3.13'::character varying, + "enableElasticsearch" boolean DEFAULT false NOT NULL, + "elasticsearchVersion" character varying DEFAULT '8.12'::character varying, + "logPaths" jsonb, + "optionalServiceResources" jsonb, + "gitUrl" character varying, + "gitToken" character varying, + "gitBranch" character varying, + "codePath" character varying, + "dbDumpPath" character varying, + "envVars" jsonb, + "cpuRequest" character varying DEFAULT '100m'::character varying NOT NULL, + "cpuLimit" character varying DEFAULT '500m'::character varying NOT NULL, + "memoryRequest" character varying DEFAULT '128Mi'::character varying NOT NULL, + "memoryLimit" character varying DEFAULT '512Mi'::character varying NOT NULL, + replicas integer DEFAULT 1 NOT NULL, + port integer DEFAULT 3000 NOT NULL, + "userId" uuid NOT NULL, + "clusterId" uuid, + "poolId" uuid, + "latestImageTag" character varying, + subdomain character varying, + "customDomain" character varying, + "customDomainStatus" public.applications_customdomainstatus_enum DEFAULT 'none'::public.applications_customdomainstatus_enum NOT NULL, + "customDomainVerifiedAt" timestamp with time zone, + "planId" character varying, + "billingCycle" public.applications_billingcycle_enum, + "lifecycleStatus" public.applications_lifecyclestatus_enum DEFAULT 'active'::public.applications_lifecyclestatus_enum NOT NULL, + "planExpiresAt" timestamp with time zone, + "suspendedAt" timestamp with time zone, + "suspendedReplicas" jsonb, + "scheduledDeletionAt" timestamp with time zone, + "dockedAt" timestamp with time zone, + "dockSnapshotId" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: audit_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.audit_logs ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + action character varying NOT NULL, + "actorUserId" uuid NOT NULL, + "targetUserId" uuid, + metadata jsonb, + ip character varying, + "userAgent" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: cluster_allocation_logs; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.cluster_allocation_logs ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "applicationId" uuid, + "userId" character varying NOT NULL, + "poolId" uuid, + "selectedClusterId" uuid, + strategy character varying DEFAULT 'weighted-resource'::character varying NOT NULL, + "estimatedRequest" jsonb, + "candidateScores" jsonb, + "rejectionReasons" jsonb, + status character varying DEFAULT 'success'::character varying NOT NULL, + message character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: cluster_health; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.cluster_health ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "clusterId" uuid NOT NULL, + status character varying DEFAULT 'unknown'::character varying NOT NULL, + "readyNodes" integer DEFAULT 0 NOT NULL, + "nodeCount" integer DEFAULT 0 NOT NULL, + "cpuAllocatable" character varying, + "memoryAllocatable" character varying, + "podCount" integer DEFAULT 0 NOT NULL, + "appCount" integer DEFAULT 0 NOT NULL, + message character varying, + resources jsonb, + "checkedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: cluster_pools; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.cluster_pools ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + description character varying, + strategy character varying DEFAULT 'weighted-resource'::character varying NOT NULL, + "clusterIds" jsonb DEFAULT '[]'::jsonb NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "isDefault" boolean DEFAULT false NOT NULL, + priority integer DEFAULT 100 NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: clusters; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.clusters ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + description character varying, + status public.clusters_status_enum DEFAULT 'active'::public.clusters_status_enum NOT NULL, + kubeconfig text NOT NULL, + "apiServer" character varying NOT NULL, + region character varying, + weight integer DEFAULT 1 NOT NULL, + tags jsonb DEFAULT '[]'::jsonb NOT NULL, + "healthStatus" character varying DEFAULT 'unknown'::character varying NOT NULL, + "lastHealthCheckedAt" timestamp with time zone, + "healthMessage" character varying, + "availableResources" jsonb, + provider character varying, + "isDefault" boolean DEFAULT false NOT NULL, + "defaultCpuLimit" character varying DEFAULT '4'::character varying NOT NULL, + "defaultMemoryLimit" character varying DEFAULT '8Gi'::character varying NOT NULL, + "maxAppsPerUser" integer DEFAULT 10 NOT NULL, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: deployments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.deployments ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + status public.deployments_status_enum DEFAULT 'pending'::public.deployments_status_enum NOT NULL, + "imageTag" character varying NOT NULL, + version character varying, + "k8sResources" jsonb, + "buildLog" text, + "deployLog" text, + "previewSubdomain" character varying(63), + "errorMessage" character varying, + "applicationId" uuid NOT NULL, + "triggeredBy" character varying NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL, + "finishedAt" timestamp without time zone +); + + +-- +-- Name: discount_redemptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.discount_redemptions ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "discountId" uuid NOT NULL, + "userId" character varying NOT NULL, + "invoiceId" character varying, + amount numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: discounts; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.discounts ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + code character varying NOT NULL, + name character varying NOT NULL, + description character varying, + "percentOff" integer NOT NULL, + services jsonb DEFAULT '[]'::jsonb NOT NULL, + "isPublic" boolean DEFAULT true NOT NULL, + "allowedUserIds" jsonb DEFAULT '[]'::jsonb NOT NULL, + "maxUses" integer, + "maxUsesPerUser" integer, + "usedCount" integer DEFAULT 0 NOT NULL, + "startsAt" timestamp with time zone, + "endsAt" timestamp with time zone, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: invoice_lines; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.invoice_lines ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "invoiceId" uuid NOT NULL, + label character varying NOT NULL, + description character varying, + quantity integer DEFAULT 1 NOT NULL, + "unitAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + amount numeric(14,2) DEFAULT '0'::numeric NOT NULL, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: invoices; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.invoices ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "invoiceNumber" character varying NOT NULL, + "userId" uuid NOT NULL, + "applicationId" uuid, + reason public.invoices_reason_enum DEFAULT 'manual'::public.invoices_reason_enum NOT NULL, + status public.invoices_status_enum DEFAULT 'issued'::public.invoices_status_enum NOT NULL, + "paymentMethod" public.invoices_paymentmethod_enum, + subtotal numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "discountAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "discountCode" character varying, + total numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "paidAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "dueAmount" numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "dueDate" timestamp with time zone, + "paidAt" timestamp with time zone, + "gatewayTrackingCode" character varying, + "gatewayReference" character varying, + "adminNote" character varying, + "statusReason" character varying, + metadata jsonb, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: optional_service_profiles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.optional_service_profiles ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + service public.optional_service_profiles_service_enum NOT NULL, + "cpuRequest" character varying, + "memoryRequest" character varying, + "cpuLimit" character varying DEFAULT '200m'::character varying NOT NULL, + "memoryLimit" character varying DEFAULT '256Mi'::character varying NOT NULL, + "storageGi" numeric(10,2) DEFAULT '1'::numeric NOT NULL, + "logShipperCpuLimit" character varying, + "logShipperMemoryLimit" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: optional_service_rates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.optional_service_rates ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + service public.optional_service_rates_service_enum NOT NULL, + "resourceType" public.optional_service_rates_resourcetype_enum NOT NULL, + "hourlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "monthlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "yearlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: platform_settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.platform_settings ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + key character varying NOT NULL, + value text NOT NULL, + description character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: pricing_rates; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.pricing_rates ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + runtime public.pricing_rates_runtime_enum NOT NULL, + "resourceType" public.pricing_rates_resourcetype_enum NOT NULL, + "hourlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "monthlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "yearlyPrice" numeric(12,2) DEFAULT '0'::numeric NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: pricing_rules; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.pricing_rules ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "resourceType" public.pricing_rules_resourcetype_enum NOT NULL, + "unitPrice" numeric(12,2) NOT NULL, + description character varying, + "planId" uuid NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: resource_credits; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.resource_credits ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "userId" uuid NOT NULL, + "sourceAppName" character varying, + "productType" character varying DEFAULT 'application'::character varying NOT NULL, + runtime public.resource_credits_runtime_enum NOT NULL, + "databaseType" public.resource_credits_databasetype_enum NOT NULL, + "cpuLimit" character varying NOT NULL, + "memoryLimit" character varying NOT NULL, + replicas integer DEFAULT 1 NOT NULL, + "dbStorageSize" character varying, + "appStorageSize" character varying, + "enableRedis" boolean DEFAULT false NOT NULL, + "enableRabbitmq" boolean DEFAULT false NOT NULL, + "enableElasticsearch" boolean DEFAULT false NOT NULL, + "billingCycle" public.resource_credits_billingcycle_enum, + "expiresAt" timestamp with time zone NOT NULL, + "consumedAt" timestamp with time zone, + "appliedApplicationId" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: service_access_grants; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.service_access_grants ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "applicationId" uuid NOT NULL, + "userId" character varying NOT NULL, + "clusterId" character varying NOT NULL, + namespace character varying NOT NULL, + target public.service_access_grants_target_enum NOT NULL, + "nodePort" integer NOT NULL, + "targetPort" integer NOT NULL, + host character varying NOT NULL, + "k8sServiceName" character varying NOT NULL, + status public.service_access_grants_status_enum DEFAULT 'active'::public.service_access_grants_status_enum NOT NULL, + persistent boolean DEFAULT false NOT NULL, + "expiresAt" timestamp with time zone NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: service_plans; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.service_plans ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + name character varying NOT NULL, + runtime public.service_plans_runtime_enum NOT NULL, + description character varying, + "billingCycle" public.service_plans_billingcycle_enum NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: snapshots; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.snapshots ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + type public.snapshots_type_enum DEFAULT 'manual'::public.snapshots_type_enum NOT NULL, + status public.snapshots_status_enum DEFAULT 'in_progress'::public.snapshots_status_enum NOT NULL, + label character varying, + "appArchivePath" character varying, + "wpContentArchivePath" character varying, + "imageTag" character varying, + "dbDumpPath" character varying, + "hasDatabase" boolean DEFAULT false NOT NULL, + "appArchiveSize" bigint, + "dbDumpSize" bigint, + "wpContentSize" bigint, + "errorMessage" character varying, + progress integer DEFAULT 0 NOT NULL, + "applicationId" uuid NOT NULL, + "createdBy" character varying NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: ticket_messages; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.ticket_messages ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + message text NOT NULL, + "ticketId" uuid NOT NULL, + "senderId" uuid NOT NULL, + "senderRole" public.ticket_messages_senderrole_enum NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: tickets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.tickets ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + subject character varying NOT NULL, + department public.tickets_department_enum NOT NULL, + status public.tickets_status_enum DEFAULT 'open'::public.tickets_status_enum NOT NULL, + priority public.tickets_priority_enum DEFAULT 'medium'::public.tickets_priority_enum NOT NULL, + "userId" uuid NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL, + "closedAt" timestamp without time zone +); + + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.users ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + phone character varying, + email character varying, + "phoneVerified" boolean DEFAULT false NOT NULL, + password character varying NOT NULL, + "firstName" character varying NOT NULL, + "lastName" character varying NOT NULL, + role public.users_role_enum DEFAULT 'user'::public.users_role_enum NOT NULL, + "isActive" boolean DEFAULT true NOT NULL, + namespace character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: verification_codes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.verification_codes ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + "userId" uuid NOT NULL, + purpose public.verification_codes_purpose_enum NOT NULL, + destination character varying NOT NULL, + "codeHash" character varying NOT NULL, + "expiresAt" timestamp with time zone NOT NULL, + attempts integer DEFAULT 0 NOT NULL, + "consumedAt" timestamp with time zone, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: wallet_transactions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.wallet_transactions ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + type public.wallet_transactions_type_enum NOT NULL, + amount numeric(14,2) NOT NULL, + "balanceAfter" numeric(14,2) NOT NULL, + description character varying, + "applicationId" character varying, + "invoiceId" uuid, + "walletId" uuid NOT NULL, + "gatewayTrackingCode" character varying, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: wallets; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.wallets ( + id uuid DEFAULT public.uuid_generate_v4() NOT NULL, + balance numeric(14,2) DEFAULT '0'::numeric NOT NULL, + "userId" uuid NOT NULL, + "createdAt" timestamp without time zone DEFAULT now() NOT NULL, + "updatedAt" timestamp without time zone DEFAULT now() NOT NULL +); + + +-- +-- Name: application_migration_jobs PK_130c380307e6aec30012c92e932; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "PK_130c380307e6aec30012c92e932" PRIMARY KEY (id); + + +-- +-- Name: verification_codes PK_18741b6b8bf1680dbf5057421d7; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.verification_codes + ADD CONSTRAINT "PK_18741b6b8bf1680dbf5057421d7" PRIMARY KEY (id); + + +-- +-- Name: audit_logs PK_1bb179d048bbc581caa3b013439; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.audit_logs + ADD CONSTRAINT "PK_1bb179d048bbc581caa3b013439" PRIMARY KEY (id); + + +-- +-- Name: deployments PK_1e5627acb3c950deb83fe98fc48; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.deployments + ADD CONSTRAINT "PK_1e5627acb3c950deb83fe98fc48" PRIMARY KEY (id); + + +-- +-- Name: optional_service_profiles PK_2625f60eb9be17174299de23f47; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_profiles + ADD CONSTRAINT "PK_2625f60eb9be17174299de23f47" PRIMARY KEY (id); + + +-- +-- Name: platform_settings PK_2934aeb70ec285196dcab4a2e96; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.platform_settings + ADD CONSTRAINT "PK_2934aeb70ec285196dcab4a2e96" PRIMARY KEY (id); + + +-- +-- Name: discount_redemptions PK_2efe685154f7c7deacaf6f0ef63; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discount_redemptions + ADD CONSTRAINT "PK_2efe685154f7c7deacaf6f0ef63" PRIMARY KEY (id); + + +-- +-- Name: tickets PK_343bc942ae261cf7a1377f48fd0; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tickets + ADD CONSTRAINT "PK_343bc942ae261cf7a1377f48fd0" PRIMARY KEY (id); + + +-- +-- Name: cluster_allocation_logs PK_34d1148bb1df92decfcdc7cab87; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "PK_34d1148bb1df92decfcdc7cab87" PRIMARY KEY (id); + + +-- +-- Name: ticket_messages PK_37beb692dedf7eccb4e519ccec1; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ticket_messages + ADD CONSTRAINT "PK_37beb692dedf7eccb4e519ccec1" PRIMARY KEY (id); + + +-- +-- Name: invoice_lines PK_3d18eb48142b916f581f0c21a65; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoice_lines + ADD CONSTRAINT "PK_3d18eb48142b916f581f0c21a65" PRIMARY KEY (id); + + +-- +-- Name: wallet_transactions PK_5120f131bde2cda940ec1a621db; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallet_transactions + ADD CONSTRAINT "PK_5120f131bde2cda940ec1a621db" PRIMARY KEY (id); + + +-- +-- Name: clusters PK_56c8e201f375e1e961dcdd6831c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.clusters + ADD CONSTRAINT "PK_56c8e201f375e1e961dcdd6831c" PRIMARY KEY (id); + + +-- +-- Name: invoices PK_668cef7c22a427fd822cc1be3ce; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "PK_668cef7c22a427fd822cc1be3ce" PRIMARY KEY (id); + + +-- +-- Name: discounts PK_66c522004212dc814d6e2f14ecc; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discounts + ADD CONSTRAINT "PK_66c522004212dc814d6e2f14ecc" PRIMARY KEY (id); + + +-- +-- Name: service_plans PK_679a9e435f1af95a94d9749a087; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.service_plans + ADD CONSTRAINT "PK_679a9e435f1af95a94d9749a087" PRIMARY KEY (id); + + +-- +-- Name: resource_credits PK_6a1f7bee5bd45667c08a63d9e3c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.resource_credits + ADD CONSTRAINT "PK_6a1f7bee5bd45667c08a63d9e3c" PRIMARY KEY (id); + + +-- +-- Name: pricing_rates PK_6b3ac518f3abafc7d0e1b6bb449; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rates + ADD CONSTRAINT "PK_6b3ac518f3abafc7d0e1b6bb449" PRIMARY KEY (id); + + +-- +-- Name: addon_rates PK_817008cdfd7d2040209665de3cf; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_rates + ADD CONSTRAINT "PK_817008cdfd7d2040209665de3cf" PRIMARY KEY (id); + + +-- +-- Name: wallets PK_8402e5df5a30a229380e83e4f7e; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "PK_8402e5df5a30a229380e83e4f7e" PRIMARY KEY (id); + + +-- +-- Name: applications PK_938c0a27255637bde919591888f; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.applications + ADD CONSTRAINT "PK_938c0a27255637bde919591888f" PRIMARY KEY (id); + + +-- +-- Name: users PK_a3ffb1c0c8416b9fc6f907b7433; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "PK_a3ffb1c0c8416b9fc6f907b7433" PRIMARY KEY (id); + + +-- +-- Name: cluster_health PK_a619d68317e11de0cdc7769936c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_health + ADD CONSTRAINT "PK_a619d68317e11de0cdc7769936c" PRIMARY KEY (id); + + +-- +-- Name: service_access_grants PK_ba880e7036fb12629d2bb20813b; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.service_access_grants + ADD CONSTRAINT "PK_ba880e7036fb12629d2bb20813b" PRIMARY KEY (id); + + +-- +-- Name: application_migration_events PK_cbbcd35db024c6ce5282dc4b826; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_events + ADD CONSTRAINT "PK_cbbcd35db024c6ce5282dc4b826" PRIMARY KEY (id); + + +-- +-- Name: cluster_pools PK_cd7c2f36783be8d628956f41734; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_pools + ADD CONSTRAINT "PK_cd7c2f36783be8d628956f41734" PRIMARY KEY (id); + + +-- +-- Name: optional_service_rates PK_dde26d2c02f198afe455543e16c; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_rates + ADD CONSTRAINT "PK_dde26d2c02f198afe455543e16c" PRIMARY KEY (id); + + +-- +-- Name: snapshots PK_f5661b5fd4224d23e26a631986b; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.snapshots + ADD CONSTRAINT "PK_f5661b5fd4224d23e26a631986b" PRIMARY KEY (id); + + +-- +-- Name: pricing_rules PK_fda27bb8db4630894decda61ff6; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rules + ADD CONSTRAINT "PK_fda27bb8db4630894decda61ff6" PRIMARY KEY (id); + + +-- +-- Name: wallets UQ_2ecdb33f23e9a6fc392025c0b97; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "UQ_2ecdb33f23e9a6fc392025c0b97" UNIQUE ("userId"); + + +-- +-- Name: platform_settings UQ_5d9031e30fac3ec3ec8b9602e17; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.platform_settings + ADD CONSTRAINT "UQ_5d9031e30fac3ec3ec8b9602e17" UNIQUE (key); + + +-- +-- Name: optional_service_profiles UQ_63c79ca1dc0b57933b800469504; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_profiles + ADD CONSTRAINT "UQ_63c79ca1dc0b57933b800469504" UNIQUE (service); + + +-- +-- Name: pricing_rates UQ_8323897b5a47d20ef92896dc5a8; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rates + ADD CONSTRAINT "UQ_8323897b5a47d20ef92896dc5a8" UNIQUE (runtime, "resourceType"); + + +-- +-- Name: discounts UQ_8c7cc2340e9ea0fc5a246e63749; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discounts + ADD CONSTRAINT "UQ_8c7cc2340e9ea0fc5a246e63749" UNIQUE (code); + + +-- +-- Name: users UQ_97672ac88f789774dd47f7c8be3; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "UQ_97672ac88f789774dd47f7c8be3" UNIQUE (email); + + +-- +-- Name: users UQ_a000cca60bcf04454e727699490; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.users + ADD CONSTRAINT "UQ_a000cca60bcf04454e727699490" UNIQUE (phone); + + +-- +-- Name: invoices UQ_bf8e0f9dd4558ef209ec111782d; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "UQ_bf8e0f9dd4558ef209ec111782d" UNIQUE ("invoiceNumber"); + + +-- +-- Name: optional_service_rates UQ_da71e6eb43a191e589c4e1d32f6; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.optional_service_rates + ADD CONSTRAINT "UQ_da71e6eb43a191e589c4e1d32f6" UNIQUE (service, "resourceType"); + + +-- +-- Name: addon_rates UQ_ef90e5f423c8ff2fd3b321763e8; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.addon_rates + ADD CONSTRAINT "UQ_ef90e5f423c8ff2fd3b321763e8" UNIQUE ("resourceType"); + + +-- +-- Name: IDX_43facb440ecfb2acadcf2922b5; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_43facb440ecfb2acadcf2922b5" ON public.service_access_grants USING btree ("applicationId", target, status); + + +-- +-- Name: IDX_5b7fbc8045a0654e5f8db27dc5; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_5b7fbc8045a0654e5f8db27dc5" ON public.audit_logs USING btree ("targetUserId"); + + +-- +-- Name: IDX_b455a8c210f79a873760a92819; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_b455a8c210f79a873760a92819" ON public.verification_codes USING btree ("userId", purpose); + + +-- +-- Name: IDX_c1e88bb93860db181f5b21a306; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_c1e88bb93860db181f5b21a306" ON public.discount_redemptions USING btree ("discountId", "userId"); + + +-- +-- Name: IDX_e36d23e1e7cf81ea77758bef79; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "IDX_e36d23e1e7cf81ea77758bef79" ON public.audit_logs USING btree ("actorUserId"); + + +-- +-- Name: application_migration_jobs FK_12568b7d01ef97d05da755833f3; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "FK_12568b7d01ef97d05da755833f3" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: wallets FK_2ecdb33f23e9a6fc392025c0b97; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallets + ADD CONSTRAINT "FK_2ecdb33f23e9a6fc392025c0b97" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: cluster_allocation_logs FK_3b1bba8052f0504c2ff400f70a4; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "FK_3b1bba8052f0504c2ff400f70a4" FOREIGN KEY ("selectedClusterId") REFERENCES public.clusters(id) ON DELETE SET NULL; + + +-- +-- Name: cluster_allocation_logs FK_3ef9df9c9317fab60fdcb5f581b; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "FK_3ef9df9c9317fab60fdcb5f581b" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE SET NULL; + + +-- +-- Name: cluster_allocation_logs FK_44764afc850c4b3a8c62547c2a9; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_allocation_logs + ADD CONSTRAINT "FK_44764afc850c4b3a8c62547c2a9" FOREIGN KEY ("poolId") REFERENCES public.cluster_pools(id) ON DELETE SET NULL; + + +-- +-- Name: service_access_grants FK_48b102a7593a5e76d9fda39dfb0; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.service_access_grants + ADD CONSTRAINT "FK_48b102a7593a5e76d9fda39dfb0" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: tickets FK_4bb45e096f521845765f657f5c8; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.tickets + ADD CONSTRAINT "FK_4bb45e096f521845765f657f5c8" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: snapshots FK_5652b27d83628dc5612245d62ce; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.snapshots + ADD CONSTRAINT "FK_5652b27d83628dc5612245d62ce" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: wallet_transactions FK_581a824e911b9baa6be19cd92fc; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallet_transactions + ADD CONSTRAINT "FK_581a824e911b9baa6be19cd92fc" FOREIGN KEY ("invoiceId") REFERENCES public.invoices(id) ON DELETE SET NULL; + + +-- +-- Name: invoices FK_875034bdd7f0ac4a726ceffe71e; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "FK_875034bdd7f0ac4a726ceffe71e" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE SET NULL; + + +-- +-- Name: wallet_transactions FK_8a94d9d61a2b05123710b325fbf; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.wallet_transactions + ADD CONSTRAINT "FK_8a94d9d61a2b05123710b325fbf" FOREIGN KEY ("walletId") REFERENCES public.wallets(id) ON DELETE CASCADE; + + +-- +-- Name: discount_redemptions FK_8b48303d6ae52e9aa69e2df2259; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.discount_redemptions + ADD CONSTRAINT "FK_8b48303d6ae52e9aa69e2df2259" FOREIGN KEY ("discountId") REFERENCES public.discounts(id) ON DELETE CASCADE; + + +-- +-- Name: applications FK_90ad8bec24861de0180f638b9cc; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.applications + ADD CONSTRAINT "FK_90ad8bec24861de0180f638b9cc" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: application_migration_jobs FK_932030f502543c1116155e7ef14; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "FK_932030f502543c1116155e7ef14" FOREIGN KEY ("targetClusterId") REFERENCES public.clusters(id) ON DELETE RESTRICT; + + +-- +-- Name: application_migration_events FK_93b1269e814ba5dfb106d408440; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_events + ADD CONSTRAINT "FK_93b1269e814ba5dfb106d408440" FOREIGN KEY ("migrationId") REFERENCES public.application_migration_jobs(id) ON DELETE CASCADE; + + +-- +-- Name: verification_codes FK_9a854eeb4598a22d554ecfe6e81; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.verification_codes + ADD CONSTRAINT "FK_9a854eeb4598a22d554ecfe6e81" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: invoice_lines FK_9f57f31e620fe759b452feb776e; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoice_lines + ADD CONSTRAINT "FK_9f57f31e620fe759b452feb776e" FOREIGN KEY ("invoiceId") REFERENCES public.invoices(id) ON DELETE CASCADE; + + +-- +-- Name: ticket_messages FK_b01e2a35417efbe04c10828266f; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ticket_messages + ADD CONSTRAINT "FK_b01e2a35417efbe04c10828266f" FOREIGN KEY ("ticketId") REFERENCES public.tickets(id) ON DELETE CASCADE; + + +-- +-- Name: resource_credits FK_c546fd3ecce0091b717b32b0c5b; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.resource_credits + ADD CONSTRAINT "FK_c546fd3ecce0091b717b32b0c5b" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: cluster_health FK_d38286a989998e278202b43f6c9; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.cluster_health + ADD CONSTRAINT "FK_d38286a989998e278202b43f6c9" FOREIGN KEY ("clusterId") REFERENCES public.clusters(id) ON DELETE CASCADE; + + +-- +-- Name: deployments FK_dca3b4d49c7df1e3a6a93b74fd7; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.deployments + ADD CONSTRAINT "FK_dca3b4d49c7df1e3a6a93b74fd7" FOREIGN KEY ("applicationId") REFERENCES public.applications(id) ON DELETE CASCADE; + + +-- +-- Name: ticket_messages FK_ddea80824c24d270ef2cb4cb0ba; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.ticket_messages + ADD CONSTRAINT "FK_ddea80824c24d270ef2cb4cb0ba" FOREIGN KEY ("senderId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- Name: pricing_rules FK_f44e102039feebaf8436a606652; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.pricing_rules + ADD CONSTRAINT "FK_f44e102039feebaf8436a606652" FOREIGN KEY ("planId") REFERENCES public.service_plans(id) ON DELETE CASCADE; + + +-- +-- Name: application_migration_jobs FK_f68e403cd35eb2b95b1f77355e5; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.application_migration_jobs + ADD CONSTRAINT "FK_f68e403cd35eb2b95b1f77355e5" FOREIGN KEY ("sourceClusterId") REFERENCES public.clusters(id) ON DELETE RESTRICT; + + +-- +-- Name: invoices FK_fcbe490dc37a1abf68f19c5ccb9; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.invoices + ADD CONSTRAINT "FK_fcbe490dc37a1abf68f19c5ccb9" FOREIGN KEY ("userId") REFERENCES public.users(id) ON DELETE CASCADE; + + +-- +-- PostgreSQL database dump complete +-- + + + + +-- Mark superseded legacy pricing migrations as applied (see generator note). +CREATE TABLE IF NOT EXISTS schema_migrations (filename TEXT PRIMARY KEY, applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()); +INSERT INTO schema_migrations (filename) VALUES + ('004_pricing_catalog.sql'), + ('005_pricing_catalog_all_runtimes.sql'), + ('006_addon_rate_resources.sql'), + ('007_optional_service_pricing_matrix.sql'), + ('008_application_optional_service_resources.sql'), + ('009_optional_service_deploy_defaults.sql') +ON CONFLICT (filename) DO NOTHING; diff --git a/backend/migrations/001_service_access_grants.sql b/backend/migrations/001_service_access_grants.sql index d2c6585..1e7c207 100644 --- a/backend/migrations/001_service_access_grants.sql +++ b/backend/migrations/001_service_access_grants.sql @@ -1,16 +1,20 @@ -- Temporary external access grants (Redis, RabbitMQ, database) -CREATE TYPE service_access_target AS ENUM ( - 'database', - 'redis', - 'rabbitmq_amqp', - 'rabbitmq_management' -); +DO $$ BEGIN + CREATE TYPE service_access_target AS ENUM ( + 'database', + 'redis', + 'rabbitmq_amqp', + 'rabbitmq_management' + ); +EXCEPTION WHEN duplicate_object THEN null; END $$; -CREATE TYPE service_access_grant_status AS ENUM ( - 'active', - 'expired', - 'revoked' -); +DO $$ BEGIN + CREATE TYPE service_access_grant_status AS ENUM ( + 'active', + 'expired', + 'revoked' + ); +EXCEPTION WHEN duplicate_object THEN null; END $$; CREATE TABLE IF NOT EXISTS service_access_grants ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), diff --git a/backend/migrations/015_application_product_type.sql b/backend/migrations/015_application_product_type.sql index a59885d..a3eaba0 100644 --- a/backend/migrations/015_application_product_type.sql +++ b/backend/migrations/015_application_product_type.sql @@ -3,7 +3,7 @@ ALTER TABLE applications ADD COLUMN IF NOT EXISTS product_type VARCHAR(32) NOT NULL DEFAULT 'application'; CREATE INDEX IF NOT EXISTS idx_applications_user_product_type - ON applications (user_id, product_type); + ON applications ("userId", product_type); ALTER TABLE resource_credits ADD COLUMN IF NOT EXISTS product_type VARCHAR(32) NOT NULL DEFAULT 'application'; diff --git a/backend/scripts/generate-base-schema.mjs b/backend/scripts/generate-base-schema.mjs new file mode 100644 index 0000000..ea0fb48 --- /dev/null +++ b/backend/scripts/generate-base-schema.mjs @@ -0,0 +1,94 @@ +#!/usr/bin/env node +/** + * Generate the greenfield base schema (000_base_schema.sql) by letting TypeORM + * `synchronize` build every table from the entities against a throwaway + * Postgres, then dumping the schema. Run when entities change materially: + * + * docker run -d --name ch-schemagen -e POSTGRES_PASSWORD=pass \ + * -e POSTGRES_USER=cloudhost -e POSTGRES_DB=cloudhost \ + * -p 55432:5432 postgres:16-alpine + * node scripts/generate-base-schema.mjs + * + * The output is wrapped so it is safe to run on an already-populated database + * (every statement uses IF NOT EXISTS / duplicate_object guards where possible; + * the migration runner also records it in schema_migrations so it runs once). + */ +import 'reflect-metadata'; +import { DataSource } from 'typeorm'; +import { execFileSync } from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const backendRoot = path.resolve(__dirname, '..'); +const outPath = path.join(backendRoot, 'migrations', '000_base_schema.sql'); + +// Use the COMPILED entities (run `npm run build` first) — union-typed columns +// only carry correct decorator metadata through the project's tsc build. +const ds = new DataSource({ + type: 'postgres', + host: process.env.SCHEMA_DB_HOST || '127.0.0.1', + port: parseInt(process.env.SCHEMA_DB_PORT || '55432', 10), + username: 'cloudhost', + password: 'pass', + database: 'cloudhost', + entities: [path.join(backendRoot, 'dist/**/*.entity.js')], + synchronize: true, +}); + +await ds.initialize(); +await ds.destroy(); + +// Dump schema-only from the container, then strip owner/ACL noise. +const dumped = execFileSync('docker', [ + 'exec', 'ch-schemagen', + 'pg_dump', '-U', 'cloudhost', '-d', 'cloudhost', + '--schema-only', '--no-owner', '--no-privileges', +], { maxBuffer: 32 * 1024 * 1024 }).toString(); + +// Strip: +// - psql client meta-commands that are version-specific (\restrict is +// pg_dump 16.13+ only) and would break on the migrations image's psql; +// - the `search_path = ''` reset, which otherwise persists into the trailing +// `INSERT INTO schema_migrations` the runner appends (unqualified) and the +// footer below, causing "no schema has been selected to create in". +const raw = dumped + .split('\n') + .filter( + (line) => + !/^\\(restrict|unrestrict)\b/.test(line) && + !/set_config\('search_path'/.test(line), + ) + .join('\n'); + +const header = `-- 000_base_schema.sql — greenfield base schema (generated from TypeORM entities). +-- Auto-generated by scripts/generate-base-schema.mjs. Do not edit by hand. +-- Incremental migrations (001+) run afterwards on top of this schema. + +`; + +// The legacy pricing-catalog migrations (004-009) target a superseded +// snake_case pricing schema that is incompatible with the current entities. +// On greenfield the base schema already creates the entity-shaped pricing +// tables and the app self-seeds their rows (PricingCatalogService.ensureDefaults +// on boot), so mark those migrations as already applied to skip them. +const supersededPricingMigrations = [ + '004_pricing_catalog.sql', + '005_pricing_catalog_all_runtimes.sql', + '006_addon_rate_resources.sql', + '007_optional_service_pricing_matrix.sql', + '008_application_optional_service_resources.sql', + '009_optional_service_deploy_defaults.sql', +]; +const footer = ` + +-- Mark superseded legacy pricing migrations as applied (see generator note). +CREATE TABLE IF NOT EXISTS schema_migrations (filename TEXT PRIMARY KEY, applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()); +INSERT INTO schema_migrations (filename) VALUES +${supersededPricingMigrations.map((m) => ` ('${m}')`).join(',\n')} +ON CONFLICT (filename) DO NOTHING; +`; + +fs.writeFileSync(outPath, header + raw + footer); +console.log(`Wrote ${outPath} (${raw.length} bytes)`); diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 9889698..7190c1b 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -53,6 +53,7 @@ import configuration from './config/configuration'; redis: { host: configService.get('redis.host'), port: configService.get('redis.port'), + password: configService.get('redis.password'), }, }), inject: [ConfigService], diff --git a/backend/src/applications/applications.controller.ts b/backend/src/applications/applications.controller.ts index 67c35bc..3ada03e 100644 --- a/backend/src/applications/applications.controller.ts +++ b/backend/src/applications/applications.controller.ts @@ -387,6 +387,29 @@ export class ApplicationsController { throw new BadRequestException('Replicas can only be changed for the main application workload.'); } + // Non-staff users must go through the billed upgrade flow for any change + // that increases cost — direct PATCH must not bypass payment. + if (!isStaff) { + const upgradeDto = + workload === 'app' + ? { + cpuLimit: dto.cpuLimit, + memoryLimit: dto.memoryLimit, + replicas: dto.replicas, + } + : workload === 'database' + ? { databaseResources: { cpuLimit: dto.cpuLimit, memoryLimit: dto.memoryLimit } } + : workload === 'redis' + ? { redisResources: { cpuLimit: dto.cpuLimit, memoryLimit: dto.memoryLimit } } + : { rabbitmqResources: { cpuLimit: dto.cpuLimit, memoryLimit: dto.memoryLimit } }; + const cost = await this.billingService.calculateUpgradeCost(app, upgradeDto as any); + if (cost.proratedAmount > 0) { + throw new BadRequestException( + 'This change increases the plan cost. Use the resource upgrade flow (with invoice payment) instead.', + ); + } + } + // Update in K8s (live) await this.kubernetesService.updateResources(app, dto, workload); diff --git a/backend/src/applications/applications.service.ts b/backend/src/applications/applications.service.ts index da435ea..3c5f689 100644 --- a/backend/src/applications/applications.service.ts +++ b/backend/src/applications/applications.service.ts @@ -22,6 +22,7 @@ import { detectRuntimeFromArchive, } from '../build/runtime-detector'; import { SourceStorageService } from '../storage/source-storage.service'; +import { userIdSlug } from '../kubernetes/k8s-workload.util'; import * as os from 'os'; @Injectable() @@ -67,6 +68,18 @@ export class ApplicationsService { dto = normalizeCreateApplicationDto(dto); const productType = dto.productType ?? ProductType.APPLICATION; + // WordPress only runs on MySQL/MariaDB — reject PostgreSQL/Mongo/none up + // front instead of failing at runtime inside the WordPress container. + if (dto.runtime === AppRuntime.WORDPRESS) { + if (!dto.databaseType || dto.databaseType === DatabaseType.NONE) { + dto.databaseType = DatabaseType.MYSQL; + } else if (![DatabaseType.MYSQL, DatabaseType.MARIADB].includes(dto.databaseType)) { + throw new BadRequestException( + `WordPress requires a MySQL or MariaDB database — "${dto.databaseType}" is not supported.`, + ); + } + } + // Placement is always decided automatically by the allocator. const allocation = await this.clustersService.selectClusterForApplication(dto, userId); const clusterId = allocation.cluster.id; @@ -96,7 +109,7 @@ export class ApplicationsService { const baseLabel = dto.name; const subdomain = customDomain - ? `${this.toDnsLabel(baseLabel)}-${this.toDnsLabel(userId.split('-')[0])}` + ? `${this.toDnsLabel(baseLabel)}-${this.toDnsLabel(userIdSlug(userId).slice(0, 12))}` : await this.generateRandomSubdomain(baseLabel); const platformDomain = this.configService.get('platform.domain') || 'apps.cloudhost.ir'; diff --git a/backend/src/applications/entities/application.entity.ts b/backend/src/applications/entities/application.entity.ts index 0a23825..d37e4c8 100644 --- a/backend/src/applications/entities/application.entity.ts +++ b/backend/src/applications/entities/application.entity.ts @@ -16,6 +16,7 @@ import { CustomDomainStatus, ProductType, } from '../../common/enums'; +import { Exclude, Expose } from 'class-transformer'; import { User } from '../../users/entities/user.entity'; import { Deployment } from '../../deployments/entities/deployment.entity'; @@ -111,8 +112,19 @@ export class Application { @Column({ nullable: true }) gitUrl: string; + /** + * Personal access token for private repos. Never serialized into API + * responses (see hasGitToken) — it is a credential to an external system. + */ + @Exclude({ toPlainOnly: true }) @Column({ nullable: true }) - gitToken: string; // Personal access token for private repos + gitToken: string; + + /** Whether a git token is configured (safe indicator for the UI). */ + @Expose() + get hasGitToken(): boolean { + return !!this.gitToken; + } @Column({ nullable: true }) gitBranch: string; // Branch to clone (default: main) diff --git a/backend/src/billing/billing-wallet.controller.ts b/backend/src/billing/billing-wallet.controller.ts index 791b7a8..d18cdd0 100644 --- a/backend/src/billing/billing-wallet.controller.ts +++ b/backend/src/billing/billing-wallet.controller.ts @@ -14,7 +14,11 @@ import { import { AuthGuard } from '@nestjs/passport'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; import { BillingService } from './billing.service'; -import { assertStubGatewayAllowed } from './payment-gateway.util'; +import { + assertStubGatewayAllowed, + issueGatewayTrackingCode, + assertGatewayTrackingCodeValid, +} from './payment-gateway.util'; import { AppLifecycleService } from '../lifecycle/app-lifecycle.service'; import { ApplicationsService } from '../applications/applications.service'; import { ChargeWalletDto, PayApplicationDto } from './dto/billing.dto'; @@ -43,8 +47,11 @@ export class BillingWalletController { } @Post('wallet/charge') - @ApiOperation({ summary: 'Charge my wallet (self top-up)' }) + @ApiOperation({ summary: 'Charge my wallet (self top-up — stub gateway, dev/staging only)' }) async chargeMyWallet(@Request() req: any, @Body() dto: ChargeWalletDto) { + // Direct self-credit is only for environments with the stub gateway enabled. + // In production a real payment gateway must credit wallets. + assertStubGatewayAllowed(); return this.billingService.chargeWallet(req.user.id, dto.amount, dto.description || 'Self top-up'); } @@ -160,7 +167,7 @@ export class BillingWalletController { @Body() body: { amount: number; description?: string; callbackUrl: string }, ) { assertStubGatewayAllowed(); - const trackingCode = `PAY-${Date.now()}-${Math.random().toString(36).substring(2, 8).toUpperCase()}`; + const trackingCode = issueGatewayTrackingCode(req.user.id, body.amount); return { success: true, trackingCode, @@ -176,6 +183,8 @@ export class BillingWalletController { @Body() body: { trackingCode: string; amount: number }, ) { assertStubGatewayAllowed(); + // The tracking code binds user + amount at initiate time; reject tampered amounts. + assertGatewayTrackingCodeValid(body.trackingCode, req.user.id, body.amount); await this.billingService.chargeWallet( req.user.id, body.amount, diff --git a/backend/src/billing/billing.service.ts b/backend/src/billing/billing.service.ts index c8fb59c..8d61622 100644 --- a/backend/src/billing/billing.service.ts +++ b/backend/src/billing/billing.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger, BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, IsNull, MoreThan, FindOptionsWhere } from 'typeorm'; +import { Repository, IsNull, MoreThan, FindOptionsWhere, EntityManager } from 'typeorm'; import { Wallet } from './entities/wallet.entity'; import { WalletTransaction } from './entities/wallet-transaction.entity'; import { Invoice } from './entities/invoice.entity'; @@ -172,6 +172,32 @@ export class BillingService { return { balance: Number(wallet.balance) }; } + /** + * Load the user's wallet inside a transaction with a row-level lock + * (SELECT ... FOR UPDATE) so concurrent charge/deduct operations serialize + * instead of racing on read-modify-write. + */ + private async lockWallet(em: EntityManager, userId: string): Promise { + let wallet = await em.getRepository(Wallet).findOne({ + where: { userId }, + lock: { mode: 'pessimistic_write' }, + }); + if (!wallet) { + // First-time wallet creation may race; the unique userId column makes + // one insert win — re-read with the lock afterwards. + try { + await em.getRepository(Wallet).insert({ userId, balance: 0 }); + } catch { + /* concurrent insert won — fall through to locked re-read */ + } + wallet = await em.getRepository(Wallet).findOneOrFail({ + where: { userId }, + lock: { mode: 'pessimistic_write' }, + }); + } + return wallet; + } + async chargeWallet( userId: string, amount: number, @@ -180,21 +206,23 @@ export class BillingService { ): Promise { if (amount <= 0) throw new BadRequestException('Amount must be positive'); - const wallet = await this.getOrCreateWallet(userId); - wallet.balance = Number(wallet.balance) + amount; - await this.walletRepo.save(wallet); + const saved = await this.walletRepo.manager.transaction(async (em) => { + const wallet = await this.lockWallet(em, userId); + wallet.balance = Number(wallet.balance) + amount; + await em.getRepository(Wallet).save(wallet); - const tx = this.txRepo.create({ - walletId: wallet.id, - type: TransactionType.CHARGE, - amount, - balanceAfter: wallet.balance, - description: description || 'Wallet charge', - invoiceId, + const tx = em.getRepository(WalletTransaction).create({ + walletId: wallet.id, + type: TransactionType.CHARGE, + amount, + balanceAfter: wallet.balance, + description: description || 'Wallet charge', + invoiceId, + }); + return em.getRepository(WalletTransaction).save(tx); }); - const saved = await this.txRepo.save(tx); - this.logger.log(`Charged wallet of user ${userId}: +${amount} Toman → balance: ${wallet.balance}`); + this.logger.log(`Charged wallet of user ${userId}: +${amount} Toman → balance: ${saved.balanceAfter}`); return saved; } @@ -207,26 +235,28 @@ export class BillingService { ): Promise { if (amount <= 0) throw new BadRequestException('Amount must be positive'); - const wallet = await this.getOrCreateWallet(userId); - if (Number(wallet.balance) < amount) { - throw new BadRequestException('Insufficient wallet balance'); - } + const saved = await this.walletRepo.manager.transaction(async (em) => { + const wallet = await this.lockWallet(em, userId); + if (Number(wallet.balance) < amount) { + throw new BadRequestException('Insufficient wallet balance'); + } - wallet.balance = Number(wallet.balance) - amount; - await this.walletRepo.save(wallet); + wallet.balance = Number(wallet.balance) - amount; + await em.getRepository(Wallet).save(wallet); - const tx = this.txRepo.create({ - walletId: wallet.id, - type: TransactionType.DEDUCTION, - amount, - balanceAfter: wallet.balance, - description: description || 'Service payment', - applicationId, - invoiceId, + const tx = em.getRepository(WalletTransaction).create({ + walletId: wallet.id, + type: TransactionType.DEDUCTION, + amount, + balanceAfter: wallet.balance, + description: description || 'Service payment', + applicationId, + invoiceId, + }); + return em.getRepository(WalletTransaction).save(tx); }); - const saved = await this.txRepo.save(tx); - this.logger.log(`Deducted from wallet of user ${userId}: -${amount} Toman → balance: ${wallet.balance}`); + this.logger.log(`Deducted from wallet of user ${userId}: -${amount} Toman → balance: ${saved.balanceAfter}`); return saved; } @@ -743,7 +773,9 @@ export class BillingService { yearly: newCost.yearly - currentCost.yearly, }; - // Calculate prorated amount based on remaining time in billing period + // Calculate prorated amount based on remaining time in billing period. + // Use the price difference of the app's own billing cycle scaled by the + // fraction of the cycle that remains — not the hourly rate for all cycles. let proratedAmount = 0; let remainingHours = 0; @@ -752,9 +784,18 @@ export class BillingService { const expiresAt = new Date(app.planExpiresAt); remainingHours = Math.max(0, (expiresAt.getTime() - now.getTime()) / (1000 * 60 * 60)); + const cycleDifference = this.amountForCycle(difference, app.billingCycle); + const cycleHours = + app.billingCycle === BillingCycle.HOURLY + ? 1 + : app.billingCycle === BillingCycle.MONTHLY + ? 30 * 24 + : 365 * 24; + // Only charge difference if upgrading (not downgrading) - if (difference.hourly > 0) { - proratedAmount = Math.ceil(difference.hourly * remainingHours); + if (cycleDifference > 0) { + const remainingFraction = Math.min(1, remainingHours / cycleHours); + proratedAmount = Math.ceil(cycleDifference * remainingFraction); } } diff --git a/backend/src/billing/payment-gateway.util.ts b/backend/src/billing/payment-gateway.util.ts index ca552dc..3fb321b 100644 --- a/backend/src/billing/payment-gateway.util.ts +++ b/backend/src/billing/payment-gateway.util.ts @@ -1,4 +1,5 @@ -import { ForbiddenException } from '@nestjs/common'; +import { BadRequestException, ForbiddenException } from '@nestjs/common'; +import { createHmac, timingSafeEqual } from 'node:crypto'; /** * Stub gateway endpoints auto-approve payments without a real provider. @@ -12,3 +13,49 @@ export function assertStubGatewayAllowed(): void { throw new ForbiddenException('Payment gateway is not configured'); } } + +function gatewaySigningSecret(): string { + return ( + process.env.PAYMENT_GATEWAY_SIGNING_SECRET || + process.env.JWT_SECRET || + 'default-jwt-secret' + ); +} + +function hmacSignature(payload: string): string { + return createHmac('sha256', gatewaySigningSecret()).update(payload).digest('hex').slice(0, 24); +} + +/** + * Issue a tracking code that cryptographically binds the initiating user and + * amount, so `verify` cannot be replayed with a different (larger) amount. + * Format: PAY--- + */ +export function issueGatewayTrackingCode(userId: string, amount: number): string { + const ts = Date.now().toString(36); + const rand = Math.random().toString(36).substring(2, 8).toUpperCase(); + const sig = hmacSignature(`${userId}|${amount}|${ts}|${rand}`); + return `PAY-${ts}-${rand}-${sig}`; +} + +/** + * Validate a tracking code issued by {@link issueGatewayTrackingCode} against + * the calling user and the amount being credited. Throws on any mismatch. + */ +export function assertGatewayTrackingCodeValid( + trackingCode: string, + userId: string, + amount: number, +): void { + const parts = String(trackingCode || '').split('-'); + if (parts.length !== 4 || parts[0] !== 'PAY') { + throw new BadRequestException('Invalid gateway tracking code'); + } + const [, ts, rand, sig] = parts; + const expected = hmacSignature(`${userId}|${amount}|${ts}|${rand}`); + const a = Buffer.from(sig); + const b = Buffer.from(expected); + if (a.length !== b.length || !timingSafeEqual(a, b)) { + throw new BadRequestException('Gateway tracking code does not match the payment details'); + } +} diff --git a/backend/src/build/build-progress.store.ts b/backend/src/build/build-progress.store.ts index 5df193b..6be3367 100644 --- a/backend/src/build/build-progress.store.ts +++ b/backend/src/build/build-progress.store.ts @@ -4,8 +4,24 @@ import Redis from 'ioredis'; import type { BuildProgress } from './build.service'; const KEY_PREFIX = 'build:progress:'; +const SESSION_KEY_PREFIX = 'build:session:'; const TTL_SECONDS = 3600; +/** + * Serializable subset of an active build session, persisted to Redis so that + * after a backend restart the orphaned cluster resources (job, PVC, secret, + * helper pod) of interrupted builds can still be located and cleaned up. + */ +export interface PersistedBuildSession { + deploymentId: string; + applicationId?: string; + namespace?: string; + buildPodName?: string; + sourcePvcName?: string; + helperPodName?: string; + gitSecretName?: string; +} + @Injectable() export class BuildProgressStore implements OnModuleDestroy { private readonly redis: Redis; @@ -14,6 +30,7 @@ export class BuildProgressStore implements OnModuleDestroy { this.redis = new Redis({ host: this.configService.get('redis.host'), port: this.configService.get('redis.port'), + password: this.configService.get('redis.password'), lazyConnect: true, maxRetriesPerRequest: 1, }); @@ -52,6 +69,36 @@ export class BuildProgressStore implements OnModuleDestroy { } } + async setSession(session: PersistedBuildSession): Promise { + try { + await this.redis.set( + `${SESSION_KEY_PREFIX}${session.deploymentId}`, + JSON.stringify(session), + 'EX', + TTL_SECONDS, + ); + } catch { + // Best-effort — cleanup falls back to prefix-based resource scan. + } + } + + async getSession(deploymentId: string): Promise { + try { + const raw = await this.redis.get(`${SESSION_KEY_PREFIX}${deploymentId}`); + return raw ? (JSON.parse(raw) as PersistedBuildSession) : null; + } catch { + return null; + } + } + + async clearSession(deploymentId: string): Promise { + try { + await this.redis.del(`${SESSION_KEY_PREFIX}${deploymentId}`); + } catch { + // ignore + } + } + onModuleDestroy(): void { this.redis.disconnect(); } diff --git a/backend/src/build/build.service.ts b/backend/src/build/build.service.ts index d2fbe17..04d5fde 100644 --- a/backend/src/build/build.service.ts +++ b/backend/src/build/build.service.ts @@ -31,12 +31,14 @@ export class BuildCancelledError extends Error { interface ActiveBuildSession { cancelled: boolean; + applicationId?: string; coreApi?: k8s.CoreV1Api; batchApi?: k8s.BatchV1Api; namespace?: string; buildPodName?: string; sourcePvcName?: string; helperPodName?: string; + gitSecretName?: string; processes: ChildProcess[]; socket?: net.Socket; } @@ -68,8 +70,71 @@ export class BuildService { private sourceStorage: SourceStorageService, ) {} - private beginBuildSession(deploymentId: string): void { - this.activeBuilds.set(deploymentId, { cancelled: false, processes: [] }); + /** + * Prefix Docker Hub base images with the configured mirror registry + * (BASE_IMAGE_REGISTRY), so generated Dockerfiles work on clusters that + * cannot reach docker.io. Images already pinned to another registry + * (gcr.io, mcr.microsoft.com, …) are returned unchanged. + */ + private baseImage(image: string): string { + const prefix = this.configService.get('build.baseImageRegistry'); + if (!prefix) return image; + const firstSegment = image.split('/')[0]; + const hasRegistry = firstSegment.includes('.') || firstSegment.includes(':'); + if (hasRegistry) return image; + return `${prefix}/${image}`; + } + + /** + * Git branch names come from users and end up in a shell command — accept + * only conservative ref characters and reject anything option-like. + */ + private assertSafeGitBranch(branch: string): string { + const b = (branch || '').trim(); + if (!b || b.length > 255 || b.startsWith('-') || b.includes('..') || !/^[A-Za-z0-9._/-]+$/.test(b)) { + throw new Error(`Invalid git branch name: "${branch}"`); + } + return b; + } + + /** + * SSRF guard for user-supplied repo URLs: only http(s), no embedded + * credentials, and no loopback/link-local/private or cluster-internal hosts. + */ + private assertSafeGitUrl(gitUrl: string): void { + let url: URL; + try { + url = new URL(gitUrl); + } catch { + throw new Error(`Invalid git URL: "${gitUrl}"`); + } + if (url.protocol !== 'https:' && url.protocol !== 'http:') { + throw new Error(`Unsupported git URL protocol: "${url.protocol}" — only http(s) is allowed`); + } + if (url.username || url.password) { + throw new Error('Git URL must not contain embedded credentials — use the git token field instead'); + } + const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, ''); + const blockedHosts = ['localhost', 'metadata.google.internal']; + const blockedSuffixes = ['.local', '.localhost', '.internal', '.svc', '.svc.cluster.local', '.cluster.local']; + const isPrivateIPv4 = + /^(127\.|10\.|192\.168\.|169\.254\.|0\.)/.test(host) || + /^172\.(1[6-9]|2\d|3[01])\./.test(host); + const isIPv6Internal = host === '::1' || host.startsWith('fe80:') || host.startsWith('fc') || host.startsWith('fd'); + if ( + blockedHosts.includes(host) || + blockedSuffixes.some((s) => host.endsWith(s)) || + isPrivateIPv4 || + isIPv6Internal || + !host.includes('.') + ) { + throw new Error(`Git URL host "${url.hostname}" is not allowed`); + } + } + + private beginBuildSession(deploymentId: string, applicationId?: string): void { + this.activeBuilds.set(deploymentId, { cancelled: false, processes: [], applicationId }); + this.persistSession(deploymentId); } private getSession(deploymentId?: string): ActiveBuildSession | undefined { @@ -80,6 +145,26 @@ export class BuildService { private updateBuildSession(deploymentId: string, update: Partial): void { const session = this.activeBuilds.get(deploymentId); if (session) Object.assign(session, update); + this.persistSession(deploymentId); + } + + /** + * Mirror the serializable part of the session to Redis, so interrupted + * builds can be detected and their cluster resources cleaned up after a + * backend restart (the in-memory map does not survive restarts). + */ + private persistSession(deploymentId: string): void { + const session = this.activeBuilds.get(deploymentId); + if (!session) return; + void this.progressStore.setSession({ + deploymentId, + applicationId: session.applicationId, + namespace: session.namespace, + buildPodName: session.buildPodName, + sourcePvcName: session.sourcePvcName, + helperPodName: session.helperPodName, + gitSecretName: session.gitSecretName, + }); } private registerProcess(deploymentId: string | undefined, proc: ChildProcess): void { @@ -122,7 +207,10 @@ export class BuildService { } private endBuildSession(deploymentId?: string): void { - if (deploymentId) this.activeBuilds.delete(deploymentId); + if (deploymentId) { + this.activeBuilds.delete(deploymentId); + void this.progressStore.clearSession(deploymentId); + } } async cancelBuild(deploymentId: string): Promise { @@ -154,7 +242,7 @@ export class BuildService { } } - const { coreApi, batchApi, namespace, buildPodName, sourcePvcName, helperPodName } = session; + const { coreApi, batchApi, namespace, buildPodName, sourcePvcName, helperPodName, gitSecretName } = session; if (coreApi && namespace) { const cleanup: Promise[] = []; if (helperPodName) { @@ -200,6 +288,11 @@ export class BuildService { .catch(() => undefined), ); } + if (gitSecretName) { + cleanup.push( + coreApi.deleteNamespacedSecret({ name: gitSecretName, namespace }).catch(() => undefined), + ); + } await Promise.all(cleanup); this.logger.log(`Cleaned up K8s build resources for deployment ${deploymentId}`); } @@ -209,7 +302,7 @@ export class BuildService { percent: 0, message: 'Cancelled by user', }); - this.activeBuilds.delete(deploymentId); + this.endBuildSession(deploymentId); } /** Delete all in-flight build artifacts for an app (helper pods, jobs, PVCs, configmaps). */ @@ -226,13 +319,14 @@ export class BuildService { const cleanup: Promise[] = []; - const [pods, pvcs, jobs, configMaps] = await Promise.all([ + const [pods, pvcs, jobs, configMaps, secrets] = await Promise.all([ coreApi.listNamespacedPod({ namespace: buildNamespace }), coreApi.listNamespacedPersistentVolumeClaim({ namespace: buildNamespace, }), batchApi.listNamespacedJob({ namespace: buildNamespace }), coreApi.listNamespacedConfigMap({ namespace: buildNamespace }), + coreApi.listNamespacedSecret({ namespace: buildNamespace }), ]); for (const pod of pods.items) { @@ -283,6 +377,12 @@ export class BuildService { cleanup.push(coreApi.deleteNamespacedConfigMap({ name, namespace: buildNamespace }).catch(() => undefined)); } } + for (const secret of secrets.items) { + const name = secret.metadata?.name || ''; + if (name.startsWith(prefix)) { + cleanup.push(coreApi.deleteNamespacedSecret({ name, namespace: buildNamespace }).catch(() => undefined)); + } + } await Promise.all(cleanup); this.logger.log(`Cleaned up all build resources matching "${prefix}*" in ${buildNamespace}`); @@ -320,7 +420,7 @@ export class BuildService { this.logger.log(`Starting image build for ${app.name} → ${imageUri}`); if (deploymentId) { - this.beginBuildSession(deploymentId); + this.beginBuildSession(deploymentId, app.id); } const hasUploadedCode = !!app.codePath; @@ -389,6 +489,8 @@ export class BuildService { // If we have uploaded code, create a PVC and upload via kubectl cp let sourcePvcName: string | undefined; + // Secret holding the git token for private-repo clones (created lazily) + let gitSecretName: string | undefined; if (hasUploadedCode && localZipPath) { sourcePvcName = `${buildPodName}-source`; if (deploymentId) { @@ -446,7 +548,7 @@ export class BuildService { // Add init container that unzips the source code from PVC initContainers.push({ name: 'unzip-source', - image: 'alpine:3.19', + image: this.baseImage('alpine:3.19'), imagePullPolicy: 'IfNotPresent', command: [ 'sh', @@ -493,36 +595,60 @@ export class BuildService { ], }); } else if (hasGitUrl) { - // Build the git clone URL — inject token for private repos - let cloneUrl = app.gitUrl!; + // Validate user-controlled values before they get anywhere near a shell. + this.assertSafeGitUrl(app.gitUrl!); + const branch = this.assertSafeGitBranch(app.gitBranch || 'main'); + + // The token never appears in the command line or the clone URL — it is + // delivered via a Secret env var and handed to git through GIT_ASKPASS, + // so it can't leak through pod specs, `ps`, or job logs. if (app.gitToken) { - // Convert https://github.com/user/repo.git → https://@github.com/user/repo.git - // Also works for GitLab, Bitbucket, etc. - try { - const url = new URL(cloneUrl); - url.username = app.gitToken; - url.password = ''; // Some providers use token as username, others as password - cloneUrl = url.toString(); - } catch { - // If URL parsing fails, try simple injection after protocol - cloneUrl = cloneUrl.replace('https://', `https://${app.gitToken}@`); - } + gitSecretName = `${buildPodName}-git`; + if (deploymentId) this.updateBuildSession(deploymentId, { gitSecretName }); + await coreApi.createNamespacedSecret({ + namespace: buildNamespace!, + body: { + apiVersion: 'v1', + kind: 'Secret', + metadata: { name: gitSecretName, namespace: buildNamespace }, + type: 'Opaque', + stringData: { GIT_TOKEN: app.gitToken }, + }, + }); } - const branch = app.gitBranch || 'main'; // Clone git repo into /workspace/source, then copy our generated Dockerfile initContainers.push({ name: 'git-clone', - image: 'alpine/git:2.43.0', + image: this.baseImage('alpine/git:2.43.0'), imagePullPolicy: 'IfNotPresent', + env: [ + { name: 'GIT_URL', value: app.gitUrl! }, + { name: 'GIT_BRANCH', value: branch }, + ...(gitSecretName + ? [ + { + name: 'GIT_TOKEN', + valueFrom: { secretKeyRef: { name: gitSecretName, key: 'GIT_TOKEN' } }, + }, + ] + : []), + ], command: [ 'sh', '-c', ` - echo ">>> Cloning branch '${branch}' from ${app.gitUrl}" && - git clone --depth 1 --branch ${branch} ${cloneUrl} /workspace-out/source && - cp /dockerfile/Dockerfile /workspace-out/Dockerfile && - echo ">>> Workspace contents:" && + set -e + if [ -n "\${GIT_TOKEN:-}" ]; then + printf '#!/bin/sh\\necho "$GIT_TOKEN"\\n' > /tmp/git-askpass.sh + chmod +x /tmp/git-askpass.sh + export GIT_ASKPASS=/tmp/git-askpass.sh + export GIT_TERMINAL_PROMPT=0 + fi + echo ">>> Cloning branch '$GIT_BRANCH' from $GIT_URL" + git clone --depth 1 --branch "$GIT_BRANCH" "$GIT_URL" /workspace-out/source + cp /dockerfile/Dockerfile /workspace-out/Dockerfile + echo ">>> Workspace contents:" ls -la /workspace-out/source/ `, ], @@ -545,7 +671,7 @@ export class BuildService { // add an init container that creates empty source dir + copies Dockerfile initContainers.push({ name: 'prepare-workspace', - image: 'alpine:3.19', + image: this.baseImage('alpine:3.19'), imagePullPolicy: 'IfNotPresent', command: [ 'sh', @@ -586,8 +712,14 @@ export class BuildService { args: kanikoArgs, volumeMounts: kanikoVolumeMounts, resources: { - requests: { cpu: '500m', memory: '1Gi' }, - limits: { cpu: '2', memory: '4Gi' }, + requests: { + cpu: this.configService.get('build.kaniko.cpuRequest') || '500m', + memory: this.configService.get('build.kaniko.memoryRequest') || '1Gi', + }, + limits: { + cpu: this.configService.get('build.kaniko.cpuLimit') || '2', + memory: this.configService.get('build.kaniko.memoryLimit') || '4Gi', + }, }, }, ], @@ -666,6 +798,17 @@ export class BuildService { } catch (e: any) { this.logger.warn(`Failed to clean up ConfigMap: ${e.message}`); } + // Clean up git-token Secret + if (gitSecretName) { + try { + await coreApi.deleteNamespacedSecret({ + name: gitSecretName, + namespace: buildNamespace!, + }); + } catch (e: any) { + this.logger.warn(`Failed to clean up git Secret: ${e.message}`); + } + } this.endBuildSession(deploymentId); cleanupSource?.(); } @@ -780,6 +923,8 @@ export class BuildService { metadata: { name: pvcName, namespace }, spec: { accessModes: ['ReadWriteOnce'], + // Explicit StorageClass — don't rely on a cluster default existing + storageClassName: this.configService.get('platform.storageClass') || undefined, resources: { requests: { storage: `${sizeGi}Gi` } }, }, }, @@ -797,7 +942,7 @@ export class BuildService { containers: [ { name: 'helper', - image: 'alpine:3.19', + image: this.baseImage('alpine:3.19'), imagePullPolicy: 'IfNotPresent', command: ['sh', '-c', 'sleep 3600'], volumeMounts: [{ name: 'source', mountPath: '/data' }], @@ -1012,10 +1157,12 @@ export class BuildService { const port = app.port || 3000; const nodeVersion = app.runtimeVersion || '20'; return `# --- Build stage --- -FROM node:${nodeVersion}-alpine AS builder +FROM ${this.baseImage(`node:${nodeVersion}-alpine`)} AS builder WORKDIR /app COPY package*.json ./ -RUN npm install --legacy-peer-deps && npm cache clean --force +# Reproducible install from the lockfile when present +RUN if [ -f package-lock.json ]; then npm ci --legacy-peer-deps; else npm install --legacy-peer-deps; fi \\ + && npm cache clean --force COPY . . # Auto-detect Next.js and enable standalone output @@ -1031,13 +1178,19 @@ RUN for cfg in next.config.js next.config.mjs next.config.ts; do \\ break; \\ done -RUN npm run build || echo ">>> Build script failed or not found — continuing" +# Run the build script when one exists — and FAIL the image build if it fails, +# instead of silently shipping a broken image. +RUN if node -e "const s=(require('./package.json').scripts||{});process.exit(s.build?0:1)"; then \\ + echo ">>> Running build script" && npm run build; \\ + else \\ + echo ">>> No build script defined — skipping"; \\ + fi # Clean up dev dependencies and caches to reduce image size RUN rm -rf node_modules/.cache .next/cache /tmp/* /root/.npm 2>/dev/null; true # --- Production stage --- -FROM node:${nodeVersion}-alpine AS runner +FROM ${this.baseImage(`node:${nodeVersion}-alpine`)} AS runner WORKDIR /app RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 @@ -1070,9 +1223,9 @@ CMD ["sh", "-c", "if [ \\"$(cat /app/.mode)\\" = \\"standalone\\" ] && [ -f serv const phpVersion = app.phpVersion || '8.3'; const port = app.port || 80; return `# --- Build stage (match production PHP version for Composer) --- -FROM php:${phpVersion}-cli-alpine AS composer +FROM ${this.baseImage(`php:${phpVersion}-cli-alpine`)} AS composer RUN apk add --no-cache git unzip -COPY --from=composer:2 /usr/bin/composer /usr/bin/composer +COPY --from=${this.baseImage('composer:2')} /usr/bin/composer /usr/bin/composer WORKDIR /app COPY composer.json composer.lock* ./ RUN composer install --no-dev --no-scripts --no-autoloader --prefer-dist --ignore-platform-reqs @@ -1080,11 +1233,15 @@ COPY . . RUN composer dump-autoload --optimize --no-dev --no-scripts # --- Production stage --- -FROM php:${phpVersion}-fpm-alpine +FROM ${this.baseImage(`php:${phpVersion}-fpm-alpine`)} -RUN apk add --no-cache nginx supervisor curl openssl \\ - && docker-php-ext-install pdo pdo_mysql opcache \\ - && docker-php-ext-install pdo_pgsql 2>/dev/null || true +# Laravel needs bcmath/gd/intl/zip beyond the built-in set; pdo_pgsql is built +# properly against libpq instead of being silently skipped. +RUN apk add --no-cache nginx supervisor curl openssl icu-libs libzip libpng libjpeg-turbo freetype postgresql-libs \\ + && apk add --no-cache --virtual .build-deps icu-dev libzip-dev libpng-dev libjpeg-turbo-dev freetype-dev postgresql-dev \\ + && docker-php-ext-configure gd --with-jpeg --with-freetype \\ + && docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_pgsql opcache bcmath zip gd intl exif pcntl \\ + && apk del .build-deps WORKDIR /var/www/html COPY --from=composer /app . @@ -1163,7 +1320,7 @@ CMD ["/usr/local/bin/cloudhost-laravel-entrypoint.sh"] const phpVersion = app.phpVersion || '8.3'; const hasUploadedCode = !!app.codePath; - return `FROM wordpress:${wpVersion}-php${phpVersion}-apache + return `FROM ${this.baseImage(`wordpress:${wpVersion}-php${phpVersion}-apache`)} # Install additional PHP extensions commonly needed by WordPress RUN docker-php-ext-install opcache @@ -1281,7 +1438,7 @@ CMD []` const port = app.port || 8080; const buildTarget = detectGoBuildTarget(archiveEntries); return `# --- Build stage --- -FROM golang:${goVersion}-alpine AS builder +FROM ${this.baseImage(`golang:${goVersion}-alpine`)} AS builder WORKDIR /app # Install git for fetching dependencies @@ -1297,8 +1454,13 @@ COPY . . # Build the application RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags="-w -s" -o main ${buildTarget} +# Collect optional runtime asset dirs — COPY has no shell so "|| true" is not +# valid there; stage them in the builder instead. +RUN mkdir -p /assets \\ + && for d in static templates public; do [ -d "$d" ] && cp -r "$d" /assets/ || true; done + # --- Production stage --- -FROM alpine:3.19 +FROM ${this.baseImage('alpine:3.19')} WORKDIR /app # Add CA certificates for HTTPS requests @@ -1307,11 +1469,9 @@ RUN apk --no-cache add ca-certificates tzdata # Create non-root user RUN addgroup -g 1001 -S appgroup && adduser -S appuser -u 1001 -G appgroup -# Copy the binary from builder +# Copy the binary and any staged asset dirs from the builder COPY --from=builder /app/main . -COPY --from=builder /app/static ./static 2>/dev/null || true -COPY --from=builder /app/templates ./templates 2>/dev/null || true -COPY --from=builder /app/public ./public 2>/dev/null || true +COPY --from=builder /assets/ ./ # Create data directory for persistent storage RUN mkdir -p /app/data && chown -R appuser:appgroup /app @@ -1331,16 +1491,14 @@ CMD ["./main"] private phpDockerfile(app: Application): string { const phpVersion = app.phpVersion || '8.3'; const port = app.port || 80; - return `FROM php:${phpVersion}-fpm-alpine + return `FROM ${this.baseImage(`php:${phpVersion}-fpm-alpine`)} -RUN apk add --no-cache nginx supervisor curl \\ - && docker-php-ext-install pdo pdo_mysql opcache \\ - && docker-php-ext-install pdo_pgsql 2>/dev/null || true - -# Install common PHP extensions -RUN apk add --no-cache libpng-dev libjpeg-turbo-dev freetype-dev \\ +# Install common PHP extensions (pdo_pgsql built properly against libpq) +RUN apk add --no-cache nginx supervisor curl postgresql-libs libpng libjpeg-turbo freetype \\ + && apk add --no-cache --virtual .build-deps postgresql-dev libpng-dev libjpeg-turbo-dev freetype-dev \\ && docker-php-ext-configure gd --with-freetype --with-jpeg \\ - && docker-php-ext-install gd + && docker-php-ext-install -j$(nproc) pdo pdo_mysql pdo_pgsql opcache gd \\ + && apk del .build-deps WORKDIR /var/www/html COPY . . @@ -1401,7 +1559,7 @@ CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"] const pythonVersion = app.runtimeVersion || '3.12'; const port = app.port || 8000; return `# --- Build stage --- -FROM python:${pythonVersion}-slim AS builder +FROM ${this.baseImage(`python:${pythonVersion}-slim`)} AS builder WORKDIR /app # Install build dependencies @@ -1409,13 +1567,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \\ build-essential libpq-dev \\ && rm -rf /var/lib/apt/lists/* -# Copy requirements and install dependencies -COPY requirements.txt* ./ -RUN pip install --no-cache-dir --user -r requirements.txt 2>/dev/null || \\ - pip install --no-cache-dir --user flask gunicorn +# Install dependencies from requirements.txt or pyproject.toml. A failing +# install FAILS the build — no silent fallback that hides missing deps. +COPY . . +RUN if [ -f requirements.txt ]; then \\ + echo ">>> Installing from requirements.txt" && pip install --no-cache-dir --user -r requirements.txt; \\ + elif [ -f pyproject.toml ]; then \\ + echo ">>> Installing from pyproject.toml" && pip install --no-cache-dir --user .; \\ + else \\ + echo ">>> No requirements.txt or pyproject.toml — installing default flask+gunicorn" \\ + && pip install --no-cache-dir --user flask gunicorn; \\ + fi # --- Production stage --- -FROM python:${pythonVersion}-slim +FROM ${this.baseImage(`python:${pythonVersion}-slim`)} WORKDIR /app # Install runtime dependencies @@ -1455,21 +1620,28 @@ CMD sh -c "if [ -f main.py ]; then if grep -qi fastapi main.py; then exec uvicor const port = app.port || 8000; const settingsModule = detectDjangoSettingsModule(archiveEntries); return `# --- Build stage --- -FROM python:${pythonVersion}-slim AS builder +FROM ${this.baseImage(`python:${pythonVersion}-slim`)} AS builder WORKDIR /app # Install build dependencies RUN apt-get update && apt-get install -y --no-install-recommends \\ - build-essential libpq-dev \\ + build-essential libpq-dev default-libmysqlclient-dev pkg-config \\ && rm -rf /var/lib/apt/lists/* -# Copy requirements and install dependencies -COPY requirements.txt* ./ -RUN pip install --no-cache-dir --user -r requirements.txt 2>/dev/null || \\ - pip install --no-cache-dir --user django gunicorn psycopg2-binary mysqlclient +# Install dependencies from requirements.txt or pyproject.toml. A failing +# install FAILS the build — no silent fallback that hides missing deps. +COPY . . +RUN if [ -f requirements.txt ]; then \\ + echo ">>> Installing from requirements.txt" && pip install --no-cache-dir --user -r requirements.txt; \\ + elif [ -f pyproject.toml ]; then \\ + echo ">>> Installing from pyproject.toml" && pip install --no-cache-dir --user .; \\ + else \\ + echo ">>> No requirements.txt or pyproject.toml — installing Django defaults" \\ + && pip install --no-cache-dir --user django gunicorn psycopg2-binary mysqlclient; \\ + fi # --- Production stage --- -FROM python:${pythonVersion}-slim +FROM ${this.baseImage(`python:${pythonVersion}-slim`)} WORKDIR /app # Install runtime dependencies diff --git a/backend/src/config/configuration.ts b/backend/src/config/configuration.ts index ad81759..27f9ed2 100644 --- a/backend/src/config/configuration.ts +++ b/backend/src/config/configuration.ts @@ -82,6 +82,7 @@ export default () => ({ redis: { host: process.env.REDIS_HOST || 'localhost', port: parseInt(process.env.REDIS_PORT || '6379', 10), + password: process.env.REDIS_PASSWORD || undefined, }, cluster: { @@ -133,6 +134,20 @@ export default () => ({ build: { namespace: process.env.BUILD_NAMESPACE || 'cloudhost-builds', serviceAccount: process.env.BUILD_SERVICE_ACCOUNT || 'kaniko-builder', + /** + * Optional registry prefix for Docker Hub base images used in generated + * Dockerfiles and managed-service charts (e.g. "mirror.example.com" makes + * `node:20-alpine` → `mirror.example.com/node:20-alpine`). Useful when + * cluster nodes cannot reach docker.io directly. + */ + baseImageRegistry: (process.env.BASE_IMAGE_REGISTRY || '').trim().replace(/\/+$/, ''), + /** Kaniko build container resources — tune for large images. */ + kaniko: { + cpuRequest: process.env.KANIKO_CPU_REQUEST || '500m', + cpuLimit: process.env.KANIKO_CPU_LIMIT || '2', + memoryRequest: process.env.KANIKO_MEMORY_REQUEST || '1Gi', + memoryLimit: process.env.KANIKO_MEMORY_LIMIT || '4Gi', + }, }, elasticsearch: { diff --git a/backend/src/config/validate-production-config.spec.ts b/backend/src/config/validate-production-config.spec.ts index e374979..908ee48 100644 --- a/backend/src/config/validate-production-config.spec.ts +++ b/backend/src/config/validate-production-config.spec.ts @@ -28,12 +28,24 @@ describe('validateProductionConfig', () => { expect(() => validateProductionConfig()).toThrow(/CLUSTER_KUBECONFIG_KEY/); }); + it('throws in production when elastic credentials keep the well-known defaults', () => { + process.env.NODE_ENV = 'production'; + process.env.JWT_SECRET = 'a-very-long-random-production-secret'; + process.env.JWT_REFRESH_SECRET = 'another-very-long-random-refresh-secret'; + process.env.DB_PASSWORD = 'strong-db-password-here'; + process.env.CLUSTER_KUBECONFIG_KEY = '0123456789abcdef0123456789abcdef'; + process.env.ELASTIC_PASSWORD = 'CloudHost2024!Secure'; + + expect(() => validateProductionConfig()).toThrow(/ELASTIC_PASSWORD/); + }); + it('passes in production with strong secrets', () => { process.env.NODE_ENV = 'production'; process.env.JWT_SECRET = 'a-very-long-random-production-secret'; process.env.JWT_REFRESH_SECRET = 'another-very-long-random-refresh-secret'; process.env.DB_PASSWORD = 'strong-db-password-here'; process.env.CLUSTER_KUBECONFIG_KEY = '0123456789abcdef0123456789abcdef'; + process.env.ELASTIC_PASSWORD = 'a-strong-rotated-elastic-password'; expect(() => validateProductionConfig()).not.toThrow(); }); diff --git a/backend/src/config/validate-production-config.ts b/backend/src/config/validate-production-config.ts index 63bae96..94f1097 100644 --- a/backend/src/config/validate-production-config.ts +++ b/backend/src/config/validate-production-config.ts @@ -25,6 +25,14 @@ export function validateProductionConfig(): void { if (!process.env.CLUSTER_KUBECONFIG_KEY?.trim()) { errors.push('CLUSTER_KUBECONFIG_KEY must be set in production to encrypt stored kubeconfigs'); } + // Elastic log-stack credentials must not fall back to the well-known dev defaults. + const elasticDefaults = ['CloudHost2024!Secure', 'FluentBit2024!Writer', 'Kibana2024!System']; + if (!process.env.ELASTIC_PASSWORD || elasticDefaults.includes(process.env.ELASTIC_PASSWORD)) { + errors.push('ELASTIC_PASSWORD must be set to a strong random value in production'); + } + if (process.env.FLUENTBIT_PASSWORD && elasticDefaults.includes(process.env.FLUENTBIT_PASSWORD)) { + errors.push('FLUENTBIT_PASSWORD must be changed from the default in production'); + } if (errors.length > 0) { throw new Error( diff --git a/backend/src/deployments/deployments.service.ts b/backend/src/deployments/deployments.service.ts index 7b2aac4..c81d52f 100644 --- a/backend/src/deployments/deployments.service.ts +++ b/backend/src/deployments/deployments.service.ts @@ -1,6 +1,6 @@ -import { Injectable, NotFoundException, BadRequestException, Logger, Inject, forwardRef } from '@nestjs/common'; +import { Injectable, NotFoundException, BadRequestException, Logger, Inject, forwardRef, OnModuleInit } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import { In, Repository } from 'typeorm'; import * as fs from 'fs'; import { Deployment } from './entities/deployment.entity'; import { ApplicationsService } from '../applications/applications.service'; @@ -16,7 +16,7 @@ import { import { ClustersService } from '../clusters/clusters.service'; @Injectable() -export class DeploymentsService { +export class DeploymentsService implements OnModuleInit { private readonly logger = new Logger(DeploymentsService.name); constructor( @@ -29,6 +29,46 @@ export class DeploymentsService { private clustersService: ClustersService, ) {} + /** + * Recover from a backend restart: any deployment still marked as in-flight + * belonged to a pipeline running in the old process and will never finish. + * Mark it failed and best-effort clean up its orphaned build resources + * (Kaniko job, source PVC, helper pod, git secret) in the cluster. + */ + onModuleInit(): void { + void this.failInterruptedDeployments().catch((err) => + this.logger.error('Failed to recover interrupted deployments on startup', err), + ); + } + + private async failInterruptedDeployments(): Promise { + const stuck = await this.deploymentsRepository.find({ + where: { + status: In([DeploymentStatus.PENDING, DeploymentStatus.BUILDING, DeploymentStatus.DEPLOYING]), + }, + }); + if (stuck.length === 0) return; + + this.logger.warn(`Found ${stuck.length} deployment(s) interrupted by a restart — marking as failed`); + for (const deployment of stuck) { + await this.deploymentsRepository.update(deployment.id, { + status: DeploymentStatus.FAILED, + errorMessage: 'Build interrupted by a platform restart — please redeploy', + }); + this.buildService.setProgress(deployment.id, { + phase: 'failed', + percent: 0, + message: 'Build interrupted by a platform restart', + }); + try { + const app = await this.applicationsService.findOne(deployment.applicationId); + if (app) await this.buildService.cleanupBuildResourcesForApp(app); + } catch (err: any) { + this.logger.warn(`Cleanup of interrupted deployment ${deployment.id} failed: ${err.message}`); + } + } + } + /** * Random 7-digit suffix for the preview host: -<7-digit>.. * Generated once per application (see resolvePreviewNumber) and persisted. @@ -58,6 +98,8 @@ export class DeploymentsService { async triggerDeployment(applicationId: string, userId: string): Promise { const app = await this.applicationsService.findOne(applicationId, userId); + this.ensureAppPaidAndActive(app, 'deploying'); + // Create deployment record const deployment = this.deploymentsRepository.create({ applicationId: app.id, @@ -451,15 +493,20 @@ export class DeploymentsService { return app.latestImageTag === MANAGED_DEPLOY_MARKER; } - private ensureRedeployAllowed(app: any): void { - if (!app.billingCycle) return; - + /** + * All deploy/start/redeploy operations require the app to be paid for: + * activated (billingCycle set via wallet/pay), lifecycle ACTIVE, and paid + * time remaining. Prevents deploying/resuming without payment. + */ + private ensureAppPaidAndActive(app: any, action = 'deploying'): void { const isActive = app.lifecycleStatus === AppLifecycleStatus.ACTIVE; const expiresAt = app.planExpiresAt ? new Date(app.planExpiresAt) : null; const hasPaidTimeRemaining = !!expiresAt && expiresAt > new Date(); - if (!isActive || !hasPaidTimeRemaining) { - throw new BadRequestException('Payment must be completed successfully before redeploying this application.'); + if (!app.billingCycle || !isActive || !hasPaidTimeRemaining) { + throw new BadRequestException( + `Payment must be completed successfully before ${action} this application.`, + ); } } @@ -636,6 +683,9 @@ export class DeploymentsService { async startDeployment(applicationId: string, userId: string): Promise { const app = await this.applicationsService.findOne(applicationId, userId); + // Prevent resuming a billing-suspended/expired app without payment — + // otherwise `start` bypasses the lifecycle suspension entirely. + this.ensureAppPaidAndActive(app, 'starting'); await this.kubernetesService.resumeApplication(app); await this.applicationsService.clearSuspendedReplicas(app.id); @@ -671,7 +721,7 @@ export class DeploymentsService { throw new NotFoundException('No source code available. Upload code or set a git URL first.'); } - this.ensureRedeployAllowed(app); + this.ensureAppPaidAndActive(app, 'redeploying'); // Create new deployment record const deployment = this.deploymentsRepository.create({ diff --git a/backend/src/kubernetes/elasticsearch.service.ts b/backend/src/kubernetes/elasticsearch.service.ts index eef149c..f411b5d 100644 --- a/backend/src/kubernetes/elasticsearch.service.ts +++ b/backend/src/kubernetes/elasticsearch.service.ts @@ -5,6 +5,7 @@ import * as crypto from 'crypto'; import { ChildProcess, spawn } from 'child_process'; import { ClustersService } from '../clusters/clusters.service'; import { HelmService, LOGGING_HELM_NAMESPACE, LOGGING_HELM_RELEASE } from './helm.service'; +import { userNamespace } from './k8s-workload.util'; interface ElasticsearchCredentials { username: string; @@ -97,9 +98,9 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy { private configService: ConfigService, private helmService: HelmService, ) { - this.ELASTIC_PASSWORD = this.configService.get('elasticsearch.password') || 'CloudHost2024!Secure'; - this.FLUENTBIT_PASSWORD = this.configService.get('elasticsearch.fluentbitPassword') || 'FluentBit2024!Writer'; - this.KIBANA_SYSTEM_PASSWORD = this.configService.get('elasticsearch.kibanaPassword') || 'Kibana2024!System'; + this.ELASTIC_PASSWORD = this.configService.get('elasticsearch.password') || ''; + this.FLUENTBIT_PASSWORD = this.configService.get('elasticsearch.fluentbitPassword') || ''; + this.KIBANA_SYSTEM_PASSWORD = this.configService.get('elasticsearch.kibanaPassword') || ''; } async onModuleInit(): Promise { @@ -649,7 +650,7 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy { generateUserCredentials(userId: string): ElasticsearchCredentials { const hash = crypto.createHash('sha256').update(`${userId}-${this.ELASTIC_PASSWORD}`).digest('hex'); return { - username: `user-${userId.split('-')[0]}`, + username: userNamespace(userId), password: hash.substring(0, 24), }; } @@ -666,16 +667,16 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy { * Get index pattern for a user's applications */ getIndexPattern(userId: string): string { - const userPrefix = userId.split('-')[0]; - return `logs-user-${userPrefix}-*`; + return `logs-${userNamespace(userId)}-*`; } /** * Build must clauses for user log isolation (new + legacy fields). */ buildUserLogMustClauses(userId: string, filters: LogSearchFilters = {}): any[] { - const userPrefix = userId.split('-')[0]; - const namespace = `user-${userPrefix}`; + // Full-UUID namespace — a truncated prefix would match other tenants' + // namespaces and leak their logs. + const namespace = userNamespace(userId); const must: any[] = [ { @@ -758,7 +759,7 @@ export class ElasticsearchService implements OnModuleInit, OnModuleDestroy { } getUserIndexPattern(userId: string): string { - return `logs-user-${userId.split('-')[0]}-*`; + return `logs-${userNamespace(userId)}-*`; } private elasticsearchFetch(url: string, auth: string, body: unknown): Promise { diff --git a/backend/src/kubernetes/k8s-client-v1-migration.spec.ts b/backend/src/kubernetes/k8s-client-v1-migration.spec.ts index 854a501..fefaed9 100644 --- a/backend/src/kubernetes/k8s-client-v1-migration.spec.ts +++ b/backend/src/kubernetes/k8s-client-v1-migration.spec.ts @@ -125,11 +125,11 @@ describe('KubernetesService — k8s v1 client shape', () => { expect(logs).toBe('hello logs'); const listArg = coreApi.listNamespacedPod.mock.calls[0][0]; - expect(listArg).toMatchObject({ namespace: 'user-abc123' }); + expect(listArg).toMatchObject({ namespace: 'user-abc123def456' }); expect(typeof listArg.labelSelector).toBe('string'); const logArg = coreApi.readNamespacedPodLog.mock.calls[0][0]; - expect(logArg).toMatchObject({ name: 'pod-1', namespace: 'user-abc123', tailLines: 200 }); + expect(logArg).toMatchObject({ name: 'pod-1', namespace: 'user-abc123def456', tailLines: 200 }); }); it('getDatabasePvcSize reads the PVC with v1 object args and unwrapped spec', async () => { @@ -144,7 +144,7 @@ describe('KubernetesService — k8s v1 client shape', () => { expect(size).toBe('5Gi'); const arg = coreApi.readNamespacedPersistentVolumeClaim.mock.calls[0][0]; - expect(arg).toMatchObject({ name: 'my-app-db', namespace: 'user-abc123' }); + expect(arg).toMatchObject({ name: 'my-app-db', namespace: 'user-abc123def456' }); }); it('scaleDeployment patches with the v1 object body and a header-options 2nd arg', async () => { @@ -157,7 +157,7 @@ describe('KubernetesService — k8s v1 client shape', () => { const [param, options] = appsApi.patchNamespacedDeployment.mock.calls[0]; expect(param).toMatchObject({ name: 'my-app', - namespace: 'user-abc123', + namespace: 'user-abc123def456', body: { spec: { replicas: 3 } }, }); // v1 takes the merge-patch content-type via the 2nd ConfigurationOptions arg diff --git a/backend/src/kubernetes/k8s-workload.util.ts b/backend/src/kubernetes/k8s-workload.util.ts index 3bd139d..c7740db 100644 --- a/backend/src/kubernetes/k8s-workload.util.ts +++ b/backend/src/kubernetes/k8s-workload.util.ts @@ -1,9 +1,18 @@ import { Application } from '../applications/entities/application.entity'; import { DatabaseType, isManagedProductType } from '../common/enums'; -/** Kubernetes namespace for a user's applications. */ +/** + * Collision-free slug for a user id: the full UUID with dashes stripped + * (32 hex chars). Never truncate the UUID — truncated prefixes collide + * between users and break tenant isolation (shared namespaces/logs). + */ +export function userIdSlug(userId: string): string { + return userId.replace(/-/g, ''); +} + +/** Kubernetes namespace for a user's applications ("user-" + 32 chars ≤ 63). */ export function userNamespace(userId: string): string { - return `user-${userId.split('-')[0]}`; + return `user-${userIdSlug(userId)}`; } /** Primary pod label selector target for an application workload. */ diff --git a/backend/src/kubernetes/kubernetes.service.spec.ts b/backend/src/kubernetes/kubernetes.service.spec.ts index 086ac22..6bfab6d 100644 --- a/backend/src/kubernetes/kubernetes.service.spec.ts +++ b/backend/src/kubernetes/kubernetes.service.spec.ts @@ -15,7 +15,7 @@ describe('buildHelmValues logic', () => { return { app: { name: app.name, - namespace: `user-${app.userId.split('-')[0]}`, + namespace: `user-${app.userId.replace(/-/g, '')}`, runtime: app.runtime, image: imageUri, port: app.port, @@ -72,7 +72,7 @@ describe('buildHelmValues logic', () => { it('should set correct namespace from userId', () => { const values = buildHelmValues(baseApp, 'registry/my-app:123'); - expect(values.app.namespace).toBe('user-abc123'); + expect(values.app.namespace).toBe('user-abc123def456'); }); it('should disable database when type is NONE', () => { diff --git a/backend/src/kubernetes/kubernetes.service.ts b/backend/src/kubernetes/kubernetes.service.ts index 24b1dd4..244534b 100644 --- a/backend/src/kubernetes/kubernetes.service.ts +++ b/backend/src/kubernetes/kubernetes.service.ts @@ -4,6 +4,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import * as k8s from '@kubernetes/client-node'; import * as fs from 'fs'; +import * as os from 'os'; import * as path from 'path'; import { execFile } from 'child_process'; import { promisify } from 'util'; @@ -17,6 +18,7 @@ import { HelmService } from './helm.service'; import { RegistryService } from './registry.service'; import { K8sClientService } from './k8s-client.service'; import { K8sLifecycleService } from './k8s-lifecycle.service'; +import { userNamespace, userIdSlug } from './k8s-workload.util'; import { registerKubeconfigNoProxy } from '../common/kubernetes-proxy.util'; const execFileAsync = promisify(execFile); @@ -209,8 +211,27 @@ export class KubernetesService implements OnModuleInit { } /** Helm values for managed_database / managed_redis / managed_rabbitmq (no app workload). */ + /** + * Return the app's database password, generating and PERSISTING one if it is + * missing. Without persistence a fresh password would be generated on every + * helm upgrade, breaking auth against the database's persisted volume. + */ + private ensureDbPassword(app: Application): string { + if (!app.dbPassword) { + app.dbPassword = this.generatePassword(); + this.deploymentsRepository.manager + .getRepository(Application) + .update(app.id, { dbPassword: app.dbPassword }) + .catch((e: any) => + this.logger.warn(`Failed to persist generated dbPassword for ${app.name}: ${e.message}`), + ); + this.logger.warn(`App ${app.name} had no dbPassword — generated and persisted one`); + } + return app.dbPassword; + } + private buildManagedHelmValues(app: Application): Record { - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const pullRegistryUrl = this.registryService.getRegistryUrl(); const isPostgres = app.databaseType === DatabaseType.POSTGRESQL; const productType = app.productType; @@ -247,7 +268,10 @@ export class KubernetesService implements OnModuleInit { type: app.databaseType, version: app.dbVersion || (isPostgres ? '16' : '8.0'), username: app.dbUsername || 'appuser', - password: app.dbPassword || this.generatePassword(), + password: + app.databaseType && app.databaseType !== DatabaseType.NONE + ? this.ensureDbPassword(app) + : '', storageSize: app.dbStorageSize || '1Gi', resources: this.resolveDatabaseResources(app), }, @@ -260,6 +284,7 @@ export class KubernetesService implements OnModuleInit { ownerId: app.userId, applicationId: app.id, }, + images: { baseRegistry: this.configService.get('build.baseImageRegistry') || '' }, changeCause: `Helm provision ${app.name} (${productType}) at ${new Date().toISOString()}`, }; @@ -287,7 +312,7 @@ export class KubernetesService implements OnModuleInit { private buildHelmValues(app: Application, imageUri: string, previewNumber?: string | null): Record { const domain = this.configService.get('platform.domain'); const previewRootDomain = this.configService.get('platform.previewRootDomain') || domain; - const namespacePrefix = app.userId.split('-')[0]; + const namespacePrefix = userIdSlug(app.userId); const previewHost = previewNumber && !app.customDomain ? `${namespacePrefix}-${previewNumber}.${previewRootDomain}` : ''; const pullRegistryUrl = this.registryService.getRegistryUrl(); const isWordPress = app.runtime === AppRuntime.WORDPRESS; @@ -299,7 +324,7 @@ export class KubernetesService implements OnModuleInit { app: { enabled: true, name: app.name, - namespace: `user-${app.userId.split('-')[0]}`, + namespace: this.getUserNamespace(app.userId), runtime: app.runtime, image: imageUri, port: app.port, @@ -330,7 +355,7 @@ export class KubernetesService implements OnModuleInit { type: app.databaseType, version: app.dbVersion || (isPostgres ? '16' : '8.0'), username: app.dbUsername || 'appuser', - password: app.dbPassword || this.generatePassword(), + password: hasDb ? this.ensureDbPassword(app) : '', storageSize: app.dbStorageSize || '1Gi', resources: this.resolveDatabaseResources(app), }, @@ -344,10 +369,11 @@ export class KubernetesService implements OnModuleInit { logPaths: app.logPaths || [], ownerId: app.userId, applicationId: app.id, - elasticPassword: this.configService.get('elasticsearch.password') || 'CloudHost2024!Secure', - fluentbitPassword: this.configService.get('elasticsearch.fluentbitPassword') || 'FluentBit2024!Writer', - kibanaPassword: this.configService.get('elasticsearch.kibanaPassword') || 'Kibana2024!System', + elasticPassword: this.configService.get('elasticsearch.password'), + fluentbitPassword: this.configService.get('elasticsearch.fluentbitPassword'), + kibanaPassword: this.configService.get('elasticsearch.kibanaPassword'), }, + images: { baseRegistry: this.configService.get('build.baseImageRegistry') || '' }, changeCause: `Deploy ${imageUri} at ${new Date().toISOString()}`, }; @@ -389,7 +415,7 @@ export class KubernetesService implements OnModuleInit { async waitForApplicationReady(app: Application, timeoutMs = 600_000, shouldAbort?: () => Promise): Promise { const { coreApi, appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const managed = isManagedProductType(app.productType); const workloads = [ ...(!managed ? [{ name: app.name, replicas: app.replicas || 1 }] : []), @@ -429,7 +455,7 @@ export class KubernetesService implements OnModuleInit { async updateIngress(app: Application): Promise { const domain = this.configService.get('platform.domain'); const subdomain = app.subdomain || app.name; - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const customDomain = app.customDomain && app.customDomainStatus === CustomDomainStatus.VERIFIED ? app.customDomain : undefined; // When there's no verified custom domain, restore the stable preview host so @@ -528,7 +554,7 @@ export class KubernetesService implements OnModuleInit { const { coreApi, appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); const kubeconfig = await this.k8sClientService.getKubeconfig(app.clusterId); await this.ensurePlatformStorageClass(kubeconfig); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const context: ManifestContext = { appName: app.name, namespace, @@ -545,7 +571,10 @@ export class KubernetesService implements OnModuleInit { domain: this.configService.get('platform.domain') || 'apps.cloudhost.ir', subdomain: app.subdomain || app.name, dbUsername: app.dbUsername || 'appuser', - dbPassword: app.dbPassword || this.generatePassword(), + dbPassword: + app.databaseType && app.databaseType !== DatabaseType.NONE + ? this.ensureDbPassword(app) + : '', dbVersion: app.dbVersion || '', dbStorageSize: app.dbStorageSize || '1Gi', dbCpuRequest: this.resolveDatabaseResources(app).cpuRequest, @@ -602,7 +631,7 @@ export class KubernetesService implements OnModuleInit { const context: ManifestContext = { appName: app.name, - namespace: `user-${app.userId.split('-')[0]}`, + namespace: this.getUserNamespace(app.userId), image: imageUri, port: app.port, replicas: app.replicas, @@ -616,7 +645,10 @@ export class KubernetesService implements OnModuleInit { domain: domain, subdomain: app.subdomain || app.name, dbUsername: app.dbUsername || 'appuser', - dbPassword: app.dbPassword || this.generatePassword(), + dbPassword: + app.databaseType && app.databaseType !== DatabaseType.NONE + ? this.ensureDbPassword(app) + : '', dbVersion: app.dbVersion || '', dbStorageSize: app.dbStorageSize || '1Gi', dbCpuRequest: this.resolveDatabaseResources(app).cpuRequest, @@ -1172,10 +1204,10 @@ export class KubernetesService implements OnModuleInit { /** Replicate logging credentials into the app namespace for Fluent Bit sidecars. */ private async ensureElasticsearchCredentialsSecret(coreApi: k8s.CoreV1Api, namespace: string): Promise { const name = 'elasticsearch-credentials'; - const stringData = { - ELASTIC_PASSWORD: this.configService.get('elasticsearch.password') || 'CloudHost2024!Secure', - FLUENTBIT_PASSWORD: this.configService.get('elasticsearch.fluentbitPassword') || 'FluentBit2024!Writer', - KIBANA_SYSTEM_PASSWORD: this.configService.get('elasticsearch.kibanaPassword') || 'Kibana2024!System', + const stringData: { [key: string]: string } = { + ELASTIC_PASSWORD: this.configService.get('elasticsearch.password') || '', + FLUENTBIT_PASSWORD: this.configService.get('elasticsearch.fluentbitPassword') || '', + KIBANA_SYSTEM_PASSWORD: this.configService.get('elasticsearch.kibanaPassword') || '', }; try { @@ -1512,7 +1544,7 @@ export class KubernetesService implements OnModuleInit { } const previewRootDomain = this.configService.get('platform.previewRootDomain') || ctx.domain; - const namespacePrefix = ctx.ownerId.split('-')[0]; + const namespacePrefix = userIdSlug(ctx.ownerId); const previewHost = previewNumber && !customDomain ? `${namespacePrefix}-${previewNumber}.${previewRootDomain}` : ''; if (previewHost) { rules.push({ @@ -2228,7 +2260,7 @@ export class KubernetesService implements OnModuleInit { async scaleDeployment(app: Application, replicas: number): Promise { const { appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); await appsApi.patchNamespacedDeployment({ name: app.name, namespace, body: { spec: { replicas } } }, k8s.setHeaderOptions('Content-Type', 'application/merge-patch+json')); } @@ -2268,7 +2300,7 @@ export class KubernetesService implements OnModuleInit { async captureWorkloadReplicaSnapshot(app: Application): Promise> { const { appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const snapshot: Record = {}; for (const workload of this.getApplicationWorkloadDeployments(app)) { @@ -2297,7 +2329,7 @@ export class KubernetesService implements OnModuleInit { */ async suspendApplication(app: Application): Promise> { const { appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); this.logger.log(`Suspending application ${app.name} in namespace ${namespace}`); @@ -2323,7 +2355,7 @@ export class KubernetesService implements OnModuleInit { */ async resumeApplication(app: Application): Promise { const { appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); this.logger.log(`Resuming application ${app.name} in namespace ${namespace}`); @@ -2355,7 +2387,7 @@ export class KubernetesService implements OnModuleInit { async restartDeployment(app: Application): Promise { const { appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const deploymentName = isManagedProductType(app.productType) ? this.primaryWorkloadLabel(app) : app.name; await appsApi.patchNamespacedDeployment( @@ -2550,7 +2582,7 @@ export class KubernetesService implements OnModuleInit { */ async getResourceUsage(app: Application): Promise { const { coreApi, appsApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const workloads: any[] = []; @@ -2640,7 +2672,7 @@ export class KubernetesService implements OnModuleInit { workload: 'app' | 'database' | 'redis' | 'rabbitmq' = 'app', ): Promise { const { appsApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const target = this.workloadDeploymentTarget(app, workload); if (!target) { @@ -2685,7 +2717,7 @@ export class KubernetesService implements OnModuleInit { } getUserNamespace(userId: string): string { - return `user-${userId.split('-')[0]}`; + return userNamespace(userId); } private getClusterHostIp(kc: k8s.KubeConfig): string { @@ -2979,7 +3011,7 @@ export class KubernetesService implements OnModuleInit { const subdomain = app.subdomain || app.name; const verifiedCustomDomain = app.customDomain && app.customDomainStatus === CustomDomainStatus.VERIFIED ? app.customDomain : null; const previewRootDomain = this.configService.get('platform.previewRootDomain') || domain; - const namespacePrefix = app.userId.split('-')[0]; + const namespacePrefix = userIdSlug(app.userId); let ingressUrl = `https://${subdomain}.${domain}`; if (verifiedCustomDomain) { @@ -3362,7 +3394,7 @@ export class KubernetesService implements OnModuleInit { */ async waitForDatabaseReady(app: Application, timeoutMs = 120_000): Promise { const { coreApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const dbLabel = `${app.name}-db`; const start = Date.now(); @@ -3469,14 +3501,12 @@ export class KubernetesService implements OnModuleInit { async restoreDatabaseDump(app: Application, dumpFilePath: string): Promise<{ success: boolean; logs: string }> { const { coreApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); const batchApi = kc.makeApiClient(k8s.BatchV1Api); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const dbName = `${app.name}-db`; const ts = Date.now(); const pvcName = `${app.name}-db-dump-${ts}`; const helperPodName = `${pvcName}-helper`; const jobName = `${app.name}-db-restore-${ts}`; - const isPostgres = app.databaseType === DatabaseType.POSTGRESQL; - const dbDatabase = app.name.replace(/-/g, '_'); const dumpSize = fs.statSync(dumpFilePath).size; const pvcSizeGi = Math.max(1, Math.ceil((dumpSize * 2) / (1024 * 1024 * 1024))); @@ -3562,14 +3592,8 @@ export class KubernetesService implements OnModuleInit { } catch {} } - // ── 4. Build restore command ── - const command = isPostgres - ? ['sh', '-c', `PGPASSWORD="$DB_PASSWORD" psql -h ${dbName} -U "$DB_USER" -d ${dbDatabase} -f /dump/dump.sql 2>&1`] - : ['sh', '-c', `mysql -h ${dbName} -u "$DB_USER" -p"$DB_PASSWORD" ${dbDatabase} < /dump/dump.sql 2>&1`]; - - const defaultDbVer = isPostgres ? '16' : '8.0'; - const restoreDbVer = app.dbVersion || defaultDbVer; - const image = isPostgres ? `postgres:${restoreDbVer}-alpine` : `mysql:${restoreDbVer}`; + // ── 4. Build restore command (per database type) ── + const { image, restoreCommand: command } = this.databaseDumpSpec(app, dbName); // ── 5. Create the restore Job ── const job: k8s.V1Job = { @@ -3777,7 +3801,7 @@ export class KubernetesService implements OnModuleInit { private async migrateDatabasePvcToResizableStorage(app: Application, newSize: string, storageClassName: string): Promise<{ success: boolean; message: string }> { const { coreApi, appsApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); const batchApi = kc.makeApiClient(k8s.BatchV1Api); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const oldPvcName = `${app.name}-db`; const newPvcName = `${app.name}-db-resizable`; const deploymentName = `${app.name}-db`; @@ -3952,7 +3976,7 @@ export class KubernetesService implements OnModuleInit { */ async resizeDatabasePvc(app: Application, newSize: string): Promise<{ success: boolean; message: string }> { const { coreApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const pvcName = `${app.name}-db`; try { @@ -4025,7 +4049,7 @@ export class KubernetesService implements OnModuleInit { async getDatabasePvcSize(app: Application): Promise { try { const { coreApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const pvcName = `${app.name}-db`; const pvc = await coreApi.readNamespacedPersistentVolumeClaim({ @@ -4051,7 +4075,7 @@ export class KubernetesService implements OnModuleInit { totalUsedGb: number; }> { const { coreApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const result = { database: null as StorageUsageSlice | null, @@ -4260,7 +4284,7 @@ export class KubernetesService implements OnModuleInit { */ async resizeNamedPvc(app: Application, pvcName: string, newSize: string, label: string): Promise<{ success: boolean; message: string }> { const { coreApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); try { const pvc = await coreApi.readNamespacedPersistentVolumeClaim({ @@ -4317,7 +4341,7 @@ export class KubernetesService implements OnModuleInit { */ async resizeAppStoragePvc(app: Application, newSize: string): Promise<{ success: boolean; message: string }> { const { coreApi } = await this.k8sClientService.getK8sClient(app.clusterId); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); // Try new unified name first, then legacy wp-content name let pvcName = `${app.name}-storage`; @@ -4374,6 +4398,60 @@ export class KubernetesService implements OnModuleInit { // ─── Snapshot helpers ─────────────────────────────── + /** + * Per-database tooling for dump/restore jobs. `dumpCommand` writes to + * `outputPath`; `restoreCommand` reads from `/dump/dump.sql` (the copied + * dump file keeps that name regardless of format — mongodump archives are + * binary but mongorestore does not care about the extension). + */ + private databaseDumpSpec(app: Application, dbHost: string): { + image: string; + outputPath: string; + dumpCommand: string[]; + restoreCommand: string[]; + } { + const dbDatabase = app.name.replace(/-/g, '_'); + switch (app.databaseType) { + case DatabaseType.POSTGRESQL: { + const image = `postgres:${app.dbVersion || '16'}-alpine`; + return { + image, + outputPath: '/dump/output.sql', + dumpCommand: ['sh', '-c', `PGPASSWORD="$DB_PASSWORD" pg_dump -h ${dbHost} -U "$DB_USER" -d ${dbDatabase} --no-owner --no-acl > /dump/output.sql 2>/tmp/err; cat /tmp/err >&2; echo "DUMP_DONE"; sleep 900`], + restoreCommand: ['sh', '-c', `PGPASSWORD="$DB_PASSWORD" psql -h ${dbHost} -U "$DB_USER" -d ${dbDatabase} -f /dump/dump.sql 2>&1`], + }; + } + case DatabaseType.MONGODB: { + const image = `mongo:${app.dbVersion || '7.0'}`; + const auth = `-u "$DB_USER" -p "$DB_PASSWORD" --authenticationDatabase admin`; + return { + image, + outputPath: '/dump/output.archive', + dumpCommand: ['sh', '-c', `mongodump --host ${dbHost} ${auth} --db ${dbDatabase} --archive=/dump/output.archive --gzip 2>/tmp/err; cat /tmp/err >&2; echo "DUMP_DONE"; sleep 900`], + restoreCommand: ['sh', '-c', `mongorestore --host ${dbHost} ${auth} --nsInclude '${dbDatabase}.*' --archive=/dump/dump.sql --gzip --drop 2>&1`], + }; + } + case DatabaseType.MARIADB: { + const image = `mariadb:${app.dbVersion || '11'}`; + return { + image, + outputPath: '/dump/output.sql', + dumpCommand: ['sh', '-c', `mariadb-dump -h ${dbHost} -u "$DB_USER" -p"$DB_PASSWORD" ${dbDatabase} > /dump/output.sql 2>/tmp/err; cat /tmp/err >&2; echo "DUMP_DONE"; sleep 900`], + restoreCommand: ['sh', '-c', `mariadb -h ${dbHost} -u "$DB_USER" -p"$DB_PASSWORD" ${dbDatabase} < /dump/dump.sql 2>&1`], + }; + } + default: { + const image = `mysql:${app.dbVersion || '8.0'}`; + return { + image, + outputPath: '/dump/output.sql', + dumpCommand: ['sh', '-c', `mysqldump -h ${dbHost} -u "$DB_USER" -p"$DB_PASSWORD" ${dbDatabase} > /dump/output.sql 2>/tmp/err; cat /tmp/err >&2; echo "DUMP_DONE"; sleep 900`], + restoreCommand: ['sh', '-c', `mysql -h ${dbHost} -u "$DB_USER" -p"$DB_PASSWORD" ${dbDatabase} < /dump/dump.sql 2>&1`], + }; + } + } + } + /** * Export (dump) the application database to a local file via a K8s Job. * Returns the dump as a Buffer, or null on failure. @@ -4383,20 +4461,12 @@ export class KubernetesService implements OnModuleInit { async exportDatabaseDump(app: Application, onProgress?: (percent: number) => void): Promise<{ data: Buffer | null; logs: string }> { const { coreApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); const batchApi = kc.makeApiClient(k8s.BatchV1Api); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const dbName = `${app.name}-db`; const jobName = `${app.name}-db-dump-${Date.now()}`; - const isPostgres = app.databaseType === DatabaseType.POSTGRESQL; - const dbDatabase = app.name.replace(/-/g, '_'); - const defaultDbVer = isPostgres ? '16' : '8.0'; - const dbVer = app.dbVersion || defaultDbVer; - const image = isPostgres ? `postgres:${dbVer}-alpine` : `mysql:${dbVer}`; - - // Dump command writes to /dump/output.sql, then sleeps to allow exec retrieval - const command = isPostgres - ? ['sh', '-c', `PGPASSWORD="$DB_PASSWORD" pg_dump -h ${dbName} -U "$DB_USER" -d ${dbDatabase} --no-owner --no-acl > /dump/output.sql 2>/tmp/err; cat /tmp/err >&2; echo "DUMP_DONE"; sleep 900`] - : ['sh', '-c', `mysqldump -h ${dbName} -u "$DB_USER" -p"$DB_PASSWORD" ${dbDatabase} > /dump/output.sql 2>/tmp/err; cat /tmp/err >&2; echo "DUMP_DONE"; sleep 900`]; + // Dump command writes to spec.outputPath, then sleeps to allow exec retrieval + const { image, outputPath, dumpCommand: command } = this.databaseDumpSpec(app, dbName); const job: k8s.V1Job = { apiVersion: 'batch/v1', @@ -4519,7 +4589,7 @@ export class KubernetesService implements OnModuleInit { }); await new Promise((resolve, reject) => { - exec.exec(namespace, podName!, 'dump', ['cat', '/dump/output.sql'], stdoutStream, stderrStream, null, false, (status: k8s.V1Status) => { + exec.exec(namespace, podName!, 'dump', ['cat', outputPath], stdoutStream, stderrStream, null, false, (status: k8s.V1Status) => { if (status.status === 'Success') resolve(); else reject(new Error(status.message || 'exec failed')); }); @@ -4564,7 +4634,7 @@ export class KubernetesService implements OnModuleInit { async archiveWpContent(app: Application): Promise<{ data: Buffer | null; logs: string }> { const { coreApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); const batchApi = kc.makeApiClient(k8s.BatchV1Api); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const pvcName = `${app.name}-storage`; const jobName = `${app.name}-wp-archive-${Date.now()}`; @@ -4712,127 +4782,92 @@ export class KubernetesService implements OnModuleInit { /** * Restore wp-content from a tar.gz archive into the WordPress PVC. + * + * The archive is streamed into a helper pod with `kubectl cp` (a Secret + * would be capped at ~1MiB — far too small for real wp-content) and + * extracted in place onto the mounted PVC. */ async restoreWpContent(app: Application, archiveBuffer: Buffer): Promise<{ success: boolean; logs: string }> { const { coreApi, kc } = await this.k8sClientService.getK8sClient(app.clusterId); - const batchApi = kc.makeApiClient(k8s.BatchV1Api); - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const pvcName = `${app.name}-storage`; - const jobName = `${app.name}-wp-restore-${Date.now()}`; - const secretName = `${jobName}-archive`; + const ts = Date.now(); + const helperPodName = `${app.name}-wp-restore-${ts}`; - // Store archive in a secret - const archiveSecret = { + const helperPod: k8s.V1Pod = { apiVersion: 'v1', - kind: 'Secret', - metadata: { name: secretName, namespace }, - data: { 'wp-content.tar.gz': archiveBuffer.toString('base64') }, - }; - - try { - await coreApi.createNamespacedSecret({ namespace, body: archiveSecret }); - } catch (e: any) { - return { - success: false, - logs: `Failed to create archive secret: ${e.message}`, - }; - } - - const job: k8s.V1Job = { - apiVersion: 'batch/v1', - kind: 'Job', - metadata: { name: jobName, namespace }, + kind: 'Pod', + metadata: { name: helperPodName, namespace }, spec: { - ttlSecondsAfterFinished: 120, - backoffLimit: 0, - template: { - spec: { - restartPolicy: 'Never', - containers: [ - { - name: 'restore', - image: 'alpine:3.19', - command: ['sh', '-c', 'apk add --no-cache tar gzip > /dev/null 2>&1 && rm -rf /wp-content/* && cd /wp-content && tar xzf /archive/wp-content.tar.gz && echo "RESTORE_DONE"'], - volumeMounts: [ - { name: 'wp-content', mountPath: '/wp-content' }, - { name: 'archive', mountPath: '/archive', readOnly: true }, - ], - resources: { - requests: { cpu: '100m', memory: '64Mi' }, - limits: { cpu: '500m', memory: '256Mi' }, - }, - }, - ], - volumes: [ - { - name: 'wp-content', - persistentVolumeClaim: { claimName: pvcName }, - }, - { name: 'archive', secret: { secretName } }, - ], + containers: [ + { + name: 'restore', + image: 'alpine:3.19', + command: ['sh', '-c', 'sleep 3600'], + volumeMounts: [{ name: 'wp-content', mountPath: '/wp-content' }], + resources: { + requests: { cpu: '100m', memory: '128Mi' }, + limits: { cpu: '500m', memory: '512Mi' }, + }, }, - }, + ], + volumes: [{ name: 'wp-content', persistentVolumeClaim: { claimName: pvcName } }], + restartPolicy: 'Never', }, }; - try { - await batchApi.createNamespacedJob({ namespace, body: job }); - } catch (e: any) { - try { - await coreApi.deleteNamespacedSecret({ name: secretName, namespace }); - } catch {} - return { - success: false, - logs: `Failed to create restore job: ${e.message}`, - }; - } + const tmpArchive = path.join(os.tmpdir(), `wp-content-restore-${ts}.tar.gz`); + const tmpKubeconfig = path.join(os.tmpdir(), `kubeconfig-wprestore-${ts}.yaml`); - // Wait - const timeout = 300_000; - const start = Date.now(); - let succeeded = false; - let failed = false; - while (Date.now() - start < timeout) { - await new Promise((r) => setTimeout(r, 3000)); - try { - const st = await batchApi.readNamespacedJob({ - name: jobName, - namespace, - }); - if (st.status?.succeeded && st.status.succeeded > 0) { - succeeded = true; - break; - } - if (st.status?.failed && st.status.failed > 0) { - failed = true; - break; - } - } catch {} - } - - let logs = ''; try { - const pods = await coreApi.listNamespacedPod({ - namespace, - labelSelector: `job-name=${jobName}`, - }); - if (pods.items.length > 0 && pods.items[0].metadata?.name) { - const logRes = await coreApi.readNamespacedPodLog({ - name: pods.items[0].metadata.name, - namespace, - }); - logs = logRes || ''; + fs.writeFileSync(tmpArchive, archiveBuffer); + fs.writeFileSync(tmpKubeconfig, kc.exportConfig()); + + await coreApi.createNamespacedPod({ namespace, body: helperPod }); + + // Wait for helper pod Running + const podTimeout = 120_000; + const podStart = Date.now(); + while (Date.now() - podStart < podTimeout) { + const pod = await coreApi.readNamespacedPod({ name: helperPodName, namespace }); + if (pod.status?.phase === 'Running') break; + if (pod.status?.phase === 'Failed') throw new Error('wp-content restore helper pod failed to start'); + await new Promise((r) => setTimeout(r, 2000)); } - } catch {} - try { - await coreApi.deleteNamespacedSecret({ name: secretName, namespace }); - } catch {} + await execFileAsync( + 'kubectl', + ['--kubeconfig', tmpKubeconfig, 'cp', tmpArchive, `${namespace}/${helperPodName}:/tmp/wp-content.tar.gz`, '--retries', '3'], + { maxBuffer: 50 * 1024 * 1024, timeout: 600_000 }, + ); - return { - success: succeeded && !failed, - logs: logs || (succeeded ? 'Restore completed' : 'Restore failed or timed out'), - }; + const { stdout, stderr } = await execFileAsync( + 'kubectl', + [ + '--kubeconfig', tmpKubeconfig, 'exec', '-n', namespace, helperPodName, '--', + 'sh', '-c', + 'rm -rf /wp-content/* /wp-content/.[!.]* 2>/dev/null; tar xzf /tmp/wp-content.tar.gz -C /wp-content && echo RESTORE_DONE', + ], + { maxBuffer: 10 * 1024 * 1024, timeout: 600_000 }, + ); + + const logs = `${stdout || ''}${stderr || ''}`; + const success = logs.includes('RESTORE_DONE'); + return { success, logs: logs || (success ? 'Restore completed' : 'Restore failed') }; + } catch (e: any) { + this.logger.error(`wp-content restore failed for ${app.name}: ${e.message}`); + return { success: false, logs: e.message || 'wp-content restore failed' }; + } finally { + try { + fs.unlinkSync(tmpArchive); + } catch {} + try { + fs.unlinkSync(tmpKubeconfig); + } catch {} + try { + await coreApi.deleteNamespacedPod({ name: helperPodName, namespace }); + } catch {} + } } // ─── K8s Revision-based Rollback ───────────────────── @@ -4852,7 +4887,7 @@ export class KubernetesService implements OnModuleInit { }>; currentRevision: number; }> { - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const releaseName = app.name; try { @@ -4890,7 +4925,7 @@ export class KubernetesService implements OnModuleInit { * Rollback a Helm release to a specific revision. */ async rollbackDeploymentRevision(app: Application, targetRevision: number): Promise<{ success: boolean; message: string }> { - const namespace = `user-${app.userId.split('-')[0]}`; + const namespace = this.getUserNamespace(app.userId); const releaseName = app.name; try { diff --git a/backend/src/kubernetes/logs.controller.ts b/backend/src/kubernetes/logs.controller.ts index 2b0a3ea..9adb610 100644 --- a/backend/src/kubernetes/logs.controller.ts +++ b/backend/src/kubernetes/logs.controller.ts @@ -18,6 +18,7 @@ import { AuthGuard } from '@nestjs/passport'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { ElasticsearchService } from './elasticsearch.service'; +import { userNamespace } from './k8s-workload.util'; import { Application } from '../applications/entities/application.entity'; import { RolesGuard } from '../common/guards/roles.guard'; import { Roles } from '../common/decorators/roles.decorator'; @@ -225,7 +226,7 @@ export class LogsController { if (appFilters.applicationName) { filterParts.push(`applicationName:${appFilters.applicationName}`); } - filterParts.push(`namespace:user-${userId.split('-')[0]}`); + filterParts.push(`namespace:${userNamespace(userId)}`); const kibanaHost = connInfo.host.replace('elasticsearch', 'kibana'); const query = filterParts.length > 0 ? filterParts.join(' AND ') : '*'; diff --git a/backend/src/main.ts b/backend/src/main.ts index e871a6a..14243ce 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,5 +1,5 @@ -import { NestFactory } from '@nestjs/core'; -import { Logger, ValidationPipe } from '@nestjs/common'; +import { NestFactory, Reflector } from '@nestjs/core'; +import { ClassSerializerInterceptor, Logger, ValidationPipe } from '@nestjs/common'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import helmet from 'helmet'; import { AppModule } from './app.module'; @@ -47,22 +47,31 @@ async function bootstrap() { }), ); + // Strip @Exclude()-marked fields (e.g. gitToken) from serialized responses. + app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); + // API prefix app.setGlobalPrefix('api/v1'); - // Swagger - const config = new DocumentBuilder() - .setTitle('CloudHost PaaS API') - .setDescription('Self-service PaaS platform API') - .setVersion('1.0') - .addBearerAuth() - .build(); - const document = SwaggerModule.createDocument(app, config); - SwaggerModule.setup('api/docs', app, document); + // Swagger — disabled in production unless explicitly opted in (SWAGGER_ENABLED=true) + const swaggerEnabled = + process.env.NODE_ENV !== 'production' || process.env.SWAGGER_ENABLED === 'true'; + if (swaggerEnabled) { + const config = new DocumentBuilder() + .setTitle('CloudHost PaaS API') + .setDescription('Self-service PaaS platform API') + .setVersion('1.0') + .addBearerAuth() + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api/docs', app, document); + } const port = process.env.PORT || 4000; await app.listen(port); console.log(`🚀 CloudHost API running on http://localhost:${port}`); - console.log(`📚 Swagger docs at http://localhost:${port}/api/docs`); + if (swaggerEnabled) { + console.log(`📚 Swagger docs at http://localhost:${port}/api/docs`); + } } bootstrap(); diff --git a/backend/src/users/entities/user.entity.ts b/backend/src/users/entities/user.entity.ts index 53cd306..9ac024d 100644 --- a/backend/src/users/entities/user.entity.ts +++ b/backend/src/users/entities/user.entity.ts @@ -6,6 +6,7 @@ import { UpdateDateColumn, OneToMany, } from 'typeorm'; +import { Exclude } from 'class-transformer'; import { UserRole } from '../../common/enums'; import { Application } from '../../applications/entities/application.entity'; @@ -30,6 +31,8 @@ export class User { @Column({ default: false }) phoneVerified: boolean; + /** Bcrypt hash — never serialized into API responses. */ + @Exclude({ toPlainOnly: true }) @Column() password: string; diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 343ea6c..094816e 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -12,6 +12,7 @@ import * as bcrypt from 'bcrypt'; import { User } from './entities/user.entity'; import { UserRole } from '../common/enums'; import { normalizeIranMobile } from '../common/phone.util'; +import { userNamespace } from '../kubernetes/k8s-workload.util'; @Injectable() export class UsersService { @@ -22,9 +23,10 @@ export class UsersService { async create(data: Partial): Promise { const user = this.usersRepository.create(data); - // Assign a unique namespace based on user ID + // Assign a unique namespace based on the FULL user UUID (truncated + // prefixes collide between users and break tenant isolation). const saved = await this.usersRepository.save(user); - saved.namespace = `user-${saved.id.split('-')[0]}`; + saved.namespace = userNamespace(saved.id); return this.usersRepository.save(saved); } diff --git a/frontend/src/app/[lang]/dashboard/apps/[id]/page.tsx b/frontend/src/app/[lang]/dashboard/apps/[id]/page.tsx index 559367a..d8810f0 100644 --- a/frontend/src/app/[lang]/dashboard/apps/[id]/page.tsx +++ b/frontend/src/app/[lang]/dashboard/apps/[id]/page.tsx @@ -1566,7 +1566,7 @@ export default function AppDetailPage() { {app.gitBranch} )} - {app.gitToken && ( + {(app.hasGitToken ?? app.gitToken) && ( {ad.private} )} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 365a887..c6c7b08 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -33,6 +33,8 @@ export interface Application { appStorageSize?: string; gitUrl?: string; gitToken?: string; + /** Server-provided indicator; raw gitToken is no longer returned by the API. */ + hasGitToken?: boolean; gitBranch?: string; codePath?: string; envVars?: Record; diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 0000000..504afef --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +package-lock.json diff --git a/scripts/audit-report.fa.html b/scripts/audit-report.fa.html new file mode 100644 index 0000000..7748442 --- /dev/null +++ b/scripts/audit-report.fa.html @@ -0,0 +1,317 @@ + + + + +گزارش بررسی فنی CloudHost + + + + +

+ +
+ BUG — قطعاً می‌شکند + SECURITY — حفره امنیتی + RISK — احتمال شکست در پروداکشن + IMPROVEMENT — بهبود +
+ +
+

خلاصه مدیریتی

+

پروژه معماری خوبی دارد اما در وضعیت فعلی آماده پروداکشن نیست. چند دسته مشکل بحرانی وجود دارد که یا هم‌اکنون باگ هستند یا حتماً در پروداکشن (به‌ویژه در شبکه ایران) می‌شکنند:

+
    +
  1. باگ‌های قطعی بیلد — برخی Dockerfileها اصلاً build نمی‌شوند (مثلاً Go).
  2. +
  3. باگ چرخه دوم آپگرید — سیستم migration در دومین helm upgrade قطعاً می‌شکند.
  4. +
  5. حفره‌های امنیتی مالی — کاربر می‌تواند کیف پول خود را رایگان شارژ کند و بدون پرداخت دیپلوی کند.
  6. +
  7. وابستگی به Docker Hub بدون آینه (mirror) برای ایمیج دیتابیس‌ها و base imageها.
  8. +
  9. چرخش رمز سرویس‌ها — رمز Redis/RabbitMQ در هر آپگرید عوض می‌شود و اتصال اپ قطع می‌شود.
  10. +
+
+ +
+

۱. فرایند بیلد اپلیکیشن‌ها (Kaniko + Dockerfile هر رانتایم)

+ +

باگ‌های قطعی

+ +
+
BUGGo — سینتکس نامعتبر COPY؛ هر بیلد Go خراب می‌شود
+

backend/src/build/build.service.ts:1311-1314 — دستور COPY ... 2>/dev/null || true از ریدایرکت شل پشتیبانی نمی‌کند؛ Kaniko این خطوط را رد می‌کند و بیلد هر اپ Go شکست می‌خورد.

+
+ +
+
BUGNode.js — شکست بیلد نادیده گرفته می‌شود
+

backend/src/build/build.service.ts:1034RUN npm run build || echo "..."؛ اگر بیلد خطا بدهد باز هم ایمیج ساخته می‌شود و اپ خراب دیپلوی می‌شود. کاربر «بیلد موفق» می‌بیند ولی اپ کار نمی‌کند.

+
+ +

ریسک‌های جدی

+ +
+
RISKBase imageها بدون آینه، از Docker Hub / GCR / MCR
+

همه رانتایم‌ها (node:, php:, python:, golang:, wordpress:) و ایمیج Kaniko و init pods (alpine:3.19, alpine/git) مستقیم از رجیستری‌های عمومی pull می‌شوند. آینه فقط برای استک لاگینگ تعریف شده (configuration.ts:151). در ایران بیشترین منبع شکست بیلد است.

+
+ +
+
RISKLaravel — نبود اکستنشن‌های ضروری PHP
+

backend/src/build/build.service.ts:1085 — فقط pdo, pdo_mysql, opcache نصب می‌شود؛ mbstring, xml, bcmath, zip, fileinfo, tokenizer که Laravel استاندارد لازم دارد نصب نمی‌شود.

+
+ +
+
RISKPython — پروژه‌های pyproject.toml پشتیبانی نمی‌شوند
+

backend/src/build/build.service.ts:1413 — تشخیص‌دهنده pyproject.toml را Python می‌شناسد ولی Dockerfile فقط requirements.txt نصب می‌کند؛ پروژه‌های Poetry/PDM فقط Flask+gunicorn پیش‌فرض می‌گیرند. اگر install خطا بدهد، fallback خاموش (2>/dev/null ||) اپ اشتباه بالا می‌آورد.

+
+ +
+
RISKحافظه Kaniko فقط ۴Gi و PVC بیلد بدون StorageClass
+

build.service.ts:588 بیلد Next.js/.NET/Composer اغلب بیشتر می‌خواهد → OOMKilled. build.service.ts:775 PVC بیلد storageClassName ندارد → در کلاستر بدون SC پیش‌فرض برای همیشه Pending می‌ماند. همچنین npm install --legacy-peer-deps به‌جای npm ci (خط ۱۰۱۸).

+
+ +

امنیت بیلد

+ +
+
SECURITYتوکن Git داخل spec پاد و تزریق دستور از branch
+

build.service.ts:498-523cloneUrl با توکن embed‌شده در command کانتینر → قابل دیدن در kubectl get pod -o yaml، etcd و audit log. همچنین ${branch} بدون کوت داخل شل → نامی مثل main; curl evil کد اجرا می‌کند. بدون اعتبارسنجی URL گیت (SSRF به IPهای داخلی کلاستر). خطر Zip slip / zip bomb در استخراج با unzip (خط ۴۶۲) با سقف آپلود ۱۰GiB.

+
+ +

پایداری فرایند

+ +
+
BUGری‌استارت backend وسط بیلد → deployment گیر می‌کند
+

build.service.ts:56 — state بیلد در Map حافظه است؛ بعد از ری‌استارت، Job روی کلاستر ادامه می‌دهد ولی deployment در وضعیت BUILDING گیر می‌کند و reconcile نمی‌شود. همچنین دیپلوی هم‌زمان برای یک اپ قفل ندارد و روی همان Helm release رقابت می‌کنند.

+
+
+ +
+

۲. پیش‌نمایش و دیپلوی

+

پیش‌نمایش با ساخت یک عدد ۷ رقمی پایدار برای هر اپ و host به‌شکل {userPrefix}-{previewNumber}.{previewRootDomain} کار می‌کند.

+ +
+
RISKبا ست‌شدن دامنه اختصاصی، پیش‌نمایش بلافاصله حذف می‌شود
+

kubernetes.service.ts:291 — حتی قبل از تأیید DNS؛ کاربر تا وریفای شدن دامنه هیچ آدرس قابل‌دسترسی ندارد. پیش‌نمایش نیازمند DNS wildcard فعال + cert-manager و مقدار PREVIEW_BASE_DOMAIN است.

+
+ +
+
RISKgetPreviewInfo روی هر فراخوانی Service را به NodePort پچ می‌کند
+

kubernetes.service.ts:2955 — عارضه جانبی که ممکن است اپ را ناخواسته روی IP نود باز کند.

+
+ +
+
BUGرجیستری per-cluster + fallback بین‌کلاستری → ImagePullBackOff
+

deployments.service.ts:360 — ایمیج روی رجیستری کلاستر A ساخته و push می‌شود، ولی deployWithClusterFallback می‌تواند روی کلاستر B دیپلوی کند که آن ایمیج را ندارد.

+
+
+ +
+

۳. دیتابیس‌ها و سرویس‌های اختیاری

+ +

باگ‌ها

+ +
+
BUGرمز Redis و RabbitMQ در هر helm upgrade عوض می‌شود
+

redis-deployment.yaml:18، rabbitmq-deployment.yaml:19randAlphaNum 16 بدون lookup هر بار مقدار جدید تولید می‌کند؛ resource-policy: keep فقط جلوی حذف را می‌گیرد نه تغییر. بعد از هر redeploy رمز عوض می‌شود ولی داده PVC رمز قدیمی دارد → قطع اتصال. الگوی درست در چارت پلتفرم (cloudhost-platform/templates/secret.yaml) با lookup موجود است.

+
+ +
+
BUGHealth probe رِدیس/مونگو بدون احراز هویت
+

redis-deployment.yaml:80redis-cli ping بدون -a؛ با --requirepass جواب NOAUTH → probe رد → CrashLoopBackOff. همین برای probe مونگو بدون credential.

+
+ +
+
BUGMongoDB در snapshot و wp-content restore پشتیبانی نمی‌شوند
+

kubernetes.service.ts:4389 — export/restore فقط Postgres و MySQL دارد؛ اپ Mongo dump خراب می‌گیرد. kubernetes.service.ts:4724 — restore محتوای wp-content از طریق Secret ذخیره می‌شود که محدودیت ~۱MiB دارد؛ هر wp-content واقعی بزرگ‌تر است → شکست.

+
+ +
+
BUGWordPress + PostgreSQL و WordPress بدون دیتابیس مجاز است
+

ایمیج رسمی وردپرس فقط MySQL/MariaDB را می‌شناسد ولی پلتفرم databaseType: postgresql یا حتی none را می‌پذیرد → سایت بالا نمی‌آید. باید هنگام رانتایم WordPress دیتابیس اجباراً MySQL شود.

+
+ +

ریسک‌ها

+
    +
  • RISK ایمیج همه سرویس‌ها از Docker Hub بدون مکانیزم آینه در چارت اپ (postgres:16-alpine, mysql:8.0, ...)؛ override database.image هست ولی backend هرگز آن را ست نمی‌کند.
  • +
  • RISK Deployment + PVC نوع RWO بدون strategy: Recreate برای دیتابیس/Redis/RabbitMQ → در آپگرید ایمیج پاد جدید منتظر ولوم می‌ماند.
  • +
  • RISK fallback تولید رمز DB (kubernetes.service.ts:333): اگر dbPassword خالی باشد هر دیپلوی رمز جدید می‌سازد و با داده قدیمی PVC ناسازگار می‌شود.
  • +
  • RISK خاموش‌کردن سرویس PVC یتیم جا می‌گذارد — کاربر آن‌ها را نمی‌بیند ولی هزینه استوریج ادامه دارد.
  • +
  • RISK دسترسی خارجی NodePort — host اشتباه (kubernetes.service.ts:2691): IP از API server گرفته می‌شود نه worker node؛ رشته اتصال بلااستفاده است. suspend هم گرنت‌های NodePort را باطل نمی‌کند.
  • +
+
+ +
+

۴. کنترل‌پلین، GitOps و CI/CD

+ +

باگ‌ها (باید قبل از دیپلوی بعدی رفع شوند)

+ +
+
BUGسیستم migration در آپگرید دوم می‌شکند
+

migrations-job.yaml:50-53 — Job همه فایل‌های SQL را در هر اجرا دوباره اجرا می‌کند بدون جدول ردیابی نسخه. 001_service_access_grants.sql:2,9 از CREATE TYPE بدون گارد استفاده می‌کند → آپگرید دوم: ERROR: type already exists → sync fail.

+
+ +
+
BUGmigration هوک بعد از دیپلوی backend اجرا می‌شود
+

migrations-job.yaml:10-11post-upgrade؛ backend جدید ممکن است قبل از آماده شدن اسکیما بالا بیاید → CrashLoop. باید pre-upgrade باشد.

+
+ +
+
BUGنبود base schema و نام ستون اشتباه در migration 015
+

هیچ SQL جدول‌های users/applications را نمی‌سازد؛ روی دیتابیس خالی اولین migration شکست می‌خورد. 015_application_product_type.sql:5-6 ستون user_id می‌سازد ولی entity آن را userId تعریف کرده (application.entity.ts:150) → ساخت ایندکس fail.

+
+ +

رمزهای هاردکد شده در گیت

+
+
SECURITYرمزهای الستیک‌سرچ در فایل commit‌شده
+

backend/k8s/logging/elasticsearch-stack.yaml:21-23ELASTIC_PASSWORD: "CloudHost2024!Secure" و FLUENTBIT_PASSWORD. باید rotate و از گیت خارج شوند. همین‌ها به‌عنوان default در configuration.ts:148-150 هستند و در validate-production بررسی نمی‌شوند.

+
+ +

ریسک‌های CI/CD و کنترل‌پلین

+
    +
  • RISK workflow کامیت‌شده auth کانیکو به Harbor و توکن clone ندارد (.gitea/workflows/build-deploy.yaml:74) → push/clone شکست می‌خورد؛ اصلاحات در تغییرات uncommit هستند.
  • +
  • RISK تست‌ها در مسیر Gitea اجرا نمی‌شوند (فقط GitHub Actions) → کد خراب می‌تواند به پروداکشن برسد.
  • +
  • RISK ایمیج backend حین بیلد Helm و kubectl را از اینترنت دانلود می‌کند (backend/Dockerfile:16-20) بدون پروکسی.
  • +
  • RISK git push بدون pull --rebase (workflow:177) → احتمال half-done deploy.
  • +
  • RISK postgres/redis پلتفرم در values-abrban.yaml آینه نشده و imagePullSecret ندارند.
  • +
  • RISK strategy: Recreate روی backend (backend-deployment.yaml:12) → داون‌تایم کامل API در هر دیپلوی.
  • +
  • RISK بدون resource limits در values پروداکشن → ریسک OOM روی k3s تک‌نود؛ Redis پلتفرم بدون requirepass؛ backup پستگرس خاموش.
  • +
  • RISK docker compose up --build کامل کار نمی‌کند — backend با NODE_ENV=productionsynchronize:false و بدون migration → جدول‌ها موجود نیست.
  • +
+
+ +
+

۵. امنیت و کیفیت کد اپلیکیشن

+ +

حفره‌های امنیتی بحرانی (P0)

+ +
+
SECURITYهر کاربر لاگین‌شده می‌تواند کیف پول خود را رایگان شارژ کند
+

billing-wallet.controller.ts:45-49POST /billing/wallet/charge بدون درگاه پرداخت مستقیم chargeWallet را صدا می‌زند → پول رایگان در پروداکشن. همچنین gateway/verify با PAYMENT_GATEWAY_STUB_ENABLED=true مبلغ دلخواه را می‌پذیرد.

+
+ +
+
SECURITYدور زدن بیلینگ در deploy / start / resources
+

deployments.service.ts:637 startDeployment اپ suspend‌شده را بدون بررسی وضعیت/کیف پول resume می‌کند. triggerDeployment (دیپلوی اول) گارد بیلینگ ندارد. applications.controller.ts:375 PATCH resources ارتقا را بدون مسیر پرداخت انجام می‌دهد.

+
+ +
+
SECURITYتداخل namespace بین کاربران (۸ کاراکتر اول UUID)
+

kubernetes.service.ts:2687-2689user-${userId.split('-')[0]}؛ دو کاربر با ۸ کاراکتر اول یکسان namespace مشترک و دسترسی به workload/secret همدیگر می‌گیرند. همین مشکل در ایزوله‌سازی لاگ الستیک (elasticsearch.service.ts:676).

+
+ +

امنیتی (P1)

+
    +
  • SECURITY gitToken و dbPassword در پاسخ API برمی‌گردند (application.entity.ts:54,114) — نیاز به @Exclude.
  • +
  • RISK عملیات کیف پول بدون transaction/lock (billing.service.ts:210) — کسر هم‌زمان می‌تواند overdraw کند.
  • +
  • RISK اسکنر auto-renew idempotent نیست بین رپلیکاها (app-lifecycle.service.ts:39) — دو پاد یک اپ را دوبار شارژ می‌کنند.
  • +
  • BUG proration ارتقا همیشه نرخ ساعتی را استفاده می‌کند (billing.service.ts:755) → ارتقای ماهانه/سالانه undercharge یا رایگان.
  • +
  • SECURITY توکن‌ها در localStorage (frontend/src/lib/store.ts:43) → در معرض XSS.
  • +
  • SECURITY refresh token بدون rotation/ابطال و context جعل هویت روی refresh دوباره اعتبارسنجی نمی‌شود (auth.service.ts:165).
  • +
+ +

ریسک‌های متوسط

+
    +
  • RISK OTP با Math.random() به‌جای CSPRNG (verification.service.ts:188) و race در مصرف OTP (خط ۲۲۵).
  • +
  • RISK Swagger بی‌قید در پروداکشن باز است (main.ts:53).
  • +
  • RISK secretهای پیش‌فرض ضعیف خارج از پروداکشن (configuration.ts:75default-jwt-secret).
  • +
+
+ +
+

اولویت‌بندی برای پروداکشن

+ +
+

باید قبل از هر دیپلوی پروداکشن رفع شود (بلاکر)

+
    +
  1. حذف/گیت کردن POST /billing/wallet/charge پشت درگاه پرداخت واقعی.
  2. +
  3. گارد بیلینگ روی triggerDeployment، startDeployment و PATCH resources.
  4. +
  5. ساخت namespace از کل UUID، نه ۸ کاراکتر اول (تداخل بین‌مستأجری).
  6. +
  7. سیستم migration: جدول ردیابی نسخه یا SQL کاملاً idempotent + هوک pre-upgrade + base schema برای نصب تازه.
  8. +
  9. اصلاح 015 (user_iduserId) و گارد duplicate_object برای CREATE TYPE در 001.
  10. +
  11. commit و deploy اصلاحات uncommit شده workflow (توکن Gitea + auth Harbor کانیکو).
  12. +
  13. rotate کردن رمزهای هاردکد الستیک‌سرچ.
  14. +
  15. رفع سینتکس COPY در Dockerfile گو و حذف || echo از بیلد Node.
  16. +
+
+ + + + + + + + + + + + + + + + + +
اولویتاقدام
۹الگوی lookup برای رمز Redis/RabbitMQ (توقف چرخش رمز).
۱۰probe رِدیس/مونگو با احراز هویت.
۱۱آینه‌کردن base imageهای بیلد + ایمیج دیتابیس‌ها برای شبکه ایران.
۱۲transaction/lock روی عملیات کیف پول.
۱۳strategy: Recreate روی سرویس‌های stateful و RollingUpdate روی backend.
۱۴رفع ImagePullBackOff در fallback بین‌کلاستری.
۱۵حذف gitToken/dbPassword از پاسخ‌ها با @Exclude.
۱۶اعتبارسنجی و کوت gitBranch، انتقال توکن گیت به Secret.
۱۷پشتیبانی MongoDB در snapshot، restore وردپرس از PVC به‌جای Secret.
۱۸اجبار MySQL برای رانتایم WordPress.
۱۹اجرای تست در مسیر Gitea قبل از دیپلوی.
۲۰resource limits و backup پستگرس روی کنترل‌پلین.
+
+ + + + + diff --git a/scripts/generate-audit-pdf.mjs b/scripts/generate-audit-pdf.mjs new file mode 100644 index 0000000..fd1092f --- /dev/null +++ b/scripts/generate-audit-pdf.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node +import puppeteer from 'puppeteer-core'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, '..'); + +const htmlPath = path.join(__dirname, 'audit-report.fa.html'); +const pdfPath = path.join(root, 'AUDIT-REPORT.fa.pdf'); + +const chromePaths = [ + '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome', + '/Applications/Chromium.app/Contents/MacOS/Chromium', + '/usr/bin/google-chrome', + '/usr/bin/chromium', +]; + +const executablePath = process.env.CHROME_PATH || chromePaths.find((p) => fs.existsSync(p)); + +if (!executablePath) { + console.error('Chrome/Chromium not found. Install Google Chrome or set CHROME_PATH.'); + process.exit(1); +} + +const browser = await puppeteer.launch({ + executablePath, + headless: true, + args: ['--no-sandbox', '--disable-setuid-sandbox'], +}); +const page = await browser.newPage(); +await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle0' }); +await page.pdf({ + path: pdfPath, + format: 'A4', + printBackground: true, + margin: { top: '14mm', right: '13mm', bottom: '14mm', left: '13mm' }, +}); +await browser.close(); +console.log(`Created: ${pdfPath}`); diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..31d161e --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,8 @@ +{ + "name": "cloudhost-pdf-scripts", + "private": true, + "type": "module", + "dependencies": { + "puppeteer-core": "^24.0.0" + } +}