tM+wHmxCY7(uY=++*z+k5&pnHxLA
zj0C%qG|Xbgx%U&-icBt!;G^vI+_YJ$64KR=)GB9mGA7kt&&?fhl_{ip0`u4+n&HOi
z;%=m?_vS9$>CkkpLhH5J-Dx^3o+>`G|D3$kgu;ypYwJRn9i01GY;5eeqjjmrqGhC|
zcNX#rVAx;p^1l2!HY1msATS+K!CCd7hgex3{>+UBvE}Cqd|mz-LSb7)aU;n0RsY~(
zu*JK)RF@NciS~Z+dE4FPXHuQ1F^3pxeu{TW;FB;t8v!g7>?uPDk8{YrUuYiCzJTC?-!ps1zJ+b*a*B
zEm?0RtSsF2?cyZd3cdf3!ZA
z#CjsOH^73QOU8S{lE%8lt(HF&VnTT{hQLUX8QpX_*w;;Q<~aY%et+4+VS&um&%9Mo
zM>aoQT>FH+-z&c!J&NRO8=1KGlPkm?6DG?Cu=Nc~1--;+f4H8TAF8YHO)VByJ!m9&
zSeHVK7u^?4X)h2QInGP3u(`1~YF}F)~3{W@etd1TyHT8StQqytXAps{7HWAhM%$}4$M_RowUMFd6{n58}
zXGa(NHW~lT>5B~L71JoI)*SfsAbR?67!}X(H(F#RKT)K@7F0cL9B1<>4WcNi_USV0
z0MUe27m0>rl=us&%Q&3!ww7G)`JXb^x^&^8=k9qtMj|3o5I#8xuCK3GC5-T4>yf>KLmZcGNCcNS4{f$YLhD`8xJmmUXIc~*ZF#1m
zq5^&vz^4j9)4AB7W=ClDmaz)QW968>c-H(jN@c^yrj^@`|Nb4c`|(Q2J4PXkl2mqe
zs_b>Li$wiN4xWMQY(b?r3&u#K(DIXJW57AEtkE@H9x&~9BS__Fn$!UUtkGvyQgw&F0;eS>bV?ORU%
zNX4dpnXe;jB1w6Urcy&e!ygnyC0Yv}+ALo^YkG+Er?oIzsr+v^2;Nw;jXOfRSD*Sf-QGzOs_@qRV85AEXCO2LicVC
z%j&BCFsDn+%CsK)^GD77!n4CoCExB{P?oI6{M~zP7Pspb##@ejym4I@dHzz^Dz$ed
z@9thn!a1he$Ir~oMKH{XQMYMbkLje&nH#c{;#IRU=c*ui#T>(u#UfHz!@ZGZ}MzF<2
zf@(oNJ`=`aWO!Ek$u=)qSy)+lUA^z-A~$x0+rw{pVIj48pZ*luPJ>T8MCsbX$QY*;tsfZaBPwyDb(7ZcG
zuTbPhJM0R>uKaE!Du8wlQa*;5waWDo)RiF)xk{id7vsQJM<{p73hkV^+*Z-#>z!
z6iH0}iqTnICASWa^O|RVo)&_Iht`Z&kvAK!VIS^v%4%`AGu9Uc5!|
zJuT@%;@%;cS6^2qf~TGcA7er+B8!UWx^VB&H>*F+e6&~rM7Q_lIrM5Pjmn(sP|9i5
zNXLk$SV~0oQAF$%rK0xTpTVqnqWXSJrnG~d3ew_h5hZH$9iQKia^ntSKZ@L4t>dLc
zN-hQ%Ndi}W$9J)AnJsaP;_=Y`MNqdo#lnC7{CSm7Znf)GJ$*%k7>?@CbxrBEob$I}
zYra?3xx17kM{|Zkd>RR{
z3)EImZIOMQz2m&fJuEj9rEC8?&PQtlyqsL|V1FBFQ@wIM+HcD`rIfT`U}`
z`5Uw4(Wd85G|nPfv$B5m%?oO)vUGq_sG%%HbJ9jx9z9FKu-SzOj6bZDCg>{+K|T0q
zqVoHXc)opM=E%?@#8nL{Vef8f;tJW)Xze;N+!1DA$&9>ew&X`%_fxAiWeZvwBJU0@
zagi6fs?69C#~6;hi~}gXnL^&=I%zh)_d6xUe-mXUMK!UyGiz)5&850)t3A^fmq{q!
z#o0OIU+=npTgkOsJei0E;Ggvb?vp<|l8k-Mg%dFO8>n@BSr8@G1pTRm3#yT2W`dMR
zAkZS)OgoQ)kyv;T;`Rxl6RY34VV#2cCOkKv5#Yo
zqW&bzME_48sLd&GBHw8^QL9o3R=#+M3$LTg6>iEB2!L$c(&)Jfx^gb|n&oMdk~ZJ;
z>dp0QAM0ju=A4|Irh=EL<+x$*@bU&!Jci2cYSPlx^-@gZ1n*G!pj+Mi44I=Um|qwA_k@6zgY?;s)Oqezrw
z1f+jejwV~1ZZ7>9zM>J6nFGa2igk0%*^5l7InOhD
zQkz9Jk}r~PUy1>hu8e{~9&4B6X>ODV#nLjEq_s7lC3tOfXR|EI(waCp3@E=v)Pd3a
z4@3KnN1nec11#PVhBVnE!I=Df>al_hH{wto|H*2Z*8p@>N&Bic%jek8
z>&!uE-a8weVGN3vRIOry8`7EpXCOvxW#7UC)p9*XjsXBapca444h%dSe5U1wjL+6x
z%eW0gC^Qe=K9r(-_j8k+zaJa0)B7KuQL8Y~|AjyB4J8{l>?KT9UjV!UGV{MGI#guUuGI80B36oB#rR{aPs&Gc22{OwNZLk!gLm@|ILt_Vqyi
z(7SJMUh34&mO0<$X2Nu}>qpK%y|K-!dI_yT^Rt+D^plAAMVIP#RxR8xrsd6E%F||X
zVeOabk8AiEk-vw#6?vXKHXjUyBB^V
z8o7%cN;Y6=oO2%bVw!Ag)cVj(l+Qg+wXi?;H_0Ti_llA>3v9s}zrN$mYiCaAS>4-6
zO<-TBQOkZH$9=iLH=M3sIGcSpPtc)1J6E8xvNCP#(d#)Gieu;LF1PtwehaoM5>BIY
zl%d2MWXEsMN7g*A8Z~ViEm@pcPEhOKdg;6kVKjHjFvps`vZf}w!ePj>|FLVVa85nT
z=rk=V(z>{{zWP13RM36@j-%8uca?wbrdRp(183c)XQhtn5jL{1?<3Fx9qWlA5&9a!
z@=$rf&%wdw+1Ucu*RCc46=P;iZ)rq)(hX?V`@D1Xs*>W
zUW7inASs=00Z73GNR>-y*>c6K_brq#kzO1(VeLDi$27quMZ)+k
z@7S=qgDkXIe2BNP5@CJvrk=CzsW{CnZ!P+RN-yS4aReZ9hF*DDwxEMePV+?#x9c59
zh1Hl-T2A&p@tK;$jS2Y)F3QKZOik5*acSlh+Cc3|PEOA6*RLRZrs}f=J#=zfjJ2z_
zBdGn&+y>L`?_GGzVQ(#RHkanF8=ZrkuBWHz&?`fUk^S*nF1DFm?naVhx*Ey%(8s4{
zdczMiBS;?oi|rz2>zq$VH-;Z58O2nuhlbA|ij}XwFBU06??vVaO<#~fJF|<6i)K~F
z(scf+K3+cIe5zw}bDHbdw9}1e9bQUCjB}AUROV-60VF@H6kFriZfa5?YDo4u)a_gkTRG-^)qBU#UEk6rv9vhr+D_exf+_vURM?mrFN7mpumUzaQE>k*tT3DarQ0!PHDY&^T=;N
z4qQ0jAG>zNL4~QAUPR^iOnr_0J+5}Eo{05HaaCaf8|8ry1k~X^UpHGpx~OrdHN8$U
zBK*~Zqv6VJrfaVU5+oejNbbHH3m-xRG-53EXBoaM|NL`oNmn}9?kH`}Z%cGPKGXeI
zS>^H(<~;kg3px2xaq9V=@3wh0bncYfyX~j2yS=Q74-E|~nnv&Gk$`WXc=V=k%tYnI
z;8(A$ALs@jqr~VTinuj^R-azUJ`BmU&QGEXNzY!tAm5nUp=?TxugxRty&rM0CiLVo
zR_%L3o*1|*pL2AGPxbcLL}$62f2$5g7o$09g6nQ
zaftyLaknr`=j)aiA5R^h86ST$cPjDIX90YuHDX}q11
za9@MWJ+R}*HPFz|U_|2VqQJKXw}@%n*|A!%%FV=hk7%Gt!oK+0fsKL&_W82Ku~xas?Q|zjih=o-dsBbQ&-on
z?X>pMt@^E7^E%rgCBD}3__6in0n=vDM}K}L+J%i8rt=RR?;a>Gkd~HatGW58tRHaG
z4VK2?jGR)zaBsuvhqdo>hK6EY!rpGbpO^6?*>>B=L?6*X0%}Q_5sGGaoHxxr&3IJ>
z7v=+^1Yu-pV}lhbA~E&!uT>)P0ta1cX2k)V6o`G`LUz?j}P2nk75v)xiU!QZ-KhNK6ec%jA6Fj_s
zb7?&6uF={rZyUBC{n(S$y_Xc3MC@APpUx9xY`v1RZ_*4)31el4xL(^G@eJuRD);*8
zEdyb6Jo<=0(s!PLn^Yciu0{H1EmI28`JeTd-Xla4fjvpo{ohL$AKuGa~*?w0ZIIh}PO
zN$oFdqm7T5Ete!RBAEzMb%BKt(4T=ro8B_>yXGnCry#3(hL4dqMi~o+Xu7NBRfpb+
zYjoW&I?Spx^6e*ChL}>Lv*P2n?Z}B=#-z?4i{13Q0HAbq(U9=uzEsj$t~^kUyl%Cx
z_a!uR&N9C7)L^1~=y8uL_?7z7xFGZ@2(oq
zP7*djM~2g_U;XQ3)y#A1<}VVZfJp2S3}8(SR**pP1%1m
zQa|<5prUnEJI1nz0h9=%0{th{U-I(uK5#Yz#cFH}1l12B$e3x)*k@mZ7RbyZUvy2o
z!~n%4B|h}h{EvWCA61zWxZu<0duz=mu@J3jIEY7(?9`{SnwM_@BIj#&w#ymb8BljU~Fq6Vy&T|XVOvF&d-|e0AAG-kDrzz0j+V6
z@-xn%^%XL}0A?+AA`N9B?xg~C0B=A-{){DIHM(X#e)5o>Th+oDOb9<5q{`Lk`tq>y
zr%y_|(cIiItYWR6i@m-5-T+P?|K4Si@9O1s*1*6(+4kBM3FEB8+rEeGe2Br0o^+`W
zXLed*h4qR4C(|>>EUQK6-H%KH2AU(8017%637r?n$-qP{)20&lHmT;+`j8*4WoY%M
zjnCY>Up%Wx3>!v(jUGL!+^9FFtMa(1xP(I{rQOS}HSs^ey6%~rq}Q?3
z(4{)Nq`DA-^{N)c=Fv2tosGr(E@i2q6e9Mkvkb?Putxg_@`3g#?iHy&-ga#-XbiQ#
zZW(FsKZCC|)~|YzVRy^yscmk*ZRYqlT4I;(qvRCV7DyRM(KgPh*N56eTAm14QOFMc
z_&p$ZmQ6E~PcyO8FV}gln$cet*3;b5mBBN>qHlWldwD^bOsAV<5;w`r{F|U@)1N&W`D{HXtn|S0KLlLi7d+rrt&+^efVE%al)tDIaSy>c6rUjWc{?@adn-uBm?IYYK_Qv=XaH-(6kpqF-WC9X5x0
zsPMe%CW+UWD5KI+I+I!)87$ZU-(Z8Mx7FQUPvc)=hM}EVLz2#r+G*BOVX96QY$-EI
zkF-3x->v=A@Ab>Um@DmHQHMl9|BiA+;t+~0lR
z+7$r*?f7NQPdlEYlstmA^pUz(M~VO~Rvs^(8IBK4?;uj%iCZfxJ>LFeUR}n0lm#x;
zd+;V#=DC|?an>iejj{NM`Gm7PgDgU`h1?vZFEHj{n>P)7s)=42Qm=Y84fHIPHgWU{
z=c={)sPi;gls?yxT|GTLzY{6nB<@E(zOq7`EFYM2cH`D+4TV6mb!0NhI433Odh7CL
z>AY7%(6A5v1Qu_DORZJE{Qk4@DOY|-kUm>OGD&Yd1k84+%25J+V<|O%IK-*
z&+RDGxcx1rvu;i)J5$fj_LFmi4<-IeVP=D13mfyQdGp7#Qo(|wyE>0b6Iv*OWm2r%
zF!gHrRK>>sNoNwH=2b%uLi)>X5aLY3$s+?o7qXxpy8K=`CIiq@xm0rSVdcN~e8Tpz
zo{q(}DC}?0^1?Sfl4M0&nHPF17|$KvPt>U2*IXI}3ZBF$w*{M&vD8{4@B(6h+!RCB
zDJKCm?8@i04T>44weA-f))q%ql!p$7jrMK6B-0{LGNoTy9@W9b>_PGxFRRw~qqj@U
z=ZLrEn6yr=&|~Tq!W#Xx2bf)wNRaewT60GmZ(ICGb}CepN#188p*}GX4e@@Z_Wfpq
zNEE1zf9!)Ux(L=LXlC`;ZeA!@Zd#1wvzYyivCa8>JQNi-#X{rGl1HNzKrS6I$L(j3BEU)-7dl(*ie+XctKy|G|QOObgM1^E0_lYLk#GcXbC^NKc@JWE2B*a!=Fp9L}^Bu#N}u09v7M
zsxNs^kCW$FtM~jDREHfO96;9V(y$KI)t3Qv9Be9?`%Ry@Upk>x8Jd-VI`%dEV;4!C
zzC$ihtLV3#+@Tq}$u+7=#U08EKQAKrffA8?uD5hx*CV6zDj?i9f1MX?&+4l*Wi-01
za0@UHuy-sUdtKex)>M!OaK=_VV-!cx+oJ>9PjM_ZE56e0Pu#w$;CWl|m+Aa~QqE3%
zddc?!N7@*TW&B?S+vifQ5*{G&u13z*B}--B*nk(*W1R
z$J0^V$lMX6WEQ3BMrRN`pbhY!TW3LWkoZ7?J)AG%j^&BW&ij0-ksfc4eLVH@D_ph2
zw~6kJuk$;7qyKzbE)rNkqqV-fRhL_8j5~?D)x9pgeQtGt4$}D}!@|No9GL}73}C4z
zLe%8_frU%|=_~12*KAN=wzlGRc38G&D~2>lWraSb;J%?H1~->SeIXb}#W
zUSAn?45I6kVjaGzy38_R`kGzo#V4nHZw)%%f@ID#<78z1`0i8F#vm42fHb~4
zUkcIwD-~B)M@HnJ3jkf?k&%(t=0}1??2qq7CAn;Jz}kLT~tJQ-FJ%M3l^d;Ij!k*246mhX<=#%g%#MZul|AXi~PK)rj
z0g(@s?~bR}@6Lu*ftRXgYp7kjbgtZ2W&-Zoc=;&)&Ze)$k9Jzraoew`&R*Drsar+j
z#{24Og6q<$Hgc`ccF$zZBIM{cE@j7AUT$U@3Ryip!B76@K~v`gc372Qk@VL;;zY~v
zY~(hR>zpX}5x=X&21YRerd$4uo`7C8G;ZO1rxFLJB@Vod$`!0a$X-u4+KG|5D-)^j
z%3y>Z7GlvWXt+Df<>0#SioRI&)Rg9B$yC9q<<&k{eS?ACxpAk%3_2ZMUfT;{}s6Pl*^yhr;mx}LV?Fd?O-$#
z>5;US`4j1OFexbK=6)wem8o^3S36tJcbDBz5G-k)G$N_`kk0x89OW>j!s_Uin`m*sCEL!4X>&
zlKv(8gyv%zcns-w$h%M8>xk#T|kv)`EOm#X*623Uhx()5=Lnrw~(*Q82YA%-aptdmKZ
z)^5fbT%4S*?FdA_%VRR3y>5E0Hfpz)R#y1H1?1=%`fUoyQX%|x2>qc?gy(8qG
z{fv*#^}0QjLhllPqK$%Ffkz%5|5O#a*U6vj&HJKBgI+k`WM$Zpt-)TKjX*471AVuR
z%W>Qrff*2?dMPNkkYL(Va9txd!woE@%NS_m!ltfDz@R$a|0yO9W1rrWQRle~9Qdje
zn$gpX{@Zb5s5N8^c6PFAXa}ZFJfHd68fD&(S6-5~dhL+SO`B+j_Z{~Az-A*UD5?-U1P!?OpBiA$Ev|Zdd|M&<-_BHttf&>=(cK$vv1t)NtgG>vgWqN?Bw@?CNJYa&
zlJafZC~s@YLykT^#OZb&OtAsjd<+Hm#3fttWr1;pB-p8c{)9&FM(`rZ{CUP$YqSl;
z94ZFvbr^v^Vt@uLv|qk}6X|kp#z&DA`>KvSl={movyjPVg)+nY#8C?dnF}EIaoj{fXfie@bkodQ^3CPDZf^xwGnvB58
z!Tc1peP!ehXk-m=d7545v=W6WC}3BDnPv~wJirH6Y2$c?glmI*K@AOww?@j;_SYi!
zhFZZ$Jp|PXfRJ`wmbV}|%!3{2O83>qNqt;2?#Fx`Bx^27
zOg$GgOM!B(@r;y0^ntUAmBhJR**sy1n3$ovSVQu5KX!bFDJTs*DGt{H3#ZE0S8Gmj
ztW6V+^nC-NoOe}QbOr1VpwgsOADvA5+wRg`947XYam(U}Qm*4NWieaFb+8CNczfFf
zA6Rv0H+xX-(>w@SO&D3lvFA9_Ov+k+^Ug-
zjEkv?(cVmx%L;A4m$u%5KNWz=pa%{K(Xy7y?}NW7
z79V+un^Va51m&xozjh7KU0fxof?!Ia@Xp=4KjDL*JMv3%ZuKH$-b`zkI+pv2v?A9`
zdIi@S!o1CPdKYC-$ch;~zdS1%lCN)i&INuaFXC6+5nnbc#nHps4-nSKSp#yx;zYVt3m6@j(K}?VZws^yH2czkoB27tLsEQ|-||GirKf
zO3XBdS{csDVH>LqLdBpIt_l%uP}SG+sj|7!2effDr7n}tV(YK#+U~o14y{=l{ay{f
zqma7Xbl#tvI|TOxEr4Ue3zvV1lWiLuUW{Ba;PAQtrNDiDuyb?NTwu<}37Yyxjbfm~
zmJb*1yg#I+<>G|2_6y`9yS1cW;<8wVm=ah3*k3u*6pU8@64p2;_+L&Q`RP
zj)j=d1Xz5T!neQD%+hUf6jde%pFkW}^>Xjtec~e^l=4v3D28M0$xElj9w%C)qV+;B
zA=ASC&}BjD9k36rZI9l~&%4Bl8Kwocz|mAwr09b5lG*JQexg7NrJ7KVQ}LpY*W<^X1xGPlq*
zseK|!nuj<2?t`!TQKU{4jwbbtJ%&cjLCqM^e}+@yQ=Wbluj(bTGclCsCB;
z*Nb(MMqrcK(Fs7Q_}oDLD5YIDCnp$-)dwEKNri5#Efb2`1S$|&^m5`7AzgZ0feLIr
zr3;Lw!_q?tBoI#k?46M4G*EgY)*qq7fw5W;CwBGaML0s_fG6o|N1ZU*a;Ah!(M|Gn}
z!AgiNq&WxmP}J)`BRS;)Hw8x)I9$cB^>}aymVWPkSUKj641?|bBv~bP4?G9EZgiMI
zW>?M+*MjYn7P3M~RIu(Deu+Ry?YAydVC(DC`ER({IlB8vg<*-Vj~)RTZ7!bY%A#%f
zr;OAd0CsyQUHz?kt|rbe+4Jisxw1Ak7Bh6BMP4$aHgII}d+Eelehm~5xv9;PxcNLI
z=axjE*$I^R^bc#nqvQ)8{YwCSgBP=yewj3(f+RCK@4t4rKJ6kk{#uqT%bFbwNw9^(
zMCOZVFvH(U1$D|fWyc7rk7wxds(o9*ch2t}kbMFXW$R}HS`p`SBfA54L@oy2fnxf5
z_aYCv-%&uL745MPBLXe&_l!=(z>73=h^-5UF|voECn!R^37l7H@%AY=zaAo`A#W%z
zoOS};kIKyXu2Er`N+0CpI{cS#44tkd
z!L6?6$Q%!V;!T)2EvDn~xg1H#;N4eG;7eGIhG0guL+5$Wgz_d=ooJl8;ffU#7S97y
z?7M!Er(VXa&bONw@KTG^RP@?9*Jb3w)(OORSB(7(beUPGwoA|5o=($@A}_LcS0|@Z
zW9mh6-Kj&oFN?ln!t*$-2%8=Z;t3)^JIW2ol_d9nQAV~ZS_75WFv
zi2kjiV3a*0i-iUX`Y_2Q3?x5)LxK%cZ=PF06Uu~51${$L-5A%~ZM8*(>XvSy$oU=NL#VZBTV}S2I`|T(;c!4Qxxq=l_FRBm-stlCm
zvh-sr78vfyomnMR+|j{<*P(FAhLS0PVei}|gd_8uG~}C`|4CKRKK;_6wrb?TacB}
z&$3%W{X&x>nVvsO0K|$XOxaV5aU&0YTK}?{+1Xr7{sHvu&|mwo{+1Vvr9`l$?Z(<2
zJG>X@9H3E7K5q17;z1t5s*xXhsY1MqsKM1#>i-jyk16iGbeOV%ff6O!nZPoh4RWxz
z2Vg@KKDMAHvDy?znq)%Uyf_(-AkL%z0Zk?U`nbdkV-1{?Xa`)3a>9oOn*K#1@0~T4
zKghs$N-RpiVL>)Z9G^eias&EdU_>i5uE*{?o9WP7?HhUyo}`8RzjZ!ob)+-tc>NYC
zD?uIJCLvj4&wJq@^acQ`U*%}B+T332rD0J@+a9Or0azNfi&9e$^K*m~Hn>F@F3;u;SnpkTksWC`pN{EGh
zi)|zYq)#~T@;~t~&{uLF2A%#|s@@`fLJn^X5N7o=yUtWo4+q)@;xc@*WfA@$=x<;_FV|MBuo;Gq*@P^Q
zWEP>4zY@c%
zY(Z5C6v50$i5A9aczMJUpDz$R3agq`AF1^
z0Tm#rH-guQ8va@8+qQdmO4R$0KfS$aZNgOyZ^6KJB%b5oqaVJtHnC|$eC~V#$%cXQ
z26D)rI371)OVHnY!_3@~Xn*wgX4)YPx*4u~;sjT~w#6{Ka_|zQGI$9sKC_c3W+)hO
z2htxjnyr3pANT$H<{UOX*KN3eeL2x@6DbUd)2jwSKJ;fo>t#
zV)&CLg9(|lbUlFprV4O1xt2V^%Udndu{%IheekDz6C)HsbG~E#vR0ruLOK|Kc!qcA
zEG4lh%5%4?#&2rFLKKUh^<23MFWlkCJ6)kyCQSlUVM9S5REBWEU50%@w>-qcq*FuJ@1fDOBCS?Puh4&EVe+5*Fvt0R0
z6-`Y|epB|8j`^9^u*o!+r>)=KJZ=&W@V?uK;!{WqY0g
z0&wH*2aR8SiaB*q@l7VmYFDhdCSyG`4d(`}0yN(FBV+|~BAAEvEEW88UzZavxvoTJ
z=G-zfJBypgh+r_Nt=}xFTaT)_^lkxSIH=TV|8IM`%)4g@Wx47-hE6+p_CSrjG{ofx
zZdiCD@#WWmgShu{2rp5k4NUb$%t?!SXOpay%p67S*VUKm`FsXkS4v;ZKz^j{#>JsI1yDj+F4OC4TpbISAxGTx<4ILN7W|!ttI!kvX-u
z5<;3C#2JY=b;U8SKzMJYBglBa9dpnKWAE7uA8CGYBSh!I>hH7NjgkLg0igr0`*BJ6
z{9br$0mFJcQ^Yd~!hYW28S;pSv&6KUOGtN`RV|_xkngI6>*3WQX?6
zONuVXm(C
\ No newline at end of file
diff --git a/examples/tutorials/vapi-voice-call/src/App.tsx b/examples/tutorials/vapi-voice-call/src/App.tsx
new file mode 100644
index 0000000..7725935
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/App.tsx
@@ -0,0 +1,22 @@
+import { Toaster } from '@/components/ui/toaster';
+import { TooltipProvider } from '@/components/ui/tooltip';
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import Index from './pages/Index';
+
+const queryClient = new QueryClient();
+
+const App = () => (
+
+
+
+
+
+ } />
+
+
+
+
+);
+
+export default App;
diff --git a/examples/tutorials/vapi-voice-call/src/assets/react.svg b/examples/tutorials/vapi-voice-call/src/assets/react.svg
new file mode 100644
index 0000000..6c87de9
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/assets/react.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/tutorials/vapi-voice-call/src/components/AboutSection.tsx b/examples/tutorials/vapi-voice-call/src/components/AboutSection.tsx
new file mode 100644
index 0000000..55ca859
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/AboutSection.tsx
@@ -0,0 +1,46 @@
+import { CheckCircle } from 'lucide-react';
+
+const AboutSection = () => {
+ return (
+
+
+
+
+
About YellowFin Real Estate
+
+ With over 15 years of experience in the Austin real estate market, YellowFin Real Estate
+ specializes in helping buyers navigate the competitive Austin housing market with confidence.
+
+
+
+
+ Local Austin market expertise since 2008
+
+
+
+ Buyer-focused approach and personalized service
+
+
+
+ Comprehensive neighborhood knowledge
+
+
+
+ 500+ successful home purchases facilitated
+
+
+
+
+

+
+
+
+
+ );
+};
+
+export default AboutSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/ContactSection.tsx b/examples/tutorials/vapi-voice-call/src/components/ContactSection.tsx
new file mode 100644
index 0000000..dce5ddd
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ContactSection.tsx
@@ -0,0 +1,155 @@
+import { Button } from '@/components/ui/button';
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import { Input } from '@/components/ui/input';
+import { Label } from '@/components/ui/label';
+import { Textarea } from '@/components/ui/textarea';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
+import { Phone, Mail, MapPin, ArrowRight } from 'lucide-react';
+import { useContactForm } from '@/hooks/useContactForm';
+
+const ContactSection = () => {
+ const { formData, handleInputChange, handleSubmit } = useContactForm();
+
+ return (
+
+
+
+ {/* Contact Information */}
+
+
Get Started Today
+
+ Ready to find your dream home in Austin? Contact YellowFin Real Estate and let our
+ experienced team guide you through the process.
+
+
+
+
+
+
+
Phone
+
(512) 555-0123
+
+
+
+
+
+
+
+
+
Email
+
info@yellowfinrealty.com
+
+
+
+
+
+
+
+
+
Office
+
+ 2901 Capital of Texas Hwy
+ Austin, TX 78746
+
+
+
+
+
+
+ {/* Lead Capture Form */}
+
+
+
+ Start Your Home Search
+
+ Fill out the form below and we'll contact you within 24 hours to discuss your needs.
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ContactSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/FeaturedListingsSection.tsx b/examples/tutorials/vapi-voice-call/src/components/FeaturedListingsSection.tsx
new file mode 100644
index 0000000..522e47a
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/FeaturedListingsSection.tsx
@@ -0,0 +1,58 @@
+import { Button } from '@/components/ui/button';
+import { Card, CardContent } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { ArrowRight } from 'lucide-react';
+import { featuredListings } from '@/data/listings';
+
+const FeaturedListingsSection = () => {
+ return (
+
+
+
+
Featured Listings
+
+ Discover some of Austin's finest available properties
+
+
+
+
+ {featuredListings.map((listing) => (
+
+
+

+
+ {listing.neighborhood}
+
+
+
+ {listing.price}
+ {listing.address}
+
+ {listing.beds} beds
+ {listing.baths} baths
+ {listing.sqft} sqft
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+};
+
+export default FeaturedListingsSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/Footer.tsx b/examples/tutorials/vapi-voice-call/src/components/Footer.tsx
new file mode 100644
index 0000000..4b89f10
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/Footer.tsx
@@ -0,0 +1,18 @@
+const Footer = () => {
+ return (
+
+ );
+};
+
+export default Footer;
diff --git a/examples/tutorials/vapi-voice-call/src/components/HeroSection.tsx b/examples/tutorials/vapi-voice-call/src/components/HeroSection.tsx
new file mode 100644
index 0000000..036f350
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/HeroSection.tsx
@@ -0,0 +1,37 @@
+import { Button } from '@/components/ui/button';
+import { Phone, ArrowRight } from 'lucide-react';
+
+const HeroSection = () => {
+ return (
+
+
+
+
+
+
+ Find Your Perfect Home in Austin
+
+
+ YellowFin Real Estate - Your trusted partner in Austin home buying
+
+
+
+
+
+
+
+ );
+};
+
+export default HeroSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/NeighborhoodsSection.tsx b/examples/tutorials/vapi-voice-call/src/components/NeighborhoodsSection.tsx
new file mode 100644
index 0000000..9adf1fd
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/NeighborhoodsSection.tsx
@@ -0,0 +1,45 @@
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import { Badge } from '@/components/ui/badge';
+import { neighborhoods } from '@/data/neighborhoods';
+
+const NeighborhoodsSection = () => {
+ return (
+
+
+
+
Austin Neighborhoods
+
+ Explore the diverse communities that make Austin unique
+
+
+
+
+ {neighborhoods.map((neighborhood, index) => (
+
+
+
+
+
+ {neighborhood.name}
+
+
+
+ {neighborhood.description}
+
+
+ {neighborhood.highlights.map((highlight, i) => (
+
+ {highlight}
+
+ ))}
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default NeighborhoodsSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/ServicesSection.tsx b/examples/tutorials/vapi-voice-call/src/components/ServicesSection.tsx
new file mode 100644
index 0000000..14681dc
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ServicesSection.tsx
@@ -0,0 +1,55 @@
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
+import { Home, Search, Users } from 'lucide-react';
+
+const ServicesSection = () => {
+ const services = [
+ {
+ icon: ,
+ title: "Home Buying Assistance",
+ description: "From pre-approval to closing, we guide you through every step of the home buying process."
+ },
+ {
+ icon: ,
+ title: "Property Search",
+ description: "Access to exclusive listings and off-market properties that match your specific criteria."
+ },
+ {
+ icon: ,
+ title: "Neighborhood Guidance",
+ description: "Expert insights into Austin neighborhoods, schools, amenities, and future development plans."
+ }
+ ];
+
+ return (
+
+
+
+
Our Services
+
+ Comprehensive real estate services tailored to Austin home buyers
+
+
+
+
+ {services.map((service, index) => (
+
+
+
+ {service.icon}
+
+ {service.title}
+
+
+
+ {service.description}
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default ServicesSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/TestimonialsSection.tsx b/examples/tutorials/vapi-voice-call/src/components/TestimonialsSection.tsx
new file mode 100644
index 0000000..fc7a62d
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/TestimonialsSection.tsx
@@ -0,0 +1,37 @@
+import { Card, CardContent } from '@/components/ui/card';
+import { Star } from 'lucide-react';
+import { testimonials } from '@/data/testimonials';
+
+const TestimonialsSection = () => {
+ return (
+
+
+
+
What Our Clients Say
+
+ Real experiences from satisfied Austin homeowners
+
+
+
+
+ {testimonials.map((testimonial, index) => (
+
+
+
+ {[...Array(testimonial.rating)].map((_, i) => (
+
+ ))}
+
+ "{testimonial.text}"
+ {testimonial.name}
+ {testimonial.location}
+
+
+ ))}
+
+
+
+ );
+};
+
+export default TestimonialsSection;
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/accordion.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/accordion.tsx
new file mode 100644
index 0000000..e1797c9
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/accordion.tsx
@@ -0,0 +1,55 @@
+import * as React from "react"
+import * as AccordionPrimitive from "@radix-ui/react-accordion"
+import { ChevronDown } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Accordion = AccordionPrimitive.Root
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AccordionItem.displayName = "AccordionItem"
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+))
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+))
+AccordionContent.displayName = AccordionPrimitive.Content.displayName
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/alert-dialog.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..fa2b442
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,139 @@
+import * as React from "react"
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
+
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+const AlertDialog = AlertDialogPrimitive.Root
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+))
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+
+const AlertDialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogHeader.displayName = "AlertDialogHeader"
+
+const AlertDialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogFooter.displayName = "AlertDialogFooter"
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogDescription.displayName =
+ AlertDialogPrimitive.Description.displayName
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel,
+}
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/avatar.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/avatar.tsx
new file mode 100644
index 0000000..991f56e
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/avatar.tsx
@@ -0,0 +1,48 @@
+import * as React from "react"
+import * as AvatarPrimitive from "@radix-ui/react-avatar"
+
+import { cn } from "@/lib/utils"
+
+const Avatar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+Avatar.displayName = AvatarPrimitive.Root.displayName
+
+const AvatarImage = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AvatarImage.displayName = AvatarPrimitive.Image.displayName
+
+const AvatarFallback = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/badge.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/badge.tsx
new file mode 100644
index 0000000..e87d62b
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/breadcrumb.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/breadcrumb.tsx
new file mode 100644
index 0000000..60e6c96
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/breadcrumb.tsx
@@ -0,0 +1,115 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { ChevronRight, MoreHorizontal } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const Breadcrumb = React.forwardRef<
+ HTMLElement,
+ React.ComponentPropsWithoutRef<"nav"> & {
+ separator?: React.ReactNode
+ }
+>(({ ...props }, ref) => )
+Breadcrumb.displayName = "Breadcrumb"
+
+const BreadcrumbList = React.forwardRef<
+ HTMLOListElement,
+ React.ComponentPropsWithoutRef<"ol">
+>(({ className, ...props }, ref) => (
+
+))
+BreadcrumbList.displayName = "BreadcrumbList"
+
+const BreadcrumbItem = React.forwardRef<
+ HTMLLIElement,
+ React.ComponentPropsWithoutRef<"li">
+>(({ className, ...props }, ref) => (
+
+))
+BreadcrumbItem.displayName = "BreadcrumbItem"
+
+const BreadcrumbLink = React.forwardRef<
+ HTMLAnchorElement,
+ React.ComponentPropsWithoutRef<"a"> & {
+ asChild?: boolean
+ }
+>(({ asChild, className, ...props }, ref) => {
+ const Comp = asChild ? Slot : "a"
+
+ return (
+
+ )
+})
+BreadcrumbLink.displayName = "BreadcrumbLink"
+
+const BreadcrumbPage = React.forwardRef<
+ HTMLSpanElement,
+ React.ComponentPropsWithoutRef<"span">
+>(({ className, ...props }, ref) => (
+
+))
+BreadcrumbPage.displayName = "BreadcrumbPage"
+
+const BreadcrumbSeparator = ({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<"li">) => (
+ svg]:w-3.5 [&>svg]:h-3.5", className)}
+ {...props}
+ >
+ {children ?? }
+
+)
+BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
+
+const BreadcrumbEllipsis = ({
+ className,
+ ...props
+}: React.ComponentProps<"span">) => (
+
+
+ More
+
+)
+BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis,
+}
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/button.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/button.tsx
new file mode 100644
index 0000000..65d4fcd
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/button.tsx
@@ -0,0 +1,57 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
+ outline:
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2",
+ sm: "h-8 rounded-md px-3 text-xs",
+ lg: "h-10 rounded-md px-8",
+ icon: "h-9 w-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button"
+ return (
+
+ )
+ }
+)
+Button.displayName = "Button"
+
+export { Button, buttonVariants }
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/calendar.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/calendar.tsx
new file mode 100644
index 0000000..804fe21
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/calendar.tsx
@@ -0,0 +1,74 @@
+import * as React from "react"
+import { ChevronLeft, ChevronRight } from "lucide-react"
+import { DayPicker } from "react-day-picker"
+
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+export type CalendarProps = React.ComponentProps
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ ...props
+}: CalendarProps) {
+ return (
+ .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
+ : "[&:has([aria-selected])]:rounded-md"
+ ),
+ day: cn(
+ buttonVariants({ variant: "ghost" }),
+ "h-8 w-8 p-0 font-normal aria-selected:opacity-100"
+ ),
+ day_range_start: "day-range-start",
+ day_range_end: "day-range-end",
+ day_selected:
+ "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
+ day_today: "bg-accent text-accent-foreground",
+ day_outside:
+ "day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground",
+ day_disabled: "text-muted-foreground opacity-50",
+ day_range_middle:
+ "aria-selected:bg-accent aria-selected:text-accent-foreground",
+ day_hidden: "invisible",
+ ...classNames,
+ }}
+ components={{
+ IconLeft: ({ className, ...props }) => (
+
+ ),
+ IconRight: ({ className, ...props }) => (
+
+ ),
+ }}
+ {...props}
+ />
+ )
+}
+Calendar.displayName = "Calendar"
+
+export { Calendar }
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/card.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/card.tsx
new file mode 100644
index 0000000..cabfbfc
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/card.tsx
@@ -0,0 +1,76 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+const Card = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+Card.displayName = "Card"
+
+const CardHeader = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardHeader.displayName = "CardHeader"
+
+const CardTitle = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardTitle.displayName = "CardTitle"
+
+const CardDescription = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardDescription.displayName = "CardDescription"
+
+const CardContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardContent.displayName = "CardContent"
+
+const CardFooter = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+))
+CardFooter.displayName = "CardFooter"
+
+export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/carousel.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/carousel.tsx
new file mode 100644
index 0000000..9c2b9bf
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/carousel.tsx
@@ -0,0 +1,260 @@
+import * as React from "react"
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react"
+import { ArrowLeft, ArrowRight } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+
+type CarouselApi = UseEmblaCarouselType[1]
+type UseCarouselParameters = Parameters
+type CarouselOptions = UseCarouselParameters[0]
+type CarouselPlugin = UseCarouselParameters[1]
+
+type CarouselProps = {
+ opts?: CarouselOptions
+ plugins?: CarouselPlugin
+ orientation?: "horizontal" | "vertical"
+ setApi?: (api: CarouselApi) => void
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0]
+ api: ReturnType[1]
+ scrollPrev: () => void
+ scrollNext: () => void
+ canScrollPrev: boolean
+ canScrollNext: boolean
+} & CarouselProps
+
+const CarouselContext = React.createContext(null)
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext)
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ")
+ }
+
+ return context
+}
+
+const Carousel = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes & CarouselProps
+>(
+ (
+ {
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+ },
+ ref
+ ) => {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins
+ )
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) {
+ return
+ }
+
+ setCanScrollPrev(api.canScrollPrev())
+ setCanScrollNext(api.canScrollNext())
+ }, [])
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev()
+ }, [api])
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext()
+ }, [api])
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault()
+ scrollPrev()
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault()
+ scrollNext()
+ }
+ },
+ [scrollPrev, scrollNext]
+ )
+
+ React.useEffect(() => {
+ if (!api || !setApi) {
+ return
+ }
+
+ setApi(api)
+ }, [api, setApi])
+
+ React.useEffect(() => {
+ if (!api) {
+ return
+ }
+
+ onSelect(api)
+ api.on("reInit", onSelect)
+ api.on("select", onSelect)
+
+ return () => {
+ api?.off("select", onSelect)
+ }
+ }, [api, onSelect])
+
+ return (
+
+
+ {children}
+
+
+ )
+ }
+)
+Carousel.displayName = "Carousel"
+
+const CarouselContent = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { carouselRef, orientation } = useCarousel()
+
+ return (
+
+ )
+})
+CarouselContent.displayName = "CarouselContent"
+
+const CarouselItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { orientation } = useCarousel()
+
+ return (
+
+ )
+})
+CarouselItem.displayName = "CarouselItem"
+
+const CarouselPrevious = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
+
+ return (
+
+ )
+})
+CarouselPrevious.displayName = "CarouselPrevious"
+
+const CarouselNext = React.forwardRef<
+ HTMLButtonElement,
+ React.ComponentProps
+>(({ className, variant = "outline", size = "icon", ...props }, ref) => {
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
+
+ return (
+
+ )
+})
+CarouselNext.displayName = "CarouselNext"
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+}
diff --git a/examples/tutorials/vapi-voice-call/src/components/ui/chart.tsx b/examples/tutorials/vapi-voice-call/src/components/ui/chart.tsx
new file mode 100644
index 0000000..aedf2b2
--- /dev/null
+++ b/examples/tutorials/vapi-voice-call/src/components/ui/chart.tsx
@@ -0,0 +1,363 @@
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+
+import { cn } from "@/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+export type ChartConfig = {
+ [k in string]: {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+}
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+const ChartContainer = React.forwardRef<
+ HTMLDivElement,
+ React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ }
+>(({ id, className, children, config, ...props }, ref) => {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+})
+ChartContainer.displayName = "Chart"
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme || config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+