<!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:#2a1d17;overflow:hidden;font-family:system-ui,sans-serif}
#sky,#g{position:absolute;inset:0;width:100%;height:100%;display:block}
#g{display:none}
#state{position:absolute;left:22px;bottom:18px;font-family:ui-monospace,Menlo,monospace;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.72);pointer-events:none;font-variant-numeric:tabular-nums;text-shadow:0 1px 12px rgba(0,0,0,.35)}
#state b{font-weight:600;color:#fff}
#state i{display:inline-block;width:7px;height:7px;border-radius:50%;background:#7ed957;margin-right:8px;vertical-align:middle;box-shadow:0 0 10px #7ed957}
#state.tutup i{background:#e8a35a;box-shadow:0 0 10px #e8a35a}
#jam{position:absolute;right:22px;bottom:18px;font-family:ui-monospace,Menlo,monospace;font-size:11px;letter-spacing:.14em;text-transform:uppercase;color:rgba(255,255,255,.6);pointer-events:none;font-variant-numeric:tabular-nums}
</style>
<canvas id=sky></canvas><canvas id=g></canvas>
<div id=state><i></i><b>Buka</b> sampai 22.00</div>
<div id=jam></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("sky"),cx=cv.getContext("2d"),dpr=TE?Math.min(TE.dpr,1.5):1;
var state=document.getElementById("state"),jam=document.getElementById("jam");
var OPEN=8,CLOSE=22;
// The sky over Jalan Senja, keyed by local hour: top colour, horizon
// colour, disc colour, and how far the disc has travelled across the day.
var KEYS=[
[0,"#0f1220","#2a1d17","#e9e4d8"],
[5,"#33415c","#c88d6a","#f6d7a6"],
[7,"#4f86c2","#f3dfc2","#ffe7b0"],
[10,"#2f6fb8","#dfe7e6","#fff2c9"],
[13,"#2b64ad","#d9e5e6","#fff7d6"],
[16,"#4d6fa8","#f2c59a","#ffd18a"],
[17.5,"#8a6a8e","#f08a4e","#ffb35c"],
[18.5,"#4b3a5c","#c2543a","#ff9a4a"],
[20,"#1d2036","#5a3a3a","#efe3cf"],
[24,"#0f1220","#2a1d17","#e9e4d8"]
];
function hex(h){var n=parseInt(h.slice(1),16);return [n>>16&255,n>>8&255,n&255]}
function mix(a,b,t){var A=hex(a),B=hex(b);return "rgb("+Math.round(A[0]+(B[0]-A[0])*t)+","+Math.round(A[1]+(B[1]-A[1])*t)+","+Math.round(A[2]+(B[2]-A[2])*t)+")"}
function palette(h){
for(var i=0;i<KEYS.length-1;i++){
var a=KEYS[i],b=KEYS[i+1];
if(h>=a[0]&&h<=b[0]){var t=(h-a[0])/(b[0]-a[0]);t=t*t*(3-2*t);return [mix(a[1],b[1],t),mix(a[2],b[2],t),mix(a[3],b[3],t)]}
}
return [KEYS[0][1],KEYS[0][2],KEYS[0][3]];
}
var override=null;
window.te_setHour=function(h){override=h;draw(true)};
function hourNow(){if(override!==null)return override;var d=new Date();return d.getHours()+d.getMinutes()/60+d.getSeconds()/3600}
function fit(){cv.width=Math.round(innerWidth*dpr);cv.height=Math.round(innerHeight*dpr);draw(true)}
var lastDrawn=-1;
function draw(force){
var h=hourNow();
if(!force&&Math.abs(h-lastDrawn)<1/120)return;
lastDrawn=h;
var W=cv.width,H=cv.height,p=palette(h);
var g=cx.createLinearGradient(0,0,0,H);g.addColorStop(0,p[0]);g.addColorStop(0.78,p[1]);g.addColorStop(1,p[1]);
cx.fillStyle=g;cx.fillRect(0,0,W,H);
// during the light hours a soft shade sits where the copy reads, so white
// type keeps its contrast on a pale sky without a scrim over the whole hero
if(h>6.5&&h<17.5){var sh=cx.createLinearGradient(0,0,W*0.72,0);sh.addColorStop(0,"rgba(20,26,48,.42)");sh.addColorStop(1,"rgba(20,26,48,0)");cx.fillStyle=sh;cx.fillRect(0,0,W,H);}
// the disc: sun from 6 to 18, moon otherwise, on a low arc over the horizon
var day=h>=6&&h<=18,u=day?(h-6)/12:((h+6)%24)/12;
var x=W*(0.56+0.36*u),y=H*(0.8-0.58*Math.sin(u*Math.PI));
var r=Math.min(W,H)*(day?0.075:0.05);
var halo=cx.createRadialGradient(x,y,r*0.4,x,y,r*4.2);
halo.addColorStop(0,day?"rgba(255,230,170,.55)":"rgba(240,236,225,.28)");halo.addColorStop(1,"rgba(255,220,160,0)");
cx.fillStyle=halo;cx.fillRect(0,0,W,H);
cx.fillStyle=p[2];cx.beginPath();cx.arc(x,y,r,0,6.2832);cx.fill();
if(!day){cx.fillStyle=p[0];cx.beginPath();cx.arc(x+r*0.42,y-r*0.18,r*0.86,0,6.2832);cx.fill();}
// haze bands near the horizon, slower in the evening
cx.globalAlpha=0.16;
for(var i=0;i<4;i++){var yy=H*(0.62+i*0.08),hh=H*0.03;var hg=cx.createLinearGradient(0,yy,0,yy+hh);hg.addColorStop(0,"rgba(255,255,255,0)");hg.addColorStop(0.5,"rgba(255,255,255,.9)");hg.addColorStop(1,"rgba(255,255,255,0)");cx.fillStyle=hg;cx.fillRect(0,yy,W,hh);}
cx.globalAlpha=1;
readout(h);
}
// the clock and the open sign, lifted out of draw() so the three.js room
// can keep computing them from the same hours without a 2D canvas.
function readout(h){
var hh24=Math.floor(h)%24,mm=Math.floor((h%1)*60);
jam.textContent=(hh24<10?"0":"")+hh24+"."+(mm<10?"0":"")+mm+" WIB";
var open=h>=OPEN&&h<CLOSE;
state.classList.toggle("tutup",!open);
state.innerHTML=open?"<i></i><b>Buka</b> sampai "+CLOSE+".00":"<i></i><b>Tutup</b>, buka lagi "+(OPEN<10?"0":"")+OPEN+".00";
}
// Three.js, tier 1 and 2: the window itself. A sky dome stands over the
// sill, the sun or the moon rides the same low arc the 2D stage draws, and
// the light that falls on the cup is that disc, so the room dims and warms
// with the hour outside. The open sign stays computed from the shop hours.
function initGL(){
var dome=null,disc=null,halo=null,key=null,amb=null,scrim=null,
cupM=null,sillM=null,brewM=null,lastP=-1,acc=9e9,duv=0,day=true;
function rgb3(css){
var m=/([0-9]+),([0-9]+),([0-9]+)/.exec(css);
return m?[+m[1]/255,+m[2]/255,+m[3]/255]:[0,0,0];
}
return TE3({
tier:2,clear:0x2a1d17,fov:30,dist:4.6,fit:false,maxDpr:1.4,
build:function(k){
var T=k.THREE,i;
k.scene.add(k.cam);
// the sky, one dome, coloured by the hour at every height
var dg=new T.SphereGeometry(60,24,16);
var dc=[];for(i=0;i<dg.attributes.position.count;i++)dc.push(1,1,1);
dg.setAttribute("color",new T.Float32BufferAttribute(dc,3));
dome=new T.Mesh(dg,new T.MeshBasicMaterial({vertexColors:true,side:T.BackSide,fog:false}));
k.scene.add(dome);
// the disc, sun from 6 to 18 and moon otherwise
disc=new T.Mesh(new T.SphereGeometry(1,24,18),new T.MeshBasicMaterial({color:0xffffff}));
k.scene.add(disc);
var hc=document.createElement("canvas");hc.width=128;hc.height=128;
var hx=hc.getContext("2d");
if(!hx)return false;
var hg=hx.createRadialGradient(64,64,4,64,64,64);
hg.addColorStop(0,"rgba(255,255,255,.55)");hg.addColorStop(0.35,"rgba(255,255,255,.22)");
hg.addColorStop(1,"rgba(255,255,255,0)");
hx.fillStyle=hg;hx.fillRect(0,0,128,128);
var ht=new T.CanvasTexture(hc);
if(T.SRGBColorSpace)ht.colorSpace=T.SRGBColorSpace;
halo=new T.Sprite(new T.SpriteMaterial({map:ht,transparent:true,depthWrite:false}));
k.scene.add(halo);
// the sill, the reveal, and what is on it
sillM=new T.MeshLambertMaterial({color:0x6f4634});
var frameM=new T.MeshLambertMaterial({color:0x3a2a20});
cupM=new T.MeshLambertMaterial({color:0xfdf6ea,side:T.DoubleSide});
brewM=new T.MeshPhongMaterial({color:0x3a2114,shininess:70,specular:0x8a6a45});
var sill=new T.Mesh(new T.BoxGeometry(7.2,0.3,1.8),sillM);
sill.position.y=-0.15;k.root.add(sill);
var lip=new T.Mesh(new T.BoxGeometry(7.5,0.11,2.05),frameM);
lip.position.y=-0.35;k.root.add(lip);
var back=new T.Mesh(new T.BoxGeometry(7.2,0.5,0.16),frameM);
back.position.set(0,0.25,-0.82);k.root.add(back);
// the cup, turned on its own profile
var prof=[new T.Vector2(0,0),new T.Vector2(0.30,0),new T.Vector2(0.37,0.46),
new T.Vector2(0.343,0.46),new T.Vector2(0.272,0.06),new T.Vector2(0,0.06)];
var cup=new T.Mesh(new T.LatheGeometry(prof,28),cupM);
cup.position.set(-0.7,0.07,0.2);cup.scale.setScalar(1.35);k.root.add(cup);
var saucer=new T.Mesh(new T.CylinderGeometry(0.66,0.6,0.06,30),cupM);
saucer.position.set(-0.7,0.04,0.2);saucer.scale.set(1.35,1,1.35);k.root.add(saucer);
var handle=new T.Mesh(new T.TorusGeometry(0.15,0.038,8,18,Math.PI*1.25),cupM);
handle.position.set(-0.2,0.4,0.2);handle.rotation.z=-0.5;handle.scale.setScalar(1.35);k.root.add(handle);
var brew=new T.Mesh(new T.CircleGeometry(0.325,28),brewM);
brew.rotation.x=-Math.PI/2;brew.position.set(-0.7,0.60,0.2);brew.scale.setScalar(1.35);k.root.add(brew);
var cast=new T.Mesh(new T.CircleGeometry(0.9,26),
new T.MeshBasicMaterial({color:0x000000,transparent:true,opacity:0.18}));
cast.rotation.x=-Math.PI/2;cast.position.set(-0.61,0.012,0.25);cast.scale.setScalar(1.35);k.root.add(cast);
// the shade the copy reads on while the sky is pale
var sc=document.createElement("canvas");sc.width=256;sc.height=4;
var sx=sc.getContext("2d");
if(!sx)return false;
var sg=sx.createLinearGradient(0,0,184,0);
sg.addColorStop(0,"rgba(20,26,48,.42)");sg.addColorStop(1,"rgba(20,26,48,0)");
sx.fillStyle=sg;sx.fillRect(0,0,256,4);
var st=new T.CanvasTexture(sc);
if(T.SRGBColorSpace)st.colorSpace=T.SRGBColorSpace;
scrim=new T.Mesh(new T.PlaneGeometry(1,1),
new T.MeshBasicMaterial({map:st,transparent:true,depthTest:false,depthWrite:false}));
scrim.renderOrder=7;k.cam.add(scrim);
amb=new T.AmbientLight(0xffffff,1.0);k.scene.add(amb);
key=new T.DirectionalLight(0xffffff,1.4);k.scene.add(key);
return true;
},
layout:function(k){
// the sill sits on the bottom edge of the frame, out of the copy
var s=Math.min(k.visH*0.36/1.9,k.visW*(k.narrow?0.62:0.40)/5.7);
k.root.scale.setScalar(s);
// a phone hero is taller than the phone and its copy runs to the
// bottom, so the sill tucks into the corner the copy leaves free
k.root.position.set(k.visW*(k.narrow?0.40:0.30),
-k.visH*(k.narrow?0.29:0.5)+s*1.05,0);
var d=0.9,fh=2*Math.tan(k.cam.fov*Math.PI/360)*d,fw=fh*k.cam.aspect;
scrim.scale.set(fw,fh,1);scrim.position.set(0,0,-d);
acc=9e9;
},
frame:function(dt,k){
acc+=dt;
if(acc>500){
acc=0;
var h=hourNow();
if(Math.abs(h-lastP)>1/120){
lastP=h;readout(h);
var p=palette(h),top=rgb3(p[0]),hz=rgb3(p[1]),dk=rgb3(p[2]);
var at=dome.geometry.attributes,c=at.color,ps=at.position;
for(var i=0;i<c.count;i++){
var f=Math.min(1,Math.max(0,(ps.getY(i)/60+0.15)/0.418));
f=f*f*(3-2*f);
c.setXYZ(i,hz[0]+(top[0]-hz[0])*f,hz[1]+(top[1]-hz[1])*f,hz[2]+(top[2]-hz[2])*f);
}
c.needsUpdate=true;
day=h>=6&&h<=18;
duv=day?(h-6)/12:((h+6)%24)/12;
disc.material.color.setRGB(dk[0],dk[1],dk[2]);
halo.material.color.setRGB(day?1:0.94,day?0.92:0.94,day?0.72:0.9);
var r=day?1.9:1.35;disc.scale.setScalar(r);halo.scale.setScalar(r*8.4);
key.color.setRGB(dk[0],dk[1],dk[2]);
key.intensity=day?2.7:0.85;
amb.color.setRGB(0.45+(hz[0]+top[0])*0.275,0.45+(hz[1]+top[1])*0.275,0.45+(hz[2]+top[2])*0.275);
amb.intensity=day?1.5:0.9;
brewM.shininess=day?70:38;
scrim.visible=h>6.5&&h<17.5;
}
}
// the disc rides the same low arc over the horizon the 2D stage draws
var tn=Math.tan(k.cam.fov*Math.PI/360),D=45;
var nx=0.12+0.72*duv,ny=-0.6+1.16*Math.sin(duv*Math.PI);
disc.position.set(nx*tn*k.cam.aspect*D,ny*tn*D,k.cam.position.z-D);
halo.position.copy(disc.position);halo.position.z+=0.4;
key.position.copy(disc.position);
}
});
}
if(!initGL()){
fit();addEventListener("resize",fit);
var clock=0;
function tick(dt){clock+=dt;if(clock>30000){clock=0;draw(false)}}
if(TE&&TE.loop)TE.loop(tick);else setInterval(function(){draw(false)},30000);
}
})();
</script></body></html>
Kedai kopi, Jalan Senja, Bandung, sejak 2019
Diseduh mengikuti langit Bandung
Pagi kami seduh terang dan ringan, sore kami seduh dalam dan manis. Halaman ini ikut berubah warna dengan jam di luar jendela kedai.
Buka 08.00 sampai 22.00Biji petani Jawa BaratAntar sekitar Dago
Empat jam seduh dalam sehari
Pagi, 08.00 sampai 11.00
Pour over biji Garut proses honey, rasa jeruk dan madu yang ringan. Roti bakar keluar dari oven jam delapan.
Siang, 11.00 sampai 15.00
Es kopi susu dengan gula aren dari Ciwidey, dan teh melati dingin untuk yang sedang tidak ingin kopi.
Senja, 15.00 sampai 18.30
Kopi susu hangat dan manual brew biji natural yang dalam. Ini jam kedai paling ramai, dan paling pelan.
Malam, 18.30 sampai 22.00
Kakao panas, affogato, dan biji 200 gram untuk dibawa pulang supaya besok pagi dimulai dari kedai kami.
Papan menu hari ini
Kopi Susu SenjaPaling sering dipesan
Rp18.000
Espresso, susu segar Lembang, gula aren Ciwidey.
Manual Brew
Rp25.000
V60 atau Kalita. Biji minggu ini: Garut honey, jeruk dan madu.
Es Kopi Senja
Rp20.000
Versi dingin dengan es batu besar supaya tidak cepat encer.
Roti Bakar Keju
Rp15.000
Sourdough dari tetangga, keju meleleh, madu tipis.
Teh Melati
Rp12.000
Diseduh per cangkir, hangat atau dingin.
Biji Kopi 200 gram
Rp85.000
Sangrai minggu ini, digiling sesuai alat seduhmu.
Bijinya kami giling sesuai alat seduhmu
2019 tahun kedai ini dibuka3 petani kopi yang kami beli langsung14 jam buka setiap hari4,9 rating dari 640 ulasan
Manual brew-nya selalu jelas dari mana bijinya. Barista sabar menjelaskan, tidak sok tahu, dan datang jam berapa pun rasanya pas dengan jamnya.DDimas, penikmat kopi, datang tiap Kamis sore
Pesan dari rumah, tiga langkah
01
Pilih dari papan menu
Semua yang ada di papan bisa diantar, kecuali manual brew yang sebaiknya diminum di kedai.
02
Kirim lewat WhatsApp
Tulis pesanan dan alamat. Kami balas dalam hitungan menit selama jam buka.
03
Diantar atau diambil
Antar untuk area Dago dan sekitarnya dalam 30 menit, atau ambil tanpa antre di bar.
Senja Sore, 15 persen
Setiap hari jam 16.00 sampai 18.00, semua kopi susu diskon 15 persen. Berlaku di kedai dan pesan antar.