From ed3150136af44a0113e4f85b23bb65f72288e8ca Mon Sep 17 00:00:00 2001 From: Priec Date: Mon, 29 Jun 2026 12:26:01 +0200 Subject: [PATCH] original logo and caching implemented --- assets/static/logo/logo.png | Bin 0 -> 1093 bytes assets/static/logo/original.png | Bin 0 -> 10093 bytes src/app.rs | 4 ++++ src/shared/cache.rs | 32 ++++++++++++++++++++++++++++++++ src/shared/mod.rs | 1 + 5 files changed, 37 insertions(+) create mode 100644 assets/static/logo/logo.png create mode 100644 assets/static/logo/original.png create mode 100644 src/shared/cache.rs diff --git a/assets/static/logo/logo.png b/assets/static/logo/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3d86446c2145514a84bc7a7dc35bbc4db2f68fcb GIT binary patch literal 1093 zcmV-L1iJf)P)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00009P)t-s|Ns900RR90|DfuL zz5oCK0d!JMQ{|9`&XE8B00Cl4M??UK1szBL000SaNLh0L01FcU01FcV0GgZ_000A= zNkl~f@chy9d=$N_M=fXlnI-A1nYntZSt1`X);y3b!)7^xTEzTp z)_9hv7_BRz>W0ydL5ZP0q3R&a42sQ5p2aaSjG;t9?Af${wWBLLHZ0YQz!laJ8yf~y zqP-4!d&I~%){fXfY>O~t2J%VPhsMi&aqbX_&Qc_tVE1g5GOVqqYI2z>@#en*qYVF7MJ!KTVr=c zigjV|`J1eWXBswamIe!6!^kM6d374Q#GX^3W%zg8u;<9tTewd8&1BG zllO+Tm`-6o&ED>V% zys#CMVse7*n*vx!=bqrp7(9Z$tgt`SBf+#Qiwzr#GGJje(m57)BZ&1B8w>k`N&_s` zBVy^?m|zXC$A)Pn!k?PC*?Q(T#?NBeO6@nUF+Rm`jt*O84=JBxq*$96U1J6*fxx=D8|rIFrqZ5&JBCSi`O{i`1VZR^f|4lLJOO)?Q#-0@gQ7 z0P8yjuV=P)t}%fN@xWLiHUdWecFvaf9FzOlN5CLXGpV0y3-QImcR_R?llQUF1fKbs zXDxr&*og7!L%^B~HN$2X5@zEczy@F(F{xgl_<7 z7P>F_4)U-Nw*w{_MmzQZtes<65q=Ak9An4eow^+Qv)sih9yTm&aW+iCs;o`cz00009a7bBm000XU z000XU0RWnu7ytk#A4x<(RCt{2UE7Z1NOFxxlDgMrx(D9z`tmgUGBErP{sHfP>e@fx zzgWPqVfevg&n&jP>n4ffCBqMgp-icyDp4Y(q>2Y9bXS#%8JQUw_YAU#{7gN1^ytx} zM~4t!nt(2XnSNXMZ$#*S+n}XKj~>^Fx^gBI8FMbWd6(yPjBtJr#}L>C9f~^igt8u| zAjCObnx;1S>kzcT1DBo?QG2`{!rbm__#)XxW+jhicn-t-8~4A?%+JHONQ-2La1~#foq;yTe!wGyT&=ah0x~P zHN~Y+x`R_*ZDH3&g|;c%2%SEnZqRrwxW=`<#yRamSLYcr-9ag@a}{ZHDfWIUtzDUC zkB^IM=h3dtFLb(tQeNl4r8Y!_{@4Gi`}d(U(xJGPd0Yzp`gIGT?pJ74*N4KVI~e74 zsnYm1af-6)5<+A2(CBFz*Eqj^PT`;{bO)on+Q6=G;W@@UL}7ho@2f|T9+yK`jp=Q= zgHT@Q5Xx=0nmU(<)nx_NW!gi-HLm3~&8e$%=@Q*RD6cl)E0Q)tl9#Yzy2M~Tdi3bg z1}?6=x+aL5^s?Gke3xkB(xXR@aB(dmL!v9~u+g;F72<|7({DqhYae>$)?J+Qj%3&P zx=XmWb-k$ay7VhC&heZ>#_0unk7IC2L9gR>op-zRTAH_LZDPz&>E8GE7gJs#7HM?N zG6~H}4T+vh(bC0EEN0lmTNn1J zF5MSVUZHqhp$L^zXxKN;kgTteXz9`8qaYOdRu_lTeKnp}f9bbJ=;)##=@AA_>2Z2V zzH5{2vn#KVi!^$ij@}^X(W5}Qu#UvrO2SKI_%No{5bW@T}pOr(;b2g zI|NPAeOBdlF0bpODS=QF@Ge|(FG60IYr#i5YoX#JDzmeN4f}9mj~+eV!!?}g5ays^ zx;k>VYjj^t!Nw^F#d>Utc`iMlOOGEau4!GmntS<(=;}VbM|Wp)ULjZs*N#i&;jS(J z*Y&w{o&Fwf54$Ee-=e$syaGelnCmTkM4{)0J=*q8u8CKOu4_J_&gDIR>=NCbR9;=` zN7kc9j~*eRtFmsJ?oKMNt}cF$^P}gLZXi^P{}hBgr?<{USLp7f^160}#60JwC8W9c zI1jFQ&$~LeF3{as<<*8oeGOK6D02vfj%!61gv%kgmU+B|Q0Lh7{8wkI4SV$H(OrZp z7~4Wu-+vY|-JSKkh_2iF`e=u{$KmKQZ*;ZOc~j`>+(V@MY|5*x$%mqBt{LZitMoa> z=`y;yMju^i`OvipVLj@0;0THCPJ3Sa5W?a%fkWrf#(XcmhE0y&8#tFozX!YFD}+m* zxjG1e?z85++UlIpbOYADp=*!O(DP$EXnS4j%98ERUP|eAf$ppHygn+r+|8jE&iipK zYx7aPuT$oINzW#9y3ek>`rOyyIAl)OBG`JoM%O|~hu|8|=^;3^RoEpJSg3SgmFMLz z@~MkP*!{5)n$il1mM&q}8ScW_?h4%(x#x9lUBgdBzZM@GA5A&*Ipyo@@_uey z?2__1iZ@OjqwPc1wO5e0wP)v;$GHS%2#2~0xON40=s7tgT69k1wo7#P9X8I4-plII zMOg#qp5>& z>~p$Yc=49PZ`v3*=U6>XL;oJV!_d_^b&c+eE3a$HL!3)t^+LTzj~=I@t8zr?zPR#g z6Xyyap3}qXD#P`-4)osF+vw_Cx=42r%IjPeeaDXT*1d;wZh9z+t0_7{nNN?)ql?0` z%joLa^XU#kc@cG^bNMCnSZ~qYAN<46uTwMVs^DuIUFEN?(jAQQY7^U=bIjqpzo&YyRv~@p|i)wLf@mbo9L=M*+q9y%ByX3O@W0>->I~P zJhvV_>Y@uuch{NgD&0f@Dz9_AU+0|T9g9%u>d`~ddtVNrpVxi#b(L$m zajiWMPl3;0mp-4ze!dHI2kChcoj-iIJ@z?E_j_U5qsMDpgHpW2`EFv&F3|lUJuk(3 zR(PA9Sg){p^mrfF;whiQt7&7MLZ+KV)U_=S|`g zb87RP`e(Fld2kBeTO*YvL_}@K6Qbs1ijR=#=C5DBu9er9FUI`xi!qA@^J2mL>#tGt z<%>C~BJuNQYk%{bvYbprCKHh#ez5lQXKVe>SWC2A5)`?W_3fK6zx)zK^Z9OC-otn-^8NSL-rQI_nTUM;Z0!#}l;vcy`ME43 zdVB<*eEn+7a%s$b&iwd@y?Sem;ZWxyG9Fv|>65iT{9x_f9p>v}u^{^87x3R=!TkJe z%wPUu%yN0${H9ZpAATsy@4mBku>h@;iAY%zor}-g#JSGyci&mNToOGz81v^pN71)$ z*c0s0`#i7@#t@B0B6oMz-rr-K+gtF;%M19sD45ffBUyZA!6-pMuffE z6u@Q4yj&WyS{bugMA5Im8uNeuhj9;4UY|Z$``3S6mcRK8^K=TMWj525btnpqF`q+8 z{?Gr!@y|a;(J{)48RKRd^I~CbQGnMM3z3(X^YD4wILFz|G7&wi-@f5|e))p&^t>LU zy!4#j-&xhR-dD`P^Z z`}WqDzxz9QXEA#;7()_8Oi@G<$0UORz7F2W9T*JotSG{>jUmx7^PFh0V19grfO>eq z;ypexKRp?KdNQ1*%*HU)p@SKk`{qXE=EmC5NQ7BL1e+B_M8hGpyfH+1Zg{yQn$O`y z=tdF2lA2Dfy}gBiAB`ZC$`W5`Y790P7HFR1=lc~1lL#!AG$np~+}yzFbZuSmV1NzG zGB(Sl$b8P#oIuBPD)#nPIM0R3QfRRdUag3;to}YRlMs=_F;f&VS<6(GOg?|)Inm3D z;m1eA&(DSz3#K$Roaao;B=0dVGfAG~Zx#!o=V!yiA@O)DoFrEKfJ9k>hA3i+VSnL?VXu z_i;=VMIu>dSVL0tIrD1eHi{YU>is>!XoCUKbc)3!f(HU2SXzV>ZCRG^*j6h%3)TXD zreTHGO6~Oq4u_GMPN8^jZbb6j+B_G^GB#-nOJp{KyFDC|j7IpHPD#e&WA}@xC=92m zr8H$)E)D0oB@rfLKs!7a!>g6yqF~B0!^9 zCgACUhBSq6EDBTGh*6BkSQ}j@W_Xo(&fGSip9^iCU1La!g6ZYOaGDZ7J;BI)em4C4 z44ycg2b5lSPVeunMOet%`+N8OY$h_9kmNaY({pPIP+Ux92}9f%HX_R7Tt#fSL4kh= zoJ`3wp~b>PDo^siXvvszZ_%f`bZLJ z>L_B?=NARMoX1DRTA{q6=;eiZK1bZXD4658-sz8HcrQ0MBD0ybDhLrG6p14ITb79J z>UgTOVsAB)gy{ZW&tJENEdqEG)4bnr{Rj zVsE3!?Ri^#eonM`c5z-XuQ(>E6qh;ntor8^-WCkR>D1-CW;5_c6ty!qW}LVv0^b{h zpVfWN-5qkbM2NUdCMpZ8jUwa$-^pyV_HU!nKmAjh?(n?Sf@5yZdSi+r5o2IMKR?sj%TggYpBqNV&Tv^$Eh~=5#m!a0 zL=lOyqAqY*5-pb)bFo+}tyk7X97C9X|Gn7HpKS>*4hL&Af^tHk2P_0@nMH)u^C}8i z=f@Dk7b*$}0Vp0-bnwui%s#Hj4<2$D+j~WDLo8Of;L3 zq$x?7*54~;cnt6!G<1lc&F4&ykC9m}4HpGTSsGK87`Lthfiy+lNmCm2jZ=7s^SNOnHpVa!SvwrA6_DKAh!{g0I^F;EUvV5oBwL;r zGrSX6Sj}3b5ZZ`eM-dXRE^PSth|pbGZhAoPPOUl+zH(vSO#h33vEXT`Rlj>2KY9G= zleM2dp+ZhAL@n+ha+Nq5Due=pa#OLT#Ub$0lr9l13Q@~M6`Fc;2s`4LsJJu+p)PnP zNDN!6UPKfTjYe39`5Ycu-Mumf9!?Z7M-iTZv52zF7_GoeQ|uKEYaOr4?OK)*&F9SH zu`!d0wOB`EthH7>F3bxw9XAego2YKY`pjmq<~8@(6rUS|!-`@M?A`P8+y0mCn<9=~ zbYM8tYAb6;Bm9nezcOp%>>8uIjqjO_F@|WdKztTH6D=rNQ&Ssk9!uQDaVh!4! zoALq#G0|)W*-`g7FqpAF0AvCpJzB_N%xZ=C`xSIaq5|JV1$3?S`PM=K;7sB(#t`YA z)CUaj#ljfu0a7ByVBJR}qR{Cc3=p~7^1MilfhC35@^Lu^A4!5|i7+jf%uz%f$Iztn zIjaT7S!TGZ+^aQSl2BDji9~ObK)CBNu2xQI&F99b(2Ann6Vj^oRE10KK z%$FIo-`$DLGGc2<%Cg>G!5TxJkr|#>mKn}-jfWe~b9|NM@i{0Rt1RJF<~i|lN&Ngw zJQ$FAr^L(~G##7o8jUm;#hS+ zD*m9!1m7R}_i9M^IcN3U(iD*qA~vIuE${Ee76roZD)`}T6A>ZLtHNE^8a%{Y6uizs z7X_jgNwQs=TNg}OMzmTP77>bL&7F&68FA=z4+bRv{_jb=Rc#2@yh@Hz6IDG)LPEr} zT7ht8Qz8U{4;dqliEeHXV$yKN?JdGh<1xaY@6)6T zji`$D5|Ju3(1I`BWW<9Z^q{~{EyzYgh=T!9QIMz|UsXr)VnH0oR^r(0QDsruG!;6g zY+0EuNtlwv@XHH~jiP`u%X6mHO2b8`@kUwVS$U3U7lmW?^-idpwP)LLYhu-9b-1bxkVIY_j0u*myAJpI?GUCQ72D{ie(X0 zd6JAqV#j01pTU5*EQR9OieJPf0jak_xd z&j@i9g)#4q5k`!bR4s2=rE44UyB7s)yuDX8L{r3?L(g9nZqG-6geAC1R<$YLWX)deL6*Q zT6340n`n~HiQIje`TVfY>jipT%-p=Ju3^GB)=;EKmfu?C(Z0GE5D&ImFr;)6q#0HqDY7+Ntnx0NJI$v6~opF!QyZJ?2;pCjk*yM2s-CA zB?+&KTb`eVtR>bvjaps!IwabLT!Po7vI)@+Q{$kE33R9045j6ma2*Ryi64!S+|@QCF42p{QWrJLG|~IM)jk+F>m1e?ktX=-(s&Bc zZN!)~MVAaMO|IN;+ExI5K}C}IITVrAN-gzzp+l5lKXr~aY zb*OVv-W&|{T&*h!8L5OQTCJR-#_u;H9qq^^cmi5TwOTo$d)VKjm`1BY!S5~tlIO-~ zia>;UG=f2bb_p(ppwIJ#zS0Hd;UZUhrcRMbz6^!82{|_$;=FZ%?kpqv$A2t}SDu%~ zmn$Y|Bp2XA`ve}F!x#(@qt%=ClL^Y7MWeKlwv2Yi2S1Vl9wswEMP6QUDt43q3R+G`_xV;m>rBn?8DAQp*nVI)LR}gpj>-~q5fK`VgmCuoj5hX* z2I;f5`J4?V(82+HWiLaO{L|5V-P0L`EYBr0G~6!WVmhNP~Yl{k=oK3)V8BU7D37F|3L8vV?`$^qe$&sf|)KJdOFdd8i=UHy>X~ zEEdG7;H&a$vKEGyyD9=BSB<9j>ez^o-t@bkI=3Rg^=H@&@HrwaL9@b?ei z6+B%1el#Mf@^unLBxwr6KXkf({IMvAc)MUjy8|Fuj)T|?A%4E5YX4LF@cLmiW3*u| ziZUw8oi)5xmT-ynp4BesO@DQr^_Kr3N#rIWfj_%Q611R*26I_1_wNOY1HdO$<=OiCx;$tMlFyn% z2lG2&*an{?fsgqniTl4+9Ok*cO@KMiMbflhn97HCY|*=cFxuSno0`+MLPmn$@fd^| z3>Cd>f5@4(7N5u8VGI$pJEJO_7sn!TjIdlA`*kXCe^u#%>_K?ky$wSnK`$@v%8ag0 zQw2{rFW4jfS6g#y8N1%eaz#Nn6Z(}Tny%;u-TI7Ojd@vJ{3^D5l4jLP78R^27|}qs zo&wW*VB0;{^x|6;Vp>-vjYcF{hPw*8o;4mLOsGlm;c)H2oUN&9<#iD}F6}D@%ctrs zws-xjYOS^Pr+EO)Qw@h=MLErYis&ZVE zqA=rUh-&fsXyn9Z#D=BiCm!iU6D^XS0tDsl-*Dw4oElyduOUHWMLhmMah3=ib z4b{SNC8@`iDEk-;5Smj$;Wfb$);m~rN+{u_IPHuA16XX@9a&4vHJ+ZPugk`f^k#-T z>KAF$W8zbw?W0yJeHDwTDhpwQyp`arE+rE#ufp=z4gshNbP3e$jw9~@9@l2lTzO+3 zpfFs)%U}R2O7A>rlbKz6amu*6pv3{WH>I5`+$yxe=ji02*A#I1c0AV3AY2Do ztka;kYuxL7+L+UL?4lj&%Rv~LafV(TFasggV({qbs4TZXbs5%7& zkZW#jke|i=YScu(PAODS;jL#%yRi74$#!uIkyA$a4l+$6H2NI(*>2ITfAgxthF8_2 z-J4H>T5mdxmcv#`gVU7Ocfbw@Z^Xx|8{7l|g~f?u61+%JY!tm7LvMU*lC^r*lUN%W zzfI4!h0r9lloZ7!u-dA!Q+ZEuB?*+pWa3^CF&epYdZ)a^T1)JgvmS=cj~lb!`qEAV z+Lgq0Z--@GH4m1u6if6TEr*Z0kCyQmg*=raedSHJ!RK++MMhggMUj=FhzyY`D&+3e zsytPBY4ia-|6I3&rq8af<7i~7yf98t)pf-%vr)6)lu({ATzRGHO>))Lpq>313~23n zXq2W-Z$lN#nv07fs>1WGuml2}xh@a;sKV&t8QImivT@e+I*R5C(RIiF8yLAsg7xzY z=|s@QrfYQX=&*s%Ls>Ep2K8GEZ9|JHRMTtzYqip{SF|Llp95nmit6T3rLxr8$7N_2 zS}+E=A76oK=%ose5s^q)8eXmNIXscpF>TGo_zLY5rG~^VLWX(|LSLg1$Glf)tKzCW zMH!DFTr}iaDekqEsutq0Z=yZ~QPuuOL%Kn|P0V7oV+8KvHFMj3A1!)kMf1p$1BG%@|jH<`u3zzf`8}fM&{FSxb*qjh9Vk%3K)yi-b z;k|L{(XUoo(Tj2Qdc!fOE@bOjtInl}t2AtOzmn=zCR+ZDD8b>>;}u0jNg~zdWU2DG zuE47D5T$Nz-$!MXh~Ddj3={FsQ{}}}#<-TXYm~wLy($U+YE3nURQ)4^qPwDN-JNKg z?sq$EG{7XDTdqwYp#)&{AZDIr+JU{^^EE_*%UKhr6t~lb>`uc2_NZoZ01|9V4 zt?eODgG77pDP7oScb7)*Zt2>d9yZ$z&9P!{u$H*^>eOW$eC`gV8c^pT=%My;>m(ZZg#`P{cO!8a&4AVQxv z74{A;pGh1jiRBPR7N;s(nXWt{pkF| zqT5C5)N@?7;o6n+s`}bB&;{7P58*1Ll7uUl(3M`I6cS~`$0#t&15C(_#nJ<&ZYIW| z>fKt}eV)CAT=+PXj>le5gGORw= zIG^LWYh|?&g7PiQim&RMXI|x%_T%$;KreMSIVTO>_=2b6d+`+*5fqpz(Yj-}Xu>+| zOfWiJ^=TV()3COiTm7e2N^AX0-CyPXI7V7Tqe{on4f>pst&yf`{PkPfmG3r{(545Y zGD?+~$_-RYy6WI?=$3UTUg_48wbdTVRrQFxeW&M8Xc)l1hoZInzK5bNE8N#(1skTN z9+)rCogmdb1T)5QArL>Xq#jZg$KFtC%A}k0!4CD3%%11AxgmaOs_zo(dT+&q*WpQ> zla`*kaOLJI_#shWapKk9b=P$NN>_Qsb!ZsMVl#BBwD`2?-)fS!DJVS}PE)17t+LzT z^ETnaW@~S|3VFF|H*FKv^qe#h=)#8Xd&M#NUl)3J5e1!#ez}}XTl zaKhEQaSFhl3zao0*N%~Geh7XStvc>5?^mNoDvi|mz!jLV>2~yPO4A*l7pxm1C;U!< za~+W&)g-K1RDL{LLx`K6QT;Jtv2BLzoDyTwZBXTbT-T-&+dZzcAavt3&$Vsc6ZwkE zJxx74fBqWo*;ZQHWu@L)dB1->u3AzkGN$zv0%Mv}7AkmrFEDZM%G&sB#grTL#4)_= zCcQ89@~SZPMi_64_Qv_VY4`?^hK$vK(u3~!fXUkK&B@Odd-)N~<@ttZuyfm%Z))HY zZ5kcwSwODsGML-PW)Y0um(pkz<`62~eu#IuG(4HSYRrp-qtkb%Rn!sr_aM~!a+t|e1iC6uz zbfeT?di%CnF{r6M(pI5629+%tiro1l=MWPTwyX)_!rwu+o z72AT{|K9hWH*>0Pej&~Y)VMiq^Yw1t#@_kQ2z72mujQLB=Tx4-V-!E{N!FtOr!%12 zqjB3{a7Ah^66W?Ie7bSfMuf|<{8>M&?xDswwFS*71gO>MYx8aQP!|TxA2(y6_}y{V zIVS(QXkz;_+e&Kt|M>T{GCP!}%>(N~YCb)i-r?r=YH@G>{4FSBY5vd;RUSt5+w<}B zg1(XP*3)%QmB-v3pYDCB@^UoozoXX%pC3anX8wB3wtwa;uYLR+!km=WUGv)J`PYhh zQ@QyFWp2ChmVEOgG~34TE_tJ-?lE#^x9r&52mg0kfxC;BLZ@2<>&({LpSK^lT5Yzy zZvrTxKK%1~ao8`ryRRJG#b(`0E!%|Vvy`@D%ctK$8~aIAe@}h1wZC;}YHMAN zMH9Rb+MI}9Unw~jA zliJ6u$0-P9PkmgPrZ)NO5VXMqm!1<*d%PXO-1eb~IiD(LE+u0wMfd*$5GN5(@-Z&8 P00000NkvXXu0mjf Result { let casbin = crate::shared::rbac::layer().await?; Ok(router + // Stamp a long-lived Cache-Control on /static/* responses only. + .layer(axum::middleware::from_fn( + crate::shared::cache::static_cache, + )) .layer(casbin) .layer(axum::middleware::from_fn_with_state( ctx.clone(), diff --git a/src/shared/cache.rs b/src/shared/cache.rs new file mode 100644 index 0000000..fa8266d --- /dev/null +++ b/src/shared/cache.rs @@ -0,0 +1,32 @@ +//! Long-lived `Cache-Control` for static assets. +//! +//! Loco's static-file middleware serves `/static/*` without any caching headers, +//! so browsers refetch the CSS/JS/images on every visit (Lighthouse: "Use +//! efficient cache lifetimes"). This middleware stamps a one-year immutable +//! cache on those responses only — dynamic HTML pages are left untouched so +//! carts, CSRF tokens and prices stay fresh. +//! +//! Safe because the cache-busting lives in the URL: `app.css?v=…` is query- +//! versioned and the vendored libs (`htmx-1.9.12`, `alpinejs-3.14.9`) carry +//! their version in the filename. The handful of unversioned images under +//! `/static/img` (logo, store hero) must be renamed (or query-busted) to force +//! a refresh. + +use axum::{ + extract::Request, + http::header::{HeaderValue, CACHE_CONTROL}, + middleware::Next, + response::Response, +}; + +const ONE_YEAR_IMMUTABLE: HeaderValue = + HeaderValue::from_static("public, max-age=31536000, immutable"); + +pub async fn static_cache(req: Request, next: Next) -> Response { + let is_static = req.uri().path().starts_with("/static/"); + let mut res = next.run(req).await; + if is_static { + res.headers_mut().insert(CACHE_CONTROL, ONE_YEAR_IMMUTABLE); + } + res +} diff --git a/src/shared/mod.rs b/src/shared/mod.rs index 3d35aeb..9b4c5ee 100644 --- a/src/shared/mod.rs +++ b/src/shared/mod.rs @@ -1,5 +1,6 @@ //! Cross-cutting helpers used across feature slices. +pub mod cache; pub mod csrf; pub mod currency; pub mod guard;