Hi, I'm Kimberly, an aspiring data analyst with a degree in Statistics and Data Science. I turn messy numbers into clear, curious stories, from spreadsheets to code.
I'm also the voice behind , my media outlet where I talk about space business, statistics, space news, and tech.
DATA ENTHUSIASTLos AngelesPST
Tap the lamp, tap the moon to visit Data In Orbit, tap the glowing books to peek inside, and drag the little trinkets around.
What's New!
Everything I've posted or built in the past two weeks.
Data In Orbit
Posts on space news, data & statistics, and space business.
Work
A collection of analytical and creative projects
Let's connect
Have a dataset, a question, or a project in mind? Say hi.
',hook+'');appLoaded=true;
}
appOpener=opener||null;appModal.hidden=false;document.body.style.overflow='hidden';$('#appclose').focus({preventScroll:true});
}
function closeApp(){appModal.hidden=true;document.body.style.overflow='';try{appOpener&&appOpener.focus({preventScroll:true})}catch(e){}}
$('#panel').addEventListener('click',e=>{const b=e.target.closest('[data-app]');if(b)openApp(b)});
$('#appclose').addEventListener('click',closeApp);
appModal.addEventListener('click',e=>{if(e.target===appModal)closeApp()});
document.addEventListener('keydown',e=>{if(e.key==='Escape'&&!appModal.hidden)closeApp()});
window.addEventListener('message',e=>{if(e.data==='vm-close'&&e.source===appFrame.contentWindow&&!appModal.hidden)closeApp()});
$('#filters').onclick=e=>{const b=e.target.closest('button');if(b){filter=b.dataset.f;drawList()}};
$('#list').onclick=e=>{const b=e.target.closest('.row');if(b){sel=+b.dataset.i;drawList()}};
$('#list').onmouseover=e=>{const b=e.target.closest('.row');if(b&&Date.now()>hoverLock&&+b.dataset.i!==sel&&matchMedia('(hover:hover)').matches){sel=+b.dataset.i;drawList()}};
/* posts: same layout as the project boards. Body text stays collapsed until the post is tapped. */
let pfilter='All',psel=0,pexpand=false;
function drawPosts(){
const list=$('#plist'),panel=$('#ppanel'),fl=$('#pfilters');
if(!POSTS.length){
fl.innerHTML='';
list.innerHTML='
Coming soonNo posts yet. New writing will show up here.
';
panel.innerHTML=art('post')+"
Fresh posts on the way
Space news, data & statistics, and space business writing will land here.
`).join('')+(p.url?`Read the post`:'');
panel.innerHTML=`
${body}
`;
}
$('#pfilters').onclick=e=>{const b=e.target.closest('button');if(b){pfilter=b.dataset.f;drawPosts()}};
$('#plist').onclick=e=>{const b=e.target.closest('.row[data-i]');if(b){psel=+b.dataset.i;pexpand=false;drawPosts()}};
$('#plist').onmouseover=e=>{const b=e.target.closest('.row[data-i]');if(b&&Date.now()>hoverLock&&+b.dataset.i!==psel&&matchMedia('(hover:hover)').matches){psel=+b.dataset.i;pexpand=false;drawPosts()}};
$('#ppanel').addEventListener('click',e=>{if(e.target.closest('#postToggle')){pexpand=!pexpand;drawPosts()}});
/* skills marquee, links, socials */
const chips=SKILLS.map(s=>`${s}`).join('');
$('#track').innerHTML=chips+chips;
$('#li').href=CONFIG.linkedin;
const ico={mail:'',
li:$('#li').innerHTML};
const zeroFix=s=>s.replace(/0/g,'0');
$('#links').innerHTML=`${ico.mail}Email${zeroFix(CONFIG.email)}${ico.li}LinkedInKimberly Gonzalez`;
/* live Los Angeles clock, also drives the window's day/night sky */
const fmt=new Intl.DateTimeFormat('en-US',{timeZone:'America/Los_Angeles',hour:'numeric',minute:'2-digit'});
const hourFmt=new Intl.DateTimeFormat('en-US',{timeZone:'America/Los_Angeles',hour:'numeric',hourCycle:'h23'});
const tick=()=>{$('#clock').textContent=fmt.format(new Date());const h=+hourFmt.format(new Date());$('#room').classList.toggle('night',h>=20||h<6)};tick();setInterval(tick,20000);
/* page routing: click-driven (no hash navigation, so it never tries to leave the page) */
function show(id){
if(!['about','orbit','projects','connect'].includes(id))id='about';
document.querySelectorAll('.page').forEach(s=>s.classList.toggle('on',s.id===id));
document.querySelectorAll('[data-nav]').forEach(a=>a.classList.toggle('on',a.dataset.nav===id));
if(id==='projects')drawList();
if(id==='orbit')drawPosts();
document.title='Kimberly Gonzalez | '+id[0].toUpperCase()+id.slice(1);
window.scrollTo(0,0);
try{history.replaceState(null,'','#'+id)}catch(e){}
}
document.querySelectorAll('[data-nav]').forEach(el=>el.addEventListener('click',()=>show(el.dataset.nav)));
/* ===== What's New!: posts and projects added in the past 2 weeks (uses each item's "added" date) ===== */
const WN_DAYS=14;
function drawWhatsNew(){
const grid=$('#wngrid');if(!grid)return;
const now=new Date(),today=new Date(now.getFullYear(),now.getMonth(),now.getDate());
const ago=d=>{const[y,m,dd]=String(d).split('-').map(Number);return Math.round((today-new Date(y,m-1,dd))/864e5)};
const items=[];
PROJECTS.forEach((p,i)=>{if(p.added){const a=ago(p.added);if(a>=0&&a<=WN_DAYS)items.push({k:'project',i,a,p})}});
POSTS.forEach((p,i)=>{if(p.added){const a=ago(p.added);if(a>=0&&a<=WN_DAYS)items.push({k:'post',i,a,p})}});
items.sort((x,y)=>x.a-y.a);
if(!items.length){grid.innerHTML='
Nothing new in the past two weeks. Check back soon!
';return}
const when=a=>a===0?'Added today':a===1?'Added yesterday':'Added '+a+' days ago';
grid.innerHTML=items.map(o=>{
const thumb=o.p.image?``:art(o.p.art||'post');
const label=o.k==='project'?'Project · '+o.p.tool:'Post'+(o.p.tag?' · '+o.p.tag:'');
return ``;
}).join('');
}
$('#wngrid').addEventListener('click',e=>{
const c=e.target.closest('.wncard');if(!c)return;
const i=+c.dataset.i;
hoverLock=Date.now()+1200;
if(c.dataset.k==='project'){filter='All';sel=i;show('projects')}
else{pfilter='All';psel=i;pexpand=false;show('orbit')}
});
drawWhatsNew();
(function(){
const el=$('#whatsnew');if(!el)return;
if(!('IntersectionObserver' in window)){el.classList.add('in');return}
const io=new IntersectionObserver(es=>{es.forEach(en=>{if(en.isIntersecting){el.classList.add('in');io.disconnect()}})},{threshold:.12});
io.observe(el);
})();
show((location.hash||'#about').slice(1));
/* draggable desk stickers */
const S={
mug:``,
chart:``,
spark:``,
glasses:``,
leaf:``,
pencil:``
};
const pos=[[3,26,-8],[91,24,10],[5,66,6],[88,58,-6],[93,82,12],[10,86,-12]];
Object.keys(S).forEach((k,i)=>{
const el=document.createElement('div');el.className='sticker';el.innerHTML=S[k];
el.style.left=pos[i][0]+'vw';el.style.top=pos[i][1]+'vh';el.style.rotate=pos[i][2]+'deg';el.setAttribute('aria-hidden','true');
let ox=0,oy=0;
el.addEventListener('pointerdown',e=>{const r=el.getBoundingClientRect();ox=e.clientX-r.left;oy=e.clientY-r.top;el.setPointerCapture(e.pointerId);el.classList.add('drag')});
el.addEventListener('pointermove',e=>{if(!el.classList.contains('drag'))return;
el.style.left=Math.max(0,Math.min(innerWidth-el.offsetWidth,e.clientX-ox))+'px';
el.style.top=Math.max(0,Math.min(innerHeight-el.offsetHeight,e.clientY-oy))+'px'});
const up=()=>el.classList.remove('drag');el.addEventListener('pointerup',up);el.addEventListener('pointercancel',up);
document.body.appendChild(el);
});
/* ===== interactive room ===== */
const BOOKS=[
{title:'My Story',topic:'Who I am',note:"Hi! My name is Kimberly Gonzalez, and I have a B.S. in Statistics and Data Science from the University of California, Los Angeles (UCLA). I am an aspiring data analyst and a fashion tech enthusiast! Welcome to my personal portfolio, where I share my projects and all my analytical interests!"}
];
const COL={S:'#EE6A5E',C:'#3D6FA8',B:'#6B4630',G:'#8A423C',Y:'#D9A441',T:'#3B6E71',P:'#6E4B5E'};
const SHELF=[
[120,[[15,70,'S',0],[11,58,'T'],[17,74,'B'],[13,62,'Y'],[16,68,'C'],[12,54,'P'],[14,66,'G']]],
[210,[[14,66,'C'],[18,76,'G'],[12,60,'S'],[16,70,'B'],[13,58,'Y'],[15,72,'T']]],
[300,[[17,72,'P'],[13,60,'C'],[15,68,'T'],[12,74,'S'],[18,66,'B'],[14,56,'Y']]],
[366,[[20,60,'G'],[16,52,'Y'],[22,62,'C'],[14,48,'T'],[18,56,'B']]]
];
let bk='';
SHELF.forEach(([base,row])=>{let x=468;row.forEach(([w,h,c,i])=>{const k=i!==undefined;
bk+=``;x+=w+3})});
$('#books').innerHTML=bk;
let cur=-1;
const card=$('#bookcard');
function renderCard(){
const b=BOOKS[cur];
card.innerHTML=`
${BOOKS.map((x,i)=>``).join('')}
`+
(b?`
${b.title}
${b.topic}
${b.note}
`
:'
Tap a book on the shelf, or pick one above, to read more.
');
document.querySelectorAll('.bk[data-i]').forEach(g=>g.classList.toggle('sel',+g.dataset.i===cur));
}
function pick(i){cur=i;renderCard()}
$('#books').addEventListener('click',e=>{const g=e.target.closest('.bk[data-i]');if(g)pick(+g.dataset.i)});
$('#books').addEventListener('keydown',e=>{const g=e.target.closest('.bk[data-i]');if(g&&(e.key==='Enter'||e.key===' ')){e.preventDefault();pick(+g.dataset.i)}});
card.addEventListener('click',e=>{const b=e.target.closest('button');if(b)pick(+b.dataset.i)});
renderCard();
/* desk lamp */
const room=$('#room'),lamp=$('#lamp');
function toggleLamp(){const off=room.classList.toggle('off');lamp.setAttribute('aria-pressed',String(!off));lamp.setAttribute('aria-label','Desk lamp, tap to turn '+(off?'on':'off'))}
lamp.addEventListener('click',toggleLamp);
lamp.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();toggleLamp()}});
/* polaroid photos taped to the wall, tap one to preview */
const WALL_PICS=[
{x:56,y:64,r:-7},{x:104,y:52,r:5},{x:60,y:118,r:6},{x:108,y:132,r:-5},{x:84,y:184,r:3}
];
$('#polawall').innerHTML=WALL_PICS.map((p,i)=>``).join('');
$('#polawall').addEventListener('click',e=>{if(e.target.closest('.wallpic'))showPolaroid()});
$('#polawall').addEventListener('keydown',e=>{if(e.target.closest('.wallpic')&&(e.key==='Enter'||e.key===' ')){e.preventDefault();showPolaroid()}});
/* draggable trinkets */
const ST=`stroke="#F2ECE2" stroke-width="2"`;
const TRK=[
{n:'Cactus',x:392,y:268,d:``},
{n:'Pen cup',x:305,y:268,d:``},
{n:'Star',x:426,y:268,d:``},
{n:'Globe',x:600,y:117,d:``},
{n:'Camera',x:598,y:207,d:``}
];
$('#trk').innerHTML=TRK.map(t=>`${t.d}`).join('');
/* polaroid popup */
const polaroid=$('#polaroid'),pback=$('#pback');
function showPolaroid(){polaroid.hidden=false;pback.hidden=false}
function hidePolaroid(){polaroid.hidden=true;pback.hidden=true}
$('#pclose').addEventListener('click',hidePolaroid);
pback.addEventListener('click',hidePolaroid);
const svgPt=e=>{const p=room.createSVGPoint();p.x=e.clientX;p.y=e.clientY;return p.matrixTransform(room.getScreenCTM().inverse())};
const place=(g,x,y)=>{x=Math.max(16,Math.min(624,x));y=Math.max(36,Math.min(404,y));g.dataset.x=x;g.dataset.y=y;g.setAttribute('transform',`translate(${x} ${y})`)};
let dragG=null,ox=0,oy=0,trkMoved=false,trkSX=0,trkSY=0;
$('#trk').addEventListener('pointerdown',e=>{const g=e.target.closest('.trk');if(!g)return;const p=svgPt(e);ox=p.x-+g.dataset.x;oy=p.y-+g.dataset.y;dragG=g;trkMoved=false;trkSX=e.clientX;trkSY=e.clientY;g.setPointerCapture(e.pointerId);g.classList.add('drag');$('#trk').appendChild(g)});
$('#trk').addEventListener('pointermove',e=>{if(!dragG)return;if(Math.abs(e.clientX-trkSX)>5||Math.abs(e.clientY-trkSY)>5)trkMoved=true;const p=svgPt(e);place(dragG,p.x-ox,p.y-oy)});
const drop=()=>{if(dragG){dragG.classList.remove('drag');if(!trkMoved&&dragG.dataset.n==='Camera')showPolaroid()}dragG=null};
$('#trk').addEventListener('pointerup',drop);$('#trk').addEventListener('pointercancel',drop);
$('#trk').addEventListener('keydown',e=>{const g=e.target.closest('.trk');const d={ArrowLeft:[-8,0],ArrowRight:[8,0],ArrowUp:[0,-8],ArrowDown:[0,8]}[e.key];if(g&&d){e.preventDefault();place(g,+g.dataset.x+d[0],+g.dataset.y+d[1])}
if(g&&(e.key==='Enter'||e.key===' ')&&g.dataset.n==='Camera'){e.preventDefault();showPolaroid()}});
const moon=$('#moon');
moon.addEventListener('click',()=>show('orbit'));
moon.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();show('orbit')}});
/* ===== Kimberly: glances at her laptop, then looks around the room (hands and mug stay put) ===== */
(function(){
if(matchMedia('(prefers-reduced-motion: reduce)').matches)return;
const g=id=>document.getElementById(id);
const head=g('head'),hairB=g('hairBack'),eL=g('eyeL'),eR=g('eyeR'),roomEl=g('room');
if(!(head&&hairB&&eL&&eR&&roomEl))return;
const clamp=(v,a,b)=>Math.max(a,Math.min(b,v)),rnd=(a,b)=>a+Math.random()*(b-a);
const SCREEN={gx:0,gy:3.4,hx:0,hy:1.2,hr:0}; /* eyes down toward the laptop */
const S={...SCREEN}; /* what is drawn (smoothed) */
const G={...SCREEN}; /* where she is heading */
let t=0,mode='screen',until=rnd(2.5,4.5),looksLeft=0,lastLook=-1;
/* --- laptop: calm reading drift --- */
const rd={next:0,dx:0,dy:0};
function stepReading(){
if(t>rd.next){rd.next=t+rnd(.5,1.3);rd.dx=rnd(-1.3,1.3);rd.dy=rnd(-.4,.5)}
G.gx=SCREEN.gx+rd.dx;G.gy=SCREEN.gy+rd.dy;
G.hx=rd.dx*.25;G.hy=1.2+rd.dy*.25;G.hr=rd.dx*.3;
}
const bl={next:2.5,start:-9};
const blinkAmt=()=>{const p=(t-bl.start)/.16;return p>=0&&p<1?Math.sin(Math.PI*p):0};
/* --- looking around: lamp/photos, window, shelf, out at you, up, down --- */
const LOOKS=[[-5,-.5],[5,-.5],[-4.5,-3],[4.5,-3],[0,-2],[0,.2],[-5,2],[5,2]];
function pickLook(){
let i;do{i=Math.floor(Math.random()*LOOKS.length)}while(i===lastLook);
lastLook=i;const L=LOOKS[i];
G.gx=L[0];G.gy=L[1];G.hx=L[0]*.7;G.hy=1.2+L[1]*.4;G.hr=L[0]*.6;
bl.start=t;
}
function step(dt){
if(t>until){
if(mode==='screen'){mode='look';looksLeft=1+Math.floor(Math.random()*3);pickLook();until=t+rnd(1.3,2.6)}
else{looksLeft--;
if(looksLeft>0){pickLook();until=t+rnd(1.2,2.4)}
else{mode='screen';until=t+rnd(4,8)}}
}
if(mode==='screen')stepReading();
if(t>bl.next){bl.start=t;bl.next=t+(Math.random()<.2?.35:rnd(2.4,6))}
const k=r=>1-Math.exp(-r*dt);
S.gx+=(G.gx-S.gx)*k(16);S.gy+=(G.gy-S.gy)*k(16);
S.hx+=(G.hx-S.hx)*k(5);S.hy+=(G.hy-S.hy)*k(5);S.hr+=(G.hr-S.hr)*k(5);
}
const f=v=>v.toFixed(2);
function render(){
const hy=S.hy+Math.sin(t*1.7)*.3;
head.setAttribute('transform',`translate(${f(S.hx)} ${f(hy)}) rotate(${f(S.hr)} 200 140)`);
hairB.setAttribute('transform',`translate(${f(S.hx*.5)} ${f(hy*.5)}) rotate(${f(S.hr*.5)} 200 140)`);
const sc=clamp(1-blinkAmt()*.95,.06,1).toFixed(3),gt=`translate(${f(S.gx)} ${f(S.gy)})`;
eL.setAttribute('transform',`${gt} translate(175 99) scale(1 ${sc}) translate(-175 -99)`);
eR.setAttribute('transform',`${gt} translate(225 99) scale(1 ${sc}) translate(-225 -99)`);
}
let vis=true,last=0;
if('IntersectionObserver' in window)new IntersectionObserver(es=>{vis=es[0].isIntersecting}).observe(roomEl);
function frame(ms){
requestAnimationFrame(frame);
if(!vis||ms-last<33)return; /* about 30 frames a second, and paused when off-screen */
const dt=Math.min((ms-last)/1000,.1);last=ms;t+=dt;
step(dt);render();
}
requestAnimationFrame(frame);
window.kimberly={look:()=>{mode='look';looksLeft=1;pickLook();until=t+rnd(1.3,2.4)}};
})();