openvibers/afresume Commits

Show the 33 OpenVibe sites that went live, an IT-support plain résumé, colocated

OP OpenVibers committed 23 days ago · c11fdb6200 · 34 files, +224 −57

The Work section's "Coming soon" chips listed nine sites that all serve their products now, and the
network has grown past them: networkLive lists the 33 live sites beyond the six core services, in
two rows (for people, for builders), and the old OpenVibe.Sites tile (13 placeholder domains, which
that repository no longer builds) becomes the "33 more live sites" tile. The counts in the group
header, the tile, the diagram and "How it fits together" come from that list.

build_ats_resume.py gains a third version, IT and technical support, for jobs near home; a
version's bullets can now be picked by index. build_resume.py keeps whatever files that script
writes. Fixes "collocated" in the LLC bullet (the designed PDF is otherwise pixel-identical).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UgqWFqsrjYdYbusKn5547T

Changes

ddiff --git a/public/Alex_Frison_Resume_Data_Analyst.pdf b/public/Alex_Frison_Resume_Data_Analyst.pdf
iindex 274f46a..3817c8d 100644
---- a/public/Alex_Frison_Resume_Data_Analyst.pdf
++++ b/public/Alex_Frison_Resume_Data_Analyst.pdf
@@@ -47,7 +47,7 @@ endobj
endobj
8 0 obj
<<
--/Author (Alex Frison) /CreationDate (D:20261020184315-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261020184315-07'00') /Producer (ReportLab PDF Library - \(opensource\))
++/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (Data Analyst: SQL, Excel and Power Query, compliance reporting and automation) /Title (Alex Frison - Resume) /Trapped /False
>>
endobj
@@@ -65,10 +65,10 @@ Gb!#]>>s9I(4OT53,_0-D9%fXR!i+YaUYY=&7B-`X7F])g3+!i>3Tp54>f?P^XFdXL&_\uC+:B<**rEr
endobj
11 0 obj
<<
--/Filter [ /ASCII85Decode /FlateDecode ] /Length 1880
++/Filter [ /ASCII85Decode /FlateDecode ] /Length 1879
>>
stream
--Gb!#\?#poC'Rf_Z\506]!b(m[,Yo9;e/:>k!7uY_MnCQB's4j*ZmK>Sj*RT`_b&XbBBmcM4L"1TY-dBKF7'VP2=ZR@H^k\=cKNf(6rAkSF,E#<%a?V?4?NCa'Q*:)p2KSuh/'GP!9Y<G*r\pugJ>.86Ek,>^X_Ps/49%F%K%Bd[i.COrci+YY`:/&=%ok&2tW!)9QEc>Sf;RXEm0@F?DB`p&YHJ*4(1+OgL"Xf2pHgE/,XqL\#YJaR%[brCU:Tj5/1H9Y?YY.#%<M=ArWp)X%?&"52/;n[5[;cZ$@%p78^N+Q*GmYZNS(!rc\A,poioB,g;"BSd0B0ogFF%HNSDXlZg:rOse1'XVTtq:'m"@,'H1T4C(]B2figkDJY;AXaFH*^0D?O'Q]iON5ADXiloG04(a>g7U&N2WYNRI>V]l+>D/#>*$\23LiCUPa($KY5c,0_$cQ)(OGsT4HbNkt'[Reo1H\[sCZE#fIKL!]S9humEL)`Y;odeK-c:!JSf1lc;MsaCeKZ1nQWfl!OGDPbA=#VT>%t=MWQlSQMi3<=dQ(Am>>eZ9Z;;(QQo=7aRbO3$jrq36.!JlK8Ek2$W!#Nf6>-4I9<?M_:Y."mbgJ2eKW<Gm9&WpZS>gXj/h]^0$g]]WONgYSkM`:;Q\pWphVf+DZk1Y/eP*-:Looj4o*e*.)&=lR't'LT>-lo[N?g?F(+eKV\Tn1q?_^d?h"pT?i>aXn>4XW5LXde]2SI_d,W:mM'JTrS-bBH(R;iRZ*St&R#&TT9/rXoUL;28b+ZWs_LlBHCUl>83Db`8_G==B4#LVLNI`gcbI_PnI5m^2`A*-3&\d`:U[p+Ld0Q;+BcoHK%6OC52Tfc-m(m'r:&SGa&$;I`k$EiCZS0te4kZ+W/hk:OO5UW3cgOtgEEU4A5&l.2_TKtc"pYD%2g=;5=>=Lh$q2_a$1P7&/1uiSU"<NVQU=6sdoEIs4&@%Z?">(^G^__<DV3<@8MW:(rZ?=,YH``VKJ6(@KUMe&PmJ`fc5:`NC+T<:pQh2nQ[cVooGN(Eo(9/E.EHH>oM+uMG@Nc_SK[-)%Z*RuK9Qk8e)B4gNF.IP%a6gJWguYZ<[\EKSZM5b/1S@kfQD`))877SCg,M^3s2OA<:9['XUI8IE-C1<9)L)C&Kslr3]u&U;G,ushh=n=Y:r3C3G7E[o0YRUrc<gC>hheu":)l.i0qP^WC=W*"4IV8Uk[nl6%S,!*\bh#M$g>tF(Z5%@npg6(8;3NhXO5qW,Y?H4_t<pMq0kB:gQ\!BeZR?!@Gs<RFCFECE]0VJeuh2GL#mEVb>^X)QfH]iBE29Xm%$e;gGiIM%N5eMk78d,dTonjap<dHHZp^FgT1)@qTjII.D/C1HSt7ZI/ETN&/C#2W@e_sL,&=D(e[gSr":j0H4D<&]&%.ni;Go>&9.YG0IbcqS%P-MBFFA$$1-Pjk=J]]!F9M%aq5tZ82,WJL_VOr'I=\ao@o]1_DgLWUB>iF"OLijHG%Qm`r40f:`EaAE@:g"32!BjqRSD1])pI"b$@Y2Gno`ihLVCbba@9Mhnkl8+RM!;`25N/7XZaMd>VXUm8k47DGA)fN%M*fn-ng'gB_U!kC*[#!=HP/GcQIkV2N`V@6ejXI^MQ!Be;u)aa"A)<D1-pfKG0)=,<&PTL=e-$b>'.?A3WN&A=<3NmDT5_qE$%qhc9gr@PMXMb:F)q]3p;Q]B:6p'n.1BkX&8lBP,lg20HsMpT.95[C.s@07:9VZ&LglU&FKR#DDUi+T2-k\Y;;,K%E;/9D-Yg?q,%fUCc6K7`-+%&Uf`9+*R93!-V$7r3c4@O\MThF"6P@:1=-7:@<sq_jpQ:U+B+FL`o$PM[GbWs^#$6HBH&^DAjjq?LK6m(<~>endstream
++Gb!#\?#poC'Rf_Z\506]!b(m[,Yo9;e/:>k!7uY_MnCQB's4j*ZmK>Sj*RT`_b&XbBBmcM4L"1TY-dBKF7'VP2=ZR@H^k\=cKNf(6rAkSF,E#<%a?V?4?NCa'Q*:)p2KSuh/'GP!9Y<G*r\pugJ>.86Ek,>^X_Ps/49%F%K%Bd[i.COrci+YY`:/&=%ok&2tW!)9QEc>Sf;RXEm0@F?DB`p&YHJ*4(1+OgL"Xf2pHgE/,XqL\#YJaR%[brCU:Tj5/1H9Y?YY.#%<M=ArWp)X%?&"52/;n[5[;cZ$@%p78^N+Q*GmYZNS(!rc\A,poioB,g;"BSd0B0ogFF%HNSDXlZg:rOse1'XVTtq:'m"@,'H1T4C(]B2figkDJY;AXaFH*^0D?O'Q]iON5ADXiloG04(a>g7U&N2WYNRI>V]l+>D/#>*$\23LiCUPa($KY5c,0_$cQ)(OGsT4HbNkt'[Reo1H\[sCZE#fIKL!]S9humEL)`Y;odeK-c:!JSf1lc;MsaCeKZ1nQWfl!OGDPbA=#VT>%t=MWQlSQMi3<=dQ(Am>>eZ9Z;;(QQo=7aRbO3$jrq36.!JlK8Ek2$W!#Nf6>-4I9<?M_:Y."mbgJ2eKW<Gm9&WpZS>gXj/h]^0$g]]WONgYSkM`:;Q\pWphVf+DZk1Y/eP*-:Looj4o*e*.)&=lR't'LT>-lo[N?g?F(+eKV\Tn1q?_^d?h"pT?i>aXn>4XW5LXde]2SI_d,W:mM'JTrS-bBH(R;iRZ*St&R#&TT9/rXoUL;28b+ZWs_LlBHCUl>83Db`8_G==B4#LVLNI`gcbI_PnI5m^1)1Tpi,D)fqSh#\"4!Ma-icJe(.9"5TDj`0k$J=AZ[3'5Sf8:hRSE#"`&4L`!jY>QtJ%Wp&#1',eUk+bV1A"U84E7uWBrUF$tn")YlX`iLV')E3\Ig#G3)*EJD)37]Bn9FR=SJG8!2-@GBL=NpXB__W2^<kUbSP12,*67uAXXdH+CV;"n>@faS\eVQ[qhIrGXH62-pM0b9ZFc@SRW$a3M9c0+W-YtldF<:c2H>rsKlBQq)KSfpcB?^5fla-*Cdd^:FlJ)p\*n'NQaa!KMV<hYig]G]J7`4lSjGK3J46&Fj!`=S)m?"q_$P[Q&oAYP=FuiQi"<I6\h[I_$?tsMZ')&/hq#)"#oA][op=^Sp^5M3Hg,m+,OTFgERaX$Gg:I]<TD9mBJr&LS&t=b*).sGmLOIfTLUM;I-2$_s2j"?'lM=$7Xnd/,Yam>e,+XEqs!Hb;d*p8]1SiQL%?;2Kr'^Lfn:\U]D@<uQIDfc;t[(qNaL81!AJY8G:mHE@rt!rBJbC65D[6NG?G5&,9/K-c&Le%YMRao5O7:(n1J^!e[u@]h:LCJ<$:1t\j06i`R*>t.oDS99@,6U3Lj(5HDc1CpB+,U_#6X:Ui:,9nR@fZ/]>g[Y^aui-Wtj$FKsjS8!ktS_$IUp<k&iAP\^KImq1tqhi)')%da-)5c63tnNIa,pDY2>D0uB&T`CVAJ@;n#lc/_U$9StP2_?37<Od[+#E&@3@t*lo$ZBL,[*uAnS)X>aor7^S1R>ka?YR$]2&3_X[]Fqq1VaB:,HqiOq[K>+./4G6XU^5S/k-q;!Qj<reIH`"[4L,[k;e2q/HN=jpFnp=+\nJW&iI6RjDk?L(#nUHoMTEa5._gEB,B1g.q\/B\(pg5.bX7W2`/ic3\oUG2mgl-Tliu,=#rcc[jh8.6MF4UFcGgD%6/HuqO^Tc$O&b(1+m*gUTn!q\&%]t$kGS-clJ?d]Ch\'B83Gr$AR.eQi`$o/^WG6#iOthYmJR/V7bZ3&#&-?JW9.6Kn1fCgMDa!`JU)3jfVESIX;X-/pmp3+X=r*?Jt&2+1';[:]~>endstream
endobj
xref
0 12
@@@ -87,7 +87,7 @@ xref
trailer
<<
/ID
--[<4db2b9d6052cdbd87e4d08758dd69ff6><4db2b9d6052cdbd87e4d08758dd69ff6>]
++[<0708869bd847a2f084ce65d9fc9de7c3><0708869bd847a2f084ce65d9fc9de7c3>]
% ReportLab generated PDF document -- digest (opensource)
/Info 8 0 R
@@@ -95,5 +95,5 @@ trailer
/Size 12
>>
startxref
--6393
++6392
%%EOF
ddiff --git a/public/Alex_Frison_Resume_Engineering.pdf b/public/Alex_Frison_Resume_Engineering.pdf
iindex ea5b120..d3b3279 100644
---- a/public/Alex_Frison_Resume_Engineering.pdf
++++ b/public/Alex_Frison_Resume_Engineering.pdf
@@@ -47,7 +47,7 @@ endobj
endobj
8 0 obj
<<
--/Author (Alex Frison) /CreationDate (D:20261020184315-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261020184315-07'00') /Producer (ReportLab PDF Library - \(opensource\))
++/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))
/Subject (Software Engineer: full-stack TypeScript and Node.js, payments, identity and live streaming) /Title (Alex Frison - Resume) /Trapped /False
>>
endobj
@@@ -68,7 +68,7 @@ endobj
/Filter [ /ASCII85Decode /FlateDecode ] /Length 2981
>>
stream
--Gb!#]flGh,(4FM1gnu%aN229Zih`8Xfh%]S@RX2SI`Mmb66b,/_#U->FnNf$\KE3!e*\oh;5tV6CbF@bQZLFU!p\bmn@t`Wk^6,Y?Nh-s1ga/-n+:=C9pbGf?X<kXbJ=`#O+Yp,&0:8Q'+XR$R(iQ8<\H./YT"[:@F,G#pZU+f*af84IPc*i\iZ\]3gQ>oD2[PG6I$dM$J2#qLVcT?LU.NCb";GWF?@)5r(Dr')(T-<=gVioNEW(DEg[Y_L&49k6CLYKi8qLOCf6i$J_u91IFk3_;<a7*?N*:_heE'33kDl5(q40>$O9%FHTQ.C^/FboMsSGERDg`rVn.hRWA"qeU*i[Q(/9b5Ue>64gX^4>[email protected]>lZL,!G2:ka?iBrWG%'R010mGKHjShe[IGl&'/[j#MRBnVCA_MiU/<9:d.t0&0Kea^pCDPr:\g4W-_nkHrhPZJ<PEK7DpdqXpL-#IPLK/!(qq%L/8/,jeD?]="bTQH9o>V1<aX-nrj1_Mk]:]jm,u5Rhe1p+Y7mIMHF8b2>Xpa=>fHnJe*Q5+G,2Gg<?dJL>tluL[+=AWRHjG_+m?"^/(+kMBVdC[Jpp"dg[5!,<.6KAVp,Ec@O!**N1csmOiOgW<U0Jb1#0<tb:<Lk-Kq+^K72-@33pCM]2TVr-Hng;d^F'8;RmrJ`lb"JQVn]h@7K!cY^8Uu4Bp"$Hc%!#]aZS9,+7k%@]@VD=[a$i7RS3Q1mUcP3P&9m/QAb*k#XZ;e\*dn=Y+Bpd)*`:"=)=;n7\d80cZRZ@cr8VWlb?oEp;6.mBqftI45=1bPak\Z(noX486;k9",]l@?jl1P*\#Q5XG6C8f6P6I6%T?9K"N^>TDiX0tL0nN@%^l=09qpBdJTN7gC(@^nuCTo/jKsEJ2eNKK"8kr7!qJP)V4b0eE/NeaP*?UYs]TR*ekd1aD=1U0G*bd19aTmECfP163'RbX3H-HAla#Eo13Ii-;ahe+:beg<MRiI:[4uH=+U%Pp"WOCIAQW8*BT)A<%XgcrnlD6Z38Q77\aH;_P4.A`R':(@<mC:)8is*EU?tOI5DJ$fMED!4Se@aJQf3AEVkQKV#To3fuLC<K=^GaJ#"mJ^.39q?)S+0>7\gItH6Z$f(kin\0S<#Q<[XGb?`YS9=.BlSZm)L0NCp^Nc4Ti!dYap/K&3#pH#epMYtn/!r[9;Ak!%;1tma>?Du+.Y2]gI-?9e0#<`)V02oK'cCj?'hK-d74N?4g'&Bs!H-=Tq0,:il(cYHh,<?._YL2Tf_VKncSpDXL>o7CNfCfmnZT0jn1$hfP9M6b,d5<bQptM(0LNBC-tqqZ*Pb!A_Y[)EC:9;=;ebMsN_OW#Dd&GQ,%j0^IElWO7c%Wc1lsU.Ba#;I<^%BB%'s`,*+RPTX`>5VP4J2+CkbH_LUaNP();@A6D]#/LR4i[OSK+Ci]'n3`7#g"Fq,nf?g0>d3l*]jnm83+0O2q==ZD9p"b)4foo(JCd.d5+\7'r@iubDY^&^gAJ:hQ66"ImO^#%<qcS(,)SLM(-o"52=E=>4U&4jr*:5,q,Xb8g;86UiY&L@kh-"mkY_1%9l<[IWIS/?H6W#ipTc\GIga;4TmKS;*4LL24MCklbM>Uj5(!N'1=]g?GT9%P6iN(>B2^Z3TgLN%-'WU@n"AV-=DQgI4Yp_Y0EVE,?KAALe9,lG0[0F[:u,FHd8QF4A*.k\uW@=U&[pUI`[1G6A6pa+O)>D-IW=XQ8dl.I<B$,@%t\TLI)*@DmRo+5cP!g`j=nEQPcNp4<d+#8*tGuXLaDC^-TNYX,93%Of:$C4)`pTLdWrrt_bH25gkp>aUL._m&YD:*#-TY;-gQbpQ7"umPJ\\b*)?Z]UX"`h%n$PmG2R-F5ahYWLC^XM_3od?p(\_&a?FEf3c!-Ep[RL06.F7st,]fs`WE=.f8J_;P]WYhJL;FrqB#>D8j[XTSHWcY]*G_jmB3Of@3.#(F)A9AgZ<'_7@YB7u.['/>1?>I]@i:QhSrks:B9X?LK8:5,cbCU+NMDa9_hWb.9"%Z>2.\M&uZj_do$&%-k%pJGO:nAl^R?9;.BY:0ce9L&\[)m(=_bkN8-.eulZ/aZ[mi+q5m54,h<;a>g:V>a_c\@$94;mKdp>,"GYZ3VJQ`m[(?F@/`lc\hrnq&4;S%#nr29jsXB-,J>YMSh=omci77IHXWOQmg*>tSi)j<0)k:J']HUM_#c'ls#:ORiZ;qNG+(c8?;j+*fMUO3WR=HhGcU`<6q!WamVZc=f7g#P[)h\XN:i]TnE>HsA1c<Ws*&QrQabKe@&eO+Nhok$h-:8#[t?bpNSK][hp-Yq+a8&*:7Z`""9A;Z]oXf%!#(P!4VrBaF44(]4tL333qQ=5/.!3+<@XTjfVZ>ZKk;`:%m%JhnJp(@-s+\*se8L^t>/f#J8Ng3^eh\lLG_%iq1So-G+I4W[4'*Fl3Zp9dFi$"r>QW?iCh/ks\PgWC;U=0Eb`pBtDc[Gn(;W\T`QLaE-9G)&:N.NZMU+<nXEIB3R0igl!6K@0$!%QZP!A=.V6(i=!TeNLF&-)]Wj?p96ji"^*.>pRE$:R)3fUuu+U!Zl8ahY\r62ld(r5?+@$fiV#n9TA^j&3jBaq;[-W)`_O\?IT?nd46!d<0LfgOD9ECJs5]%CdoD_;$_0-(V7!(fuR-.f,b=VDEc\?#uBr2nGCV`ksi(9!s^!RU&H>c%.np9Z>2E-EUPic;3^Mh[!t!op3%Uf7ks\Q!QIjLGa@sN(OT7d&3btba?e#?-uR1m@+bGJIIDZ[_97atj3deik'b.;W7t,IIf^%%V\%arE!/N-;*jkj"kBdn4P'nHUeU'4no?J43p"8o%]W[#ED"o!c[IFnW(]ih6"WAUTg0ZOI!jH$_OoTLQk^gcgCF&=cj?XI[pp/gg"qk0&`Nse3I;IP.UO)km5LAZ;Z!EY!0-Len6!X?,)kL.UF+?4Oie)G/cY6^HLX4or(:2~>endstream
++Gb!#]flGh,(4FM1gnu%aN229Zih`8Xfh%]S@RX2SI`Mmb66b,/_#U->FnNf$\KE3!e*\oh;5tV6CbF@bQZLFU!p\bmn@t`Wk^6,Y?Nh-s1ga/-n+:=C9pbGf?X<kXbJ=`#O+Yp,&0:8Q'+XR$R(iQ8<\H./YT"[:@F,G#pZU+f*af84IPc*i\iZ\]3gQ>oD2[PG6I$dM$J2#qLVcT?LU.NCb";GWF?@)5r(Dr')(T-<=gVioNEW(DEg[Y_L&49k6CLYKi8qLOCf6i$J_u91IFk3_;<a7*?N*:_heE'33kDl5(q40>$O9%FHTQ.C^/FboMsSGERDg`rVn.hRWA"qeU*i[Q(/9b5Ue>64gX^4>[email protected]>lZL,!G2:ka?iBrWG%'R010mGKHjShe[IGl&'/[j#MRBnVCA_MiU/<9:d.t0&0Kea^pCDPr:\g4W-_nkHrhPZJ<PEK7DpdqXpL-#IPLK/!(qq%L/8/,jeD?]="bTQH9o>V1<aX-nrj1_Mk]:]jm,u5Rhe1p+Y7mIMHF8b2>Xpa=>fHnJe*Q5+G,2Gg<?dJL>tluL[+=AWRHjG_+m?"^/(+kMBVdC[Jpp"dg[5!,<.6KAVp,Ec@O!**N1csmOiOgW<U0Jb1#0<tb:<Lk-Kq+^K72-@33pCM]2TVr-Hng;d^F'8;RmrJ`lb"JQVn]h@7K!cY^8Uu4Bp"$Hc%!#]aZS9,+7k%@]@VD=[a$i7RS3Q1mUcP3P&9m/QAb*k#XZ;e\*dn=Y+Bpd)*`:"=)=;n7\d80cZRZ@cr8VWlb?oEp;6.mBqftI45=1bPak\Z(noX486;k9",]l@?jl1P*\#Q5XG6C8f6P6I6%T?9K"N^>TDiX0tL0nN@%^l=09qpBdJTN7gC(@^nuCTo/jKsEJ2eNKK"8kr7!qJP)V4b0eE/NeaP*?UYs]TR*ekd1aD=1U0G*bd19aTmECfP163'RbX3H-HAla#Eo13Ii-;ahe+:beg<MRiI:[4uH=+U%Pp"WOCIAQW8*BT)A<%XgcrnlD6Z38Q77\aH;_P4.A`R':(@<mC:)8is*EU?tOI5DJ$fMED!4Se@aJQf3AEVkQKV#To3fuLC<K=^GaJ#"mJ^.39q?)S+0>7\gItH6Z$f(kin\0S<#Q<[XGb?`YS9=.BlSZm)L0NCp^Nc4Ti!dYap/K&3#pH#epMYtn/!r[9;Ak!%;1tma>?Du+.Y2]gI-?9e0#<`)V02oK'cCj?'hK-d74N?4g'&Bs!H-=Tq0,:il(cYHh,<?._YL2Tf_VKncSpDXL>o7CNfCfmnZT0jn1$hfP9M6b,d5<bQptM(0LNBC-tqqZ*Pb!A_Y[)EC:9;=;ebMsN_OW#Dd&GQ,%j0^IElWO7c%Wc1lsU.Ba#;I<^%BB%'s`,*+RPTX`>5VP4J2+CkbH_LUaNP();@A6D]#/LR4i[OSK+Ci]'n3`7#g"Fq,nf?g0>d3l*]jnm83+0O2q==ZD9p"b)4foo(JCd.d5+\7'r@iubDY^&^gAJ:hQ66"ImO^#%<qcS(,)SLM(-o"52=E=>4U&4jr*:5,q,Xb8g;86UiY&L@kh-"mkY_1%9l<[IWIS/?H6W#ipTc\GIga;4TmKS;*4LL24MCklbM>Uj5(!N'1=]g?GT9%P6iN(>B2^Z3TgLN%-'WU@n"AV-=DQgI4Yp_Y0El:<Uj"X!\qnDi*8Ycf^=?oa!HQL\(Y\0gfj!Ph7`j5NDO\FP70QX$,!!K(T'S`k/CB9/-$^cEV$efU/uS9U,h6aUtC8H-6*X*8kLUArefaQ1V,;:n7IXn9Rm>,]Z_XQ5j"8.rVm^V5$o)t6,Sj[6r>O&)Ar!-br["f"5.qGrtg$3TT5^hf7r$B(X.d<TF@\Co:^8>O4,VUKm:mn\9.:<,/VT=C3M@ahR0iM$#Ia;%`b.Mt"dg$_)uni-`j.3R1GB!60Q,)@!b-0`o$0X]'Xrfr/6db/g$+]^,Ti$.BZ(b4@)eS2Z0"]`p_dP4\I^3JO\XXPcPe)q,7'E?<2c,thdnWfq&$a04QepJijV[(]^+A&9,!9TQo1*gJ.%sD/ABY>'HH&>^:6I**6nWr#dj,q[9-ED8"a3Z]0>aM2)NcuGWD(nPgdk'ZYnEZ6%nWNW/)s[CkA?]:kGDhs,<nmg`^;f/Ye^?(R0?<\-$^sb-hVr;$1j?-pGt;_`HHR,Ts68hc\Smp&WH6"#.Ab^]K+%rK-(1Bc\o"BGaOt>qc5Xjg:MM%7f+8dJjLqc\3[9+B`CCjN'1=C0P-%J>UZq[sgjAq\C>qVN<\C9@E@jjf'h#E=(+FMD\tDVZ$;0$AM`joZV5doXT*TQ1;C<*rY't"DPhV'/4")p]#uKS_<Wd&,qA>Bm+i)!%n5E`RY[2Q]mIi^%LcOOLMRaXE<K1K%^Y*$'c?rSnX>fFh_D="k5$rP;T/lsemhGri>.LFmF)_!$=&A,TGU@uaN4"8jf-3p8S<7TbQ=/4R1-sr:.pF]7]nTeI08(L7MOS@Yb"+-.RKQ=:*W?bLGS$KtL]j(;YnF-pG:n1N7^GlMkhH0$@H)]E[&(<7pY\)5S:2E.q%2tF=0thIN>P#F<?ef5Sq"iiGe@m=n$b"+XO@"F9SnX;@m,N"2:ff6r+oBqNt_@qjIE2o;(iKL2\c^;WlZtD8@?92_+FE#p*("+."jS'$XMKOBOn-NCQAYf2P4dZKW"N)q!I;1E22.(JV+Nd;#c[l#("!-feo:R\G(@"W6'5ng-;$GH^bT$PF24%!95D64Ni$8lTM:"#Tko\jIg(0'K>,G0^Sf6[eE#6i8qqunS!Aoo=eX.PjKju561u#6\t293!,fRW1a!pK'HDGT'HM_dO+TUH+B;USatH@#K87=32LH!ANprE<2LtE+>Y.;:`k`a5!J7MiQl@abR0KmD23LUkQFk`gT_+DD!sdO#k<MCS3;a8PqS,q[[ToRW.!0<JAugCGc]m0&]X>(;%mWU87`"4Q@F*?^%`Zj-qWn~>endstream
endobj
xref
0 12
@@@ -87,7 +87,7 @@ xref
trailer
<<
/ID
--[<3cb1e6404bca482100e41159cff17f6c><3cb1e6404bca482100e41159cff17f6c>]
++[<858ee61722a5f5c2c69b17e80403fd90><858ee61722a5f5c2c69b17e80403fd90>]
% ReportLab generated PDF document -- digest (opensource)
/Info 8 0 R
ddiff --git a/public/Alex_Frison_Resume_IT_Support.pdf b/public/Alex_Frison_Resume_IT_Support.pdf
nnew file mode 100644
iindex 0000000..a268c1c
---- /dev/null
++++ b/public/Alex_Frison_Resume_IT_Support.pdf
@@@ -0,0 +1,99 @@
++%PDF-1.4
++%���� ReportLab Generated PDF document (opensource)
++1 0 obj
++<<
++/F1 2 0 R /F2 3 0 R /F3 4 0 R
++>>
++endobj
++2 0 obj
++<<
++/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
++>>
++endobj
++3 0 obj
++<<
++/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
++>>
++endobj
++4 0 obj
++<<
++/BaseFont /Helvetica-Oblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
++>>
++endobj
++5 0 obj
++<<
++/Contents 10 0 R /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources <<
++/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
++>> /Rotate 0 /Trans <<
++
++>>
++ /Type /Page
++>>
++endobj
++6 0 obj
++<<
++/Contents 11 0 R /MediaBox [ 0 0 612 792 ] /Parent 9 0 R /Resources <<
++/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
++>> /Rotate 0 /Trans <<
++
++>>
++ /Type /Page
++>>
++endobj
++7 0 obj
++<<
++/PageMode /UseNone /Pages 9 0 R /Type /Catalog
++>>
++endobj
++8 0 obj
++<<
++/Author (Alex Frison) /CreationDate (D:20261021063644-07'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20261021063644-07'00') /Producer (ReportLab PDF Library - \(opensource\))
++ /Subject (IT and Technical Support: Linux and servers, hardware troubleshooting, customer support and escalations) /Title (Alex Frison - Resume) /Trapped /False
++>>
++endobj
++9 0 obj
++<<
++/Count 2 /Kids [ 5 0 R 6 0 R ] /Type /Pages
++>>
++endobj
++10 0 obj
++<<
++/Filter [ /ASCII85Decode /FlateDecode ] /Length 3089
++>>
++stream
++Gb!#]?$?rB&q/qEeD#WsgXtns3;^!.#tnk:"P^`Y3I-bAZ7XM\lB0S9bi-V2/ZIRO\&jdh#N.S=5GV1Wh-7?</Sa9`bC@M\dH_+PHIJTh7$BX=3fOVDhU1lS`Et9%jceLA0(u&@@imA^HDB02T0D*1BbiA+=K>W?9p69,8(?8U[A+M@]JiOF'tl?ElKffk^=)'a]\>sS]sV?gd'%VOb+3GK]Y,Q6h[c3bB[@#4MScMC;;&hNX@?QOFM9]"_RW't0N+FUV!khCM7WK5me,o5_4Zi^!g`hD5N^hV7ODDdou7.dHm*,PCW&8^V6X0MPJ1t\[^T(N=\Se=&%TnJX/Rq!a2r\aVu"!TDfdLR2u,N?di\O!66_oH7)mDD`?M!qOltH."+$lNil^/cdTjYj)D-Z2Xl'n67MjNAg83@1SuY^n=Jo)3aG/=B^$:^-@U\5[c?:51dsgT0U3fYdp*Uf/3^"LDrjusd(,@'"fV91!rkVsY^X9f'AH(PP3WmZp-G_q@/^s)Mg$i<lRu<i!MZI+1DrEc^f,;+K-juG7,8+Q.WJk_jCBLibBJ"qCA;VqU*D4qZ<uE0^EfmJRZ^V4Ai(Gi/:CEH_#iUV.a25r8XBM7oNTa:UlrqJ`)J';AKnMEHdUP&\W^Or[`Y3_3&Ou[%c&3iuQ#e1O1I^X;j@cKg`[:bgORVm:)GN#TkYs$MUe8-AS=i8SgRt78@4K"M`K?`9f4%B#=Su'#V\X).b_QX:pscjGeS&REV5tK$$3sgMF(&A,H7%f1G1L"R;\JQ2V+drWSYjg!nNTV7;fm'TE>XWHR7PnJrWrbc3NWrf)Xl\(f'6@G/PCf:JeFY]j0^`gZSYRGYn<![9;D81?^-ZQ%i2*7DD,A^9EJ[K,-kUNLgrQBNUiEq::3#^e'M8DK#eq&_QF&9fYj=$P<"WiYFUeaf7;s=*>^/BdqT1n:9'lJlKS@f`bOXS)Wfnl*.)/FWR_m%RLQF)MJYJ9WG^n@`S_=Bs6&J!]CCmjgOj,>EN=dh^W]FGlnU9D$D;3HM:m[1#KsSMGag>sr->GRIr@u^5&prBe`_;d!YBn`mP(f[Q[13EEb0DDKPV=A._(t@q_Y6W:PmebOSWfQ!IrnZSc'VbqL!/^Y%2;9ECi_`J\/?#3o>:?`!GGs$Ij4tZ6Us5lrOEF*9m5bRMm1n\sU_UcYG7b-PS08'NHbIhg6s7MeOBL8U;Cc_%D0UgGD#SZ+Mo7<4ubEMV<6aXl%eWl!5>%7%_/ZNeo>AS=JHr]=5ZiA`9W"1Er,m^M0rT0M@r)iU2@:N)+MMbJ2-_R%<?#,*PftpGdQ%_;[NZ=f4'#<qO="jdbRd$O`m@n)\oAHF@ui:@:?bV$O9@jN0HLiBK1<Isb=mZaXR,O%=>+:,Ol]Rb?Csrs2AGHkjg-n"&^.1fZ/U/==UfdHB-!*N+rZcO#IbWUiO7F4Nrr)<h-:/o(IhR4T%@q5#>E&W.7@>/X)&5R7[YP+GU-bK#'k1o.#Aq/9hpIT8CC>LcSrJSP<&g>RTp[.1rXZi9KeB?-d2j7e>L]/FO*4R]cRUJ9nM\E3T,b6O%*;SD`i_:!DfH0UbG80V$rH\XXl&3@h#:U!3Ni5\XEmPgclhfoO]W&>*t8ZgF:O_!j0An5!p-u)47:4k?1r`Z.C^l%>.AOV8m>D^s4F5'2JQI4ltIc'UJNIK*pXd^7=)XYu!Tk?FgAM#[!^/l[TQ01HWd1HL`JdaS3fW;"+>apsgk/A!_*,K+M;],rr60Z:kE6F'*AdQ`T/P*lXHaZ1InS2RY>;>3s5N"t;5_(3hic6%e+OITq_o$e7*8#O>Bdi>B:gj`ZKkVr1)?q]JX67>ZBXU%DF"rQ@h9&&7Mj^_d:17ih\XQh8KE:e\<,r.8MMs!#Y*28WHsrX&\fm!JkWoS;Ks_X,:)X@0`#=eX,jQo`c*0CAecC^M]&#7U;p-q`4ZZXKg/lV`$9C+S4,LQ_grZc`KRPD]d-QjJm:PN!@J5H3L_qA9%;66I`SL5i-X5NZB[]jDDHJ`OSdfGYXI%SEOY@IG:9++O'NQ[tAGu8#TeF:GIJA`sNV;7]^Zuip_I("0BF_VP8ul/mgupNQnmpfNKlHjm5\:e[gbJU%Ff-&(pJE**k^p>9iKQMKmmQL5d;*(FVQN8-'-#gd]a^=N\3_J6.dL.[8L,FA!\\"-o$F@6X#Htus5of\WP7A-;ZkrcpI-2,XNo(3b&-4.GX+V`i7:c;f<-u.F3oE[/P'lg=O4jBK-oRee2,M5>'XMmP2I+_V_V'?\ocJVKN-XsDUUK5h?b1+"%Z:qIui%S7;-tb,j4*Y$QK@#"akmXF]QEdkGESAV&Lga\R\crF=?^Jn"Q4P6KA15qZ[8KHB<$Ye#&)%!\EJlD:cZU/0U3tK`!c%M&+<cGS<g$@[X<CLp@`:]PP"jZ>26(TW]P5'>T_MnZPC(biqH4&*,@^AJf[Acq$fZ==tcO]m1\^Q\&ng/AQNGL.faV2[:Qbs4Cpq^k/poIP3E@;tOjejh]C9N`UKS$#@Q(SbdG@\3dZZ.Z\4DCObjGmeoG?52ok'@<tiC"-m%Md+MhrJ%oa_J!Q;EL<#F['A&#PSr13&4r))dnd^;`eV*AqkTKA!IgbJCDfcq?+')O4^#KWQep6VK.%`@EUSEsLff>;5!C7E&NF2_e>^ll+-[3g/Btn!o*mGDQ,(M&!<S8FX;_Mt&BiO1ukcW+fJ`L6kcZBAtKn-IML#p@l9EjXcro#=mf-Q:l+CjJ>"f_YtO.?Rph:Gd.,+]mC_@s#k4_gim?.^+&R0?C:]M&09YD*t5jc8#P7l;U"rRfBkM(P`.#b:ABP!nAs]t2^I\P[B*&`fmVZd#04XFrL&m<!.0s-V4tM">&4#btG78[LC/#$W77C:@ib5D]8Mo@^?T9oTf`AFPj;.3AA'`]eIZj2U`'^FnkG2j_n+4XZm!:s=g&Rebub>82fl#7r<I-t:KX13VQl"<'F^i&10aPUV(X`f/ctQ<XY(F)BdZ*!>!BGNkI^I:Li8`2+inI^S[lD\[$;HlXm&_Du>9425P"^Kp=F[t1L5$F`A&F!o-AL<Uj3pm2!E^ol`=5gtYk!ICk4q>~>endstream
++endobj
++11 0 obj
++<<
++/Filter [ /ASCII85Decode /FlateDecode ] /Length 2105
++>>
++stream
++Gb!#\gN(b6&:Ml+o[@]$f/Aq5M;cQ%!C@A(Z!"*8p15G-X]^]s`69XL'Hb)f8%'$Qi(u[=P#O!u17-If:S1ZS?2Q&>Ip*CHT$`0<#pha'bpaNP"uLZm-aUL1HcbEqi<UeAqV=mW&)_i542T;EIK&IHHPe;J`p\$[Bi\X6*f]t,_tQ2:*e&Uu^%+IV5\IriKlls;VF!pO(UA/29+s]i=0b8E9;asU6Z86R\F0BifmbjUP?7Mt^Rq\b3Q)@!.XRZ8IJ3Ku.S'.]q_X=K9K$\QFaIC$QLNT-p8k%Z@#N9hTmN"44%VRnnEnd,nBbMIOS8]K%M%K7R-JQS-_F-jmdrG7a1:msKNZHV6t+>l67s\hnkQ%?GE:RF<(]*YY)pr<.(+fi!qc/SjSMp!9]7E_)KM?n=4n]YM:>\sWQ_K,Gc9-/psp4SI?CB4PBA?]F#M)r-j3A6N987gP@DVY%R<NXR!T2(\[)H_Pqb53i:/%".+4UdV]\DoG"r.Ro8oInJU$U![E'e2>LQ>tcs0f@lA$3k7`d<Kf!M`#l#U(cKGGAp0P!gj3*P$^#$I?E7*N5bHs6AGBYff->pp5<pjZ(:SlO&9`Kefn[PAnn"GfLCK5,15,Y4<Vb(OOuRCfuAOUVEXqY"/S+37*WR<(4.7A*mj.\$m*Z-PiFc,o@1:2-Ib%GH8j9IP8k23Y$1GCd,FYU/^5:rt*;@0UTH8B:9nC4r.=Vjhg'Z9jrp]il<?]dSS;\*1ehUE'_97ZEB,\3MB.VY9h`^th3EE1G_@P^Zjd*=unXf0Q7),qZ>LJmD-I92<];\(g1k]75qI^M"bt$!t.maQ>j/F!3\Z>RT_J9Cips.6lCX9_eP6$CB:2dOI]Rb,f#S.9nQ_lf@unGA&5P2/aY,:T[o98QZ?i`ahD7+o]BgTE;.P%Q4ee#PU0nVc(jqQQ+j=:fuIOYcK0iiecN]/\o.<0EqR=!kpib2L;c=FbmOmmUU_)pt/?8Z<h5k9j40!\`BKob,ps=kXZ@806DR\O#f6d]U)k;Bc@agN]S]SE,]$pC3"Zjco6"C>>UptLE/@CaYYp`gS<ZiETNm?W4HnE(0[dE<aPlYO7`(gAlGYC`3F\@eiQpd5Q$rFhl@2a1>FU"d_G7m+%m"4ILY6$m&jp.`U65#o`8GNduKb?C5jgpL,GB&W.6#,lXX"QMWCnbR^+bESLVMjS$A(8p4e2ogidXPE&bSH8UMTR$tpDOPW>NQ$<WRiFb)<khdd\6$0DlbW8Ws`M)At#"WfD-U="N">2enhi0[X,q9d?`&<X\(QoX2NTlr#9G;$nPD';ZcI#NLICT0@?EH3lt3MuD+=3kkCh)4r"0``M&iSnoBW;eKAm[8<@6%e\E0Qp%:o[[II^IGnTO7"p%[79h5C"/pigm,R(7/P&ERT:of$@@CRq[[D@hU>.4n1J>h`GJ=Lo*h*I3&2ihMjg(@KcXaLXU3H#b-PQr7p2_<UXt]#O3*TlL'YV!>o-Wm9CU+U_4_u(;%KJ=3-g(4dA"fV0B!`9,3/hor15,3CiV8JIf1i0T4mk!%[`qD0"InO*/do>;c/:l7?HFCeU/AJ)9D#b5-VH2aRIB(o.^"]mO@G<bTkW"DD9,UmEEBKnSFQB@^3P7lQ8#Zq>Gs=j_!Oe'gP*@;Hp*=7[a0U./f3K[[l*:#n#5@qYj#g_$'qR$oup/G&8QE)&Nfb`QYI=Xtk-4dXZXRauV7\gcjjqb4\FFbX?^<db6"LG[+V.V*o[p_aZGi>_mK],o$Jfae<Ko1>ShZ;Be3U4KaMK';l-CG-SZr2Yo:L21A0McHZ_#&t>[Qd$dd(DbZ2b6)'"Sg6XQ^^KgSb!!2q*-/b*kC<JLSN[cEdK$P1M,8,T8=^YKE9.5YpA@DL-[=*6rU38g_k^EKSZ`dB(PN+SlPM,3$QTO?rO.03Z5>[(18b0P8D8NIk)T+8](kt=^8r_)(SU=2Y<-1Vj1p'2b-QR:o2!+GeLZftCKMF%'iTS32i#U0D*B>"lH[>!WfY/r*U@Z@S%`O/H#ad6kh*@;PdM]2J^%VD6+gI8efA98j\J%.dppS>eedWR^ULf?7iAjr[,L2t78q84d0LbqtiUak6hZ#r2W"/~>endstream
++endobj
++xref
++0 12
++0000000000 65535 f
++0000000061 00000 n
++0000000112 00000 n
++0000000219 00000 n
++0000000331 00000 n
++0000000446 00000 n
++0000000640 00000 n
++0000000834 00000 n
++0000000902 00000 n
++0000001275 00000 n
++0000001340 00000 n
++0000004521 00000 n
++trailer
++<<
++/ID
++[<a8f118002d6fd8b1daaf8b897f8fb0a6><a8f118002d6fd8b1daaf8b897f8fb0a6>]
++% ReportLab generated PDF document -- digest (opensource)
++
++/Info 8 0 R
++/Root 7 0 R
++/Size 12
++>>
++startxref
++6718
++%%EOF
ddiff --git a/public/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf b/public/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..e237904
BBinary files /dev/null and b/public/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Oct_2026_68186113.pdf b/public/Alex_Frison_Resume_Oct_2026_68186113.pdf
ddeleted file mode 100644
iindex 10f61a9..0000000
BBinary files a/public/Alex_Frison_Resume_Oct_2026_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..bc5c26f
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_amber_68186113.pdf b/public/Alex_Frison_Resume_Themed_amber_68186113.pdf
ddeleted file mode 100644
iindex 284c138..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_amber_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..e1aa6a4
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_cyan_68186113.pdf b/public/Alex_Frison_Resume_Themed_cyan_68186113.pdf
ddeleted file mode 100644
iindex 035d6c7..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_cyan_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_green_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_green_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..022576f
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_green_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_green_68186113.pdf b/public/Alex_Frison_Resume_Themed_green_68186113.pdf
ddeleted file mode 100644
iindex 1c4b8a0..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_green_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..2e5ad3d
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_indigo_68186113.pdf b/public/Alex_Frison_Resume_Themed_indigo_68186113.pdf
ddeleted file mode 100644
iindex 0557280..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_indigo_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..8b173ba
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_lime_68186113.pdf b/public/Alex_Frison_Resume_Themed_lime_68186113.pdf
ddeleted file mode 100644
iindex 7b2cfde..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_lime_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..abc46f7
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_orange_68186113.pdf b/public/Alex_Frison_Resume_Themed_orange_68186113.pdf
ddeleted file mode 100644
iindex a3c5b1a..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_orange_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..22be974
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_pink_68186113.pdf b/public/Alex_Frison_Resume_Themed_pink_68186113.pdf
ddeleted file mode 100644
iindex dd035f1..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_pink_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_red_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_red_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..10e3162
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_red_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_red_68186113.pdf b/public/Alex_Frison_Resume_Themed_red_68186113.pdf
ddeleted file mode 100644
iindex a48d991..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_red_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..394e6b4
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_teal_68186113.pdf b/public/Alex_Frison_Resume_Themed_teal_68186113.pdf
ddeleted file mode 100644
iindex a92bdc0..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_teal_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..becb154
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_violet_68186113.pdf b/public/Alex_Frison_Resume_Themed_violet_68186113.pdf
ddeleted file mode 100644
iindex e2f7d37..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_violet_68186113.pdf and /dev/null differ
ddiff --git a/public/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf b/public/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf
nnew file mode 100644
iindex 0000000..12ba750
BBinary files /dev/null and b/public/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf differ
ddiff --git a/public/Alex_Frison_Resume_Themed_yellow_68186113.pdf b/public/Alex_Frison_Resume_Themed_yellow_68186113.pdf
ddeleted file mode 100644
iindex 4dc0128..0000000
BBinary files a/public/Alex_Frison_Resume_Themed_yellow_68186113.pdf and /dev/null differ
ddiff --git a/resume/build_ats_resume.py b/resume/build_ats_resume.py
iindex 777d107..35bfd19 100644
---- a/resume/build_ats_resume.py
++++ b/resume/build_ats_resume.py
@@@ -1,14 +1,15 @@
#!/usr/bin/env python3
"""Build plain, one-column résumés for applicant tracking systems, from the same content as the site and the designed
--PDF (build_resume.py), in two versions aimed at different roles:
++PDF (build_resume.py), in versions aimed at different roles:
public/Alex_Frison_Resume_Engineering.pdf software engineering (full-stack, payments, identity, streaming)
public/Alex_Frison_Resume_Data_Analyst.pdf data / BI / operations analyst
++ public/Alex_Frison_Resume_IT_Support.pdf IT, help desk and technical support (near-home jobs)
The designed PDF is multi-column with icons; a tracking system reads its text layer column by column and scrambles it.
These have one column, real text in reading order, standard fonts and plain section names, so a parser gets every
line right. Facts only come from src/data/profile.json and src/content/; what differs per version is the headline, the
--summary, which skills lead and how many bullets each role keeps.
++summary, which skills lead and which bullets each role keeps (a count, or the bullets' indexes).
Run from the repo root: python3 resume/build_ats_resume.py
"""
@@@ -79,6 +80,32 @@ VERSIONS = {
"founder": 2, "earlier": 1},
"projects": ["irdr-autonomous-bot", "irdr-quality-recap", "amazon-userscripts"],
},
++ "support": {
++ "file": "Alex_Frison_Resume_IT_Support.pdf",
++ "headline": "IT and Technical Support: Linux and servers, hardware troubleshooting, customer support and escalations",
++ "summary": (
++ "Hands-on technical generalist in Arlington, WA. Ten years running my own company's servers: self-built "
++ "and colocated 1U and 2U hardware, Linux, nginx and DDoS resilience, and later a full infrastructure "
++ "migration with zero downtime and zero data loss. At Amazon Robotics I investigated recurring pod, drive-unit "
++ "and workstation defects and escalated them to RME; as an ICQA data analyst I built tools other associates "
++ "used every day. Three years at Verizon Wireless on phones, plans and accessories, promoted to Assistant "
++ "Manager running inventory and escalations. I document what I fix: incident write-ups, runbooks and "
++ "support docs."),
++ "skills": [
++ ("Support and troubleshooting", ["hardware and electro-mechanical troubleshooting", "root-cause analysis",
++ "incident documentation", "escalations", "support docs and runbooks"]),
++ ("Systems", ["Linux", "server hardware (self-built and colocated 1U / 2U)", "nginx", "systemd", "Docker",
++ "Cloudflare", "cloud storage (Backblaze B2 / R2 / S3 APIs)", "zero-downtime migrations"]),
++ ("Network and web", ["HAR network-trace debugging (HARTools)", "DDoS resilience", "WebSockets", "TURN / coturn"]),
++ ("Scripting and automation", ["Python", "JavaScript / TypeScript", "SQL", "PHP", "TamperMonkey userscripts",
++ "Excel / Power Query / VBA"]),
++ ("People", ["customer retention and consultative sales", "coaching a team of 4 to 8", "opens, closes and inventory",
++ "technical writing"]),
++ ],
++ "bullets": {"PowerChat": 1, "OpenVibe": [0, 2], "Amazon/analytics": [0, 2], "Amazon/operations": 2,
++ "Verizon Wireless": 3, "founder": 2},
++ "projects": ["amazon-userscripts", "hartools", "openvibe-cutover", "mp-select-mini"],
++ },
}
@@@ -105,12 +132,14 @@ def plain(s: str) -> str:
.replace("‘", "'").replace("’", "'").replace("“", '"').replace("”", '"'))
--def bullets_for(role: dict, counts: dict) -> int:
++def bullets_for(role: dict, counts: dict) -> list[str]:
key = role["company"] if role["company"] in counts else (
f"Amazon/{role.get('kind')}" if role["company"] == "Amazon" else role.get("kind", ""))
if role["company"].startswith("OpenVibe"):
key = "OpenVibe"
-- return counts.get(key, counts.get(role.get("kind", ""), 2))
++ pick = counts.get(key, counts.get(role.get("kind", ""), 2))
++ bullets = role.get("bullets", [])
++ return [bullets[i] for i in pick if i < len(bullets)] if isinstance(pick, list) else bullets[:pick]
def build(version: str, data: dict) -> Path:
@@@ -143,11 +172,11 @@ def build(version: str, data: dict) -> Path:
story.append(Paragraph(f"<b>{plain(group)}:</b> {plain(', '.join(items))}", st["body"]))
story += [Paragraph("EXPERIENCE", st["h"]), rule()]
for r in data["roles"]:
-- n = bullets_for(r, v["bullets"])
++ chosen = bullets_for(r, v["bullets"])
head = Paragraph(plain(f"{r['role']}, {r['company']}"), st["role"])
meta = Paragraph(plain(" | ".join(x for x in [r.get("location", ""), f"{r['start']} to {r['end']}"] if x)), st["meta"])
-- items = [Paragraph(plain(b), st["bullet"], bulletText="•") for b in r.get("bullets", [])[:n]]
-- if r.get("stack") and version == "engineering" and n >= 2:
++ items = [Paragraph(plain(b), st["bullet"], bulletText="•") for b in chosen]
++ if r.get("stack") and version == "engineering" and len(chosen) >= 2:
items.append(Paragraph(plain("Stack: " + ", ".join(r["stack"])), st["meta"]))
story.append(KeepTogether([head, meta, *items[:1]]))
story += items[1:]
ddiff --git a/resume/build_resume.py b/resume/build_resume.py
iindex 833df65..5ceeff2 100644
---- a/resume/build_resume.py
++++ b/resume/build_resume.py
@@@ -1280,8 +1280,9 @@ def main() -> None:
json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
# the one-column versions for tracking systems (build_ats_resume.py) live next to these: never swept
++ from build_ats_resume import VERSIONS as ATS_VERSIONS
keep |= {p for p in (ROOT / "public").glob("Alex_Frison_Resume_*.pdf")
-- if p.name in ("Alex_Frison_Resume_Engineering.pdf", "Alex_Frison_Resume_Data_Analyst.pdf")}
++ if p.name in {v["file"] for v in ATS_VERSIONS.values()}}
clean_old_files("Alex_Frison_Resume_*.pdf", keep)
print(f"\ncontent hash: {h} (filenames only change when the résumé content does)")
print(f"text font: {FONT['label']}; icons: Font Awesome 4")
ddiff --git a/src/components/OpenVibeBento.astro b/src/components/OpenVibeBento.astro
iindex 95c2df4..e1f7376 100644
---- a/src/components/OpenVibeBento.astro
++++ b/src/components/OpenVibeBento.astro
@@@ -1,6 +1,11 @@
---
import profile from "../data/profile.json";
--const sites = profile.network;
++// The "sites" tile stands for everything past the six core services (profile.networkLive); its
++// text carries {more} and {total} so the counts come from that list and never drift.
++const more = profile.networkLive.length;
++const total = profile.network.filter((s) => s.key !== "sites").length + more;
++const fill = (t: string) => t.replace("{more}", String(more)).replace("{total}", String(total));
++const sites = profile.network.map((s) => ({ ...s, name: fill(s.name), blurb: fill(s.blurb), stat: fill(s.stat) }));
const big = sites[0];
const rest = sites.slice(1);
const icons: Record<string, string> = {
@@@ -17,7 +22,7 @@ const nodes = [
{ key: "tools", label: "Tools", x: 400, y: 50 },
{ key: "games", label: "Games", x: 400, y: 372 },
{ key: "community", label: "Community", x: 670, y: 90 },
-- { key: "sites", label: "13 Sites", x: 670, y: 330 },
++ { key: "sites", label: `${more} more`, x: 670, y: 330 },
];
const edges = [
["live", "media"],
@@@ -61,7 +66,7 @@ const edges = [
<div class="arch card" data-reveal>
<div class="arch__copy">
<div class="eyebrow"><i class="fa-solid fa-diagram-project" aria-hidden="true"></i> How it fits together</div>
-- <h3>One login, one wallet, seven products that feel like one.</h3>
++ <h3>One login, one wallet, {total} sites that feel like one.</h3>
<details class="arch__more" open>
<summary><span class="arch__more-closed">Read more</span><span class="arch__more-open">Show less</span></summary>
<p>Sign in once and every OpenVibe site knows who you are, what you own and what you look like. The identity service in the middle handles accounts, security and the shared virtual currency; every other product plugs into it through clear, documented APIs. That's the same playbook I bring to any company: secure single sign-on, payments and APIs designed so new products can be added without breaking the old ones.</p>
@@@ -73,7 +78,7 @@ const edges = [
<li><i class="fa-solid fa-shield-halved"></i> Every migration is rehearsed as a dry run before it touches real users</li>
</ul>
</div>
-- <svg class="arch__svg" viewBox="0 0 800 420" role="img" aria-label="Diagram: OpenVibe.Network at the center connected to Live, Media, Tools, Games, Community and 13 static sites; Live also talks to Media.">
++ <svg class="arch__svg" viewBox="0 0 800 420" role="img" aria-label={`Diagram: OpenVibe.Network at the center connected to Live, Media, Tools, Games, Community and ${more} more sites; Live also talks to Media.`}>
<defs>
<linearGradient id="agrad" x1="0" y1="0" x2="1" y2="0"><stop offset="0" stop-color="var(--accent)"/><stop offset="1" stop-color="var(--accent-2)"/></linearGradient>
<filter id="aglow" x="-50%" y="-50%" width="200%" height="200%"><feGaussianBlur stdDeviation="6" result="b"/><feMerge><feMergeNode in="b"/><feMergeNode in="SourceGraphic"/></feMerge></filter>
ddiff --git a/src/components/WorkShowcase.astro b/src/components/WorkShowcase.astro
iindex e0bf9b5..b06abc5 100644
---- a/src/components/WorkShowcase.astro
++++ b/src/components/WorkShowcase.astro
@@@ -23,11 +23,19 @@ const netIcon: Record<string, string> = {
games: "fa-solid fa-gamepad", community: "fa-solid fa-comments", sites: "fa-solid fa-globe",
};
++// Everything past the six core services: live sites, shown as chips under the bento.
++const liveMore = profile.networkLive;
++const liveTotal = profile.network.filter((s) => s.key !== "sites").length + liveMore.length;
++const liveGroups = [
++ { label: "Also live", icon: "fa-solid fa-circle-check", items: liveMore.filter((n) => n.group === "people") },
++ { label: "For builders", icon: "fa-solid fa-code", items: liveMore.filter((n) => n.group === "builders") },
++];
++
const groups = [
{
key: "openvibe", tone: "a", anchor: "network", icon: "fa-solid fa-diagram-project",
-- title: "The OpenVibe network", blurb: "Seven live services behind one sign-in, all built and run by me since 2026.",
-- count: profile.network.length, allHref: "/portfolio/?cat=openvibe,infra", allLabel: "All OpenVibe case notes",
++ title: "The OpenVibe network", blurb: `${liveTotal} live sites behind one sign-in, all built and run by me since 2026.`,
++ count: liveTotal, allHref: "/portfolio/?cat=openvibe,infra", allLabel: "All OpenVibe case notes",
items: [] as { href: string; external: boolean; image?: string | null; icon: string; title: string; sub: string; text: string; meta: string; soon?: boolean }[],
},
{
@@@ -73,16 +81,16 @@ const groups = [
<a class="wk-group__all" href={g.allHref}><i class="fa-solid fa-grip" aria-hidden="true"></i> <span>{g.allLabel}</span> <i class="fa-solid fa-arrow-right wk-group__arrow" aria-hidden="true"></i></a>
</header>
{g.key === "openvibe" && <OpenVibeBento />}
-- {g.key === "openvibe" && (
-- <div class="wk-planned" aria-label="Planned OpenVibe sites">
-- <span class="wk-planned__label"><i class="fa-solid fa-clock" aria-hidden="true"></i> Coming soon</span>
++ {g.key === "openvibe" && liveGroups.map((lg) => (
++ <div class="wk-planned" aria-label={`${lg.label}: more OpenVibe sites`}>
++ <span class="wk-planned__label"><i class={lg.icon} aria-hidden="true"></i> {lg.label}</span>
<ul class="wk-planned__list">
-- {profile.networkPlanned.map((n) => (
-- <li><a href={n.url} target="_blank" rel="noopener" title={`${n.name}: ${n.role} (placeholder page)`}>{n.name.replace("OpenVibe.", ".")}<small>{n.role}</small></a></li>
++ {lg.items.map((n) => (
++ <li><a href={n.url} target="_blank" rel="noopener" title={`${n.name}: ${n.role}`}>{n.name.replace("OpenVibe.", ".")}<small>{n.role}</small></a></li>
))}
</ul>
</div>
-- )}
++ ))}
<ul class="wk-grid" data-stagger hidden={g.items.length === 0} style={`--cols:${Math.min(Math.max(g.items.length, 1), 4)};--cols-t:${Math.min(Math.max(g.items.length, 1), 2)}`}>
{g.items.map((it) => (
<li class={`wk-card ${it.image ? "wk-card--img" : ""} ${it.soon ? "wk-card--soon" : ""}`}>
@@@ -167,8 +175,9 @@ const groups = [
.mono { font-family: var(--font-mono); }
.wk-card__soon { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; font-size: .6rem; letter-spacing: .08em; text-transform: uppercase; padding: 2px 8px; border-radius: 999px; color: var(--accent-3); background: rgba(var(--accent-3-rgb), .12); border: 1px solid rgba(var(--accent-3-rgb), .35); white-space: nowrap; }
.wk-card--soon .wk-card__link { border-style: dashed; }
-- /* Planned domains: every one already has a truthful "coming soon" page, so the chips link there. */
-- .wk-planned { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; padding: 12px 14px; border-radius: 14px; border: 1px dashed var(--border-strong); background: var(--surface); }
++ /* The rest of the live network, as chips that open each site (2026-10: every one of them serves its product). */
++ .wk-planned { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 14px; padding: 12px 14px; border-radius: 14px; border: 1px solid var(--border); background: var(--surface); }
++ .wk-planned + .wk-planned { margin-top: 8px; }
.wk-planned__label { display: inline-flex; align-items: center; gap: 7px; font: 600 .68rem var(--font-mono); letter-spacing: .14em; text-transform: uppercase; color: var(--accent-3); }
.wk-planned__list { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.wk-planned__list a { display: inline-flex; align-items: baseline; gap: 6px; padding: 5px 10px; border-radius: 999px; border: 1px solid var(--border); background: var(--surface-2); color: var(--fg); font: 600 .74rem var(--font-body); text-decoration: none; transition: border-color .25s var(--ease), transform .25s var(--ease); }
ddiff --git a/src/content/experience/06-gameserverstats.json b/src/content/experience/06-gameserverstats.json
iindex 06bb024..ae61dbc 100644
---- a/src/content/experience/06-gameserverstats.json
++++ b/src/content/experience/06-gameserverstats.json
@@@ -7,7 +7,7 @@
"order": 6,
"bullets": [
"Built analytics platforms tracking 200,000+ live game servers: ingestion, search, ranking, dashboards and licensed partner APIs. Ranked first on Google for several major titles.",
-- "Self-built and collocated 1U and 2U servers in datacenters; designed for scale and DDoS resilience and ran the whole LLC, from licenses and taxes to partner relations.",
++ "Self-built and colocated 1U and 2U servers in datacenters; designed for scale and DDoS resilience and ran the whole LLC, from licenses and taxes to partner relations.",
"Rebuilding the idea in 2026 as GameServerLists (launching soon): a 14-service, game-agnostic server-discovery platform with multi-region probes, time-weighted ranking, an anti-fraud integrity engine and a tiered public API."
],
"stack": [
ddiff --git a/src/data/profile.json b/src/data/profile.json
iindex b52e338..b42efc4 100644
---- a/src/data/profile.json
++++ b/src/data/profile.json
@@@ -36,11 +36,11 @@
"githubDisplay": "github.com/OpenVibers",
"linkedin": "https://www.linkedin.com/in/fris/",
"linkedinDisplay": "linkedin.com/in/fris",
-- "resumePdf": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",
++ "resumePdf": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",
"availability": "Open to pretty much anything — engineering, platform, analytics, ops, sales, whatever fits · remote or Pacific Northwest",
"status": {
"now": "Contract software engineer at PowerChat, building its developer platform and payments program.",
-- "also": "Founder and sole engineer of OpenVibe: seven services, 20+ domains, one operator."
++ "also": "Founder and sole engineer of OpenVibe: 39 live sites behind one login, one operator."
},
"pitch": "I'm a software engineer and business owner who builds complete products and keeps them running: websites, platforms, payment systems, AI agents and the automation behind them. Over the last decade I've shipped 25+ sites and services, rebuilt a live payments program across five processors, led a sales team, and turned an Amazon data-analysis job into tooling other Amazon associates used every day. Give me a problem or a rough idea and I'll deliver the whole thing: design, code, deployment and support.",
"seeking": [
@@@ -68,18 +68,42 @@
{ "key": "tools", "name": "OpenVibe.Tools", "url": "https://openvibe.tools", "role": "Tools suite", "blurb": "169 server-rendered tools across 8 families, a host registry with custom-domain mirrors, and full SEO plumbing.", "stat": "169 tools · 8 families", "image": "/assets/img/sites/tools.webp" },
{ "key": "games", "name": "OpenVibe.Games", "url": "https://openvibe.games", "role": "Scraplandia", "blurb": "Browser multiplayer survival game: authoritative server, client prediction, Havok physics, physgun building.", "stat": "56k LOC TypeScript", "image": "/assets/img/sites/games.webp" },
{ "key": "community", "name": "OpenVibe.Community", "url": "https://openvibe.community", "role": "Pastes & posts", "blurb": "Server-rendered paste hub with forks, search, feeds and silent SSO, proxied to Live so identity stays consistent.", "stat": "No DB of its own", "image": "/assets/img/sites/community.webp" },
-- { "key": "sites", "name": "OpenVibe.Sites", "url": "https://openvibe.chat", "role": "13 front doors", "blurb": "One catalog and one template generate the pages and nginx configs for openvibe.chat, .codes, .wiki, .news and more.", "stat": "13 domains · 1 build", "image": null }
++ { "key": "sites", "name": "{more} more live sites", "url": "https://openvibe.network", "role": "The rest of the network", "blurb": "Chat, tips, memberships, video, blogs, wikis, cited news, restreaming, job and home search, robot control, coding agents and a developer platform, all on the same login.", "stat": "{total} live sites · 1 login", "image": null }
],
-- "networkPlanned": [
-- { "name": "OpenRestream", "url": "https://openre.stream", "role": "Restreaming" },
-- { "name": "OpenVibe.Tips", "url": "https://openvibe.tips", "role": "Creator tips" },
-- { "name": "OpenVibe.VIP", "url": "https://openvibe.vip", "role": "Memberships" },
-- { "name": "OpenVibe.Host", "url": "https://openvibe.host", "role": "Hosting" },
-- { "name": "OpenVibe.News", "url": "https://openvibe.news", "role": "News" },
-- { "name": "OpenVibe.Reviews", "url": "https://openvibe.reviews", "role": "Reviews" },
-- { "name": "OpenVibe.Deals", "url": "https://openvibe.deals", "role": "Deals" },
-- { "name": "OpenVibe.Coupons", "url": "https://openvibe.coupons", "role": "Coupons" },
-- { "name": "OpenVibe.Trade", "url": "https://openvibe.trade", "role": "Markets" }
++ "networkLive": [
++ { "name": "OpenRestream", "url": "https://openre.stream", "role": "Restreaming", "group": "people" },
++ { "name": "OpenVibe.Chat", "url": "https://openvibe.chat", "role": "Global chat", "group": "people" },
++ { "name": "OpenVibe.Tips", "url": "https://openvibe.tips", "role": "Creator tips", "group": "people" },
++ { "name": "OpenVibe.VIP", "url": "https://openvibe.vip", "role": "Memberships", "group": "people" },
++ { "name": "OpenVibe.Video", "url": "https://openvibe.video", "role": "Video", "group": "people" },
++ { "name": "OpenVibe.Pics", "url": "https://openvibe.pics", "role": "Pictures", "group": "people" },
++ { "name": "OpenVibe.Download", "url": "https://openvibe.download", "role": "File sharing", "group": "people" },
++ { "name": "OpenVibe.Blog", "url": "https://openvibe.blog", "role": "Blogs", "group": "people" },
++ { "name": "OpenVibe.Wiki", "url": "https://openvibe.wiki", "role": "Wikis", "group": "people" },
++ { "name": "OpenVibe.News", "url": "https://openvibe.news", "role": "Cited news", "group": "people" },
++ { "name": "OpenVibe.Reviews", "url": "https://openvibe.reviews", "role": "Sourced reviews", "group": "people" },
++ { "name": "OpenVibe.Help", "url": "https://openvibe.help", "role": "Help and support", "group": "people" },
++ { "name": "OpenVibe.Quest", "url": "https://openvibe.quest", "role": "Quests", "group": "people" },
++ { "name": "OpenVibe.Play", "url": "https://play.openvibe.network", "role": "Achievements", "group": "people" },
++ { "name": "OpenVibe.Inventory", "url": "https://inventory.openvibe.network", "role": "Items", "group": "people" },
++ { "name": "OpenVibe.Deals", "url": "https://openvibe.deals", "role": "Deals", "group": "people" },
++ { "name": "OpenVibe.Coupons", "url": "https://openvibe.coupons", "role": "Coupons", "group": "people" },
++ { "name": "OpenVibe.Trade", "url": "https://openvibe.trade", "role": "Market data", "group": "people" },
++ { "name": "OpenVibe.Work", "url": "https://openvibe.work", "role": "Job search", "group": "people" },
++ { "name": "OpenVibe.Rent", "url": "https://openvibe.rent", "role": "Rentals", "group": "people" },
++ { "name": "OpenVibe.Homes", "url": "https://openvibe.homes", "role": "Home buying", "group": "people" },
++ { "name": "OpenVibe.Food", "url": "https://openvibe.food", "role": "Food and meals", "group": "people" },
++ { "name": "OpenVibe.Host", "url": "https://openvibe.host", "role": "Hosting compared", "group": "people" },
++ { "name": "OpenVibe.Services", "url": "https://openvibe.services", "role": "Developer platform", "group": "builders" },
++ { "name": "OpenVibe.AI", "url": "https://ai.openvibe.services", "role": "AI layer", "group": "builders" },
++ { "name": "OpenVibe.Codes", "url": "https://openvibe.codes", "role": "Coding agents", "group": "builders" },
++ { "name": "OpenVibe.Actor", "url": "https://openvibe.actor", "role": "Task agents", "group": "builders" },
++ { "name": "OpenVibe.Bot", "url": "https://openvibe.bot", "role": "Robot control", "group": "builders" },
++ { "name": "OpenVibe.Run", "url": "https://openvibe.run", "role": "Jobs on nodes", "group": "builders" },
++ { "name": "OpenVibe.Events", "url": "https://openvibe.events", "role": "Event delivery", "group": "builders" },
++ { "name": "OpenVibe.Watch", "url": "https://openvibe.watch", "role": "Watches", "group": "builders" },
++ { "name": "OpenVibe.Space", "url": "https://openvibe.space", "role": "Static hosting", "group": "builders" },
++ { "name": "OpenVibe.Zone", "url": "https://openvibe.zone", "role": "S3 storage", "group": "builders" }
],
"education": "Snohomish High School, WA. AP Statistics, Calculus, English, Creative Writing and Spanish. Self-taught engineer since age 12: systems, infrastructure, graphics, hardware and whatever the problem needs.",
"capabilities": ["websites", "platforms", "APIs", "dashboards", "AI features", "automations", "identity systems", "payment systems", "real-time chat", "streaming infra", "whatever you need"]
ddiff --git a/src/data/theme-pdf-manifest.json b/src/data/theme-pdf-manifest.json
iindex 969cd43..3cdb3b2 100644
---- a/src/data/theme-pdf-manifest.json
++++ b/src/data/theme-pdf-manifest.json
@@@ -1,15 +1,15 @@
{
-- "amber": "/Alex_Frison_Resume_Themed_amber_68186113.pdf",
-- "blue": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",
-- "cyan": "/Alex_Frison_Resume_Themed_cyan_68186113.pdf",
-- "default": "/Alex_Frison_Resume_Oct_2026_68186113.pdf",
-- "green": "/Alex_Frison_Resume_Themed_green_68186113.pdf",
-- "indigo": "/Alex_Frison_Resume_Themed_indigo_68186113.pdf",
-- "lime": "/Alex_Frison_Resume_Themed_lime_68186113.pdf",
-- "orange": "/Alex_Frison_Resume_Themed_orange_68186113.pdf",
-- "pink": "/Alex_Frison_Resume_Themed_pink_68186113.pdf",
-- "red": "/Alex_Frison_Resume_Themed_red_68186113.pdf",
-- "teal": "/Alex_Frison_Resume_Themed_teal_68186113.pdf",
-- "violet": "/Alex_Frison_Resume_Themed_violet_68186113.pdf",
-- "yellow": "/Alex_Frison_Resume_Themed_yellow_68186113.pdf"
++ "amber": "/Alex_Frison_Resume_Themed_amber_38b37ff8.pdf",
++ "blue": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",
++ "cyan": "/Alex_Frison_Resume_Themed_cyan_38b37ff8.pdf",
++ "default": "/Alex_Frison_Resume_Oct_2026_38b37ff8.pdf",
++ "green": "/Alex_Frison_Resume_Themed_green_38b37ff8.pdf",
++ "indigo": "/Alex_Frison_Resume_Themed_indigo_38b37ff8.pdf",
++ "lime": "/Alex_Frison_Resume_Themed_lime_38b37ff8.pdf",
++ "orange": "/Alex_Frison_Resume_Themed_orange_38b37ff8.pdf",
++ "pink": "/Alex_Frison_Resume_Themed_pink_38b37ff8.pdf",
++ "red": "/Alex_Frison_Resume_Themed_red_38b37ff8.pdf",
++ "teal": "/Alex_Frison_Resume_Themed_teal_38b37ff8.pdf",
++ "violet": "/Alex_Frison_Resume_Themed_violet_38b37ff8.pdf",
++ "yellow": "/Alex_Frison_Resume_Themed_yellow_38b37ff8.pdf"
}