Remove Coffee Station and Drinks from schedule; drop all prices

Menu is food stands only. Cart, checkout, and order cards no longer
show or compute dollar amounts (free pre-orders). e2e still 14/14.
This commit is contained in:
avi 2026-09-28 15:37:11 -05:00
commit f91c29c035

View file

@ -102,7 +102,6 @@ header{position:sticky;top:0;z-index:40;background:rgb(255 255 255/.85);backdrop
.item-emoji{width:46px;height:46px;flex:0 0 46px;border-radius:14px;background:var(--bg2);border:1px solid var(--line);display:grid;place-items:center;font-size:24px}
.item-name{font-size:15px;font-weight:800;display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.item-desc{font-size:12px;color:var(--ink2);margin-top:2px}
.item-price{margin-left:auto;font-family:var(--display);font-weight:900;font-size:16px;white-space:nowrap}
.item-foot{display:flex;align-items:center;gap:8px;margin-top:12px;flex-wrap:wrap}
.status-pill{font-size:10px;font-weight:800;letter-spacing:.8px;text-transform:uppercase;padding:5px 10px;border-radius:999px;display:inline-flex;align-items:center;gap:6px}
.status-pill .dot{width:6px;height:6px;border-radius:99px;background:currentColor}
@ -300,46 +299,36 @@ function dayLabel(offset){
const FEST={
days:[
{ tag:'DAY 1', items:[
{id:'d1-coffee', name:'Coffee Station', emoji:'☕', price:4, start:540, end:1080, tags:['VG'], desc:'Slow-drip single origin + oat milk latte, served from the wood-fired cart.'},
{id:'d1-sweets', name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1320, tags:['V','GF'], desc:'Fresh fruit skewers, sorbet cups, and festival brownies.'},
{id:'d1-taco', name:'Taco Stand', emoji:'🌮', price:8, start:720, end:870, tags:['GF'], desc:'Hand-pressed corn tortillas, slow-braised grass-fed filling, house salsa.'},
{id:'d1-bbq', name:'BBQ Pit', emoji:'🍖', price:12, start:780, end:930, tags:[], desc:'12-hour offset-smoked brisket & ribs. No seed oils — rendered tallow only.'},
{id:'d1-chicken',name:'Chicken Grill', emoji:'🍗', price:9, start:1050, end:1230, tags:['GF'], desc:'Buttermilk fried chicken (tallow-fried) with slaw.'},
{id:'d1-pasta', name:'Pasta Bar', emoji:'🍝', price:10, start:1080, end:1200, tags:['V'], desc:'Fresh-cut daily pasta, garden vegetables, house olive oil.'},
{id:'d1-drinks', name:'Drinks', emoji:'🥤', price:3, start:600, end:1320, tags:['VG'], desc:'Cold brew, kombucha, iced herbal tea, lemonade.'}
]},
{id:'d1-sweets', name:'Sweets Cart', emoji:'🍭',start:660, end:1320, tags:['V','GF'], desc:'Fresh fruit skewers, sorbet cups, and festival brownies.'},
{id:'d1-taco', name:'Taco Stand', emoji:'🌮',start:720, end:870, tags:['GF'], desc:'Hand-pressed corn tortillas, slow-braised grass-fed filling, house salsa.'},
{id:'d1-bbq', name:'BBQ Pit', emoji:'🍖',start:780, end:930, tags:[], desc:'12-hour offset-smoked brisket & ribs. No seed oils — rendered tallow only.'},
{id:'d1-chicken',name:'Chicken Grill', emoji:'🍗',start:1050, end:1230, tags:['GF'], desc:'Buttermilk fried chicken (tallow-fried) with slaw.'},
{id:'d1-pasta', name:'Pasta Bar', emoji:'🍝',start:1080, end:1200, tags:['V'], desc:'Fresh-cut daily pasta, garden vegetables, house olive oil.'}
]},,
{ tag:'DAY 2', items:[
{id:'d2-coffee', name:'Coffee Station', emoji:'☕', price:4, start:570, end:1020, tags:['VG'], desc:'Slow-drip single origin + oat milk latte.'},
{id:'d2-sweets', name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1260, tags:['V','GF'], desc:'Sorbet cups, fruit skewers, brownies.'},
{id:'d2-burger', name:'Garden Burgers', emoji:'🍔', price:11, start:720, end:900, tags:[], desc:'Grass-fed beef patty, brioche, charred onion, herb aioli.'},
{id:'d2-grill', name:'Grill 484', emoji:'🔥', price:9, start:750, end:960, tags:['GF'], desc:'Skewered vegetables & chicken over open flame.'},
{id:'d2-ramen', name:'Ramen Tent', emoji:'🍜', price:12, start:1020, end:1200, tags:['V'], desc:'Miso broth simmered all day, soft egg, scallion, pickled ginger.'},
{id:'d2-drinks', name:'Drinks', emoji:'🥤', price:3, start:570, end:1290, tags:['VG'], desc:'Cold brew, kombucha, herbal tea, lemonade.'}
]},
{id:'d2-sweets', name:'Sweets Cart', emoji:'🍭',start:660, end:1260, tags:['V','GF'], desc:'Sorbet cups, fruit skewers, brownies.'},
{id:'d2-burger', name:'Garden Burgers', emoji:'🍔',start:720, end:900, tags:[], desc:'Grass-fed beef patty, brioche, charred onion, herb aioli.'},
{id:'d2-grill', name:'Grill 484', emoji:'🔥',start:750, end:960, tags:['GF'], desc:'Skewered vegetables & chicken over open flame.'},
{id:'d2-ramen', name:'Ramen Tent', emoji:'🍜',start:1020, end:1200, tags:['V'], desc:'Miso broth simmered all day, soft egg, scallion, pickled ginger.'}
]},,
{ tag:'DAY 3', items:[
{id:'d3-coffee', name:'Coffee Station', emoji:'☕', price:4, start:540, end:1050, tags:['VG'], desc:'Slow-drip single origin, Saturday long pour.'},
{id:'d3-sweets', name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1320, tags:['V','GF'], desc:'Sorbet cups, fruit skewers, Saturday brownie batch.'},
{id:'d3-flatbread',name:'Flatbread Oven', emoji:'🫓', price:9, start:720, end:930, tags:['V','GF'], desc:'Wood-fired flatbreads — charred vegetables, whipped feta, house olive oil.'},
{id:'d3-bbq', name:'BBQ Pit', emoji:'🍖', price:12, start:780, end:960, tags:[], desc:'Offset-smoked brisket & ribs. The weekend is for the pit.'},
{id:'d3-dumplings',name:'Dumpling Bar', emoji:'🥟', price:10, start:1020, end:1230, tags:['GF'], desc:'Pan-seared vegetable & chicken dumplings, chili crisp.'},
{id:'d3-drinks', name:'Drinks', emoji:'🥤', price:3, start:600, end:1320, tags:['VG'], desc:'Cold brew, kombucha, iced herbal tea, lemonade.'}
]},
{id:'d3-sweets', name:'Sweets Cart', emoji:'🍭',start:660, end:1320, tags:['V','GF'], desc:'Sorbet cups, fruit skewers, Saturday brownie batch.'},
{id:'d3-flatbread',name:'Flatbread Oven', emoji:'🫓',start:720, end:930, tags:['V','GF'], desc:'Wood-fired flatbreads — charred vegetables, whipped feta, house olive oil.'},
{id:'d3-bbq', name:'BBQ Pit', emoji:'🍖',start:780, end:960, tags:[], desc:'Offset-smoked brisket & ribs. The weekend is for the pit.'},
{id:'d3-dumplings',name:'Dumpling Bar', emoji:'🥟',start:1020, end:1230, tags:['GF'], desc:'Pan-seared vegetable & chicken dumplings, chili crisp.'}
]},,
{ tag:'DAY 4', items:[
{id:'d4-coffee', name:'Coffee Station', emoji:'☕', price:4, start:540, end:1020, tags:['VG'], desc:'Slow-drip single origin + oat milk latte.'},
{id:'d4-sweets', name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1290, tags:['V','GF'], desc:'Sorbet cups, fruit skewers, brownies.'},
{id:'d4-taco', name:'Taco Stand', emoji:'🌮', price:8, start:720, end:870, tags:['GF'], desc:'Corn tortillas, slow-braised grass-fed filling, house salsa.'},
{id:'d4-pasta', name:'Pasta Bar', emoji:'🍝', price:10, start:900, end:1140, tags:['V'], desc:'Sunday pasta — fresh-cut, garden vegetables, house olive oil.'},
{id:'d4-grill', name:'Grill 484', emoji:'🔥', price:9, start:960, end:1170, tags:['GF'], desc:'Skewered vegetables & chicken over open flame.'},
{id:'d4-drinks', name:'Drinks', emoji:'🥤', price:3, start:600, end:1290, tags:['VG'], desc:'Cold brew, kombucha, iced herbal tea, lemonade.'}
]},
{id:'d4-sweets', name:'Sweets Cart', emoji:'🍭',start:660, end:1290, tags:['V','GF'], desc:'Sorbet cups, fruit skewers, brownies.'},
{id:'d4-taco', name:'Taco Stand', emoji:'🌮',start:720, end:870, tags:['GF'], desc:'Corn tortillas, slow-braised grass-fed filling, house salsa.'},
{id:'d4-pasta', name:'Pasta Bar', emoji:'🍝',start:900, end:1140, tags:['V'], desc:'Sunday pasta — fresh-cut, garden vegetables, house olive oil.'},
{id:'d4-grill', name:'Grill 484', emoji:'🔥',start:960, end:1170, tags:['GF'], desc:'Skewered vegetables & chicken over open flame.'}
]},,
{ tag:'FINAL DAY', items:[
{id:'d5-coffee', name:'Coffee Station', emoji:'☕', price:4, start:600, end:960, tags:['VG'], desc:'One last pour. Single origin, slow drip.'},
{id:'d5-sweets', name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1200, tags:['V','GF'], desc:'Sorbet, fruit, brownies — farewell batch.'},
{id:'d5-taco', name:'Taco Stand', emoji:'🌮', price:8, start:720, end:840, tags:['GF'], desc:'Corn tortillas, slow-braised filling.'},
{id:'d5-feast', name:'Farewell Feast · BBQ', emoji:'🍖', price:14, start:720, end:960, tags:[], desc:'The full spread: brisket, ribs, tallow fries, slaw, bread.'},
{id:'d5-pasta', name:'Pasta Bar', emoji:'🍝', price:10, start:1020, end:1170, tags:['V'], desc:'Fresh pasta, garden vegetables, olive oil.'},
{id:'d5-drinks', name:'Drinks', emoji:'🥤', price:3, start:600, end:1230, tags:['VG'], desc:'Cold brew, kombucha, iced herbal tea, lemonade.'}
]}
{id:'d5-sweets', name:'Sweets Cart', emoji:'🍭',start:660, end:1200, tags:['V','GF'], desc:'Sorbet, fruit, brownies — farewell batch.'},
{id:'d5-taco', name:'Taco Stand', emoji:'🌮',start:720, end:840, tags:['GF'], desc:'Corn tortillas, slow-braised filling.'},
{id:'d5-feast', name:'Farewell Feast · BBQ', emoji:'🍖',start:720, end:960, tags:[], desc:'The full spread: brisket, ribs, tallow fries, slaw, bread.'},
{id:'d5-pasta', name:'Pasta Bar', emoji:'🍝',start:1020, end:1170, tags:['V'], desc:'Fresh pasta, garden vegetables, olive oil.'}
]},
]
};
FEST.days.forEach((d,i)=>{ const L=dayLabel(i); d.name=L.name; d.date=L.date; d.iso=L.iso; });
@ -363,7 +352,6 @@ const fmtT=min=>{let h=Math.floor(min/60),m=min%60,ap=h>=12?'PM':'AM';h=h%12||12
const fmtDur=min=>{const h=Math.floor(min/60),m=Math.round(min%60);return h? (h+'h'+(m?' '+m+'m':'')) : m+'m';};
const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
function toast(msg){const t=$('#toast');t.textContent=msg;t.classList.add('show');clearTimeout(t._x);t._x=setTimeout(()=>t.classList.remove('show'),2200);}
function cartTotal(){let t=0;for(const id in state.cart){const it=findItem(id);if(it)t+=it.price*state.cart[id];}return t;}
function cartCount(){return Object.values(state.cart).reduce((a,b)=>a+b,0);}
function findItem(id){for(const d of FEST.days)for(const it of d.items)if(it.id===id)return it;return null;}
function findDayOf(id){for(let i=0;i<FEST.days.length;i++)if(FEST.days[i].items.some(x=>x.id===id))return i;}
@ -407,7 +395,6 @@ function renderTimeline(){
'<div class="item-emoji">'+item.emoji+'</div>'+
'<div><div class="item-name">'+item.name+' <span class="tags">'+item.tags.map(t=>'<span class="tag">'+t+'</span>').join('')+'</span></div>'+
'<div class="item-desc">'+item.desc+'</div></div>'+
'<div class="item-price">'+fmt$(item.price)+'</div>'+
'</div>'+
'<div class="item-foot">'+
'<span class="status-pill '+pill+'"><span class="dot"></span>'+st.label+'</span>'+
@ -479,12 +466,10 @@ function stepCart(){
'<div class="em">'+it.emoji+'</div>'+
'<div><div class="nm">'+it.name+'</div><div class="pt">'+fmtT(it.start)+' – '+fmtT(it.end)+'</div></div>'+
'<div class="qty"><button data-dec="'+id+'">−</button><span>'+state.cart[id]+'</span><button data-inc="'+id+'">+</button></div>'+
'<div class="lp">'+fmt$(it.price*state.cart[id])+'</div></div>';
'</div>';
}).join('');
const total=cartTotal();
return '<div class="grab"></div><h3>YOUR ORDER</h3><div class="sub">SOLARPUNK SUMMIT · pickup at the stand window</div>'+
(rows||'<div class="empty" style="padding:30px"><div class="big">🧺</div>Nothing here yet — add something from the schedule.</div>')+
'<div class="total-row"><span class="t">Total</span><span class="v">'+fmt$(total)+'</span></div>'+
'<div class="btc-est">Free pre-order — pay at the stand when you pick up.</div>'+
'<button class="btn btn-primary" id="to-checkout" '+(cartCount()===0?'disabled':'')+'>Reserve pickup →</button>';
}
@ -502,7 +487,7 @@ function stepCheckout(){
'<div class="field"><label>Name</label><input id="co-name" type="text" placeholder="Your name at the stand" maxlength="40"></div>'+
'<div class="field"><label>Pickup window</label><select id="co-window">'+opts+'</select></div>'+
'<div class="field"><label>Notes (optional)</label><textarea id="co-notes" placeholder="Allergies, extras, anything the kitchen should know…"></textarea></div>'+
'<div class="total-row"><span class="t">Total · '+cartCount()+' items</span><span class="v">'+fmt$(cartTotal())+'</span></div>'+
'<div class="total-row"><span class="t">Total</span><span class="v">'+cartCount()+' items</span></div>'+
'<div id="reserve-err" style="color:#b91c1c;font-size:12px;font-weight:700;margin-top:6px;display:none"></div>'+
'<button class="btn btn-pink" id="to-reserve" style="margin-top:8px">🍽️&nbsp; Place pre-order</button>'+
'<button class="btn btn-ghost" style="width:100%;margin-top:8px" id="back-cart">← Back to cart</button>';
@ -552,7 +537,6 @@ function afterCartChange(){saveCart();renderFab();renderTimeline();renderSheet()
/* ============================== ORDERS (server-backed) ============================== */
async function placeOrder(info){
const usd=cartTotal();
const day=FEST.days.find(d=>d.items.some(it=>info.window&&Math.abs(it.start-info.window[0])<1&&Math.abs(it.end-info.window[1])<1))||FEST.days[state.dayIdx];
const pickupLabel=fmtT(info.window[0])+' \u2013 '+fmtT(info.window[1]);
const ids=Object.keys(state.cart);
@ -565,7 +549,7 @@ async function placeOrder(info){
body:JSON.stringify({
name:info.name,notes:info.notes,items:state.cart,
window:info.window,pickupLabel,itemsSummary,
stand,totalUsd:usd,
stand,
day:day.name+' '+day.date
})
});
@ -644,7 +628,7 @@ function renderOrders(){
'<div class="order-top"><span class="order-code">'+esc(o.code)+'</span>'+badge+'</div>'+
'<div class="order-meta">'+esc(o.day||'')+' \u00b7 pickup '+esc(o.pickupLabel||'')+' \u00b7 '+esc(o.name||'')+(o.notes?' \u00b7 \u201c'+esc(o.notes)+'\u201d':'')+'</div>'+
'<div class="order-items">'+esc(o.itemsSummary||'')+'</div>'+
'<div class="order-total"><span>'+fmt$(o.totalUsd||0)+'</span><span class="btc">'+esc(o.stand||'')+'</span></div>'+
'<div class="order-total"><span class="btc">'+esc(o.stand||'')+'</span></div>'+
readyBanner+
(o.status!=='COLLECTED'&&o.status!=='CANCELLED'?'<div class="order-actions"><button class="btn btn-ghost btn-sm" data-recheck="'+esc(o.code)+'">Re-check status</button></div>':'')+
'</div>';