Food truck websiteOne-line
GLM 5.3
by Z.aiReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 11 min 59 s
- Cost
- $0.113
- Output tokens
- 63,661
- Spent thinking
- 40,548 tokens
- Speed
- 88.6 tok/s
- Console errors
- 0
- First token after
- 642 ms
- First visible text after
- 8 min 27 s
- Prompt tokens
- 362
- Code size
- 67.6 KB
- Finish reason
- stop
- Served by
- Decart
- Model id
z-ai/glm-5.3- Reasoning sent
model default- Max output
- 943,718 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
“i'm opening a korean taco food truck called "Kimchi Loco", make me a website that makes people really want to come”
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 1153 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Kimchi Loco — Korean tacos, bulgogi burritos and gochujang everything from one very loud food truck.">
<title>KIMCHI LOCO — Korean Tacos That Slap</title>
<style>
/* ============ RESET & TOKENS ============ */
*{margin:0;padding:0;box-sizing:border-box}
:root{
--paper:#F9EFDB;
--cream:#FFFAEC;
--ink:#1C130E;
--red:#E0311F;
--red-deep:#B31217;
--gold:#FFC531;
--green:#3F9B4F;
--sky:#7FC7EA;
--dFont:"Archivo Black","Arial Black",Impact,"Haettenschweiler",sans-serif;
--shadow:7px 7px 0 var(--ink);
}
html{scroll-behavior:smooth}
html,body{overflow-x:clip}
body{
background:var(--paper);
background-image:radial-gradient(rgba(28,19,14,.07) 1.4px, transparent 1.4px);
background-size:24px 24px;
color:var(--ink);
font-family:"Helvetica Neue",Helvetica,Arial,"Segoe UI",sans-serif;
line-height:1.55;
-webkit-font-smoothing:antialiased;
}
::selection{background:var(--red);color:var(--cream)}
img,svg{display:block}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer}
section{scroll-margin-top:96px}
.wrap{width:min(1180px,92%);margin-inline:auto}
.disp{font-family:var(--dFont);text-transform:uppercase;line-height:.92;letter-spacing:0;font-weight:900}
/* ============ KEYFRAMES ============ */
@keyframes slide{to{transform:translateX(-50%)}}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
@keyframes puff{0%{transform:translateY(6px) scale(.5);opacity:0}25%{opacity:.85}70%{opacity:.45}100%{transform:translateY(-28px) scale(1.35);opacity:0}}
@keyframes swing{0%,100%{transform:rotate(0)}30%{transform:rotate(-1.6deg)}70%{transform:rotate(1.6deg)}}
@keyframes rot{to{transform:rotate(360deg)}}
@keyframes ping{0%{transform:scale(.35);opacity:.7}100%{transform:scale(1.9);opacity:0}}
@keyframes flow{to{stroke-dashoffset:-24}}
@keyframes pop{0%{transform:scale(1)}45%{transform:scale(1.5)}100%{transform:scale(1)}}
@keyframes blink{0%,92%,100%{transform:scaleY(1)}96%{transform:scaleY(.08)}}
@keyframes sweatd{0%{transform:translateY(-5px);opacity:0}35%{opacity:1}100%{transform:translateY(9px);opacity:0}}
@keyframes cry{0%{transform:translateY(0);opacity:1}100%{transform:translateY(16px);opacity:0}}
@keyframes flick{0%,100%{transform:scale(1) rotate(0)}50%{transform:scale(1.14,.9) rotate(4deg)}}
@keyframes shakechar{0%,100%{transform:rotate(0)}25%{transform:rotate(-2deg)}75%{transform:rotate(2deg)}}
@keyframes shakeX{0%,100%{transform:translateX(0)}20%{transform:translateX(-7px)}40%{transform:translateX(7px)}60%{transform:translateX(-5px)}80%{transform:translateX(5px)}}
@keyframes toastin{from{transform:translateY(18px);opacity:0}to{transform:translateY(0);opacity:1}}
/* ============ SHARED PIECES ============ */
.btn{
display:inline-flex;align-items:center;gap:10px;
font-family:var(--dFont);text-transform:uppercase;letter-spacing:.04em;
background:var(--gold);color:var(--ink);
border:3px solid var(--ink);border-radius:14px;
padding:14px 22px;box-shadow:5px 5px 0 var(--ink);
transition:transform .12s ease,box-shadow .12s ease;white-space:nowrap;
}
.btn:hover{transform:translate(-2px,-2px);box-shadow:7px 7px 0 var(--ink)}
.btn:active{transform:translate(3px,3px);box-shadow:1px 1px 0 var(--ink)}
.btn.red{background:var(--red);color:var(--cream)}
.btn.ghost{background:var(--cream)}
.btn.small{padding:9px 14px;font-size:13px;border-radius:10px;box-shadow:4px 4px 0 var(--ink)}
.btn:focus-visible,input:focus-visible,a:focus-visible,button:focus-visible{outline:3px dashed var(--red);outline-offset:3px}
.kicker{
display:inline-block;font-family:var(--dFont);font-size:12px;letter-spacing:.14em;
background:var(--ink);color:var(--cream);padding:7px 14px;border-radius:999px;
rotate:-2deg;margin-bottom:18px;
}
h2.secTitle{font-family:var(--dFont);text-transform:uppercase;font-size:clamp(2.2rem,5.4vw,4rem);line-height:.95;margin-bottom:14px}
.hl{display:inline-block;background:var(--gold);padding:.02em .18em;rotate:-1.2deg}
.hl.red{background:var(--red);color:var(--cream)}
.secHead{margin-bottom:clamp(28px,4vw,48px)}
.secHead p.sub{max-width:56ch;font-size:1.02rem;opacity:.85}
.reveal{opacity:0;transform:translateY(26px);transition:opacity .7s cubic-bezier(.2,.7,.2,1),transform .7s cubic-bezier(.2,.7,.2,1)}
.reveal.in{opacity:1;transform:none}
/* ============ ANNOUNCE + NAV ============ */
#top.announce{
background:var(--ink);color:var(--cream);text-align:center;
font-size:11px;letter-spacing:.16em;padding:9px 14px;text-transform:uppercase;
}
#top.announce b{color:var(--gold)}
.nav{position:sticky;top:0;z-index:60;background:var(--paper);border-bottom:4px solid var(--ink)}
.navIn{display:flex;align-items:center;gap:26px;padding:12px 0}
.logo{display:flex;align-items:center;gap:10px}
.logoChi{width:34px;height:34px;rotate:-10deg;transition:rotate .3s}
.logo:hover .logoChi{rotate:12deg}
.logoTxt{font-family:var(--dFont);font-size:20px;letter-spacing:.02em;line-height:1}
.logoTxt em{font-style:normal;color:var(--red);display:block;font-size:20px}
.links{display:flex;gap:22px;margin-left:auto}
.links a{font-family:var(--dFont);font-size:13px;letter-spacing:.06em;position:relative;padding:6px 2px}
.links a::after{content:"";position:absolute;left:0;right:100%;bottom:0;height:4px;background:var(--red);transition:right .25s}
.links a:hover::after{right:0}
.bagBtn{
display:flex;align-items:center;gap:10px;font-family:var(--dFont);font-size:13px;letter-spacing:.06em;
border:3px solid var(--ink);background:var(--red);color:var(--cream);border-radius:12px;
padding:10px 16px;box-shadow:4px 4px 0 var(--ink);transition:transform .12s,box-shadow .12s;
}
.bagBtn:hover{transform:translate(-2px,-2px);box-shadow:6px 6px 0 var(--ink)}
.bagBtn:active{transform:translate(2px,2px);box-shadow:1px 1px 0 var(--ink)}
.count{
background:var(--gold);color:var(--ink);border:2px solid var(--ink);border-radius:999px;
min-width:26px;height:26px;display:grid;place-items:center;font-size:13px;
}
.count.pop{animation:pop .4s ease}
/* ============ HERO ============ */
.hero{position:relative;overflow:hidden;padding:clamp(36px,5vw,64px) 0 clamp(44px,6vw,84px)}
.heroGrid{display:grid;grid-template-columns:1.04fr .96fr;gap:clamp(24px,4vw,56px);align-items:center}
.heroKicker{display:inline-flex;align-items:center;gap:10px;border:3px solid var(--ink);background:var(--cream);
border-radius:999px;padding:8px 16px;font-family:var(--dFont);font-size:12px;letter-spacing:.12em;rotate:-1.5deg;box-shadow:4px 4px 0 var(--ink)}
.heroKicker svg{width:16px;height:16px}
h1.heroTitle{font-family:var(--dFont);text-transform:uppercase;font-size:clamp(3rem,7.4vw,6.4rem);line-height:.88;margin:22px 0 20px}
h1.heroTitle .row{display:block}
h1.heroTitle .stroke{color:transparent;-webkit-text-stroke:2.5px var(--ink)}
h1.heroTitle .red{color:var(--red)}
.heroSub{max-width:46ch;font-size:clamp(1rem,1.6vw,1.18rem);margin-bottom:26px}
.heroSub b{background:var(--gold);padding:0 .2em;font-weight:700}
.heroCtas{display:flex;flex-wrap:wrap;gap:16px;margin-bottom:24px}
.openPill{
display:inline-flex;align-items:center;gap:12px;border:3px dashed var(--ink);
border-radius:14px;padding:10px 16px;background:var(--cream);font-size:14px;rotate:-.6deg;
}
.openPill b{font-family:var(--dFont);font-size:13px;letter-spacing:.05em}
.openPill span#cd{font-variant-numeric:tabular-nums;font-weight:700;color:var(--red)}
.pulseDot{width:12px;height:12px;border-radius:50%;background:var(--red);border:2px solid var(--ink);animation:pop 1.4s ease infinite}
.heroArt{position:relative}
.bigword{
position:absolute;right:-4%;top:-8%;z-index:0;pointer-events:none;
font-family:var(--dFont);font-size:clamp(90px,15vw,168px);line-height:1;rotate:-5deg;
color:transparent;-webkit-text-stroke:2px rgba(28,19,14,.13);user-select:none;
}
.truck{position:relative;z-index:1;width:100%;max-width:660px;margin-inline:auto}
.stampWrap{position:absolute;z-index:3;top:-34px;right:-6px;width:clamp(110px,14vw,158px)}
.sticker{
display:inline-block;font-family:var(--dFont);font-size:12px;letter-spacing:.08em;text-transform:uppercase;
background:var(--gold);border:3px solid var(--ink);border-radius:999px;padding:10px 16px;
box-shadow:4px 4px 0 var(--ink);
}
.heroSticker{position:absolute;z-index:3;bottom:44px;left:-14px;rotate:-8deg}
.heroSticker2{position:absolute;z-index:3;bottom:-8px;right:8%;rotate:5deg;background:var(--cream)}
.float{will-change:transform}
/* truck svg bits */
.tbounce{animation:bounce 3.4s ease-in-out infinite}
.spin{animation:spin 2.4s linear infinite;transform-box:fill-box;transform-origin:center}
.puff{animation:puff 2.8s linear infinite;transform-box:fill-box;transform-origin:center}
.puff.p2{animation-delay:.9s}.puff.p3{animation-delay:1.8s}
.swing{animation:swing 5s ease-in-out infinite;transform-box:fill-box;transform-origin:50% 100%}
.rotor{animation:rot 16s linear infinite;transform-box:fill-box;transform-origin:center}
.tMain{font-family:var(--dFont);font-size:22px;fill:var(--cream);letter-spacing:1px}
.tKr{font-size:15px;fill:var(--gold);letter-spacing:2px}
.tSign{font-family:var(--dFont);font-size:26px;fill:var(--ink);letter-spacing:3px}
.tOpen{font-family:var(--dFont);font-size:17px;fill:var(--gold);letter-spacing:7px}
.tSmall{font-size:11px;fill:var(--cream);letter-spacing:2px}
.circText{font-family:var(--dFont);font-size:13px;letter-spacing:2px;fill:var(--ink)}
/* ============ TICKER ============ */
.ticker{background:var(--red);border-top:4px solid var(--ink);border-bottom:4px solid var(--ink);overflow:hidden;padding:14px 0}
.ticker.rev{background:var(--ink)}
.tTrack{display:flex;width:max-content;animation:slide 26s linear infinite}
.ticker.rev .tTrack{animation-direction:reverse;animation-duration:30s}
.tGroup{display:flex;align-items:center;gap:30px;padding-right:30px}
.tk{font-family:var(--dFont);font-size:19px;letter-spacing:.08em;color:var(--cream);white-space:nowrap;text-transform:uppercase}
.ticker .chi{width:22px;height:22px;flex:none}
.ticker .chi .cb{fill:var(--cream)}
.ticker .chi .cs{fill:var(--gold)}
/* chili icon */
.chi{width:18px;height:18px;display:inline-block;vertical-align:middle}
.chi .cb{fill:var(--red)}
.chi .cs{fill:var(--green)}
.chi.off .cb{fill:none;stroke:rgba(28,19,14,.4);stroke-width:1.8}
.chi.off .cs{opacity:.4}
/* ============ MENU ============ */
#menu{padding:clamp(64px,9vw,110px) 0}
.menuBar{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:18px;margin-bottom:30px}
.menuNote{font-size:13px;letter-spacing:.05em;border:3px solid var(--ink);background:var(--cream);border-radius:12px;padding:9px 14px;rotate:1.4deg;box-shadow:4px 4px 0 var(--ink)}
.menuGrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:26px}
.card{
position:relative;background:var(--cream);border:3px solid var(--ink);border-radius:20px;
padding:20px 20px 18px;box-shadow:var(--shadow);rotate:var(--rot,0deg);
transition:transform .18s ease,box-shadow .18s ease,rotate .18s ease;
display:flex;flex-direction:column;gap:10px;
}
.card:hover{transform:translateY(-6px);rotate:0deg;box-shadow:10px 10px 0 var(--ink)}
.cardTop{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}
.cardArt{width:110px}
.cardArt svg{width:100%;height:auto}
.price{
font-family:var(--dFont);font-size:15px;background:var(--ink);color:var(--gold);
border-radius:10px;padding:7px 11px;rotate:4deg;white-space:nowrap;
}
.card h3{font-family:var(--dFont);font-size:19px;line-height:1.1;text-transform:uppercase}
.card h3 em{display:block;font-style:normal;font-family:inherit;font-size:12px;color:var(--red);letter-spacing:.14em;margin-top:4px}
.card p{font-size:.92rem;flex:1}
.cardFoot{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:4px}
.heat{display:flex;gap:3px}
.ribbon{
position:absolute;top:-13px;right:16px;background:var(--red);color:var(--cream);
font-family:var(--dFont);font-size:10px;letter-spacing:.1em;padding:6px 11px;border:3px solid var(--ink);
border-radius:999px;rotate:4deg;
}
.menuFine{margin-top:26px;text-align:center;font-size:13px;letter-spacing:.06em;opacity:.75}
/* ============ SPICE-O-MATIC ============ */
#spice{padding:clamp(30px,5vw,60px) 0 clamp(70px,9vw,120px)}
.machine{position:relative;background:var(--cream);border:4px solid var(--ink);border-radius:28px;box-shadow:10px 10px 0 var(--ink);padding:clamp(18px,3vw,36px)}
.rivet{position:absolute;width:14px;height:14px;border-radius:50%;background:var(--ink)}
.rivet.r1{top:12px;left:12px}.rivet.r2{top:12px;right:12px}.rivet.r3{bottom:12px;left:12px}.rivet.r4{bottom:12px;right:12px}
.machineTop{display:flex;flex-wrap:wrap;align-items:center;gap:14px;border:3px solid var(--ink);border-radius:14px;background:var(--paper);padding:12px 18px;margin-bottom:22px}
.machineTop b{font-family:var(--dFont);font-size:18px;letter-spacing:.08em}
.machineTop span{font-size:13px;letter-spacing:.05em;opacity:.75}
.screw{width:12px;height:12px;border-radius:50%;border:3px solid var(--ink);margin-left:auto}
.machineGrid{display:grid;grid-template-columns:.9fr 1.1fr;gap:clamp(18px,3vw,36px);align-items:center}
.screen{background:var(--paper);border:3px solid var(--ink);border-radius:20px;padding:18px;text-align:center;position:relative}
.screen svg{width:min(300px,86%);margin:0 auto}
.screenLabel{margin-top:8px;font-family:var(--dFont);font-size:13px;letter-spacing:.14em}
.screenLabel b{color:var(--red)}
.eye{animation:blink 4.6s infinite;transform-box:fill-box;transform-origin:center}
.cheek{fill:#FF9D8F;opacity:.55}
#sweat{animation:sweatd 1.5s ease-in-out infinite;transform-box:fill-box}
#tears ellipse{animation:cry 1.1s ease-in infinite;transform-box:fill-box}
#tears ellipse:last-child{animation-delay:.35s}
.flame{animation:flick .5s ease-in-out infinite;transform-box:fill-box;transform-origin:50% 100%}
.flame.f2{animation-delay:.22s}
.shaking{animation:shakechar .28s linear infinite;transform-box:fill-box;transform-origin:center}
.sliderRow{margin:6px 0 10px}
input[type=range]{
-webkit-appearance:none;appearance:none;width:100%;height:34px;background:transparent;cursor:pointer;
}
input[type=range]::-webkit-slider-runnable-track{height:16px;border:3px solid var(--ink);border-radius:999px;background:var(--cream)}
input[type=range]::-webkit-slider-thumb{
-webkit-appearance:none;width:32px;height:32px;border-radius:50%;background:var(--red);
border:3px solid var(--ink);margin-top:-11px;box-shadow:3px 3px 0 var(--ink);
}
input[type=range]::-moz-range-track{height:16px;border:3px solid var(--ink);border-radius:999px;background:var(--cream)}
input[type=range]::-moz-range-thumb{width:26px;height:26px;border-radius:50%;background:var(--red);border:3px solid var(--ink)}
.ticks{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin:10px 0 20px}
.ticks span{
text-align:center;font-size:10.5px;letter-spacing:.08em;font-family:var(--dFont);
border:2px solid transparent;border-radius:8px;padding:5px 2px;opacity:.55;transition:.15s;
}
.ticks span.on{background:var(--red);color:var(--cream);opacity:1}
.ticket{
position:relative;border:3px dashed var(--ink);border-radius:16px;background:var(--paper);
padding:18px 20px;rotate:-.7deg;
}
.ticket b{display:block;font-family:var(--dFont);font-size:22px;letter-spacing:.04em;color:var(--red);text-transform:uppercase}
.ticket .pick{display:inline-block;font-family:var(--dFont);font-size:13px;background:var(--ink);color:var(--gold);border-radius:8px;padding:5px 10px;margin:10px 0 8px;letter-spacing:.05em}
.ticket p{font-size:.95rem;margin-bottom:14px}
.machineFoot{margin-top:18px;font-size:12.5px;letter-spacing:.06em;text-align:center;opacity:.7}
/* ============ STORY ============ */
#story{background:var(--red);border-top:4px solid var(--ink);border-bottom:4px solid var(--ink);padding:clamp(64px,9vw,110px) 0;color:var(--cream)}
#story .kicker{background:var(--gold);color:var(--ink)}
#story h2{color:var(--cream)}
#story .secHead p{color:#FFE9C9}
.statStrip{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-bottom:clamp(34px,5vw,54px)}
.stat{border:3px solid var(--ink);background:var(--cream);color:var(--ink);border-radius:18px;padding:18px;text-align:center;box-shadow:6px 6px 0 var(--ink);rotate:-.7deg}
.stat:nth-child(2){rotate:.8deg}
.stat:nth-child(3){rotate:-.5deg}
.stat .num{display:block;font-family:var(--dFont);font-size:clamp(2.4rem,5vw,3.6rem);color:var(--red);line-height:1}
.stat small{font-family:var(--dFont);font-size:12px;letter-spacing:.14em}
.steps{display:grid;grid-template-columns:repeat(4,1fr);gap:20px}
.step{background:var(--cream);color:var(--ink);border:3px solid var(--ink);border-radius:18px;padding:20px;box-shadow:6px 6px 0 rgba(28,19,14,.9)}
.step i{font-style:normal;font-family:var(--dFont);font-size:40px;display:block;color:transparent;-webkit-text-stroke:2px var(--red);margin-bottom:8px}
.step b{font-family:var(--dFont);display:block;font-size:16px;margin-bottom:8px;letter-spacing:.04em}
.step p{font-size:.9rem}
/* ============ FIND US ============ */
#find{padding:clamp(64px,9vw,110px) 0}
.status{
display:flex;align-items:center;gap:14px;border:3px solid var(--ink);background:var(--cream);
border-radius:16px;padding:14px 18px;box-shadow:6px 6px 0 var(--ink);margin-bottom:26px;rotate:-.5deg;max-width:820px;
}
.liveDot{width:16px;height:16px;flex:none;border-radius:50%;background:var(--green);border:3px solid var(--ink);position:relative}
.liveDot::after{content:"";position:absolute;inset:0;border-radius:50%;background:var(--green);animation:ping 1.6s ease-out infinite}
.liveDot.rest{background:rgba(28,19,14,.25)}
.liveDot.rest::after{display:none}
.status b{display:block;font-family:var(--dFont);font-size:16px;letter-spacing:.03em}
.status span{font-size:.9rem;opacity:.8}
.findGrid{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(20px,3vw,40px);align-items:start}
.sched{list-style:none;background:var(--cream);border:4px solid var(--ink);border-radius:20px;box-shadow:var(--shadow);overflow:hidden}
.sched li{display:grid;grid-template-columns:64px 1fr auto;gap:12px;align-items:center;padding:14px 18px;border-top:3px solid var(--ink)}
.sched li:first-child{border-top:none}
.sched li.off{background:#F2E4C4}
.sched li.off .spot,.sched li.off .time{opacity:.65}
.sched li.today{background:var(--red);color:var(--cream)}
.sched .day{font-family:var(--dFont);font-size:14px;position:relative}
.sched .day i{
font-style:normal;position:absolute;left:2px;top:-16px;background:var(--gold);color:var(--ink);
font-size:9px;letter-spacing:.1em;padding:2px 7px;border:2px solid var(--ink);border-radius:999px;rotate:-4deg;
}
.sched .spot{font-size:.92rem;font-weight:700}
.sched .spot small{display:block;font-weight:400;font-size:.78rem;opacity:.8}
.sched .time{font-variant-numeric:tabular-nums;font-size:.85rem;white-space:nowrap}
.sched li.today .time{color:var(--gold);font-weight:700}
.mapWrap{background:var(--cream);border:4px solid var(--ink);border-radius:20px;box-shadow:var(--shadow);padding:16px}
.mapWrap svg{width:100%;height:auto}
.ping{animation:ping 1.6s ease-out infinite;transform-box:fill-box;transform-origin:center}
.route{animation:flow 1.2s linear infinite}
.mtxt{font-family:var(--dFont);font-size:9px;letter-spacing:1px;fill:var(--ink);text-anchor:middle;opacity:.85}
.mapNote{display:block;text-align:center;font-size:11.5px;letter-spacing:.08em;margin-top:12px;opacity:.75;font-family:var(--dFont)}
/* ============ TESTIMONIALS ============ */
#love{padding:clamp(40px,6vw,80px) 0 clamp(80px,10vw,130px)}
.loveGrid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:26px}
.loveCard{
background:var(--cream);border:3px solid var(--ink);border-radius:6px 22px 6px 22px;
padding:22px;box-shadow:var(--shadow);rotate:var(--rot,0deg);transition:transform .18s;
}
.loveCard:hover{transform:translateY(-5px) rotate(0deg)}
.stars{display:flex;gap:3px;margin-bottom:12px}
.stars svg{width:17px;height:17px;fill:var(--gold);stroke:var(--ink);stroke-width:1.4}
.loveCard q{display:block;font-size:1rem;line-height:1.5;quotes:"“" "”"}
.loveCard footer{margin-top:14px;font-family:var(--dFont);font-size:11.5px;letter-spacing:.08em;color:var(--red)}
/* ============ FOOTER ============ */
footer{background:var(--ink);color:var(--cream);padding:clamp(56px,8vw,96px) 0 26px}
.footTitle{font-family:var(--dFont);text-transform:uppercase;font-size:clamp(2.6rem,8vw,6rem);line-height:.9;margin-bottom:30px}
.footTitle .hl{rotate:-2deg}
.footGrid{display:grid;grid-template-columns:1.2fr 1fr;gap:clamp(24px,4vw,60px);align-items:start;margin-bottom:44px}
.newsLabel{font-family:var(--dFont);font-size:15px;letter-spacing:.06em;margin-bottom:6px}
.newsSub{font-size:.92rem;opacity:.7;margin-bottom:16px;max-width:44ch}
.newsForm{display:flex;gap:12px;flex-wrap:wrap}
.newsForm input{
flex:1;min-width:220px;font:inherit;padding:14px 16px;border:3px solid var(--ink);
border-radius:12px;background:var(--paper);color:var(--ink);box-shadow:4px 4px 0 rgba(249,239,219,.25);
}
.newsForm input.shakeX{animation:shakeX .45s ease}
.socials{display:flex;flex-direction:column;gap:14px}
.chip{
display:flex;align-items:center;gap:12px;border:3px solid var(--cream);border-radius:14px;
padding:13px 16px;font-family:var(--dFont);font-size:13px;letter-spacing:.06em;transition:.15s;
}
.chip svg{width:20px;height:20px;flex:none}
.chip:hover{background:var(--red);border-color:var(--red);transform:translate(-2px,-2px)}
.chip small{margin-left:auto;font-size:10px;opacity:.6;letter-spacing:.1em}
.fine{border-top:2px dashed rgba(249,239,219,.35);padding-top:18px;display:flex;flex-wrap:wrap;gap:10px;justify-content:space-between;font-size:12px;opacity:.7;letter-spacing:.04em}
.fine b{color:var(--gold)}
/* ============ TOAST & MODAL ============ */
#toasts{position:fixed;bottom:22px;left:50%;transform:translateX(-50%);z-index:200;display:flex;flex-direction:column;gap:10px;width:min(400px,92vw);pointer-events:none}
.toast{
background:var(--ink);color:var(--cream);border:3px solid var(--ink);border-left:10px solid var(--gold);
border-radius:12px;padding:13px 16px;box-shadow:6px 6px 0 rgba(28,19,14,.35);
animation:toastin .3s ease;font-size:.92rem;
}
.toast b{display:block;font-family:var(--dFont);font-size:13px;letter-spacing:.04em}
.toast span{opacity:.8;font-size:.84rem}
.overlay{
position:fixed;inset:0;z-index:150;background:rgba(28,19,14,.6);display:flex;align-items:center;justify-content:center;
padding:20px;visibility:hidden;opacity:0;transition:opacity .25s,visibility .25s;
}
.overlay.open{visibility:visible;opacity:1}
.modal{
width:min(480px,100%);background:var(--cream);border:4px solid var(--ink);border-radius:22px;
box-shadow:12px 12px 0 var(--ink);padding:24px;transform:translateY(14px);transition:transform .25s;
}
.overlay.open .modal{transform:translateY(0)}
.modalHead{display:flex;align-items:center;justify-content:space-between;margin-bottom:14px}
.modalHead h3{font-family:var(--dFont);font-size:22px;letter-spacing:.04em}
.x{width:38px;height:38px;border:3px solid var(--ink);border-radius:10px;background:var(--gold);display:grid;place-items:center;box-shadow:3px 3px 0 var(--ink)}
.x:active{transform:translate(2px,2px);box-shadow:0 0 0}
#bagList{list-style:none;max-height:38vh;overflow:auto;border-top:3px solid var(--ink);border-bottom:3px solid var(--ink);margin-bottom:14px}
#bagList li{display:flex;justify-content:space-between;gap:12px;padding:11px 4px;font-size:.94rem;border-bottom:2px dashed rgba(28,19,14,.25)}
#bagList li:last-child{border-bottom:none}
#bagList li.empty{justify-content:center;text-align:center;opacity:.75;padding:22px 8px}
.bagTotal{display:flex;justify-content:space-between;font-family:var(--dFont);font-size:16px;margin-bottom:10px}
.bagTotal b{color:var(--red)}
.modalNote{font-size:.85rem;opacity:.78;margin-bottom:18px}
.modalBtns{display:flex;gap:12px;flex-wrap:wrap}
/* ============ RESPONSIVE ============ */
@media (max-width:980px){
.heroGrid{grid-template-columns:1fr}
.heroArt{margin-top:26px}
.machineGrid{grid-template-columns:1fr}
.findGrid{grid-template-columns:1fr}
.steps{grid-template-columns:repeat(2,1fr)}
.footGrid{grid-template-columns:1fr}
}
@media (max-width:760px){
.links{display:none}
.statStrip{grid-template-columns:1fr}
.sched li{grid-template-columns:52px 1fr auto;padding:12px 12px}
}
@media (max-width:540px){
.steps{grid-template-columns:1fr}
.heroSticker{left:-4px;bottom:20px}
.bigword{right:0}
.openPill{flex-wrap:wrap}
}
@media (prefers-reduced-motion:reduce){
*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}
</style>
</head>
<body>
<div id="top" class="announce">GRAND OPENING WEEK — <b>FIRST 50 GUESTS DAILY</b> GET A FREE KIMCHI QUESADILLA · NO DANCE REQUIRED (ENCOURAGED)</div>
<header class="nav">
<div class="wrap navIn">
<a class="logo" href="#top" aria-label="Kimchi Loco home">
<svg class="logoChi" viewBox="0 0 24 24" aria-hidden="true">
<path class="cs" d="M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z"/>
<path class="cb" d="M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z"/>
</svg>
<span class="logoTxt">KIMCHI<em>LOCO</em></span>
</a>
<nav class="links" aria-label="Sections">
<a href="#menu">MENU</a>
<a href="#spice">SPICE-O-MATIC</a>
<a href="#story">STORY</a>
<a href="#find">FIND US</a>
</nav>
<button class="bagBtn" id="bagBtn" aria-label="Open your bag">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true">
<path d="M5.5 8h13l-1.1 12.1a2 2 0 0 1-2 1.9H8.6a2 2 0 0 1-2-1.9L5.5 8Z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linejoin="round"/>
<path d="M9 8V6.5a3 3 0 0 1 6 0V8" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
</svg>
BAG
<span class="count" id="bagCount">0</span>
</button>
</div>
</header>
<main>
<!-- ================= HERO ================= -->
<section class="hero" id="hero">
<div class="wrap heroGrid">
<div>
<span class="heroKicker">
<svg class="chi" viewBox="0 0 24 24" aria-hidden="true"><path class="cs" d="M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z"/><path class="cb" d="M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z"/></svg>
SEOUL STREET FOOD × TORTILLA ENERGY
</span>
<h1 class="heroTitle">
<span class="row">FLAVOR</span>
<span class="row stroke">WITH NO</span>
<span class="row"><span class="red">CHILL.</span></span>
</h1>
<p class="heroSub">
Slow-marinated bulgogi, <b>90-day kimchi</b>, and tortillas pressed to order —
stacked into tacos loud enough to hear from the next block. One truck.
Zero chill. Grand opening week.
</p>
<div class="heroCtas">
<a class="btn red" href="#menu">SEE THE MENU</a>
<a class="btn" href="#find">FIND THE TRUCK</a>
</div>
<div class="openPill">
<span class="pulseDot" aria-hidden="true"></span>
<b>GRAND OPENING · SAT 11:00</b>
<span>in <span id="cd">…</span></span>
</div>
</div>
<div class="heroArt">
<span class="bigword float" data-depth="6" aria-hidden="true">맛있겠다</span>
<!-- THE TRUCK -->
<svg class="truck" viewBox="0 0 640 400" role="img" aria-label="Illustration of the Kimchi Loco food truck">
<ellipse cx="320" cy="350" rx="250" ry="16" fill="rgba(28,19,14,.16)"/>
<!-- chimney steam -->
<g class="puffset" aria-hidden="true">
<circle class="puff" cx="86" cy="46" r="11" fill="var(--cream)" stroke="var(--ink)" stroke-width="4"/>
<circle class="puff p2" cx="98" cy="52" r="8" fill="var(--cream)" stroke="var(--ink)" stroke-width="4"/>
<circle class="puff p3" cx="78" cy="54" r="7" fill="var(--cream)" stroke="var(--ink)" stroke-width="4"/>
</g>
<g class="tbounce">
<!-- chimney -->
<rect x="74" y="72" width="24" height="48" fill="var(--ink)"/>
<rect x="66" y="62" width="40" height="14" rx="6" fill="var(--red)" stroke="var(--ink)" stroke-width="4"/>
<!-- roof sign -->
<g class="swing">
<rect x="268" y="78" width="10" height="40" fill="var(--ink)"/>
<rect x="328" y="78" width="10" height="40" fill="var(--ink)"/>
<rect x="206" y="28" width="196" height="52" rx="12" fill="var(--gold)" stroke="var(--ink)" stroke-width="5"/>
<text class="tSign" x="304" y="64" text-anchor="middle">TACOS</text>
<path d="M226 44 l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z" fill="var(--red)" stroke="var(--ink)" stroke-width="1.5"/>
<path d="M382 44 l3 6 7 1-5 5 1 7-6-3-6 3 1-7-5-5 7-1z" fill="var(--red)" stroke="var(--ink)" stroke-width="1.5"/>
</g>
<!-- bumpers & lights -->
<rect x="46" y="248" width="26" height="32" rx="9" fill="var(--ink)"/>
<rect x="524" y="248" width="26" height="32" rx="9" fill="var(--ink)"/>
<circle cx="58" cy="236" r="9" fill="var(--gold)" stroke="var(--ink)" stroke-width="4"/>
<!-- body -->
<rect x="64" y="112" width="470" height="172" rx="20" fill="var(--red)" stroke="var(--ink)" stroke-width="6"/>
<rect x="64" y="262" width="470" height="26" rx="13" fill="var(--ink)"/>
<!-- windshield -->
<rect x="88" y="130" width="92" height="58" rx="14" fill="#BCE3F0" stroke="var(--ink)" stroke-width="5"/>
<path d="M96 180 L148 138" stroke="var(--ink)" stroke-width="3" opacity=".45"/>
<path d="M100 138 l-7 34 M114 138 l-7 34" stroke="var(--cream)" stroke-width="6" opacity=".65" stroke-linecap="round"/>
<!-- door seam -->
<path d="M204 116 V258" stroke="var(--ink)" stroke-width="4" opacity=".5"/>
<rect x="188" y="170" width="15" height="6" rx="3" fill="var(--ink)"/>
<!-- brand block -->
<text class="tMain" x="249" y="166" text-anchor="middle">KIMCHI</text>
<text class="tMain" x="249" y="196" text-anchor="middle">LOCO</text>
<text class="tKr" x="249" y="226" text-anchor="middle">김치 로코</text>
<text class="tSmall" x="249" y="250" text-anchor="middle">SEOUL × CDMX</text>
<!-- serving window -->
<rect x="294" y="144" width="172" height="100" rx="16" fill="var(--cream)" stroke="var(--ink)" stroke-width="5"/>
<rect x="302" y="152" width="156" height="84" rx="12" fill="#241812"/>
<text class="tOpen" x="380" y="190" text-anchor="middle">OPEN</text>
<path d="M302 216 H458" stroke="var(--cream)" stroke-width="5"/>
<path d="M330 214 A14 12 0 0 0 358 214 Z" fill="var(--gold)" stroke="var(--ink)" stroke-width="3"/>
<circle cx="344" cy="208" r="4" fill="var(--red)"/>
<!-- awning -->
<g>
<rect x="292" y="118" width="22" height="28" fill="var(--cream)"/>
<rect x="314" y="118" width="22" height="28" fill="var(--red)"/>
<rect x="336" y="118" width="22" height="28" fill="var(--cream)"/>
<rect x="358" y="118" width="22" height="28" fill="var(--red)"/>
<rect x="380" y="118" width="22" height="28" fill="var(--cream)"/>
<rect x="402" y="118" width="22" height="28" fill="var(--red)"/>
<rect x="424" y="118" width="22" height="28" fill="var(--cream)"/>
<rect x="446" y="118" width="22" height="28" fill="var(--red)"/>
<rect x="292" y="118" width="176" height="28" fill="none" stroke="var(--ink)" stroke-width="4"/>
<path d="M292 146 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0" fill="none" stroke="var(--ink)" stroke-width="4"/>
</g>
<!-- rear deco -->
<circle cx="497" cy="184" r="26" fill="var(--gold)" stroke="var(--ink)" stroke-width="5"/>
<g transform="translate(497 186) scale(2.1) translate(-11 -13)">
<path d="M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z" fill="var(--green)" stroke="var(--ink)" stroke-width="1"/>
<path d="M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z" fill="var(--red)" stroke="var(--ink)" stroke-width="1"/>
</g>
<text class="tSmall" x="497" y="238" text-anchor="middle">EST. 2025</text>
</g>
<!-- wheels -->
<g>
<circle cx="170" cy="302" r="36" fill="var(--ink)"/>
<circle cx="170" cy="302" r="22" fill="var(--cream)" stroke="var(--ink)" stroke-width="5"/>
<g class="spin">
<path d="M170 282 V322 M150 302 H190 M156 288 L184 316 M184 288 L156 316" stroke="var(--ink)" stroke-width="6" stroke-linecap="round"/>
</g>
<circle cx="170" cy="302" r="7" fill="var(--red)"/>
</g>
<g>
<circle cx="450" cy="302" r="36" fill="var(--ink)"/>
<circle cx="450" cy="302" r="22" fill="var(--cream)" stroke="var(--ink)" stroke-width="5"/>
<g class="spin">
<path d="M450 282 V322 M430 302 H470 M436 288 L464 316 M464 288 L436 316" stroke="var(--ink)" stroke-width="6" stroke-linecap="round"/>
</g>
<circle cx="450" cy="302" r="7" fill="var(--red)"/>
</g>
</svg>
<!-- rotating stamp -->
<div class="stampWrap float" data-depth="18">
<svg viewBox="0 0 160 160" role="img" aria-label="Kimchi Loco grand opening stamp">
<circle cx="80" cy="80" r="74" fill="var(--gold)" stroke="var(--ink)" stroke-width="5"/>
<defs><path id="cp" d="M80 80 m-56 0 a56 56 0 1 1 112 0 a56 56 0 1 1 -112 0"/></defs>
<g class="rotor">
<text class="circText"><textPath href="#cp">KIMCHI LOCO × SEOUL × CDMX × GRAND OPENING ×</textPath></text>
</g>
<g transform="translate(80 82) scale(3.2) translate(-11 -13)">
<path d="M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z" fill="var(--green)" stroke="var(--ink)" stroke-width=".8"/>
<path d="M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z" fill="var(--red)" stroke="var(--ink)" stroke-width=".8"/>
</g>
</svg>
</div>
<span class="sticker heroSticker float" data-depth="10">24-HR GOCHUJANG MARINADE</span>
<span class="sticker heroSticker2 float" data-depth="12">PRESSED-TO-ORDER TORTILLAS</span>
</div>
</div>
</section>
<!-- ================= TICKER ================= -->
<div class="ticker" aria-hidden="true"><div class="tTrack"></div></div>
<!-- ================= MENU ================= -->
<section id="menu">
<div class="wrap">
<div class="secHead reveal">
<span class="kicker">TACOS · BURRITOS · FRIES · SLUSH</span>
<div class="menuBar">
<h2 class="secTitle">THE <span class="hl red">LINEUP</span></h2>
<span class="menuNote">CASH · CARD · COMPLIMENTS ACCEPTED</span>
</div>
<p class="sub">Every tortilla hits the press when you order — so the line moves at the speed of drama. Heat rated in halmoni units.</p>
</div>
<div class="menuGrid" id="menuGrid"></div>
<p class="menuFine">Prices include tax. Level 5 orders require a witness. Slush sold as emotional support.</p>
</div>
</section>
<!-- ================= SPICE-O-MATIC ================= -->
<section id="spice">
<div class="wrap">
<div class="secHead reveal">
<span class="kicker">INTERACTIVE · SLIDE WITH CAUTION</span>
<h2 class="secTitle">PICK YOUR <span class="hl">PAIN</span></h2>
<p class="sub">Slide the dial. Our chili ripens, panics, and matches you with your order. It is very scientific. Trust the chili.</p>
</div>
<div class="machine reveal">
<span class="rivet r1"></span><span class="rivet r2"></span><span class="rivet r3"></span><span class="rivet r4"></span>
<div class="machineTop">
<b>SPICE-O-MATIC™ v2.0</b>
<span>OFFICIAL HEAT NEGOTIATOR — DO NOT TAUNT</span>
<span class="screw" aria-hidden="true"></span>
</div>
<div class="machineGrid">
<div class="screen">
<svg id="chiliChar" viewBox="0 0 260 310" role="img" aria-label="A chili pepper character reacting to your spice level">
<g id="flames" style="display:none">
<g class="flame" transform="translate(58,38)">
<path d="M0 24 C -9 16 -7 4 0 -8 C 2 -2 8 -4 8 2 C 14 0 14 10 8 18 C 5 22 3 24 0 24 Z" fill="var(--gold)" stroke="var(--ink)" stroke-width="3"/>
<path d="M0 17 C -4 12 -3 6 0 0 C 2 4 5 5 4 9 C 6 10 4 14 0 17 Z" fill="var(--red)"/>
</g>
<g class="flame f2" transform="translate(176,32)">
<path d="M0 24 C -9 16 -7 4 0 -8 C 2 -2 8 -4 8 2 C 14 0 14 10 8 18 C 5 22 3 24 0 24 Z" fill="var(--gold)" stroke="var(--ink)" stroke-width="3"/>
<path d="M0 17 C -4 12 -3 6 0 0 C 2 4 5 5 4 9 C 6 10 4 14 0 17 Z" fill="var(--red)"/>
</g>
</g>
<path id="cBody" d="M132 40 C 186 46 218 96 210 150 C 203 205 168 258 118 282 C 108 287 96 284 92 274 C 66 214 58 148 74 100 C 88 60 108 38 132 40 Z" fill="#F1912B" stroke="var(--ink)" stroke-width="7"/>
<path d="M124 44 C120 26 108 14 92 10 C110 4 128 10 136 24 C140 32 138 38 134 46 Z" fill="var(--green)" stroke="var(--ink)" stroke-width="5"/>
<g id="face">
<ellipse class="cheek" cx="94" cy="150" r="11"/>
<ellipse class="cheek" cx="166" cy="150" r="11"/>
<circle class="eye" cx="112" cy="126" r="9" fill="var(--ink)"/>
<circle class="eye" cx="148" cy="126" r="9" fill="var(--ink)"/>
<circle cx="115" cy="123" r="3" fill="var(--cream)"/>
<circle cx="151" cy="123" r="3" fill="var(--cream)"/>
<path id="tongue" d="M118 164 a12 9 0 0 0 24 0 z" fill="#FF7A6B" stroke="var(--ink)" stroke-width="3" style="display:none"/>
<path id="mouth" d="M110 146 Q130 172 150 146 Q130 158 110 146 Z" fill="var(--ink)"/>
<g id="tears" style="display:none">
<ellipse cx="106" cy="142" rx="4.5" ry="6.5" fill="var(--sky)" stroke="var(--ink)" stroke-width="2.5"/>
<ellipse cx="154" cy="142" rx="4.5" ry="6.5" fill="var(--sky)" stroke="var(--ink)" stroke-width="2.5"/>
</g>
</g>
<path id="sweat" d="M186 112 C 194 126 194 134 186 138 C 178 134 178 126 186 112 Z" fill="var(--sky)" stroke="var(--ink)" stroke-width="3"/>
</svg>
<div class="screenLabel">HEAT INDEX <b id="heatIndex">2/5</b></div>
</div>
<div class="controls">
<div class="sliderRow">
<input type="range" id="spiceSlider" min="0" max="5" step="1" value="2" aria-label="Spice level from 0 to 5">
</div>
<div class="ticks" id="ticks">
<span>MILD</span><span>WARM</span><span>GOCHU</span><span>FIRE</span><span>VETERAN</span><span>PAIN</span>
</div>
<div class="ticket">
<b id="recName">GOCHU GANG</b>
<span class="pick" id="recItem">GOCHUJANG CRUNCH TACO</span>
<p id="recText">The regulars' frequency. Double-crunch chicken, medium glaze. Napkins nearby. Pride intact.</p>
<button class="btn red add" id="recAdd" data-name="GOCHUJANG CRUNCH TACO" data-price="4.5">ADD THE PICK — $4.50</button>
</div>
</div>
</div>
<p class="machineFoot">NO REFUNDS ON PRIDE. ANTIDOTE SOLD ON PREMISE (SEE: YUZU MELT SLUSH).</p>
</div>
</div>
</section>
<!-- ================= STORY ================= -->
<section id="story">
<div class="wrap">
<div class="secHead reveal">
<span class="kicker">THE ORIGIN, BRIEFLY</span>
<h2 class="secTitle">HOW WE GET <span class="hl">LOCO</span></h2>
<p class="sub">One family recipe book, one tortilla press, and zero chill. That's the whole business plan, and honestly it's working.</p>
</div>
<div class="statStrip reveal">
<div class="stat"><span class="num" data-n="24">0</span><small>HRS MARINATING</small></div>
<div class="stat"><span class="num" data-n="90">0</span><small>DAY KIMCHI FERMENT</small></div>
<div class="stat"><span class="num" data-n="0">0</span><small>CHILL GIVEN</small></div>
</div>
<div class="steps">
<div class="step reveal"><i>01</i><b>MARINATE</b><p>Bulgogi sleeps 24 hours in gochujang, pear and garlic. Dreams included.</p></div>
<div class="step reveal"><i>02</i><b>FERMENT</b><p>Kimchi aged in-house for 90 days. Halmoni inspects. Halmoni approves.</p></div>
<div class="step reveal"><i>03</i><b>PRESS</b><p>Tortillas pressed per order. Thirty seconds of pure theater at the window.</p></div>
<div class="step reveal"><i>04</i><b>SMASH</b><p>Seared hard, sauced loud, wrapped with love and a little bit of regret.</p></div>
</div>
</div>
</section>
<!-- ================= FIND US ================= -->
<section id="find">
<div class="wrap">
<div class="secHead reveal">
<span class="kicker">GPS FOR YOUR STOMACH</span>
<h2 class="secTitle">CATCH THE <span class="hl red">TRUCK</span></h2>
<p class="sub">We move. That's the deal. Here's where the rig lands this week — highlight reel below updates with your actual today.</p>
</div>
<div class="status reveal" id="statusBox">
<span class="liveDot" id="liveDot" aria-hidden="true"></span>
<div><b id="statusMain">Checking the rig…</b><span id="statusSub">Warming up the tortilla press.</span></div>
</div>
<div class="findGrid">
<ul class="sched reveal" id="sched"></ul>
<div class="mapWrap reveal">
<svg id="map" viewBox="0 0 300 300" role="img" aria-label="Stylized map of this week's truck stops">
<rect x="0" y="0" width="300" height="300" rx="22" fill="#F3E4C6"/>
<path d="M0 60H300M0 150H300M0 240H300M50 0V300M140 0V300M230 0V300" stroke="rgba(28,19,14,.13)" stroke-width="16"/>
<g><rect x="152" y="14" width="64" height="32" rx="8" fill="rgba(63,155,79,.5)" stroke="var(--ink)" stroke-width="2.5"/><text class="mtxt" x="184" y="34">HANA PARK</text></g>
<g><rect x="60" y="14" width="64" height="32" rx="8" fill="rgba(224,49,31,.35)" stroke="var(--ink)" stroke-width="2.5"/><text class="mtxt" x="92" y="34">NIGHT MKT</text></g>
<g><rect x="152" y="112" width="64" height="44" rx="8" fill="rgba(255,197,49,.6)" stroke="var(--ink)" stroke-width="2.5"/><text class="mtxt" x="184" y="132">IRON NODE</text><text class="mtxt" x="184" y="146">BREWING</text></g>
<g><rect x="152" y="162" width="64" height="62" rx="8" fill="rgba(63,155,79,.35)" stroke="var(--ink)" stroke-width="2.5"/><text class="mtxt" x="184" y="196">KSU</text><text class="mtxt" x="184" y="210">COMMONS</text></g>
<g><rect x="152" y="252" width="64" height="34" rx="8" fill="rgba(28,19,14,.12)" stroke="var(--ink)" stroke-width="2.5"/><text class="mtxt" x="184" y="272">DEPOT</text></g>
<path class="route" d="M50 60 H230 V268 H140 V60" fill="none" stroke="var(--red)" stroke-width="5" stroke-dasharray="12 12" stroke-linecap="round"/>
<circle cx="140" cy="150" r="10" fill="var(--gold)" stroke="var(--ink)" stroke-width="3"/>
<text class="mtxt" x="140" y="176">MAIN & 3RD</text>
<g id="pin">
<circle class="ping" r="18" fill="rgba(224,49,31,.35)"/>
<circle r="6" fill="var(--red)" stroke="var(--ink)" stroke-width="3"/>
<g transform="translate(-16,-36)">
<rect x="0" y="0" width="32" height="18" rx="4" fill="var(--red)" stroke="var(--ink)" stroke-width="3"/>
<rect x="23" y="3" width="7" height="9" rx="2" fill="#BCE3F0" stroke="var(--ink)" stroke-width="2"/>
<rect x="3" y="16" width="8" height="6" rx="3" fill="var(--ink)"/>
<rect x="21" y="16" width="8" height="6" rx="3" fill="var(--ink)"/>
</g>
</g>
<text id="pinLabel" class="mtxt" x="140" y="150"></text>
</svg>
<span class="mapNote">IF THE PIN MOVES, FOLLOW IT. THAT'S THE WHOLE BUSINESS MODEL.</span>
</div>
</div>
</div>
</section>
<!-- ================= TESTIMONIALS ================= -->
<section id="love">
<div class="wrap">
<div class="secHead reveal">
<span class="kicker">UNPAID. UNPROMPTED. UNHINGED.</span>
<h2 class="secTitle">THE STREETS ARE <span class="hl">TALKING</span></h2>
</div>
<div class="loveGrid">
<div class="loveCard reveal" style="--rot:-1.4deg">
<div class="stars" aria-label="5 stars"></div>
<q>I ordered one taco "to try it." Four tacos later I was asking about a loyalty program that did not exist yet.</q>
<footer>— DANA R. · OPENING WEEK SURVIVOR</footer>
</div>
<div class="loveCard reveal" style="--rot:1.2deg">
<div class="stars" aria-label="5 stars"></div>
<q>The fries have nori dust. NORI. DUST. I think about them during meetings I'm supposed to be leading.</q>
<footer>— MARCUS T. · THURSDAY REGULAR</footer>
</div>
<div class="loveCard reveal" style="--rot:-0.8deg">
<div class="stars" aria-label="5 stars"></div>
<q>My abuela nodded once while eating. She never nods. I have witnesses. I have video.</q>
<footer>— SOFIA L. · TACO #214</footer>
</div>
<div class="loveCard reveal" style="--rot:1.6deg">
<div class="stars" aria-label="5 stars"></div>
<q>Went level 5. Cried a little. Five stars for flavor, one star for my ego. Averaged out: five. Math is healing.</q>
<footer>— JAE P. · PAIN LOCO CERTIFIED</footer>
</div>
</div>
</div>
</section>
</main>
<!-- ================= FOOTER ================= -->
<div class="ticker rev" aria-hidden="true"><div class="tTrack"></div></div>
<footer>
<div class="wrap">
<h2 class="footTitle reveal">FOLLOW<br>THE <span class="hl">FLAVOR</span></h2>
<div class="footGrid">
<div class="reveal">
<div class="newsLabel">GET THE DAILY LOCATION PING</div>
<p class="newsSub">One message a day. Zero spam. Occasional free-topping codes for the loyal and the hungry.</p>
<form class="newsForm" id="newsForm" novalidate>
<input type="email" id="newsInput" placeholder="you@hungry.com" aria-label="Email address" required>
<button class="btn red" type="submit">PING ME</button>
</form>
</div>
<div class="socials reveal">
<a class="chip" href="#" aria-label="Instagram">
<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="5.5" fill="none" stroke="var(--cream)" stroke-width="2.2"/><circle cx="12" cy="12" r="4" fill="none" stroke="var(--cream)" stroke-width="2.2"/><circle cx="17.2" cy="6.8" r="1.4" fill="var(--cream)"/></svg>
@KIMCHI.LOCO<small>DAILY</small>
</a>
<a class="chip" href="#" aria-label="TikTok">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9.5 17.6A2.8 2.8 0 1 1 7 15v-8.4l10-1.8v7a2.8 2.8 0 1 1-2.5-2.8v-3L9 7.2z" fill="var(--cream)"/></svg>
@KIMCHILOCO<small>CHAOS</small>
</a>
<a class="chip" href="#" aria-label="Call or text">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6.5 3.5c.8 0 1.5.5 1.8 1.2l1 2.3c.3.7.1 1.5-.5 2l-.9.8a12.5 12.5 0 0 0 5.8 5.8l.8-.9c.5-.6 1.3-.8 2-.5l2.3 1c.7.3 1.2 1 1.2 1.8v2c0 1.1-.9 2-2 1.9C9 19.6 4.4 15 3.9 8.2 3.8 7.1 4.7 6.1 5.8 6.1h.7z" fill="var(--cream)"/></svg>
(555) 010-LOCO<small>TEXT US</small>
</a>
</div>
</div>
<div class="fine">
<span>© 2025 KIMCHI LOCO — ONE TRUCK, FULL HEART. EVERYTHING TOUCHES SESAME, JOY, AND PROBABLY YOUR SOUL.</span>
<span><b>김치 로코 — 맛있겠다!</b> SITE MADE WITH GOCHUJANG.</span>
</div>
</div>
</footer>
<!-- ================= TOASTS & MODAL ================= -->
<div id="toasts" aria-live="polite"></div>
<div class="overlay" id="overlay" aria-hidden="true">
<div class="modal" role="dialog" aria-modal="true" aria-label="Your bag">
<div class="modalHead">
<h3>YOUR BAG</h3>
<button class="x" id="closeModal" aria-label="Close bag">
<svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path d="M5 5 19 19 M19 5 5 19" stroke="var(--ink)" stroke-width="3.4" stroke-linecap="round"/></svg>
</button>
</div>
<ul id="bagList"></ul>
<div class="bagTotal"><span>TOTAL</span><b id="bagTotal">$0.00</b></div>
<p class="modalNote">Pay at the window — cash, card, or an extremely good compliment.</p>
<div class="modalBtns">
<button class="btn ghost" id="clearBag">CLEAR</button>
<button class="btn red" id="checkout">LOCK IT IN</button>
</div>
</div>
</div>
<script>
(function(){
"use strict";
/* ---------- tiny helpers ---------- */
var $=function(id){return document.getElementById(id)};
function pad(n){return (n<10?"0":"")+n}
function money(n){return "$"+n.toFixed(2)}
function toast(title,sub){
var t=document.createElement("div");
t.className="toast";
t.innerHTML="<b></b><span></span>";
t.firstChild.textContent=title;
t.lastChild.textContent=sub||"";
$("toasts").appendChild(t);
setTimeout(function(){t.style.transition="opacity .35s";t.style.opacity="0";},3000);
setTimeout(function(){t.remove();},3450);
}
/* ---------- chili icon factory ---------- */
function chili(on){
return '<svg class="chi'+(on?"":" off")+'" viewBox="0 0 24 24" aria-hidden="true">'+
'<path class="cs" d="M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z"/>'+
'<path class="cb" d="M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z"/></svg>';
}
/* ---------- tickers ---------- */
var TICK=["KIMCHI LOCO","GOCHUJANG EVERYTHING","90-DAY KIMCHI","PRESSED-TO-ORDER TORTILLAS","SEOUL × CDMX","GRAND OPENING WEEK","HEAT RATED IN HALMONI UNITS"];
function tickerHTML(){
var s="";
for(var i=0;i<TICK.length;i++){s+='<span class="tk">'+TICK[i]+"</span>"+chili(true);}
return s;
}
var tracks=document.querySelectorAll(".tTrack");
for(var ti=0;ti<tracks.length;ti++){
tracks[ti].innerHTML='<div class="tGroup">'+tickerHTML()+'</div><div class="tGroup">'+tickerHTML()+"</div>";
}
/* ---------- menu art ---------- */
var INK="#1C130E",CREAM="#FFF3DC",GOLD="#FFC531",RED="#E0311F",GREEN="#3F9B4F",SHELL="#F2A93B";
var ART={
taco:'<svg viewBox="0 0 120 100" aria-hidden="true"><path d="M10 58 A50 50 0 0 0 110 58 Z" fill="'+SHELL+'" stroke="'+INK+'" stroke-width="4"/><circle cx="34" cy="52" r="9" fill="'+RED+'" stroke="'+INK+'" stroke-width="3"/><circle cx="54" cy="46" r="8" fill="'+GREEN+'" stroke="'+INK+'" stroke-width="3"/><circle cx="72" cy="52" r="8" fill="'+CREAM+'" stroke="'+INK+'" stroke-width="3"/><circle cx="90" cy="55" r="8" fill="#8A4B2A" stroke="'+INK+'" stroke-width="3"/><path d="M24 56 Q60 42 96 54" fill="none" stroke="'+RED+'" stroke-width="4" stroke-linecap="round" stroke-dasharray="2 10"/></svg>',
crunch:'<svg viewBox="0 0 120 100" aria-hidden="true"><path d="M10 58 A50 50 0 0 0 110 58 Z" fill="'+SHELL+'" stroke="'+INK+'" stroke-width="4"/><circle cx="30" cy="54" r="9" fill="'+RED+'" stroke="'+INK+'" stroke-width="3"/><circle cx="50" cy="47" r="9" fill="'+RED+'" stroke="'+INK+'" stroke-width="3"/><circle cx="70" cy="54" r="8" fill="'+GOLD+'" stroke="'+INK+'" stroke-width="3"/><circle cx="89" cy="49" r="8" fill="'+GREEN+'" stroke="'+INK+'" stroke-width="3"/><path d="M18 58 Q60 40 102 52" fill="none" stroke="'+RED+'" stroke-width="5" stroke-linecap="round"/><path d="M60 24 l4 7 8 1-6 6 2 8-8-4-8 4 2-8-6-6 8-1z" fill="'+GOLD+'" stroke="'+INK+'" stroke-width="2"/></svg>',
burrito:'<svg viewBox="0 0 120 100" aria-hidden="true"><rect x="26" y="34" width="74" height="42" rx="21" fill="#F6E7C9" stroke="'+INK+'" stroke-width="4"/><ellipse cx="28" cy="55" rx="14" ry="21" fill="'+CREAM+'" stroke="'+INK+'" stroke-width="4"/><circle cx="26" cy="47" r="5" fill="'+RED+'"/><circle cx="30" cy="58" r="5" fill="'+GREEN+'"/><circle cx="24" cy="67" r="4" fill="'+GOLD+'"/><path d="M44 40 h48 M44 70 h48" stroke="'+INK+'" stroke-width="3" opacity=".3"/><path d="M58 28 q3 -8 0 -13 M74 28 q3 -8 0 -13" stroke="'+INK+'" stroke-width="3" fill="none" opacity=".45" stroke-linecap="round"/></svg>',
queso:'<svg viewBox="0 0 120 100" aria-hidden="true"><path d="M18 74 L44 28 L66 74 Z" fill="'+SHELL+'" stroke="'+INK+'" stroke-width="4"/><path d="M42 74 L70 24 L94 74 Z" fill="'+GOLD+'" stroke="'+INK+'" stroke-width="4"/><circle cx="48" cy="44" r="4" fill="'+GREEN+'"/><circle cx="66" cy="36" r="4" fill="'+RED+'"/><circle cx="58" cy="52" r="3.5" fill="'+CREAM+'" stroke="'+INK+'" stroke-width="1.6"/><path d="M30 62 q22 10 56 -2" stroke="'+RED+'" stroke-width="4" fill="none" stroke-linecap="round"/></svg>',
fries:'<svg viewBox="0 0 120 100" aria-hidden="true"><g stroke="'+INK+'" stroke-width="3" fill="'+GOLD+'"><rect x="36" y="12" width="10" height="34" rx="4" transform="rotate(-9 41 30)"/><rect x="52" y="8" width="10" height="38" rx="4" transform="rotate(4 57 28)"/><rect x="66" y="12" width="10" height="34" rx="4" transform="rotate(12 71 30)"/><rect x="80" y="16" width="10" height="30" rx="4" transform="rotate(20 85 30)"/></g><path d="M30 44 L26 86 L94 86 L90 44 Z" fill="'+RED+'" stroke="'+INK+'" stroke-width="4"/><rect x="40" y="58" width="40" height="15" rx="7" fill="'+CREAM+'" stroke="'+INK+'" stroke-width="3"/><path d="M60 61.5 l1.6 3.4 3.8.5-2.8 2.6.7 3.8-3.3-1.8-3.3 1.8.7-3.8-2.8-2.6 3.8-.5z" fill="'+RED+'"/></svg>',
slush:'<svg viewBox="0 0 120 100" aria-hidden="true"><rect x="64" y="8" width="9" height="36" rx="4" fill="'+GREEN+'" stroke="'+INK+'" stroke-width="3" transform="rotate(14 68 24)"/><path d="M36 40 A24 22 0 0 1 84 40 Z" fill="'+GOLD+'" stroke="'+INK+'" stroke-width="4"/><path d="M38 40 L34 86 L86 86 L82 40 Z" fill="'+CREAM+'" stroke="'+INK+'" stroke-width="4"/><path d="M41 54 L38 80 L82 80 L79 54 Z" fill="'+GOLD+'"/><g fill="'+CREAM+'" stroke="'+INK+'" stroke-width="2.4"><rect x="48" y="58" width="12" height="12" rx="3" transform="rotate(-10 54 64)"/><rect x="66" y="64" width="11" height="11" rx="3" transform="rotate(14 71 69)"/></g></svg>'
};
/* ---------- menu data & render ---------- */
var MENU=[
{id:"kbbq",name:"THE K-BBQ TACO",kr:"불고기 타코",desc:"24-hour gochujang bulgogi, kimchi-lime slaw, sesame crema, scallion snow. The one that started the line.",price:4.5,heat:2,art:ART.taco,tag:"MOST LOVED"},
{id:"chicken",name:"GOCHUJANG CRUNCH TACO",kr:"고추장 타코",desc:"Double-fried chicken thighs, sticky gochujang glaze, pickled daikon, cucumber ribbons. Loud. Very loud.",price:4.5,heat:3,art:ART.crunch,tag:""},
{id:"burrito",name:"SEOUL STACK BURRITO",kr:"서울 스택",desc:"Galbi, kimchi fried rice, black beans, queso-but-make-it-gochujang. Two hands. No exceptions.",price:11,heat:2,art:ART.burrito,tag:""},
{id:"queso",name:"KIMCHI QUESADILLA",kr:"김치 케사디아",desc:"90-day aged kimchi + stretchy mozzarella, green-onion crema, toasted until it hisses at you.",price:9,heat:2,art:ART.queso,tag:""},
{id:"fries",name:"LOADED KIMCHI FRIES",kr:"김치 감자",desc:"Crinkle fries buried under bulgogi, gochujang queso, nori dust, jalapeño confetti. Share… or don't.",price:10,heat:3,art:ART.fries,tag:""},
{id:"slush",name:"YUZU MELT SLUSH",kr:"유자 슬러시",desc:"Yuzu, plum, crushed ice, salvation. Zero heat, one hundred percent rescue mission.",price:4,heat:0,art:ART.slush,tag:"FIRE EXTINGUISHER"}
];
var grid=$("menuGrid");
var html="";
for(var i=0;i<MENU.length;i++){
var m=MENU[i];
var rot=((i%2===0?1:-1)*(0.5+(i%3)*0.35)).toFixed(2);
html+='<article class="card reveal" style="--rot:'+rot+'deg">'+
'<div class="cardTop"><div class="cardArt">'+m.art+'</div><span class="price">'+money(m.price)+"</span></div>"+
"<h3>"+m.name+"<em>"+m.kr+"</em></h3><p>"+m.desc+"</p>"+
'<div class="cardFoot"><div class="heat" data-heat="'+m.heat+'" role="img" aria-label="Heat '+m.heat+' of 5"></div>'+
'<button class="btn small add" data-name="'+m.name+'" data-price="'+m.price+'">ADD +</button></div>'+
(m.tag?'<span class="ribbon">'+m.tag+"</span>":"")+
"</article>";
}
grid.innerHTML=html;
/* heat meters */
var heats=document.querySelectorAll(".heat");
for(var h=0;h<heats.length;h++){
var n=+heats[h].getAttribute("data-heat"),s="";
for(var c=0;c<5;c++){s+=chili(c<n);}
heats[h].innerHTML=s;
}
/* ---------- bag ---------- */
var bag=[];
function addToBag(name,price){
var found=null;
for(var i=0;i<bag.length;i++){if(bag[i].name===name){found=bag[i];break;}}
if(found){found.qty++;}else{bag.push({name:name,price:price,qty:1});}
renderBag();
var bc=$("bagCount");
bc.textContent=totalQty();
bc.classList.remove("pop");void bc.offsetWidth;bc.classList.add("pop");
toast("Added: "+name,"Bag total "+money(totalPrice())+" — pay at the window.");
}
function totalQty(){var q=0;for(var i=0;i<bag.length;i++){q+=bag[i].qty;}return q;}
function totalPrice(){var t=0;for(var i=0;i<bag.length;i++){t+=bag[i].qty*bag[i].price;}return t;}
function renderBag(){
var list=$("bagList");
if(bag.length===0){
list.innerHTML='<li class="empty">Bag\u2019s empty. The menu is right up there \u2014 go make friends with it.</li>';
}else{
var s="";
for(var i=0;i<bag.length;i++){
s+="<li><span>"+bag[i].name+" ×"+bag[i].qty+"</span><span>"+money(bag[i].price*bag[i].qty)+"</span></li>";
}
list.innerHTML=s;
}
$("bagTotal").textContent=money(totalPrice());
}
renderBag();
/* add-to-bag delegation (works for menu cards + spice pick) */
document.addEventListener("click",function(e){
var btn=e.target.closest(".add");
if(!btn)return;
addToBag(btn.getAttribute("data-name"),parseFloat(btn.getAttribute("data-price")));
var old=btn.textContent;
btn.textContent="ADDED!";
setTimeout(function(){btn.textContent=old;},900);
});
/* modal */
var overlay=$("overlay");
function openModal(){overlay.classList.add("open");overlay.setAttribute("aria-hidden","false");}
function closeModal(){overlay.classList.remove("open");overlay.setAttribute("aria-hidden","true");}
$("bagBtn").addEventListener("click",openModal);
$("closeModal").addEventListener("click",closeModal);
overlay.addEventListener("click",function(e){if(e.target===overlay)closeModal();});
document.addEventListener("keydown",function(e){if(e.key==="Escape")closeModal();});
$("clearBag").addEventListener("click",function(){bag=[];renderBag();$("bagCount").textContent="0";toast("Bag cleared","Bold move. We respect it.");});
$("checkout").addEventListener("click",function(){
if(bag.length===0){toast("Nothing to lock in yet","Add something loud first.");return;}
toast("Locked in!","Cooked fresh when you arrive \u2014 see you at the window.");
bag=[];renderBag();$("bagCount").textContent="0";closeModal();
});
/* ---------- SPICE-O-MATIC ---------- */
var LEVELS=[
{name:"MILD CHILD",color:"#7FBE58",item:"KIMCHI QUESADILLA",price:9,
text:"Zero-heat entry point. Aged kimchi and mozzarella, easy on the crema. Friendly. Warm. A gateway taco.",
mouth:"M114 150 Q130 160 146 150",fill:false},
{name:"WARM & CURIOUS",color:"#E9B83E",item:"THE K-BBQ TACO",price:4.5,
text:"K-BBQ taco with the mellow slaw. Flavor walks in first; heat just waves politely from the doorway.",
mouth:"M110 148 Q130 166 150 148",fill:false},
{name:"GOCHU GANG",color:"#F1912B",item:"GOCHUJANG CRUNCH TACO",price:4.5,
text:"The regulars\u2019 frequency. Double-crunch chicken, medium glaze. Napkins nearby. Pride intact.",
mouth:"M110 146 Q130 172 150 146 Q130 158 110 146 Z",fill:true},
{name:"FIRE-NOODLE CERTIFIED",color:"#EE5A23",item:"LOADED KIMCHI FRIES",price:10,
text:"You have eaten fire noodles on purpose. Extra queso, nori dust. You will be fine. Probably.",
mouth:"M130 156m-11 0a11 13 0 1 0 22 0a11 13 0 1 0 -22 0",fill:true},
{name:"KIMCHI VETERAN",color:"#E0311F",item:"SEOUL STACK \u2014 EXTRA LOCO",price:11,
text:"Say \u201cextra loco\u201d at the window. We nod slowly, with deep and sincere respect. Bring a friend for backup.",
mouth:"M130 158m-13 0a13 15 0 1 0 26 0a13 15 0 1 0 -26 0",fill:true},
{name:"PAIN LOCO",color:"#B31217",item:"THE PAIN LOCO (CHEF\u2019S DARE)",price:4.5,
text:"Tell us \u201cmake me cry.\u201d We whisper a prayer, sauce accordingly, and stage a Yuzu Melt for the aftermath.",
mouth:"M130 160m-17 0a17 18 0 1 0 34 0a17 18 0 1 0 -34 0",fill:true}
];
var slider=$("spiceSlider"),mouth=$("mouth"),tongue=$("tongue");
function setLevel(v){
var L=LEVELS[v];
$("cBody").setAttribute("fill",L.color);
mouth.setAttribute("d",L.mouth);
if(L.fill){mouth.setAttribute("fill","#1C130E");mouth.setAttribute("stroke","none");}
else{mouth.setAttribute("fill","none");mouth.setAttribute("stroke","#1C130E");mouth.setAttribute("stroke-width","7");}
mouth.setAttribute("stroke-linecap","round");
tongue.style.display=(v===5)?"":"none";
$("sweat").style.display=(v>=3)?"":"none";
$("sweat").style.opacity=(v>=3)?"1":"0";
$("tears").style.display=(v>=4)?"":"none";
$("flames").style.display=(v===5)?"":"none";
$("chiliChar").classList.toggle("shaking",v===5);
$("heatIndex").textContent=v+"/5";
$("recName").textContent=L.name;
$("recItem").textContent=L.item;
$("recText").textContent=L.text;
var ra=$("recAdd");
ra.setAttribute("data-name",L.item);
ra.setAttribute("data-price",L.price);
ra.textContent="ADD THE PICK \u2014 "+money(L.price);
var ticks=$("ticks").children;
for(var i=0;i<ticks.length;i++){ticks[i].classList.toggle("on",i===v);}
}
slider.addEventListener("input",function(){setLevel(+slider.value);});
setLevel(2);
/* ---------- grand opening countdown ---------- */
function nextOpening(){
var n=new Date(),t=new Date(n);
t.setDate(n.getDate()+((6-n.getDay()+7)%7));
t.setHours(11,0,0,0);
if(t<=n){t.setDate(t.getDate()+7);}
return t;
}
var target=nextOpening();
function tickCd(){
var diff=Math.max(0,target-new Date());
var d=Math.floor(diff/86400000),
h=Math.floor(diff/3600000)%24,
mn=Math.floor(diff/60000)%60,
s=Math.floor(diff/1000)%60;
$("cd").textContent=pad(d)+"d "+pad(h)+"h "+pad(mn)+"m "+pad(s)+"s";
if(diff<=0){target=nextOpening();}
}
tickCd();
setInterval(tickCd,1000);
/* ---------- schedule / live status / map ---------- */
var DAYL=["SUN","MON","TUE","WED","THU","FRI","SAT"];
var DAYFULL=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];
var SCHED=[
{off:true,spot:"OFF \u2014 DEPOT NAP",note:"Recharging. Rigorous quality control (we eat the leftovers).",pin:[140,268],short:"DEPOT"},
{off:true,spot:"OFF \u2014 MARINATING",note:"The bulgogi needs its 24 hours. Respect the process.",pin:[140,268],short:"DEPOT"},
{off:false,spot:"HANA PARK \u2014 SOUTH GATE",time:"11:00\u201315:00",open:11,close:15,tag:"LUNCH RUSH",pin:[230,46],short:"HANA PARK"},
{off:false,spot:"IRON NODE BREWING",time:"16:00\u201321:00",open:16,close:21,tag:"BEER GARDEN",pin:[140,120],short:"BREWERY"},
{off:false,spot:"KSU CAMPUS COMMONS",time:"11:00\u201315:00",open:11,close:15,tag:"STUDENT FUEL",pin:[230,205],short:"CAMPUS"},
{off:false,spot:"RIVERSIDE NIGHT MARKET",time:"17:00\u201323:00",open:17,close:23,tag:"NEON NIGHTS",pin:[100,60],short:"NIGHT MKT"},
{off:false,spot:"MAIN & 3RD \u2014 GRAND PLAZA",time:"11:00\u201323:00",open:11,close:23,tag:"THE BIG DAY",pin:[140,150],short:"MAIN & 3RD"}
];
var now=new Date(),todayIdx=now.getDay(),hrNow=now.getHours()+now.getMinutes()/60;
function nextStop(from){
for(var i=1;i<=7;i++){
var idx=(from+i)%7;
if(!SCHED[idx].off){return{idx:idx,day:DAYFULL[idx]};}
}
return null;
}
var rowsHtml="";
for(var r=0;r<7;r++){
var sc=SCHED[r],isToday=(r===todayIdx);
rowsHtml+='<li class="'+(sc.off?"off":"")+" "+(isToday?"today":"")+'">'+
'<span class="day">'+DAYL[r]+(isToday?'<i>TODAY</i>':"")+"</span>"+
'<span class="spot">'+sc.spot+(sc.off?"<small>"+sc.note+"</small>":'<small>'+sc.tag+"</small>")+"</span>"+
'<span class="time">'+(sc.off?"\u2014":sc.time)+"</span></li>";
}
$("sched").innerHTML=rowsHtml;
var T=SCHED[todayIdx],nxt=nextStop(todayIdx);
if(T.off){
$("statusMain").textContent="TODAY: OFF \u2014 "+T.note;
$("statusSub").textContent=(nxt?"Next stop: "+nxt.day+" \u00b7 "+SCHED[nxt.idx].spot+" \u00b7 "+SCHED[nxt.idx].time:"");
$("liveDot").classList.add("rest");
}else if(hrNow>=T.open&&hrNow<T.close){
$("statusMain").textContent="THE RIG IS LIVE RIGHT NOW \u2014 "+T.spot;
$("statusSub").textContent="Window open till "+pad(T.close)+":00 \u00b7 "+T.tag+". Run, don\u2019t walk.";
}else if(hrNow<T.open){
$("statusMain").textContent="TODAY\u2019S SHIFT STARTS "+pad(T.open)+":00 \u2014 "+T.spot;
$("statusSub").textContent="Set an alarm. The line forms before the awning is even up.";
$("liveDot").classList.add("rest");
}else{
$("statusMain").textContent="WRAPPED FOR TODAY \u2014 STOVE\u2019S COOLING";
$("statusSub").textContent=(nxt?"Next stop: "+nxt.day+" \u00b7 "+SCHED[nxt.idx].spot+" \u00b7 "+SCHED[nxt.idx].time:"");
$("liveDot").classList.add("rest");
}
var pin=$("pin"),pinLabel=$("pinLabel");
pin.setAttribute("transform","translate("+T.pin[0]+" "+T.pin[1]+")");
pinLabel.setAttribute("x",T.pin[0]);
pinLabel.setAttribute("y",T.pin[1]+28);
pinLabel.textContent="TODAY \u00b7 "+T.short;
/* ---------- testimonial stars ---------- */
var starSvg='<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 2l2.9 6.2 6.6.8-4.9 4.6 1.3 6.5L12 17l-5.9 3.1 1.3-6.5L2.5 9l6.6-.8Z"/></svg>';
var starRows=document.querySelectorAll(".stars");
for(var sr=0;sr<starRows.length;sr++){
starRows[sr].innerHTML=starSvg+starSvg+starSvg+starSvg+starSvg;
}
/* ---------- scroll reveals & counters ---------- */
var reveals=document.querySelectorAll(".reveal");
var io=new IntersectionObserver(function(entries){
entries.forEach(function(en){
if(en.isIntersecting){en.target.classList.add("in");io.unobserve(en.target);}
});
},{threshold:.12,rootMargin:"0px 0px -40px 0px"});
for(var rv=0;rv<reveals.length;rv++){
reveals[rv].style.transitionDelay=((rv%4)*70)+"ms";
io.observe(reveals[rv]);
}
function countUp(el){
var t=+el.getAttribute("data-n"),st=performance.now(),dur=1300;
function frame(nowT){
var p=Math.min(1,(nowT-st)/dur);
el.textContent=Math.round(t*(1-Math.pow(1-p,3)));
if(p<1){requestAnimationFrame(frame);}
}
requestAnimationFrame(frame);
}
var nums=document.querySelectorAll(".num");
var io2=new IntersectionObserver(function(entries){
entries.forEach(function(en){
if(en.isIntersecting){countUp(en.target);io2.unobserve(en.target);}
});
},{threshold:.6});
for(var nm=0;nm<nums.length;nm++){io2.observe(nums[nm]);}
/* ---------- hero parallax (desktop pointers only) ---------- */
if(window.matchMedia("(pointer:fine)").matches){
var hero=$("hero"),floats=document.querySelectorAll(".float");
hero.addEventListener("mousemove",function(e){
var rc=hero.getBoundingClientRect();
var x=(e.clientX-rc.left)/rc.width-.5;
var y=(e.clientY-rc.top)/rc.height-.5;
for(var f=0;f<floats.length;f++){
var d=parseFloat(floats[f].getAttribute("data-depth"))||10;
floats[f].style.transform="translate("+(-x*d).toFixed(1)+"px,"+(-y*d).toFixed(1)+"px)";
}
});
}
/* ---------- newsletter ---------- */
$("newsForm").addEventListener("submit",function(e){
e.preventDefault();
var inp=$("newsInput"),v=inp.value.trim();
if(!/^\S+@\S+\.\S+$/.test(v)){
inp.classList.add("shakeX");
setTimeout(function(){inp.classList.remove("shakeX");},500);
toast("That email looks undercooked","Give us a real one and the pings will flow.");
return;
}
inp.value="";
toast("You\u2019re on the list!","First ping = free topping. This is a legally binding taco promise.");
});
})();
</script>
</body>
</html>Reasoning trace (40,548 tokens)
Let me design a killer single-page site for a Korean taco food truck called "Kimchi Loco". The vibe: Korean-Mexican fusion, bold, energetic, street-food energy. Think hot pink / korean chili red, lime green, cream, black. Bold typography, playful Korean text accents (김치 로코), marquee ticker, spice meter, interactive menu cards, location/schedule, testimonials, hero with animated elements.
Key requirements:
- Single HTML file, all inline
- No external fonts except allowed CDNs? "No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task." The task doesn't specify CDN URLs, so I should avoid Google Fonts. Hmm, that's restrictive for typography. I can use system font stacks creatively. Actually I can't load custom fonts. So I need to make system fonts look great — use big heavy weights of system stacks: "Arial Black", "Helvetica Neue", Impact-ish. I can use font stacks like `'Archivo Black', 'Arial Black', sans-serif` (Archivo Black won't load but Arial Black fallback works). Or use SVG text. I'll use heavy system stacks and letter-spacing, big sizes.
Actually wait — "no network requests" — I could embed a font as base64 but that's huge. Better to design with system fonts and make it look great via scale, weight, spacing, and color.
I could draw the truck as an SVG illustration — a stylized food truck with the brand. That's a signature visual. Also animated flame/steam, floating taco emojis? No emoji — I'll draw SVG tacos/chili peppers.
Design concept:
- Dark background? Or cream/paper. Street food = bold. Let me go with a warm cream (#FFF6E9 or similar) with hot red/pink (#E6332C or #FF3B30... Korean chili red) and lime/green accents, black ink outlines. Kind of a neo-brutalist / street poster aesthetic. Thick borders, hard shadows, sticker-like badges, marquee ticker tape, halftone dots.
- Hero: giant headline "KOREAN MEETS TACO. NOBODY WINS. EVERYBODY WINS." with big "KIMCHI LOCO" wordmark, Korean subtitle 김치 로코, SVG truck illustration with animated wheels and steam, floating stickers.
- Ticker marquee: "KIMCHI LOCO ★ TACOS ★ K-BBQ ★ ..." scrolling.
- Menu section: interactive cards with heat meter (chili icons), price, hover tilt. Maybe a "build your bite" interactive: pick protein + toppings? Keep it snappy.
- Signature interactive moment: "Spice-o-Meter" slider — drag to see recommended taco matched to your heat tolerance, with the character changing expression. Or a flame that grows. That's fun and on-brand.
- Location section: weekly schedule as cards + a live "open now" indicator based on actual local time? Could compute. "Find us" with a map-like graphic (stylized streets). Maybe show today's location highlighted.
- Testimonials as sticker notes rotated.
- Big CTA footer: "HUNGRY? FOLLOW THE TRUCK" with phone/instagram fake links (no real requests — use # links).
- Social proof numbers: "12,483 tacos served" counting up animation.
- Confetti-ish floating chili SVGs subtle parallax on mousemove.
Interactivity requirements: game-like demos... this isn't a game, but "Something meaningful must be visible on first paint" — fine. There's no keyboard need really. But adding a small interactive element is good. Since it's a website, not a game, the "self-playing demo mode" doesn't strictly apply, but everything interesting should be visible immediately. I'll add scroll-triggered reveals, a marquee always moving, animated steam, wheels.
Let me plan sections:
1. Top bar / nav: logo mark (SVG chili + wordmark), links: Menu, Find the Truck, Story, Catering. "Order Ahead" button.
2. Hero: split — left: giant stacked headline, Korean text, CTA buttons ("See the Menu", "Find Today's Spot"), small badges ("Seoul x Mexico City"). Right: SVG truck illustration with animated steam, bouncing wheels, roof flame sign. Background: halftone dot pattern, giant outlined text behind.
3. Ticker marquee between hero and menu.
4. "The Lineup" menu grid: 6 items, each card with SVG illustration (taco, burrito, quesadilla, loaded fries, slush, side). Heat level chilis. Price. "Add +" fake cart? Could do a mini cart counter in nav — fun and functional without payment. Click "I'll take one" → increments a bag count with toast "Added to your bag — 2 items". Nice interactivity. Keep it simple: bag counter + toast.
5. Spice-o-Meter interactive: slider 0–5, shows matching recommendation with character face changing (SVG face going from chill to crying) and text. Signature moment.
6. Story section: short punchy copy, "Marinated 24 hours in gochujang...", stats counters animated on scroll (tacos served, hours marinated, napkins sacrificed).
7. Find the truck: weekly schedule with today highlighted based on Date(). Plus "right now" status dot. Stylized mini-map SVG with truck pin pulsing.
8. Testimonials: rotated sticker cards with handwritten-ish style (cursive fallback: 'Brush Script MT', cursive).
9. Big footer CTA: huge "FOLLOW THE FLAVOR" with social handles, marquee again, credits.
Colors:
- bg cream: #FFF3E2 / paper #FFF8ED
- ink: #1A1512 (near black)
- red: #E5322D or Korean red #D62828... gochujang red #E23D28
- pink accent: #FF5D8F? Maybe keep palette tight: red, cream, black, plus green (cilantro/lime) #3E9B4F and yellow/gold for cheese #FFC53D. Korean flag-ish plus lime.
Neo-brutalist: 3px black borders, hard offset shadows (box-shadow: 6px 6px 0 #1A1512), rotate stickers, big display type.
Typography: font stacks:
- Display: `font-family: 'Archivo Black','Arial Black','Avenir Next Heavy',Futura,sans-serif; font-weight:900; text-transform:uppercase; letter-spacing:-0.02em`. Arial Black widely available.
- Body: `'Helvetica Neue', Arial, sans-serif`.
- Accent Korean: use system fonts render Hangul fine (Malgun Gothic on Windows, Apple SD Gothic Neo on Mac). Fine.
Marquee: CSS keyframes translateX with duplicated content, `white-space:nowrap`.
Truck SVG: Draw a cute truck side view: box body with rounded top, serving window with awning stripes, wheels with rotating spokes animation, chimney with steam puffs animating, a roof sign "TACOS". Korean text on side "김치 로코". Little chili decal. Colors red/cream/black. Add `transform-origin` animations: wheels spin (rotate), steam puff translate+fade, truck gentle bounce.
Menu item illustrations: small SVGs — taco (semicircle shell with fillings), burrito (wrapped), fries in cup, quesadilla wedges, drink cup with straw, dumpling/mandu? Korean-Mex items:
Menu (fusion fun):
1. THE K-BBQ TACO — bulgogi, kimchi slaw, sesame-lime crema — $4.50 — heat 2
2. GOCHUJANG CHICKEN TACO — double crunch, gochujang glaze, pickled daikon — $4.50 — heat 3
3. SEOUL STACK BURRITO — galbi rice, black beans, queso riff — $11 — heat 2
4. KIMCHI QUESADILLA — mozzarella + aged kimchi, green onion crema — $9 — heat 2? 3
5. BULGOGI NACHOS / LOADED "KIMCHI FRIES" — gochujang queso, bulgogi, nori dust — $10 — heat 3
6. MANDU & DIP? Or "SIDES": kimchi fried rice? Let's do "SPICY KIMCHI SLUSH" (soju-free) or "YUZAADE" — yuza-ade drink $4 — heat 0. Good to have a cold item.
Maybe 6 items in 3x2 grid.
Each card: illustration, name, description, heat chili meter (SVG chilis filled count), price tag, button "ADD +". Click → bag count bump + toast + button micro feedback.
Spice-o-Meter: range input styled, 0–5 labels: "Mild Child", "Slightly Brave", "Gochu Gang", "Fire Noodles Fan", "Kimchi Veteran", "PAIN LOCO". Character face SVG morphs: eyes and mouth change; sweat drop at high levels, flames at 5. Recommendation text changes: e.g., at 5 → "The Reaper Riff... ask for 'Extra Loco' — signed waiver vibes." Fun copy.
Stats counters: animate when scrolled into view: 84,000+ tacos? Keep believable for a new truck? It's a new opening... but they said "opening". Copy could be "marinated for 24 hours", "3 generations of family recipes", "1 truck, zero chill". Cute. Counters: "24 HRS marinate", "72 HR kimchi ferment", "0 CHILL". Fun animated numbers.
Find the truck: Since it's a new truck, show "Grand Opening Week" schedule. Table of days with neighborhood, time, address-ish. Highlight today with `new Date().getDay()`. Pulsing "OPEN NOW-ish" if within hours? Compute local time; show status badge: if day matches and hour in range, "HURRY — TRUCK'S OUT NOW", else "NEXT STOP: ..." find next day. That's a nice live touch.
Map: stylized SVG street grid with a pulsing pin at today's location, labels like "HANA PARK", "NIGHT MARKET". Truck icon moving along a path? Could animate truck dot along the streets subtly. Keep simple: pulsing pin.
Testimonials: 3 sticker cards rotated: "I came for one taco. I left with a loyalty card I didn't know existed." — "★★★★★ — not sponsored (yet)". Fun.
Footer: giant outlined text "FOLLOW THE FLAVOR", Instagram handle @kimchiloco, phone, newsletter input (fake submit shows toast "You're on the list!"), hours note.
Extra polish:
- Custom cursor? Maybe not needed.
- Scroll reveal via IntersectionObserver adding class.
- Halftone dot background via radial-gradient pattern.
- Noise? Skip.
- Hover effects: cards lift, buttons press (translate + shadow shrink).
- Marquee pauses on hover maybe.
- Top announcement bar: "GRAND OPENING — FIRST 50 TACOS FREE*" with tiny asterisk "*with dance. we're serious." — fun.
- Accessibility: aria labels, alt-ish titles on SVGs.
Responsive: grid collapses to 1 col; hero stacks; font sizes clamp(). No horizontal scroll — watch marquee (overflow hidden) and rotated elements (keep within padding).
No console errors. Careful with IntersectionObserver availability (fine in Chrome). Avoid localStorage. Toast implementation: fixed div, add class, timeout.
Let me think about the truck SVG carefully — it's the centerpiece.
viewBox 0 0 560 380. Ground line? Truck:
- Body: rounded rect x=40 y=110 w=420 h=150 rx=18, fill red (#E5322D), stroke black 5.
- Cab on left? Side view: cab at front (left), box body behind. Actually food truck: cab + cargo box with serving window. Let's do: cab x=40..150 (windshield, hood), box from 150..470.
- Cab: path with rounded hood: rect 40,130 to 160 height 130; windshield slanted; window fill cream w/ stroke.
- Box: rect 150,100 w=320 h=160 rx=14 fill cream? Or red box with cream panel. Branding: "KIMCHI LOCO" big on box side in black on red, plus Korean below in cream.
- Serving window: rounded rect opening near rear with awning (striped scalloped awning) above, and a tiny counter. Steam from window.
- Wheels: two circles with rims; rotate spokes group via CSS animation (transform-origin per wheel). Rotation on circles won't be visible unless spokes; add spokes lines + hubcap.
- Roof: sign board "TACOS ★ TACOS" with small supports; a chimney pipe behind with animated steam puffs (three ellipses, keyframe rise & fade, staggered delays).
- Bounce: whole truck group translateY 0→4px loop.
- Speed lines / motion dashes behind to suggest driving? Maybe tiny motion lines at left when "en route". Or skip; truck is parked vibe with steam. Actually steam from window implies cooking. Add little chili hanging decal.
Text inside SVG: use <text> with font-family inherited? SVG text uses CSS fonts fine. Use Arial Black stack, uppercase. "KIMCHI LOCO" along the box side, plus 김치 로코 smaller.
Ground: ellipse shadow under truck.
Also floating sticker elements in hero around truck: "GOCHUJANG GLAZED", "24-HR MARINATED", star badge rotating slowly "GRAND OPENING • GRAND OPENING •" circular text? Circular text via SVG textPath rotating slowly — nice signature detail. Yes! A rotating badge with circular text "KIMCHI LOCO • SEOUL STREET MEETS MEXICO •" around a chili icon. Place overlapping hero corner.
Big background word: "맛있겠다" (delicious) outlined behind? Or "LOCO" giant outline text behind truck. Use -webkit-text-stroke with transparent fill. Careful cross-browser — fine in Chrome.
Spice-o-Meter face: SVG with head (circle), eyes (change with level), mouth path changes (smile → open → wavy → screaming), sweat drop at 3+, flames at 5, color of face shifts from cream → pink → red. Implement via JS updating path `d` and toggling groups. Simpler: 6 predefined mouth paths array; eyes maybe same except level 5 X eyes? Let's do:
- eyes: normal dots levels 0-2, level 3-4 dots with raised brow lines, level 5 replaced with "><" or squint arcs. Simple approach: keep dot eyes, add sweat drop (level>=2), add flame near head (level>=4), mouth path per level.
- face color: interpolate cream → #FFD9C2 → #FF9E6B → #FF5C4D? Set fill directly per level.
Face drawn as a chili character? Cute idea: the character IS a chili pepper with a face! Perfect on-brand. Chili body: curved elongated shape with stem on top. Face on chili. As heat rises, chili blushes/reddens.
Chili SVG: path like elongated teardrop curved. Stem green on top. Eyes near top, mouth center. At level 5, steam from ears, tears. That's charming and memorable.
Spice slider UI: custom range input, thumb as chili emoji? No emoji — style thumb via ::-webkit-slider-thumb with red circle + border. Ticks with labels below.
Copy for recommendations per level:
0: "Mild Child — Start with the Kimchi Quesadilla, easy on the gochujang crema. The kimchi's still friendly."
1: "Slightly Brave — The K-BBQ Taco with mild slaw. Flavor first, whisper of heat."
2: "Gochu Gang — Double crunch chicken taco, medium glaze. This is where regulars live."
3: "Fire Noodle Fan — Bulgogi Loaded Fries, extra gochujang queso. Grab extra napkins."
4: "Kimchi Veteran — Anything with 'Extra Loco' sauce. We'll nod with respect."
5: "PAIN LOCO — Tell us 'make me cry'. We have a legal-ish waiver and a slush with your name on it."
Nav bag counter: shopping bag icon with count badge; starts "0". Add item → count++, badge pop animation, toast "Added: K-BBQ Taco ×1 — see you at the truck". Also fly-to-cart animation? Maybe too much; toast + badge pop is fine. Could add small flying dot... skip for robustness. Actually a small flying element is doable: clone card price chip, animate to nav. Keep simple: skip.
Order buttons in nav: "ORDER AHEAD" scrolls to menu? Or toast "Ordering opens at grand opening — find us at the truck first!" Since no backend, clicking could open a modal with a mini "order summary" of bag? Let's implement: clicking bag/nav button shows a modal listing added items with total price and a big "SEE YOU AT THE TRUCK" button, copy "Pay at the window — we take cards, cash, and compliments". That makes the cart meaningful. Modal must not use alert; fine. Close on backdrop click and Esc (keyboard ok, it's a click-triggered context).
Keep code tidy. Let's estimate sections and write.
CSS plan:
- :root vars: --paper:#FFF4E3; --ink:#181210; --red:#E5312B; --red-dark:#B71F1B? --cream:#FFF8EC; --lime:#7BC950 (cilantro); --gold:#FFC531; --pink? maybe not.
- body background paper with subtle dot pattern: radial-gradient(#00000008 1.2px, transparent 1.2px) size 22px.
- .wrap max-width 1200 padding.
- Display class .disp { font-family: "Archivo Black","Arial Black",...; font-weight:900; letter-spacing:-.01em; text-transform:uppercase; line-height:.92 }
- Buttons: .btn { border:3px solid var(--ink); box-shadow:5px 5px 0 var(--ink); background:var(--gold); padding; transition transform .12s; } hover translate(-2,-2) shadow 7; active translate(3,3) shadow 2.
- Sticker: rotate, bg, border, shadow, radius pill.
- Section headers: small kicker sticker + big heading with highlight marker effect (span with skewed red bg behind text? Use background linear-gradient? gradients banned as decoration... a highlighter solid block via box-shadow inset or a ::after skewed rect — solid color, fine. "No decorative gradients" — I'll use solid skewed highlight blocks, no gradients. Dot pattern via radial-gradient — that's a pattern technique, not decorative gradient banner; radial-gradient dots produce flat dots. I think acceptable since it renders as flat dots, not a gradient. To be safe, dots are tiny 1.5px with hard stop — visually flat. OK.
Watch "no gradient" rule: avoid linear-gradient except hard-stop patterns (dots/halftone). I'll use hard-stop radial dots only.
Marquee: .ticker { border-top/bottom 4px solid ink; background red; color cream; } inner flex with two identical spans, animation scroll 18s linear infinite; content separated by ★ (SVG star? just use ★ char — it's a text glyph, not emoji — allowed? "No emoji as icons" — ★ is a dingbat; safer to use SVG stars inline or "•". I'll inline tiny SVG star or chili between words. Use an inline SVG star repeated — fine, or a CSS diamond via ::before. I'll use a small SVG chili/star. Simpler: use "✦"? Also dingbat. Use SVG inline for safety in marquee items: alternate words and small SVG chilis.
Marquee content: "KIMCHI LOCO" "K-BBQ TACOS" "GOCHUJANG EVERYTHING" "SEOUL ✕ MEXICO" "OPENING SOON" ... build via JS duplicating for seamless loop, or just hardcode two copies. Hardcode two copies with aria-hidden on second.
Hero layout: grid 1.05fr 1fr, min-height ~ 78vh align center. Left: kicker sticker "SEOUL ✕ MEXICO CITY, ONE TRUCK", h1: "TACOS THAT / MAKE YOU / SAY 맛있어!" Hmm English audience: Big lines: "KOREAN FLAVOR." "MEXICAN HEAT." hmm reversed: Korean heat? Copy: "BULGOGI MEETS BIRRIA." then sub: "Slow-marinated K-BBQ, funky kimchi, and tortillas pressed to order — served out of one very loud food truck." CTAs: "SEE THE MENU" (scroll) and "FIND THE TRUCK". Below: mini stats or a "Grand opening" pill with live countdown? Countdown to a fixed date — if date passed it looks broken. Use "GRAND OPENING: SAT JUNE 7" generic? Since "opening a truck" — a countdown adds urgency. Compute next Saturday? Cute: "Opens in Xd Yh" dynamic — always valid if target = next Saturday 11am. Let's do: next Saturday from now, 11:00. Label "GRAND OPENING — {date}". Countdown ticks. That's alive. But maybe too much copy; keep small pill with countdown.
Right: truck SVG in a bordered panel? Or free-floating on paper with big outline "LOCO" behind. Free floating with rotating badge overlapped at top-right, sticker "24-HR GOCHUJANG MARINADE" bottom-left rotated. Motion: parallax on mousemove for stickers (translate slight). Add that.
Menu cards: article.card { bg cream; border 3px ink; shadow; padding; radius 18px; display flex column; } top row: illustration 84px + price chip rotated. Title, desc, bottom: heat meter + add button. Hover: translateY(-6px) rotate(-0.5deg). Each card slight rotation nth-child alternate for sticker feel.
Heat meter: 5 small chili SVGs, filled = red, unfilled = gray outline. Generate via JS from data attribute.
Menu data in JS array to render? Hardcoding HTML is fine but JS render keeps file shorter... I'll hardcode HTML for SEO-ish clarity but use JS for heat meters. Eh, rendering from JS array is cleaner and less repetitive. I'll render menu from JS array into a container. Ensure first paint shows menu even before JS? JS runs immediately at end of body, fine.
But careful: "Something meaningful must be visible on first paint" — hero is static HTML, fine even if menu renders after. JS executes synchronously so it's fine anyway.
Menu illustrations: draw simple SVG per type:
- taco: half-circle shell (path arc), filling bumps (circles: red/green/brown), red salsa drizzle.
- burrito: rounded rect wrapped with diagonal line, foil? simple with steam.
- quesadilla: triangle wedges stack with cheese pull? 2 triangles.
- fries: cup with sticks.
- slush: cup with dome lid, straw, ice cubes.
- nachos: chips triangles with toppings dots.
I'll write compact inline SVGs in the data as strings.
Spice section layout: two-col: chili character SVG (big) + slider/labels/recommendation card. Background of section: red? Make section bg red with cream text for contrast punch — but avoid "colored blocks bolted on"; a full-bleed section with purpose (interactive tool) is fine. Let's make it ink (black) bg with cream/red elements — strong contrast midway. Actually keep paper bg, put the interactive inside a big bordered "machine" panel styled like a control panel: gauges, rivets (dots), "SPICE-O-MATIC 5000" label plate. Fun skeuomorphic-lite. Panel bg cream with dot rivets in corners, red slider track, big chili character in a "screen" (rounded rect with inset shadow, cream bg). Recommendation in a ticket-style strip with perforated edge (dashed border).
Stats/story section: bg red full-bleed with big copy: "HOW LOCO IS MADE" — 3 steps with numbers (01 MARINATE, 02 FERMENT, 03 PRESS, 04 SMASH)? Steps: "24 hours in gochujang", "Kimchi fermented in-house", "Tortillas pressed to order", "Queso, but make it gochujang". Steps as horizontal cards on red? On red bg, cream cards pop. Numbers big outlined.
Counters: "18,000+ tacos at our test popup" hmm new truck... use: "24 hr marinate", "90 day kimchi", "100% no chill". Animated count-up on scroll for 24 and 90 and 100 — small.
Schedule section: "CATCH THE TRUCK" — grid of 7 day cards (or table rows): day name, neighborhood, time, vibe tag. Today highlighted with red bg + "TODAY" sticker and pulsing dot. Live status line above: computed "RIGHT NOW: TRUCK'S COOKING AT {today spot} — or next stop...". Map SVG on side: simple street grid (lines), a park block, pin with pulse animation, truck emoji? SVG mini truck pin. Keep map modest.
Schedule data: Mon: OFF (밤에 쉬어요 / "Fridge restock — we're marinating"), Tue: Hana Park Lunch 11–3, Wed: Brewery "Iron Node" 4–10, Thu: Campus 11–3, Fri: Night Market 5–12, Sat: Grand Opening Plaza 11–11, Sun: Off ("eating our own research"). Highlight today via getDay.
Testimonials: heading "WORD ON THE STREET", 3-4 sticker notes rotated with marker names ("— Dana R., taco 47 owner"). Stars as SVG.
Footer: black bg, giant text "FOLLOW / THE FLAVOR", social rows: Instagram @kimchi.lc, TikTok, phone. Newsletter: input + button → toast "You're in. First ping = free topping." Then bottom bar small print: "© 2025 Kimchi Loco LLC — Najung & Familia" plus "No emojis were harmed". Marquee at very bottom again reversed direction.
Nav: sticky top with cream bg, bottom border 3px ink. Logo: chili SVG + "KIMCHI LOCO" text; right: links (hidden on small), bag button with count, hamburger? For small screens just hide links, keep bag. Simple.
Announcement bar above nav: red bg cream text small marquee? Might be too many marquees; static centered: "GRAND OPENING WEEK — FIRST 50 GUESTS GET A FREE KIMCHI QUESADILLA". Keep static, small.
Toast system: #toast container fixed bottom center; function toast(msg) creates div, animates in/out.
Modal: #orderModal overlay fixed, hidden by default; shows bag items, total; "Clear bag" + close.
Bag logic: array of {name, price, qty}. Add from menu. Modal renders list. Escape/backdrop close.
Reveal on scroll: IntersectionObserver on .reveal → adds .in (translateY 20→0, opacity). Stagger via transition-delay via inline style based on index.
Count-up: observer triggers counters animating 0→target with rAF over 1200ms.
Countdown: setInterval 1s updating to next Saturday 11:00.
Cursor-following? skip. Parallax mousemove on hero stickers: translate based on mouse relative center; use rAF throttle.
Now write code carefully, ensuring no errors. Watch out: template literals with SVG strings — fine. Avoid `</script>` sequence inside strings — SVG strings won't include that. Careful with quotes.
Fonts: define
--font-d: "Archivo Black","Arial Black","Haettenschweiler","Franklin Gothic Heavy",sans-serif? Arial Black exists on Win/Mac. Also 'Helvetica Neue' heavy weights: font-weight 900 gives "Helvetica Neue" not on Windows; Arial Black is safe. Add letter-spacing slight.
For Korean glyphs, fallback to system Korean fonts automatically.
Headline sizing: clamp(3rem, 8vw, 7rem).
Section spacing: padding: clamp(64px,9vw,120px) 0.
Let me also add a subtle "grain"? skip.
Check horizontal scrollbar: marquee inside overflow hidden; body overflow-x hidden to be safe (rotated stickers near edges). Use overflow-x:hidden on html/body? Better: keep stickers inside padded container and set body overflow-x hidden clip. Use `overflow-x: clip` fallback hidden.
Modal accessible: role dialog, aria-modal.
Let me draft the truck SVG concretely.
```html
<svg class="truck" viewBox="0 0 640 420" ...>
<ellipse cx="330" cy="360" rx="240" ry="18" fill="#00000018"/> <!-- shadow -->
<g class="truckBody"> <!-- bounce animation -->
<!-- cab -->
<path d="M60 190 ..."/>
<!-- box -->
<rect x="150" y="120" width="330" height="150" rx="16" fill="#E5312B" stroke="#181210" stroke-width="6"/>
...
</g>
<g class="wheel" style="transform-origin: 205px 320px">...</g>
</svg>
```
Wheel transform-origin with CSS: use `transform-box: fill-box; transform-origin: center;` on the wheel group — cleaner! `transform-box: fill-box` works in Chrome. So each wheel group rotates about own center.
Truck geometry plan (viewBox 0 0 640 420):
- ground shadow ellipse cy≈352.
- Box body: x=170..500, y=118..272 (w330? 170+330=500). rx 16. Fill red. Stroke ink 6.
- Underskirt: rect x=170 y=272 w=330 h=22 fill ink rx 6 (chassis skirt) — merges with body bottom.
- Cab: from x=60 to 170: path: start at (170,272) left to (110,272) arc up hood to (75,240) to windshield slant... Simplify: cab shape path:
M170 272 L110 272 Q60 272 60 222 L60 150 Q60 132 78 132 L130 132 L170 118? Hmm windshield slant: front of cab vertical-ish with slanted windshield: Let's define cab as: `M170 272 L170 118 L120 118 Q104 118 96 132 L74 168 Q60 190 60 216 L60 258 Q60 272 74 272 Z`? This is getting fiddly. Alternative: draw cab as simple rounded rect with a slanted windshield polygon:
- cab body: path `M170 262 L170 122 H92 Q78 122 72 136 L60 176 L60 250 Q60 262 72 262 Z` fill cream stroke ink.
- windshield: path `M160 132 H100 L84 176 L160 176 Z`? Slant from top-left to bottom-left matches hood slant. Eh.
Simpler approach: modern food trucks often drawn as one big box (van style) — even simpler: single box body from x=70 to 500 with a windshield at front top. Food trucks ARE boxy! Great — one big rounded rect x=70 y=110 w=430 h=160 rx=18, red. Windshield: rounded rect near front-left top: x=88 y=128 w=70 h=48 rx=10 fill #BFE3F2? Use cream with ink stroke and a wiper line. Add a hood notch: small front bumper rect. Serving window: big rounded rect x=300 y=150 w=150 h=90 rx=12 fill ink (dark opening) with cream frame; inside window: tiny shelves, a taco on counter, steam puffs above window. Awning above window: striped rect with scalloped bottom (path with arcs), stripes red/cream alternating. Chimney pipe at rear top: rect x=460 y=80 w=22 h=34 rx=6 fill ink; steam puffs from (471,70): three circles animating up.
Wheels: front cx=150 cy=286? body bottom at 110+160=270, plus skirt? Let's set body y=110..270, wheels cy=286 r=34, so wheels overlap body bottom by ~18. cx: 150 and 420. Wheels: outer circle r=34 fill ink stroke none? Tire black with cream rim: circle r34 fill ink; circle r=20 fill cream stroke ink 5; spokes: group of 3 lines rotated; hub dot r=5 ink. Rotating spokes group with CSS.
Branding on box: between windshield and window there's space x≈180..290 for big text. Put "KIMCHI LOCO" stacked? Width ~110px too narrow. Instead put text under window? Box height 160: window occupies y150..240. Space below window y 245..265 too thin. Rethink layout: window smaller: x=310 w=130 y=140 h=80. Brand text big across upper box: "KIMCHI LOCO" at x centered across whole box top area y=140? Overlaps window.
Alternative composition: text BELOW window isn't possible. Common food trucks have text big beside window. Move window to rear (right) x=360..480, windshield front-left x=88..150, middle area x=170..350 for branding: "KIMCHI LOCO" in two stacked lines at x≈260 centered, y=150 and y=195, font-size 34, Arial Black, fill cream. Korean "김치 로코" below at y=230 size 18 fill gold. Awning above window x=356..484 y=120? Awning sits at top of window y=132 with scallops.
Also small chili decal near front, and "SEOUL ✕ CDMX" small text.
Roof sign: stands from box top: two posts x=250, x=330 from y=110 to y=70, sign rect x=200 y=38 w=180 h=40 rx=8 fill gold stroke ink 5, text "TACOS" size 26 ink centered, plus small star. Slight rotate? Keep straight, animate a gentle swing? Keep static.
Steam from window: three circles at (410, 120) area above window? Window top y=140; steam puffs rise from (415,130) to y=90 fading. Class .puff with animation: translateY(-26px) opacity 0→1→0, scale. Use three with delays.
Bounce animation on whole truck group (transform-box: fill-box, translateY small). But careful: whole group includes shadow? Keep shadow outside. Wheels inside bounce group — bounce moves wheels too (fine, slight). Actually wheels should stay on ground ideally; amplitude tiny (2px), acceptable.
Add small birds? skip.
Rotating circular badge (in hero, overlapping truck panel): SVG 150px: textPath around circle "KIMCHI LOCO • GRAND OPENING • SEOUL ✕ CDMX • " with center chili. CSS animation rotate 12s linear infinite. Text on path uppercase small. Position absolute top-right of truck wrap.
Sticker "24-HR GOCHUJANG MARINADE" bottom-left rotated -8deg, bg gold pill.
Hero background giant outline word "맛있겠다!"? Use English "SO LOCO" behind truck, absolute, font huge, color transparent, -webkit-text-stroke: 2px #18121022, z-index 0. Maybe skip to reduce clutter — the dot pattern plus truck is enough. Add it faint; it's a nice depth cue. I'll include "LOCO" behind at low opacity outline. Hmm "large low-contrast gray areas on dark backgrounds" — this is light bg with faint outline; fine but keep subtle. Actually could be nice: rotate -4deg, right side behind truck.
Now the spice character chili SVG:
```svg
<svg viewBox="0 0 240 280">
<g id="char">
<!-- stem -->
<path class="stem" d="M120 46 C112 22 96 14 84 12 C98 4 122 8 132 22 C136 30 134 38 130 46 Z" fill="#4E9B47" stroke ink 5 />
<!-- body: chili curved -->
<path id="chiliBody" d="M120 40 C 190 60 210 150 150 240 C 135 258 105 258 92 238 C 40 152 66 66 120 40 Z" fill="#E5312B" stroke ink 6/>
```
Hmm design a plump chili: teardrop: wide at top, curved point at bottom-right. Path: `M118 42 C176 52 206 120 176 196 C160 236 132 252 112 246 C66 226 52 150 70 96 C84 62 98 40 118 42 Z` — I'll iterate visually mentally... It's fine to approximate; chili shapes are forgiving.
Face: eyes: two ellipses (black) at (100,120) and (140,120), white glint dots. Brows: short lines above (toggleable "worried" angle). Mouth path id="mouth" changing d. Blush: two circles fill #ffffff55 at cheeks when level>=1 (pinkish #FFB3A7?). Sweat drop: path teardrop near temple, hidden until level>=3, with drip animation. Flames at level 5: two flame paths at both sides of stem, class .flames (hidden default). Tears level>=4: two drops at eyes, animated.
Mouth d values:
0: smile small: `M104 152 Q120 162 136 152`
1: bigger smile: `M100 148 Q120 164 140 148` plus maybe open? keep line smiles with stroke.
2: open grin: filled path `M100 148 Q120 168 140 148 Q120 158 100 148 Z`? For open mouth use path fill ink: `M98 148 Q120 172 142 148 Q120 158 98 148 Z` (crescent).
3: wavy: `M100 154 Q110 146 120 154 Q130 162 140 154` (squiggle).
4: shocked O: circle — mouth as ellipse: use rect? use path circle: `M120 146 A 10 12 0 1 0 120.01 146 Z`? Simpler: <ellipse> shown/hidden, plus mouth path for others. I'll manage both: #mouthO (ellipse) and #mouthPath; JS toggles.
5: screaming big open: bigger ellipse with tongue? Use #mouthO scaled bigger (JS sets rx/ry) + tongue path red inside.
Simplify: mouth element is always <path>; for O mouths use circle-approx path `M120 150 m-12 0 a12 14 0 1 0 24 0 a12 14 0 1 0 -24 0` (circle path). Level 5 bigger: a16 18. And add tongue only at 5: small path fill #FF6B5E. OK.
Body color per level: interpolate from warm green-ish? A chili should be red... but then heat color change less visible. Make chili start PALE (underripe): level colors: ["#F4A93C"?]. Underripe chili = green → yellow → orange → red → deep red. That's intuitive!
0: #8FBF5A (green) hmm face on green with black eyes fine.
1: #E8B23A (yellow-orange)
2: #F08A24 (orange)
3: #E8541F (red-orange)
4: #E5312B (red)
5: #B31217 (deep red) + flames.
Nice visual metaphor: ripening = heat.
Also face container group could shake at level 5 (CSS class .shake animation).
Recommendation ticket text updates + item name chip. Also update the "recommended order" string maybe clickable add? Could add "ADD TO BAG" button in spice panel that adds recommended item per level mapping:
0 → Kimchi Quesadilla
1 → K-BBQ Taco
2 → Gochujang Chicken Taco
3 → Loaded Kimchi Fries
4 → Seoul Stack Burrito "Extra Loco"
5 → "Pain Loco" custom (map to bulgogi taco + extra sauce, price 4.5)
Map to menu items by id.
Slider: input type=range min 0 max 5 step 1 value 2. Styled track: height 14px, border 3px ink, bg cream, filled portion via JS setting background? Without gradient: use a fill div under a transparent track. Implementation: wrap: .track (relative) with .fill absolute width = value/5*100% bg red. Put range input transparent on top with opacity 0? Then thumb invisible... Alternatively style webkit thumb and accept unfilled track uniformly cream with tick marks below. Simpler robust: custom slider built from divs + handle with pointer events? More code. Use native range with styled thumb; track cream; ticks as separate row of labels below; no fill needed (thumb + character feedback is enough). Thumb: red circle 26px with ink border via ::-webkit-slider-thumb, track via ::-webkit-slider-runnable-track height 12 border. Also add JS to color ticks labels active.
OK. Also keyboard: slider arrow keys work natively; the page isn't keyboard-dependent so the "click to start" note isn't needed (that's for games). This is a website — no game. The requirement about demo mode applies to games; this is a marketing site with live animations, always "self-playing". Good.
Map SVG for schedule: viewBox 0 0 300 300: streets: horizontal/vertical thick cream lines on red? Design: bg cream panel with ink street lines (stroke-width 10, opacity .15?), a couple blocks (rects with labels "HANA PARK", "NIGHT MARKET", "CAMPUS"), a route dashed path animated (stroke-dashoffset) from point to point, and pin at today's location (JS positions pin by day index among preset coords). Pin: small truck marker with pulse circle. Labels font tiny. This is charming. Positioning pin: 7 coords for 7 days (Mon and Sun off → pin at "depot"). Fun: OFF days show pin at garage labeled "DEPOT (napping)".
Route path: a dashed loop connecting spots, animating dashoffset — suggests movement.
Live status text: compute now; find today's entry; if today is OFF → "Today the truck naps. Next stop: {next}." else if hour within window → "The truck is LIVE at {spot} right now — come say 안녕." else "Next stop today at {time}" etc. Since truck not actually open, generic is fine — it's a fictional demo; live logic adds believability.
Also add "OPEN" neon-ish badge in nav? compute status dot: red pulsing when "live".
Newsletter form: prevent default, validate email-ish contains @, toast success or shake input.
Footer socials: use text + small SVG icons (instagram, tiktok, phone) drawn simply.
Icons: I said icon library for small icons normally; can't load CDN libs? CDN allowed only if task explicitly allows; task didn't mention any CDN, so "no external files... except CDN URLs explicitly allowed" — none allowed, so inline SVG icons. Fine.
Let me now write all the code mentally organized, then output. Aim for a rich but manageable file (~1000-1300 lines). Let's write.
Structure:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Kimchi Loco — Korean Tacos That Slap</title>
<style> ... </style>
</head>
<body>
<div class="announce">GRAND OPENING WEEK ...</div>
<header class="nav"> logo | links | bag </header>
<main>
<section class="hero">...</section>
<div class="ticker">...</div>
<section id="menu">...</section>
<section id="spice">...</section>
<section id="story">...</section>
<section id="find">...</section>
<section id="love">testimonials</section>
</main>
<footer>...</footer>
<div id="toasts"></div>
<div id="modal" ...></div>
<script>...</script>
</body>
</html>
```
CSS details:
```css
*{margin:0;padding:0;box-sizing:border-box}
:root{
--paper:#FBEFD8?
```
Let me pick: paper #FFF3DC warm cream. Card cream #FFFBEF. ink #1B140F. red #E23325? Korean gochujang red more like #D7263D? Gochujang paste is deep red #B7352C..#C0392B. Brand red: #E0311F bright for pop on cream. gold #FFC531. lime #59A544? greens: #3E8948. sky for windshield: #BEE3EE.
--paper:#FBF0D9? maybe slightly less yellow: #FAF0DA. Let's use #F8EDD7? I'll go #FAF0DB. dots: rgba(27,20,15,.07).
Typography sizes with clamp.
Selection color: red bg cream text.
Focus styles: outline 3px dashed ink offset.
Buttons variants: .btn (gold), .btn.red (red bg cream text), .btn.ghost (cream bg).
Ticker: bg ink? or red. Use ink bg with cream text and red separators? Let's do red bg, cream text, borders ink. Ticker item font display 20px letterspacing 1px. Two spans width max-content animation.
Actually smooth infinite marquee: .tickerTrack{display:flex; gap:0; width:max-content; animation:slide 20s linear infinite} containing two identical .tickerGroup (display:flex). @keyframes slide{to{transform:translateX(-50%)}} — with two identical groups each exactly half. Good.
Wheel spoke rotation: .spin {transform-box:fill-box; transform-origin:center; animation: spin 2.2s linear infinite}.
Puff: .puff{transform-box:fill-box; transform-origin:center; animation:puff 2.4s ease-in-out infinite} keyframes {0%{transform:translateY(0) scale(.6);opacity:0} 30%{opacity:.75} 100%{transform:translateY(-34px) scale(1.25);opacity:0}} delays .p2 .p3.
Bounce: .bounce{animation: bounce 2.6s ease-in-out infinite} translateY 0→-5→0? subtle: 0/3px. Actually bounce the body group only (not wheels): y shift 0,-4. Wheels separate groups after body. But wheels visually attached to body; 4px gap acceptable? Body bottom at y=270, wheels center cy=286 r=34 top of wheel at 252 overlapping body. If body lifts 4px, wheels stay — looks like suspension bounce.
Sign swing: rotate -2deg..2deg transform-origin bottom center on sign group. Cute. Add.
Reveal: .reveal{opacity:0; transform:translateY(26px); transition: .7s cubic-bezier(.2,.7,.2,1)} .reveal.in{opacity:1;transform:none}
Stickers rotate classes: .r-2{rotate:-2deg} etc using CSS `rotate` property.
Badge (circular text) rotation: .rotor{animation: rot 14s linear infinite} keyframes rot{to{transform:rotate(360deg)}}.
Nav sticky: position sticky top 0 z 50 bg paper border-bottom 4px solid ink.
Announce bar: bg ink color cream text-align center font 12px letter-spacing 2px padding 8px; content includes small chili svg inline.
Hero grid: .hero{padding: clamp(40px,6vw,72px) 0 clamp(48px,7vw,90px)}. Inside .wrap grid-template-columns: 1.05fr .95fr gap 40 align-items center; media <900: single column, truck after text, maybe smaller.
H1 lines with spans; highlight word "LOCO" with red bg? Headline idea:
Line1: "BULGOGI," (ink)
Line2: "MEET" small?
Better:
"KOREAN BEEF."
"MEET YOUR" hmm.
Punchy:
H1: "TACOS, BUT MAKE IT SEOUL."? cute but weak.
Or big: "K-BBQ TACOS / THAT SLAP / HARDER THAN HALMONI." long.
I like: line 1 "SEOUL." line 2 "MEETS" small, line 3 "TACO."?
Final:
Line1: "KIMCHI" outlined? Wordmark already in nav. Headline: "FLAVOR / WITH NO / CHILL." — strong. With "NO CHILL" highlighted in red box or gold marker. Sub: "Slow-marinated bulgogi, funky 90-day kimchi, and tortillas pressed to order — stacked into tacos loud enough to hear from the next block. One truck. Zero chill. Grand opening week."
Kicker pill above: chili svg + "SEOUL STREET FOOD ✕ TORTILLA ENERGY".
Buttons: "SEE THE MENU ↓" no arrows-as-text? fine text arrow char "↓" is a glyph not emoji; ok, or just text. "FIND THE TRUCK".
Countdown pill: "GRAND OPENING — SAT 11AM • in 04d 07h 12m 33s".
Hero art column: .truckStage{position:relative} with big outline word behind: absolutely positioned span "맛있겠다" (means "looks delicious") rotated, outline stroke ink at 8% opacity, font display 150px. Then truck svg width 100% max 620px. Badge top -20 right -10 absolute. Sticker bottom left.
Marquee content items: KIMCHI LOCO, GOCHUJANG GLAZED, PRESSED TORTILLAS, 90-DAY KIMCHI, SEOUL ✕ CDMX, GRAND OPENING WEEK, SPICE AHEAD — separated by chili svg or star svg between.
Menu section: header row: left heading "THE LINEUP" with kicker "TACOS • BURRITOS • FRIES • SLUSH"; right note "cash, card, and compliments accepted". Grid: repeat(auto-fit,minmax(280px,1fr)) gap 22. Cards.
Card structure:
```html
<article class="card reveal">
<div class="cardArt">[svg]</div>
<div class="price">$4.50</div> (absolute top-right rotated chip)
<h3>K-BBQ TACO</h3>
<p>...</p>
<div class="cardFoot">
<div class="heat" data-heat="2" aria-label="2 of 5 chilis"></div>
<button class="add" data-id="kbbq">ADD +</button>
</div>
<span class="tag">MOST LOVED</span> optional ribbon on some cards
</article>
```
Heat chilis: JS fills .heat with 5 svg chili icons, first n solid red, rest outline gray.
Chili small svg path (simple curved chili ~24x24): path like `M12 2c5 1 9 6 9 12 0 5-4 9-9 9-... ` make simple: teardrop curved: `M5 3 Q14 2 17 8 Q21 15 15 20 Q12 22 9 20 Q2 14 5 3 Z`? Whatever, draw: stem + body:
```
<svg viewBox="0 0 24 24"><path d="M8 4 C4 6 3 11 5 15 C7 19 12 21 16 19 C19 17 19 13 17 11 C14 8 12 6 8 4 Z"/>...
```
I'll craft once and reuse string.
Simplify chili icon:
- body: `M7 5 C3.7 6.5 2.6 10.6 4.4 14.3 C6.2 18 10.7 20 14.4 18.6 C17.2 17.5 17.9 14.4 16.3 12.2 C13.8 8.7 10.9 6.3 7 5 Z` hmm this is a blob. A chili: narrow top-left stem, bulbous bottom-right curve. Path: `M9 3 C10 7 13 9 15 12 C18 16 17 20 13 21 C9 22 5 19 4 15 C3 11 5 6 9 3 Z` with a small green stem `M9 3 C8 2 7 2 6 1`? Add stem as separate tiny path `M9 4 C8 2.5 6.5 2 5 2.4 C6.8 3 8 4.4 9 4`. Honestly small icons at 18px — anything chili-ish reads. I'll define:
chiliIcon = `<svg viewBox="0 0 24 24" class="chi"><path class="stem" d="M10 4.6C9.6 3 8.4 1.8 6.8 1.6c.2 1.3 1 2.4 2.2 3z"/><path class="body" d="M10.2 4.5c3.1.9 5.6 3 7 6.2 1.5 3.5.3 7.7-3.2 9.3-3.4 1.6-7.6.3-9.3-3.2C3 13.4 4.7 8.6 8.6 6c.5-.4 1.1-.7 1.6-.9z"/></svg>` and CSS .chi .stem{fill:#3E8948}.chi .body{fill:var(--red)} and for off state .chi.off .body{fill:none;stroke:#1B140F33;stroke-width:1.6}. body path: starts near stem (10,4.5) goes right-down... My path: starts M10.2 4.5 then curve to bottom-right area, loops bottom, back up left. Roughly chili-like tilted. Good enough.
Ribbon "MOST LOVED": absolute -top rotate, bg red cream text small pill.
Card art svg examples (60-84px). Let me design each quickly:
taco:
```svg
<svg viewBox="0 0 120 100">
<!-- shell -->
<path d="M10 62 A50 50 0 0 0 110 62 L110 62 Q60 74 10 62Z"??
```
Half-disc shell: `M10 60 A50 44 0 1 0 110 60 Z` gives lower half circle? Arc from (10,60) to (110,60) with large-arc sweep... For lower half: sweep-flag 0 goes counterclockwise = lower bulge: `M10 60 A50 50 0 0 0 110 60 Z`? Let's think: from (10,60) to (110,60), rx=ry=50, distance 100 = diameter, so semicircle either up or down depending flags: sweep 0 → arc drawn counterclockwise → below. Yes `M10 60 A50 50 0 0 0 110 60 Z` is a lower semicircle. Fill gold (#F2A93B shell) stroke ink 4.
Fillings on top edge: circles: red blob cx30 cy52 r8, green cx48 cy46 r7, cream cx66 cy50 r7, brown cx88 cy54 r8; plus lettuce wavy path? Keep circles + a drizzle path `M20 50 Q40 42 60 50 T100 48` stroke red 3 dashed? Keep circles with ink strokes; add small steam? fine.
burrito:
```
<rect x="20" y="30" width="80" height="46" rx="23" fill="#F2E4C8" stroke ink 4/> wrap line diagonal: path M30 42 L92 62 stroke...
```
Add foil end: small rect at right rotated. Add folded lines: two arcs. Plus filling peeking at left end: circle red/green at x22. Cute: ellipse ends visible: left end shows cross-section: circle cx=22 cy=53 r=20 fill cream stroke, inner circles fillings (r small red, gold, green, cream). Body extends right rounded rect. Steam two curves above.
quesadilla: two triangles: `M20 70 L60 26 L64 70 Z` rotated squares; second offset; fill gold stroke ink; cheese pull: path melted `M60 40 q10 6 4 14` small red dot filling; sprinkle dots green/red.
fries: cup: trapezoid path `M32 40 L28 78 L92 78 L88 40 Z` fill red stroke ink; fries: rects rotated slightly above cup: 4 rects fill gold stroke ink 3 (x 40..80, y 14..42). red stripe label rect on cup with "K"? small star.
slush: cup trapezoid `M38 34 L34 80 L86 80 L82 34 Z` fill cream stroke ink; dome: half circle top `A` fill red; straw rect rotated; ice cubes (small rounded rects cream stroke) inside; boba? make it "YUZAADE" yellow: dome fill gold, liquid gold. Label "YUZA".
nachos: 3 triangles fanned bottom-up over a plate ellipse: plate ellipse cx60 cy78 rx42 ry10 fill cream stroke ink; triangles gold; toppings: red circles, green bits, white crema drizzle path.
These strings go in the JS data.
Menu data array:
```js
const MENU=[
{id:'kbbq', name:'THE K-BBQ TACO', korean:'불고기 타코', desc:'24-hour gochujang bulgogi, kimchi-lime slaw, sesame crema, scallion snow.', price:4.5, heat:2, art:ART.taco, tag:'MOST LOVED'},
{id:'chicken', name:'GOCHUJANG CRUNCH TACO', korean:'고추장 타코', desc:'Double-fried chicken, gochujang glaze, pickled daikon, cooling cucumber ribbons.', price:4.5, heat:3, tag:''},
{id:'burrito', name:'SEOUL STACK BURRITO', korean:'서울 스택', desc:'Galbi, kimchi fried rice, black beans, queso-but-gochujang. Two hands mandatory.', price:11, heat:2, big:true},
{id:'queso', name:'KIMCHI QUESADILLA', korean:'김치 케사디아', desc:'Aged kimchi + stretchy mozzarella, green-onion crema, toasted till it hisses.', price:9, heat:2},
{id:'fries', name:'LOADED KIMCHI FRIES', korean:'김치 감자', desc:'Crinkle fries buried under bulgogi, gochujang queso, nori dust, jalapeño confetti.', price:10, heat:3},
{id:'slush', name:'YUZU MELT SLUSH', korean:'유자 슬러시', desc:'The fire extinguisher. Yuzu, plum, ice, salvation. 0 heat, 100 rescue.', price:4, heat:0, tag:'FIRE EXTINGUISHER'},
];
```
Render into grid. Also each card gets slight rotation class based on index.
Story section (bg red): heading "HOW WE GET LOCO" cream text. 4 steps as cards on red? Cards cream with ink borders: step number big outlined red on cream, title, line. Steps:
01 MARINATE — "Beef bathes in gochujang, pear & garlic for 24 hours. Patience you can taste."
02 FERMENT — "Our kimchi sits 90 days until it's funk-forward. Halmoni approved. She checks."
03 PRESS — "Tortillas pressed to order, 30 seconds of pure drama."
04 SMASH — "Seared hard, sauced louder, wrapped with love and a little regret."
Layout: 4 columns responsive; big numbers "01" outlined (stroke via text-shadow trick or -webkit-text-stroke on transparent fill — allowed, it's text styling not gradient).
Add counters row inside red: three big numbers: 24 (HRS MARINATING), 90 (DAYS FERMENTING), 0 (CHILL GIVEN). Count-up on view. Style: display numbers huge cream, labels small.
Actually steps could just be the 4 cards with numbers; counters separate strip above steps. Good.
Find section: heading "CATCH THE TRUCK", status pill live, two-col: schedule list left, map right. Schedule rows: day pill + spot + time; today row red bg + "TODAY" sticker + pulse dot. Off rows dashed style "OFF — marinating".
Testimonials: heading "PEOPLE ARE YELLING ABOUT US"; 3 cards rotated with stars svg row, quote, name. Quotes:
- "I ordered one taco 'to try it.' Four tacos later I applied for their loyalty card." — Dana R., opening week survivor
- "The fries have nori dust. NORI. DUST. I think about them at work." — Marcus T., frequenter of the Thursday spot
- "My abuela nodded once. She never nods." — Sofia L., taco 214 consumed
- "I cried a little. 5 stars for flavor, 1 star for my ego. Averaged out: 5." — Jae P., spice level 5 certified
Star icon svg: 5 filled gold stars small.
Footer (ink bg, cream text): marquee reversed at top? Put ticker with reverse direction before footer content: "SEE YOU AT THE WINDOW ★". Footer content: giant "FOLLOW / THE FLAVOR" display huge with red "FLAVOR" block? Newsletter row: label "GET THE DAILY LOCATION PING", input + button "PING ME". Social chips: INSTAGRAM @KIMCHI.LOCO, TIKTOK @KIMCHILOCO, CALL/TEXT (555) 0110-LOCO. Fine print: "© 2025 Kimchi Loco — One truck, full heart. Allergy note: everything touches sesame, joy, and possibly your soul." + "Site made with gochujang." Bottom small: "김치 로코 — 맛있겠다!"
Modal: overlay; panel cream border 4 ink shadow; title "YOUR BAG"; list items rows name×qty price; total; note "Pay at the window — cash, card, or an extremely good compliment."; buttons: "CLEAR" ghost, "SEE YOU AT THE TRUCK" red → toast "Locking it in. See you at the window!" close modal. Empty state: "Bag's empty. The menu is right there ↑" with arrow char.
Toast: bottom-left or bottom-center stacked; style: ink bg cream text border 3 red? gold left bar; slide up fade.
Now the JS parts:
1. Menu render + heat meters.
2. Bag add/qty/render/open/close/clear + toast + badge pop.
3. Spice slider logic.
4. Schedule + today highlight + status + map pin.
5. Countdown.
6. IntersectionObserver reveals + counters.
7. Parallax hero stickers on mousemove (desktop only, pointer:fine check).
8. Newsletter submit.
9. Ticker duplication maybe static HTML.
Edge: getDay(): 0 Sun..6 Sat. Schedule array indexed 0..6.
Let me define schedule:
- 0 Sun: OFF "Depot nap — quality control on our own couch"
- 1 Mon: OFF "Marinating. It's a whole thing."
- 2 Tue: "Hana Park South Gate", "11:00–15:00", "Lunch rush"
- 3 Wed: "Iron Node Brewing patio", "16:00–21:00", "Bingo + birria vibes"? keep "Beer garden"
- 4 Thu: "Campus Commons, KSU", "11:00–15:00", "Student fuel"
- 5 Fri: "Riverside Night Market", "17:00–23:00", "Neon + lineups"
- 6 Sat: "Grand Opening Plaza — Main & 3rd", "11:00–23:00", "THE BIG DAY"
Map coords for pin per day: depot (40,250), park (230,70), brewery (90,120), campus (250,190), market (60,60)? Provide 7 coords. Map 300x300 viewBox with streets: lines at x=50,150,250 vertical? Let's: horizontal streets y=60,150,240; vertical x=50,140,230; blocks colored subtly; labels tiny: "HANA PARK" near (230,40), "NIGHT MKT" (40,40), "CAMPUS" (250,205), "DEPOT" (40,270), "BREWERY" (90,100). Route dashed path connecting spots: `M40 250 L140 250 L140 150 L230 150 L230 70 M40 60 ...` maybe two segments; animate dashoffset on a path connecting all: `M40 250 L40 60 L230 60 L230 150 L140 150 L140 240` whatever, decorative.
Pin: group with circle pulse (r 10 animating via CSS scale) + truck marker (tiny rect red) + label "TODAY". JS sets transform translate to coords and label text.
Status logic:
```js
const now=new Date(); const d=now.getDay();
today = SCHED[d];
if off → "TODAY: truck's recharging. Next stop: {next spot} — {dayname}."
else parse hours from "11:00–15:00": compute open/close hour; if hour within → "LIVE NOW at {spot}. Window's open, tortillas hot." pulse green? red.
else if before open → "Today: {spot} from {time}. Set an alarm."
else → "Today's shift wrapped. Next stop: {nextday} {spot}."
```
Countdown: next Saturday 11:00:
```js
function nextSat(){const n=new Date();const r=new Date(n); r.setDate(n.getDate()+((6-n.getDay())%7||7)); r.setHours(11,0,0,0); return r;}
```
If today Saturday before 11am, ((6-6)%7||7)=7 → next week; edge acceptable but better handle: if today Sat and hour<11 → today. Minor; add check.
Update per second: days/hours/mins/secs two-digit.
Counters: data-target attr, observer, animate.
Parallax: hero stage listens mousemove → move .float elements by (dx*depth). Use CSS var --mx.
Reveal stagger: assign transition-delay inline in JS by index within section? Simply set style.transitionDelay = (i%6)*60ms at observe time via data-d attr. I'll add data-d attributes manually per element or compute in JS: elements with .reveal get delay = (index % 4)*80.
Toast implementation:
```js
function toast(msg, sub){...create div.toast, innerHTML strong + span, append, rAF add .show, setTimeout remove 3200}
```
Bag:
```js
let bag={}; // id → qty
function addToBag(id, qty=1){...update badge, toast(`Added ${name}`), pop}
```
Bag button opens modal. Modal list renders.
Escape key closes modal. Backdrop click closes.
Spice logic:
```js
const LEVELS=[
{label:'MILD CHILD', color:'#8FBF5A', mouth:'M104 150 Q120 160 136 150', line:'...'},
...
];
```
mouth paths for character (face coords). Character SVG plan (viewBox 0 0 260 300):
- stem top center.
- chili body path.
- eyes: two groups: pupil circles + brows (brows as paths I can rotate? simpler: two brow rects I tilt via transform set per level? skip brows; add sweat/tears/flames toggles).
- mouth path element updated d.
- O-mouth levels: mouth path set to circle path string.
- sweat drop: path teardrop at (196,120) class toggled.
- tears: two small paths under eyes class .tears shown level>=4.
- flames group near stem sides level 5.
- shake class on level 5.
Level config:
0: color '#79B85B'? underripe green #86BE5A; mouth small smile `M106 152 Q122 160 138 152`; rec: queso.
1: '#E9B83E'; smile bigger `M102 150 Q122 166 142 150`; rec kbbq.
2: '#F1912B'; open grin path (crescent fill): `M104 148 Q122 172 140 148 Q122 160 104 148 Z` — filled ink; rec chicken.
3: '#EE5A23'; O mouth small: circle path r10: `M122 152 m-10 0 a10 12 0 1 0 20 0 a10 12 0 1 0 -20 0`; sweat on; rec fries.
4: '#E5312B'; O mouth r13 + tears; rec burrito extra loco.
5: '#B31217'; O mouth r16 + tongue + flames + shake; rec pain loco.
mouth element: `<path id="mouth" fill="#1B140F">` — for smile lines fill=none stroke ink width 6 round caps; for crescent/O fill ink stroke none. Toggle fill/stroke via JS attrs.
Tongue: `<path id="tongue" d="M112 158 a10 8 0 0 0 20 0 z" fill="#FF7A6B">` shown at 5 only.
Rec card: shows LEVEL name big, item recommendation, description line, "ADD TO BAG — $x" button updates label. Ticks labels 0..5 below slider: ["MILD","WARM","GOCHU","FIRE NOODLE","VETERAN","PAIN"] highlight current.
Also numeric readout "HEAT INDEX: 3/5".
Panel decor: rivet dots at corners (absolutely positioned circles), a top plate label "SPICE-O-MATIC v2.0" with screws. Screen area bg cream border 4 ink inner.
Alright, also the "signature" hover: menu cards tilt with pointer? Keep translate lift.
Small print in menu: " Heat rated in halmoni units. Your mileage may vary. Definitely cry at 5."
Let me write the chili character body path concretely. Chili: plump top (shoulders near y=70) tapering to curved tip bottom-left? Classic chili curves like a comma. Coordinates (viewBox 260x300):
Body outline:
```
M132 40
C 186 46, 218 96, 210 150
C 203 205, 168 258, 118 282
C 108 287, 96 284, 92 274
C 66 214, 58 148, 74 100
C 88 60, 108 38, 132 40
Z
```
Check: starts top (132,40), bulges right to (210,150), curves down to bottom-left tip (118,282), small tip curve, left side back up (74,100) to top. Plausible chili leaning right with tip at bottom. Face placement: center of face around (130,140). Eyes at (108,128) and (152,128) r=9 ink; glints white r=3 at (-3,-3). Cheeks blush circles at (96,152) & (164,152) r=10 fill #ffffff40 shown >=1? make cheeks always pinkish #FF9D8F66. Brows: paths above eyes `M96 108 q12 -8 24 -2` etc; default hidden? Keep simple: brows shown always, but rotate for level>=3? Skip brows entirely. Simpler.
Stem: at top: `M120 44 C116 26 104 16 88 12 C106 6 126 12 134 26 C137 33 135 40 132 46 Z` fill green stroke ink 5. Plus a little leaf? skip.
Sweat drop: `M186 118 c6 10 10 16 0 22 c-10 -6 -6 -12 0 -22 z`? teardrop: `M188 116 C 196 130, 196 138, 188 142 C 180 138, 180 130, 188 116 Z` fill #7FC7EA stroke ink 3. Position near right temple (right side of face ~ x185 y125). Show with class + drip animation translateY.
Tears at eyes: two small ellipses below eyes (112,146) (152,146) fill sky.
Flames: left of stem at (70,52) and right (170,48): flame path `M0 20 C -8 8 -2 -6 6 -18 C 8 -6 16 -2 14 8 C 22 4 24 12 20 20 Z` fill gold stroke ink 3, with inner flame red smaller. Class .flameL/.flameR hidden default, flicker animation scale.
Shake: keyframes translate/rotate jitter .3s infinite.
OK time to write final code. Also include `<meta name="description">`.
One more consideration: the announcement bar + sticky nav: keep announce static (not sticky) so nav sticky below it — position sticky on nav works after announce scrolls away. Fine.
Also add smooth scroll: html{scroll-behavior:smooth} and scroll-padding-top: 90px.
Nav links: MENU, SPICE-O-MATIC, STORY, FIND US. Hide under 800px.
Let me make sure no horizontal overflow: hero outline word absolute right maybe overflow — set hero overflow hidden. Body overflow-x hidden too.
Write CSS carefully. Then HTML. Then JS.
Approximate final size ~ 45-55KB. Good.
Let me write it.
Details for heat meter aria: role img aria-label.
Ticker HTML: I'll build items in HTML twice (group aria-hidden second). Each item: `<span>KIMCHI LOCO</span> <svg chili> ...`. To keep concise, define chili svg inline many times — it's small; fine.
Alternatively build ticker via JS from array to avoid duplication in source: document ready fills .tickerTrack with 2× items. Yes — do it in JS: `function fillTicker(el, items){el.innerHTML = ...}` called for both tickers (second reversed via CSS animation-direction reverse). Good, saves bytes.
Countdown display element in hero pill: `<span id="cd">—</span>`.
Also "OPEN NOW" nav dot? skip; status in schedule section is enough. Maybe add tiny live dot in nav next to "FIND US": pulse if live. Nice: `<span class="dot" id="navDot"></span>`. Compute once.
Order of JS functions then init.
For menu card slight rotations: .card:nth-child(odd){rotate:.6deg} even -.6deg; hover rotate 0.
Fonts for Korean display: Arial Black won't have Hangul → falls back to system sans for Hangul glyphs — fine, bold.
The "맛있겠다!" outline word: use -webkit-text-stroke: 2px rgba(27,20,15,.18); color transparent. Add standard `text-stroke` not standard; Chrome fine.
Buttons focus-visible outline.
Toast container: fixed bottom 24 left 50% translateX(-50%)? Use left:24 bottom:24 stack column gap 10; z 200.
Modal: .overlay fixed inset 0 bg rgba(27,20,15,.55) display flex center; hidden via .open class toggling visibility+opacity; panel maxwidth 480 width 92%.
Write the JS add-to-bag pop animation: .badge.pop {animation:pop .35s} re-trigger by removing/adding class with void offsetWidth.
Amount formatting: price.toFixed(2).
Also "ADD +" buttons: after click, briefly text "GOT IT ✓"? Check glyph — use "ADDED!" then revert 900ms. Fun feedback.
Now, schedule "OFF" days show dashes and note. Map pin coords for off days → depot. Label text under status.
Time parse: store open/close as numbers in data: {open:11, close:15} hours in 24h; off: null.
Status logic detail:
```js
const SCHED=[
{d:'SUN', spot:'OFF — depot nap', off:true, note:'Recharging. Quality control on the couch.', pin:[150,255]},
{d:'MON', off:true, spot:'OFF — marinating', note:'The beef needs its 24 hours. Respect the process.', pin:[150,255]},
{d:'TUE', spot:'Hana Park — South Gate', time:'11:00–15:00', open:11, close:15, tag:'Lunch rush', pin:[235,60]},
{d:'WED', spot:'Iron Node Brewing', time:'16:00–21:00', open:16, close:21, tag:'Beer garden', pin:[60,150]},
{d:'THU', spot:'KSU Campus Commons', time:'11:00–15:00', open:11, close:15, tag:'Student fuel', pin:[240,205]},
{d:'FRI', spot:'Riverside Night Market', time:'17:00–23:00', open:17, close:23, tag:'Neon night', pin:[60,60]},
{d:'SAT', spot:'Main & 3rd — Grand Plaza', time:'11:00–23:00', open:11, close:23, tag:'THE BIG DAY', pin:[150,150]},
];
```
Status text per logic above.
Map svg static with blocks; pin group id="pin" transform set via JS: `translate(x,y)`; label id="pinLabel" text under. Pulse circle CSS animation.
Counters: `<span class="num" data-n="24">0</span>` etc. Story strip: "24" HRS MARINATING / "90" DAY KIMCHI / "30" SEC PRESS-TO-ORDER / "0" CHILL GIVEN. Animate on view.
Alright — also ensure `prefers-reduced-motion` reduce animations? Add media query to disable animations — good practice, cheap:
```css
@media (prefers-reduced-motion: reduce){*{animation-duration:.01ms!important; animation-iteration-count:1!important; transition-duration:.01ms!important}}
```
Let me now write the whole file. I'll be careful with SVG path syntax and JS strings (use template literals with double quotes inside).
One tricky thing: in JS template literals building HTML with SVG containing `${}`? I'll use plain concatenation carefully.
Write CSS:
```css
:root{
--paper:#F9EFDB;
--cream:#FFFAEC;
--ink:#1C130E;
--red:#E0311F;
--red-deep:#B31217;
--gold:#FFC531;
--green:#3F9B4F;
--sky:#7FC7EA;
--line:3px solid var(--ink);
}
```
paper #F9EFDB is nice warm.
Display font stack: `font-family:"Archivo Black","Arial Black","Arial Bold",Arial,sans-serif` — note Arial Black ignores font-weight? Setting font-weight:900 fine.
Let me define classes: .d (display). body base font: 16/1.55 stack `"Helvetica Neue", "Segoe UI", Arial, sans-serif`, color ink.
I'll now produce the final answer code. I'll write ~ everything with care and double-check the JS for errors.
Key JS pitfalls to check:
- querySelectorAll on .heat before render — heat elements exist after menu render; do render first then wire.
- Event delegation for add buttons: container listener reading e.target.closest('.add').
- Slider input event.
- Modal render function.
- Countdown setInterval.
- IntersectionObserver.
- Ticker fill.
- Pin transform.
- Newsletter.
Also ensure IDs unique.
Hero countdown label: "GRAND OPENING — SAT 11AM • " + time parts. If countdown passed (shouldn't), recompute next.
Let me write mouth path strings and face properly.
Face group elements:
```html
<svg id="chiliChar" viewBox="0 0 260 310">
<path id="cBody" d="..." fill="#F1912B" stroke="#1C130E" stroke-width="7"/>
<path d="stem" .../>
<g id="face">
<ellipse class="cheek" cx="94" cy="150" r="11"/>
<ellipse class="cheek" cx="166" cy="150" r="11"/>
<circle class="eye" cx="112" cy="126" r="9"/><circle class="eye" cx="148" cy="126" r="9"/>
<circle class="glint" cx="115" cy="123" r="3"/><circle class="glint" cx="151" cy="123" r="3"/>
<path id="mouth" .../>
<path id="tongue" .../>
<g id="tears"><ellipse .../><ellipse .../></g>
</g>
<path id="sweat" .../>
<g id="flames">...</g>
</svg>
```
Order: draw body then stem then face. Sweat above face. Flames behind body? Flames near stem top on background: place before body group so body overlaps their base — draw flames first then body. But flames should be visible at sides of stem; fine either way. Put flames group before body.
Mouth default level 2 config.
Stroke widths: body stroke 7.
JS setLevel(v):
```js
const L=LEVELS[v];
cBody.setAttribute('fill', L.color);
mouth.setAttribute('d', L.mouth);
mouth.setAttribute('fill', L.fill ? '#1C130E' : 'none');
mouth.setAttribute('stroke', '#1C130E');
mouth.setAttribute('stroke-width', L.fill? '0' : '7');
tongue.style.display = v===5 ? '' : 'none';
sweat.style.display = v>=3 ? '' : 'none';
tears.style.display = v>=4 ? '' : 'none';
flames.style.display = v===5 ? '' : 'none';
charSvg.classList.toggle('shaking', v===5);
recTitle.textContent = L.name; recItem.textContent = L.item; recText.textContent = L.text;
addBtn.dataset.price = L.price; addBtn.dataset.name = L.item;
heatIndex.textContent = v + '/5';
ticks highlight
```
Level texts (fun copy):
0 MILD CHILD: "Zero-heat entry. The Kimchi Quesadilla, easy on the funk — friendly, warm, a gateway taco." item 'KIMCHI QUESADILLA' price 9.
1 WARM & CURIOUS: "K-BBQ Taco with the mellow slaw. Flavor shows up first; heat just waves from the door." item 'THE K-BBQ TACO' 4.5.
2 GOCHU GANG: "Double-crunch gochujang chicken. The regulars' frequency. Napkins nearby, pride intact." 'GOCHUJANG CRUNCH TACO' 4.5.
3 FIRE-NOODLE CERTIFIED: "Loaded Kimchi Fries, extra queso. You've eaten fire noodles on purpose. You'll be fine. Probably." 'LOADED KIMCHI FRIES' 10.
4 KIMCHI VETERAN: "Seoul Stack Burrito, say 'extra loco' at the window. We nod with deep, sincere respect." 'SEOUL STACK — EXTRA LOCO' 11.
5 PAIN LOCO: "Tell us 'make me cry.' We whisper a prayer, sauce accordingly, and hand you a Yuzu Melt for after." 'THE PAIN LOCO (chef's dare)' 4.5.
Add button in spice panel: "ADD THE PICK — $4.50" updates.
Slider ticks: labels under: MILD, WARM, GOCHU, FIRE, VETERAN, PAIN.
Panel header: sticker "SPICE-O-MATIC™" + "OFFICIAL HEAT NEGOTIATOR".
Menu heat chips also get title tooltip text.
Now compose the truck SVG full markup (careful):
```html
<svg class="truck" viewBox="0 0 640 430" role="img" aria-label="The Kimchi Loco food truck">
<ellipse cx="330" cy="366" rx="250" ry="20" fill="rgba(28,19,14,.14)"/>
<!-- steam chimney -->
<g class="puffset">
<circle class="puff p1" cx="470" cy="74" r="10" fill="var(--cream)" stroke="var(--ink)" stroke-width="4"/>
<circle class="puff p2" cx="486" cy="80" r="8" .../>
<circle class="puff p3" cx="462" cy="82" r="7" .../>
</g>
```
Wait puffs animate transform translate; with transform-box fill-box, translate fine. But they need individual delays.
Chimney at rear: rear is right side (window at rear)? Food truck serving window is on the side, but for illustration the window on the right part of body. Chimney also top rear: pipe at x=430? Let's place chimney near front? Typically exhaust at rear. Put pipe at x=88 (front top, above cab area) so steam doesn't cover window. Pipe: rect x=76 y=64 w=26 h=40 rx=8 fill ink; cap rect x=70 y=58 w=38 h=12 rx=6 fill red stroke ink 4. Steam from (89,52) upward. But roof sign is centered x~320. Fine.
Body big rect: x=64 y=112 w=470 h=170 rx=20 fill red stroke ink 6.
Front bumper: rect x=56 y=252 w=30 h=26 rx=8 fill ink? Place small: x=56,y=250,w=22,h=30 rx=8 fill ink. And rear bumper x=520 similar. Headlight: circle at (70,240) r=8 gold stroke ink 4.
Windshield: rounded rect x=92 y=132 w=88 h=54 rx=12 fill var(--sky)? sky blue glass with cream frame: stroke ink 5, plus shine line path. Door line: vertical line x=190 from y=120 to 280? Door handle small circle. Draw door seam: path M196 118 V278 stroke ink 5 opacity .5? Actually a thin ink line ok.
Serving window: opening rect x=300 y=150 w=160 h=92 rx=14 fill ink (dark interior). Inside: counter shelf line cream at y=228; a small taco on counter: mini arc gold at (330,222) with red dot; and a hanging sign inside? Add "OPEN" neon: text cream small at (380,180)? Use text "OPEN" fill gold Arial 16 letter-spacing 4 — like neon. Also a little chef hat peeking? too much.
Awning over window: at y=118..150: striped: path with scallops: base rect x=292 y=118 w=176 h=26 fill cream stroke ink 5; scallop bottom: series of semicircle notches: path `M292 144 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 ...` — arcs of r=11, 8 scallops for 176 width (176/22=8). And stripes: red rects vertical inside clipped... simpler: draw awning as group of alternating stripes: for i in 0..7 rect x=292+i*22 w=22 h=26 fill i%2? red:cream, stroke none; then overlay outline rect stroke ink; then scallop path stroke ink fill none across bottom. Hardcode 8 rects in HTML — fine, verbose but okay. Or use a pattern? Just hardcode.
Scallop arcs: starting M292 144, `a11 11 0 0 1 22 0` repeated 8 times draws humps upward? Arc a rx ry rot large-arc sweep dx dy: from current point, sweep=1 draws clockwise; with dy=0, dx=22, that bulges downward (since going left→right clockwise bulges up? For arc from (0,0) to (22,0) with sweep 1: center below? Let's reason: clockwise sweep from start to end... For a semicircle above the line (bulge up, like awning scallops pointing down we want bulges pointing DOWN for scallop bottom edge — awning scallops hang down). Standard awning: bottom edge has half-circle scallops hanging downward. Bulge down = for left-to-right path, arc passes below the chord: that's sweep=1 (clockwise: starting (0,0)→(22,0) clockwise passes through (11,11) below). Hmm clockwise from (0,0) to (22,0): going clockwise means passing through negative y first? Visualize circle center (11,0), radius 11. Start at (0,0) angle 180°, end (22,0) angle 0°. Clockwise from 180°→0° passes angle 90° which is (11,11) in SVG coords (y down = below line). Yes sweep=1 bulges down.
So scallop path: `M292 132 v14 a11 11 0 0 1 22 0 a11 11 0 0 1 22 0 ×8` then `v-14 z`? I'll just draw stroke-only scallop line under the awning rect: path d="M292 142" + 8 arcs + fill none stroke ink 4. And awning stripes drawn as 8 rects x from 292 step 22 h=26 y=116 fill alternating, wrapped in group with stroke ink 5 rect outline on top. To make stripes not cover outline: draw stripes first, then outline rect (fill none), then scallop stroke, then also maybe scallop filled alternating? Overkill; stroke scallops suffice.
Brand text on body: between door (x196) and window (x300): width ~100: vertical stack: "KIMCHI" y=170? and "LOCO" y=205, font display 30 fill cream stroke? add slight rotate. Then Korean 김치 로코 y=232 fill gold 16. Hmm text width "KIMCHI" at 30px Arial Black ≈ 6 chars*~22 = 132 > 100. Reduce to 22px → ~95. Or rotate text block -90 (vertical) along side? Many trucks have vertical text. Simplest: put "KIMCHI LOCO" under the window? No space.
Alternative: move window smaller (x=320 w=140) and put stacked text centered x=260 size 26: "KIMCHI" (Arial Black 26 ≈ 115px wide) from 202..318 — overlaps window at 320. OK tight but fits with x=260 center. Let me set: text x=258 text-anchor middle: "KIMCHI" y=168 size 25, "LOCO" y=200 size 25, "김치 로코" y=232 size 15 gold. Space from door seam x=196 to window x=316: center ~256, half-width 57. "KIMCHI" at 25px Arial Black: approx per char 18px*6=108 → half 54. Fits.
Roof sign: posts: rects x=270 y=76 w=10 h=40 fill ink; x=330 same. Sign rect x=210 y=30 w=190 h=48 rx=10 fill gold stroke ink 6; text "TACOS" x=305 y=63 anchor middle size 26 ink display letter-spacing 2. Add small star svg? Add red small chili on sign left: circle? Add two mini stars (paths) at sign ends. Sign group class .swing (transform-origin center bottom).
Wheels: cx=170 and cx=450, cy=302, r=36. Body bottom at 282; wheels top at 266 — good overlap.
Wheel markup:
```html
<g class="wheel"><circle cx="170" cy="302" r="36" fill="#1C130E"/><circle cx="170" cy="302" r="22" fill="var(--cream)" stroke="#1C130E" stroke-width="5"/><g class="spin"><path d="M170 286 V318 M154 302 H186 M159 291 l22 22 M181 291 l-22 22" stroke="#1C130E" stroke-width="6" stroke-linecap="round"/></g><circle cx="170" cy="302" r="7" fill="var(--red)"/></g>
```
Hmm spokes cross full rim: lines through center at 0/45/90/135 degrees within r22: endpoints ±20. Use path with lines: vertical M170 282 V322? that's r=20: 302±20 → 282..322. horizontal M150 302 H190. diagonals: 20/√2≈14: M156 288 L184 316 M184 288 L156 316. Good.
.spin animation with transform-box:fill-box origin center.
Body bounce group: contains body, window, awning, texts, sign, chimney, bumpers — everything except wheels and ground shadow and puffs? Puffs should stay in place while body bounces? If chimney bounces, puffs should bounce too... put puffs inside bounce group — their own animation combines (nested transforms on different elements: bounce on group, puff on child — transforms compose fine since separate elements). Yes nested OK.
But wheels: inside bounce group would lift off ground; keep wheels outside bounce group, after it (drawn on top). Bounce amplitude 4px slow — the body gap barely visible since wheels overlap body by 16px. Fine.
Also motion: slight rotate? no.
Window steam: puffs also above window (steam from cooking): add 2 puffs at window top (330,140)? window top y=150, awning above... puffs would overlap awning; skip — chimney puffs enough.
Add small dangling cardboard sign on bumper? skip.
Under truck maybe a red chili mascot sitting? skip — chili appears in spice section.
Big outline word behind truck: `<span class="bigword" aria-hidden="true">맛있겠다</span>` positioned absolute right 0 top 0 rotate -6deg font 140px stroke.
Hero badge circular:
```html
<svg class="stampRotor" viewBox="0 0 160 160">
<defs><path id="circlePath" d="M80 80 m-58 0 a58 58 0 1 1 116 0 a58 58 0 1 1 -116 0"/></defs>
<circle cx="80" cy="80" r="76" fill="var(--gold)" stroke="var(--ink)" stroke-width="5"/>
<g class="rotor">
<text class="circText"><textPath href="#circlePath" startOffset="0">KIMCHI LOCO • SEOUL ✕ CDMX • GRAND OPENING • </textPath></text>
</g>
<chili icon center: use small chili paths scaled>
</svg>
```
textPath length: circumference 2π58≈364. Text "KIMCHI LOCO • SEOUL ✕ CDMX • GRAND OPENING •" at 13px letter-spacing 1.5 — approx 44 chars*7.5≈330 fits. Use `✕` glyph ok? It's a multiplication x (U+2715). Should render. Alternatively "x". Use "×" (multiplication sign, safe).
Rotating: apply CSS animation to the <text> group: transform-origin 80px 80px; in SVG CSS transform-origin uses user units if transform-box view-box? Set style: .rotor{transform-box:fill-box; transform-origin:center;} — fill-box of text group is its bbox which is the circle → center = circle center. Rotation about center works.
But careful: rotating a text on circular path looks like text orbiting; the classic effect rotates whole badge text ring. Fine.
Center of badge: small chili: reuse chili paths translated/scaled: `<g transform="translate(80 84) scale(3.2) translate(-12 -13)">chiliPaths</g>`. I'll inline the same two paths.
Stamp positioned: absolute; top:-28px; right:8px; width:150px; drop hard shadow via filter? CSS filter: drop-shadow(6px 6px 0 rgba(28,19,14,.9))? drop-shadow with 0 blur = hard shadow — works in Chrome. Nice.
Also floating stickers: `<span class="sticker s1">24-HR GOCHUJANG MARINADE</span>` absolute bottom 40 left -10 rotate -7; parallax class .float data-depth.
OK, and "reveal" classes on section elements.
Now menu art SVGs as JS strings:
```js
const ART={
taco:`<svg viewBox="0 0 120 100"><path d="M10 58 A50 50 0 0 0 110 58 Z" fill="#F2A93B" stroke="#1C130E" stroke-width="4"/><circle cx="32" cy="52" r="9" fill="#E0311F" stroke="#1C130E" stroke-width="3"/><circle cx="52" cy="46" r="8" fill="#3F9B4F" stroke="#1C130E" stroke-width="3"/><circle cx="70" cy="52" r="8" fill="#FFF3DC" stroke="#1C130E" stroke-width="3"/><circle cx="90" cy="55" r="8" fill="#8A4B2A" stroke="#1C130E" stroke-width="3"/><path d="M22 56 Q60 44 98 54" fill="none" stroke="#E0311F" stroke-width="4" stroke-linecap="round" stroke-dasharray="2 10"/></svg>`,
```
Check taco shell: `M10 58 A50 50 0 0 0 110 58 Z`: chord 100, r 50 → semicircle. sweep 0: from (10,58) to (110,58) counterclockwise → passes (60,108) below. So shell bulges downward = taco held sideways opening up. Fillings above chord line at cy≈46-55 sit in opening.
burrito:
```
<svg viewBox="0 0 120 100">
<rect x="26" y="34" width="74" height="42" rx="21" fill="#F6E7C9" stroke="#1C130E" stroke-width="4"/>
<ellipse cx="28" cy="55" rx="14" ry="21" fill="#FFF3DC" stroke="#1C130E" stroke-width="4"/>
<circle cx="26" cy="47" r="5" fill="#E0311F"/><circle cx="30" cy="58" r="5" fill="#3F9B4F"/><circle cx="24" cy="66" r="4" fill="#FFC531"/>
<path d="M40 40 h54 M40 70 h54" stroke="#1C130E" stroke-width="3" opacity=".35"/>
<path d="M55 26 q3 -8 0 -12 M70 26 q3 -8 0 -12" stroke="#1C130E" stroke-width="3" fill="none" opacity=".4" stroke-linecap="round"/>
</svg>
```
Hmm steam lines cute.
quesadilla:
```
<svg viewBox="0 0 120 100">
<path d="M18 74 L44 30 L64 74 Z" fill="#F2A93B" stroke ink 4
<path d="M40 74 L70 26 L92 74 Z" fill="#FFC531" stroke ink 4
<circle toppings on top: cx48 cy44 r4 green, cx66 cy38 r4 red, cx58 cy50 r3.5 cream...
<path cheese drizzle: "M30 60 q20 10 56 -2" stroke #FFC531? overlapping triangle colors hmm fine
</svg>
```
fries:
```
<svg viewBox="0 0 120 100">
fries: <rect x="34" y="12" width="9" height="36" rx="4" fill gold stroke ink 3 transform rotate(-8 38 30)
...4 sticks various x 40,55,70,82 rotations
cup: <path d="M30 44 L26 84 L94 84 L90 44 Z" fill="#E0311F" stroke ink 4
label: <rect x="40" y="56" width="40" height="14" rx="7" fill cream stroke ink 3/> + tiny star path center
</svg>
```
slush:
```
<svg viewBox="0 0 120 100">
straw: <rect x="66" y="8" width="8" height="34" rx="4" fill="#3F9B4F" stroke ink 3 transform="rotate(14 70 24)"/>
dome: <path d="M36 40 A24 22 0 0 1 84 40 Z" fill="#FFC531" stroke ink 4/>
cup: <path d="M38 40 L34 86 L86 86 L82 40 Z" fill="#FFF3DC" stroke ink 4/>
liquid: <path d="M40 52 L37 80 L83 80 L80 52 Z" fill="#FFC531"/> with ice cubes: rects cream stroke.
Actually simpler: cup fill cream; inner liquid gold polygon; two ice rect cream stroke ink.
```
nachos (loaded fries already have fries art; nachos not in menu — skip).
Menu = 6 items: taco, chicken taco (use taco art variant with different fillings colors? create ART.taco2: shell gold same, fillings red-heavy), burrito, queso, fries, slush.
taco2: same shell, fillings: red x3 + green + cream, drizzle red thicker + extra flame? Add small flame icon on card corner? The heat meter communicates. Fine.
Now card render function:
```js
const grid=document.getElementById('menuGrid');
grid.innerHTML = MENU.map((m,i)=>`
<article class="card reveal" style="--rot:${(i%2? -1:1) * (0.4+ (i%3)*0.3)}deg">
<div class="cardTop">
<div class="cardArt">${m.art}</div>
<span class="price">$${m.price.toFixed(2)}</span>
</div>
<h3>${m.name}<em>${m.korean}</em></h3>
<p>${m.desc}</p>
<div class="cardFoot">
<div class="heat" data-heat="${m.heat}" role="img" aria-label="Heat ${m.heat} of 5"></div>
<button class="add btn small" data-id="${m.id}" data-name="${m.name}" data-price="${m.price}">ADD +</button>
</div>
${m.tag?`<span class="ribbon">${m.tag}</span>`:''}
</article>`).join('');
```
Rotation via CSS var: .card{rotate:var(--rot)}. Hover rotate 0.
Then heat meters:
```js
document.querySelectorAll('.heat').forEach(h=>{const n=+h.dataset.heat;let s='';for(let i=0;i<5;i++)s+=chili(i<n);h.innerHTML=s;});
function chili(on){return `<svg class="chi ${on?'':'off'}" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z" fill="#3F9B4F"/><path d="M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z"/></svg>`}
```
CSS: .chi{width:18px;height:18px}.chi path:last-child{fill:var(--red)} .chi.off path:last-child{fill:none;stroke:rgba(28,19,14,.35);stroke-width:1.8}. But stem of off chili also gray? stem fill green always, off stem maybe opacity .35: .chi.off path:first-child{opacity:.35}.
Wait: for stroke outline version, the body path with fill:none stroke — the path drawn as outline ok.
Bag logic:
```js
const bag=new Map();
function addToBag(id,name,price){
const cur=bag.get(id)||{name,price,qty:0};
cur.qty++; bag.set(id,cur);
updateBadge(); renderBag();
}
```
Add buttons handler + spice pick button handler (same but custom name/price).
updateBadge: total qty; badge text; pop class.
Modal open/close functions; renderBag builds list HTML; total.
Toast function.
Spice: elements ids.
Countdown:
```js
function nextOpening(){
const n=new Date();
const t=new Date(n);
const day=n.getDay();
let add=(6-day+7)%7;
t.setDate(n.getDate()+add); t.setHours(11,0,0,0);
if(t<=n) {t.setDate(t.getDate()+7);}
return t;
}
```
If today Sat before 11: add=0 → t today 11:00 > n → countdown to today 11. If Sat after 11: t<=n → +7. Good. Sun: add=6. Good.
Tick: show `in 04d 07h 12m 33s`.
Also countdown label: "GRAND OPENING · SAT 11AM".
Schedule rendering:
```js
const schedEl=document.getElementById('sched');
SCHED rows: `<li class="row ${off?'off':''} ${today?'today':''}">`
content: `<span class="day">${s.d}${today?'<i class="todayTag">TODAY</i>':''}</span><span class="spot">${s.spot}${off?`<small>${s.note}</small>`:''}</span><span class="time">${off?'—':s.time}</span>`
```
Status: id="truckStatus" big line + sub. Compute:
```js
const now=new Date();const d=now.getDay();const hr=now.getHours()+now.getMinutes()/60;
const t=SCHED[d];
function nextStop(from){for(let i=1;i<=7;i++){const s=SCHED[(from+i)%7];if(!s.off)return{s,diff:i,dayName:...};}}
```
Day names array full: SUN MON... use ['Sunday',...].
if t.off: status "TODAY: OFF — {note}" sub "Next stop: {day} · {spot} · {time}".
else if hr within [open, close): "LIVE NOW AT {spot}" pulse; sub "{time} today · {tag}".
else if hr < open: "TODAY'S SHIFT STARTS {open}:00 — {spot}".
else: "WRAPPED FOR TODAY" sub next stop.
navDot: green pulse if live, gray otherwise.
Map pin: set transform on <g id="pin"> to translate(pin coords), label textContent = day d short + spot short? Label "TODAY · HANA PARK" small under pin. Keep pinLabel = t.off? 'DEPOT' : t.spot.split(' ')[0]? Just set to short name field I add: s.short like 'HANA PARK','BREWERY','CAMPUS','NIGHT MKT','GRAND PLAZA','DEPOT'.
Map svg (300x300):
```svg
<svg viewBox="0 0 300 300">
bg rect fill #F3E4C6? panel cream. streets: paths stroke #1C130E22 width 12? Draw grid lines:
<path d="M0 60 H300 M0 150 H300 M0 240 H300 M50 0 V300 M140 0 V300 M230 0 V300" stroke="rgba(28,19,14,.15)" stroke-width="14"/>
route: <path id="route" d="M150 255 L150 150 L50 150 M50 60 L230 60 L230 205" ...> hmm make one path: "M150 255 V60 H50 M230 60 V205"? Multiple subpaths ok. stroke red dashed 5, animation dashoffset.
Blocks: rounded rects with labels:
park: rect x=160 y=14 w=130 h=32 fill #3F9B4F33? blocks subtle fill rgba(63,155,79,.25) stroke ink 2, label text 11px "HANA PARK" at (225,35)
market: rect x=14 y=14 w=130 h=32? label "NIGHT MARKET" at 80,35
campus: rect x=160? y=205... label "KSU"
brewery: rect near (60,120) small "IRON NODE"
depot: rect x=100 y=232 w=100 h=40 "DEPOT"
pin group id="pin": pulse circle (r 16 fill red opacity .35 animation), truck marker: small rounded rect 28x16 red stroke ink 3 with white window dot; sits above pin point; text label below.
</svg>
```
Set transform translate to pin coords minus offsets: I'll build pin with its anchor at (0,0) = truck bottom; contents drawn around (0,-16) etc; then transform translate(x, y-6). Label text element separate id="pinLabel" with its own transform translate(x, y+22).
Blocks coords vs pin coords must match the spot labels roughly:
- park pin (225,46): inside park block (x160..290 y14..46)? y=46 at bottom edge, fine (pin y placed slightly below label block).
- market pin (80,46) in market block (14..144,14..46).
- brewery pin (85,132): block "IRON NODE" rect x=64 y=110 w=90 h=44.
- campus pin (240,215): block rect x=160 y=190 w=130 h=50 label "KSU COMMONS".
- grand pin (150,150): intersection — draw special star plaza circle at (150,150) r=10 gold stroke ink label "MAIN & 3RD".
- depot pin (150,255).
Route dashed path connecting: "M225 46 L230 60 M80 46 L50 60 ..." — decorative only; I'll make simple dashed path along streets: `M50 60 H230 V150 H150 V240` plus `M50 60 V150 H140`? Keep one path: d="M50 60 H150 V255 M150 150 H230 V205" hmm whatever decorative: choose `d="M80 46 L50 60 L50 150 L150 150 L150 240 M225 46 L230 60 L230 205 L150 150"` — connects market→depot→campus etc. With dash animation `stroke-dasharray:10 10; animation: dash 3s linear infinite` keyframes {to{stroke-dashoffset:-40}} wait direction: to{stroke-dashoffset:-40}? For continuous flow: from 0 to -40 loops seamlessly if dash total 20*? offset multiple of 20: use 20 → -20*2=-40 fine (2 cycles). Actually any multiple works: -40.
Labels on map: small uppercase 10px ink with letter-spacing.
Now testimonials HTML static.
Footer: static.
Counters: in story strip: `<b class="num" data-n="24">0</b>` label. Animate:
```js
const io2=new IntersectionObserver(es=>{es.forEach(e=>{if(e.isIntersecting){countUp(e.target);io2.unobserve(e.target)}})},{threshold:.6});
document.querySelectorAll('.num').forEach(n=>io2.observe(n));
function countUp(el){const t=+el.dataset.n;const st=performance.now();const dur=1400;function f(now){const p=Math.min(1,(now-st)/dur);el.textContent=Math.round(t*(1-Math.pow(1-p,3)));if(p<1)requestAnimationFrame(f)}requestAnimationFrame(f)}
```
Reveal observer with stagger:
```js
const io=new IntersectionObserver(es=>{es.forEach(en=>{if(en.isIntersecting){en.target.classList.add('in');io.unobserve(en.target)}})},{threshold:.12,rootMargin:'0px 0px -40px'});
document.querySelectorAll('.reveal').forEach((el,i)=>{el.style.transitionDelay=(i%4)*70+'ms';io.observe(el)});
```
Hmm delay by global index fine.
Parallax:
```js
const stage=document.querySelector('.heroArt');
if(matchMedia('(pointer:fine)').matches){
hero.addEventListener('mousemove',e=>{ ... document.querySelectorAll('.float').forEach ... set style transform translate}
}
```
Use rAF guard simple: direct set is fine.
Set on .float elements: `el.style.transform = translate(x*depth, y*depth) rotate(var(--r))` — store rotation separately to not override sticker rotate... Sticker rotation via CSS `rotate` property (separate from transform) — great: use `rotate:-8deg` in CSS and transform for parallax. `rotate` property supported in Chrome.
Newsletter:
```js
form.addEventListener('submit',e=>{e.preventDefault();const v=inp.value.trim();if(!/^\S+@\S+\.\S+$/.test(v)){toast('That email looks a little undercooked.','Try again — we need a real one to ping you.');inp.classList.add('shakeX');setTimeout(()=>inp.classList.remove('shakeX'),500);return;}inp.value='';toast("You're on the list!","First ping = free topping. Fact.")});
```
toast(title, sub).
Modal Esc: keydown listener when open.
Ticker fill:
```js
const TICK=['KIMCHI LOCO','GOCHUJANG EVERYTHING','90-DAY KIMCHI','PRESSED-TO-ORDER TORTILLAS','SEOUL × CDMX','GRAND OPENING WEEK','HEAT RATED IN HALMONI UNITS'];
function tickerHTML(){return TICK.map(t=>`<span class="tk">${t}</span>${chili(true)}`).join('')}
document.querySelectorAll('.tTrack').forEach(tr=>{tr.innerHTML=`<div class="tGroup">${tickerHTML()}</div><div class="tGroup" aria-hidden="true">${tickerHTML()}</div>`});
```
CSS: .tTrack{display:flex;width:max-content;animation:slide 26s linear infinite}.tGroup{display:flex;align-items:center;gap:28px;padding-right:28px}.tk{white-space:nowrap;font display 18px letter-spacing 1px}.chi{flex none}. Reverse: .rev .tTrack{animation-direction:reverse}.
chili(true) returns svg with class "chi"— but tickers chilis colored red on red bg? Ticker bg red → chili invisible. For ticker use cream chili: pass a variant flag: chili(true,'cream') set fill? Simpler: in ticker CSS: `.ticker .chi path:last-child{fill:var(--cream)} .ticker .chi path:first-child{fill:var(--gold)}`. And in heat meter default red/green. Good — override via CSS.
Marquee item gap consistent so 50% translate loops perfectly: each group same width (identical content) — yes duplicated groups equal width; translating parent by -50% of its own width moves exactly one group.
Buttons in nav: bag with count:
```html
<button class="bagBtn" id="bagBtn" aria-label="Your bag">
<svg ...bag icon.../><span class="count" id="bagCount">0</span>
</button>
```
Bag icon: simple: `<path d="M6 8h12l-1.2 12.2A2 2 0 0 1 14.8 22H9.2a2 2 0 0 1-2-1.8L6 8Z"/><path d="M9 8V6a3 3 0 0 1 6 0v2" fill="none" stroke ...>` style ink stroke.
Nav logo chili + wordmark "KIMCHI LOCO" with "로코" small.
OK — also `scroll-margin-top` on sections.
Let me also consider the "story" red section content contrast: cream text on red — fine, large headings.
Testimonial cards bg cream on paper bg — need distinction: paper bg + cards cream with borders; ok. Add section heading style consistent.
Section heading pattern:
```html
<div class="secHead reveal">
<span class="kicker">THE LINEUP</span>?
```
Actually kicker = small label above big heading: kicker sticker pill (ink bg cream text small rotated slight), then h2 huge display with a red-highlight underline block on a key word: h2 with <i class="hl">highlight</i> where .hl{background:var(--gold); box-shadow:0 0 0 3px... } simpler: .hl{background:var(--gold);padding:0 .18em; box-decoration-break:clone} skewed: display inline-block rotate(-1.5deg). Good.
Now write everything. Also `::selection`.
Potential pitfalls double-check:
- CSS `rotate: .4deg` on .card via var: `rotate: var(--rot)` where --rot like `0.7deg`. Set inline style="--rot:0.7deg".
- .today styling in schedule.
- SVG `<text>` font: set via CSS class names inside svg works (inherits page CSS since inline SVG).
- For textPath href: use `href` attr (Chrome ok).
- Ensure IDs referenced by JS exist.
- Range input styling: include -webkit-slider-runnable-track & thumb; also -moz for safety.
Also viewport 1280x800: hero should fit nicely: nav ~76px, announce ~34px, hero ~ 620px; menu below fold — fine, screenshot shows hero with animated truck + headline — meaningful first paint.
Make sure first paint shows: static HTML renders immediately; JS at bottom executes quickly. Screenshot at ~3s includes ticker moving, puffs, wheels.
One more: countdown in hero — at load compute immediately (call tick once before interval).
Let me write mouth circle path helper strings:
- small O: `M122 154m-11 0a11 13 0 1 0 22 0a11 13 0 1 0-22 0`
- mid O: `M122 156m-13 0a13 15 0 1 0 26 0a13 15 0 1 0-26 0`
- big O: `M122 158m-17 0a17 18 0 1 0 34 0a17 18 0 1 0-34 0`
Tongue at 5: `M110 162a12 9 0 0 0 24 0z` fill #FF7A6B stroke ink 3 — sits inside big O (center 158, ry 18 bottom at 176; tongue top ~162) ok.
Face positions relative to chili body: body top y≈40, widest ~ y 120-160 area; eyes at y=126 within body (body spans x from ~74..210 at mid). Eyes cx 112/148 fine. Mouth center x=122? Face center-ish x=130. Shift mouth x to 130: adjust mouth paths centered 130: smiles: `M114 150 Q130 160 146 150`; big grin `M110 148 Q130 172 150 148 Q130 158 110 148 Z`; O circles centered 130: `M130 154m-11 0 a11 13 0 1 0 22 0 a11 13 0 1 0 -22 0` etc. Update tongue: `M118 162 a12 9 0 0 0 24 0 z`.
Cheeks cx 92/168 cy 150 r 11 fill #FF9D8F opacity .55.
Sweat drop at (182,118): path `M182 112 C 190 126, 190 134, 182 138 C 174 134, 174 126, 182 112 Z` fill #7FC7EA stroke ink 3.
Tears: ellipses cx=110 cy=142? below eyes: eye cy126 r9 bottom 135; tears at cy=144 rx4 ry6 fill sky stroke ink 2.5. Animate fall via CSS translateY loop.
Flames: two groups translate: left at (66,44), right at (178,40): flame path centered: `M0 26 C -10 14 -4 -2 2 -14 C 4 -4 12 -2 10 8 C 18 4 20 14 14 24 C 8 30 -6 30 0 26 Z`? eh. Draw flame: outer: `M0 24 C -9 16 -7 4 0 -8 C 2 -2 8 -4 8 2 C 14 0 14 10 8 18 C 5 22 3 24 0 24 Z` fill gold stroke ink 3; inner small: `M0 18 C -4 12 -3 6 0 0 C 2 4 5 5 4 9 C 6 10 4 15 0 18 Z` fill red. class .flame with flicker animation scaleY wobble. Show at level 5.
Add class .shaking to char svg at 5: keyframes shake {0%{transform:rotate(0)}25%{transform:rotate(-2deg)}75%{transform:rotate(2deg)}100%{transform:rotate(0)}} .3s infinite; transform-origin center.
Spice panel layout:
```html
<section id="spice">
<div class="wrap">
<div class="secHead">... "PICK YOUR PAIN" heading with kicker "SPICE-O-MATIC™"...</div>
<div class="machine reveal">
<div class="machineTop"> plate label + screws + "OFFICIAL HEAT NEGOTIATOR" </div>
<div class="machineGrid">
<div class="screen"><charSvg><div class="screenLabel">HEAT INDEX <b id="heatIndex">2/5</b></div></div>
<div class="controls">
<input range>
<div class="ticks">6 labels</div>
<div class="ticket">
<b id="recName">GOCHU GANG</b>
<span id="recItem">GOCHUJANG CRUNCH TACO</span>
<p id="recText">...</p>
<button id="recAdd">ADD THE PICK — $4.50</button>
</div>
</div>
</div>
</div>
</div>
</section>
```
Ticket with dashed left edge: border-left:4px dashed ink + notches? Use border style dashed overall? ticket: bg cream border 3 ink; left edge dashed to mimic receipt: add ::before circle notches? Keep simple: dashed border + slight rotate.
Ticks: 6 spans; active gets red bg cream text pill.
recAdd button click: adds recItem to bag with LEVEL price.
Slider initial value 2.
Also keyboard: range works with arrows — nice interactive without page focus issue (user must click page first anyway; fine).
Machine styling: bg cream border 4 ink shadow 8px; rivets: .rivet circles absolute corners bg ink.
Screen: bg #FFF7E6 border 3 ink inner radius; chili char svg width min(320px, 100%).
Also small sub note under machine: "No refunds on pride. Slush available on premise."
Now, footer big text: "FOLLOW THE FLAVOR" as h2 giant cream with "FLAVOR" in gold? use .hl gold block with ink text. On ink bg, gold highlight block + cream text works.
Footer socials chips row: bordered cream chips with small svg icons (instagram: rounded rect+circle+dot; tiktok: note approximated? drawing tiktok logo is fiddly — use a simple music note path; phone: handset path).
Instagram icon: `<rect x="3" y="3" width="18" height="18" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.2" cy="6.8" r="1.3"/>` stroke ink fill none.
Phone: `<path d="M6.5 3.5c.8 0 1.5.5 1.8 1.2l1 2.3c.3.7.1 1.5-.5 2l-.9.8a12.5 12.5 0 0 0 5.8 5.8l.8-.9c.5-.6 1.3-.8 2-.5l2.3 1c.7.3 1.2 1 1.2 1.8v2c0 1.1-.9 2-2 1.9C9 19.6 4.4 15 3.9 8.2 3.8 7.1 4.7 6.1 5.8 6.1h.7z"/>` fill ink.
Note (tiktok-ish): `<path d="M10 4h3v9a3 3 0 1 0 3-3V7c3 1 5 3.6 5 6.7A6.7 6.7 0 1 1 9.6 8v6.2a3.4 3.4 0 1 1-3.4-3.4c.3 0 .6 0 .8.1V7A6.7 6.7 0 1 0 13 13.7"/>` hmm messy. Just do a simple double-note music glyph: `<path d="M9 18a3 3 0 1 1-2-2.8V6l11-2v9.2A3 3 0 1 1 16 10.4V7.6L11 8.5v9.5z"?` approximate: two note stems. I'll write: `<path d="M9.5 17.6A2.7 2.7 0 1 1 7 15v-8l10-1.8v6.4a2.7 2.7 0 1 1-2.5-2.6v-2L9 7.6z" fill="ink"/>` fine.
These small icons drawn imperfectly are acceptable at 18px.
Footer bottom small print + korean line.
Also add "back to top"? skip.
I think content plan is complete. Now carefully write the final file. I'll keep CSS organized with comments. Let me write JS with const functions and guard nulls. Avoid syntax errors — mentally compile.
Regarding `<style>` inside and SVG inside template strings — no `</script>` risk.
One concern: In HTML, using `×` char directly fine (UTF-8).
Also `<i>` elements used as sticker tags etc — fine.
Write it now. Also make sure `heat` chips in spice ticks use `<span>`.
For schedule rows on small screens: grid-template-columns: 54px 1fr auto; fine.
Let me also handle: `.wrap{width:min(1180px, 92%); margin-inline:auto}`.
Hero min-height: none needed.
Announce bar text: "GRAND OPENING WEEK — FIRST 50 GUESTS EACH DAY GET A FREE KIMCHI QUESADILLA · NO DANCE REQUIRED (BUT ENCOURAGED)". Small font 11px ls 1.5px.
Nav links: MENU / SPICE-O-MATIC / STORY / FIND US anchors.
Let me write hero right column sizing: truck svg width: 100%; max-width 640px. Stamp overlapping top right; sticker bottom left; big word behind.
The heroArt position relative; bigword absolute right:-4%; top:-6%; font-size clamp(90px,16vw,170px); z-index:0; opacity stroke color rgba(28,19,14,.10).
truck z-index 1.
For balance on mobile: heroArt margin-top 40.
Alright, writing final code. I'll be thorough and double check bracket matching in JS.
Also: define keyframes list: slide, spin, bounce, puff, swing, rot, pop, dashflow, pulse, shakeX, flicker, fall (tears), sweatdrip, shake (char), blink? maybe eyes blink occasionally: cute: .eye animation blink 4s infinite (scaleY). transform-box fill-box on circles: scaleY squish. Add.
Cute detail: window "OPEN" text inside truck window in gold.
Also truck text elements need font-family inherit; SVG inherits CSS — set class="t" with font settings.
Chassis skirt: rect x=64 y=262 w=470 h=26 rx=13 fill ink — covering body bottom (body ends 282; skirt y 262..288) so wheels overlap skirt. Wheels cy=302 → wheel top 266 hidden behind skirt? skirt drawn before wheels; wheels drawn after skirt → wheels over skirt. Good. Wait skirt bottom 288, wheels center 302 r36 → 266..338. Wheels overlap skirt region 266..288. Wheels on top = correct (they appear mounted below skirt). Shadow ellipse cy 366. Wheel bottom 338, so truck floats 28px above shadow ellipse center line — shadow ellipse cy should be ≈ 345-350 so wheels touch it: set shadow cy=350 ry 16. Wheels bottom 338 → slight overlap with shadow top (334). Good.
Bumpers: front rect x=48 y=248 w=26 h=30 rx=8 fill ink; rear x=524 y=248 w=26 h=30.
Headlight circle cx=62 cy=236 r=9 gold stroke ink 4. Place before body? body x=64; headlight at x~54 on bumper area. ok.
Exhaust pipe bottom front: small rect under bumper with puff? We have chimney steam. skip.
Door seam: line x=204: path "M204 116 V258" stroke ink width 4 opacity .55, plus handle: rect x=190 y=170 w=14 h=5 rx=2 ink.
Windshield: rect x=88 y=130 w=92 h=56 rx=14 fill #BCE3F0 stroke ink 5. Wiper: path M96 178 L150 140 stroke ink 3 opacity .5. Shine: path "M100 136 l-8 40" stroke cream 6 opacity .7? diagonal glints: two lines.
Wait — body top y=112, windshield y=130 fine.
Also add small Korean flag-esque circle decal near rear? Add taeguk-ish simplified: red circle over blue circle half? Use half-moon: circle cx=560? rear x=534 body ends 534. Between window end (460) and rear (534): space 74px: place a decal: rounded square with "매운" (spicy)? Add text "매운맛!" vertical? Simply put a red circle sticker with white chili? Use small gold star + "EST. 2025" text at (497,180)? Let's put "SEOUL × CDMX" small vertical? Keep: circle cx=497 cy=185 r=26 fill gold stroke ink 5 with chili icon centered (small chili paths scaled). And below "EST. 2025" text 12px cream at y=232? area body bottom 262; text y=240 near bottom: "SEOUL × CDMX · EST 2025" width ~150 at 12px fits x centered 497 (spans 422..572 too wide) → put at y=238 centered x=497 size 11: "SEOUL × CDMX" ≈ 90px fine. And "EST. 2025" skip.
The window occupies x300..460: opening rect x=300 y=150 w=160 h=88 rx=14 fill #241812? interior ink-ish #241812 (dark brown-black). Inside: "OPEN" gold text x=380 y=182 anchor middle size 20 letter-spacing 6 (neon vibe); counter: rect x=300 y=218 w=160 h=6? shelf line: path M300 220 H460 stroke cream 6; taco on counter: small shell arc at (336,214): path `M322 216 A14 12 0 0 0 350 216 Z` fill gold stroke ink 3, dot red. Cute.
Window frame: cream border around opening: rect x=294 y=144 w=172 h=100 rx=18 fill none stroke cream? Instead draw cream rect behind: rect x=294 y=144 w=172 h=100 rx=16 fill var(--cream) stroke ink 5, then opening ink rect inset. Good depth.
Awning: above window: y=112..146: stripes rect 8 × 22 wide: x start 292 w total 176 (292..468). Base line y=112 aligns body top? body top y=112 with stroke; awning slightly below top: y=118 h=28 (118..146), scallops at 146: arcs radius 11 → 8 arcs of 22 = 176. stripes: 8 rects x=292+i*22 w=22 h=28 fill alternating red/cream. Outline: rect x=292 y=118 w=176 h=28 fill none stroke ink 4. Scallops: path "M292 146 a11 11 0 0 1 22 0" ×8 stroke ink 4 fill none. Also add tiny shadow line under awning? skip.
Hmm awning overlaps window frame (frame top 144, awning scallops to ~157 covering frame top) — awning drawn AFTER frame → scallop strokes over frame top area; looks like awning hanging over window. OK.
Brand text block: door seam at 204, window frame starts 294 → center ≈ 249. "KIMCHI" at 24px Arial Black ≈ 24*0.78*6 ≈ 112 wide → 193..305 slight overlaps both sides by ~1-2px... reduce to 22px: ~103 → 197..300. tight ok. "LOCO" 4 chars ≈ 70. Set both anchor middle x=249: "KIMCHI" y=166 (font 22), "LOCO" y=196 (font 22), 김치 로코 y=226 (font 14, gold). Add class tBrand fill cream stroke? Arial Black cream on red — good contrast. Add slight letter-spacing.
Hmm 22px "KIMCHI" might still be wide; SVG will just render whatever — overlapping edges by a couple px acceptable visually? Overlapping door seam line looks sloppy. Let me reposition: door seam x=196; frame x=294; span 98px usable. 6 chars at 22px Black: Black is wide: ~0.72em avg → 6*15.8=95. OK fits 98. Fine.
Alternatively stack words vertically rotated -90 like graffiti: nah.
Roof sign text "TACOS" gold sign ink text; add tiny chilis at sign sides: two small chili paths at (222,54) and (388,54) scaled 1.2, fill red on gold. Sign width 190 x210..400. Posts at 270/330 — under sign. Swing group contains sign+text+posts; transform-origin: posts bottom = y=116 → set transform-box:fill-box origin bottom center.
Chimney front-left: pipe rect x=76 y=70 w=24 h=46 fill ink stroke? on top of body (body top y=112: pipe from 70..116 goes behind body edge) draw pipe before body? Pipe base hidden by body top edge — draw pipe first, then body covers bottom. But body drawn after would cover pipe bottom: good. Cap: rect x=68 y=60 w=40 h=14 rx=6 fill red stroke ink 4 drawn after pipe but before body? Cap at y60..74, pipe 70..116 — cap covers pipe top. Puffs from (88, 48) rising to ~10: circles r 8-11 at (84,44),(94,36),(80,28) with animation translateY(-20) opacity. Since bounce group includes them, combined ok.
Puff keyframes:
```css
@keyframes puff{0%{transform:translateY(6px) scale(.5);opacity:0}25%{opacity:.85}70%{opacity:.5}100%{transform:translateY(-26px) scale(1.3);opacity:0}}
.puff{transform-box:fill-box;transform-origin:center;animation:puff 2.8s linear infinite}
.p2{animation-delay:.9s}.p3{animation-delay:1.8s}
```
Bounce:
```css
@keyframes bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
.tbounce{animation:bounce 3.4s ease-in-out infinite}
```
Spin: `@keyframes spin{to{transform:rotate(360deg)}}` .spin{animation:spin 2.6s linear infinite; transform-box:fill-box;transform-origin:center}.
Swing: `@keyframes swing{0%,100%{transform:rotate(0)}30%{transform:rotate(-1.6deg)}70%{transform:rotate(1.6deg)}}` .swing{transform-box:fill-box;transform-origin:50% 100%;animation:swing 5s ease-in-out infinite}.
Stamp rotor: `@keyframes rot{to{transform:rotate(360deg)}}` .rotor{...animation:rot 16s linear infinite}.
Pulse pin: `@keyframes ping{0%{transform:scale(.4);opacity:.7}100%{transform:scale(1.8);opacity:0}}` circle class .ping with transform-box fill-box.
Pop badge: scale 1→1.35→1.
toast: transform translateY(20px) opacity 0 → show translate 0.
shakeX: translateX ±6 3 times.
Tear fall: `@keyframes cry{0%{transform:translateY(0);opacity:1}100%{transform:translateY(14px);opacity:0}}` .tears ellipse animation cry 1s infinite? Both ellipses with delays.
Sweat drip: reuse cry on sweat path: `#sweat{animation: sweatd 1.4s ease-in-out infinite}` keyframes: opacity .0→1→ translateY(6)→0? Simpler: keyframes sweatd{0%{transform:translateY(-4px);opacity:0}30%{opacity:1}100%{transform:translateY(8px);opacity:0}}.
Flicker flames: `@keyframes flick{0%,100%{transform:scale(1) rotate(0)}50%{transform:scale(1.15,0.92) rotate(3deg)}}`.
Dash flow for route: `@keyframes flow{to{stroke-dashoffset:-60}}` with dasharray 10 14 (sum 24; 60 = 24*2.5 not multiple → jump). Use dasharray 12 12 (sum 24), offset -24 per cycle seamless: `@keyframes flow{to{stroke-dashoffset:-24}}`? Continuous moving along path direction; -24 per 1s. Wait for dashes to appear moving forward, offset should decrease: yes to -24 loops perfectly.
Marquee slide: `@keyframes slide{to{transform:translateX(-50%)}}`.
Blink eyes: `@keyframes blink{0%,92%,100%{transform:scaleY(1)}96%{transform:scaleY(.1)}}` .eye{animation:blink 4.6s infinite; transform-box:fill-box;transform-origin:center}. When tears visible eyes still blink fine.
OK now — schedule section markup:
```html
<section id="find">
<div class="wrap">
<div class="secHead reveal">
<span class="kicker">GPS FOR YOUR STOMACH</span>
<h2>CATCH THE <i class="hl red">TRUCK</i></h2>
</div>
<div class="status reveal">
<span class="liveDot" id="liveDot"></span>
<div><b id="statusMain">…</b><span id="statusSub">…</span></div>
</div>
<div class="findGrid">
<ul id="sched" class="reveal"> ... rows ... </ul>
<div class="mapWrap reveal">
<svg id="map" ...>...</svg>
<span class="mapNote">IF THE PIN MOVES, FOLLOW IT. THAT'S THE WHOLE BUSINESS MODEL.</span>
</div>
</div>
</div>
</section>
```
Schedule rows built by JS into #sched.
Style: rows list bg cream border 4 ink shadow, divide by border-top; .today row bg red color cream; .off row dashed border cream? .off{background:repeating? no gradients... use opacity/dashed border style: background:#F4E8CF; color:rgba(28,19,14,.75)} with time "—". TODAY sticker: absolute pill rotate.
Now statuses colors: liveDot green pulsing when live else ink hollow.
Time to write final output. Also remember: after code block, nothing else. Explanation not allowed per format ("No text before or after the code block") — the format rules override the usual explanation note; just the code block.
Also title/description for SEO in head — fine.
Let me carefully write JS at end with an IIFE or plain script. Use 'use strict' optional.
Let me also make sure to define everything referenced:
IDs: bagCount, bagBtn, menuGrid, spiceSlider, heatIndex, chiliChar, cBody, mouth, tongue, tears, sweat, flames, recName, recItem, recText, recAdd, ticks (container id="ticks"), cd, statusMain, statusSub, liveDot, sched, pin, pinLabel, navDot?, newsForm, newsInput, modal, modalList, modalTotal, openModal..., toasts, orderModal overlay.
Also modal: clearBag button, closeModal, checkout btn.
Write modal open: overlay.classList.add('open'); render list. Bag btn toggles.
Empty bag state text.
Countdown element id="cd" inside pill: content set by JS: `04d 07h 12m 33s`.
Edge: `pad` helper.
Write everything now, being meticulous.
CSS length will be long but fine.
Media queries:
- max-width 980: hero grid 1col; findGrid 1col; machineGrid 1col; steps 2col.
- max-width 620: menu cards 1col (auto-fit handles), steps 1col, nav links hidden, h1 smaller (clamp handles), schedule row font smaller, machine padding smaller.
Menu grid: repeat(auto-fill,minmax(300px,1fr)).
Footer big text clamp.
Ensure no horizontal scroll: html,body{overflow-x:clip} + .hero{overflow:hidden} because bigword & stamp may overflow right.
Stamp positioned right:-10px — inside heroArt with overflow? If hero overflow hidden, stamp clipped top? heroArt not clipped; hero section overflow hidden could clip stamp top (-28) if at section top edge. Hero has padding-top ~48 so stamp -28 relative to heroArt (which starts lower) — within section. OK.
bigword right:-4% could overflow viewport → hero overflow hidden handles.
Let me write the file.
Also: for the nav "ORDER AHEAD" maybe rename to bag only. Keep bag button prominent (red bg).
Announce bar top, then nav sticky. z-index nav 50 above announce.
Alright — code:
Also one more check on chili icon body path — I wrote: `M10.4 4.2c3.2.8 5.7 3 7.2 6.3 1.6 3.6.4 8-3.3 9.7-3.7 1.7-8.2.3-10-3.4C2.6 13.2 4.2 8 8.4 5.4c.6-.4 1.3-.8 2-1.2z` — starts top near stem (10.4,4.2), curves right-down to (17.6? ) Let's compute: c3.2 .8 → to (13.6,5.0); then "5.7 3 7.2 6.3" is continuation of same c? No — after first c with multiple triplets: c takes triplets; "c3.2.8 5.7 3 7.2 6.3" = one cubic from (10.4,4.2) to (17.6,10.5). Then next "1.6 3.6.4 8-3.3 9.7" = cubic rel to (14.3,20.2). Then "-3.7 1.7-8.2.3-10-3.4" → to (4.3,16.8). Then "C2.6 13.2 4.2 8 8.4 5.4" absolute cubic to (8.4,5.4). Then "c.6-.4 1.3-.8 2-1.2z" to (10.4,4.2). Closed. Shape: rounded blob tilted right-down — chili-ish with stem at top-left. Good enough at 18px.
Stem path: `M10 4.6C9.6 3 8.4 1.7 6.8 1.5c.2 1.4 1 2.6 2.3 3.2z` small green flag. OK.
For the big chili character body path, verify it closes and looks chili-like:
`M132 40 C 186 46 218 96 210 150 C 203 205 168 258 118 282 C 108 287 96 284 92 274 C 66 214 58 148 74 100 C 88 60 108 38 132 40 Z`
Path: start (132,40). C1: (186,46)(218,96) → (210,150): right bulge. C2: (203,205)(168,258)→(118,282): down-left to bottom tip. C3 small: (108,287)(96,284)→(92,274): tip curl. C4: (66,214)(58,148)→(74,100): left side up. C5: (88,60)(108,38)→(132,40): top. Looks like plump chili leaning right, tip bottom-left. Face at (130,126) fits within x 92..200 at y 100-160? At y=126 the body spans roughly x 78..212 — eyes at 112/148 inside. Cheeks at 92/168 — 92 near left edge; cheek r=11 → extends to 81; at y=150 left edge ~72. OK.
Stem: at top center (132,40): `M124 44 C120 26 108 14 92 10 C110 4 128 10 136 24 C140 32 138 38 134 46 Z` fill green stroke ink 5. Overlaps body top. Good.
Flames positioned at stem sides: left (58,36), right (170,26). transform translate. Path drawn around origin.
Now the SVG for map pin truck marker: group id="pin":
```svg
<g id="pin">
<circle class="ping" r="18" fill="rgba(224,49,31,.35)"/>
<circle r="6" fill="#E0311F" stroke="#1C130E" stroke-width="3"/>
<g transform="translate(-16,-34)">
<rect x="0" y="0" width="32" height="18" rx="4" fill="#E0311F" stroke="#1C130E" stroke-width="3"/>
<rect x="24" y="2" width="8" height="10" rx="2" fill="#BCE3F0" stroke="#1C130E" stroke-width="2"/>
<rect x="3" y="16" width="8" height="6" rx="3" fill="#1C130E"/>
<rect x="21" y="16" width="8" height="6" rx="3" fill="#1C130E"/>
</g>
</g>
<text id="pinLabel" ...>
```
JS: pin.setAttribute('transform',`translate(${x} ${y})`); pinLabel x/y set via setAttribute transform translate(x,y+30) and textContent. Pin marker above point.
ping animation scale about center: transform-box fill-box.
Also map has id="map"; blocks & labels:
```svg
<rect x="0" y="0" width="300" height="300" rx="22" fill="#F5E7C8"/>
<path d="M0 60H300M0 150H300M0 240H300M50 0V300M140 0V300M230 0V300" stroke="rgba(28,19,14,.13)" stroke-width="16"/>
<path id="route" d="M50 60 H150 V255 M230 60 V150 H150" fill="none" stroke="#E0311F" stroke-width="5" stroke-dasharray="12 12" stroke-linecap="round" class="route"/>
<!-- blocks -->
<g class="blk">
<rect x="160" y="14" width="126" height="32" rx="8" fill="rgba(63,155,79,.5)" stroke="#1C130E" stroke-width="2.5"/>
<text x="223" y="35" class="mtxt">HANA PARK</text>
</g>
<g class="blk"><rect x="14" y="14" width="118" height="32" rx="8" fill="rgba(224,49,31,.35)" stroke="#1C130E" stroke-width="2.5"/><text x="73" y="35" class="mtxt">NIGHT MARKET</text></g>
<g class="blk"><rect x="64" y="112" width="90" height="44" rx="8" fill="rgba(255,197,49,.6)" stroke="#1C130E" stroke-width="2.5"/><text x="109" y="132" class="mtxt">IRON NODE</text><text x="109" y="146" class="mtxt s">BREWING</text></g>
<g class="blk"><rect x="160" y="190" width="126" height="50" rx="8" fill="rgba(63,155,79,.35)" stroke="#1C130E" stroke-width="2.5"/><text x="223" y="219" class="mtxt">KSU COMMONS</text></g>
<g class="blk"><rect x="100" y="255" width="100" height="34" rx="8" fill="rgba(28,19,14,.12)" stroke="#1C130E" stroke-width="2.5"/><text x="150" y="276" class="mtxt">DEPOT</text></g>
<circle cx="150" cy="150" r="10" fill="#FFC531" stroke="#1C130E" stroke-width="3"/>
<text x="150" y="172" class="mtxt" text-anchor="middle">MAIN & 3RD</text>
```
Wait blocks overlap streets: park rect y 14..46 covers street y60? No: y14..46, street at 60 below. Streets drawn before blocks → blocks over streets = parks beside roads fine. But block x 160..286 covers vertical street x230? x230 is within 160..286 → block covers street. Draw blocks first, streets over them? Streets as lines over blocks looks like roads crossing parks — bad. Reorder: blocks first, then streets (semi-transparent roads over everything is typical stylized map), then route, then plaza, pin. Roads with .13 opacity over blocks look like roads on top — acceptable stylized. Alternatively avoid overlap: park rect x160..286 but street x=230 crosses it... set park width to end at 220: x=160 w=60? tiny. Simpler: draw streets first, blocks after (blocks sit "between" streets visually since they're placed in the gaps): park rect y14..46 between streets y0? top street row: streets at y=60±8 → block y14..46 fits above street 60. vertical streets x=50,140,230 with width 16 (x±8): park x=160..286 overlaps x=230 street (222..238) → covers part of vertical street with green — looks like park crossing road. To avoid: choose block x ranges avoiding street x±10: park x=156? vertical streets at 230: park between 140+8=148 and 230-8=222 → x=152 w=64. Labels "HANA PARK" 10px ≈ 58px wide — fits 64. Night market block between x=58 and x=132: x=60 w=70 "NIGHT MARKET" ~70px at 9px font... tight; use font 9. OK adjust:
- park: x=152 y=14 w=64 h=32, text y=34.
- market: x=60 y=14 w=64 h=32, text "NIGHT MKT".
- brewery block between x=148..222? x=152 y=112 w=64 h=44, texts two lines.
- campus block x=152..222? y between streets y150+8=158 and 240-8=232: x=152 y=162 w=64 h=62, text "KSU".
- depot: between x140..230 horizontal y 240..300: x=152 y=248 w=64 h=40 "DEPOT".
Then pin coords must be ON streets/intersections:
- market pin: (100,60) on horizontal street y60 between x50..140.
- park pin: (230,60)? vertical street x230 at y60 intersection. park block at x152..216 y14..46; pin at (230,42) on vertical street next to park. Use (230,46).
- brewery pin: (140,120) on vertical x140 between y60..150.
- campus: (230,205) on vertical x230 between 150..240.
- night market: (100,60)? wait I assigned market pin... Let me map:
- TUE Hana Park → (230,46)
- WED Iron Node → (140,120)
- THU KSU → (230,205)
- FRI Night Market → (100,60)
- SAT Grand Plaza → (150,150) intersection
- OFF → depot (184,268) on street x140..230? y between 240..300 at x=184 horizontal street y=240? Put (140,268) on vertical street x140 south. Use (140,268).
Route path along streets: connect: (100,60)→(140,60)→(140,120)? etc. Route: d="M100 60 H140 V120 M230 46 V150 H140 M140 268 V240 M230 205 V240"? Make simple long path: "M100 60 H230 V205 H140 V268 M140 60 V150 H230 V46"? Fine decorative: I'll use d="M230 46 V240 H50 V60 H230 M140 60 V150" hmm covers lots. Keep: d="M230 46 V60 H140 V268 H230 V205 M230 150 H140". Eh — honestly decorative dashes along a few streets: d="M50 60 H230 V268 H140 V60" (a loop). That draws: (50,60)→(230,60)→(230,268)→(140,268)→(140,60). Nice loop passing most pins. Plus plaza at (150,150) not on it (it's at intersection x140? plaza at 150,150 near). Move plaza star to (140,150) intersection, label "MAIN & 3RD" → pin coords SAT (140,150). Good, plaza on route.
Final pin coords:
- OFF(depot): (140,268) — on loop corner? loop passes (140,268). yes.
- TUE park: (230,46)
- WED brewery: (140,120)
- THU campus: (230,205)
- FRI market: (100,60)
- SAT plaza: (140,150)
Store as pin:[x,y].
pinLabel transform translate(x, y+26) maybe anchor middle; for pins near right edge (230) text may overflow 300? "HANA PARK" centered at 230: width ~55 → 202..258 fine.
Block labels mtxt: font 9px display letterspacing 1 fill ink opacity .8 text-anchor middle.
OK. Also `.mapNote` under map.
Status section and schedule highlight: today index known.
Now writing the final file for real. I'll assemble and double check JS carefully as I go.
One more consideration on countdown format: `04d 07h 12m 33s` — fine.
For countdown pill include a small flame svg? include chili icon.
Alright, final write-up. I'll try to keep it clean and complete. Estimated ~900 lines. Go.
Also don't forget `lang="en"`.
Small risk: CSS `rotate` property on .card with var — supported Chrome 104+. fine.
For safety with older parsing, avoid CSS nesting.
Let me write the HTML sections fully.
Also nav anchor hrefs: #menu #spice #story #find.
In footer maybe id="order" not needed.
Write modal markup:
```html
<div class="overlay" id="overlay" aria-hidden="true">
<div class="modal" role="dialog" aria-modal="true" aria-label="Your bag">
<div class="modalHead"><h3>YOUR BAG</h3><button class="x" id="closeModal" aria-label="Close">✕</button></div>
```
✕ char U+2715 — fine (not emoji). Use SVG instead to be safe: simple X path. I'll use an svg X.
```html
<ul id="bagList"></ul>
<div class="bagTotal"><span>TOTAL</span><b id="bagTotal">$0.00</b></div>
<p class="modalNote">Pay at the window — cash, card, or an extremely good compliment.</p>
<div class="modalBtns">
<button class="btn ghost" id="clearBag">CLEAR</button>
<button class="btn red" id="checkout">LOCK IT IN</button>
</div>
</div>
</div>
```
Overlay hidden default: visibility hidden opacity 0; .open visible.
JS checkout: toast("Locked in!","Cook it fresh when you get here — see you at the window."); close overlay.
Toast container top-right? bottom-center. Style toast: bg ink color cream border-left 6px gold; strong block + sub small opacity .8.
Now writing JS render of schedule:
```js
const DAYL=['SUN','MON','TUE','WED','THU','FRI','SAT'];
const SCHED=[
{off:true, spot:'OFF — DEPOT NAP', note:'Recharging. Rigorous quality control (we eat leftovers).', pin:[140,268], short:'DEPOT'},
{off:true, spot:'OFF — MARINATING', note:'The bulgogi needs its 24 hours. Respect the process.', pin:[140,268], short:'DEPOT'},
{spot:'HANA PARK — SOUTH GATE', time:'11:00–15:00', open:11, close:15, tag:'LUNCH RUSH', pin:[230,46], short:'HANA PARK'},
{spot:'IRON NODE BREWING', time:'16:00–21:00', open:16, close:21, tag:'BEER GARDEN', pin:[140,120], short:'BREWERY'},
{spot:'KSU CAMPUS COMMONS', time:'11:00–15:00', open:11, close:15, tag:'STUDENT FUEL', pin:[230,205], short:'CAMPUS'},
{spot:'RIVERSIDE NIGHT MARKET', time:'17:00–23:00', open:17, close:23, tag:'NEON NIGHTS', pin:[100,60], short:'NIGHT MKT'},
{spot:'MAIN & 3RD — GRAND PLAZA', time:'11:00–23:00', open:11, close:23, tag:'THE BIG DAY', pin:[140,150], short:'MAIN & 3RD'},
];
```
Render rows with today class.
Status compute + liveDot + pin place.
Also "today tag" ribbon.
Menu: check name lengths fine.
Now — writing final. Also remember heat for slush 0 → 0 chilis filled; aria-label "Heat 0 of 5".
Ribbon tags: kbbq 'MOST LOVED', slush 'FIRE EXTINGUISHER', fries? maybe 'LINE FORMS EARLY'. Two-three ribbons fine.
Let me finalize menu descriptions:
1. THE K-BBQ TACO — 불고기 타코 — "24-hour gochujang bulgogi, kimchi-lime slaw, sesame crema, scallion snow. The one that started the line." 4.50 heat2
2. GOCHUJANG CRUNCH TACO — 고추장 타코 — "Double-fried chicken thighs, sticky gochujang glaze, pickled daikon, cucumber ribbons. Loud. Very loud." 4.50 heat3
3. SEOUL STACK BURRITO — 서울 스택 — "Galbi, kimchi fried rice, black beans, queso-but-make-it-gochujang. Two hands. No exceptions." 11 heat2
4. KIMCHI QUESADILLA — 김치 케사디아 — "90-day aged kimchi + stretchy mozzarella, green-onion crema, toasted until it hisses at you." 9 heat2
5. LOADED KIMCHI FRIES — 김치 감자 — "Crinkle fries buried under bulgogi, gochujang queso, nori dust, jalapeño confetti. Share… or don't." 10 heat3
6. YUZU MELT SLUSH — 유자 슬러시 — "Yuzu, plum, crushed ice, salvation. 0 heat, 100% rescue mission. Rated: the antidote." 4 heat0
Menu footer note: "Heat rated in halmoni units. Level 5 requires a witness. Slush sold as emotional support."
Spice panel note under: same vibe.
Story steps copy:
01 MARINATE — "Bulgogi sleeps 24 hours in gochujang, pear, garlic. Dreams included."
02 FERMENT — "Kimchi in-house for 90 days. Halmoni inspects. Halmoni approves."
03 PRESS — "Tortillas pressed per order. Thirty seconds of pure theater."
04 SMASH — "Seared hard, sauced loud, wrapped with love and mild regret."
Stats: 24 HRS MARINATING / 90 DAYS FERMENTING / 5280 LINE DANCES WITNESSED? Keep 0 CHILL GIVEN. Use: 24, 90, 0. Cute zero. Maybe "12 SECONDS AVG FIRST BITE SILENCE". Keep 3: 24 / 90 / 0.
Footer socials:
- INSTAGRAM — @KIMCHI.LOCO
- TIKTOK — @KIMCHILOCO
- CALL/TEXT — (555) 010-LOCO
Newsletter label: "GET THE DAILY LOCATION PING — one message a day, zero spam, occasional free topping codes."
Fine print: "© 2025 KIMCHI LOCO — one truck, full heart. Everything touches sesame, joy, and probably your soul. / 맛있겠다!"
Now writing final code block. During writing, I'll double-check JS for typos.
For heat chili aria and titles ok.
Bag icon svg path: use stroke style:
`<svg viewBox="0 0 24 24"><path d="M5.5 8h13l-1.1 12.1a2 2 0 0 1-2 1.9H8.6a2 2 0 0 1-2-1.9L5.5 8Z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linejoin="round"/><path d="M9 8V6.5a3 3 0 0 1 6 0V8" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>`
Menu hamburger not needed (links hidden mobile).
Nav:
```html
<header class="nav">
<div class="wrap navIn">
<a class="logo" href="#top">
<span class="logoChili">chili svg</span>
<span class="logoTxt">KIMCHI<em>LOCO</em></span>
</a>
<nav class="links"><a href="#menu">MENU</a><a href="#spice">SPICE-O-MATIC</a><a href="#story">STORY</a><a href="#find">FIND US</a></nav>
<button class="bagBtn" id="bagBtn">BAG <span class="count" id="bagCount">0</span></button>
</div>
</header>
```
body id="top"? add id="top" to body? anchor to top: href="#top" with element id — put id="top" on announce div. fine.
logoTxt: display font; em red block? em{color red} maybe second line small "로코 김치"? keep "KIMCHI LOCO" with LOCO in red.
Bag btn: bordered, count badge red circle.
OK writing now. Also ensure `.hl.red` variant: background red color cream.
Also add hover states on schedule rows? Not needed.
Testimonial cards: stars svg row: 5 small gold stars: star path `M12 2l2.9 6.2 6.6.8-4.9 4.6 1.3 6.5L12 17l-5.9 3.1 1.3-6.5L2.5 9l6.6-.8Z`.
Testimonials heading: "THE STREETS ARE TALKING".
Also add press-ish strip? skip.
Finally, double-check requirement "Games must start in self-playing demo mode" — not a game, N/A.
Let me now write the complete file text. I'll be careful about escaping in template literals — no backticks inside strings.
While writing schedule "today" logic, also handle minutes for close boundary using hours only (close:15 means until 15:00). hr >= open && hr < close.
Countdown pill markup:
```html
<div class="openPill reveal">
<span class="pulseDot"></span>
<b>GRAND OPENING — SAT 11:00</b>
<span id="cd">…</span>
</div>
```
Parallax code:
```js
const floats=document.querySelectorAll('.float');
const heroEl=document.getElementById('hero');
if(window.matchMedia('(pointer:fine)').matches){
heroEl.addEventListener('mousemove',e=>{
const r=heroEl.getBoundingClientRect();
const x=(e.clientX-r.left)/r.width-.5, y=(e.clientY-r.top)/r.height-.5;
floats.forEach(f=>{const d=+f.dataset.depth||10; f.style.transform=`translate(${(-x*d).toFixed(1)}px, ${(-y*d).toFixed(1)}px)`;});
});
}
```
Note floats use CSS `rotate` separate property so transform free. Stamp: make stamp also float data-depth 18. sticker depth 10. bigword depth 6.
Stamp svg has CSS animation rot on inner .rotor group; outer .stampWrap .float handles parallax transform — separate elements, fine.
OK: also ensure `hero` id on section.
Time to write. I'll carefully produce final answer now. Length is fine.
One more check: `--rot` inline style on cards: I'm generating via template with style="--rot:...deg". compute rot values: `const rot = (i%2===0?1:-1)*(0.5+(i%3)*0.35);`
Add buttons `.btn.small` variant gold.
Menu heading area note: right side: "PRICES INCLUDE TAX · TIP JAR GOES TO SLUSH RESEARCH" small rotated sticker? Add small print under grid: note text.
Alright — final code:
Also ensure clicking ADD uses data attrs: name may contain quotes? Names have no quotes. OK.
For recAdd button: dataset updated on setLevel: recAdd.dataset.name = L.item; recAdd.dataset.price = L.price.
Add click handler shared:
```js
document.addEventListener('click',e=>{
const b=e.target.closest('[data-add]');
...
});
```
Simplify: give add buttons class 'add' with data-name data-price; delegation on document:
```js
document.addEventListener('click',e=>{
const btn=e.target.closest('.add');
if(!btn) return;
addToBag({name:btn.dataset.name, price:+btn.dataset.price});
btn.textContent='ADDED!';
setTimeout(()=>btn.textContent='ADD +',900);
});
```
Card add buttons generated with data-name="${m.name}" data-price.
Spice recAdd class 'add' too with updated dataset.
addToBag(name,price): bag array of {name, price, qty}.
Badge pop: bagCount.classList.remove('pop'); void bagCount.offsetWidth; add 'pop'.
renderBag(): if empty: bagList.innerHTML = `<li class="empty">Bag's empty. The menu is right up there — go make friends with it.</li>`; total $0.00.
Modal open: overlay.classList.add('open'); overlay.setAttribute('aria-hidden','false'); document.body style overflow hidden? optional; fine to add class no-scroll. Eh keep simple, overlay covers.
Esc handler global: if overlay open close.
Backdrop click: overlay addEventListener click e.target===overlay → close.
Newsletter shake class.
Now finalize the LEVELS array with mouth configs:
```js
const LEVELS=[
{name:'MILD CHILD', color:'#7FBE58', item:'KIMCHI QUESADILLA', price:9,
text:'Zero-heat entry point. Aged kimchi + mozzarella, easy on the crema. Friendly. Warm. A gateway taco.',
mouth:'M114 150 Q130 160 146 150', fill:false},
{name:'WARM & CURIOUS', color:'#E9B83E', item:'THE K-BBQ TACO', price:4.5,
text:'K-BBQ taco with the mellow slaw. Flavor walks in first; heat just waves from the doorway.',
mouth:'M110 148 Q130 166 150 148', fill:false},
{name:'GOCHU GANG', color:'#F1912B', item:'GOCHUJANG CRUNCH TACO', price:4.5,
text:'The regulars\' frequency. Double-crunch chicken, medium glaze. Napkins nearby. Pride intact.',
mouth:'M110 146 Q130 172 150 146 Q130 158 110 146 Z', fill:true},
{name:'FIRE-NOODLE CERTIFIED', color:'#EE5A23', item:'LOADED KIMCHI FRIES', price:10,
text:'You have eaten fire noodles on purpose. Extra queso, nori dust. You will be fine. Probably.',
mouth:'M130 156m-11 0a11 13 0 1 0 22 0a11 13 0 1 0-22 0', fill:true},
{name:'KIMCHI VETERAN', color:'#E0311F', item:'SEOUL STACK — EXTRA LOCO', price:11,
text:'Say "extra loco" at the window. We nod slowly, with deep, sincere respect. Bring a friend for backup.',
mouth:'M130 158m-13 0a13 15 0 1 0 26 0a13 15 0 1 0-26 0', fill:true},
{name:'PAIN LOCO', color:'#B31217', item:'THE PAIN LOCO (CHEF\'S DARE)', price:4.5,
text:'Tell us "make me cry." We whisper a prayer, sauce accordingly, and stage a Yuzu Melt for the aftermath.',
mouth:'M130 160m-17 0a17 18 0 1 0 34 0a17 18 0 1 0-34 0', fill:true},
];
```
setLevel(v) as planned. ticks: container with 6 spans; update active index.
Slider id spiceSlider; label aria.
Initial call setLevel(2).
Char svg ids set. Note svg elements need `document.getElementById` fine.
heatIndex update: `${v}/5`.
recName uses LEVELS[v].name.
Also blush cheeks visible always.
OK. Compose final. Also I must double check the SVG `<textPath>` requires xlink? Modern Chrome supports href. Good.
Also stamp circText: `<text class="circText"><textPath href="#cp" >KIMCHI LOCO × SEOUL × CDMX × GRAND OPENING × </textPath></text>` maybe repeat to fill 364px: text at 13px avg 8px/char: string length ~44 chars → 350. ok.
Now — the truck brand texts: class "tMain" fill cream font display size 22 letter-spacing 1; "tKr" gold 14; "tSign" ink 24 on gold; "tOpen" gold 18 ls 5; "tDeco" cream 11.
SVG text inherits body font; set classes in CSS:
```css
.tMain{font-family:var(--dFont);font-size:22px;fill:var(--cream);letter-spacing:1px}
.tKr{font-size:15px;fill:var(--gold);letter-spacing:2px}
.tSign{font-family:var(--dFont);font-size:26px;fill:var(--ink)}
.tOpen{font-family:var(--dFont);font-size:18px;fill:var(--gold);letter-spacing:6px}
.tSmall{font-size:11px;fill:var(--cream);letter-spacing:2px}
```
--dFont defined in :root as the display stack.
Windshield wiper line etc fine.
Let me make sure the truck svg viewBox height: sign top at y=30, shadow at 350+16=366, wheels bottom 338. viewBox "0 0 640 400"? Puffs rise to y≈10. Sign swing may push text slightly. viewBox 0 0 640 400 with shadow at 350? wheel bottom 338, shadow cy 348 ry 14 → bottom 362 <400 fine. Set viewBox 0 0 640 396.
Chimney puffs start cy≈44, animate -26 → 18, scale 1.3*11=14 → top 4. OK within.
Stamp badge: `<svg class="stamp" viewBox="0 0 160 160">`.
Also I should double check text on path orientation: starts at (80-58,80)=(22,80) going... circle path: "M80 80 m-58 0 a58 58 0 1 1 116 0 a58 58 0 1 1 -116 0" starts left point going clockwise (sweep 1) with large arc: top half first. Text reads along clockwise from left → at top reads left-to-right upright.
Inner rotor rotating whole group including text — good.
Add center chili in stamp: `<g transform="translate(80 82) scale(3.4) translate(-11 -13)"><path .../><path .../></g>` reusing icon paths scaled: body path spans ~x2..19,y4..22 → scale 3.4 → ~60px. Place translate(-11,-13) centers approx. fill: override classes: stamp chili body fill ink? On gold bg, red chili pops: body fill var(--red), stem green, stroke? Add stroke ink 0.8? The icon paths have no stroke; small at scale strokes thin — set stroke="#1C130E" stroke-width="1.5" on group? paths individually. I'll inline with strokes.
Fine.
Now WRITE. (Also include `aria-hidden="true"` on decorative svgs.)
Let me pre-count JS ids used and define carefully in HTML as I write.
Also `overlay` initial `aria-hidden="true"` and hidden via CSS; toggling aria in JS.
Potential issue: sticky nav + anchor scroll-margin: set `section{scroll-margin-top:96px}`.
Write final answer now. Keep everything consistent. Also verify no stray `</script>` in strings — none.
CSS var --dFont string: `--dFont:"Archivo Black","Arial Black",Impact,"Haettenschweiler",sans-serif;` Impact fallback ugly but Arial Black exists basically everywhere modern. Fine.
Also body base: `font-family:"Helvetica Neue",Helvetica,Arial,"Segoe UI",sans-serif;`
Numbers font in stats use dFont.
Write it!
Actually one more small thing: countdown pill pulse dot red pulsing — css ping animation reuse.
And `#cd` monospace-ish? use font-variant-numeric: tabular-nums.
Final answer below. I'll write the code in one block, aiming for correctness over brevity. Also double-check every `document.getElementById` string matches an id.
List of JS references to verify at end:
- menuGrid ✓ (create)
- bagCount ✓
- overlay ✓
- bagList, bagTotal ✓
- closeModal, clearBag, checkout, bagBtn ✓
- spiceSlider, heatIndex ✓
- chiliChar, cBody, mouth, tongue, tears, sweat, flames ✓
- recName, recItem, recText, recAdd ✓
- ticks ✓
- cd ✓
- statusMain, statusSub, liveDot ✓
- sched, pin, pinLabel ✓
- newsForm, newsInput ✓
- toasts ✓
- hero ✓
- .tTrack elements ✓
- .reveal/.num via querySelectorAll ✓
- navDot — I decided? status has liveDot; nav dot optional — skip navDot to reduce failure points. Skip.
Toast function with (title, sub).
Menu render then heat fill then delegation.
setLevel on slider input + init.
Schedule render + status + pin.
Countdown init + interval.
Reveal observer + counters.
Ticker fill — must run before reveal? unrelated.
Parallax.
Newsletter submit.
Esc key.
Let me also add focus-visible styles for range.
Write final.
During HTML writing, the awning stripes: 8 rects — I'll write them out.
Also door seam & handle.
Let me also add a tiny "menu prices include tax" microcopy.
Also grid `gap`.
Ready. Also mind apostrophes inside JS single-quoted strings — use escapes or double quotes. I'll use double quotes for strings containing apostrophes or template literals carefully.
In template literal HTML, attribute values with double quotes fine.
Careful: LEVELS text for level 2: "The regulars' frequency..." — put in double quotes.
Writing final code now, then done.