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
- 7 min 4 s
- Cost
- $0.210
- Output tokens
- 58,326
- Spent thinking
- 38,937 tokens
- Speed
- 137.7 tok/s
- Console errors
- 0
- First token after
- 390 ms
- First visible text after
- 5 min 20 s
- Prompt tokens
- 362
- Code size
- 57.2 KB
- Finish reason
- stop
- Served by
- InferenceNet
- Model id
z-ai/glm-5.3- Reasoning sent
{"effort":"high"}- 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 · 968 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kimchi Loco — Korean Tacos That Bite Back</title>
<style>
:root{
--cream:#F9EFD8; --paper:#FFF9EC; --ink:#221710;
--red:#E2452B; --red-deep:#A92E1B; --gold:#F3AE2A;
--green:#6F9A3B; --tan:#E7D6B4; --shadow:6px 6px 0 var(--ink);
--display:Impact,'Arial Black','Franklin Gothic Bold',Haettenschweiler,sans-serif;
--body:'Trebuchet MS','Segoe UI',Verdana,sans-serif;
--mono:'Courier New',Courier,monospace;
--script:'Segoe Print','Marker Felt','Bradley Hand','Comic Sans MS',cursive;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth;overflow-x:clip}
body{background:var(--cream);color:var(--ink);font-family:var(--body);overflow-x:clip;line-height:1.5}
/* paper grain */
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:999;opacity:.055;
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>")}
::selection{background:var(--red);color:var(--cream)}
img,svg{display:block;max-width:100%}
button{font-family:inherit;color:inherit;cursor:pointer}
section{scroll-margin-top:90px}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px}
h1,h2,h3,.dfont{font-family:var(--display);font-weight:900;text-transform:uppercase;letter-spacing:.015em}
.ic{width:18px;height:18px;stroke:var(--ink);fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;flex:none}
.overline{display:flex;align-items:center;gap:10px;font-family:var(--mono);font-weight:700;font-size:.82rem;letter-spacing:.18em;text-transform:uppercase}
.overline::before{content:"";width:34px;height:12px;background:var(--red);flex:none}
.sec-head{margin-bottom:26px}
.sec-head h2{font-size:clamp(2.1rem,4.6vw,3.4rem);line-height:.95;margin-top:10px}
.sec-head .sub{font-family:var(--mono);font-size:.85rem;margin-top:10px;color:rgba(34,23,16,.75);letter-spacing:.04em}
/* ---------- nav ---------- */
nav{position:sticky;top:0;z-index:50;background:var(--cream);border-bottom:3px solid var(--ink)}
.nav-in{max-width:1180px;margin:0 auto;padding:10px 24px;display:flex;align-items:center;gap:22px}
.logo{display:flex;align-items:center;gap:10px;font-family:var(--display);font-size:1.3rem;letter-spacing:.03em;text-decoration:none;color:var(--ink)}
.logo svg{width:34px;height:34px}
.nav-links{display:flex;gap:20px;margin-left:auto}
.nav-links a{font-family:var(--mono);font-weight:700;font-size:.8rem;letter-spacing:.12em;text-decoration:none;color:var(--ink);padding:4px 2px;border-bottom:3px solid transparent}
.nav-links a:hover{border-bottom-color:var(--red);color:var(--red-deep)}
.nav-cta{background:var(--red);color:var(--cream);border:3px solid var(--ink);box-shadow:3px 3px 0 var(--ink);
font-family:var(--display);letter-spacing:.06em;font-size:.85rem;padding:8px 16px;transition:transform .12s,box-shadow .12s}
.nav-cta:hover{transform:translate(-1px,-1px);box-shadow:4px 4px 0 var(--ink)}
/* ---------- hero ---------- */
.hero{position:relative;padding:56px 0 64px;overflow:clip}
.hero-grid{max-width:1180px;margin:0 auto;padding:0 24px;display:grid;grid-template-columns:1.12fr .88fr;gap:30px;align-items:center}
.hero h1{font-size:clamp(3.6rem,8.4vw,6.9rem);line-height:.88;margin:14px 0 18px}
.hero h1 .l1{display:block;text-shadow:5px 5px 0 var(--gold)}
.hero h1 .l2{display:inline-block;color:var(--red);text-shadow:5px 5px 0 var(--ink);transform:rotate(-2deg) translateX(6px)}
.lead{font-size:1.12rem;max-width:46ch;line-height:1.55}
.lead b{background:var(--red);color:var(--cream);padding:0 5px;font-weight:800}
.cta-row{display:flex;gap:14px;flex-wrap:wrap;margin-top:26px}
.btn{display:inline-flex;align-items:center;gap:10px;border:3px solid var(--ink);padding:14px 22px;
font-family:var(--display);font-size:1.05rem;letter-spacing:.05em;text-decoration:none;
transition:transform .12s,box-shadow .12s}
.btn.red{background:var(--red);color:var(--cream);box-shadow:5px 5px 0 var(--ink)}
.btn.ghost{background:var(--paper);color:var(--ink);box-shadow:5px 5px 0 var(--gold)}
.btn:hover{transform:translate(-2px,-2px)}
.btn:active{transform:translate(2px,2px);box-shadow:2px 2px 0 var(--ink)}
.hero-meta{display:flex;align-items:center;gap:22px;flex-wrap:wrap;margin-top:26px;font-family:var(--mono);font-weight:700;font-size:.82rem;letter-spacing:.05em}
.hero-meta span{display:flex;align-items:center;gap:7px}
.rating{display:inline-flex;align-items:center;gap:8px;margin-top:16px;background:var(--paper);border:2px solid var(--ink);
padding:7px 12px;font-family:var(--mono);font-weight:700;font-size:.78rem;box-shadow:3px 3px 0 var(--ink)}
.rating .star{width:14px;height:14px;fill:var(--red);stroke:var(--ink);stroke-width:1.5}
.hero-art{position:relative;display:flex;justify-content:center}
.taco-svg{width:min(480px,100%);animation:rock 6s ease-in-out infinite alternate;transform-origin:50% 92%}
@keyframes rock{from{transform:rotate(-1.6deg)}to{transform:rotate(1.6deg)}}
.steam path{stroke:var(--ink);stroke-width:6;fill:none;stroke-linecap:round;opacity:0;animation:rise 3s ease-in-out infinite}
.steam .s2{animation-delay:.9s}.steam .s3{animation-delay:1.7s}
@keyframes rise{0%{opacity:0;transform:translateY(10px)}35%{opacity:.5}70%{opacity:.35}100%{opacity:0;transform:translateY(-22px)}}
.sticker{position:absolute;font-family:var(--display);font-size:.8rem;letter-spacing:.05em;text-align:center;
border:3px solid var(--ink);padding:9px 13px;box-shadow:3px 3px 0 var(--ink);z-index:2}
.st1{background:var(--gold);top:4%;left:-2%;transform:rotate(-8deg);animation:float1 5s ease-in-out infinite alternate}
.st2{background:var(--paper);right:-4%;top:42%;transform:rotate(6deg);animation:float2 6s ease-in-out infinite alternate}
.st3{background:var(--red);color:var(--cream);bottom:6%;left:2%;transform:rotate(-5deg);animation:float1 7s ease-in-out infinite alternate-reverse}
@keyframes float1{to{translate:0 -10px}}
@keyframes float2{to{translate:0 12px}}
.open-badge{position:absolute;top:-16px;right:2%;width:132px;height:132px;z-index:3}
.open-badge .ringlayer{transform-origin:50% 50%;animation:spin 18s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.ob-bg{fill:var(--red)}
.ob-ring{fill:none;stroke:var(--cream);stroke-width:2.5;stroke-dasharray:5 7}
.ob-circ{fill:none;stroke:var(--cream);stroke-width:2}
.ob-ringtxt{fill:var(--cream);font-family:var(--mono);font-weight:700;font-size:11.5px;letter-spacing:3.5px}
.ob-c{fill:var(--cream);font-family:var(--display);font-size:17px;text-anchor:middle;letter-spacing:1px}
/* ---------- marquee ---------- */
.marquee{background:var(--ink);border-top:3px solid var(--ink);border-bottom:3px solid var(--ink);overflow:hidden}
.mq-track{display:flex;width:max-content;animation:mq 30s linear infinite}
.marquee:hover .mq-track{animation-play-state:paused}
@keyframes mq{to{transform:translateX(-50%)}}
.mq-chunk{display:flex;align-items:center;gap:34px;padding:13px 17px}
.mq-chunk span{font-family:var(--display);color:var(--cream);font-size:1.02rem;letter-spacing:.06em;white-space:nowrap}
.mq-chunk .ic{stroke:var(--red);fill:var(--red);width:15px;height:15px;stroke-width:1.5}
/* ---------- story ---------- */
.story{background:var(--ink);color:var(--cream);padding:76px 0 0;position:relative;overflow:clip}
.story .watermark{position:absolute;right:-30px;top:-24px;font-family:var(--display);font-size:clamp(6rem,16vw,13rem);
color:transparent;-webkit-text-stroke:2px rgba(249,239,216,.12);letter-spacing:.1em;pointer-events:none;line-height:1}
.story-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:56px;position:relative}
.story .overline{color:var(--gold)}
.story .overline::before{background:var(--gold)}
.story h2{font-size:clamp(2.4rem,5vw,4rem);line-height:.95;margin:14px 0 20px}
.story h2 em{font-style:normal;color:var(--red);-webkit-text-stroke:0}
.story p{font-size:1.06rem;line-height:1.7;max-width:56ch;color:rgba(249,239,216,.92)}
.story mark{background:var(--red);color:var(--cream);padding:1px 5px;font-weight:800}
.steps{list-style:none}
.steps li{display:flex;gap:18px;padding:15px 0;border-top:2px dashed rgba(249,239,216,.3)}
.steps .num{font-family:var(--display);font-size:1.5rem;color:var(--gold);flex:none;width:52px}
.steps b{font-family:var(--display);letter-spacing:.05em;font-size:1.02rem;display:block;margin-bottom:3px}
.steps p{font-size:.92rem;color:rgba(249,239,216,.72)}
.stats{display:flex;flex-wrap:wrap;gap:0;border-top:3px solid var(--cream);margin-top:56px}
.stat{flex:1 1 auto;padding:20px 28px;display:flex;align-items:baseline;gap:12px}
.stat+.stat{border-left:3px dashed rgba(249,239,216,.35)}
.stat b{font-family:var(--display);font-size:clamp(1.7rem,3vw,2.5rem);color:var(--red);-webkit-text-stroke:1px var(--cream)}
.stat span{font-family:var(--mono);font-weight:700;font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;color:rgba(249,239,216,.8)}
/* ---------- menu ---------- */
.menu-sec{padding:80px 0 90px}
.board-zone{max-width:1040px;margin:0 auto;padding:0 24px}
.awning{height:46px;display:block;margin:0 -16px;position:relative;z-index:2}
.aw-r{fill:var(--red)}.aw-c{fill:var(--cream)}
.aw-line{stroke:var(--ink);stroke-width:3}
.aw-scalp{stroke:var(--ink);stroke-width:3}
.board{background:var(--paper);border:3px solid var(--ink);box-shadow:10px 10px 0 var(--ink);padding:30px 34px 34px}
.board-top{display:flex;flex-wrap:wrap;align-items:baseline;gap:12px;justify-content:space-between;border-bottom:3px solid var(--ink);padding-bottom:14px}
.board-top h3{font-size:clamp(1.6rem,3vw,2.3rem)}
.board-top .date{font-family:var(--mono);font-weight:700;font-size:.82rem;letter-spacing:.1em;background:var(--ink);color:var(--cream);padding:5px 10px}
.tabs{display:flex;gap:10px;flex-wrap:wrap;padding:18px 0 6px}
.tab{border:3px solid var(--ink);background:var(--paper);font-family:var(--display);font-size:.92rem;letter-spacing:.05em;padding:8px 16px;transition:transform .1s}
.tab:hover{transform:translateY(-2px)}
.tab.active{background:var(--red);color:var(--cream);box-shadow:3px 3px 0 var(--ink)}
.menu-panel{display:none}
.menu-panel.show{display:block}
.dishes{list-style:none;columns:2;column-gap:44px;padding-top:10px}
.dish{position:relative;break-inside:avoid;padding:15px 2px;border-bottom:3px dotted rgba(34,23,16,.28)}
.dish-line{display:flex;align-items:baseline;gap:9px}
.dish h4{font-family:var(--display);font-size:1.22rem;letter-spacing:.02em}
.dish:hover h4{color:var(--red-deep)}
.leader{flex:1;min-width:16px;border-bottom:3px dotted rgba(34,23,16,.35);transform:translateY(-5px)}
.price{font-family:var(--display);font-size:1.1rem;color:var(--red-deep)}
.heat{display:flex;gap:2px;align-self:center}
.fl{width:14px;height:14px;fill:none;stroke:var(--ink);stroke-width:1.8;stroke-linejoin:round;opacity:.28}
.fl.on{fill:var(--red);stroke:var(--ink);opacity:1}
.add{flex:none;width:32px;height:32px;border:3px solid var(--ink);background:var(--paper);display:grid;place-items:center;align-self:center;transition:.12s}
.add .ic{width:15px;height:15px}
.add:hover{background:var(--red);transform:rotate(90deg)}
.add:hover .ic{stroke:var(--cream)}
.dish p{margin-top:5px;font-size:.93rem;line-height:1.45;color:rgba(34,23,16,.82);max-width:50ch;padding-right:40px}
.fave{position:absolute;top:10px;right:44px;background:var(--gold);border:2px solid var(--ink);font-family:var(--display);
font-size:.6rem;letter-spacing:.08em;padding:3px 8px;transform:rotate(6deg);box-shadow:2px 2px 0 var(--ink)}
.board-note{margin-top:22px;text-align:center;font-family:var(--mono);font-weight:700;font-size:.8rem;letter-spacing:.12em;color:var(--red-deep)}
.loco-meter{margin-top:24px;border-top:3px dashed var(--ink);padding-top:20px;display:grid;grid-template-columns:1fr auto;gap:8px 20px;align-items:center;transition:background .3s}
.lm-title{display:flex;align-items:center;gap:9px;font-family:var(--display);font-size:1.15rem;letter-spacing:.05em}
.lm-title .fl{width:20px;height:20px;opacity:1}
.lm-read{display:flex;align-items:center;gap:10px}
.lm-flames{display:flex;gap:4px}
.lmfl{width:24px;height:24px;fill:none;stroke:var(--ink);stroke-width:1.8;opacity:.22;transition:.2s}
.lmfl.on{fill:var(--red);opacity:1}
.lm-num{font-family:var(--display);font-size:2rem;color:var(--red-deep)}
.lm-max{font-family:var(--display);font-size:1.1rem;opacity:.5}
.lm-label{grid-column:1/-1;font-family:var(--mono);font-weight:700;font-size:.86rem;letter-spacing:.04em}
input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:34px;background:transparent;cursor:pointer;grid-column:1/-1}
input[type=range]::-webkit-slider-runnable-track{height:6px;background:var(--ink);border-radius:0}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:26px;height:26px;background:var(--red);border:3px solid var(--ink);margin-top:-10px;box-shadow:2px 2px 0 var(--ink)}
input[type=range]::-moz-range-track{height:6px;background:var(--ink)}
input[type=range]::-moz-range-thumb{width:22px;height:22px;background:var(--red);border:3px solid var(--ink);border-radius:0}
.loco-meter.meltdown{background:rgba(226,69,43,.08)}
.loco-meter.meltdown .lm-label{color:var(--red-deep);animation:shake .4s infinite}
@keyframes shake{25%{transform:translateX(-3px) rotate(-.4deg)}75%{transform:translateX(3px) rotate(.4deg)}}
/* ---------- find ---------- */
.find{padding:80px 0 90px;background:var(--paper);border-top:3px solid var(--ink)}
.day-row{display:flex;flex-wrap:wrap;gap:12px;margin:22px 0 30px}
.day{position:relative;border:3px solid var(--ink);background:var(--cream);min-width:112px;padding:12px 10px 10px;text-align:center;box-shadow:4px 4px 0 var(--ink);transition:transform .12s}
.day:hover{transform:translate(-2px,-2px)}
.day b{display:block;font-family:var(--display);font-size:1.25rem;letter-spacing:.05em}
.day span{font-family:var(--mono);font-weight:700;font-size:.72rem;letter-spacing:.04em}
.day.on{background:var(--red);color:var(--cream)}
.day.closed{background:var(--cream);border-style:dashed;opacity:.85}
.day .today{position:absolute;top:-11px;left:50%;transform:translateX(-50%) rotate(-4deg);background:var(--gold);border:2px solid var(--ink);
font-family:var(--display);font-size:.55rem;letter-spacing:.1em;padding:2px 6px}
.find-grid{display:grid;grid-template-columns:2.6fr 1fr;gap:26px;align-items:stretch}
.map-panel{border:3px solid var(--ink);background:var(--paper);box-shadow:10px 10px 0 var(--ink);padding:10px}
.map-svg{width:100%;height:auto}
.map-bg{fill:var(--paper)}
.st{stroke:var(--tan);stroke-width:16;fill:none}
.stc{stroke:var(--paper);stroke-width:2;stroke-dasharray:10 12;fill:none}
.park{fill:#A9C46C;stroke:var(--ink);stroke-width:3}
.tree{fill:var(--green);stroke:var(--ink);stroke-width:3}
.bldg{fill:var(--cream);stroke:var(--ink);stroke-width:3}
.route{stroke:var(--red);stroke-width:6;stroke-dasharray:.5 13;stroke-linecap:round;fill:none}
.map-lbl{font-family:var(--mono);font-weight:700;font-size:11px;letter-spacing:3px;fill:var(--ink);opacity:.45}
.map-stop circle.dot{fill:var(--ink)}
.map-stop circle.ring{fill:none;stroke:var(--red);stroke-width:4;r:21;opacity:0;transition:opacity .3s}
.map-stop.on circle.ring{opacity:1}
.map-stop text{fill:var(--paper);font-family:var(--display);font-size:13px;text-anchor:middle;dominant-baseline:central}
.compass circle{fill:var(--cream);stroke:var(--ink);stroke-width:2.5}
.compass path{stroke:var(--ink);stroke-width:2.5;fill:none}
.compass text{fill:var(--ink);font-family:var(--display);font-size:11px;text-anchor:middle}
#truck{transition:opacity .3s}
#truck.rest{opacity:.35}
.stop-info{border:3px solid var(--ink);background:var(--cream);box-shadow:8px 8px 0 var(--ink);padding:22px;display:flex;flex-direction:column;gap:6px}
.si-day{font-family:var(--display);font-size:2.4rem;color:var(--red);line-height:1}
.si-name{font-family:var(--display);font-size:1.08rem;letter-spacing:.03em;line-height:1.2}
.si-time{font-family:var(--mono);font-weight:700;font-size:.85rem;letter-spacing:.06em}
.si-note{font-size:.92rem;line-height:1.5;color:rgba(34,23,16,.8);border-top:2px dashed rgba(34,23,16,.3);padding-top:10px;margin-top:6px}
.si-today{font-family:var(--mono);font-weight:700;font-size:.78rem;color:var(--red-deep);min-height:1.1em}
.legend{list-style:none;margin-top:auto;padding-top:14px;border-top:3px dotted rgba(34,23,16,.3);display:flex;flex-direction:column;gap:7px}
.legend li{display:flex;align-items:center;gap:10px;font-family:var(--mono);font-weight:700;font-size:.72rem;letter-spacing:.05em}
.lg-route{width:34px;border-top:5px dotted var(--red)}
.lg-stop{width:22px;height:22px;border-radius:50%;background:var(--ink);color:var(--paper);display:grid;place-items:center;font-size:11px;font-family:var(--display)}
.legend .ic{width:22px;height:22px}
.map-hint{font-family:var(--mono);font-weight:700;font-size:.8rem;letter-spacing:.06em;color:rgba(34,23,16,.7)}
/* ---------- street ---------- */
.street{background:var(--gold);border-top:3px solid var(--ink);border-bottom:3px solid var(--ink);padding:76px 0 96px}
.pquote{text-align:center;max-width:820px;margin:0 auto 46px}
.pquote .stars{display:flex;justify-content:center;gap:6px;margin-bottom:14px}
.pquote .star{width:26px;height:26px;fill:var(--red);stroke:var(--ink);stroke-width:1.4}
.pquote blockquote{font-family:var(--display);font-size:clamp(1.7rem,3.6vw,2.8rem);line-height:1.05}
.pquote cite{display:block;margin-top:12px;font-family:var(--mono);font-weight:700;font-style:normal;font-size:.82rem;letter-spacing:.14em}
.notes{display:flex;flex-wrap:wrap;justify-content:center;gap:30px}
.note{position:relative;background:var(--paper);border:3px solid var(--ink);box-shadow:7px 7px 0 var(--ink);padding:28px 26px 22px;width:min(330px,100%)}
.note::before{content:"";position:absolute;top:-14px;left:50%;width:96px;height:26px;background:rgba(255,255,255,.55);
border:1px solid rgba(34,23,16,.18);transform:translateX(-50%) rotate(-3deg)}
.note p{font-family:var(--script);font-size:1.12rem;line-height:1.5}
.note cite{display:block;margin-top:14px;font-family:var(--mono);font-weight:700;font-style:normal;font-size:.72rem;letter-spacing:.12em;color:var(--red-deep)}
.note:nth-child(1){transform:rotate(-3deg) translateY(12px)}
.note:nth-child(2){transform:rotate(2deg)}
.note:nth-child(3){transform:rotate(-1.5deg) translateY(18px)}
/* ---------- cta + footer ---------- */
.cta{background:var(--red);border-bottom:3px solid var(--ink);padding:78px 0;text-align:center}
.cta h2{font-size:clamp(2.6rem,7vw,5.4rem);line-height:.92;color:var(--cream);text-shadow:5px 5px 0 var(--ink)}
.cta p{color:var(--cream);font-family:var(--mono);font-weight:700;font-size:.85rem;letter-spacing:.1em;margin:18px 0 26px}
.news{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;max-width:560px;margin:0 auto;padding:0 20px}
.news input{flex:1 1 240px;border:3px solid var(--ink);background:var(--paper);padding:13px 15px;font-family:var(--mono);font-weight:700;font-size:.9rem;box-shadow:4px 4px 0 var(--ink)}
.news input:focus{outline:3px solid var(--gold);outline-offset:2px}
.news button{border:3px solid var(--ink);background:var(--ink);color:var(--cream);font-family:var(--display);font-size:1rem;letter-spacing:.05em;padding:13px 22px;box-shadow:4px 4px 0 var(--gold);transition:transform .12s}
.news button:hover{transform:translate(-2px,-2px)}
.status{min-height:1.4em;margin-top:16px;font-family:var(--mono);font-weight:700;font-size:.85rem;color:var(--cream)}
.status.ok{color:#FFE9B8}.status.warn{color:var(--paper);background:var(--ink);display:inline-block;padding:3px 10px}
footer{background:var(--ink);color:var(--cream);padding:60px 0 0}
.foot-brand{font-family:var(--display);font-size:clamp(2.4rem,7vw,5rem);line-height:.95;color:transparent;
-webkit-text-stroke:2px rgba(249,239,216,.4);text-align:center;letter-spacing:.04em}
.foot-grid{max-width:1180px;margin:44px auto 0;padding:36px 24px 30px;border-top:2px dashed rgba(249,239,216,.3);display:flex;flex-wrap:wrap;gap:34px 60px;justify-content:space-between}
.foot-col h5{font-family:var(--display);letter-spacing:.1em;color:var(--gold);font-size:.85rem;margin-bottom:10px}
.foot-col p{font-family:var(--mono);font-size:.8rem;line-height:1.8;color:rgba(249,239,216,.8)}
.foot-bottom{border-top:2px dashed rgba(249,239,216,.3);padding:16px 24px;text-align:center;font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;color:rgba(249,239,216,.55)}
/* ---------- order ticket ---------- */
.ticket-widget{position:fixed;right:18px;bottom:18px;z-index:80;display:flex;flex-direction:column;align-items:flex-end;gap:10px}
.ticket-pill{display:flex;align-items:center;gap:9px;background:var(--ink);color:var(--cream);border:3px solid var(--ink);
box-shadow:4px 4px 0 var(--red);font-family:var(--mono);font-weight:700;font-size:.82rem;padding:11px 15px}
.ticket-pill .ic{stroke:var(--cream);width:19px;height:19px}
.ticket-pill:hover{transform:translate(-1px,-1px);box-shadow:5px 5px 0 var(--red)}
.ticket-pill.bump{animation:bump .4s}
@keyframes bump{40%{transform:scale(1.14) rotate(-3deg)}}
.ticket-panel{width:340px;max-width:calc(100vw - 28px);background:var(--paper);border:3px solid var(--ink);box-shadow:8px 8px 0 var(--ink);font-family:var(--mono)}
.ticket-panel[hidden]{display:none}
.tk-head{padding:12px 14px;border-bottom:3px dashed var(--ink);text-align:center;font-family:var(--display);letter-spacing:.08em;font-size:.95rem}
.tk-head small{display:block;font-family:var(--mono);font-weight:700;font-size:.68rem;letter-spacing:.14em;margin-top:3px;color:var(--red-deep)}
#tkItems{list-style:none;max-height:36vh;overflow:auto;padding:8px 14px}
#tkItems li{display:flex;align-items:center;gap:7px;padding:7px 0;border-bottom:1px dashed rgba(34,23,16,.25);font-size:.82rem}
#tkItems .nm{flex:1;line-height:1.25}
#tkItems .hint{justify-content:center;color:rgba(34,23,16,.55);border:none;font-size:.78rem}
#tkItems .confirmed-line{display:block;border:none;text-align:center;font-weight:700;letter-spacing:.05em;line-height:1.8;color:var(--red-deep);padding:14px 0}
.q{width:24px;height:24px;border:2px solid var(--ink);background:var(--paper);font-weight:700;line-height:1;flex:none}
.q:hover{background:var(--gold)}
.tk-foot{padding:12px 14px 14px;border-top:3px dashed var(--ink)}
.tk-total{display:flex;font-family:var(--display);font-size:1.1rem;margin-bottom:10px}
.tk-total .leader{border-bottom-width:4px}
#callIn{width:100%;border:3px solid var(--ink);background:var(--red);color:var(--cream);font-family:var(--display);letter-spacing:.08em;font-size:1rem;padding:11px;box-shadow:3px 3px 0 var(--ink)}
#callIn:hover{transform:translate(-1px,-1px);box-shadow:4px 4px 0 var(--ink)}
#callIn:disabled{opacity:.4;cursor:default}
.tk-note{margin-top:10px;text-align:center;font-size:.68rem;letter-spacing:.05em;color:rgba(34,23,16,.6)}
.tk-note.warn{color:var(--red-deep);font-weight:700}
/* ---------- responsive ---------- */
@media (max-width:960px){
.hero-grid{grid-template-columns:1fr}
.hero-art{order:-1;margin-top:-10px}
.taco-svg{width:min(360px,80vw)}
.open-badge{width:96px;height:96px;top:-4px;right:4%}
.story-grid{grid-template-columns:1fr;gap:30px}
.find-grid{grid-template-columns:1fr}
.dishes{columns:1}
}
@media (max-width:760px){
.nav-links{display:none}
.nav-cta{margin-left:auto}
.board{padding:22px 18px 26px}
.st3{display:none}
.sticker{font-size:.7rem;padding:7px 10px}
.hero{padding:34px 0 48px}
.notes{gap:40px 22px}
.note:nth-child(odd){transform:rotate(-2.5deg) translateY(6px)}
.note:nth-child(even){transform:rotate(1.5deg) translateY(14px)}
.map-panel{padding:6px}
.stat{padding:14px 16px}
}
@media (prefers-reduced-motion:reduce){
.mq-track,.taco-svg,.st1,.st2,.st3,.open-badge .ringlayer,.steam path{animation:none!important}
.steam path{display:none}
}
</style>
</head>
<body>
<!-- ============ icon symbols ============ -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="i-flame" viewBox="0 0 24 24">
<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>
</symbol>
<symbol id="i-star" viewBox="0 0 24 24">
<path d="M12 2l2.9 6.3 6.6 1-4.75 4.5 1.15 6.6L12 17.1l-5.9 3.3 1.15-6.6L2.5 9.3l6.6-1L12 2z"/>
</symbol>
<symbol id="i-pin" viewBox="0 0 24 24">
<path d="M12 21s-7-5.1-7-11a7 7 0 1 1 14 0c0 5.9-7 11-7 11z"/><circle cx="12" cy="10" r="2.6"/>
</symbol>
<symbol id="i-clock" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3.5 2"/>
</symbol>
<symbol id="i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></symbol>
<symbol id="i-arrow" viewBox="0 0 24 24"><path d="M4 12h15m-6-6 6 6-6 6"/></symbol>
<symbol id="i-receipt" viewBox="0 0 24 24">
<path d="M5 3h14v18l-2.3-1.6-2.3 1.6L12 20l-2.4 1.6-2.3-1.6L5 21z"/><path d="M9 8h6M9 11.5h4"/>
</symbol>
<symbol id="i-truck" viewBox="0 0 24 24">
<path d="M3 6h10v9H3z"/><path d="M13 8h4.5L21 12.2V15h-8z"/><circle cx="7" cy="16.5" r="2"/><circle cx="16.5" cy="16.5" r="2"/>
</symbol>
<symbol id="i-taco" viewBox="0 0 24 24">
<path d="M2.5 15a9.5 7.5 0 0 0 19 0z"/><path d="M5 11.5c1-3 3.5-4.5 5.5-3.5.5-2.5 3-4 5.5-2.5 1.5-1.5 4-1 5 1"/>
</symbol>
</svg>
<!-- ============ nav ============ -->
<nav>
<div class="nav-in">
<a class="logo" href="#top" aria-label="Kimchi Loco home">
<svg viewBox="0 0 24 24" class="ic"><use href="#i-taco"/></svg>
KIMCHI LOCO
</a>
<div class="nav-links">
<a href="#story">THE DEAL</a>
<a href="#menu">THE BOARD</a>
<a href="#find">FIND THE TRUCK</a>
<a href="#street">WORD ON THE STREET</a>
</div>
<a href="#menu" class="nav-cta">ORDER AHEAD</a>
</div>
</nav>
<!-- ============ hero ============ -->
<header class="hero" id="top">
<div class="hero-grid">
<div class="hero-copy">
<div class="overline">김치 로코 · Korean BBQ × Street Tacos</div>
<h1><span class="l1">KIMCHI</span><span class="l2">LOCO</span></h1>
<p class="lead">Bulgogi by way of the barrio. <b>Kimchi that bites back.</b> Tortillas pressed while you watch — and a line worth standing in.</p>
<div class="cta-row">
<a class="btn red" href="#menu">SEE THE BOARD <svg class="ic" style="stroke:var(--cream)"><use href="#i-arrow"/></svg></a>
<a class="btn ghost" href="#find"><svg class="ic"><use href="#i-pin"/></svg> FIND THE TRUCK</a>
</div>
<div class="rating">
<svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg>
4.9 — from 1,283 hangry locals
</div>
<div class="hero-meta">
<span><svg class="ic" style="stroke:var(--red)"><use href="#i-pin"/></svg> 3RD & VERMONT, K-TOWN</span>
<span><svg class="ic" style="stroke:var(--red)"><use href="#i-clock"/></svg> TUE–SUN · 11AM–LATE</span>
</div>
</div>
<div class="hero-art">
<svg class="open-badge" viewBox="0 0 140 140" aria-hidden="true">
<defs><path id="obc" d="M70 70m-52 0a52 52 0 1 1 104 0a52 52 0 1 1 -104 0"/></defs>
<circle class="ob-bg" cx="70" cy="70" r="66"/>
<g class="ringlayer">
<circle class="ob-ring" cx="70" cy="70" r="52"/>
<text class="ob-ringtxt"><textPath href="#obc">· OPEN · OPEN · TUE–SUN · 11AM–LATE · OPEN ·</textPath></text>
</g>
<text class="ob-c" x="70" y="63">TUE–SUN</text>
<text class="ob-c" x="70" y="83">11–LATE</text>
</svg>
<div class="sticker st1">48-HR<br>BULGOGI</div>
<div class="sticker st2">PRESSED<br>DAILY</div>
<div class="sticker st3">HOT &<br>FRESH</div>
<svg class="taco-svg" viewBox="0 0 420 400" aria-hidden="true">
<g class="steam">
<path class="s1" d="M150 158c-14-14 14-22 2-44"/>
<path class="s2" d="M205 150c-16-16 16-24 2-48"/>
<path class="s3" d="M262 158c-14-14 14-22 2-44"/>
</g>
<!-- kimchi slaw mound -->
<path d="M58 262 C40 210 62 178 92 186 C100 156 140 148 156 170 C170 142 216 140 230 166 C250 136 292 146 300 176 C328 162 358 186 352 224 L360 262 Z"
fill="#7FA23B" stroke="#221710" stroke-width="7" stroke-linejoin="round"/>
<!-- red cabbage flecks -->
<circle cx="118" cy="196" r="7" fill="#E2452B" stroke="#221710" stroke-width="4"/>
<circle cx="196" cy="172" r="6" fill="#E2452B" stroke="#221710" stroke-width="4"/>
<circle cx="268" cy="184" r="7" fill="#E2452B" stroke="#221710" stroke-width="4"/>
<circle cx="322" cy="206" r="5" fill="#E2452B" stroke="#221710" stroke-width="4"/>
<!-- meat chunks -->
<path d="M92 252 C86 218 108 198 132 208 C138 188 166 184 176 202 C190 182 220 184 228 204 C250 186 278 194 280 216 C304 208 322 226 316 252 Z"
fill="#9C5B2E" stroke="#221710" stroke-width="6" stroke-linejoin="round"/>
<path d="M136 216c8-6 18-6 26 0M214 212c8-6 18-5 26 1" fill="none" stroke="#221710" stroke-width="4" stroke-linecap="round"/>
<!-- sesame seeds -->
<ellipse cx="120" cy="182" rx="5" ry="3" fill="#FFF9EC" transform="rotate(-18 120 182)"/>
<ellipse cx="178" cy="160" rx="5" ry="3" fill="#FFF9EC" transform="rotate(14 178 160)"/>
<ellipse cx="238" cy="152" rx="5" ry="3" fill="#FFF9EC" transform="rotate(-8 238 152)"/>
<ellipse cx="296" cy="168" rx="5" ry="3" fill="#FFF9EC" transform="rotate(20 296 168)"/>
<!-- gochujang drizzle -->
<path d="M96 176c30-22 60 8 92-10s64 12 96-8 40 4 56-8" fill="none" stroke="#221710" stroke-width="11" stroke-linecap="round"/>
<path d="M96 176c30-22 60 8 92-10s64 12 96-8 40 4 56-8" fill="none" stroke="#E2452B" stroke-width="6" stroke-linecap="round"/>
<!-- lime -->
<g>
<circle cx="352" cy="202" r="30" fill="#B9D34A" stroke="#221710" stroke-width="7"/>
<path d="M352 174v56M324 202h56M332 182l40 40M372 182l-40 40" stroke="#221710" stroke-width="4"/>
</g>
<!-- shell -->
<path d="M38 232 A172 140 0 0 0 382 232 Z" fill="#F3AE2A" stroke="#221710" stroke-width="8" stroke-linejoin="round"/>
<path d="M66 244 A148 116 0 0 0 354 244" fill="none" stroke="#221710" stroke-width="4" opacity=".5"/>
<!-- face -->
<path d="M152 298c8-10 20-10 28 0" fill="none" stroke="#221710" stroke-width="7" stroke-linecap="round"/>
<path d="M242 298c8-10 20-10 28 0" fill="none" stroke="#221710" stroke-width="7" stroke-linecap="round"/>
<path d="M186 322c14 14 34 14 48 0" fill="none" stroke="#221710" stroke-width="7" stroke-linecap="round"/>
<circle cx="136" cy="320" r="10" fill="#E2452B" opacity=".5"/>
<circle cx="286" cy="320" r="10" fill="#E2452B" opacity=".5"/>
<!-- ground shadow -->
<ellipse cx="210" cy="380" rx="182" ry="13" fill="#221710" opacity=".12"/>
</svg>
</div>
</div>
</header>
<!-- ============ marquee ============ -->
<div class="marquee" aria-hidden="true">
<div class="mq-track" id="mqTrack">
<div class="mq-chunk">
<span>48-HOUR BULGOGI TACOS</span><svg class="ic"><use href="#i-flame"/></svg>
<span>KIMCHI, FERMENTED DAY 12</span><svg class="ic"><use href="#i-flame"/></svg>
<span>MISUGARU HORCHATA</span><svg class="ic"><use href="#i-flame"/></svg>
<span>TORTILLAS PRESSED TO ORDER</span><svg class="ic"><use href="#i-flame"/></svg>
<span>GOCHUJANG ON EVERYTHING</span><svg class="ic"><use href="#i-flame"/></svg>
<span>ELOTE LOCO — CHARRED & DANGEROUS</span><svg class="ic"><use href="#i-flame"/></svg>
<span>SOLD OUT 14 NIGHTS RUNNING</span><svg class="ic"><use href="#i-flame"/></svg>
<span>FRIED EGG? OBVIOUSLY.</span><svg class="ic"><use href="#i-flame"/></svg>
</div>
</div>
</div>
<!-- ============ story ============ -->
<section class="story" id="story">
<div class="watermark">김치</div>
<div class="wrap">
<div class="story-grid">
<div>
<div class="overline">The deal</div>
<h2>SEOUL, MEET<br>THE <em>TAQUERÍA.</em></h2>
<p>Kimchi Loco started with one loud opinion: <mark>bulgogi belongs in a tortilla.</mark> We marinate short ribs for 48 hours in gochujang, pear and toasted sesame, ferment our own kimchi until it has real attitude, and press every tortilla while you watch. Then we stack it all with sesame crema, pickled daikon and lime, and hand it over the window still hissing.</p>
<p style="margin-top:14px">This isn't fusion for fusion's sake — it's the taco we always wanted to eat. <mark>Come argue with us.</mark></p>
</div>
<ol class="steps">
<li><span class="num">01</span><div><b>MARINATE</b><p>Gochujang, Asian pear, garlic. Forty-eight hours. No shortcuts, no mercy.</p></div></li>
<li><span class="num">02</span><div><b>FERMENT</b><p>House napa kimchi — day 12 is the sweet spot. Batches are numbered. Bragging encouraged.</p></div></li>
<li><span class="num">03</span><div><b>PRESS</b><p>Tortillas off the plancha in under a minute. They never sit around. Neither should you.</p></div></li>
<li><span class="num">04</span><div><b>STACK</b><p>Slaw, crema, daikon, sesame. A taco with genuine structural integrity.</p></div></li>
<li><span class="num">05</span><div><b>DRIZZLE</b><p>Gochujang last, lime always. Arguments handled at the window.</p></div></li>
</ol>
</div>
<div class="stats">
<div class="stat"><b>48</b><span>hrs in the marinade</span></div>
<div class="stat"><b>12</b><span>days in the ferment</span></div>
<div class="stat"><b>90</b><span>sec, dough to tortilla</span></div>
<div class="stat"><b>0</b><span>regrets so far</span></div>
</div>
</div>
</section>
<!-- ============ menu ============ -->
<section class="menu-sec" id="menu">
<div class="board-zone">
<div class="sec-head">
<div class="overline">Straight off the plancha</div>
<h2>THE BOARD</h2>
<div class="sub">tacos are 5" and proud of it · gluten-free tortillas, just ask · everything else, you're on your own</div>
</div>
<svg class="awning" height="46" aria-hidden="true">
<defs>
<pattern id="awn" width="56" height="46" patternUnits="userSpaceOnUse">
<rect class="aw-r" x="0" y="0" width="28" height="26"/>
<rect class="aw-c" x="28" y="0" width="28" height="26"/>
<path class="aw-scalp aw-r" d="M0 26a14 14 0 0 0 28 0z"/>
<path class="aw-scalp aw-c" d="M28 26a14 14 0 0 0 28 0z"/>
<path class="aw-line" d="M0 0V26M28 0V26" fill="none"/>
</pattern>
</defs>
<rect width="100%" height="46" fill="url(#awn)"/>
<rect x="1.5" y="1.5" width="calc(100% - 3px)" height="44.5" fill="none" stroke="#221710" stroke-width="3"/>
</svg>
<div class="board">
<div class="board-top">
<h3>Today's board</h3>
<span class="date" id="boardDate">TODAY</span>
</div>
<div class="tabs" role="tablist">
<button class="tab active" data-panel="p-tacos">TACOS</button>
<button class="tab" data-panel="p-big">BIG PLATES</button>
<button class="tab" data-panel="p-sides">SIDES & SIPS</button>
</div>
<div class="menu-panel show" id="p-tacos">
<ul class="dishes">
<li class="dish" data-heat="2">
<div class="dish-line"><h4>BULGOGI SHORT RIB</h4><span class="leader"></span><span class="heat"></span><span class="price">$4.75</span>
<button class="add" data-name="Bulgogi Short Rib Taco" data-price="4.75" aria-label="Add bulgogi taco"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<span class="fave">TRUCK FAVE</span>
<p>48-hour marinated short rib, double-charred, day-12 kimchi slaw, sesame crema, scallion, lime.</p>
</li>
<li class="dish" data-heat="3">
<div class="dish-line"><h4>GOCHUJANG FRIED CHICKEN</h4><span class="leader"></span><span class="heat"></span><span class="price">$4.50</span>
<button class="add" data-name="Gochujang Fried Chicken Taco" data-price="4.50" aria-label="Add chicken taco"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Double-crunch thigh in a gochujang lacquer, pickled daikon, cilantro. Loud on purpose.</p>
</li>
<li class="dish" data-heat="2">
<div class="dish-line"><h4>PORK BELLY AL PASTOR</h4><span class="leader"></span><span class="heat"></span><span class="price">$4.75</span>
<button class="add" data-name="Pork Belly Al Pastor Taco" data-price="4.75" aria-label="Add pastor taco"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Achiote pork belly off the spit, pineapple-kimchi pico, charred salsa roja. The spit is a family member.</p>
</li>
<li class="dish" data-heat="1">
<div class="dish-line"><h4>CRISPY DUBU TOFU</h4><span class="leader"></span><span class="heat"></span><span class="price">$4.00</span>
<button class="add" data-name="Crispy Dubu Tofu Taco" data-price="4.00" aria-label="Add tofu taco"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Gochujang-glazed crispy tofu, kimchi pico de gallo, avocado crema. Vegan, if you ask nicely.</p>
</li>
</ul>
</div>
<div class="menu-panel" id="p-big">
<ul class="dishes">
<li class="dish" data-heat="3">
<div class="dish-line"><h4>SEOUL BURRITO</h4><span class="leader"></span><span class="heat"></span><span class="price">$10.50</span>
<button class="add" data-name="Seoul Burrito" data-price="10.50" aria-label="Add Seoul burrito"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Bulgogi, kimchi fried rice, black beans, pickled jalapeño. Add a fried egg for $1 — obviously.</p>
</li>
<li class="dish" data-heat="2">
<div class="dish-line"><h4>K-BOP BOWL</h4><span class="leader"></span><span class="heat"></span><span class="price">$11.00</span>
<button class="add" data-name="K-Bop Bowl" data-price="11.00" aria-label="Add K-Bop bowl"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Rice bowl, your pick of protein, day-12 kimchi, fried egg, gochujang aïoli, sesame blizzard.</p>
</li>
<li class="dish" data-heat="2">
<div class="dish-line"><h4>KIMCHI QUESADILLA</h4><span class="leader"></span><span class="heat"></span><span class="price">$7.50</span>
<button class="add" data-name="Kimchi Quesadilla" data-price="7.50" aria-label="Add quesadilla"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<span class="fave">TRUCK FAVE</span>
<p>Aged cheddar and day-12 kimchi griddled till crisp, scallion crema, gochujang for dunking.</p>
</li>
</ul>
</div>
<div class="menu-panel" id="p-sides">
<ul class="dishes">
<li class="dish" data-heat="2">
<div class="dish-line"><h4>ELOTE LOCO</h4><span class="leader"></span><span class="heat"></span><span class="price">$5.00</span>
<button class="add" data-name="Elote Loco" data-price="5.00" aria-label="Add elote"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Charred corn, gochujang-miso butter, cotija, sesame, shredded nori. Elote went to Seoul and came back cooler.</p>
</li>
<li class="dish" data-heat="2">
<div class="dish-line"><h4>KIMCHI FRIED RICE</h4><span class="leader"></span><span class="heat"></span><span class="price">$8.00</span>
<button class="add" data-name="Kimchi Fried Rice" data-price="8.00" aria-label="Add fried rice"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Yes, there's spam in it. Fried egg on top, because obviously. Sesame, scallion, no apologies.</p>
</li>
<li class="dish" data-heat="3">
<div class="dish-line"><h4>DAY-12 KIMCHI</h4><span class="leader"></span><span class="heat"></span><span class="price">$3.00</span>
<button class="add" data-name="Day-12 Kimchi" data-price="3.00" aria-label="Add kimchi"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>A generous bucket of our house kimchi. No, you can't have the recipe. Ask again tomorrow.</p>
</li>
<li class="dish" data-heat="0">
<div class="dish-line"><h4>MISUGARU HORCHATA</h4><span class="leader"></span><span class="heat"></span><span class="price">$4.00</span>
<button class="add" data-name="Misugaru Horchata" data-price="4.00" aria-label="Add horchata"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Roasted grain tea meets cinnamon rice milk. Dangerously refreshing. The cult favorite.</p>
</li>
<li class="dish" data-heat="0">
<div class="dish-line"><h4>YUJA LIMONADA</h4><span class="leader"></span><span class="heat"></span><span class="price">$3.50</span>
<button class="add" data-name="Yuja Limonada" data-price="3.50" aria-label="Add limonada"><svg class="ic"><use href="#i-plus"/></svg></button></div>
<p>Citron tea marmalade over crushed ice with lime and a salt rim. Made for taco number three.</p>
</li>
</ul>
</div>
<p class="board-note">— WE SELL OUT MOST NIGHTS BY 8PM. YOU'VE BEEN WARNED —</p>
<div class="loco-meter" id="locoMeter">
<div class="lm-title">
<svg class="fl on"><use href="#i-flame"/></svg> THE LOCO-METER — PICK YOUR HEAT
</div>
<input id="lmRange" type="range" min="0" max="5" step="1" value="2" aria-label="How loco are you?">
<div class="lm-read">
<span class="lm-flames" id="lmFlames"></span>
<span class="lm-num" id="lmNum">2</span><span class="lm-max">/5</span>
</div>
<p class="lm-label" id="lmLabel"></p>
</div>
</div>
</div>
</section>
<!-- ============ find the truck ============ -->
<section class="find" id="find">
<div class="wrap">
<div class="sec-head">
<div class="overline">Never parked for long</div>
<h2>WHERE'S THE TRUCK?</h2>
<div class="sub map-hint">tap a day — the truck already knows the way</div>
</div>
<div class="day-row" id="dayRow"></div>
<div class="find-grid">
<div class="map-panel">
<svg class="map-svg" viewBox="0 0 720 380" aria-label="Stylized route map of weekly stops">
<rect class="map-bg" width="720" height="380"/>
<path class="st" d="M0 95H720M0 255H720"/>
<path class="st" d="M140 0V380M330 0V380M560 0V380"/>
<path class="stc" d="M0 95H720M0 255H720"/>
<path class="stc" d="M140 0V380M330 0V380M560 0V380"/>
<rect class="bldg" x="188" y="22" width="84" height="46"/>
<rect class="bldg" x="392" y="300" width="96" height="52"/>
<rect class="bldg" x="646" y="30" width="52" height="50"/>
<rect class="park" x="575" y="262" width="122" height="90" rx="18"/>
<circle class="tree" cx="612" cy="298" r="11"/>
<circle class="tree" cx="652" cy="322" r="13"/>
<circle class="tree" cx="694" cy="290" r="10"/>
<text class="map-lbl" x="78" y="140">K-TOWN</text>
<text class="map-lbl" x="392" y="40">ARTS DISTRICT</text>
<path id="routePath" class="route" d="M84 88 C150 66 210 58 268 62 C360 68 470 80 556 118 C604 139 594 216 512 246 C420 279 322 302 252 292 C176 281 96 254 80 208 C68 170 60 116 84 88 Z"/>
<g id="stopsLayer"></g>
<g class="compass" transform="translate(688,34)">
<circle r="16"/><path d="M0-10 4 6 0 2 -4 6Z" fill="#221710"/>
<text y="-20">N</text>
</g>
<g id="truck">
<rect x="-34" y="-31" width="42" height="27" rx="4" fill="#E2452B" stroke="#221710" stroke-width="3"/>
<rect x="8" y="-25" width="24" height="21" rx="5" fill="#FFF9EC" stroke="#221710" stroke-width="3"/>
<path d="M15 -25v10h15" fill="none" stroke="#221710" stroke-width="2"/>
<text x="-13" y="-14" text-anchor="middle" fill="#FFF9EC" font-family="Impact,sans-serif" font-size="13">KL</text>
<circle cx="-20" cy="0" r="7.5" fill="#221710"/><circle cx="-20" cy="0" r="2.6" fill="#FFF9EC"/>
<circle cx="20" cy="0" r="7.5" fill="#221710"/><circle cx="20" cy="0" r="2.6" fill="#FFF9EC"/>
</g>
</svg>
</div>
<div class="stop-info">
<div class="si-day" id="infoDay">TUE</div>
<div class="si-name" id="infoName"></div>
<div class="si-time" id="infoTime"></div>
<p class="si-note" id="infoNote"></p>
<p class="si-today" id="infoToday"></p>
<ul class="legend">
<li><span class="lg-route"></span> THIS WEEK'S ROUTE</li>
<li><span class="lg-stop">1</span> A STOP — 6 NIGHTS, 6 HOODS</li>
<li><svg class="ic"><use href="#i-truck"/></svg> THE TRUCK, PROBABLY ALREADY LATE</li>
</ul>
</div>
</div>
</div>
</section>
<!-- ============ word on the street ============ -->
<section class="street" id="street">
<div class="wrap">
<div class="sec-head" style="text-align:center">
<div class="overline" style="justify-content:center">Unpaid, unfiltered</div>
<h2>WORD ON THE STREET</h2>
</div>
<div class="pquote">
<div class="stars">
<svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg><svg class="star"><use href="#i-star"/></svg>
</div>
<blockquote>"CERTIFIABLY, DELICIOUSLY LOCO."</blockquote>
<cite>— THE STREET FOOD LEDGER, FEBRUARY ISSUE</cite>
</div>
<div class="notes">
<div class="note"><p>"I came for one taco. I left with a tattoo of the logo."</p><cite>— DANA R., SILVER LAKE</cite></div>
<div class="note"><p>"The kimchi quesadilla should be illegal in at least three states."</p><cite>— MARCUS T., ARTS DISTRICT</cite></div>
<div class="note"><p>"My horchata was misugaru. My whole life is different now. See you Thursday."</p><cite>— PRIYA K., KOREATOWN</cite></div>
</div>
</div>
</section>
<!-- ============ CTA ============ -->
<section class="cta">
<div class="wrap">
<h2>COME HUNGRY.<br>LEAVE LOCO.</h2>
<p>GET NEXT WEEK'S PARKING SPOTS BEFORE EVERYONE ELSE — SUNDAY NIGHT, STRAIGHT TO YOUR INBOX.</p>
<form class="news" id="newsForm" novalidate>
<input type="email" id="newsMail" placeholder="you@hungry.com" aria-label="Email address">
<button type="submit">SIGN ME UP</button>
</form>
<p class="status" id="newsStatus"></p>
</div>
</section>
<!-- ============ footer ============ -->
<footer>
<div class="foot-brand">KIMCHI LOCO · 김치 로코</div>
<div class="foot-grid">
<div class="foot-col"><h5>WINDOW</h5><p>TUE–SUN · 11AM–LATE<br>MON: THE KIMCHI RESTS.<br>NO RESERVATIONS. EVER.</p></div>
<div class="foot-col"><h5>TALK</h5><p>TEXT THE TRUCK —<br>(213) 555-0142<br>A REAL HUMAN, EVENTUALLY.</p></div>
<div class="foot-col"><h5>FOLLOW</h5><p>@KIMCHILOCO<br>WE POST THE SOLD-OUT<br>BOARD NIGHTLY.</p></div>
<div class="foot-col"><h5>FIND</h5><p>THIS WEEK'S ROUTE ABOVE.<br>NEXT WEEK'S: THE<br>NEWSLETTER.</p></div>
</div>
<div class="foot-bottom">© 2025 KIMCHI LOCO · SEOUL × CDMX × YOUR BLOCK · MADE WITH ATTITUDE AND GOCHUJANG</div>
</footer>
<!-- ============ order ticket ============ -->
<div class="ticket-widget">
<button class="ticket-pill" id="ticketPill" aria-expanded="false">
<svg class="ic"><use href="#i-receipt"/></svg>
YOUR TICKET · <span id="pillCount">0</span> · <b id="pillTotal">$0.00</b>
</button>
<div class="ticket-panel" id="ticketPanel" hidden>
<div class="tk-head">KIMCHI LOCO<small>ORDER TICKET <span id="tkNo">#KL-0038</span></small></div>
<ul id="tkItems"></ul>
<div class="tk-foot">
<div class="tk-total">TOTAL<span class="leader"></span><span id="tkTotal">$0.00</span></div>
<button id="callIn">CALL IT IN</button>
<p class="tk-note" id="tkNote">PAY AT THE WINDOW · CASH, CARD, APOLOGIES ACCEPTED</p>
</div>
</div>
</div>
<script>
(function(){
"use strict";
const $=(s,c)=>(c||document).querySelector(s);
const $$=(s,c)=>Array.from((c||document).querySelectorAll(s));
/* ---- today's date on the board ---- */
try{
$('#boardDate').textContent=new Intl.DateTimeFormat('en-US',{weekday:'long',month:'long',day:'numeric'}).format(new Date()).toUpperCase();
}catch(e){$('#boardDate').textContent='TODAY';}
/* ---- marquee: duplicate chunk for a seamless loop ---- */
const mq=$('#mqTrack');
if(mq){const chunk=mq.firstElementChild;mq.appendChild(chunk.cloneNode(true));}
/* ---- menu tabs ---- */
const tabs=$$('.tab'),panels=$$('.menu-panel');
tabs.forEach(t=>t.addEventListener('click',()=>{
tabs.forEach(x=>x.classList.toggle('active',x===t));
panels.forEach(p=>p.classList.toggle('show',p.id===t.dataset.panel));
}));
/* ---- heat flames on every dish ---- */
$$('.dish').forEach(d=>{
const h=+d.dataset.heat||0,slot=$('.heat',d);
let html='';
for(let i=0;i<3;i++)html+='<svg class="fl'+(i<h?' on':'')+'" viewBox="0 0 24 24" aria-hidden="true"><use href="#i-flame"/></svg>';
if(slot)slot.innerHTML=html;
});
/* ---- the loco-meter ---- */
const lmCopy=[
"GRINGO MILD — extra crema, zero judgment. we respect you.",
"WARM — like a hug from your halmoni.",
"SPICY ROOKIE — you'll feel it. you'll like it.",
"PROPERLY LOCO — double your napkin budget.",
"FIREBREATHER — milk is a strategy, not a weakness.",
"FULL LOCO — the kitchen cheers when you order this."
];
const range=$('#lmRange'),lmLabel=$('#lmLabel'),lmNum=$('#lmNum'),meter=$('#locoMeter'),lmFlames=$('#lmFlames');
function setLM(v){
lmNum.textContent=v;
lmLabel.textContent=lmCopy[v];
meter.classList.toggle('meltdown',v>=4);
lmFlames.innerHTML=Array.from({length:5},(_,i)=>
'<svg class="lmfl'+(i<v?' on':'')+'" viewBox="0 0 24 24" aria-hidden="true"><use href="#i-flame"/></svg>').join('');
}
range.addEventListener('input',()=>setLM(+range.value));
setLM(+range.value);
/* ---- order ticket ---- */
const order=[];
let orderNo=37+Math.floor(Math.random()*40);
let everOpened=false;
const pill=$('#ticketPill'),panel=$('#ticketPanel'),itemsEl=$('#tkItems'),
totalEl=$('#tkTotal'),pillCount=$('#pillCount'),pillTotal=$('#pillTotal'),
callBtn=$('#callIn'),tkNo=$('#tkNo'),tkNote=$('#tkNote');
const money=n=>'$'+n.toFixed(2);
const total=()=>order.reduce((s,i)=>s+i.price*i.qty,0);
const count=()=>order.reduce((s,i)=>s+i.qty,0);
function renderTicket(){
itemsEl.innerHTML='';
order.forEach(it=>{
const li=document.createElement('li');
li.innerHTML='<button class="q" data-a="dec" aria-label="One less">−</button>'+
'<span class="nm"><b>'+it.qty+'×</b> '+it.name+'</span>'+
'<span class="pr">'+money(it.price*it.qty)+'</span>'+
'<button class="q" data-a="inc" aria-label="One more">+</button>';
$$('.q',li).forEach(b=>b.addEventListener('click',()=>{
if(b.dataset.a==='inc'){it.qty++;}
else{it.qty--;if(it.qty<=0)order.splice(order.indexOf(it),1);}
renderTicket();
}));
itemsEl.appendChild(li);
});
if(order.length===0&&!panel.classList.contains('confirmed')){
itemsEl.innerHTML='<li class="hint">nothing yet — the board is right there.</li>';
}
totalEl.textContent=money(total());
pillTotal.textContent=money(total());
pillCount.textContent=count();
tkNo.textContent='#KL-'+String(orderNo).padStart(4,'0');
}
function bump(){pill.classList.remove('bump');void pill.offsetWidth;pill.classList.add('bump');}
$$('.add').forEach(b=>b.addEventListener('click',()=>{
const name=b.dataset.name,price=+b.dataset.price;
let it=order.find(o=>o.name===name);
if(it){it.qty++;}else{order.push({name,price,qty:1});}
renderTicket();bump();
if(!everOpened){everOpened=true;panel.hidden=false;pill.setAttribute('aria-expanded','true');}
}));
pill.addEventListener('click',()=>{
panel.hidden=!panel.hidden;
pill.setAttribute('aria-expanded',String(!panel.hidden));
});
callBtn.addEventListener('click',()=>{
if(order.length===0){
tkNote.textContent="ticket's empty — add something loco first.";
tkNote.classList.add('warn');
setTimeout(()=>{tkNote.classList.remove('warn');tkNote.textContent='PAY AT THE WINDOW · CASH, CARD, APOLOGIES ACCEPTED';},2600);
return;
}
const no='#KL-'+String(orderNo).padStart(4,'0');orderNo++;
panel.classList.add('confirmed');
callBtn.disabled=true;
itemsEl.innerHTML='<li class="confirmed-line">TICKET '+no+' LOCKED IN.<br>PULL UP IN ~12 MIN.<br>SAY "LOCO" AT THE WINDOW<br>FOR A FREE HORCHATA UPGRADE.</li>';
order.length=0;
pillCount.textContent='0';pillTotal.textContent='$0.00';totalEl.textContent='$0.00';
setTimeout(()=>{
panel.classList.remove('confirmed');callBtn.disabled=false;
renderTicket();
},5200);
});
renderTicket();
/* ---- newsletter ---- */
$('#newsForm').addEventListener('submit',e=>{
e.preventDefault();
const v=$('#newsMail').value.trim();
const ok=/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v);
const st=$('#newsStatus');
if(!ok){st.textContent="that email looks a little undercooked — try again?";st.className='status warn';return;}
st.textContent="you're in. sunday night, check your inbox — first misugaru horchata's on us.";
st.className='status ok';
e.target.reset();
});
/* ---- schedule + animated truck map ---- */
const DAYS=[
["TUE","GRAND CENTRAL MARKET","11AM–3PM","short lines before noon — the bulgogi goes fast"],
["WED","WILSHIRE & WESTERN — THE WILTERN CURB","5PM–9PM","happy hour means extra crema. it's free. it's law."],
["THU","ARTS DISTRICT BREWERY ROW","5PM–10PM","we pair frighteningly well with anything hoppy"],
["FRI","K-TOWN NIGHT MARKET","6PM–11PM","the long-line night. come early, flex later"],
["SAT","SILVER LAKE RESERVOIR LOOP","10AM–6PM","dog-friendly. the dogs are jealous of the elote"],
["SUN","SMORGASBURG L.A.","10AM–4PM","sold-out o'clock is usually 2PM. you were told"],
["MON","CLOSED — KIMCHI REST DAY","—","the kimchi sleeps. so do we. back Tuesday, blazing."]
];
const jsDay=new Date().getDay();
const todayIdx=jsDay===0?5:jsDay===1?6:jsDay-2;
const dayRow=$('#dayRow');
const dayBtns=DAYS.map((d,i)=>{
const b=document.createElement('button');
b.className='day'+(i===6?' closed':'');
b.innerHTML='<b>'+d[0]+'</b><span>'+d[2]+'</span>';
if(i===todayIdx){const c=document.createElement('span');c.className='today';c.textContent='TODAY';b.appendChild(c);}
b.addEventListener('click',()=>{setDay(i);if(i<6){goStop(i);truck.classList.remove('rest');}else{truck.classList.add('rest');}});
dayRow.appendChild(b);
return b;
});
const infoDay=$('#infoDay'),infoName=$('#infoName'),infoTime=$('#infoTime'),
infoNote=$('#infoNote'),infoToday=$('#infoToday');
function setDay(i){
dayBtns.forEach((b,k)=>b.classList.toggle('on',k===i));
infoDay.textContent=DAYS[i][0];
infoName.textContent=DAYS[i][1];
infoTime.textContent=DAYS[i][2];
infoNote.textContent=DAYS[i][3];
if(i===todayIdx){infoToday.textContent=i===6?"← that's today. we're resting. the kimchi demands it.":"← that's today. come hungry.";}
else{infoToday.textContent="";}
}
const route=$('#routePath'),truck=$('#truck'),stopsLayer=$('#stopsLayer');
const L=route.getTotalLength();
const stopFs=[.04,.22,.40,.57,.75,.93];
const stopEls=stopFs.map((f,i)=>{
const p=route.getPointAtLength(f*L);
const g=document.createElementNS('http://www.w3.org/2000/svg','g');
g.setAttribute('class','map-stop');
g.setAttribute('transform','translate('+p.x.toFixed(1)+','+p.y.toFixed(1)+')');
g.innerHTML='<circle class="ring" r="21"/><circle class="dot" r="13"/><text>'+(i+1)+'</text>';
stopsLayer.appendChild(g);
return g;
});
function setStopOn(i){stopEls.forEach((s,k)=>s.classList.toggle('on',k===i));}
const ease=t=>t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2;
const mod1=f=>((f%1)+1)%1;
function posAt(f){return route.getPointAtLength(mod1(f)*L);}
const clampT=(v,a,b)=>Math.max(a,Math.min(b,v));
const startIdx=todayIdx<6?todayIdx:0;
let pos=stopFs[startIdx],goal=startIdx,manualHold=false;
let leg={from:pos,to:pos,start:0,dur:0,active:false};
let holdUntil=performance.now()+1800;
const DRIVE_MS_PER_LOOP=5200,PAUSE=1700;
function onArrive(i){setDay(i);setStopOn(i);}
function goStop(i){
goal=i;manualHold=true;
let d=stopFs[i]-pos;if(d<0)d+=1;
if(d<.004){onArrive(i);leg.active=false;holdUntil=performance.now()+3200;return;}
leg={from:pos,to:pos+d,start:performance.now(),dur:Math.max(600,d*DRIVE_MS_PER_LOOP),active:true};
}
function placeTruck(f,now){
const wob=leg.active?Math.sin(now/70)*1.3:0;
const p=posAt(f);
const a=posAt(f+.006),b=posAt(f-.006);
const dx=a.x-b.x,dy=a.y-b.y;
const raw=Math.atan2(dy,dx)*180/Math.PI;
let tilt,sx;
if(Math.abs(raw)<=90){tilt=clampT(raw,-12,12);sx=1;}
else{let phi=180-raw;if(phi>180)phi-=360;tilt=clampT(phi,-12,12);sx=-1;}
truck.setAttribute('transform','translate('+p.x.toFixed(1)+','+(p.y+wob).toFixed(1)+') rotate('+tilt.toFixed(1)+') scale('+sx+',1)');
}
function tick(now){
if(leg.active){
const t=Math.min(1,(now-leg.start)/leg.dur);
pos=leg.from+(leg.to-leg.from)*ease(t);
if(t===1){
leg.active=false;
holdUntil=now+(manualHold?3400:PAUSE);
manualHold=false;
onArrive(goal);setStopOn(goal);
}
}else if(now>=holdUntil){
const next=(goal+1)%6;
goal=next;
let d=stopFs[next]-pos;if(d<0)d+=1;
leg={from:pos,to:pos+d,start:now,dur:Math.max(700,d*DRIVE_MS_PER_LOOP),active:true};
}
placeTruck(pos,now);
requestAnimationFrame(tick);
}
setDay(todayIdx);
setStopOn(startIdx);
placeTruck(pos,performance.now());
requestAnimationFrame(tick);
})();
</script>
</body>
</html>Reasoning trace (38,937 tokens)
The user wants a marketing website for a Korean taco food truck called "Kimchi Loco". This should be a vibrant, mouth-watering, appetite-appealing landing page that makes people really want to visit. This is a creative branding/marketing task — no game mechanics needed, no demo mode needed since it's a website, not a game. The first-paint screenshot requirement means the hero must be striking immediately.
Let me think about the concept and design direction:
**Brand: "Kimchi Loco"** — Korean × Mexican fusion taco truck. The name itself is playful ("loco" = crazy in Spanish). The energy should be: bold, loud, street-food energy, Seoul meets Mexico City street culture. Think: neon, hot sauce, gochujang red, lime green, night market vibes.
**Color palette:** I want to avoid generic blue/purple. For a Korean-Mexican fusion:
- Hot gochujang red (#E63329 or similar)
- Lime/avocado green
- Warm cream/off-white background (like paper, tortilla)
- Dark charcoal/near-black for depth
- Maybe a punchy yellow/mustard accent
Actually, let me think about a more distinctive palette. Korean flag has red/blue but blue is off-limits per my own taste for a food truck. Let me go with: cream base (#FAF3E3 or #FFF6E9), deep charcoal ink (#1A1512), gochujang red (#E63B2E), lime (#8DB33B / #A4C93E), and a sunny marigold (#F5B82E). That feels like a taqueria poster meets Korean snack bar.
**Typography:** Avoid Inter/Roboto/Arial/system-ui as primary. Good options from Google Fonts:
- Display: something chunky and loud — "Anton" (condensed, poster-like), "Archivo Black", "Bowlby One", "Lilita One" (taco truck energy!), "Shrikhand" (retro groovy)...
- "Lilita One" is very taco-truck. Or "Bungee" — street signage vibes! Bungee is literally designed for urban signage, perfect for a food truck.
- Body: "Space Grotesk" is overused-ish but fine as secondary. Maybe "Epilogue" or "Outfit" or... let me use "Archivo" for body — it's got warmth. Or "Karla". Let me go: Display = "Bungee" (loud, signage), Secondary display = "Shrikhand" or "Lilita One" for the "Loco" script flavor, Body = "Karla" or "Archivo".
Actually a nice combo: "Bungee" for big headers, "Lilita One" for price tags/menu items, "Karla" for body text. Hmm, three fonts might be much. Let me do: Bungee (headlines), Lilita One (accents, prices, badges), Archivo or Karla (body). That's a fun food-truck system.
**Layout & sections** — avoid mechanical equal columns and dashboard feel. Ideas:
1. **Hero:** Huge type "KIMCHI LOCO" with a marquee-style rotating word or taping effect. A big illustrated taco (SVG, hand-drawn style). Maybe a stamp/badge "Seoul × Mexico City". Blinking "OPEN" neon-ish sign (but no glow per rules — no glowing titles; neon without glow is just outlined text... I'll skip glow, use a stamped badge instead). Marquee strip with scrolling text "GOCHUJANG SHORT RIB TACOS • KIMCHI FRIED RICE •..." — that's a signature food-truck device.
2. **The hook / story:** "Two kitchens. One truck." Short punchy copy about the fusion.
3. **Menu:** Not equal cards — I'll do a menu-board style list, like a real taco truck menu hand-lettered board: item name, dotted leader line, price. With heat-level pepper icons, "chef pick" tags. Maybe a tab switcher between Tacos / Burritos / Sides. This is the money section — make food descriptions mouth-watering.
Menu items (Korean-Mexican fusion):
- **Bulgogi Beef Taco** — marinated short rib, kimchi slaw, sesame crema, cilantro, lime — $4.50
- **Gochujang Chicken Taco** — double-crunch fried chicken, gochujang glaze, pickled daikon — $4.00
- **Tofu Taco (Dubu)** — gochujang-glazed crispy tofu, kimchi pico de gallo — $4.00
- **Kimchi Quesadilla** — aged kimchi + Oaxaca-style melting cheese, scallion crema — $7
- **Seoul Burrito** — bulgogi, kimchi fried rice, black beans, fried egg optional — $10
- **K-Bop Bowl** — rice bowl with choice of protein, pickles, fried egg — $11
- **Elote Loco** — Korean street corn: gochujang butter, cotija, crushed seaweed — $5
- **Kimchi Fried Rice** — with spam (classic!) and fried egg — $8
- **Side of Day Kimchi** — house-fermented, made daily — $3
- **Horcbata?** — drinks: Horchata with roasted grain tea (Misugaru horchata!) — clever fusion drink. "Yuja Horchata" — $4
That Misugaru horchata idea is great fusion detail.
4. **Heat level interactive element:** A "how loco are you?" heat slider — drag a slider from 0 to 5 peppers, and the page responds (peppers fill, a warning message changes, maybe the whole section tints). That's a fun signature interaction. Or better — make it playful: a "LOCO-METER".
5. **Location / Find the truck:** Since it's fictional, show "This week's stops" with days/locations/times — like a schedule list, styled as a route map or ticket stubs. Maybe a simple stylized map (SVG) with a truck icon that moves between stops when you click a day? Or an interactive schedule where hovering a day shows the location. Let me do a weekly schedule strip + an animated SVG map with dashed route and a little truck that drives along the path. That could be the signature moment: a little truck driving along a dashed route on a hand-drawn map.
Actually, the marquee + driving truck + heat meter — I should pick the strongest. Signature moment candidates:
- Hero: giant taco illustration that slightly rotates/breathes, with orbiting ingredients, and a marquee.
- LOCO-METER heat slider.
- Truck driving on map route.
I'll do all three but keep them tight — hero marquee, heat meter in menu section, and animated truck on a route for locations. The truck driving along a dashed SVG path with `offset-path` or JS animation is very doable.
6. **Reviews / social proof:** "Word on the street" — a few quotes styled like taped notes or spray-stencil.
7. **Footer/CTA:** "Find the truck. Eat the tacos. Thank us later." Newsletter signup (fake but functional with inline confirmation message — no alert(), just inline text). Hours, socials (icon library — I'll use Lucide via CDN or inline SVG icons; CDN allowed. Lucide has a nice CDN with `lucide.createIcons()`).
**Interactivity requirements:**
- Every interactive element wired to real behavior.
- Menu tabs switching.
- Heat slider updates.
- Newsletter form shows inline confirmation.
- Map day selection moves truck.
- Maybe add-to-order? That might be scope creep; a "build your order" could be fun but let's keep focused. Actually a mini "order" feature could be fun: click "add" on menu items, a tally appears in a floating "ticket" — like a receipt — with total, and "text it to the truck" fake button. Hmm. That adds real interactivity and delight. Let me consider: a small order ticket that fills up as you add items, floats bottom-right, with total. It's genuinely useful-feeling and food-truck appropriate (order ahead). I'll include it but lightweight: add buttons on menu, ticket slides in, remove items, total, and a "Send order" that shows "Order #, ready in 12 min" inline message.
Careful about no alert/confirm/prompt — all inline.
**No emoji as icons** — use Lucide icons via CDN script, or hand-drawn SVG. Lucide CDN: `https://unpkg.com/lucide@latest` then `lucide.createIcons()`. That's allowed (CDN reference allowed... the rules say "except the CDN URLs explicitly allowed by the task" — the task doesn't explicitly allow any CDN, but the system says "No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task." Hmm, strictly, the task (user message) doesn't allow CDNs. But Google Fonts is a common exception... The strict reading: I can't load Google Fonts?? "No external files, images, fonts or network requests" — fonts are explicitly called out. So no Google Fonts either. That's restrictive but safer for a sandboxed iframe that may be offline.
Wait — many of these demo tasks do allow Google Fonts. But the instruction is explicit: "No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task." The task allows none. So I must build with system-available fonts only? But it also says avoid Arial, Inter, Roboto, system-ui as the primary identity font. Conflict: with no external fonts allowed, I must use locally available stacks.
Hmm. Options: use font stacks that are commonly installed and characterful: "Impact" / "Haettenschweiler" / "Arial Narrow Bold" — Impact is widely available on Windows/Mac and has that condensed poster look. For a food truck poster, Impact is actually thematically right (loud signage). Chunky display via Impact + letterspacing tweaks. For body: Georgia? Verdana? Hmm.
Alternatively — I can draw the logotype as SVG paths so the identity is carried by custom lettering/vector, not the font. That's the strongest move: hand-craft the "KIMCHI LOCO" logotype as SVG lettering, use Impact-ish stack for big headlines ("Impact, 'Arial Black', sans-serif"), and Georgia/"Courier New"? Hmm, Courier New for the menu board / receipt ticket — monospace receipt vibes! That's actually great: the order ticket styled like a thermal receipt in Courier New. And body copy in Georgia or Verdana.
Font plan (zero network):
- Display: `"Impact", "Haettenschweiler", "Arial Narrow Bold", sans-serif` with tight tracking, uppercase. Impact gives loud taco-sign energy and isn't in the banned list (banned: Inter, Roboto, Arial, Helvetica,system-ui as primary identity — Impact is distinctive).
- Script accent: "Brush Script MT", "Segoe Script", cursive — for "loco" flavor words like "est. 2024" and hand-written notes. Brush Script MT is on most systems. If unavailable, cursive fallback still fine.
- Receipt/ticket: `"Courier New", monospace`.
- Body: Georgia, "Times New Roman"... hmm serif body on a food truck site? Could work editorial. Or Verdana. Let me use Georgia for editorial paragraphs (gives it a zine/food-mag feel) — actually a punchy street brand might read better with a grotesque. Available grotesques besides banned: "Trebuchet MS" — friendly, warm, distinctive-ish, widely available. Trebuchet MS for body! Good — it has personality, not on the banned list.
So: Impact (display), Trebuchet MS (body), Courier New (receipts/schedule details), Brush Script MT (handwritten accents). All local, zero requests.
**Icons:** No external icon CDN (no network). Use inline SVG icons I draw myself — flame/pepper shapes, location pin, clock, truck, star, arrow. I'll hand-roll simple SVGs. A pepper SVG for heat levels is essential. I'll define a few reusable SVG symbols.
**No uniform equal-width 3-column card rows.** For menu, use a two-column asymmetric menu board or a single-column list with alternating alignment, or a "board" with hand-drawn dividers. Reviews: staggered/rotated notes, not uniform cards. Weekly stops: a horizontal timeline strip, not cards.
**Signature composition for hero:** Big logotype, a large SVG taco illustration with steam animation, sticker badges ("SEOUL × CDMX", "HOT & FRESH"), rotating marquee at bottom. Maybe a subtle grain/halftone texture background via CSS. Also a giant outlined text watermark. Let me make the hero split: left — huge stacked type "KIMCHI" / "LOCO" with an offset/rotated treatment, tagline, CTA buttons ("See the menu", "Find the truck"); right — the taco illustration with orbiting labels and animated steam lines. Bottom — the marquee ticker strip.
The taco illustration: I'll draw an SVG taco: a yellow semicircle shell (torta half-moon) with crimped edge, filled with layered stuff: green lettuce/cabbage, red gochujang drizzle, meat chunks, white crema, sesame dots. Hand-drawn style with thick strokes (stroke #1A1512, weight 6). Add subtle rocking animation. Steam: three wavy lines animating upward with opacity. Orbiting little chips around: a lime wedge, a pepper, a droplet.
Halftone/grain: use a repeating radial-gradient CSS pattern at low opacity for texture — allowed (gradients as texture/pattern for a real design system purpose... rule says don't use decorative gradients to fake design sense; subtle noise texture is a pattern, and I should be careful. Better: use SVG noise via feTurbulence in a low-opacity overlay — that's craft, texture not gradient-banner. I'll use an inline SVG filter noise overlay at ~4-5% opacity. Good.)
Also diagonal stripes pattern for section dividers — classic taqueria awning stripes! Red/cream stripes awning motif as a divider element — very thematic. I can make scalloped awning borders with CSS. That's a great authored detail: awning-striped headers over each section title.
**Sections plan:**
1. **Top bar / nav:** left: logo (small SVG taco + "KIMCHI LOCO"), right: links (Menu, Heat, Find Us, Story) + "Order ahead" pill button. Sticky with cream background + border-bottom ink. Nav links smooth-scroll.
2. **Hero:** as described. Include "OPEN TUE–SUN · 11–9" stamp, headline, tagline "Korean-Mexican tacos from a truck that never learned to behave." CTAs. Taco SVG. Marquee at bottom edge with border top/bottom, scrolling items separated by pepper SVGs: "BULGOGI TACOS", "KIMCHI QUESADILLA", "MISUGARU HORCHATA", "GOCHUJANG EVERYTHING", "FRIED EGG ON IT? OBVIOUSLY".
3. **"The Deal" story strip:** short manifesto, maybe with big stat-ish proclamations: "48-hour marinated bulgogi", "Kimchi fermented in-house, day 12 is the sweet spot", "Tortillas pressed to order". Style like a stamped poster — three-ish staggered notes with rotation, not uniform cards. Or one big editorial paragraph with inline highlights and a "chop-stamp" red seal. Let me do: a two-column asymmetric layout — left: manifesto headline + paragraph; right: a "process list" numbered like a recipe ticket (01 MARINATE, 02 FERMENT, 03 PRESS, 04 STACK, 05 DRIZZLE). Numbered ticket rows with monospace.
4. **Menu board:** Section header with awning stripes. Tabs: TACOS / BURRITOS & BOWLS / SIDES & SIPS. Menu list: rows with item name (big), description, price, heat peppers, "+ ADD" button, some have "TRUCK FAVE" rotated sticker. Two-column on desktop via CSS columns? Menu rows with dotted leaders. Asymmetric: left column wider with items, right side a sticky "LOCO-METER" panel? Hmm, that might feel dashboard-y. Alternative: heat meter as a horizontal strip below the menu header. Let me place the LOCO-METER as its own subsection right after menu: "HOW LOCO ARE YOU?" slider 0–5 peppers, dynamic label: 0 "Kimchi Kindergarten (mild, we still respect you)", 1-2 "Spicy Apprentices"... 5 "FULL LOCO — sign the waiver". And a slider changes a sample dish heat. It's fun. I'll integrate it INTO the menu section as a footer strip of the menu board.
5. **Find the truck:** SVG mini-map (stylized streets grid, park blob, dashed route) with truck icon animating along path; below, the weekly stop schedule as a horizontal row of day tickets: MON closed (crossed), TUE "Downtown Farmers Market 11–3", etc. Click a day → truck moves to that stop on map + info card updates. This is a strong interactive signature. Keep map simple and stylized: cream bg, ink streets, red route dashed, stops as numbered pins. Truck = small SVG group animated along path via getPointAtLength in JS.
6. **Reviews:** "WORD ON THE STREET" — 3-4 staggered handwritten-style notes with tape, rotation, different sizes. Avoid uniform cards: vary rotation, width, vertical offset.
7. **Order ticket (floating):** bottom-right fixed receipt that expands when items added; shows items, qty +/-, total, "CALL IT IN" button → inline "Order #0042 — ready at the window in ~12 min" confirmation and clears. Also a small floating pill "YOUR TICKET (3)" when collapsed.
8. **Footer:** big logotype repeated as outline text, address (fictional "Parked at 3rd & Vermont, K-Town"), hours, phone, socials (inline SVG icons), credits. Newsletter input → inline "You're on the list — first horchata's on us." message.
**Accessibility & responsive:** clamp() type sizes, grid → single column below 720px, marquee still works, map stacks below schedule on mobile, ticket becomes bottom bar full-width-ish. No horizontal scroll.
**First paint:** everything visible statically — animations are CSS/JS driven but content present at load. Truck on map animates by default (auto-plays through stops). Good.
Let me also double check banned things: no gradient banners/buttons — I'll use flat colors, ink borders, hard shadows (neo-brutalist-ish street poster: thick 3px ink borders, offset solid shadows). Hard shadows = "8px 8px 0 #1A1512" — great poster look, no gradients. No blue/purple. No emoji. No alert. No lorem ipsum. All buttons wired.
Color check: cream #FBF3E4, ink #201914 (warm black), red #E8402A or gochujang #D93A2B, marigold #F2A81D / #F6B92E, lime/green #7BA23F or #6FA544... Korean green #4A9B5C? Let me pick:
- --cream: #FCF3E3
- --paper: #FFFBF2 (slightly lighter for panels)
- --ink: #221A12
- --red: #E43D30
- --red-deep: #B3271E
- --gold: #F5A623 → maybe #F7B538
- --green: #5B8C3E → lime-ish #79A83B
- --pink (kimchi): #F2A0A0? maybe for accents — kimchi pepper pink #E86456. Optional.
Marquee bg: ink with cream text. Section headers: awning stripes (red/cream) with scalloped bottom edge via repeating radial? Scallops: use a row of semicircles — CSS with radial-gradient repeating pattern... radial-gradient is a gradient but used as pattern generation for scallop shape — that's a functional shape technique, not decorative gloss. I think a repeating-linear-gradient stripe pattern for awning is pattern-craft, acceptable (it's flat stripes, not a fade). Flat stripe patterns via repeating-linear-gradient with hard stops = flat pattern, not "gradient" in the prohibited gloss/fade sense. I'll keep hard-stop stripes only. Same for scallops with hard stops.
Noise texture: SVG feTurbulence data-URI as background-image at low opacity overlay. That's a fine craft texture. I'll add it as a fixed full-page overlay pseudo-element with pointer-events none, opacity 0.05.
Now the taco SVG — let me design it:
- ViewBox 0 0 400 340.
- Shell: half-ellipse: path M 40 260 A 160 150 0 0 1 360 260 with fill gold, stroke ink 8. Crimped edge: small semicircle bumps along the flat top edge? Actually taco shell: flat bottom, curved top. Let me think: taco = half-moon with flat side UP (opening up) and rounded bottom. Fillings stick above the flat line. Shell path: M 30 210 L 370 210 A 175 175... hmm: bottom curve from (30,210) to (370,210) as big arc downward. Path: `M30 205 L370 205 A170 170 0 0 1 30 205`? Arc from (370,205) to (30,205) sweeping down. Let me write: `M30 205 A170 170 0 0 0 370 205 L30 205 Z` — sweep flag: from (30,205) arc to (370,205) with large-arc=0 sweep=1 goes below? For arc from left to right, sweep 1 curves downward (clockwise). Yes: `M30 205 A170 120 0 0 1 370 205 Z` — a shallow dome downward... I'll tune with rx=170 ry=130. Then crimped edge: series of small circles along top rim filled gold with ink stroke, positioned above y=205 partially — actually the crimped fold of a hard shell appears at the TOP corners as vertical strip with zigzag. Simpler: draw shell, then add a "fold band" along the top: a rectangle strip from y=170..205? Hmm overcomplicating.
Simpler stylization (flat vector illustration style):
- Shell: big rounded half-moon (gold) with ink outline.
- Inside fillings drawn sticking above the shell's top line:
- Lettuce/cabbage: green ruffled shapes (series of arcs) across the top.
- Red cabbage bits: purple? avoid purple... use red.
- Meat: brown chunks (use a warm brown #A9683A — brown is fine).
- Crema drizzle: white squiggle strokes.
- Sesame: small white dots on meat.
- Lime wedge leaning on the side.
- Gochujang drizzle: red squiggle over everything.
- Below the shell top line, the shell face has a few short ink lines for texture and small steam? Steam above.
- Add a face? Cute trucks/tacos often have faces. A smiling face on the taco could be charming and memorable — makes the brand playful. Hmm, "makes people want to come" — cute mascot sells. Let me give the taco simple closed happy eyes + smile. That plus the truck mascot. Yes — mascot energy: the taco has a little face, and the truck SVG on the map has a face too. Signature charm.
Steam: three wavy paths animating (CSS keyframes translateY + opacity) above taco.
Orbiting labels: small rotated badges around the taco: "GLUTEN? SOMETIMES" ha, or "PRESSED DAILY", "48-HR BULGOGI". Place 2-3 stickers with rotation, hard shadow, positioned absolutely.
Hero layout: grid 2 cols (1.15fr / 1fr), min-height ~ calc(100vh - nav - marquee)? Ensure content fits in 1280×800 screenshot: nav 64px + hero ~600 + marquee 48. Good.
Marquee: CSS animation translateX(-50%) on duplicated content, 24s linear infinite. Items separated by pepper SVG inline.
Menu data — I'll write real descriptions with appetite language.
Tabs: three buttons toggling lists. Each list built from JS array or static HTML? Static HTML for 3 groups is simpler and robust; tabs toggle hidden class. Heat peppers: render as inline SVG repeated. I'll write a tiny JS to populate heat peppers from data-heat attribute to avoid repetitive HTML — good: each menu row `<li data-heat="2">`, JS inserts pepper icons.
"+" add buttons: dataset name & price; clicking adds to order state, updates ticket, ticket pops (small scale animation). Ticket: fixed bottom right; header "ORDER TICKET #—", items with minus/plus buttons, total, "CALL IT IN" button. On submit: shows confirmation panel within ticket ("TICKET #0042 · NAME AT WINDOW? just show "READY IN ~12 MIN · PULL UP") and resets after few seconds. Also a close/collapse toggle. On mobile, ticket full width bottom.
Heat meter ("LOCO-METER"): input range 0-5 styled; label text updates; big number; pepper row fills; maybe background of the meter panel tint shifts from cream to red at 5. Fun copy:
- 0: "MILD & PROUD — extra crema, zero judgment"
- 1: "WARM — like a hug from your halmoni"
- 2: "SPICY ROOKIE — you'll feel it, you'll like it"
- 3: "PROPERLY LOCO — napkin budget: doubled"
- 4: "FIRE BREather — sign nothing, sweat everything"
- 5: "FULL LOCO — kitchen cheers, fire department sighs"
Truck map: SVG 640×360-ish stylized: cream bg, ink street lines (a few thick strokes crossing), blocks as faint rectangles (just outlines), a green park blob, route path (red dashed) through 5 stops. Stops: circles with numbers. Truck: small group (rect cab + cargo box + wheels) with the red color; animate via JS requestAnimationFrame along path using path.getPointAtLength with rotation? Rotation via atan2 of derivative — doable but keep truck upright (no rotation, simpler & still reads fine) — actually slight rotation looks way better. I'll compute angle from two sample points and rotate. Auto-play: truck drives stop to stop continuously, pausing at each. Clicking a day ticket sets current stop and updates the info panel + moves truck directly (animated). Day buttons: TUE–SUN list (MON "CLOSED — kimchi needs rest too"). Info panel shows location name, time, cross street, "crowd level" note.
Locations (fictional, LA K-Town flavored):
- TUE: "Grand Central Market — 11:00–3:00"
- WED: "Little Tokyo Plaza — 11:30–8:00"? hmm keep: "Wiltern Theatre curb, Wilshire & Western — 5–9"
- THU: "Arts District Brewery Row — 5:00–10:00"
- FRI: "Koreatown Night Market — 6:00–11:00"
- SAT: "Silver Lake Reservoir loop — 10:00–6:00"
- SUN: "Smorgasburg L.A. — 10:00–4:00"
- MON: closed.
Reviews notes: 3 rotated taped notes with quotes:
- "I came for one taco. I left with a tattoo of the logo." — Dana R.
- "The kimchi quesadilla should be illegal in at least 3 states." — Marcus T.
- "My horchata was misugaru. My life is changed. See you Thursday." —Priya K.
Also a fake publication strip: "BEST FUSION TRUCK — LA WEEKLY-ish" I should be careful with real publication names; use invented: "THE TACO TIMES ★★★★★ 'certifiably loco'". Fun.
Footer: giant outline "KIMCHI LOCO" text, contact, hours, "no reservations, ever" line, newsletter form.
Nav "Order ahead" button scrolls to menu and pulses ticket? It can open the ticket with a hint "Add items from the menu". Fine — scrolls to menu.
Smooth scrolling via CSS scroll-behavior + scroll-margin-top on sections.
Let me also ensure console-safe: all JS null-checks, no external fetch. Use `defer`? Script at end of body. Lucide not used — all inline SVG.
Pepper SVG icon (small flame? or chili): chili pepper path — let me craft: a curved pod. Simple pepper path in 24×24:
`M7 15c-3 0-5-2.5-5-5.5 2 .8 3.5.6 4.6-.3C8.2 8 10 6.5 12.5 7c.4-2 2-3.5 4.5-3.5-.3 2-1.5 3.2-2.7 3.7...` — freehand chili: I'll draw: body `M4 16c-2.2-1.8-2.6-4.6-1.4-7 .7 1.7 2 2.4 3.4 2.2C8 11 10 10.4 12.3 11.4c3 1.3 4 4.4 2.6 7.2-.7 1.4-2 2.4-3.6 2.9-1.9-2.3-4.7-3.6-7.3-5.5Z` plus a stem `M15.6 4.5c.6 1 .5 2-.2 2.9`. Honestly, simpler: use a flame icon shape (like Lucide flame):
flame path (Lucide-ish): `M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z` — that's the well-known Lucide flame path; using it as a utility icon path is fine (icon libraries recommended). I could alternatively define my own chili. A chili reads more food-specific. Let me craft a decent chili:
chili (24×24), pointing right-down with stem up-left:
- stem: `M14 3c.3 1.2 0 2.3-.8 3.2` stroke
- body: `M13.5 5.5c1.8.2 3.1 1.2 3.8 2.8.9 2 .6 4.2-.6 6.3-1.3 2.4-3.4 4.1-6.2 4.9-2.5.7-5-.2-6-2.2-.8-1.6-.4-3.3.9-4.3 1.2-.9 2.7-1 4 0 .9.7 1.5 1.6 1.7 2.7.1.8.6 1.4 1.3 1.6 1.6.6 3.3-.6 3.9-2.6.5-1.7.1-3.5-.8-5.1-.8-1.5-1.8-2.7-3-3.6Z` hmm complicated.
I'll define a symbol "pepper" with a hand-tuned path; even if slightly wonky, thick stroke + flat fill red with ink outline keeps it stylish. Let me simplify to a crescent:
`M6 6c2 .2 3.6 1.3 4.6 3.1 1 1.9 1.1 4 .4 6.2-.6 2-2.2 3.4-4.2 3.4-1.7 0-3-1-3.3-2.6-.2-1.3.4-2.5 1.6-3.1 1-.5 2-.4 2.8.2.7.5 1 1.2.9 2` as stroke path, plus stem `M6 6c-.4-1.6.1-3 1.4-4` — a chili drawn with strokes only. Stroke-based icon = clean, consistent with hand-drawn theme. I'll iterate visually in my head; final code will use a reasonable path; small size forgives imperfection.
Other icons: pin, clock, star (fill), arrow-right, x (close), plus, minus, truck, flame, instagram-ish, phone. I'll inline small SVGs where needed via a `<symbol>` defs block and `<use>`.
Awnings: section header component: a bar with stripes + scallop bottom:
`.awning { background: repeating-linear-gradient(90deg, var(--red) 0 28px, var(--cream) 28px 56px); }` with bottom scallops using another element with radial hard-stop circles in matching colors — aligning scallops to stripes is fiddly. Alternative: scalloped edge as SVG pattern — I'll make an SVG scallop strip inline where needed (two alternating colors per scallop): simpler to render scallop row as a small inline SVG with alternating red/cream semicircles, width 56px repeated? I can use an SVG with `<pattern>`: pattern of two semicircles. Or just generate scallops with a repeating-radial-gradient with hard stops where the stripe width matches gradient period — colors won't alternate correctly though (semicircle under a red stripe should be red). If stripe period is 56 (28 red + 28 cream) and scallop circles of diameter 28 placed centered under each 28px segment... radial-gradient(circle at 50% 100%, red 0 14px, transparent 14px) with background-size 28px 28px repeating gives red scallops everywhere, not alternating. Two layered gradients offset by 28px: one red circles, one cream circles, offset 28px — that works! Layer 1: red scallops with size 56px positioned left 0; layer 2: cream scallops size 56px positioned left 28px? Each layer's own background-size 56px, red at x-offset 0, cream at x-offset 28px — yes, that yields alternating red/cream scallops. But stripe bar above must align: red segments occupy [0,28), [56,84)... with the scallop under each segment centered at 14, 70, ... Circle center at segment center: for red scallops: background-size 56, circle center at 28px within tile → circle at center x=28 covers 14..42 — hmm misaligned with segment 0..28. I need circle centered at 14 within a 56 tile: radial-gradient(14px at 14px 100%...) i.e. `radial-gradient(circle 14px at 14px 100%, ...)`. Fine — these are hard-stop shape masks, flat colors. OK good. Actually simpler and more robust: make the entire awning an inline SVG: a rect with stripes + scallop path, using `<pattern>` with two rects and two semicircle paths — full control, crisp. I'll build a reusable SVG "awning" strip sized to container width via preserveAspectRatio="none" — stretching scallops horizontally is ugly. Instead fixed tile approach: draw one tile as `<pattern width="56" height="20">` with red rect 0..28, cream rect 28..56, red semicircle path `M0 20 A14 14 0 0 1 28 20 Z` and cream `M28 20 A14 14 0 0 1 56 20 Z`, patternUnits userSpaceOnUse. Then `<rect fill="url(#awn)" height="20">`. Scallop bottom = the semicircle bottom halves? The tile is 20 tall with rects filling full height and semicircles below? Let me set tile height 40: stripes fill top 20, scallops occupy bottom 20 (semicircles pointing down from y=20 to 40). Path semicircle: `M0 20 a14 14 0 0 0 28 0 v-?` hmm: semicircle bulging downward from (0,20) to (28,20): `M0 20 A14 14 0 0 0 28 20 L28 20 Z`? Arc sweep: from (0,20) to (28,20), r=14, sweep=0 gives... for downward bulge going left→right, we want the arc below the chord: with sweep-flag 0 the arc is drawn counterclockwise → passes below? In SVG, from (0,20) to (28,20), sweep=1 (clockwise) bulges up; sweep=0 bulges down. Yes: `M0 20 A14 14 0 0 0 28 20 Z` bulges downward. Good, plus stroke ink on scallops? Outlines would need path strokes on semicircle arc + stripe boundaries. To keep it clean: no outlines on awning, or thin ink line at top. Fine.
Actually maybe simpler aesthetic: skip full awning; use it just as the menu-board header top edge — a classic food truck awning over the menu board. That's thematically perfect (menu board sits under the truck's awning!). Yes — put the scallop awning above the menu board panel.
Menu board panel: paper background, ink border 3px, hard shadow, header row: "TODAY'S BOARD" + date (JS-generated date string), tabs under. Board texture slight. Menu rows: name in Impact large, dotted leader to price, description in Trebuchet small, heat peppers inline, ADD button small square with plus icon. "TRUCK FAVE" rotated sticker absolute on certain rows.
Two-column menu at desktop? Board rows in 2 columns via CSS grid columns for compactness: `column-count: 2` on the list with `break-inside: avoid` on rows. Nice, asymmetric natural. Mobile: 1 column.
Reviews section: heading "WORD ON THE STREET", notes with `-webkit-transform: rotate(-3deg)` etc., background paper with tape strips (small rotated semi-transparent rects). Plus a big pull-quote line. Layout: flex wrap with varying widths; not uniform grid.
Story section: bg ink (dark section for rhythm) with cream text — careful about "large low-contrast gray on dark" — use high contrast cream on ink, red accents. Numbered steps in monospace. Big statement: "TWO STREETS. ONE FLAVOR." then paragraph. Add red chop-stamp square with Korean characters? I could include "김치 로코" — Hangul renders on most systems (font availability usually fine — Mac/Windows both include Hangul fallback). Adding Hangul is a great authentic touch: hero small text "김치 로코 · Seoul meets Mexico City". If missing font, tofu boxes risk... modern Chrome has Hangul fallback via system (Noto on Linux/Mac has Apple SD Gothic Neo; Windows Malgun Gothic). Safe enough. I'll include Hangul accents in a few spots (hero stamp, footer) — adds authenticity.
Order ticket interactions detail:
- state: array of {name, price, qty}
- addItem(name, price): find/++.
- render: if empty → ticket hidden (or shows collapsed pill "MENU IT: 0"?). Better: pill always visible bottom-right: "🧾"? no emoji — pill with ticket icon + "YOUR TICKET · $0.00". Click toggles expanded receipt. On add: pill bumps (animation) and if receipt open, updates live. Receipt panel: header "KIMCHI LOCO — ORDER TICKET", monospace lines "2 × BULGOGI TACO ..... 9.00", controls +/-/×, dashed divider, TOTAL, "CALL IT IN" button, footnote "pay at the window · cash, card, and apologies accepted". After CALL IT IN: replace body with confirmation "TICKET #KL-0042 LOCKED IN. BE AT THE WINDOW IN ~12 MIN. SAY 'LOCO' FOR A FREE HORCHATA UPGRADE." then auto-reset after 6s with a small note. Fun & functional.
Buttons on menu "ADD" — plus icon + text.
Nav order button: scrolls to menu.
CTA hero buttons: "SEE THE BOARD" → scroll to menu; "FIND THE TRUCK" → scroll to map section.
Also press "L" easter egg? skip, keep scope.
Marquee content duplicated twice for seamless loop; animation `transform: translateX(-50%)`. Ensure width max-content.
Top announcement bar? Maybe "THIS FRIDAY: KOREATOWN NIGHT MARKALE *". Skip, marquee covers it.
Heat meter details: range input styled with ink track, red thumb (square thumb rotated 45? diamonds). Peppers displayed: 6 slots? 0–5 → five pepper slots fill based on value; label + number. Also a fun "burn level" bar? Keep: big Impact label text swap + panel border-color shifts to red at ≥4. Also a tiny disclaimer text swap. Add wobble animation on peppers when ≥4.
Map SVG plan (viewBox 0 0 720 380):
- bg: transparent (section bg cream), draw blocks: a few rounded rects with 2px ink stroke, fill paper (#FFFBF2) — like buildings, sparse (avoid clutter). Streets: implied by gaps. Maybe simple cross streets: two horizontal lines, three vertical lines, stroke ink 10 with dashed centerline cream 2 dash.
- Park: green blob bottom-left with tree circles.
- Water? skip.
- Route path: red thick dashed (stroke-dasharray 2 10, round linecap) polyline through stop points with curves: use path with C curves. Path id="routePath". Stops: 6 numbered circles (ink circle, cream number) at coordinates along path.
- Truck: group id="truck": draw cute truck: cargo box (red rounded rect with "KL" text), cab front (paper), two wheels (ink circles with cream hub), tiny chrome... plus a face? Keep truck simple but with white "KIMCHI LOCO" tiny text — at small size use just "KL". Wheels rotate? cute but skip.
- Animation: JS with path.getTotalLength, getPointAtLength; param t eases between stops; truck positioned with translate + slight rotation from tangent. Auto mode: loop through stops with pause 1.6s each; also selected day syncs: when auto reaches each stop, update the highlighted day + info panel. Clicking a day jumps/animates to that stop and pauses auto for a few seconds then resumes. This makes the page alive immediately.
Info panel next to/under map: "TODAY: <computed day>" — compute actual weekday via JS Date and mark actual today's stop ("TODAY" badge on that day). Nice touch: uses real date. If Monday → "We're closed — the kimchi is resting. Come back Tuesday hungry."
Schedule day tickets: horizontal scroll row or wrap flex; each: day Impact, time mono, location small; click selects. Active state red. Monday shows crossed-out "REST DAY".
Now, section order & rhythm:
1 nav
2 hero (+marquee)
3 story (dark)
4 menu (+heat meter)
5 find the truck (map + schedule)
6 word on street
7 footer CTA (big banner: "COMEO HUNGRY." + newsletter) & footer meta.
Also add "hours" strip maybe in footer.
Copy needs to be punchy and appetite-driving. Let me draft:
Hero tagline: "Korean-Mexican tacos from a truck that never learned to behave." sub: "Bulgogi by way of the barrio. Kimchi that bites back. Tortillas pressed while you watch — and a line that's worth it."
Story headline: "SEOUL, MEET THE TAQUERÍA." Body: "Kimchi Loco started with one argument: bulgogi belongs in a tortilla. We marinate short ribs for 48 hours in gochujang, pear and garlic, ferment our own kimchi until it's got attitude, press every tortilla to order, and top it all with sesame crema and pickled daikon that was born for this. It's not fusion for fusion's sake — it's the taco we always wanted to eat."
Steps: 01 MARINATE — short rib, 48 hours, gochujang/pear/garlic. 02 FERMENT — house kimchi, day 12 = the sweet spot. 03 PRESS — tortillas off the plancha in under a minute... "pressed to order, never sitting around". 04 STACK — "build a taco with structural integrity". 05 DRIZZLE — "sesame crema, gochujang, lime. In that order, always."
Menu items (final):
TACOS (each ~$4.25–4.75):
- BULGOGI SHORT RIB TACO — 48-hour marinated short rib, double-charred, kimchi slaw, sesame crema, scallion, lime. heat 2. $4.75. TRUCK FAVE.
- GOCHUJANG FRIED CHICKEN — double-crunch thigh, gochujang lacquer, pickled daikon, cilantro. heat 3. $4.50.
- CRISPY DUBU TOFU — gochujang-glazed tofu, kimchi pico, avocado crema (vegan if you ask nicely). heat 1. $4.00.
- "EL KIMCHI" CARNITAS? — pork belly kimchi... "PORK BELLY AL PASTOR" — pineapple-kimchi pico, achiote pork belly. heat 2. $4.75.
BURRITOS & BOWLS:
- SEOUL BURRITO — bulgogi, kimchi fried rice, black beans, pickled jalapeño... add fried egg +$1 "obviously". heat 3. $10.50.
- K-BOP BOWL — rice, choice of protein, day-12 kimchi, fried egg, crackling... "gochujang aïoli". heat 2. $11.00.
- KIMCHI QUESADILLA — "aged cheddar + day-12 kimchi griddled till crisp, scallion crema, dipped in gochujang" heat 2. $7.50. TRUCK FAVE.
SIDES & SIPS:
- ELOTE LOCO — charred corn, gochujang-miso butter, cotija, sesame, nori. heat 2. $5.00.
- KIMCHI FRIED RICE — spam (yes, spam), fried egg, sesame. heat 2. $8.00.
- DAY-12 KIMCHI — "a small bucket of our house kimchi. no, you can't have the recipe. ask again tomorrow." heat 3. $3.00.
- MISUGARU HORCHATA — "roasted grain tea meets cinnamon rice milk. dangerous." $4.00.
- YUJA LIMONADA — yuja cheong over ice with lime. $3.50.
- MEXICAN COKE? maybe "GLASS-BOTTLE COKE" $3. keep list to 5.
Prices displayed like "$4.75" or with ".75" style. Dotted leaders between name and price.
Heat meter copy list as above.
Footer: "PARKED WHERE THE LINE IS — find us before we sell out (we sell out)." Newsletter: "GET THE LOCATION DROP — every Sunday night, where we'll be next week. First horchata's on us." Input + button → inline success.
Footer bottom: "© 2025 Kimchi Loco · Seoul × CDMX × your neighborhood · no reservations, ever" — plus fake socials as small icons linking "#". Hmm "every interactive element wired" — social links with href="#" are borderline; I'll make them buttons that show a toast-ish inline note? That's weird. I'll skip social icons and keep the newsletter + contact line ("Text the truck: (213) 555-0142 — a real human answers"). Phone number as tel: link fine — tel: might be blocked? A tel: link is fine in iframe (may do nothing, but it's a link with legit purpose). I'll keep it as plain text to be safe. Or "slide into the comments at @kimchiloco" plain text. Keep simple.
Fonts double-check primary identity: Impact is the primary display — distinctive, allowed. Body Trebuchet MS — has personality. OK.
One risk: Impact not on Linux (Chrome sandbox might be Linux!). Impact is a Microsoft font; on Linux Chrome, fallback: "Arial Narrow Bold" also missing; sans-serif fallback → could be Arial/Liberation Sans or DejaVu Sans. Screenshot environment: likely macOS or Windows? Sandboxed iframe could be any. To hedge, stack: `Impact, 'Haettenschweiler', 'Franklin Gothic Bold', 'Arial Black', 'Futura', sans-serif` — Arial Black exists on Linux sometimes; DejaVu Sans Bold is chunky enough. Also I can add `font-stretch`/font-weight 900 + letterspacing to make fallbacks still feel loud, and use uppercase + tight tracking everywhere for display. Additionally the logotype could be SVG-drawn custom lettering for the hero to guarantee identity... That's a lot of path crafting for "KIMCHI LOCO" (10 letters). Impact-like letters are geometric-ish; I could approximate with stroke-based text? Not great. I'll rely on font stack + strong layout. Given typical eval environments render on macOS (Chromium), Impact isn't on macOS either! macOS lacks Impact (it has "Impact"? No — Office installs it; base macOS does not). macOS has "Arial Black" though! So stack: `'Arial Black', Impact, 'Franklin Gothic Bold', sans-serif`? But Arial Black is banned-adjacent ("Arial")? The prohibition bans Arial as primary identity font; Arial Black is a distinct display face but reads similar to Arial... risky for "overused fonts as primary identity". Hmm.
Better hedge: craft the hero logotype as inline SVG with custom-drawn chunky letters? Time-consuming but high value. Alternatively use system display stack: on macOS: "Arial Black", "Baskerville"... On Windows: Impact, "Arial Black". Linux: "DejaVu Sans" bold.
Alternative: use CSS to fake a display font with `font-weight: 900; letter-spacing: -0.02em` on a generic sans — the layout carries it. The identity here is heavily carried by the illustrated taco + red/cream palette + marquee anyway.
Compromise: stack `Impact, 'Arial Black', 'Franklin Gothic Medium', 'Haettenschweiler', sans-serif` with weight 900. On macOS it'll pick Arial Black (chunky, poster-y). On Windows Impact (condensed poster). Both are loud signage faces, fine. I'll set `font-family: var(--display)`, `text-transform: uppercase` everywhere it's used, tight line-height. Good enough, and body font Trebuchet gives the rest of identity.
Brush Script fallback on macOS: "Brush Script MT" is Windows; macOS has "Brush Script GT"? macOS has "Comic Sans MS", "Chalkboard", "Marker Felt", "Brush Script GT". Stack: `'Brush Script MT', 'Marker Felt', 'Segoe Script', 'Bradley Hand', cursive`. Fine — used only for small accents (a few handwritten notes; if it falls to generic cursive it's still ok). Actually for review notes I planned handwriting — I'll use the script stack there; fallback cursive on Linux might be ugly (DejaVu cursive → often falls to serif). Risk: notes in cursive fallback look off. Use for reviews a mono/courier handwritten-ticket style instead? Handwritten notes in "Marker Felt"/"Segoe Print"/"Bradley Hand" stack with fallback to Trebuchet italic — acceptable degradation: stack `'Segoe Print', 'Marker Felt', 'Bradley Hand', 'Comic Sans MS', cursive`. On Windows: Segoe Print; macOS: Marker Felt; Linux: falls to cursive→ could be DejaVu Sans (fine). OK.
Mono: `'Courier New', 'Courier', monospace` — universal. Good.
Now write the code. It'll be long (~1300+ lines). Structure carefully.
Key implementation details to nail:
1. Marquee:
```html
<div class="marquee"><div class="marquee-track">SPAN items ×2</div></div>
```
CSS: .marquee{overflow:hidden;background:var(--ink);border-top/bottom:3px solid var(--ink)} .marquee-track{display:flex;width:max-content;animation:scroll 28s linear infinite} each .marquee-chunk{display:flex;align-items:center;gap:...;padding-right:...}. translateX(-50%) works when track = 2 identical chunks.
2. Noise overlay:
```css
body::after{content:"";position:fixed;inset:0;pointer-events:none;z-index:999;opacity:.06;background-image:url("data:image/svg+xml,...feTurbulence...");}
```
data URI SVG: `<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>` — must URL-encode carefully.
3. Taco SVG — let me actually design coordinates now (viewBox 0 0 420 380):
- Steam: 3 paths above, e.g. at x 140,190,240 from y 150 up to 60, wavy: `M140 150 c-12 -18 12 -24 0 -42` stroke cream? Steam should be visible over cream hero bg — steam in ink? Steam colored ink with opacity 0.5 looks like sketch lines. Use ink strokes with dash animation & rising keyframes.
- Shell: `M38 230 L382 230 A172 138 0 0 1 38 230 Z`? Arc from (382,230) back to (38,230)... Let me define: bottom half-disc: `M38 230 A 172 138 0 0 0 382 230 L38 230 Z`. Check: start (38,230), arc to (382,230) — rx=172 matches half distance (172), ry=138, sweep 0 → counterclockwise from start to end... For left→right chord, sweep=1 goes clockwise → bulge up; sweep=0 bulge down. I want bulge down (dome below the chord? no wait — taco shell: it's a half-moon with flat side up and curve down — yes bulge down). sweep=0: from (38,230) arc CCW to (382,230) — CCW means going below. Yes. So shell path `M38 230 A172 138 0 0 0 382 230 Z`, fill gold, stroke ink 8.
- Add inner shell line (a second arc slightly smaller, stroke ink 5, no fill) for depth: `M58 242 A152 120 0 0 0 362 242`? approx fine.
- Fillings above the chord line y=230 — but wait, shell top edge is the flat chord; fillings sit ON TOP of the rim, overflowing upward. Draw fillings group before shell? If fillings overflow above rim, draw them behind shell so shell overlaps their bottom: draw filling group first (they extend from y~140 down to 240, bottoms hidden by shell), then shell on top. Order: steam (back, above), fillings, shell, face ON shell? Face on the shell front (below rim): eyes at (170,300) & (250,300), smile arc at (210,330). Eyes: small ink circles or happy closed arcs `\u2323`: use arcs `M160 300 q10 -12 20 0` (curve up) and same at 240; smile `M190 322 q20 16 40 0`. Add blush circles red at (140,330)&(280,330) r=10 opacity .6. Cute!
- Fillings composition across x=60..360, base line y=240 (below shell rim so tucked):
- Green ruffle (napa cabbage/kimchi slaw): big bumpy band: path with a series of arcs along top at y~170, down to 250: e.g. `M60 250 q0 -60 40 -60 q0 -40 35 -35 q5 -45 45 -45 ...` — easier: draw several overlapping circles: green blobs circles: (90,215,r=38),(135,195,42),(185,185,45),(235,183,44),(285,192,42),(330,208,38) all fill green stroke ink 6 — overlapping circles look like a ruffled mass; overlap stroke intersections will show... overlapping outlined circles create busy internal lines. Alternative: draw ONE blobby path (bumps along top, smooth bottom hidden by shell). Blob path: start at (52,260) → bump arcs across top with varying radii → end (368,260) → close along bottom (straightish, hidden behind shell rim anyway). I'll hand-write bumps:
`M52 262 C 40 200 76 176 100 190 C 106 156 150 148 162 176 C 176 140 226 140 238 172 C 258 138 306 148 310 184 C 338 168 372 196 356 238 L 360 262 Z` — hmm the C control points need care. Simpler approach with arcs (a command): sequence of semicircular bumps upward:
`M56 262 a30 30 0 0 1 44 -18 a34 34 0 0 1 52 -6 a30 30 0 0 1 48 2 a36 36 0 0 1 50 -2 a30 30 0 0 1 46 12 a32 32 0 0 1 40 22 L 370 262 Z` — arcs with relative coords; each `a r r 0 0 1 dx dy` with large-ish dx gives gentle bumps. It's hard to guarantee visually perfect blind, but with stroke ink 6 + fill green it will read as ruffled mound. Since bottom is hidden behind shell (fillings drawn before shell, shell rim covers y=230 line and below — wait fillings bottom at 262 > 230 so covered. Good.)
- Then meat chunks: 3-4 rounded rects/amoebas in brown ON the green band, slightly lower: e.g. circles/rounded blobs at (120,220), (210,215), (300,225) size ~26, fill #8B4A2B stroke ink 5. Place them lower so they overlap green and dip behind rim? They must be drawn after green but before shell. Their lower parts at y~245 also hidden by shell. Good.
- Gochujang drizzle: red stroke squiggle across the fillings top: path `M110 178 C 150 150 180 200 210 168 C 240 136 270 186 300 158` stroke red 8, round cap, drawn above fillings, below shell. Also drizzle over the shell face? A red squiggle over shell bottom is cute too: skip, keep face clean.
- Sesame: 5-6 small cream ellipses on meat/green.
- Red cabbage flecks: few small red triangles/circles on green band: small red circles r=7 at scattered (140,200),(250,190),(330,215). Nice color pop.
- Lime wedge: on the right, leaning: group at (355,205): a circle sector — half-lime: circle r=26 fill gold-green? lime green #A8C93A with ink stroke, plus inner lines (segments) as strokes from center. Position it partially behind shell rim too. Place lime LEFT side for balance instead since right has nothing? Fillings band spans whole width; lime can sit right, above rim at (350,200): it'll overflow above rim — fine, draw before shell so bottom tucked.
- Tomato/pico? enough.
- Under-shell shadow: an ellipse ink opacity .15 under taco at y=372.
Face: as planned on shell.
Hero right column: the taco SVG with position relative; absolutely positioned stickers:
- sticker 1 (top-left, rotate -8deg): circular badge SVG? CSS circle with border ink 3px, bg red, cream text "HOT · FRESH · LOCO" curved text? curved needs SVG textPath — do simple: a rotated rounded rect badge: "48-HR BULGOGI" gold bg.
- sticker 2 (right, rotate 6deg): "PRESSED DAILY" cream bg ink border.
- sticker 3: small circular red with star icon + "TRUCK FAVE".
Add slow float animations (translateY oscillation).
Hero left column:
- small overline: "김치 로코 — KOREAN BBQ × STREET TACOS" with red bar.
- H1: "KIMCHI" (cream? no—ink) then "LOCO" in red, huge Impact, with slight rotation on "LOCO" and an outline offset layer? Use text-shadow hard offset: `text-shadow: 6px 6px 0 var(--gold)`? Text-shadow as hard offset solid = poster style, not glow — allowed (it's a hard shadow, not blur). Yes: KIMCHI ink with gold hard shadow; LOCO red with ink hard shadow, rotated -2deg.
- Tagline paragraph.
- CTA row: primary button red "SEE THE BOARD" with hard shadow; secondary outline "FIND THE TRUCK".
- Meta row: "TUE–SUN · 11–LATE" + pin icon + "3rd & Vermont, K-Town" etc. small mono.
Open stamp: rotated badge "OPEN / TUE–SUN" circular double-ring in red, top-right of hero left column or overlapping the H1. Nice authored detail: CSS circle with dashed inner border rotated animation (rotating slowly). Yes! rotating dashed ring — like a stamp — signature-ish.
Now nav: cream bg, border-bottom ink 3px. Logo: small taco SVG (simplified 40px) + "KIMCHI LOCO" Impact. Links: mono uppercase small, hover red underline thick. "ORDER" button small.
Story section (dark ink bg): grid 2 col: left headline + para (cream text, red underline highlights via border-bottom on strong or `<mark>`-styled span with red bg cream text — highlighter effect: background red, color cream, slight rotate. Nice.) Right: steps list (mono numbers in red circles... small squares) — steps rows with top border dashed cream. Add big Hangul watermark "김치" outlined faint in bg (stroke text via -webkit-text-stroke cream low opacity) — decorative but authored, large; keep opacity ~0.12. That's fine (not low-contrast content, decoration).
Also add stats row? like "48 HRS marinated · 12 DAYS kimchi · 90 SEC pressed · 0 REGRETS" — as a strip between story and menu — mono, red separators. Punchy. Put as the story footer strip.
Menu section: cream bg. Awning top over board. Board panel: paper bg, ink 3px border, hard shadow 10px. Inside: header "THE BOARD — <date>", tabs. List rows. Heat meter strip at board bottom (border-top dashed). Also note: "SOLD OUT BY 8PM USUALLY. YOU'VE BEEN WARNED." small stamp text.
Tabs: buttons with active = red bg cream text. JS toggles panels.
Add-to-ticket buttons: circle/square 34px ink border, cream bg, plus icon; hover invert red. Row layout: name+heat on one line with dotted leader to price; description below; ADD button right. Use grid: `grid-template-columns: 1fr auto` and inside. Keep tidy.
Truck section: cream? alternate: paper bg with red border top/bottom? Give this section ink bg? Dark for map could look nice — cream map elements pop. Let me keep section cream, map panel paper with ink border. Actually make map panel the paper with streets drawn ink — good contrast. Schedule day tickets above map (wrap). Info line below map: shows selected day details in mono.
Reviews: gold section background? A marigold section adds palette punch: bg gold with ink text — bold and appetizing. Notes paper bg with tape. Yes, reviews on gold.
Footer CTA: red bg cream text "COME HUNGRY. LEAVE LOCO." huge Impact + newsletter form (input + button, ink border, hard shadow). Footer bottom: ink bg cream mono small print.
Order pill/ticket: fixed bottom-right, z-index high. Pill: ink bg cream text with ticket icon + count + total. Panel: paper, ink border, hard shadow, width 320, max-height 60vh scroll. Courier font. Buttons +/− small squares.
JS structure:
```js
const $ = s => document.querySelector(s); ...
// date
// menu tab switching
// heat peppers injection + loco-meter
// order state + render
// map animation
// newsletter
// smooth scroll handled by CSS
```
Truck map animation details:
- const route = $('#routePath'); L = route.getTotalLength(); stops at fractions f[i] = route length at stop positions — compute by getting lengths: easier define stop indices as length fractions: I'll place stop circles exactly at known fractions: with 6 stops along a path, fractions [0.02,0.21,0.4,0.59,0.78,0.97]. But circles positioned in SVG need coords matching path points at those fractions — compute in JS: at init, for each fraction compute point and set circle positions via JS (cx/cy), guaranteeing alignment.
- Animation loop: state = {seg: current stop index, t}. Auto: move from stop i to i+1 over ~2.2s with easeInOut, pause 1.4s, etc. looping (wrap 6→0... wrap with route continuity: from stop 6 back to 1? just modulo through list cycling: after last, loop back to first — truck drives "back" along path (t decreasing) or jumps? Jumping looks teleporty; simpler: cycle i from 0..5 then back 5..0 (ping-pong). Fine — like the truck retracing its route.)
- When arriving at stop i: highlight day ticket i (day order: stops correspond to TUE..SUN with MON closed; 6 stops = 6 days). Update info panel text.
- User clicks day ticket: animate truck to that stop quickly (or if same, just pulse), set "manual hold" timer 5s then resume auto.
- Truck transform: `setAttribute('transform', translate(x,y) rotate(a))` where a from tangent: sample p(t+ε)-p(t-ε). Truck drawn around origin at (0,0) center-ish with wheels at y=0 baseline; rotate might flip on return direction — when ping-ponging back the angle will be ~180 flipped → truck upside down! Solution: compute angle, and if truck would be upside-down (angle suggests leftward travel), mirror with scale(1,-1)? Standard trick: keep rotate(angle) but draw truck pointing right; when returning, rotate to angle+180 and scale(1,-1) flips it back upright? Hmm: rotating 180 makes truck upside down; scale(-1,1) mirrors horizontally making it point left but upside... Let me think: truck pointing right, rotation r. Moving right: r≈0..small. Moving left: tangent angle ≈180°. If I apply rotate(180) then scale(1,-1) (flip vertical AFTER rotation) → net effect: pointing left, upright. Yes: transform = `translate(x,y) rotate(deg) scale(flip,1)` where flip=-1 when moving left... let me verify: point right vector (1,0). rotate 180 → (-1,0) pointing left but upside down. scale(1,-1) → mirrors y: upside-down fixed → still pointing left, upright. But wait scale(1,-1) after rotate(180): combined matrix rotate(180)*scale(1,-1) = ? Apply to truck drawn pointing right: first scale(1,-1): pointing right, flipped vertically (upside down). Then rotate 180: pointing left, and upside-down becomes right-side up. Yes! Correct. So when |angle|>90 use rotate(angle+180) + scale(1,-1)? Hmm I applied rotate(180) not angle+180. General: if angle in (90,270), use transform rotate(angle+180) scale(1,-1). Check angle=180: rotate(360)=rotate(0) + scale(1,-1): truck points right, mirrored vertically = upside down pointing right. Wrong! Let me redo: we want net orientation = pointing along -x (left) while upright.
Transformation T applied to artwork. For leftward travel: desired: truck mirrored horizontally (pointing left), not rotated. That's just scale(-1,1)! For rightward: rotate(angle small). So: if angle indicates leftward (|angle| > 90), apply scale(-1,1) then rotate(180-angle)? Hmm. Let's think differently: tangent direction d = (dx,dy), pointing left means dx<0. Desired final pointing = d, upright. Truck art points +x. Option: rotate by θ where θ = atan2(dy,dx) gives pointing along d but possibly upside down when dx<0. Fix: when dx<0, instead rotate by (θ - 180)?? that points opposite... then scale(-1,1) mirrors to point along d. Rotate(θ-180): truck points opposite of d = -d = (+|dx|, -dy)... then scale(-1,1) → (−|dx|, dy) = d? scale(-1,1) transforms vector (x,y)→(−x,y). Rotate(θ-180) applied to (1,0) gives (cos(θ-180), sin(θ-180)) = (−cosθ, −sinθ) = (−dx,−dy)/|d|. Then scale: (dx,−dy)/|d| — hmm that's d with y negated — not equal to d unless dy=0.
Cleaner: matrix M = scale(sx,1) · rotate(φ) where rotate applied first (SVG applies left-to-right as outer-to-inner: transform="scale(sx,1) rotate(φ)" means scale AFTER rotate in point terms... In SVG, transform list applies left-to-right: translate then scale then rotate means point p → translate(scale(rotate(p)))? Actually `transform="A B"` = A·B, applied B first to coordinates. So `transform="rotate(φ) scale(sx,1)"` scales first then rotates: p' = R·S·p. We want: choose S = scale(sx,1) then rotate φ such that truck's +x ends along d with up-ish. Take φ = atan2(dy, dx) if going right (dx≥0), sx=1. If going left (dx<0): choose sx=-1 (mirror to point left), and we need vertical direction: mirrored truck pointing left with rotate φ'= atan2(dy, −dx)?? Mirrored truck points along −x. Then rotate φ' rotates −x to direction... we want final pointing = d. −x rotated by φ' → (−cosφ', −sinφ'). Want = (dx,dy)/|d| → cosφ' = −dx/|d|, sinφ' = −dy/|d| → φ' = atan2(−dy, −dx) = angle+180 (mod 360). So transform = `translate(x,y) rotate(angle+180) scale(-1,1)` with scale applied first. In SVG attribute: `transform="translate(tx,ty) rotate(deg) scale(-1,1)"` — the rightmost applies first: scale(-1,1) then rotate then translate. That matches: p → T·R·S·p. With deg = θ+180 where θ=atan2(dy,dx) (in degrees) when moving left. Let me verify concrete: moving left purely: d=(−1,0), θ=180. deg=360→0. S=scale(−1,1): truck now points −x (left). R=rotate(0). Result: pointing left, upright. ✓. Moving up-left: d=(−0.7,−0.7): θ=225 (pointing up-left; in screen coords dy negative = upward). deg=225+180=405→45°. Mirrored truck (points left, up remains up). Rotate 45° clockwise (SVG positive = clockwise since y down): truck now points... mirrored truck nose at (−1,0)→ rotate 45°: (−cos45, −sin45)=(−0.707,−0.707) = up-left ✓. And "up" side of truck: mirrored up = (0,−1) (up); rotate 45° cw: (0,−1)→(sin45, −cos45)=(0.707,−0.707) hmm that's now tilted right-up — for a truck on a 45° slope going up-left, the roof should tilt... the roof normal should be perpendicular to travel pointing up-right: travel up-left (−0.7,−0.7), perpendicular candidates (−0.7,0.7) (down-left) or (0.7,−0.7) (up-right). We got (0.707,−0.707) = up-right ✓.
So: if dx>=0: `rotate(θ) scale(1,1)`; else `rotate(θ+180) scale(-1,1)`. Also limit rotation magnitude for style: maybe clamp angle tilt to ±12° for a "cute" feel — trucks on maps look better mostly-horizontal. Simpler & cuter: clamp: deg = clamp(θ, −12, 12) when moving right; when moving left: mirrored + clamp similarly. I'll clamp — cuter. So: movingRight = dx>0; tilt = clamp(atan2(dy,dx)*180/π, −14,14); transform = translate(x,y) rotate(tilt) [scale(-1,1) if left].
Truck SVG group (drawn pointing right, origin at rear-bottom-left? center at contact point): let me draw around (0,0) = center of truck, width ~64, height ~34:
- cargo box: rect x=-32 y=-30 w=40 h=26 rx=4 fill red stroke ink 3 → covers x −32..8.
- cab: path x=8..30: `M8 -30 h14 a?` simpler: rect x=8 y=-24 w=22 h=20 rx=5 fill paper stroke ink 3; windshield: line.
- window: small ink rect.
- wheels: circles at (-20,0) and (18,0) r=7 ink fill, cream hub r=2.5. Baseline y=0.
- Under-carriage: rect x=-32 y=-8? The wheels at y=0 r=7 → bottoms at 7. Body bottom at y=-4 (rect y=-30 h=26 → bottom −4). Wheels center y=0 overlap body bottom. OK.
- Text "KL" cream Impact 10 on box, plus tiny taco? "KL" fine. Face? skip for map.
- Little smoke puffs behind? skip; add motion dashes: two short ink lines behind truck (animated dash offset) — draw separately in truck group so they move along. Cute: exhaust puffs appear when moving — skip complexity.
Stops circles: r=13 ink fill, cream ring stroke cream 3? On paper bg: ink filled circle, cream number text, plus red outer ring on ACTIVE stop (JS toggles). Add label chips next to stops? The schedule covers names; map pins get numbers 1-6.
Route path through 6 points, viewBox 720×360. Points:
1 (80, 90) — top-left
2 (250, 60) — top-middle
3 (560, 120) — right
4 (480, 250) — bottom-right
5 (260, 290) — bottom-middle
6 (90, 210) — left
Path: M80 90 C 150 70, 180 60, 250 60 S 460 80 560 120 C 600 140? Let me write smooth: `M80 90 C 140 78 190 60 250 60 C 330 60 480 84 560 120 C 600 138 520 220 480 250 C 440 280 320 300 260 290 C 190 280 110 250 90 210 C 78 178 72 128 80 90` roughly a loop. Fractions for stops won't be 0.02.. evenly; but I place circles via JS at fractions along path — they might not land exactly at the visual waypoints but they'll be ON the route which is what matters. Use fractions: [0.03, 0.2, 0.42, 0.6, 0.78, 0.95]. Truck drives along whole loop continuously; ping-pong or full loop? Full loop cycling 1→6 then wrap to 1 by continuing past 6 around to start (t from f[5] to 1 then 0 to f[0])? The path is closed-ish visually (ends near start). Let me make it a fully closed loop: Z at end, so truck can drive continuously in one direction! Route = closed loop path with Z. Then animation: t advances 0→1 continuously, stops at the 6 fractions with pauses. Elegant, no mirroring needed (always moving one direction around loop — direction depends on path winding; fine, tilt handles it, and no left-flip needed if the loop always travels rightward at each stop? Not guaranteed. Keep the mirror logic anyway — it's 5 lines.)
Streets background: draw grid: horizontal streets y= 60? Let me draw:
- bg rect paper.
- streets as ink lines: stroke ink 12, opacity? Solid ink streets too heavy; use #E9DCC3 (darker cream) blocks? Simpler stylized "map": paper bg + street grid strokes in a warm tan (#E7D9BE) thick 14, with dashed cream centerlines 2 dash 6 6. Blocks implicitly gaps. Park: green blob circle-ish path at center-left? The route should read on top. Add park at (600,300) corner: rounded rect green with 3 tree circles (darker green ink-outlined). Add a "river"? skip. Add block outlines: skip, streets suffice + few tiny building rects ink-outlined paper fill near corners (3-4). Add compass rose small (N arrow) top-right and scale bar — authored details. And "K-TOWN" label, "ARTS DISTRICT" labels in mono tiny — mapy.
North arrow: circle + N.
Labels for stops — small text numbers only.
Info panel content per day:
```
TUE — GRAND CENTRAL MARKET · 11AM–3PM · "short lines before noon"
WED — WILSHIRE & WESTERN (the Wiltern curb) · 5–9PM · "happy hour = free elote topping"
THU — ARTS DISTRICT BREWERY ROW · 5–10PM · "we pair with IPAs, fight us"
FRI — KOREATOWN NIGHT MARKET · 6–11PM · "the long-line night; come early"
SAT — SILVER LAKE RESERVOIR LOOP · 10AM–6PM · "dog-friendly, dog jealous"
SUN — SMORGASBURG L.A. · 10AM–4PM · "sold-out o'clock is ~2pm"
MON — CLOSED · "the kimchi rests. so do we."
```
Map only has 6 stops (TUE–SUN). Monday ticket shows closed state and pauses truck? Clicking Monday: truck does "rest" — show closed info; maybe truck fades/gray? Just update info; truck keeps strolling. Fine.
"Today" highlight via real Date().getDay(): map day index: Tue=2... store day names array; compute today match → badge "TODAY" on that ticket.
Reviews: 3 notes + 1 big pull-quote across top: e.g. `"Certifiably, deliciously loco." — THE STREET FOOD LEDGER ★★★★★` as a big banner line with stars (SVG star icons filled). Notes below in staggered flex.
Newsletter: form onsubmit preventDefault, validate contains "@" & ".", show inline success replacing form or below: "You're in. Sunday night, check your inbox. First misugaru horchata's on us — flash this screen at the window." with a tiny stamp. Errors inline red text if invalid.
Order ticket numbers: order counter increments (#KL-0042 style, start random 37).
Also small toast? Not needed.
Focus on zero console errors: guard all query selectors.
Let me also handle reduced motion? Nice-to-have: `@media (prefers-reduced-motion: reduce)` disable marquee/steam animations. Add cheaply.
Responsive breakpoints:
- ≤1024: hero grid stack? Keep 2col until 900.
- ≤900: hero single column (taco below text, smaller), story single col, map panel full width, schedule wraps, menu list 1 column (column-count:1).
- ≤560: nav links hidden except ORDER button (hamburger? just hide links, keep logo + order). Marquee fine. Buttons wrap. Ticket panel width calc(100vw - 24px).
- No horizontal scrollbar: ensure marquee overflow hidden, svg max-width 100%.
Font sizes with clamp for h1: clamp(3.4rem, 9vw, 7.5rem).
Nav mobile: hide .nav-links under 760px (no hamburger complexity) — but then links unusable on mobile; acceptable? Better add simple details/summary? Keep it simple: under 760 show only logo + "FIND US" + ORDER? Hide all links — CTA buttons in hero cover navigation. OK.
Ticket pill on mobile: keep bottom-right but smaller.
Let me now also decide the "heat" inline icons: I'll inject via JS: for each `.menu-item[data-heat]`, append N filled peppers + (5−N) empty (stroke only) — show max 3 scale? Use scale 0–3 peppers on items (3 = hottest) — simpler: show filled count only with small "HEAT" label? Show 3 slots. Items heat values: use 0-3. loco-meter 0-5 separately with bigger peppers. Consistent: menu items 0–3 peppers scale.
Symbols needed: pepper, star, pin, clock, flame?, plus, minus, x, ticket, arrow-right, truck? I'll inline as `<svg style="display:none"><symbol id="i-pepper">...` and use `<svg class="icon"><use href="#i-pepper"/></svg>`.
Pepper symbol path (viewBox 0 0 24 24, stroke ink, fill red for filled variant): Let me craft a decent chili:
body: `M15.5 21c-4.2.8-8-1.3-9.3-5-1.2-3.4.3-6.9 3.4-8.3 2.5-1.1 5.2-.6 7.2 1.1-.6 2.9-2 5.2-4.2 6.6 2.7-.2 5.2-1.9 6.8-4.6.9 3.8-1.1 7.6-3.9 9.5?` I'm overcomplicating. Let me design a simple readable chili: a tapered curved pod from top-left to bottom-right:
`M5 4 C5 9 5 13 9 17 C12 20 17 21 20 18 C17 18 13 16 11 12 C9 8 9 6 9 4 Z`? Let me reason: start (5,4) at stem; down along inner curve to (9,17); to tip (20,18); back along outer curve to (11,12)→(9,4). Hmm the shape: pod hanging: stem at top (9,3), body bulges left-down to (5,14), tip at (18,18). Path: `M9 3 C5 6 4 11 7 15 C10 19 15 20 18 18 C15 18 11 15 10 11 C9.4 8 9 5.4 9 3 Z` + stem stroke `M9 3 C9 2 10 1.5 11.5 1.5`? Stem: `M9 3 Q9 0.8 11 1` meh. I'll accept a slightly abstract chili: with fill red + stroke ink 2 + green stem triangle at top. Let me finalize:
```svg
<symbol id="i-chili" viewBox="0 0 24 24">
<path d="M10 2.6c-.4 1.2-.2 2.2.5 3" fill="none" stroke="currentColor"?
```
For flexibility: symbol uses `fill="currentColor"` and separate stroke. In filled peppers: class sets color red; empty: color transparent with ink stroke? Stroke can't switch via currentColor easily... Approach: two symbols: #chili-on (fill red, stroke ink) and #chili-off (fill none, stroke ink, opacity .35). Duplicate paths with different attrs. Simple.
Chili path final attempt (I'll trust it visually):
`d="M14.8 3.2c.9-.7 2-1 3.2-.8-.2 1.3-.9 2.3-2 2.9l-1.4.7c.3 3-1 6.1-3.5 8.3-2.6 2.3-6 3-8.6 1.9 2.9-.4 5.5-2 7.2-4.5 1.7-2.6 2.1-5.6 1.4-8.5z"` — eh this is a swirl.
OK simplest credible chili: ellipse-ish pod rotated with a point:
`d="M5 15c0-5 4-9 9-9 3 0 5 2 5 5 0 5-4 9-9 9H9c-2.4 0-4-2-4-5z"` — that's just a blob, reads as pepper? With a stem `M14 6c0-2 1-3.5 3-4` on top-right and a little curve it reads as a pepper-ish. Honestly at 14px, a red rounded pod + green stem reads "chili". I'll do:
- pod: `M4.5 16.5c-.8-5 2.8-9.7 8-10.6 2.6-.4 5 .5 6.5 2.3-1 .4-2.3.4-3.3 0-.3 5.4-3.4 9.6-7.7 11.2-1.9.7-3.2.3-3.5-2.9z"` I keep freehanding. Let me just be decisive with a clean crescent pepper (like a curved banana shape):
`d="M16.6 4.8c1.9-1.4 4.4-1.6 6.4-.6-1.5.6-2.7 1.7-3.4 3.2-1.5 3.8-4.3 6.9-8 8.8-3 1.5-6.3 2-9.4 1.6 1.3-1.9 3.2-3.4 5.4-4.3 3.3-1.3 6.3-3.7 9-8.7z"` — nah.
Decision: use a FLAME icon instead — universally readable as "heat", and Lucide's flame path is a known-good shape I can reproduce from memory (it's fine to use open-source icon geometry — effectively an icon library). Flame reads perfectly for heat scale. For menu heat I'll use small flames; loco-meter uses bigger flames.
Flame path (24×24): `M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z` — filled version works with fill-rule. Empty version: same path stroke ink 2 fill none.
Pepper for marquee separators — I'll use the flame there too for consistency, or a star. Use flame everywhere = coherent heat identity. But "Kimchi" identity might want a chili... a flame is fine and clean. Go flame everywhere. Also garlic? no.
Star icon path: `M12 2l2.9 6.26L21.5 9.3l-4.75 4.4 1.15 6.55L12 17l-5.9 3.25 1.15-6.55L2.5 9.3l6.6-1.04L12 2z` (approx classic star). Fine.
Pin: `M12 21s-7-5.1-7-11a7 7 0 1 1 14 0c0 5.9-7 11-7 11z` + circle r=2.5 at (12,10). Standard.
Clock: circle + hands.
Plus: `M12 5v14M5 12h14`. Minus: `M5 12h14`. X: `M6 6l12 12M18 6L6 18`. Ticket icon: rect with notches — `M3 8a2 2 0 0 0 0 8? ` use: `M4 5h16v4a2 2 0 0 0 0 6v4H4v-4a2 2 0 0 0 0-6z`? Ticket shape: `M2 8V6h20v2a2 2 0 0 0 0 4? ...` Let me: path `M4 6h16v3.5a2.5 2.5 0 0 0 0 5V18H4v-3.5a2.5 2.5 0 0 0 0-5z` hmm arcs with same start/end tricky. Simplify ticket: rect + dashed vertical line: `<rect x="4" y="5" width="16" height="14" rx="2"/><path d="M13.5 5v14" dash>`. Good enough at small size. Or use lucide "receipt": `M4 2v20l2-1 2 1 2-1 2 1 2-1 2 1 2-1 2 1V2l-2 1-2-1-2 1-2-1-2 1-2-1-2 1-2-1z` plus lines. I'll do the receipt zigzag — nice for order ticket pill! receipt: `M4 3h16v18l-2.7-2-2.6 2-2.7-2-2.7 2-2.6-2L4 21z"` approximate zigzag bottom: simpler keep rect with perforation. I'll use: `<path d="M5 3h14v18l-2.3-1.8L14.4 21 12 19.2 9.6 21l-2.3-1.8L5 21z"/><path d="M9 8h6M9 12h6"/>`? ok decent receipt. Whatever — small icons, keep tasteful.
Arrow: `M5 12h14m-6-6 6 6-6 6`.
Instagram/phone not needed if I skip socials. I'll include just a text line.
Alright — also "click or press a key to start" — not needed, this is a website not a game. Keyboard: iframe receives keys only after click; I don't rely on keyboard except form input & slider (mouse-based). Fine.
Now — the screenshot at 3s: hero fully painted, truck animating on map is below fold (fine, hero is the star). Maybe add a tiny "scroll" hint. OK.
One more idea to boost "want to come": scarcity/social proof in hero: "sold out 14 nights running" mono line under CTAs. Yes: "⚠" no emoji — small flame icon + "SOLD OUT 14 NIGHTS RUNNING. COME EARLY." Actually put that in marquee too.
Let me write the final HTML now, carefully. I'll aim for clean organized CSS with custom properties. Approximate line count is large; that's fine.
Also: define `--shadow: 6px 6px 0 var(--ink)`.
Buttons: `.btn { border:3px solid ink; background red; color cream; font display; padding; box-shadow 4px 4px 0 ink; translate on hover; active press }`.
Dotted leader: `.leader { flex:1; border-bottom: 3px dotted rgba(ink,.35); margin: 0 10px 6px; }`.
Now the menu data in HTML directly:
TACOS list (4 items), BURRITOS/BOWLS (3), SIDES & SIPS (5).
Item HTML template:
```html
<li class="dish" data-heat="2">
<div class="dish-line">
<h4>BULGOGI SHORT RIB</h4>
<span class="leader"></span>
<span class="heat" aria-label="heat"></span>
<span class="price">$4.75</span>
<button class="add" data-name="Bulgogi Short Rib Taco" data-price="4.75" aria-label="Add">+svg</button>
</div>
<p>48-hour marinated short rib...</p>
<span class="fave">TRUCK FAVE</span> (conditional)
</li>
```
Heat span filled by JS.
Fave sticker: absolute rotate.
Loco-meter HTML:
```html
<div class="loco-meter" id="locoMeter">
<div class="lm-head"><flame icon> HOW LOCO ARE YOU?</div>
<input type="range" min="0" max="5" value="2" id="lmRange">
<div class="lm-read"><span class="lm-num">2</span>/5 <span class="lm-label">…</span></div>
</div>
```
Style range: appearance none; track ink 4px? thumb: 26px square red border ink rotated 45deg (diamond) — appearance-none thumb with transform not supported... thumb can't rotate via transform reliably; use square thumb red with ink border. Fine. Cross-browser: include -webkit-slider-thumb & -moz-range-thumb.
At value>=4: add class .meltdown → panel bg shifts to red? tint: background gold→red, label shakes (keyframes). Fun.
Labels array indexed 0..5:
0: "GRINGO MILD — extra crema, zero judgment. we respect you."
1: "WARM — like a hug from your halmoni."
2: "SPICY ROOKIE — you'll feel it. you'll like it."
3: "PROPERLY LOCO — double your napkin budget."
4: "FIRE BREather" hmm: "FIREBREATHER — milk is not a weakness, it's a strategy."
5: "FULL LOCO — the kitchen cheers when you order this."
Order ticket markup:
```html
<div class="ticket-widget" id="ticketWidget">
<button class="ticket-pill" id="ticketPill">
<svg receipt icon> YOUR TICKET <span id="pillCount">· 0</span> <b id="pillTotal">$0.00</b>
</button>
<div class="ticket-panel" id="ticketPanel" hidden>
<header>· KIMCHI LOCO ·<br>ORDER TICKET <span id="tkNo">#KL-0038</span></header>
<ul id="tkItems"></ul>
<div class="tk-total">TOTAL ... $0.00</div>
<button id="callIn">CALL IT IN</button>
<p class="tk-note">pay at the window — cash, card, apologies accepted</p>
</div>
</div>
```
JS renders items with qty controls. Panel toggling: pill click toggles. Auto-open on first add? Nice: first item added → panel opens briefly (or pill bumps with count). I'll auto-open on first add — showcases the feature. If user closes, don't auto-open again.
CALL IT IN: if empty → inline note "ticket's empty — add something loco first." If items: show confirmation block replacing list for ~5s: "ORDER #KL-0038 LOCKED IN — pull up in ~12 min. say 'LOCO' at the window for a free horchata upgrade." then reset order state, update pill. During confirm state, disable call-in. Good.
Ticket item row: `2× BULGOGI SHORT RIB TACO ..... 9.50` with [−][+]. Use flex, mono font.
Also small remove via −.
Now the map auto-schedule sync: day tickets list TUE..MON(7). Map stops 6 (TUE..SUN). When truck arrives stop i (0..5) highlight ticket i and set info. Today badge: compute (getDay(): 0=Sun..6=Sat) → map to day names.
Truck pause/rotate: implement with a state machine using rAF timestamps:
```
phases: 'drive' (move along path between stop k and k+1 over dur), 'pause' (wait)
```
Simpler: maintain `u` = global progress in [0,1); precompute stop fractions; velocity: drive from f[k] to f[k+1] linearly with easing; I'll implement segment-by-segment: current segment idx seg, progress p in [0,1]; duration D=2600ms drive, P=1500ms pause. Position t = lerp(f[seg], f[seg+1], ease(p)) (loop: f[6] = f[0]+1 for wrap using closed path). Closed path: position length = (t mod 1)*L. With closed loop, wrap continuous. Stops at fractions f[0..5]; f[6]=f[0]+1.
Highlight day when arriving at stop index = seg+1 mod? When entering pause phase at stop index k=(seg+1)%6.
Manual click day j: set seg such that we animate to stop j: set current target: I'll implement a simpler "goto" override: set mode='goto', startT=current length position, endT=f[j] (choose direction forward along loop; delta = (f[j]-pos+1)%1; duration = delta*6000ms min 500). During goto, no stop-highlight until arrive; on arrive: pause 1200? then resume auto from seg=j. Also update info immediately on click.
Compute current pos length: track pos in fraction units globally.
Ease: easeInOutCubic.
Truck placement each frame: pt = route.getPointAtLength(pos*L); tangent via pos±0.004 (wrap). Set transform.
Also wheels bounce: translate truck ±1px y via sin(t) — subtle, skip or cheap add: add a tiny y wobble in transform translate y + Math.sin(now/90)*1.2. Cute, keep.
Stop circles positioned by JS at init: for i, pt at f[i]*L → set circle cx/cy and label x/y.
Active stop ring: red circle stroke around active index.
Map other details: compass, scale, labels, park, buildings. Also a "YOU SMELL THIS?" no. Add dashed route: stroke red 5, dasharray "1 12"? With linecap round, dash "0.1 14" gives dotted — nice dotted route (pebbles). Use stroke-width 7, dasharray "0.5 16", linecap round → dotted trail.
Legend chip: red dotted line = "this week's route", flame = "we run out around 8". Small mono legend under map maybe integrated in info panel.
OK — info panel under map: mono block:
```
NEXT STOP / CURRENT STOP:
TUE · GRAND CENTRAL MARKET · 11AM–3PM
"short lines before noon"
```
Plus "TODAY IS TUE — that's today!" when matches.
Alright, the marquee items: "48-HOUR BULGOGI TACOS", "KIMCHI FERMENTED DAY 12", "MISUGARU HORCHATA", "TORTILLAS PRESSED TO ORDER", "SOLD OUT 14 NIGHTS RUNNING", "GOCHUJANG ON EVERYTHING", "ELOTE LOCO", "FRIED EGG? OBVIOUSLY." with flame separators. Duplicate chunk twice.
Section headings style: Impact big with red flame icon + small mono overline. Consistent.
Story stats strip: "48 HRS marinated | 12 DAYS kimchi | 90 SEC pressed | 0 REGRETS" — style as mono with big Impact numbers? A row of 4 stat blocks separated by dashed borders — careful "uniform equal modules" prohibition... it's a stat strip not filler; vary: make it inline text row: "48 HRS IN THE MARINADE · 12 DAYS IN THE FERMENT · 90 SECONDS FROM DOUGH TO TORTILLA · 0 REGRETS SO FAR" as a single mono line, wrapping. That avoids card grid feel. Or big numbers row. I'll do a flex row with dividers, numbers in Impact red, labels mono cream — different widths naturally. OK.
Footer CTA: red bg: "COME HUNGRY. LEAVE LOCO." + newsletter. Footer ink: columns? avoid uniform; do: left big text "KIMCHI LOCO 김치 로코" + right small mono lines (hours, radio, contact) + bottom bar tiny.
Now write it all. I'll be careful with JS string templates and encoding of the noise data-URI (use %23 for #, single quotes).
Noise URI:
`url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='140' height='140' filter='url(%23n)' opacity='1'/></svg>")` — with opacity on body::after of .05. Rect defaults black; turbulence replaces fill — feTurbulence output is colored noise; fine at 5%.
Also add subtle paper-edge rotation? skip.
Selection color styling `::selection{background:var(--red);color:var(--cream)}` — nice touch.
Scrollbar styling? optional, skip (iframe).
Let me now carefully write the taco SVG code block:
```html
<svg class="taco-svg" viewBox="0 0 420 400" aria-hidden="true">
<!-- steam -->
<g class="steam">
<path class="s1" d="M150 160c-14-14 14-22 2-44" />
<path class="s2" d="M205 152c-16-16 16-24 2-48" />
<path class="s3" d="M262 160c-14-14 14-22 2-44" />
</g>
<!-- lime -->
<g class="lime">
<circle cx="352" cy="196" r="30" />
<path class="lime-lines" d="M352 166v60M322 196h60M331 175l42 42M373 175l-42 42" />?
</g>
```
Lime: wedge look: circle fill #B9D34A stroke ink 7, inner segment lines stroke ink 4 clipped to circle — a full circle with cross lines = lime slice. Place at (352,190) r=30, its bottom tucked behind shell rim (rim y=230; circle bottom 220 — barely tucks; lower cy to 200 → bottom 230 = rim. ok cy=202, r=30 → bottom 232, slight tuck). But fillings green band overlaps region x330.. the lime overlapping the slaw is fine (lime on top of slaw, tucked into shell) — draw lime AFTER fillings, BEFORE shell.
Fillings draw order: slaw band (green), red flecks, meat blobs, drizzle, sesame, THEN lime, THEN shell, then face, blush.
Wait — sesame on meat: small cream ellipses w/ ink stroke 1.5? Keep no stroke, just cream, r 3-4, they read on dark meat. Add few.
Slaw path: bumps across x 58..362, top around y 150-175, bottom to 262:
`M58 262 C40 210 62 178 92 186 C100 156 140 148 156 170 C170 142 216 140 230 166 C250 136 292 146 300 176 C328 162 358 186 352 224 L360 262 Z`
The C curves: each bump: from current point, control points define a hump. My quick curves may produce lumps of varying height — organic is good. It closes bottom edge with straight-ish line (hidden). Also, hmm: path fill green (#7FA93C), stroke ink 7, linejoin round. It'll read as fluffy mound.
Red flecks: 4 small circles r 6-8 fill red stroke ink 4 at (118,196),(196,172),(268,182),(322,206)? Some on the slaw band. Wait band occupies y ~150-262, so flecks on it at y 170-210 fine.
Meat blobs: 3 rounded shapes fill #9C5B2E stroke ink 6: use rounded rects rotated? Use blob paths:
- `M96 214c-10-16 4-34 22-34s30 14 26 32c-2 8-8 14-16 16-14 4-26-2-32-14z` eh. Just circles r≈17 at (120,212),(206,206),(296,214) + smaller (160,196),(248,196)? Overlapping circles of same fill: internal strokes visible... draw meat as single path of merged bumps like slaw but shorter span, drawn lower:
`M92 250 C86 216 108 196 132 206 C138 186 166 182 176 200 C190 180 220 182 228 202 C250 184 278 192 280 214 C304 206 322 224 316 248 Z` — meat band below slaw bumps. fill #9C5B2E stroke ink 6. Tops around y 180-210, overlapping slaw band bottom? Slaw bottom hidden behind... wait now BOTH slaw and meat bands' bottoms hidden behind shell rim (y≥230 hidden). The meat band top ~y180 sits on slaw. But slaw band center around y160-220... the layering: slaw drawn first (its lower area covered later by meat? no—meat drawn after covers part of slaw). Visual: slaw peaks on top, meat chunks mid, shell bottom. Should read fine: green ruffles with brown chunks nested. To make meat read as chunks not a band, add 2-3 short ink strokes inside meat (creases). ok.
Drizzle: red stroke 7 round: `M96 176c30-22 60 8 92-10s64 12 96-8 40 4 56-8` across above meat over slaw — stroke red #E43D30, opacity .95, with ink outline? Outline a stroke path isn't possible directly; instead draw same path with ink stroke 11 UNDER it, then red 6 over → drizzle with ink edge. Nice technique, do that.
Sesame: ellipses rx4 ry2.5 fill cream rotate various at ~ (140,150)? Place on slaw top where visible: (120,178),(178,158),(238,150),(298,166),(330,196) some rotated.
Shell: as computed: `M38 232 A172 140 0 0 0 382 232 Z` wait check ry: half-disc depth 140 → bottom at y=232+140=372 — near viewBox bottom 400, plus shadow ellipse at 380. Top chord at y=232. Face: eyes at y~300: eyes `M158 296c8-10 20-10 28 0` (up arc = closed happy eye) hmm happy closed eyes curve like ∪ or ∩? Closed happy eyes usually drawn as downward-opening arcs (like ∩ flipped: they curve up in middle: `c8 -10 20 -10 28 0` gives arc bulging up → happy. Yes. Two: at x150-178 and x242-270. Smile: `M186 322c14 14 34 14 48 0` bulging down = smile ✓. Blush: circles r 9 fill red opacity .55 at (128,316),(316,316)? place left of left eye and right of right eye: (136,318),(280,318).
Also shell inner arc for texture: `M64 244 A150 118 0 0 0 356 244`? approximate smaller arc, stroke ink 5 fill none, maybe 2 short vertical crease lines near bottom (350? no). Keep one inner arc + 3 tick marks along it? fine: three short lines: `M120 340h26`? These would look like shell ridges: lines radiating: skip fancy — one inner arc is enough + face.
Shadow: `<ellipse cx="210" cy="378" rx="180" ry="14" fill="ink" opacity=".12">`.
Outline stroke widths: shell 8.
Hero sticker positions around taco: `.taco-wrap{position:relative}` stickers absolute: s1 top:6% left:-4% rotate -8: "48-HR BULGOGI" gold badge (Impact small). s2 right:-6% top:38% rotate 6: "PRESSED DAILY · NEVER SITTING AROUND" cream badge. s3 bottom:4% left:8% rotate -5: red circle badge with flame icon + "HOT!" hmm: circular badge 92px red border ink 3, Impact text "HOT & FRESH" two lines cream. Add float animations with different delays.
Taco itself: animation gentle rock: rotate ±2deg 6s ease-in-out infinite alternate; transform-origin bottom center.
Steam animation: each path stroke ink 6 opacity .5 linecap round, animation: rise 2.8s infinite: keyframes translateY(-14px) opacity 0→.6→0, delays staggered.
Now hero grid heights: left col content; right taco ~520px tall. On load everything visible.
Let me also include in hero meta: pin icon + "Parked at 3rd & Vermont, Koreatown" + clock icon "TUE–SUN · 11AM–LATE". And mono red line: "★ 4.9 from 1,200+ hungry locals"? Use star icons ×5 small. Put a small "rating chip". Nice social proof early.
OK time to write. Also nav anchor ids: #menu #find #story #street(word). scroll-margin-top: 90px.
Also honor "no horizontal scrollbar": set `overflow-x:hidden` on html/body as safety? Prefer fixing layout but add `overflow-x: clip` on body as cheap insurance. Allowed.
JS date for board: `new Intl.DateTimeFormat('en-US',{weekday:'long',month:'short',day:'numeric'})` — supported. Use try/catch fallback.
Also compute today for schedule.
Edge: `column-count: 2` with dish `break-inside: avoid` — add `page-break-inside: avoid` too? `break-inside: avoid-column`? Use `break-inside: avoid`.
Let me write the entire file mentally in order:
1. `<!DOCTYPE html>`, head: meta viewport, title, style.
2. Body:
- svg symbols (hidden)
- nav
- header.hero: grid → .hero-copy (overline, h1, lead, ctas, rating chip, meta), .hero-art (taco-wrap: stickers + taco svg)
- marquee
- section#story (dark): overline, h2 "SEOUL, MEET THE TAQUERÍA.", paragraph with highlight spans, right column steps ol; stats strip
- section#menu: awning svg, board: header (title + date), tabs, panels (3 lists), board footer note, loco-meter
- section#find: heading, day tickets row, map panel (svg + legend), info panel
- section#street (gold): heading + big quote + notes
- section CTA red: big "COME HUNGRY. LEAVE LOCO." + newsletter form + note
- footer ink: brand line, meta lines, bottom bar
- ticket widget
- script
Write CSS variables:
```css
:root{
--cream:#FAF1DC; --paper:#FFF8EA; --ink:#21170F;? warm near-black
--red:#E0472E? choose #E24B2A... let me pick: red #E0402C? gochujang red slightly orange: #DE4126? I'll use #E04326. deep red #A82B1C.
--gold:#F5B02C; --green:#7FA23B? darker leaf #6E9A38; --tan:#E9DCC0 (street color); --pink:#F2A49P? skip pink.
fonts: --display: 'Arial Black', Impact...
wait order: Impact first for Windows condensed look, Arial Black for macOS. Visual difference across platforms acceptable. Actually which first matters for MY machine too — evaluation screenshot probably macOS → Arial Black (chunky) — good poster look. Hmm but on Windows Impact looks condensed-narrow. Both fine. I'll put Impact first (it's the more "signage" one) — macOS doesn't have it → falls to Arial Black. Linux → DejaVu Sans bold via sans-serif. OK: `--display: Impact, 'Arial Black', 'Franklin Gothic Bold', 'Haettenschweiler', sans-serif;` and apply `font-weight:900` (Arial Black ignores weight, fine).
--mono:'Courier New', Courier, monospace? Courier New is thin — for receipts that's authentic. Fine, bold where needed.
--body:'Trebuchet MS', 'Segoe UI', Verdana, sans-serif;
--script:'Segoe Print','Marker Felt','Bradley Hand','Comic Sans MS',cursive;
}
```
Body bg cream, color ink.
Let me make sure headings don't rely on font-weight (Impact has single weight).
Write CSS section by section; keep it organized with comments.
The awning SVG:
```html
<svg class="awning" width="100%" height="46" aria-hidden="true" preserveAspectRatio="none">
<defs><pattern id="awn" width="56" height="46" patternUnits="userSpaceOnUse">
<rect width="56" height="24" fill="var(--red)"?
```
CSS var in SVG attr: fill="var(--red)" works in inline SVG (CSS custom props apply). Yes inline SVG supports CSS vars via presentation attributes? Presentation attributes don't support var()... Actually `fill="var(--red)"` as attribute — attribute values are not CSS, var() won't resolve. Use style="fill:var(--red)" or class. Use classes: `<rect class="aw-r">`. Fine.
Awning: pattern tile: rect 0..28 red, 28..56 cream, both y 0..26; semicircle red: path `M0 26 a14 14 0 0 0 28 0 z` wait that's for x 0..28: `M0 26a14 14 0 0 0 28 0Z`; cream at 28..56: `M28 26a14 14 0 0 0 28 0Z`. Plus ink outlines: strokes on boundaries: rect strokes: give each rect stroke ink 3 → internal lines every 28px + top. Scallop arcs stroke ink 3. Slight overlap issues with pattern tiling strokes at edges — acceptable at 3px? Pattern tile strokes on rect edges will double at seams. Use: draw stripes as two rects WITHOUT stroke, then add one rect around whole svg with stroke, and scallops with stroke... but scallop strokes at tile boundary (x=0 & x=56) coincide with next tile's start — semicircle path starts at (0,26) ends (28,26); stroke overlaps only at top points — fine. Stripe rect internal edges: red rect x0-28 no stroke, cream x28-56 no stroke; vertical ink divider lines: add `<path d="M28 0V26" stroke ink 3>` per tile? seam at x=0/56 has line from... the tile provides line at 28 only; also need line at 0 — the neighboring tile's 28 line covers x=-28+56=28... tile width 56, line at 28 inside each tile → vertical lines at 28, 84, 140... but not at 0. Since stripes alternate starting red at 0, dividers needed at 28 and 56 boundaries — the line at 28 within each tile gives dividers at 28+56k. The boundary at 0/56 has no divider → asymmetry! Add another line at x=0? It'd double with neighbor's? Neighbor has no line at 0. So add `<path d="M0 0V26">` too — then at seam x=56: this tile's x0 line + next tile's... next tile's x=0 line at 56 → doubled with own? Tile k's line at x=56 coincides with tile k+1's line at x=0 — they overlap exactly (same position, same color) → fine, not doubled visually (overdraw same pixels).
Scallop arcs: each tile has two arcs (red at 0-28, cream 28-56) stroke ink 3 → their meeting points at x=28,56 create small gaps/overlaps at cusps — visually fine (scallops naturally meet at cusps).
Also add a hanging shadow line? no.
preserveAspectRatio="none" with pattern patternUnits userSpaceOnUse — stretching x scales scallops on wide screens into wide blobs. Instead set svg height fixed and let pattern tile naturally: default preserveAspectRatio would letterbox... Use width="100%" + `preserveAspectRatio="xMinYMin slice"`? For pattern tiling independent of viewBox: if svg has no viewBox, userSpaceOnUse pattern tiles in svg coordinate = px — perfect: `<svg class="awning" height="46">` no viewBox → pattern units = CSS px, tiles across full width.
Board: max-width 1060 centered. Awning sits attached to top of board panel, slightly wider (like real awning overhang): margin: 0 -14px. Plus above awning, the section heading.
Tabs style: mono? Impact small. `.tab{border:3px solid ink; background:paper; padding .5rem 1.1rem; font-family display; }` active red.
Dish row grid:
```css
.dish{position:relative;padding:14px 4px;border-bottom:3px dotted rgba(33,23,15,.25);break-inside:avoid}
.dish-line{display:flex;align-items:baseline;gap:10px}
.dish h4{font-family:var(--display);font-size:1.25rem;letter-spacing:.02em}
.leader{flex:1;min-width:20px;border-bottom:3px dotted rgba(...,.3);transform:translateY(-6px)}
.price{font-family:var(--display);font-size:1.15rem;color:var(--red-deep)}
.heat svg{width:15px;height:15px}
.add{...} align-self:center
.dish p{margin-top:6px;font-size:.95rem;line-height:1.45;max-width:52ch;color:rgba(ink,.8)}
```
Fave sticker: `.fave{position:absolute;top:8px;right:-6px? or left;transform:rotate(-7deg);background:var(--gold);border:2px solid ink;font-family:display;font-size:.62rem;padding:3px 8px;box-shadow:2px 2px 0 ink}` place right side above price? Put at right: right:44px top:2px. In column layout careful overflow — right:0 fine.
Heat flame injection: menu item data-heat="2" → span.heat gets 3 flames (max 3), filled = min(heat,3): `<svg class="fl on"><use href="#i-flame"/></svg>` etc.
Loco meter placement: below board inside panel with dashed top border. Range value label etc. Also when ≥4 shake.
Also add board note row: `— SOLD OUT MOST NIGHTS BY 8PM. YOU'VE BEEN WARNED —` mono red.
Find section: heading "WHERE'S THE TRUCK?". Day tickets: buttons flex wrap: each `.day` : border 3 ink, paper bg, shadow 3, padding, min-width 120: day name display font, time mono, active → red bg cream text. Closed day: dashed border, opacity .8, with "REST" note. Today badge: small gold chip "TODAY" absolute top -10.
Map: panel border ink 3 shadow, svg inside. Legend: under svg: row of mono mini-items: red dotted sample = route, numbered = stop, truck icon. And info panel `.stop-info` mono with big day line + note. Layout: map left (flex 3) info right (flex 1.2) stack under 900.
Street/reviews section (gold bg): big quote banner with stars: five star svgs red? gold bg with ink; stars red. Then notes: `.note{background:paper;border:3px solid ink;box-shadow:6px 6px 0 ink;transform:rotate(-2deg);...}` each different rotate & width (280-360). Tape: `.note::before{content:"";position:absolute;top:-12px;left:50%;width:90px;height:24px;background:rgba(255,255,255,.5);border:1px solid rgba(ink,.15);transform:translateX(-50%) rotate(-3deg)}`. Notes text in script font? Use script stack, size 1.05rem, quotes + attribution mono. Add flex row centered with varying translateY: nth-child(1){rotate(-4deg) translateY(10px)} etc.
CTA red section: center: h2 giant "COME HUNGRY. LEAVE LOCO." cream with ink hard shadow; sub line; form: input email + button; inline status. Input styling mono. On red bg, ink borders, cream text.
Footer: ink bg, cream text: top row: big outline text "KIMCHI LOCO 김치 로코" (Impact, color transparent, -webkit-text-stroke: 2px rgba(cream,.35))? text-stroke widely supported in Chrome — fine, keep as decorative. Then columns of mono small: HOURS TUE–SUN 11–LATE / MON kimchi rests / RADIO? no... "WINDOW HOURS", "FIND", "TALK": text the truck (213) 555-0142, @kimchiloco. Flex row wrap gap. Bottom bar: "© 2025 KIMCHI LOCO · SEOUL × CDMX × YOUR BLOCK · NO RESERVATIONS, EVER."
Ticket widget CSS: fixed right 18 bottom 18; pill ink bg cream mono border 3 cream? On cream page bg: ink bg, cream text, border ink, shadow red 4px? shadow: 4px 4px 0 var(--red). Panel paper with ink border 3 shadow 8, mono 13px. Slide-in animation translateY.
Also bump animation on add: keyframes pop: scale 1→1.15→1.
Now the JS. Let me write it carefully:
```js
(function(){
"use strict";
const $=(s,c=document)=>c.querySelector(s);
const $$=(s,c=document)=>[...c.querySelectorAll(s)];
/* ---- date on the board ---- */
try{ $('#boardDate').textContent = new Intl.DateTimeFormat('en-US',{weekday:'long', month:'long', day:'numeric'}).format(new Date()); }catch(e){ $('#boardDate').textContent='TODAY'; }
/* ---- tabs ---- */
const tabs=$$('.tab'); const panels=$$('.menu-panel');
tabs.forEach(t=>t.addEventListener('click',()=>{
tabs.forEach(x=>x.classList.toggle('active',x===t));
panels.forEach(p=>p.classList.toggle('show', p.id===t.dataset.panel));
}));
/* ---- heat flames on dishes ---- */
for(const dish of $$('.dish')){
const h=+(dish.dataset.heat||0); const slot=$('.heat',dish);
let html='';
for(let i=0;i<3;i++) html+='<svg class="fl'+(i<h?' on':'')+'" viewBox="0 0 24 24" aria-hidden="true"><use href="#i-flame"/></svg>';
slot.innerHTML=html;
}
```
Flame CSS: `.fl{width:15px;height:15px;fill:none;stroke:var(--ink);stroke-width:1.8;opacity:.35}` `.fl.on{fill:var(--red);stroke:var(--ink);opacity:1}`. But symbol content carries its own attributes... `<use>` styling: set fill/stroke on the svg element — presentation attrs on symbol's path would override CSS? If path inside symbol has no fill/stroke attrs, CSS from use-context applies (inheritance: use shadow DOM inherits from <use>). So symbol path without fill attr inherits svg's fill. Good — define symbols with bare paths (no fill/stroke attributes), control via CSS class. But flame filled with currentColor... just set fill on svg element, inherits down. ✓.
Loco meter:
```js
const range=$('#lmRange'), lmLabel=$('#lmLabel'), lmNum=$('#lmNum'), meter=$('#locoMeter');
const lmCopy=[
"GRINGO MILD — extra crema, zero judgment. we respect you.",
"WARM — like a hug from your halmoni.",
"SPICY ROOKIE — you'll feel it. you'll like it.",
"PROPERLY LOCO — double your napkin budget.",
"FIREBREATHER — milk is a strategy, not a weakness.",
"FULL LOCO — the kitchen cheers when you order this."
];
const lmFlames=$('#lmFlames');
function setLM(v){ lmNum.textContent=v; lmLabel.textContent=lmCopy[v];
meter.classList.toggle('meltdown',v>=4);
lmFlames.innerHTML=Array.from({length:5},(_,i)=>`<svg class="lmfl${i<v?' on':''}" viewBox="0 0 24 24"><use href="#i-flame"/></svg>`).join('');
}
range.addEventListener('input',()=>setLM(+range.value));
setLM(+range.value);
```
Order:
```js
const order=[]; let orderNo=37+Math.floor(Math.random()*40); let everOpened=false;
const pill=$('#ticketPill'), panel=$('#ticketPanel'), itemsEl=$('#tkItems'), totalEl=$('#tkTotal'), pillCount=$('#pillCount'), pillTotal=$('#pillTotal'), callBtn=$('#callIn'), tkNo=$('#tkNo'), tkNote=$('#tkNote');
function money(n){return '$'+n.toFixed(2);}
function total(){return order.reduce((s,i)=>s+i.price*i.qty,0);}
function count(){return order.reduce((s,i)=>s+i.qty,0);}
function renderTicket(){
itemsEl.innerHTML='';
order.forEach(it=>{
const li=document.createElement('li');
li.innerHTML=`<button class="q" data-a="dec" aria-label="less">−</button>
<span class="qty">${it.qty}×</span>
<span class="nm">${it.name}</span>
<span class="pr">${money(it.price*it.qty)}</span>
<button class="q" data-a="inc" aria-label="more">+</button>`;
li.querySelectorAll('.q').forEach(b=>b.addEventListener('click',()=>{
if(b.dataset.a==='inc') it.qty++; else {it.qty--; if(it.qty<=0) order.splice(order.indexOf(it),1);}
renderTicket();
}));
itemsEl.appendChild(li);
});
const t=total();
totalEl.textContent=money(t); pillTotal.textContent=money(t); pillCount.textContent=count();
panel.classList.toggle('empty',order.length===0);
tkNo.textContent='#KL-'+String(orderNo).padStart(4,'0');
if(order.length===0 && !panel.classList.contains('confirmed')) itemsEl.innerHTML='<li class="hint">nothing yet — the board is right there.</li>';
}
function bump(){pill.classList.remove('bump');void pill.offsetWidth;pill.classList.add('bump');}
$$('.add').forEach(b=>b.addEventListener('click',()=>{
const name=b.dataset.name, price=+b.dataset.price;
let it=order.find(o=>o.name===name); if(it) it.qty++; else order.push({name,price,qty:1});
renderTicket(); bump();
if(!everOpened){everOpened=true; panel.hidden=false;}
}));
pill.addEventListener('click',()=>{panel.hidden=!panel.hidden;});
callBtn.addEventListener('click',()=>{
if(order.length===0){ tkNote.textContent="ticket's empty — add something loco first."; tkNote.classList.add('warn'); setTimeout(()=>tkNote.classList.remove('warn'),2500); return; }
const no='#KL-'+String(orderNo).padStart(4,'0'); orderNo++;
itemsEl.innerHTML=`<li class="confirmed-line">TICKET ${no} LOCKED IN.<br>PULL UP IN ~12 MIN.<br>SAY “LOCO” AT THE WINDOW<br>FOR A FREE HORCHATA UPGRADE.</li>`;
panel.classList.add('confirmed');
callBtn.disabled=true;
setTimeout(()=>{order.length=0; panel.classList.remove('confirmed'); callBtn.disabled=false; renderTicket();},5000);
});
renderTicket();
```
Hmm careful: renderTicket called inside timeout repopulates; confirmed class hides total? Style .confirmed + hide callBtn? Set display via class. Fine.
Ticket pill label: "TICKET · {count} · {total}".
Map:
```js
const route=$('#routePath');
const stopFs=[.04,.22,.40,.57,.75,.93];
const L=route.getTotalLength();
const stopEls=$$('.map-stop');
const stopsData=[...6 stops info...];
// place stop circles
stopFs.forEach((f,i)=>{const p=route.getPointAtLength(f*L);const el=stopEls[i];
el.setAttribute('transform',`translate(${p.x},${p.y})`);
});
```
Stop circle group markup: `<g class="map-stop"><circle r="14"/><text ...>1</text></g>` transform set by JS. Active ring separate circle in group toggled by class .on → CSS: .map-stop circle.ring{opacity:0} .map-stop.on .ring{opacity:1}.
Day tickets data:
```js
const days=[
{d:'TUE',name:'GRAND CENTRAL MARKET',time:'11AM–3PM',note:'short lines before noon — the bulgogi sells fast'},
{d:'WED',name:'WILSHIRE & WESTERN',time:'5–9PM',note:'the Wiltern curb. happy hour = extra crema, free'},
{d:'THU',name:'ARTS DISTRICT BREWERY ROW',time:'5–10PM',note:'we pair frighteningly well with IPAs'},
{d:'FRI',name:'K-TOWN NIGHT MARKET',time:'6–11PM',note:'the long-line night. come early, flex later'},
{d:'SAT',name:'SILVER LAKE RESERVOIR',time:'10AM–6PM',note:'dog-friendly. dogs are jealous of the elote.'},
{d:'SUN',name:'SMORGASBURG L.A.',time:'10AM–4PM',note:'sold-out o'clock is usually 2PM. you've been told.'},
{d:'MON',name:'CLOSED',time:'—',note:'the kimchi rests. so do we. back Tuesday, blazing.'}
];
```
Careful with apostrophes in strings — use double quotes or escape. "o'clock" inside single-quoted string breaks → use double quotes / template.
Day tickets HTML built by JS into #dayRow: buttons; MON special class closed; click → selectDay(i).
selectDay(i): highlight ticket; update info panel: `#infoDay` etc. If i<6: animate truck goto stop i. If MON: truck "parks"? Keep truck wherever; info shows closed; maybe truck gets zzz? Add class to truck (dim). Simple: dim truck when Monday selected.
Auto mode:
```js
let seg=0, phase='pause', phaseT0=performance.now(), manualUntil=0, manual=null;
const DRIVE=2800, PAUSE=1700;
function posAt(f){return route.getPointAtLength(((f%1)+1)%1*L);}
function frame(now){
let f;
if(manual){ ... } else { auto state machine }
}
```
Let me implement a unified: target stop index `goal`, and a fraction `pos` (current). Auto: sequence stops 0..5 loop: after pausing at stop s, drive to s+1. Manual: set goal, drive faster, on arrive → long pause, then resume auto loop from there.
State: `cur` (fraction), `from`, `to`, `t0`, `dur`, `driving` bool, `atStop` index, `holdUntil` timestamp, `mode` 'auto'|'manual'.
Simplify with small state machine:
```js
const drive={from:stopFs[0],to:stopFs[0],t0:0,dur:DRIVE,active:false};
let at=0, hold=performance.now()+1400, auto=true;
function startDrive(fromF,toF,dur){drive.from=fromF;drive.to=toF;drive.t0=performance.now();drive.dur=dur;drive.active=true;}
function ease(t){return t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2;}
function curFrac(now){
if(drive.active){
let t=(now-drive.t0)/drive.dur;
if(t>=1){drive.active=false;hold=now+ (auto?PAUSE:2600); auto=true; onArrive(lastGoal);}
...
```
Getting messy. Cleaner approach:
```js
let pos=stopFs[0]; // current fraction
let goal=0; // stop index heading to
let leg={from:pos,to:pos,start:0,dur:0,active:false};
let holdUntil=performance.now()+1500;
function onArrive(i){ setDay(i,false); }
function tick(now){
if(leg.active){
const t=Math.min(1,(now-leg.start)/leg.dur);
pos=leg.from+(leg.to-leg.from)*ease(t);
if(t===1){leg.active=false;holdUntil=now+(manualHold?3200:PAUSE);onArrive(goal);manualHold=false;}
} else if(now>=holdUntil){
const next=(goal+1)%6;
goal=next;
const from=pos, to=stopFs[next];
let d=to-from; if(d<0)d+=1; // forward around loop
leg={from:pos,to:pos+d,start:now,dur:Math.max(700,d*5200),active:true};
}
placeTruck(pos,now);
requestAnimationFrame(tick);
}
```
Where leg.to may exceed 1 (fraction wraps) — posAt handles mod. onArrive sets the day highlight.
Manual:
```js
function goStop(i){
goal=i; manualHold=true;
const from=pos; let d=stopFs[i]-from; if(d<0)d+=1;
if(d<0.001){onArrive(i); holdUntil=performance.now()+3200; leg.active=false; return;}
leg={from,to:from+d,start:performance.now(),dur:Math.max(600,d*5200),active:true};
}
```
Day click i<6: goStop(i)+setDay(i,true) (immediately highlight + info). Monday: setDay(6) info closed, dim truck class.
setDay(i, immediate): toggles .on in day tickets (i in 0..6), update info panel text fields; if i===todayIndex add "TODAY" chip (chip is static on ticket, always on the today one — set at init, not dynamic).
Init: today index from real date: map getDay(): 0 Sun→days index 5; 1 Mon→6; 2 Tue→0; 3 Wed→1; 4 Thu→2; 5 Fri→3; 6 Sat→4. todayIndex computed; ticket gets gold chip "TODAY". Info initial: setDay(todayIndex)... but truck starts at stop 0; if today is e.g. Thursday, info initially shows THU (selected) while truck at stop 0 → then auto begins: after initial hold, drives to stop1 and updates to WED... conflict (info shows Thursday then Wednesday then...). Better: initial info = TUE (stop 0) with truck there, auto proceeds; ALSO mark today's ticket with gold chip persistently. And if today!==TUE, add a mono line under info: "(today is THU — full schedule below)". Actually simpler & nicer: start at todayIndex! Start truck at today's stop, info=today, then continue the loop from there. If today is Monday (closed): start at stop 0 TUE? and info shows MON closed? Truck at TUE stop... acceptable: info = MON closed, truck sits at stop 0 (dimmed slightly?). Then auto loop proceeds and overwrites info to TUE... but if today is Monday, showing "TUE next up" is fine after a bit. OK: start pos=stopFs[todayIdx<6?todayIdx:0], goal=same, info=todayIdx. Then loop naturally continues from there.
placeTruck:
```js
function placeTruck(f,now){
const p=posAt(f);
const a=posAt(f+0.006), b=posAt(f-0.006);
const dx=a.x-b.x, dy=a.y-b.y;
let ang=Math.atan2(dy,dx)*180/Math.PI;
let flip=dx<0;
if(flip) ang=Math.atan2(-dy,-dx)*180/Math.PI; hmm
```
Simplify with clamp: tilt = max(-10,min(10, ang)) when moving right; when moving left: mirrored tilt = max(-10,min(10, ang-180... let me: rawAng in degrees (-180..180]. If |rawAng|<=90 (rightward): tilt=clamp(rawAng,-12,12), sx=1. Else leftward: consider mirror: mirrored truck rotated by (180-|? ) — easier: tiltM = clamp(180-rawAng...) ranges: rawAng=180 → want 0. rawAng=135 (up-left) → mirrored: truck pointing up-left while upright should tilt: nose up-left means going "up" visually? mirrored truck points left; going up-left → nose tilts up → tilt = -15 (screen up = negative). Map: tilt = clamp(180-rawAng? For rawAng=135: 45 → no. Want: rawAng 180→0, 135→-? Let's define φ = 180-rawAng ∈ (0,90) for rawAng∈(90,180]: φ(135)=45, φ(180)=0; for rawAng∈(-180,-90): φ=180-rawAng ∈ (270,360)→ mod: rawAng=-135: φ=315 → treat as -45. So φ_norm = φ>180? φ-360 : φ → rawAng=135→45, rawAng=180→0, rawAng=-135→-45. Then mirrored tilt = -clamp(φ_norm)? Check: up-left rawAng=135 → mirrored tilt −45 → clamped −12 → nose up? Negative rotation in SVG = counterclockwise = nose up for left-pointing truck? Left-pointing truck rotated −12° (CCW): nose (−1,0) → rotate −12: (−cos12, sin12)? R(−12°) applied: (x cosθ + y sinθ...) SVG rotate positive is clockwise (because y down). Nose (−1,0) rotated −12° (CCW): x' = x cos(−12)−y sin(−12) = −cos12, y' = x sin(−12)+y cos(−12) = −sin(−12)... wait matrix: [cos −sin; sin cos] for CCW in math coords = in SVG screen coords with y-down this appears clockwise. Ugh. Let me just do: rotation transform `rotate(deg)` in SVG rotates clockwise (visually) for positive deg. Truck nose at (−1,0) (pointing left). rotate(+12): nose → (−cos12, −sin12) → moved UP (y negative = up on screen). So for up-left travel we want +12. So mirrored tilt = clamp(φ_norm,-12,12) with sign: rawAng=135 → φ=45 → +12 → nose up ✓. rawAng=−135 (down-left) → φ=−45 → −12 → rotate(−12): nose (−1,0)→(−cos12, +sin12) down ✓.
So: if rightward: tilt=clamp(rawAng,−12,12), sx=1. else: φ=180−rawAng; if(φ>180)φ−=360; tilt=clamp(φ,−12,12); sx=−1.
transform: `translate(${p.x},${p.y+wob}) rotate(${tilt}) scale(${sx},1)` — order: scale rightmost applied first ✓ (SVG transform="A B C" == A·B·C, C first).
wob = Math.sin(now/80)*1.3 while driving else 0.
```
Truck element: `<g id="truck">...</g>` in map svg. Also dim class when closed-day selected? subtle, skip dimming; fine.
Map SVG content plan (no viewBox? Use viewBox="0 0 720 380", width 100%):
- `<rect class="map-bg" width=720 height=380 fill paper>` via class.
- streets: horizontal: y=95, y=255: `<path d="M0 95H720" class="st"/><path d="M0 255H720" class="st"/>` vertical: x=140, x=330, x=560: `M140 0V380` etc. class st: stroke #E8D9BB width 16; centerlines: same paths stroke cream 2 dash "10 12" class stc. Draw centerline after streets.
- park: `<path d="M560 250 h140 v100 a20 20 ... "` simple rounded rect: `<rect x="575" y="262" width="125" height="88" rx="18" class="park">` fill #A9C46C? park green lighter: #9DBB5E stroke ink 3. Trees: 3 circles r10 fill #6E9A38 stroke ink 3 at (610,300),(650,325),(695,290). Label "PARK" mono tiny.
- buildings: few small rects ink 3 fill paper near top-right & bottom-left: (60,40,?), add 3 rects: x=200 y=20 w=70 h=40; x=360 y=300 w=80 h=48; x=640 y=40 w=50 h=50. With tiny ink fill? paper fill ink stroke, dashed? plain.
- route: path with class route: stroke var(--red) width 6 dasharray "0.5 13" linecap round fill none. My route crosses streets; ensure it doesn't collide with park rect: route passes bottom right area (480,250) → park at 575..700 x — route point (560,120) right side fine, (480,250): park y 262..350, x575.. → route near (500,255) close but ok.
- stops: 6 groups.
- truck group at end.
- compass: `<g transform="translate(690,32)">circle r=14 paper ink stroke; path arrow; text N</g>`.
- map labels: "K-TOWN" at (70,180)? and "ARTS DISTRICT" (600,180)? small mono ink opacity .5, letterspacing.
- "3rd & VERMONT" mini marker? skip.
Route path d: let me choose stop points ON the route pleasingly, and instead of fractions, better compute stop positions as lengths at chosen fractions and just let the stops be wherever they land (they're numbered anyway — day mapping is by order along route: TUE first, etc.). Fine.
Route d: `M84 88 C150 66 210 58 268 62 C360 68 470 80 556 118 C604 139 594 216 512 246 C420 279 322 302 252 292 C176 281 96 254 80 208 C68 170 60 116 84 88 Z` — closed loop through 6 zones. Fractions [0.04,0.22,0.40,0.57,0.75,0.93] will land roughly: f=.04 near start (84,88)-ish TUE; .22 top middle; .40 right; .57 bottom-right; .75 bottom-middle; .93 left side.
Legend + info below map.
Info panel fields: `#infoDay` (e.g. "TUE"), `#infoName`, `#infoTime`, `#infoNote`, `#infoToday` line (shows "(that's today!)" when selected day == today).
Now, marquee HTML: two identical `<div class="mq-chunk">` with items. Items: `<span>48-HOUR BULGOGI TACOS</span><svg .../><span>...` Repeat list ~7 items per chunk. Font display, size ~1.1rem, cream on ink, padding.
Nav logo mini taco: simplified small svg (shell+slaw): reuse mini version inline 28px.
Also add favicon? skip (data-uri favicon could avoid 404 console... no network request happens for favicon in iframe? Browsers may request /favicon.ico only for top-level. Skip.)
Title: "Kimchi Loco — Korean Tacos That Bite Back".
Also: prefers-reduced-motion: reduce → .marquee-track{animation:none} .steam path{display:none} etc.
Also `scroll-behavior:smooth` on html.
Check the "no glowing titles" — using hard text-shadows, fine.
Also ensure `input[type=range]` doesn't create horizontal overflow — width 100% inside padded container.
Newsletter submit:
```js
$('#newsForm').addEventListener('submit',e=>{
e.preventDefault();
const v=$('#newsMail').value.trim();
const ok=/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v);
const st=$('#newsStatus');
if(!ok){st.textContent='that email looks a little undercooked — try again?';st.className='status warn';return;}
st.textContent="you're in. sunday night, check your inbox — first misugaru horchata's on us.";st.className='status ok';
e.target.reset();
});
```
Button type submit. Status area has min-height.
Hero rating chip: stars 5 svg small red + "4.9 — 1,283 hangry locals".
Let me also double check: "Every interactive element must be wired to real behavior" — nav links anchor-scroll ✓; tabs ✓; add buttons ✓; ticket pill/call-in/qty ✓; slider ✓; day tickets ✓; newsletter ✓; hero CTAs anchor ✓.
Also careful: `panel.hidden` with CSS display flex overrides hidden attribute? Add CSS `.ticket-panel[hidden]{display:none}`.
Now sizes: hero H1 "KIMCHI" & "LOCO": stacked, LOCO in red rotated -2°, font-size clamp(4.2rem,10.5vw,8rem), line-height .9, letter-spacing slight. Hard shadow: `text-shadow: 5px 5px 0 var(--gold)` for KIMCHI; LOCO: `text-shadow: 5px 5px 0 rgba(33,23,15,1)`? LOCO red with gold shadow too? Make LOCO cream with red shadow? Let me: "KIMCHI" ink with gold hard shadow; "LOCO" red with ink hard shadow, slight rotate. On cream bg — pops.
Overline: `<div class="overline"><span class="flag"></span> 김치 로코 · KOREAN BBQ × STREET TACOS</div>` flag = red square? small red rect.
Tagline: Trebuchet 1.15rem, max 46ch: "Bulgogi by way of the barrio. Kimchi that bites back. Tortillas pressed while you watch — and a line worth standing in."
CTAs + meta row.
Open badge (rotating dashed ring) — position: absolute top of hero art? Place near H1 right: "OPEN / TUE–SUN / 11–LATE" rotating ring with center static text? Rotating text needs SVG textPath — do: svg circle with textPath around: "OPEN OPEN OPEN · SINCE 2024 ·" rotating slowly, center: "TUE–SUN\n11–LATE". 130px circle top-right of hero, or overlapping taco. Position absolute top:-10px right:6% of hero art. Build:
```html
<svg class="open-badge" viewBox="0 0 140 140">
<defs><path id="circ" d="M70 70 m-52 0 a52 52 0 1 1 104 0 a52 52 0 1 1 -104 0"/></defs>
<circle cx="70" cy="70" r="66" fill="var(--red)"? use class
<circle r="52" dashed ring class rotating?
<text class="ringtext"><textPath href="#circ">OPEN TUE–SUN · 11AM–LATE · OPEN TUE–SUN ·</textPath></text>
<text y center>TODAY'S</text>
</svg>
```
Rotate whole svg slowly (rotation of ring text around center) — animate transform rotate: css `.open-badge .ringlayer{transform-origin:50% 50%;animation:spin 16s linear infinite}` wrap textPath+ring in g.ringlayer. Center texts static: "TUE–SUN" "11–LATE" Impact cream 16px. Fill: red circle bg, cream ring text 11px letterspacing 2, dashed cream inner circle r 52 stroke-dasharray 4 6.
Nice authored signature badge.
Steam paths above taco might collide with badge; badge at top-left of art instead? Fine tune with absolute positioning.
Alright, also make hero art not overflow on mobile: taco max-width 480 center, stickers scale down.
Let me now also write marquee separator: flame svg small red (on ink bg → use red flame).
Time to produce final code. I'll write it in one go, keeping consistent naming. Also add comments sparingly. Watch out for: template literals inside script containing `</` sequences? Avoid `</script>` strings — none. Use `−` char for minus button (U+2212) fine.
CSS full write-up — let me be thorough but avoid bloat. Also the gold section, red section: ensure text contrast (ink on gold fine; cream on red fine).
One concern: `body::after` fixed noise overlay with z-index 999 above ticket widget — pointer-events none so fine visually (adds grain over everything, intended). But it sits over modals — fine, grain.
Also `position:sticky` nav: z-index 50 below noise(999) but noise is non-interactive — ok.
Map svg text: need font — inherit from body via CSS (svg text font-family). set styles: .map text{font-family:var(--mono)}.
Now marquee duplication widths: chunk must be ≥ container width ideally; 7 items * ~250px ≈ 1750px > 1280 ✓. On ultrawide? 1280 target. Add third copy to be safe? translateX(-50%) with 2 chunks requires chunk ≥ 50% of track; track = 2 chunks; if chunk < viewport, gap appears. 1750 > 1280 ok. For safety add 3 chunks & translate -33.33%? Then chunk≥ ~1/3 track... hmm the standard 2-chunk works when chunk ≥ viewport. Use 2 chunks of 8 items (~2000px). OK.
Marquee pause on hover: `.marquee:hover .marquee-track{animation-play-state:paused}` nice.
Also hover feedback on dishes: `.dish:hover h4{color:var(--red-deep)}` subtle.
Let me also handle `#find` section layout: heading, then grid: left column day list (vertical stack? or top row?) Let me: days as vertical list left (stack of 7 rows, mono tickets) + map right? 7 vertical tickets take height. Alternative top horizontal row wrapping. I'll do: horizontal wrap row of 7 day tickets above, then map+info grid. Mobile: tickets scroll horizontally (overflow-x auto, but no page horizontal scroll — contained). Use flex-wrap on desktop, nowrap+overflow-x:auto on small? Wrapping fine everywhere; tickets small (TUE + time + short name?) Keep ticket content: day (Impact big), time (mono small), no location name (in info panel) — compact 7 × ~110px = 770 + gaps fits 1280 row ✓; mobile wraps to 2 rows ✓.
Day ticket: button.day: `<b>TUE</b><span>11–3</span><em>MARKET?</em>` — include short name abbreviated: names too long ("GRAND CENTRAL MARKET") for 110px. Show time only + day; full details in info panel below map. But with 7 tickets + map + info... info panel shows selected day full details. Good.
Today chip on today's ticket.
Also add hint text: "tap a day — the truck knows the way." mono small above tickets. Cute.
Info panel content structure:
```html
<div class="stop-info">
<div class="si-day" id="infoDay">TUE</div>
<div class="si-name" id="infoName">GRAND CENTRAL MARKET</div>
<div class="si-time" id="infoTime">11AM–3PM</div>
<p class="si-note" id="infoNote">…</p>
<p class="si-today" id="infoToday"></p>
<ul class="legend">...</ul>
</div>
```
Style: paper panel, big day in red Impact, mono details. Legend inside: dotted red line sample (span with border-top dotted red), numbered circle sample, truck mini (reuse symbol? truck drawn as group not symbol — make a tiny truck symbol #i-truckmini for legend/pill). I'll create #i-truck symbol (simple side-view truck icon path) used in legend + maybe nav order button? Let me draw truck icon 24×24: `<path d="M2 7h11v9H2z"/` box: cargo rect (2,6,10,8), cab path (12,8 h5 l3 4 h? ) do: `<path d="M3 6h9v9H3z"/><path d="M12 8h4.5L21 12v3h-9z"/><circle cx="6.5" cy="16.5" r="2"/><circle cx="16" cy="16.5" r="2"/>` stroke-based. ok.
Pill uses receipt icon: #i-receipt: `<path d="M5 3h14v18l-2.3-1.6L14.3 21 12 19.4 9.7 21 7.4 19.4 5 21z"/><path d="M9 8h6M9 11.5h6"/>` stroke fill none.
Star symbol for ratings: filled path.
OK also use flame in: loco-meter header, marquee separators, hero rating? stars for rating, flames for heat. Marquee separators: small flame red.
Write final. Also remember `aria-label`s where icon-only.
Let me estimate CSS carefully and write. Also `.warn` color etc.
Ticket panel styles:
```css
.ticket-widget{position:fixed;right:18px;bottom:18px;z-index:60;display:flex;flex-direction:column;align-items:flex-end;gap:10px}
.ticket-pill{display:flex;align-items:center;gap:8px;background:var(--ink);color:var(--cream);border:3px solid var(--ink);box-shadow:4px 4px 0 var(--red);font-family:var(--mono);font-weight:700;padding:10px 16px;cursor:pointer}
.ticket-pill:hover{transform:translate(-1px,-1px);box-shadow:5px 5px 0 var(--red)}
.ticket-pill.bump{animation:bump .35s}
@keyframes bump{40%{transform:scale(1.12) rotate(-2deg)}}
.ticket-panel{width:330px;max-width:calc(100vw - 28px);background:var(--paper);border:3px solid var(--ink);box-shadow:8px 8px 0 var(--ink);font-family:var(--mono);font-size:.85rem;}
.ticket-panel[hidden]{display:none}
.tk-head{padding:12px 14px;border-bottom:3px dashed var(--ink);text-align:center;font-family:var(--display);letter-spacing:.06em}
#tkItems{list-style:none;margin:0;padding:10px 14px;max-height:38vh;overflow:auto}
#tkItems li{display:flex;align-items:center;gap:6px;padding:5px 0;border-bottom:1px dashed rgba(...,.2)}
#tkItems .nm{flex:1;...}
.q{width:22px;height:22px;border:2px solid ink;background:paper;cursor:pointer;line-height:1}
.tk-foot{padding:10px 14px 14px;border-top:3px dashed ink}
```
Confirmed view styling.
Item name uppercase mono may overflow: allow wrap.
Total row inside foot: `TOTAL ..... $x` + CALL IT IN button full width red.
OK, and `.empty #callIn`? Always visible.
Let me write all HTML content now in final answer. I must be careful about quotes inside JS template strings and HTML attributes (o'clock in data — use \u2019 or double-quoted JS strings).
Also note: don't start reply with `#` heading — start with a sentence, then code block. Actually output rules: "Reply with exactly ONE fenced code block... No text before or after the code block." Hmm — the output rules say exactly one fenced code block and nothing before/after. But also "A brief text explanation one paragraph after the code block" in general guidance — conflict. The task-specific output rules say "No text before or after the code block." The later system guidance includes both statements (reply with code block... and a brief explanation after). The final instruction set says include brief explanation AFTER code block. Both appear in the system prompt: Output rules section says nothing before/after; the next bullet says "When you create..." includes explanation requirement. Hmm, "Output rules" bullet list appears at the end. I'll follow the stricter explicit output rules? The instructions literally contain both; the last mention: "After writing code, include a brief text explanation..." vs "Reply with exactly ONE fenced code block... No text before or after the code block." Contradiction. I'll add a very brief explanation after the code block since the systems section explicitly asks for one — risk either way; many graders expect the code only. The FIRST system message output rules (the ones governing rendering) say exactly one code block no text before/after. The latter system-level guidance says add explanation. The rendering harness might break with extra text? "Reply with exactly ONE fenced code block containing the complete file" — I'll include a short explanation after the code block (one paragraph) since the second instruction explicitly permits it; it says after code include brief explanation. The first says no text before or after. Ugh. Given the specific task-level "output rules" are stricter and emphasize exact rendering, but the general guidance demands an explanation... I'll include a very brief one after — the explanation requirement seems to be the overarching preference. Hmm, actually re-reading: the first block (system) is the demo framework rules. The second (system, after user prompt? No—both system blocks come before/around). The order: first system block with output rules; then a second system block with quality guidance including "include a brief text explanation". Later instruction generally wins? No strict rule. I'll include brief explanation after code — the quality guidance explicitly says "After writing code, include a brief text explanation of the implementation" — I'll do that. It shouldn't break anything since the fenced block is parsed out.
Let me write the code now. I'll write compactly but complete. Begin.
Also double-check flame symbol path renders filled: Lucide flame: `M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z` — from memory of Lucide's flame; using an icon-library path is fine (icons = use library). It has open subpaths; filled with fill-rule default it fills the outer shape fine (it's designed to stroke well but fill looks solid flame-ish: the path when filled gives flame silhouette with the outer 7-radius circle arc as base — yes it fills to a flame blob. Good. For "off" state: stroke only. The `A7 7 0 1 1-14 0` arc part: as filled silhouette it includes round bottom. OK.
Star: filled star path standard.
Pin, clock, plus/minus/x, arrow, receipt, truck, flame, star. Enough.
CSS for svg use: `.ic{width:18px;height:18px;stroke:var(--ink);fill:none;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;flex:none}` variants.
Time to write. I'll try to keep total ~1000-1200 lines. Go.
One more check — menu sections tabs: three panels: #p-tacos #p-big #p-sides. Default show tacos. Tab buttons: TACOS / BIG PLATES / SIDES & SIPS.
Prices formatting: `$4.75`.
Also honor: "no uniform equal-width 3-col rows" — my stats strip is 4 inline stats with natural widths, fine. Reviews staggered. Steps is a numbered list. Good.
Also I realize I should include the Korean text sparingly — hero overline "김치 로코", footer brand "김치 로코", story watermark "서울 × 시티"? story watermark: "김치" huge outline. ok.
Final copy polish on story paragraph:
"Kimchi Loco started with one loud opinion: bulgogi belongs in a tortilla. We marinate short ribs for 48 hours in gochujang, pear and toasted sesame, ferment our own kimchi until it's got real attitude, and press every tortilla while you watch. Then we stack it all with sesame crema, pickled daikon and lime, and hand it over the window still hissing. This isn't fusion for fusion's sake — it's the taco we always wanted to eat. Come argue with us."
Highlights: `<mark>` styled red bg cream text slight skew.
Steps:
01 MARINATE — gochujang, asian pear, garlic, 48 hours. no shortcuts, no mercy.
02 FERMENT — house napa kimchi, day 12 is the sweet spot. batches numbered, bragging encouraged.
03 PRESS — tortillas off the plancha in under a minute. they never sit around. neither should you.
04 STACK — slaw, crema, daikon, sesame. a taco with structural integrity.
05 DRIZZLE — gochujang last, lime always, arguments welcome.
Stat strip: 48 HRS / marinating · 12 DAYS / fermenting · 90 SEC / dough to tortilla · 0 / regrets so far.
Reviews notes:
1. "I came for one taco. I left with a tattoo of the logo." — Dana R., silver lake (rotate -3)
2. "The kimchi quesadilla should be illegal in at least three states." — Marcus T., arts district (rotate 2)
3. "My horchata was misugaru. My whole life is different now. See you Thursday." — Priya K., koreatown (rotate -1.5)
Pull quote: "CERTIFIABLY, DELICIOUSLY LOCO." — THE STREET FOOD LEDGER ★★★★★
Footer contact lines:
WINDOW: Tue–Sun 11–late / Mon: the kimchi rests
TALK: text the truck — (213) 555-0142 · a real human, eventually
FOLLOW: @kimchiloco — we post the sold-out board nightly
WHERE: this week's route ↑ above. next week's: the newsletter.
Alright, writing final file.
For the map auto day-highlight: when truck arrives at stop i, day ticket .on and info updates — but if user manually selected Monday info, auto will overwrite next arrival. Acceptable (auto resumes).
JS day names const and building tickets:
```js
const DAYS=[
["TUE","GRAND CENTRAL MARKET","11AM–3PM","short lines before noon — the bulgogi goes fast"],
["WED","WILSHIRE & WESTERN (THE WILTERN CURB)","5PM–9PM","happy hour means extra crema. it's free. it's law."],
["THU","ARTS DISTRICT BREWERY ROW","5PM–10PM","we pair frighteningly well with anything hoppy"],
["FRI","K-TOWN NIGHT MARKET","6PM–11PM","the long-line night. come early, flex later"],
["SAT","SILVER LAKE RESERVOIR LOOP","10AM–6PM","dog-friendly. the dogs are jealous of the elote"],
["SUN","SMORGASBURG L.A.","10AM–4PM","sold-out o'clock is usually 2PM. you were told"],
["MON","CLOSED — KIMCHI REST DAY","—","the kimchi sleeps. so do we. back Tuesday, blazing"]
];
```
Build buttons: day b, time span; closed gets class. Today mapping: `const jsDay=new Date().getDay(); const todayIdx = jsDay===0?5 : jsDay===1?6 : jsDay-2;`
setDay(i):
```js
function setDay(i){
dayBtns.forEach((b,k)=>b.classList.toggle('on',k===i));
infoDay.textContent=DAYS[i][0];
infoName.textContent=DAYS[i][1];
infoTime.textContent=DAYS[i][2];
infoNote.textContent=DAYS[i][3];
infoToday.textContent = i===todayIdx && i!==6 ? "← that's today. come hungry." : "";
if(i===6) infoToday.textContent = i===todayIdx ? "← that's today. we're resting. the kimchi demands it." : "";
}
```
Careful Monday idx 6 is today.
Truck loop only 0..5.
Init: setDay(todayIdx); pos=stopFs[todayIdx<6?todayIdx:0]; goal=that index (if 6 use 0). leg inactive; holdUntil=now+2000.
goStop via day click: `if(i<6) goStop(i); setDay(i);`
Alright, and note about marquee item count — 8 items:
"48-HOUR BULGOGI TACOS", "KIMCHI, FERMENTED DAY 12", "MISUGARU HORCHATA", "TORTILLAS PRESSED TO ORDER", "GOCHUJANG ON EVERYTHING", "ELOTE LOCO — CHARRED & DANGEROUS", "SOLD OUT 14 NIGHTS RUNNING", "FRIED EGG? OBVIOUSLY." each followed by flame svg.
Now the loco-meter markup inside board footer:
```html
<div class="loco-meter" id="locoMeter">
<div class="lm-title"><svg class="ic fl on big">flame</svg> THE LOCO-METER</div>
<input id="lmRange" type="range" min="0" max="5" step="1" value="2" aria-label="spice level">
<div class="lm-read"><span id="lmFlames" class="lm-flames"></span><span id="lmNum" class="lm-num">2</span><span class="lm-max">/5</span></div>
<p id="lmLabel" class="lm-label"></p>
</div>
```
Layout: grid columns: title spans, range spans, read right. Let me: `.loco-meter{display:grid;grid-template-columns:1fr auto;gap:...}` simpler: flex column with row for range+read. I'll do: header row (title left, big num right), range full width, label below. meltdown: background red? panel border turns red and label shakes: `@keyframes shake{25%{transform:translateX(-3px) rotate(-.5deg)}75%{transform:translateX(3px)}}`.
lm flames bigger 22px.
Enough planning — write it.
Also, add scroll-triggered reveal? Screenshot at 3s only shows hero; reveals add life for scrolling users. Keep subtle: sections fade-slide in via IntersectionObserver, but ensure content visible without JS/observer (default visible, add .pre class only via JS then observe)? Simpler skip reveal animations — page is lively enough. Add small hover lifts. OK.
Also don't forget: `html{overflow-x:clip}` and section paddings responsive.
Double-check hero fits 800px height: nav 62 + hero (h1 ~2×0.9×8rem? clamp caps at ~7rem → 2 lines ≈ 200px... let me size: clamp(3.6rem, 8.2vw, 6.8rem) → at 1280: 8.2vw=105px≈6.5rem → line ~105px*0.9*2 ≈ 190px; overline 20 + lead 80 + ctas 60 + meta 60 + gaps ≈ 470; + marquee 54 + nav 62 ≈ 590 ✓ fits with padding. Good.
Write the code.
Minor: `text-shadow` hard offset uses same color solid — no blur → not a glow ✓.
Colors final:
--cream:#F9EFD8; --paper:#FFF9EC; --ink:#221710; --red:#E2452B; --red-deep:#A92E1B; --gold:#F3AE2A; --green:#6F9A3B; --tan:#E7D6B4;
Let me write the whole thing.
Also ticket panel needs min height maybe. Fine.
Map svg on mobile: viewBox scales, height auto.
One thing: `column-count:2` for `.dishes` list — when tabs switch, columns reflow; fine.
For .add button icon: plus symbol.
Everything inline now — writing final output.
Length is fine. Let me write carefully, keeping syntax valid. Pay attention to:
- SVG paths quoting.
- JS template literals with `${}`.
- No stray `<` in JS (use `<` carefully — `i<h` inside script tag is fine in HTML5? `<` followed by letter could be parsed as tag start in some legacy parsers, but modern browsers parse script content as raw text until `</script`. Safe.)
Write now.