583 lines
30 KiB
Text
583 lines
30 KiB
Text
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||
<meta name="theme-color" content="#FFFFFF">
|
||
<link rel="icon" type="image/png" sizes="64x64" href="icon.png">
|
||
<link rel="icon" type="image/png" sizes="192x192" href="icon-192.png">
|
||
<link rel="icon" type="image/png" sizes="512x512" href="icon-512.png">
|
||
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png">
|
||
<link rel="manifest" href="manifest.webmanifest">
|
||
<title>K484 · A Private Membership Association</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root{
|
||
--bg:#FFFFFF; --bg2:#f8fafc;
|
||
--ink:#0A0A0A; --ink2:#475569; --muted:#94a3b8;
|
||
--line:#e2e8f0; --line2:#cbd5e1;
|
||
--black:#0a0a0a;
|
||
--radius:18px;
|
||
--shadow:0 1px 2px rgb(10 10 10 / .05), 0 8px 24px -12px rgb(10 10 10 / .12);
|
||
--font:-apple-system,'Avenir Next','Avenir',system-ui,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
|
||
--display:'Orbitron',var(--font);
|
||
}
|
||
*{box-sizing:border-box;margin:0;padding:0}
|
||
html{line-height:1.5;scroll-behavior:smooth}
|
||
body{font-family:var(--font);background:var(--bg);color:var(--ink);-webkit-font-smoothing:antialiased}
|
||
a{color:inherit}
|
||
.wrap{max-width:1040px;margin:0 auto;padding:0 22px}
|
||
|
||
/* ============ HEADER ============ */
|
||
header{position:sticky;top:0;z-index:50;background:rgba(255,255,255,.9);backdrop-filter:blur(10px);border-bottom:1px solid var(--line)}
|
||
.header-in{display:flex;align-items:center;gap:12px;padding:12px 22px;max-width:1040px;margin:0 auto}
|
||
.logo{width:46px;height:46px;flex:0 0 46px;object-fit:contain}
|
||
.brand b{font-family:var(--display);font-weight:900;font-size:15px;letter-spacing:2px;display:block;line-height:1.2}
|
||
.brand span{font-size:11px;color:var(--muted);letter-spacing:1px}
|
||
.header-right{margin-left:auto;display:flex;align-items:center;gap:10px}
|
||
.member-chip{font-family:var(--display);font-size:10px;font-weight:700;letter-spacing:1px;border:1px solid var(--line2);border-radius:99px;padding:6px 12px;color:var(--ink2)}
|
||
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;border:none;cursor:pointer;font-family:var(--font);font-weight:700;font-size:14px;border-radius:99px;padding:12px 22px;transition:transform .12s,opacity .12s;text-decoration:none}
|
||
.btn:active{transform:scale(.97)}
|
||
.btn-primary{background:var(--black);color:#fff}
|
||
.btn-primary:hover{opacity:.88}
|
||
.btn-ghost{background:var(--bg);color:var(--ink);border:1.5px solid var(--line2)}
|
||
.btn-ghost:hover{border-color:var(--ink)}
|
||
.btn-sm{padding:9px 16px;font-size:13px}
|
||
|
||
/* ============ HERO ============ */
|
||
.hero{text-align:center;padding:72px 22px 56px;position:relative;overflow:hidden}
|
||
.hero-logo{width:140px;height:140px;object-fit:contain;margin:0 auto 26px;display:block;animation:fadeup 1s ease both}
|
||
.hero-kicker{font-family:var(--display);font-size:11px;font-weight:700;letter-spacing:4px;color:var(--muted);animation:fadeup .9s ease both .1s}
|
||
.hero h1{font-family:var(--display);font-weight:900;font-size:clamp(40px,8vw,72px);letter-spacing:6px;margin:10px 0 6px;animation:fadeup .9s ease both .2s}
|
||
.hero .assoc{font-size:clamp(15px,2.4vw,19px);color:var(--ink2);letter-spacing:2px;text-transform:uppercase;animation:fadeup .9s ease both .3s}
|
||
.hero-sub{max-width:560px;margin:18px auto 0;font-size:14px;color:var(--ink2);animation:fadeup .9s ease both .4s}
|
||
.tagline-row{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin:28px auto 6px;animation:fadeup .9s ease both .5s}
|
||
.tagline{font-family:var(--display);font-size:10px;font-weight:700;letter-spacing:2px;border:1.5px solid var(--ink);border-radius:99px;padding:8px 16px;text-transform:uppercase}
|
||
.hero-cta{display:flex;flex-wrap:wrap;justify-content:center;gap:12px;margin-top:30px;animation:fadeup .9s ease both .6s}
|
||
@keyframes fadeup{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}
|
||
|
||
/* ============ SECTIONS ============ */
|
||
section{padding:64px 0}
|
||
.sec-head{text-align:center;margin-bottom:36px}
|
||
.sec-head h2{font-family:var(--display);font-weight:900;font-size:clamp(22px,4vw,30px);letter-spacing:3px;text-transform:uppercase}
|
||
.sec-head .sub{font-size:12px;color:var(--muted);letter-spacing:2px;text-transform:uppercase;margin-top:8px}
|
||
.divider{border:none;border-top:1px solid var(--line);max-width:1040px;margin:0 auto}
|
||
|
||
/* ============ MENU ============ */
|
||
#menu{background:var(--bg2)}
|
||
.day-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;align-items:start}
|
||
.day-card{background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
|
||
.day-card.today{border-color:var(--ink);border-width:2px}
|
||
.day-head{padding:16px 18px 12px;border-bottom:1px solid var(--line);display:flex;align-items:baseline;gap:10px}
|
||
.day-tag{font-family:var(--display);font-size:11px;font-weight:900;letter-spacing:2px}
|
||
.day-date{font-size:12px;color:var(--muted);letter-spacing:1px;margin-left:auto;text-transform:uppercase}
|
||
.today-chip{font-family:var(--display);font-size:9px;font-weight:700;letter-spacing:1px;background:var(--black);color:#fff;border-radius:99px;padding:3px 8px}
|
||
.day-list{padding:6px 0}
|
||
.day-row{display:flex;align-items:center;gap:10px;padding:10px 18px;border-bottom:1px solid var(--line)}
|
||
.day-row:last-child{border-bottom:none}
|
||
.day-em{font-size:18px;width:26px;text-align:center;flex:0 0 26px}
|
||
.day-info{min-width:0}
|
||
.day-name{font-weight:700;font-size:14px}
|
||
.day-time{font-family:var(--display);font-size:10px;color:var(--muted);letter-spacing:.5px;margin-top:1px}
|
||
.day-price{font-family:var(--display);font-weight:900;font-size:14px;margin-left:auto}
|
||
.day-foot{padding:14px 18px}
|
||
.day-foot a{display:block;text-align:center}
|
||
.tags{display:inline-flex;gap:4px;margin-left:6px;vertical-align:1px}
|
||
.tag{font-family:var(--display);font-size:8px;font-weight:700;letter-spacing:.5px;border:1px solid var(--line2);border-radius:99px;padding:1px 6px;color:var(--muted)}
|
||
|
||
/* ============ HOW IT WORKS ============ */
|
||
.steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:18px}
|
||
.step{border:1px solid var(--line);border-radius:var(--radius);padding:24px 20px;background:var(--bg)}
|
||
.step .n{font-family:var(--display);font-weight:900;font-size:26px;color:var(--muted)}
|
||
.step h3{font-family:var(--display);font-size:13px;font-weight:700;letter-spacing:2px;text-transform:uppercase;margin:8px 0 6px}
|
||
.step p{font-size:13px;color:var(--ink2)}
|
||
|
||
/* ============ MEMBERSHIP ============ */
|
||
#join{background:var(--bg2)}
|
||
.join-wrap{max-width:640px;margin:0 auto}
|
||
.note-box{border:1px solid var(--line2);border-radius:14px;padding:14px 18px;font-size:13px;color:var(--ink2);background:var(--bg);margin-bottom:22px}
|
||
.note-box b{color:var(--ink)}
|
||
.form-card{background:var(--bg);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);padding:26px}
|
||
.field{margin-bottom:16px}
|
||
.field label{display:block;font-family:var(--display);font-size:10px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--ink2);margin-bottom:6px}
|
||
.field input{width:100%;border:1px solid var(--line2);border-radius:12px;padding:13px 14px;font-family:var(--font);font-size:14px;background:var(--bg2);color:var(--ink);outline:none;transition:border-color .15s}
|
||
.field input:focus{border-color:var(--ink)}
|
||
.sig-wrap{border:1.5px dashed var(--line2);border-radius:12px;position:relative;background:var(--bg2)}
|
||
.sig-canvas{display:block;width:100%;height:140px;touch-action:none;cursor:crosshair;border-radius:12px}
|
||
.sig-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-size:12px;color:var(--muted);letter-spacing:1px;pointer-events:none;text-transform:uppercase}
|
||
.sig-tools{display:flex;align-items:center;justify-content:space-between;margin-top:8px}
|
||
.sig-status{font-size:11px;color:var(--muted);letter-spacing:.5px}
|
||
.sig-status.ok{font-weight:700;color:var(--ink)}
|
||
.link-btn{background:none;border:none;cursor:pointer;font-family:var(--display);font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--muted);text-decoration:underline}
|
||
.link-btn:hover{color:var(--ink)}
|
||
.covenant{background:var(--bg2);border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin:18px 0;max-height:190px;overflow-y:auto;font-size:12.5px;color:var(--ink2)}
|
||
.covenant h4{font-family:var(--display);font-size:11px;font-weight:700;letter-spacing:1.5px;text-transform:uppercase;color:var(--ink);margin-bottom:8px}
|
||
.covenant ol{padding-left:18px;display:grid;gap:6px}
|
||
.agree-row{display:flex;gap:10px;align-items:flex-start;margin:16px 0}
|
||
.agree-row input{width:18px;height:18px;margin-top:1px;accent-color:var(--black);flex:0 0 18px}
|
||
.agree-row label{font-size:12.5px;color:var(--ink2)}
|
||
.form-err{font-size:12px;font-weight:700;color:var(--ink);background:var(--bg2);border:1.5px solid var(--ink);border-radius:10px;padding:9px 12px;margin-bottom:12px;display:none}
|
||
.form-err.show{display:block}
|
||
.form-actions{display:grid;gap:10px}
|
||
.member-done{text-align:center;padding:10px 0}
|
||
.member-done .check{width:56px;height:56px;border-radius:50%;background:var(--black);color:#fff;font-size:26px;display:flex;align-items:center;justify-content:center;margin:0 auto 14px}
|
||
.member-done h3{font-family:var(--display);font-weight:900;font-size:17px;letter-spacing:2px}
|
||
.member-code{font-family:var(--display);font-weight:900;font-size:22px;letter-spacing:3px;background:var(--bg2);border:1px solid var(--line);border-radius:12px;padding:12px;margin:16px 0}
|
||
.member-meta{font-size:13px;color:var(--ink2);margin-bottom:6px}
|
||
|
||
/* ============ FOOTER ============ */
|
||
footer{border-top:1px solid var(--line);padding:28px 22px 34px;text-align:center}
|
||
footer .f-logo{width:52px;height:52px;object-fit:contain;margin:0 auto 10px;display:block}
|
||
footer .f-line{font-family:var(--display);font-size:10px;font-weight:700;letter-spacing:3px;text-transform:uppercase;color:var(--muted)}
|
||
footer .f-sub{font-size:11px;color:var(--muted);margin-top:6px}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header>
|
||
<div class="header-in">
|
||
<img class="logo" src="logo.png" alt="K484">
|
||
<div class="brand"><b>KITCHEN 484</b><span>SOLARPUNK SUMMIT</span></div>
|
||
<div class="header-right">
|
||
<span class="member-chip" id="member-chip" hidden></span>
|
||
<a class="btn btn-ghost btn-sm" href="#join" id="nav-join">Join</a>
|
||
<a class="btn btn-primary btn-sm" href="index.html">Food Menu</a>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="hero">
|
||
<img class="hero-logo" src="logo.png" alt="K484">
|
||
<div class="hero-kicker">SOLARPUNK SUMMIT · Oct 8–12</div>
|
||
<h1>K484</h1>
|
||
<div class="assoc">A Private Membership Association</div>
|
||
<p class="hero-sub">Five days. Off-grid kitchen. No seed oils, all organic grass-fed, gluten free and vegetarian friendly. See when the food is on, pre-order, and pay with Bitcoin — no cash, no cards.</p>
|
||
<div class="tagline-row">
|
||
<span class="tagline">No Seed Oils</span>
|
||
<span class="tagline">All Organic Grass Fed</span>
|
||
<span class="tagline">Gluten Free & Vegetarian Friendly</span>
|
||
<span class="tagline">Off Grid — Zero Waste</span>
|
||
</div>
|
||
<div class="hero-cta">
|
||
<a class="btn btn-primary" href="index.html">Food Menu →</a>
|
||
<a class="btn btn-ghost" href="#join">Become a Member</a>
|
||
</div>
|
||
</div>
|
||
|
||
<section id="menu">
|
||
<div class="wrap">
|
||
<div class="sec-head">
|
||
<h2>Food Menu</h2>
|
||
<div class="sub">Off Grid — Zero Waste · What's on, and when</div>
|
||
</div>
|
||
<div class="day-grid" id="day-grid"></div>
|
||
<div style="text-align:center;margin-top:28px">
|
||
<a class="btn btn-primary" href="index.html">Open the Full Schedule & Order →</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="divider"></div>
|
||
|
||
<section id="how">
|
||
<div class="wrap">
|
||
<div class="sec-head">
|
||
<h2>How It Works</h2>
|
||
<div class="sub">Five days, three steps</div>
|
||
</div>
|
||
<div class="steps">
|
||
<div class="step">
|
||
<div class="n">01</div>
|
||
<h3>Check the Schedule</h3>
|
||
<p>Every stand posts its serving window for each day. The live timeline shows what's open right now and what's coming.</p>
|
||
</div>
|
||
<div class="step">
|
||
<div class="n">02</div>
|
||
<h3>Pre-Order Your Window</h3>
|
||
<p>Add items to your order, pick a pickup window, and name your order so we can call it out at the stand.</p>
|
||
</div>
|
||
<div class="step">
|
||
<div class="n">03</div>
|
||
<h3>Pay with Bitcoin</h3>
|
||
<p>Pay on-chain or via Lightning through BTCPay Server. Get your pickup code the moment your payment confirms.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<div class="divider"></div>
|
||
|
||
<section id="join">
|
||
<div class="wrap join-wrap">
|
||
<div class="sec-head">
|
||
<h2>Membership</h2>
|
||
<div class="sub">Kitchen 484 · Private Membership Association</div>
|
||
</div>
|
||
|
||
<div id="join-form-wrap">
|
||
<div class="note-box">
|
||
<b>Note:</b> This is a private, members-only association. Participation is absolutely voluntary. Membership is granted for the duration of the festival (24 hours per day, Oct 8–12), beginning upon acceptance of this application.
|
||
</div>
|
||
|
||
<form class="form-card" id="join-form" novalidate>
|
||
<div class="form-err" id="join-err"></div>
|
||
|
||
<div class="field">
|
||
<label for="m-name">Full Name</label>
|
||
<input id="m-name" type="text" placeholder="Your full name" maxlength="80" autocomplete="name">
|
||
</div>
|
||
<div class="field">
|
||
<label for="m-email">Email</label>
|
||
<input id="m-email" type="email" placeholder="you@example.com" maxlength="120" autocomplete="email">
|
||
</div>
|
||
<div class="field">
|
||
<label for="m-phone">Phone</label>
|
||
<input id="m-phone" type="tel" placeholder="+1 (555) 000-0000" maxlength="30" autocomplete="tel">
|
||
</div>
|
||
|
||
<div class="field">
|
||
<label>Digital Signature</label>
|
||
<div class="sig-wrap">
|
||
<canvas class="sig-canvas" id="sig-canvas"></canvas>
|
||
<div class="sig-hint" id="sig-hint">Sign here with mouse or finger</div>
|
||
</div>
|
||
<div class="sig-tools">
|
||
<span class="sig-status" id="sig-status">Signature required to apply</span>
|
||
<button type="button" class="link-btn" id="sig-clear">Clear</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="covenant">
|
||
<h4>K484 Membership Covenant</h4>
|
||
<p style="margin-bottom:8px">By submitting this application and becoming a member of Kitchen 484, I acknowledge and agree that:</p>
|
||
<ol>
|
||
<li>I am joining a private, members-only association, not a public business.</li>
|
||
<li>Access to kitchen activities, meals, and services is limited to accepted members only.</li>
|
||
<li>I agree to abide by the rules, governance, and confidentiality requirements outlined in this Covenant.</li>
|
||
<li>Membership is granted for a 24-hour period beginning upon acceptance of this application, unless ended earlier by the member or by Kitchen 484.</li>
|
||
<li>I acknowledge that my membership, participation, and personal information are confidential, and that I will respect the privacy of Kitchen 484 and other members.</li>
|
||
<li>I acknowledge that Kitchen 484 operates under internal Articles, Bylaws, and policies, which are not publicly distributed, and that I do not acquire governance, voting, or inspection rights by virtue of my membership.</li>
|
||
</ol>
|
||
</div>
|
||
|
||
<div class="agree-row">
|
||
<input type="checkbox" id="m-agree">
|
||
<label for="m-agree">"I have read and agree to the Kitchen 484 Membership Covenant, and I voluntarily apply for membership."</label>
|
||
</div>
|
||
|
||
<div class="form-actions">
|
||
<button type="submit" class="btn btn-primary">Sign & Apply for Membership</button>
|
||
<a class="btn btn-ghost" href="index.html" style="text-decoration:none">Browse the Food Menu First</a>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="form-card member-done" id="join-done" hidden>
|
||
<div class="check">✓</div>
|
||
<h3>APPLICATION SUBMITTED</h3>
|
||
<div class="member-code" id="done-code">K484-XXXX</div>
|
||
<div class="member-meta" id="done-name"></div>
|
||
<div class="member-meta" id="done-date"></div>
|
||
<p class="member-meta" style="margin-top:10px;font-size:12px;color:var(--muted)">Keep this code — it is your member reference. Your signed covenant is available for download and printing.</p>
|
||
<div class="form-actions" style="margin-top:18px">
|
||
<button class="btn btn-primary" id="btn-download">Download Signed Agreement</button>
|
||
<div style="display:flex;gap:10px">
|
||
<a class="btn btn-ghost" href="index.html" style="flex:1;text-decoration:none">Open Food Menu</a>
|
||
<button class="btn btn-ghost" id="btn-new-apply" style="flex:1">New Application</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<footer>
|
||
<img class="f-logo" src="logo.png" alt="K484">
|
||
<div class="f-line">Off Grid — Zero Waste</div>
|
||
<div class="f-sub">KITCHEN 484 · SOLARPUNK SUMMIT · Oct 8–12 · No cash, no cards — Bitcoin only</div>
|
||
</footer>
|
||
|
||
<script>
|
||
/* ============================== DATA (mirrors index.html) ============================== */
|
||
const FEST_START=new Date(2026,9,8); /* Oct 8, 2026 — festival start (coincides with hero dates) */
|
||
function dayLabel(offset){
|
||
const d=new Date(FEST_START); d.setDate(d.getDate()+offset);
|
||
return {
|
||
name:d.toLocaleDateString('en-US',{weekday:'short'}),
|
||
date:d.toLocaleDateString('en-US',{month:'short',day:'numeric'}),
|
||
iso:d.toISOString().slice(0,10)
|
||
};
|
||
}
|
||
const FEST={
|
||
days:[
|
||
{ tag:'DAY 1', items:[
|
||
{name:'Coffee Station', emoji:'☕', price:4, start:540, end:1080, tags:['VG']},
|
||
{name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1320, tags:['V','GF']},
|
||
{name:'Taco Stand', emoji:'🌮', price:8, start:720, end:870, tags:['GF']},
|
||
{name:'BBQ Pit', emoji:'🍖', price:12, start:780, end:930, tags:[]},
|
||
{name:'Chicken Grill', emoji:'🍗', price:9, start:1050, end:1230, tags:['GF']},
|
||
{name:'Pasta Bar', emoji:'🍝', price:10, start:1080, end:1200, tags:['V']},
|
||
{name:'Drinks', emoji:'🥤', price:3, start:600, end:1320, tags:['VG']}
|
||
]},
|
||
{ tag:'DAY 2', items:[
|
||
{name:'Coffee Station', emoji:'☕', price:4, start:570, end:1020, tags:['VG']},
|
||
{name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1260, tags:['V','GF']},
|
||
{name:'Garden Burgers', emoji:'🍔', price:11, start:720, end:900, tags:[]},
|
||
{name:'Grill 484', emoji:'🔥', price:9, start:750, end:960, tags:['GF']},
|
||
{name:'Ramen Tent', emoji:'🍜', price:12, start:1020, end:1200, tags:['V']},
|
||
{name:'Drinks', emoji:'🥤', price:3, start:570, end:1290, tags:['VG']}
|
||
]},
|
||
{ tag:'DAY 3', items:[
|
||
{name:'Coffee Station', emoji:'☕', price:4, start:540, end:1050, tags:['VG']},
|
||
{name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1320, tags:['V','GF']},
|
||
{name:'Flatbread Oven', emoji:'🫓', price:9, start:720, end:930, tags:['V','GF']},
|
||
{name:'BBQ Pit', emoji:'🍖', price:12, start:780, end:960, tags:[]},
|
||
{name:'Dumpling Bar', emoji:'🥟', price:10, start:1020, end:1230, tags:['GF']},
|
||
{name:'Drinks', emoji:'🥤', price:3, start:600, end:1320, tags:['VG']}
|
||
]},
|
||
{ tag:'DAY 4', items:[
|
||
{name:'Coffee Station', emoji:'☕', price:4, start:540, end:1020, tags:['VG']},
|
||
{name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1290, tags:['V','GF']},
|
||
{name:'Taco Stand', emoji:'🌮', price:8, start:720, end:870, tags:['GF']},
|
||
{name:'Pasta Bar', emoji:'🍝', price:10, start:900, end:1140, tags:['V']},
|
||
{name:'Grill 484', emoji:'🔥', price:9, start:960, end:1170, tags:['GF']},
|
||
{name:'Drinks', emoji:'🥤', price:3, start:600, end:1290, tags:['VG']}
|
||
]},
|
||
{ tag:'FINAL DAY', items:[
|
||
{name:'Coffee Station', emoji:'☕', price:4, start:600, end:960, tags:['VG']},
|
||
{name:'Sweets Cart', emoji:'🍭', price:5, start:660, end:1200, tags:['V','GF']},
|
||
{name:'Taco Stand', emoji:'🌮', price:8, start:720, end:840, tags:['GF']},
|
||
{name:'Farewell Feast · BBQ', emoji:'🍖', price:14, start:720, end:960, tags:[]},
|
||
{name:'Pasta Bar', emoji:'🍝', price:10, start:1020, end:1170, tags:['V']},
|
||
{name:'Drinks', emoji:'🥤', price:3, start:600, end:1230, tags:['VG']}
|
||
]}
|
||
]
|
||
};
|
||
FEST.days.forEach((d,i)=>{ const L=dayLabel(i); d.name=L.name; d.date=L.date; d.iso=L.iso; });
|
||
|
||
const $=s=>document.querySelector(s);
|
||
const esc=s=>String(s).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
|
||
const fmtT=min=>{let h=Math.floor(min/60),m=min%60,ap=h>=12?'PM':'AM';h=h%12||12;return h+':'+String(m).padStart(2,'0')+' '+ap;};
|
||
|
||
/* ============================== MENU CARDS ============================== */
|
||
function renderMenu(){
|
||
$('#day-grid').innerHTML=FEST.days.map((d,i)=>{
|
||
const rows=[...d.items].sort((a,b)=>a.start-b.start).map(it=>
|
||
'<div class="day-row">'+
|
||
'<div class="day-em">'+it.emoji+'</div>'+
|
||
'<div class="day-info">'+
|
||
'<div class="day-name">'+it.name+'<span class="tags">'+it.tags.map(t=>'<span class="tag">'+t+'</span>').join('')+'</span></div>'+
|
||
'<div class="day-time">'+fmtT(it.start)+' – '+fmtT(it.end)+'</div>'+
|
||
'</div>'+
|
||
'<div class="day-price">$'+it.price+'</div>'+
|
||
'</div>').join('');
|
||
return '<div class="day-card'+(i===0?' today':'')+'">'+
|
||
'<div class="day-head">'+
|
||
'<span class="day-tag">'+d.tag+'</span>'+
|
||
(i===0?'<span class="today-chip">TODAY</span>':'')+
|
||
'<span class="day-date">'+d.name+' '+d.date+'</span>'+
|
||
'</div>'+
|
||
'<div class="day-list">'+rows+'</div>'+
|
||
'<div class="day-foot"><a class="btn btn-ghost btn-sm" href="index.html">Order for '+d.tag+' →</a></div>'+
|
||
'</div>';
|
||
}).join('');
|
||
}
|
||
renderMenu();
|
||
|
||
/* ============================== SIGNATURE PAD ============================== */
|
||
const canvas=$('#sig-canvas');
|
||
const ctx=canvas.getContext('2d');
|
||
let drawing=false, lastPt=null, hasInk=false;
|
||
|
||
function sizeCanvas(){
|
||
const r=canvas.getBoundingClientRect();
|
||
const dpr=window.devicePixelRatio||1;
|
||
const img=canvas.width?canvas.toDataURL():null;
|
||
canvas.width=Math.max(1,r.width*dpr);
|
||
canvas.height=140*dpr;
|
||
ctx.scale(dpr,dpr);
|
||
ctx.lineWidth=2.2;
|
||
ctx.lineCap='round';
|
||
ctx.lineJoin='round';
|
||
ctx.strokeStyle='#0A0A0A';
|
||
if(img){const im=new Image();im.onload=()=>ctx.drawImage(im,0,0,r.width,140);im.src=img;}
|
||
}
|
||
function pt(e){
|
||
const r=canvas.getBoundingClientRect();
|
||
const t=e.touches?e.touches[0]:e;
|
||
return {x:t.clientX-r.left,y:t.clientY-r.top};
|
||
}
|
||
function startDraw(e){
|
||
drawing=true; lastPt=pt(e);
|
||
// draw a dot so taps (touch users) register ink, not just drags
|
||
ctx.beginPath();
|
||
ctx.arc(lastPt.x,lastPt.y,1.2,0,Math.PI*2);
|
||
ctx.fillStyle='#0A0A0A';
|
||
ctx.fill();
|
||
if(!hasInk){
|
||
hasInk=true;
|
||
$('#sig-hint').style.display='none';
|
||
const st=$('#sig-status');
|
||
st.textContent='✓ Signed — you can apply now';
|
||
st.classList.add('ok');
|
||
}
|
||
e.preventDefault();
|
||
}
|
||
function moveDraw(e){
|
||
if(!drawing)return;
|
||
const p=pt(e);
|
||
ctx.beginPath();
|
||
ctx.moveTo(lastPt.x,lastPt.y);
|
||
ctx.lineTo(p.x,p.y);
|
||
ctx.stroke();
|
||
lastPt=p;
|
||
if(!hasInk){
|
||
hasInk=true;
|
||
$('#sig-hint').style.display='none';
|
||
const st=$('#sig-status');
|
||
st.textContent='✓ Signed — you can apply now';
|
||
st.classList.add('ok');
|
||
}
|
||
e.preventDefault();
|
||
}
|
||
function endDraw(){drawing=false;lastPt=null;}
|
||
|
||
canvas.addEventListener('mousedown',startDraw);
|
||
canvas.addEventListener('mousemove',moveDraw);
|
||
window.addEventListener('mouseup',endDraw);
|
||
canvas.addEventListener('touchstart',startDraw,{passive:false});
|
||
canvas.addEventListener('touchmove',moveDraw,{passive:false});
|
||
canvas.addEventListener('touchend',endDraw);
|
||
$('#sig-clear').onclick=()=>{
|
||
ctx.clearRect(0,0,canvas.width,canvas.height);
|
||
hasInk=false;
|
||
$('#sig-hint').style.display='flex';
|
||
const st=$('#sig-status');
|
||
st.textContent='Signature required to apply';
|
||
st.classList.remove('ok');
|
||
};
|
||
window.addEventListener('resize',sizeCanvas);
|
||
sizeCanvas();
|
||
|
||
/* ============================== MEMBERSHIP ============================== */
|
||
const LS_MEMBER='slk_member';
|
||
const LS_MEMBERS='slk_members';
|
||
|
||
function loadMember(){try{return JSON.parse(localStorage.getItem(LS_MEMBER)||'null');}catch(e){return null;}}
|
||
function saveMember(m){
|
||
localStorage.setItem(LS_MEMBER,JSON.stringify(m));
|
||
const all=JSON.parse(localStorage.getItem(LS_MEMBERS)||'[]');
|
||
all.push(m);
|
||
localStorage.setItem(LS_MEMBERS,JSON.stringify(all));
|
||
}
|
||
function showMember(m){
|
||
$('#join-form-wrap').hidden=true;
|
||
$('#join-done').hidden=false;
|
||
$('#done-code').textContent=m.code;
|
||
$('#done-name').textContent=m.fullName;
|
||
$('#done-date').textContent='Applied '+new Date(m.ts).toLocaleDateString('en-US',{weekday:'long',month:'long',day:'numeric',year:'numeric'});
|
||
const chip=$('#member-chip');
|
||
chip.hidden=false;
|
||
chip.textContent=m.code;
|
||
document.getElementById('nav-join').textContent='Member';
|
||
$('#join').scrollIntoView({behavior:'smooth'});
|
||
}
|
||
const existing=loadMember();
|
||
if(existing)showMember(existing);
|
||
|
||
$('#join-form').addEventListener('submit',e=>{
|
||
e.preventDefault();
|
||
const err=$('#join-err');
|
||
err.classList.remove('show');
|
||
const name=$('#m-name').value.trim();
|
||
const email=$('#m-email').value.trim();
|
||
const phone=$('#m-phone').value.trim();
|
||
const agree=$('#m-agree').checked;
|
||
|
||
let problem=null;
|
||
if(!name)problem='Please enter your full name.';
|
||
else if(!email||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))problem='Please enter a valid email address.';
|
||
else if(!phone)problem='Please enter a phone number.';
|
||
else if(!hasInk)problem='Please sign in the signature box.';
|
||
else if(!agree)problem='Please check the covenant agreement box.';
|
||
|
||
if(problem){
|
||
err.textContent=problem;
|
||
err.classList.add('show');
|
||
err.scrollIntoView({behavior:'smooth',block:'center'});
|
||
return;
|
||
}
|
||
|
||
const member={
|
||
code:'K484-'+Math.random().toString(36).slice(2,6).toUpperCase(),
|
||
fullName:name, email, phone,
|
||
ts:Date.now(),
|
||
signature:canvas.toDataURL('image/png')
|
||
};
|
||
saveMember(member);
|
||
showMember(member);
|
||
});
|
||
|
||
$('#btn-new-apply').onclick=()=>{
|
||
localStorage.removeItem(LS_MEMBER);
|
||
location.reload();
|
||
};
|
||
|
||
/* ============================== AGREEMENT DOWNLOAD ============================== */
|
||
$('#btn-download').onclick=()=>{
|
||
const m=loadMember();
|
||
if(!m){toast('No application found');return;}
|
||
const d=new Date(m.ts);
|
||
const html='<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Kitchen 484 Membership Agreement - '+esc(m.fullName)+'</title>'+
|
||
'<style>'+
|
||
'body{font-family:Georgia,serif;color:#000;max-width:720px;margin:40px auto;padding:0 24px;line-height:1.6;font-size:14px}'+
|
||
'h1{font-size:22px;letter-spacing:2px;text-transform:uppercase;text-align:center}'+
|
||
'h2{font-size:14px;letter-spacing:1px;text-transform:uppercase;text-align:center;font-weight:normal;color:#333;margin-top:4px}'+
|
||
'.box{border:1px solid #000;padding:18px 22px;margin:22px 0}'+
|
||
'.info p{margin:6px 0}'+
|
||
'.info b{display:inline-block;width:110px}'+
|
||
'ol{padding-left:20px}li{margin:8px 0}'+
|
||
'.sig{margin-top:30px;text-align:center}'+
|
||
'.sig img{height:90px;border:1px solid #999;border-radius:6px;padding:6px;background:#fff}'+
|
||
'.sig .line{margin-top:6px;font-size:12px;color:#333}'+
|
||
'.declare{margin-top:24px;font-style:italic}'+
|
||
'@media print{body{margin:20px auto}}'+
|
||
'</style></head><body>'+
|
||
'<h1>Kitchen 484</h1><h2>Membership Agreement — '+esc(m.code)+'</h2>'+
|
||
'<div class="box info">'+
|
||
'<p><b>Member:</b> '+esc(m.fullName)+'</p>'+
|
||
'<p><b>Email:</b> '+esc(m.email)+'</p>'+
|
||
'<p><b>Phone:</b> '+esc(m.phone)+'</p>'+
|
||
'<p><b>Applied:</b> '+d.toLocaleString('en-US')+'</p>'+
|
||
'</div>'+
|
||
'<p>Note: This is a private, members-only association. Participation is absolutely voluntary.</p>'+
|
||
'<h2 style="text-align:left;margin-top:20px">K484 Membership Covenant</h2>'+
|
||
'<p>By submitting this application and becoming a member of Kitchen 484, I acknowledge and agree that:</p>'+
|
||
'<ol>'+
|
||
'<li>I am joining a private, members-only association, not a public business.</li>'+
|
||
'<li>Access to kitchen activities, meals, and services is limited to accepted members only.</li>'+
|
||
'<li>I agree to abide by the rules, governance, and confidentiality requirements outlined in this Covenant.</li>'+
|
||
'<li>Membership is granted for a 24-hour period beginning upon acceptance of this application, unless ended earlier by the member or by Kitchen 484.</li>'+
|
||
'<li>I acknowledge that my membership, participation, and personal information are confidential, and that I will respect the privacy of Kitchen 484 and other members.</li>'+
|
||
'<li>I acknowledge that Kitchen 484 operates under internal Articles, Bylaws, and policies, which are not publicly distributed, and that I do not acquire governance, voting, or inspection rights by virtue of my membership.</li>'+
|
||
'</ol>'+
|
||
'<p class="declare">"I have read and agree to the Kitchen 484 Membership Covenant, and I voluntarily apply for membership."</p>'+
|
||
'<div class="sig"><img src="'+m.signature+'" alt="signature"><div class="line">Digital signature — '+esc(m.fullName)+' · '+d.toLocaleDateString('en-US')+'</div></div>'+
|
||
'<script>setTimeout(function(){window.print()},600)</'+'script>'+
|
||
'</body></html>';
|
||
const w=window.open('','_blank');
|
||
if(!w){toast('Pop-up blocked — allow pop-ups to download');return;}
|
||
w.document.write(html);
|
||
w.document.close();
|
||
};
|
||
|
||
function toast(msg){
|
||
let t=document.getElementById('h-toast');
|
||
if(!t){t=document.createElement('div');t.id='h-toast';t.style.cssText='position:fixed;left:50%;bottom:36px;transform:translateX(-50%) translateY(20px);background:#0A0A0A;color:#fff;font-size:12px;font-weight:700;padding:10px 18px;border-radius:99px;opacity:0;transition:all .25s;z-index:200;pointer-events:none';document.body.appendChild(t);}
|
||
t.textContent=msg;t.style.opacity='1';t.style.transform='translateX(-50%) translateY(0)';
|
||
clearTimeout(t._x);t._x=setTimeout(()=>{t.style.opacity='0';t.style.transform='translateX(-50%) translateY(20px)';},2200);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|