<!DOCTYPE html><html><head><meta charset="utf-8" /><meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'unsafe-inline'; img-src https: data:; font-src https: data:; media-src https: data:; connect-src https://cdn.jsdelivr.net; frame-src 'none'" /><script>
(function(){
var nav=navigator;
var reduced=matchMedia("(prefers-reduced-motion: reduce)").matches;
var conn=nav.connection||{};
var saveData=!!conn.saveData;
var slow=/(^|[^a-z])(2g|slow-2g)$/.test(conn.effectiveType||"");
var mem=nav.deviceMemory||8;
var cores=nav.hardwareConcurrency||8;
var webgl2=false;
try{var c=document.createElement("canvas");webgl2=!!(c.getContext("webgl2")||c.getContext("webgl"));}catch(e){}
var tier=1;
if(reduced||saveData||slow)tier=4;
else if(mem<=2||cores<=2||!webgl2)tier=3;
else if(mem<=4||cores<=4||conn.effectiveType==="3g")tier=2;
var q=new URLSearchParams(location.search||"");
var forced=parseInt(document.documentElement.getAttribute("data-te-tier")||q.get("tier")||"",10);
if(forced>=1&&forced<=4)tier=forced;
var listeners={tier:[],scroll:[],visible:[],copy:[]};
var copy=null;
document.documentElement.style.setProperty("--te-band","0.5");
var visible=true,hidden=document.hidden;
var loops=[];var loopHandle=0;var last=0;
var nativeRAF=window.requestAnimationFrame.bind(window);
var pending=[];
function active(){return visible&&!hidden&&tier<4}
function pump(now){
loopHandle=0;
if(!active())return;
var dt=last?Math.min(64,now-last):16;last=now;
for(var i=0;i<loops.length;i++){try{loops[i](dt,now)}catch(e){if(!loops[i].te_err){loops[i].te_err=1;console.error("TE.loop",e)}}}
var queued=pending;pending=[];
for(var j=0;j<queued.length;j++){try{queued[j](now)}catch(e){if(!queued[j].te_err){queued[j].te_err=1;console.error("TE.raf",e)}}}
// A callback above may already have scheduled the next frame through the
// wrapped requestAnimationFrame; never queue pump twice for one frame.
if(!loopHandle&&(loops.length||pending.length))loopHandle=nativeRAF(pump);
}
function schedule(){if(!loopHandle&&active())loopHandle=nativeRAF(pump)}
window.requestAnimationFrame=function(fn){pending.push(fn);schedule();return pending.length};
window.cancelAnimationFrame=function(){};
var videoState=new Map();
function pauseAll(){
document.querySelectorAll("video").forEach(function(v){videoState.set(v,!v.paused);if(!v.paused)v.pause()});
}
function resumeAll(){
document.querySelectorAll("video").forEach(function(v){if(videoState.get(v)){var p=v.play();if(p&&p.catch)p.catch(function(){})}});
last=0;schedule();
}
function setVisible(v){
if(v===visible)return;visible=v;
if(v)resumeAll();else pauseAll();
listeners.visible.forEach(function(f){f(v)});
}
document.addEventListener("visibilitychange",function(){hidden=document.hidden;if(hidden)pauseAll();else resumeAll()});
var frames=0,slowSince=0,checked=0;
loops.push(function(dt,now){
frames++;
if(now-checked<1000)return;
var fps=frames*1000/(now-checked);frames=0;checked=now;
if(fps<40){if(!slowSince)slowSince=now;else if(now-slowSince>2000){degrade();slowSince=0}}
else slowSince=0;
});
function degrade(){
if(tier>=3)return;tier++;
document.documentElement.setAttribute("data-te-tier",String(tier));
listeners.tier.forEach(function(f){f(tier)});
}
var pointer={x:0.5,y:0.5,vx:0,vy:0,down:false,fine:matchMedia("(pointer:fine)").matches,active:false};
var px=0,py=0,pt=0;
function onMove(e){
var w=innerWidth||1,h=innerHeight||1,now=performance.now();
var x=e.clientX/w,y=e.clientY/h;
if(pt){var s=Math.max(1,now-pt);pointer.vx=(x-px)*1000/s;pointer.vy=(y-py)*1000/s}
px=x;py=y;pt=now;pointer.x=x;pointer.y=y;pointer.active=true;
}
addEventListener("pointermove",onMove,{passive:true});
addEventListener("pointerdown",function(e){pointer.down=true;onMove(e)},{passive:true});
addEventListener("pointerup",function(){pointer.down=false},{passive:true});
addEventListener("pointerleave",function(){pointer.active=false;pointer.vx=0;pointer.vy=0});
addEventListener("message",function(e){
var d=e.data;if(!d||typeof d!=="object")return;
if(d.type==="te:scroll"&&typeof d.t==="number")listeners.scroll.forEach(function(f){f(d.t)});
if(d.type==="te:visible")setVisible(!!d.visible);
if(d.type==="te:copy"&&typeof d.y0==="number"){copy={x0:d.x0,y0:d.y0,x1:d.x1,y1:d.y1};document.documentElement.style.setProperty("--te-band",String(Math.round(d.y0*1000)/1000));listeners.copy.forEach(function(f){f(copy)})}
if(d.type==="te:tier"&&d.tier>=1&&d.tier<=4){tier=d.tier;document.documentElement.setAttribute("data-te-tier",String(tier));listeners.tier.forEach(function(f){f(tier)})}
});
document.documentElement.setAttribute("data-te-tier",String(tier));
window.TE={
get tier(){return tier},
get visible(){return visible&&!hidden},
get reduced(){return reduced},
get dpr(){return tier===1?Math.min(devicePixelRatio||1,2):tier===2?Math.min(devicePixelRatio||1,1.5):1},
caps:{memory:mem,cores:cores,webgl:webgl2,saveData:saveData,effectiveType:conn.effectiveType||"",reduced:reduced},
pointer:pointer,
loop:function(fn){loops.push(fn);schedule();return function(){var i=loops.indexOf(fn);if(i>=0)loops.splice(i,1)}},
onScroll:function(fn){listeners.scroll.push(fn)},
onTier:function(fn){listeners.tier.push(fn)},
onVisible:function(fn){listeners.visible.push(fn)},
onCopy:function(fn){listeners.copy.push(fn);if(copy)fn(copy)},
get copy(){return copy},
get band(){return copy?copy.y0:0.5},
degrade:degrade,
video:function(el,src){
if(tier>=4)return false;
var small=tier>=2||Math.min(innerWidth,innerHeight)<700;
el.src=small?src.replace(/\.mp4$/,"-sm.mp4"):src;
if(tier>=3)el.preload="metadata";
return true;
},
rendition:function(){return tier>=2||Math.min(innerWidth,innerHeight)<700?"-sm":""},
debug:function(){return {handle:loopHandle,loops:loops.length,pending:pending.length,visible:visible,hidden:hidden,tier:tier,active:active()}}
};
})();
</script><style>html,body{margin:0;padding:0;overflow:hidden;background:transparent}</style><script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script></head><body><style>
html,body{margin:0;height:100%;background:#eef2ea;overflow:hidden;font-family:system-ui,sans-serif}
#c,#g{position:absolute;inset:0;width:100%;height:100%;display:block}
#g{display:none}
#leg{position:absolute;left:22px;top:14px;font-family:ui-monospace,Menlo,monospace;font-size:clamp(9px,2.6vw,11px);letter-spacing:.14em;text-transform:uppercase;color:#3c4a35;pointer-events:none;font-variant-numeric:tabular-nums}
#leg i{display:inline-block;width:8px;height:8px;border-radius:50%;background:#b3541e;margin-right:8px;vertical-align:middle}
</style>
<canvas id=c></canvas><canvas id=g></canvas><div id=leg><i></i>Pengiriman hari ini</div>
<script>
window.TE3=function(o){
var TE=window.TE;
if(!TE||!TE.caps||!TE.caps.webgl)return false;
if(TE.tier>(o.tier||2))return false;
if(typeof THREE==="undefined")return false;
var gc=document.getElementById(o.canvas||"g");
if(!gc)return false;
var ren;
try{ren=new THREE.WebGLRenderer({canvas:gc,antialias:o.aa!==false,alpha:!!o.alpha})}catch(e){return false}
if(THREE.SRGBColorSpace)ren.outputColorSpace=THREE.SRGBColorSpace;
if(o.tone){ren.toneMapping=THREE.ACESFilmicToneMapping;ren.toneMappingExposure=o.tone}
if(!o.alpha)ren.setClearColor(o.clear===undefined?0x000000:o.clear,1);
var scene=new THREE.Scene();
var cam=new THREE.PerspectiveCamera(o.fov||32,1,0.05,160);
cam.position.set(o.camX||0,o.camY||0,o.dist||4);
cam.lookAt(o.lookX||0,o.lookY||0,0);
var root=new THREE.Group();scene.add(root);
var k={THREE:THREE,ren:ren,scene:scene,cam:cam,root:root,band:0.5,w:0,h:0,
narrow:false,px:0,py:0,pw:0,t:0,visW:0,visH:0,tier:TE.tier,dpr:1,time:0};
// A scene that throws while it is being built must not take the stage down
// with it: the 2D canvas is still there and still correct, so we hand back
// false and let it boot. Same for the frame loop, which freezes on the last
// good frame rather than throwing sixty times a second.
try{if(o.build&&o.build(k)===false)return false}catch(e){return false}
var dead=o.replace===null?null:document.getElementById(o.replace||"c");
if(dead&&dead.parentNode)dead.parentNode.removeChild(dead);
gc.style.display="block";
function layout(){
k.w=innerWidth;k.h=innerHeight;k.narrow=k.w<(o.narrow||640);
if(o.host){var hb=o.host.getBoundingClientRect();if(hb.width>1&&hb.height>1){k.w=hb.width;k.h=hb.height}}
// A hero shares its frame with the headline, so the subject is fitted into
// the band the parent reports. An embed owns its whole box, so it passes
// band:1 and gets all of it.
k.band=o.band!==undefined?o.band:Math.max(TE.band||0.5,o.minBand||0.24);
k.dpr=Math.min(TE.dpr,o.maxDpr||1.75);
ren.setPixelRatio(k.dpr);ren.setSize(k.w,k.h,false);
cam.aspect=k.w/Math.max(1,k.h);cam.updateProjectionMatrix();
k.visH=2*Math.tan(cam.fov*Math.PI/360)*cam.position.z;
k.visW=k.visH*cam.aspect;
if(o.fit!==false){
var size=o.size||1;
var s=Math.min(k.band*k.visH*(o.fitH||0.74)/size,k.visW*(o.fitW||0.84)/size);
root.scale.setScalar(Math.max(o.minScale||0.12,s));
root.position.y=(0.5-k.band*0.5)*k.visH+(o.lift||0)*root.scale.x;
}
try{if(o.layout)o.layout(k)}catch(e){}
}
layout();addEventListener("resize",layout);
if(TE.onCopy)TE.onCopy(layout);
var frozen=false;
if(TE.onTier)TE.onTier(function(n){k.tier=n;if(n>=3)frozen=true});
addEventListener("pointermove",function(e){
k.px=(e.clientX/Math.max(1,innerWidth))*2-1;
k.py=-((e.clientY/Math.max(1,innerHeight))*2-1);
k.pw=1;
try{if(o.onPointer)o.onPointer(k)}catch(e){}
},{passive:true});
if(o.onScroll){
var os=function(v){k.t=Math.min(1,Math.max(0,v));try{o.onScroll(k)}catch(e){}};
if(TE.onScroll)TE.onScroll(os);
else addEventListener("message",function(e){if(e.data&&e.data.type==="te:scroll")os(e.data.t)});
}
TE.loop(function(dt){
if(frozen)return;
k.time+=dt;
k.pw*=Math.pow(0.988,dt/16);
try{
if(o.frame)o.frame(dt,k);
ren.render(scene,cam);
}catch(e){frozen=true}
});
return true;
};
</script>
<script>
(function(){
var TE=window.TE,cv=document.getElementById("c"),cx=cv.getContext("2d"),dpr=TE?Math.min(TE.dpr,1.5):1,leg=document.getElementById("leg");
var W=0,H=0;function fit(){W=innerWidth;H=innerHeight;cv.width=W*dpr;cv.height=H*dpr;cx.setTransform(dpr,0,0,dpr,0,0)}fit();addEventListener("resize",fit);
var K={x:0.5,y:0.24,name:"Dapur, Jalan Cempaka"};
var V=[{x:0.16,y:0.12,n:"Pernikahan, Harapan Indah",p:120,at:8},{x:0.8,y:0.09,n:"Kantor, Summarecon",p:60,at:11},{x:0.88,y:0.34,n:"Pengajian, Jatiasih",p:200,at:15},{x:0.26,y:0.38,n:"Ulang tahun, Galaxy",p:40,at:13},{x:0.64,y:0.42,n:"Kantor, Tambun",p:80,at:11},{x:0.09,y:0.26,n:"Syukuran, Pondok Gede",p:150,at:17}];
var roads=[];var s=11;function rnd(){s=(s*9301+49297)%233280;return s/233280}
for(var i=0;i<26;i++)roads.push({x1:rnd(),y1:rnd()*0.48,x2:rnd(),y2:rnd()*0.48});
var trucks=V.map(function(v,i){return {v:v,t:(i*0.17)%1,speed:0.00006+rnd()*0.00004}});
function hour(){var d=new Date();return d.getHours()+d.getMinutes()/60}
function bez(a,b,t){var mx=(a.x+b.x)/2+(b.y-a.y)*0.25,my=(a.y+b.y)/2-(b.x-a.x)*0.25;var u=1-t;return {x:u*u*a.x+2*u*t*mx+t*t*b.x,y:u*u*a.y+2*u*t*my+t*t*b.y}}
function draw(dt){
var top=W<640?44:30,narrow=W<640,BH=(H*(TE&&TE.band||0.5)-top)*2;
cx.clearRect(0,0,W,H);
cx.fillStyle="#eef2ea";cx.fillRect(0,0,W,H);
cx.save();cx.translate(0,top);
cx.strokeStyle="rgba(60,74,53,.12)";cx.lineWidth=1;
for(var i=0;i<roads.length;i++){var r=roads[i];cx.beginPath();cx.moveTo(r.x1*W,r.y1*BH);cx.lineTo(r.x2*W,r.y2*BH);cx.stroke()}
var h=hour(),done=0,total=0;
for(var j=0;j<V.length;j++){
var v=V[j];total+=v.p;
var delivered=h>=v.at;if(delivered)done+=v.p;
cx.strokeStyle=delivered?"rgba(179,84,30,.35)":"rgba(179,84,30,.9)";cx.lineWidth=delivered?1.5:2.5;cx.setLineDash(delivered?[]:[6,6]);
cx.beginPath();cx.moveTo(K.x*W,K.y*BH);
for(var k=1;k<=24;k++){var p=bez(K,v,k/24);cx.lineTo(p.x*W,p.y*BH)}cx.stroke();cx.setLineDash([]);
cx.fillStyle="#3c4a35";cx.beginPath();cx.arc(v.x*W,v.y*BH,6,0,6.2832);cx.fill();
cx.fillStyle="#3c4a35";cx.font=narrow?"600 10px system-ui,sans-serif":"600 12px system-ui,sans-serif";cx.textAlign=v.x>0.5?"right":"left";
var label=narrow?v.n.split(",")[0]+" . "+(v.at<10?"0":"")+v.at+".00":v.n+" . "+v.p+" porsi . "+(v.at<10?"0":"")+v.at+".00",lw=cx.measureText(label).width,lx=v.x*W+(v.x>0.5?-12:12);
cx.fillStyle="rgba(238,242,234,.86)";cx.fillRect(v.x>0.5?lx-lw-4:lx-4,v.y*BH-6,lw+8,18);
cx.fillStyle="#3c4a35";cx.fillText(label,lx,v.y*BH+4);
}
// kitchen
cx.fillStyle="#b3541e";cx.beginPath();cx.arc(K.x*W,K.y*BH,11,0,6.2832);cx.fill();cx.fillStyle="#eef2ea";cx.beginPath();cx.arc(K.x*W,K.y*BH,4,0,6.2832);cx.fill();
cx.fillStyle="#3c4a35";cx.font="700 12px system-ui,sans-serif";cx.textAlign="center";cx.fillText(K.name,K.x*W,K.y*BH-18);
// trucks on routes that are not yet delivered
for(var m=0;m<trucks.length;m++){var tr=trucks[m];if(h>=tr.v.at)continue;tr.t=(tr.t+tr.speed*dt)%1;var q=bez(K,tr.v,tr.t);cx.fillStyle="#b3541e";cx.fillRect(q.x*W-7,q.y*BH-5,14,10);cx.fillStyle="#fff";cx.fillRect(q.x*W-5,q.y*BH-3,4,3)}
cx.restore();
tally();
}
// the line under the map: porsi delivered against porsi contracted, and how
// many vans are still out. Factored out of draw() so the relief model can
// keep saying the same thing without a 2D canvas under it.
function tally(){
var h=hour(),done=0,total=0;
for(var j=0;j<V.length;j++){total+=V[j].p;if(h>=V[j].at)done+=V[j].p}
leg.innerHTML="<i></i>"+done+" dari "+total+" porsi sudah sampai . "+V.filter(function(v){return h<v.at}).length+" armada di jalan";
}
// Three.js, tier 1 and 2: the delivery area as a relief model standing on
// the page. The ground is displaced, the kali is cut into it, the roads lie
// on the surface, the six acara stand on pins at their contract hour, and
// the routes arc over the relief with a van on every one that has not
// arrived. A route that has arrived fades back into the board.
function initGL(){
var tubes=[],vans=[],pins=[],lastHour=-1;
// pin heights chosen so no two labels land on the same line of the board
var PH=[0.58,0.95,0.62,1.05,0.50,0.78];
function hAt(u,w){
var h=0.26*Math.sin(u*6.4+1.1)*Math.cos(w*4.2+0.5)
+0.17*Math.sin(u*3.1-w*4.6+2.2)
+0.09*Math.sin(u*11.3+w*8.1);
h-=0.34*Math.exp(-Math.pow((u-(0.34+0.17*Math.sin(w*3.1+0.6)))/0.05,2));
var e=Math.min(1,Math.min(u,1-u)*7)*Math.min(1,Math.min(w,1-w)*7);
return (h+0.30)*e;
}
function wx(u){return (u-0.5)*10.4}
function wz(v){return (v/0.48-0.5)*6.6}
function wy(u,v){return hAt(u,v/0.48)}
return TE3({
tier:2,clear:0xeef2ea,fov:30,dist:4.6,fit:false,maxDpr:1.4,
build:function(k){
var T=k.THREE,i,j;
// the ground, displaced and coloured by what it is
var seg=new T.PlaneGeometry(10.4,6.6,104,66);
var ps=seg.attributes.position,col=[];
for(i=0;i<ps.count;i++){
var u=ps.getX(i)/10.4+0.5,w=0.5-ps.getY(i)/6.6;
var h=hAt(u,w);ps.setZ(i,h);
var riv=Math.exp(-Math.pow((u-(0.34+0.17*Math.sin(w*3.1+0.6)))/0.05,2));
var lift=Math.min(1,Math.max(0,h/0.62));
var r=0.84-lift*0.24,g=0.88-lift*0.18,b=0.80-lift*0.26;
if(riv>0.35){r=0.56;g=0.72;b=0.76}
col.push(r,g,b);
}
seg.setAttribute("color",new T.Float32BufferAttribute(col,3));
seg.computeVertexNormals();
var relief=new T.Mesh(seg,new T.MeshLambertMaterial({vertexColors:true}));
relief.rotation.x=-Math.PI/2;k.root.add(relief);
// the board it is carved on, so the map is an object and not a picture
var base=new T.Mesh(new T.BoxGeometry(10.4,0.62,6.6),new T.MeshLambertMaterial({color:0xd6dccd}));
base.position.y=-0.31;k.root.add(base);
var rim=new T.LineSegments(new T.EdgesGeometry(base.geometry),
new T.LineBasicMaterial({color:0x3c4a35,transparent:true,opacity:0.35}));
rim.position.y=-0.31;k.root.add(rim);
// the roads, lying on the ground they cross
var rp=[];
for(i=0;i<roads.length;i++){
var rd=roads[i];
for(j=0;j<6;j++){
var a=j/6,b2=(j+1)/6;
var ua=rd.x1+(rd.x2-rd.x1)*a,va=rd.y1+(rd.y2-rd.y1)*a;
var ub=rd.x1+(rd.x2-rd.x1)*b2,vb=rd.y1+(rd.y2-rd.y1)*b2;
rp.push(wx(ua),wy(ua,va)+0.012,wz(va),wx(ub),wy(ub,vb)+0.012,wz(vb));
}
}
var rg=new T.BufferGeometry();
rg.setAttribute("position",new T.Float32BufferAttribute(rp,3));
k.root.add(new T.LineSegments(rg,new T.LineBasicMaterial({color:0x3c4a35,transparent:true,opacity:0.3})));
function plate(txt){
var mc=document.createElement("canvas"),mg=mc.getContext("2d");
if(!mg)return null;
var FT="600 30px system-ui,sans-serif";mg.font=FT;
var tw=Math.ceil(mg.measureText(txt).width)+22;
var cn=document.createElement("canvas");cn.width=tw;cn.height=48;
var g2=cn.getContext("2d");
if(!g2)return null;
g2.font=FT;
g2.fillStyle="rgba(238,242,234,.88)";g2.fillRect(0,4,tw,40);
g2.fillStyle="#3c4a35";g2.fillText(txt,11,34);
var tx=new T.CanvasTexture(cn);
if(T.SRGBColorSpace)tx.colorSpace=T.SRGBColorSpace;
return {tex:tx,ar:tw/48};
}
// every pin carries the full line and the phone line, and the layout
// picks the one the frame has room for
function sprite(full,shrt,ax){
var a=plate(full),b=plate(shrt);
if(!a||!b)return null;
var sp=new T.Sprite(new T.SpriteMaterial({map:a.tex,transparent:true,depthTest:false}));
sp.renderOrder=8;sp.center.set(ax,0.5);k.root.add(sp);
var rec={lab:sp,wide:a,thin:b};pins.push(rec);return sp;
}
var pinG=new T.CylinderGeometry(0.022,0.022,1,6),headG=new T.SphereGeometry(0.085,12,10);
var pinM=new T.MeshLambertMaterial({color:0x3c4a35});
var hotM=new T.MeshLambertMaterial({color:0xb3541e});
var doneM=new T.MeshLambertMaterial({color:0xb3541e,transparent:true,opacity:0.3});
for(i=0;i<V.length;i++){
var v=V[i],vx=wx(v.x),vz=wz(v.y),vy=wy(v.x,v.y);
// pins of three lengths, so six labels over one board never sit on
// top of each other
var ph=PH[i];
var stem=new T.Mesh(pinG,pinM);stem.scale.y=ph;stem.position.set(vx,vy+ph/2,vz);k.root.add(stem);
var hd=new T.Mesh(headG,pinM);hd.position.set(vx,vy+ph,vz);k.root.add(hd);
var lab=sprite(v.n+" . "+v.p+" porsi . "+(v.at<10?"0":"")+v.at+".00",
v.n.split(",")[0]+" . "+(v.at<10?"0":"")+v.at+".00",v.x>0.5?1:0);
if(!lab)return false;
lab.position.set(vx,vy+ph+0.24,vz);
// the route, arcing over the relief between the dapur and the acara
var pts=[];
for(j=0;j<=26;j++){
var q=bez(K,v,j/26),f=j/26;
pts.push(new T.Vector3(wx(q.x),wy(q.x,q.y)+0.05+0.42*Math.sin(Math.PI*f),wz(q.y)));
}
var cur=new T.CatmullRomCurve3(pts);
var tb=new T.Mesh(new T.TubeGeometry(cur,44,0.034,7,false),hotM);
k.root.add(tb);tubes.push(tb);
var van=new T.Group();
var body=new T.Mesh(new T.BoxGeometry(0.26,0.15,0.15),hotM);van.add(body);
var cab=new T.Mesh(new T.BoxGeometry(0.09,0.09,0.13),new T.MeshLambertMaterial({color:0xfdfdfb}));
cab.position.x=0.11;van.add(cab);
k.root.add(van);vans.push({g:van,cur:cur});
}
// the dapur every route leaves from
var kx=wx(K.x),kz=wz(K.y),ky=wy(K.x,K.y);
var kt=new T.Mesh(new T.CylinderGeometry(0.15,0.19,0.34,16),hotM);
kt.position.set(kx,ky+0.17,kz);k.root.add(kt);
var kc=new T.Mesh(new T.SphereGeometry(0.075,12,10),new T.MeshLambertMaterial({color:0xeef2ea}));
kc.position.set(kx,ky+0.38,kz);k.root.add(kc);
var kl=sprite(K.name,K.name,0.5);
if(!kl)return false;
kl.position.set(kx,ky+0.66,kz);
k.scene.add(new T.HemisphereLight(0xffffff,0xb9c3ad,1.9));
var sun=new T.DirectionalLight(0xfff6e6,1.6);sun.position.set(-4,7,5);k.scene.add(sun);
k.root.rotation.x=0.95;
tubes.doneM=doneM;tubes.hotM=hotM;
return true;
},
layout:function(k){
var s=Math.min(k.band*k.visH*0.94/6.3,k.visW*(k.narrow?1.0:0.86)/11.0);
k.root.scale.setScalar(s);
k.root.position.y=(0.5-k.band*0.5)*k.visH+s*0.1;
// the labels are typography, so they keep one size on the page no
// matter how far back the board had to be pushed to fit
var lz=0.055/Math.max(0.02,s);
for(var i=0;i<pins.length;i++){
var pn=pins[i],ch=k.narrow?pn.thin:pn.wide;
pn.lab.material.map=ch.tex;pn.lab.scale.set(lz*ch.ar,lz,1);
}
},
frame:function(dt,k){
var h=hour(),i;
if(Math.floor(h*60)!==lastHour){
lastHour=Math.floor(h*60);tally();
for(i=0;i<V.length;i++){
var out=h<V[i].at;
tubes[i].material=out?tubes.hotM:tubes.doneM;
vans[i].g.visible=out;
}
}
k.root.rotation.y=k.px*0.1;
k.root.rotation.x=0.95-k.py*0.05;
for(i=0;i<trucks.length;i++){
var tr=trucks[i];
if(h>=tr.v.at)continue;
tr.t=(tr.t+tr.speed*dt)%1;
var c=vans[i].cur,pt=c.getPointAt(tr.t),tg=c.getTangentAt(tr.t);
vans[i].g.position.copy(pt);
vans[i].g.lookAt(pt.x+tg.x,pt.y+tg.y,pt.z+tg.z);
vans[i].g.rotateY(Math.PI/2);
}
}
});
}
if(!initGL()){
if(TE&&TE.loop)TE.loop(draw);else(function raf(){requestAnimationFrame(raf);draw(16)})();
}
})();
</script></body></html>
Katering harian dan acara, Bekasi, sejak 2012
Tepat waktu, atau gratis
Peta di atas adalah pengiriman kami hari ini: enam acara, satu dapur, dan jam sampai yang tertulis di kontrak. Porsi yang datang lewat dari jam itu tidak kami tagih. Begitu sejak 2012.
Jam sampai ditulis di kontrak. Kalau armada kami lewat dari jam itu, porsi yang terlambat tidak ditagih. Tahun lalu 2 dari 1.900 pengiriman.
Rasa yang sama dari kotak ke-1 sampai ke-1.000
Resep ditimbang, bukan dikira-kira. Bumbu dibuat per 200 porsi, dicicipi kepala dapur sebelum dikemas.
Uji rasa gratis
Untuk acara di atas 100 porsi, kami kirim enam kotak contoh sehari sebelum keputusan.
Satu adukan bumbu untuk setiap 200 porsi
1.900 pengiriman tahun lalu2 yang terlambat dan digratiskan4 armada sendiri13 tahun dapur di Jalan Cempaka
Resepsi 400 porsi, semua sampai jam 10.15 seperti tertulis. Pramusajinya rapi dan pemanasnya menyala sampai tamu terakhir.Keluarga Wirawan, Harapan Indah
Langganan kantor 60 porsi setiap hari selama dua tahun. Menunya berganti, rasanya tidak pernah turun.HRD PT Sinar Mandiri, Summarecon
Uji rasanya datang sehari sebelum kami putuskan. Itu yang membuat kami percaya.Panitia pengajian, Jatiasih
Pernah telat 12 menit karena banjir, dan 80 porsi benar-benar tidak ditagih.Ibu Lestari, Pondok Gede
Dari penawaran sampai piring terakhir
01
Ceritakan acaramu
Tanggal, tempat, jumlah porsi, dan anggaran per porsi lewat WhatsApp. Penawaran tertulis dalam dua jam.
02
Uji rasa
Untuk di atas 100 porsi, enam kotak contoh dikirim gratis. Menu dikunci setelah kamu setuju.
03
Kontrak dengan jam sampai
Jam kedatangan ditulis, berikut pasal telat berarti gratis. DP 30 persen.
04
Antar, sajikan, angkut
Armada sendiri, pramusaji untuk prasmanan, peralatan diangkut kembali malam itu juga.