.stage{position:absolute;inset:0;display:grid;place-items:center;overflow:hidden}
.phone{position:relative;height:90vmin;max-height:640px;aspect-ratio:9/19.5;background:#08080c;
border-radius:20%/8.6%;box-shadow:0 0 0 2px rgba(255,255,255,.08) inset,0 12px 30px rgba(0,0,0,.35)}
.phone .notch{position:absolute;left:50%;top:0;transform:translateX(-50%);width:38%;height:5.6%;
background:#08080c;border-radius:0 0 46% 46%;z-index:80}
.phone .screen{position:absolute;top:7.6%;left:3%;right:3%;bottom:3%;border-radius:11%/4.6%;overflow:hidden;
background:var(--surface);color:var(--fg)}
.phone .home{position:absolute;left:50%;bottom:1.1%;transform:translateX(-50%);width:32%;height:3px;
border-radius:2px;background:rgba(255,255,255,.45);z-index:80}
.pfx{position:absolute;left:0;top:0;width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;
background:color-mix(in srgb,var(--accent) 55%,transparent);
box-shadow:0 0 0 6px color-mix(in srgb,var(--accent) 15%,transparent);pointer-events:none;z-index:999;
transition:opacity .2s}
.pfx.hold{width:30px;height:30px;margin:-15px 0 0 -15px;
box-shadow:0 0 0 9px color-mix(in srgb,var(--accent) 20%,transparent);
transition:width .5s linear,height .5s linear,margin .5s linear,box-shadow .5s linear,opacity .2s}
.feed{position:absolute;inset:0;overflow-y:auto;padding:6%}
.fcard{height:clamp(26px,17vmin,46px);border-radius:10%;background:var(--line);margin-bottom:6%}
.fcard.sk{position:relative;overflow:hidden;opacity:.6}
.fcard.sk::after{content:'';position:absolute;inset:0;
background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--fg) 8%,transparent),transparent);
animation:sk 1.1s infinite}
@keyframes sk{from{transform:translateX(-100%)}to{transform:translateX(100%)}}
const pfx=document.createElement('div');pfx.className='pfx';document.body.appendChild(pfx);
const screenEl=document.getElementById('screen');
let pfxAuto=true;
function pfxHide(){pfx.style.opacity='0';}
addEventListener('pointerdown',()=>{pfxAuto=false;pfxHide();},{capture:true});
function pfxAt(fx,fy){
const r=screenEl.getBoundingClientRect();
const x=r.left+r.width*fx, y=r.top+r.height*fy;
pfx.style.transform='translate('+x+'px,'+y+'px)';
return {x,y};
}
function pfxWalk(x0,y0,x1,y1,ms){
return new Promise(res=>{
const start=performance.now();
function step(now){
if(!pfxAuto) return res();
const p=Math.min(1,(now-start)/ms), e=1-Math.pow(1-p,3);
pfxAt(x0+(x1-x0)*e, y0+(y1-y0)*e);
if(p<1) requestAnimationFrame(step); else res();
}
requestAnimationFrame(step);
});
}
function pfxWait(ms){
return new Promise(res=>{
const start=performance.now();
function step(now){ if(!pfxAuto) return res(); if(now-start<ms) requestAnimationFrame(step); else res(); }
requestAnimationFrame(step);
});
}
const feed=document.getElementById('feed');
let count=3, loading=false;
function addSkeletons(n){ for(let i=0;i<n;i++){ const d=document.createElement('div'); d.className='fcard sk'; feed.appendChild(d); } }
function resolveSkeletons(){ [...feed.querySelectorAll('.fcard.sk')].forEach(d=>d.classList.remove('sk')); }
async function loadMore(){
if(loading||count>=12) return; loading=true;
addSkeletons(2);
await pfxWait(650);
resolveSkeletons(); count+=2; loading=false;
}
feed.addEventListener('scroll',()=>{ if(feed.scrollTop+feed.clientHeight>feed.scrollHeight-60) loadMore(); });
async function autoSeq(){
while(pfxAuto){
while(pfxAuto && count<12){
const start=performance.now();
while(pfxAuto){
const t=(performance.now()-start)/650; if(t>=1)break;
feed.scrollTop=feed.scrollTop+2; pfxAt(0.5,0.55);
await new Promise(r=>requestAnimationFrame(r));
}
if(!pfxAuto)break;
if(feed.scrollTop+feed.clientHeight>feed.scrollHeight-60) await loadMore();
await pfxWait(150);
}
if(!pfxAuto)break;
await pfxWait(900);
feed.scrollTop=0;
while(feed.children.length>3) feed.removeChild(feed.lastChild);
count=3;
await pfxWait(500);
}
}
autoSeq();