Food truck websiteOne-line
GLM 5.3
by Z.aiReasoning20 console errors
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 6 min 52 s
- Cost
- $0.111
- Output tokens
- 79,427
- Spent thinking
- 47,798 tokens
- Speed
- 193 tok/s
- Console errors
- 20
- First token after
- 409 ms
- First visible text after
- 5 min 26 s
- Prompt tokens
- 362
- Code size
- 58.3 KB
- Finish reason
- stop
- Served by
- Wafer
- Model id
z-ai/glm-5.3- Reasoning sent
{"effort":"max"}- Max output
- 943,718 tokens
- Generated
- Nov 12, 2026
Console errors
Caught in the first 3 seconds after load, in a sandboxed iframe like the one above.
Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad". Error: <path> attribute d: Expected number, "…6.6-2.1-.2-4.4-2-ad".
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 · 941 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>KIMCHI LOCO — Korean Tacos · Seoul × Mexico City</title>
<meta name="description" content="Korean BBQ meets Mexico City street food. Find the truck, pick your heat, get loco.">
<style>
:root{
--ink:#231610; --cream:#FFF6E9; --cream2:#F6E7CC; --paper:#FFFDF6;
--red:#E23B2E; --red-deep:#B01F14; --yellow:#FFC53D; --green:#2FA84F;
--lime:#9BC53B; --tortilla:#F5B849; --purple:#7B4A9E;
--salsa:#E23B2E; --glow:10px; --fo:0;
--shadow:8px 8px 0 var(--ink);
--display:"Arial Black","Helvetica Neue",Helvetica,Arial,sans-serif;
--body:"Helvetica Neue",Helvetica,Arial,"Segoe UI",sans-serif;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
html,body{overflow-x:hidden;overflow-x:clip}
body{background:var(--cream);color:var(--ink);font-family:var(--body);line-height:1.55;padding-top:96px;-webkit-font-smoothing:antialiased}
::selection{background:var(--red);color:var(--cream)}
:focus-visible{outline:3px dashed var(--red);outline-offset:3px}
h1,h2,h3,h4{font-family:var(--display);text-transform:uppercase;letter-spacing:-.01em;line-height:1}
a{color:inherit}
svg{display:block}
.ico{width:18px;height:18px;flex:none}
/* ---------- reveal ---------- */
.reveal{opacity:0;translate:0 26px;transition:opacity .65s ease var(--d,0ms),translate .65s ease var(--d,0ms)}
.reveal.in{opacity:1;translate:0 0}
/* ---------- tickers ---------- */
.ticker{overflow:hidden;background:var(--ink)}
.divider{overflow:hidden;background:var(--red);border-top:3px solid var(--ink);border-bottom:3px solid var(--ink)}
.t-track{display:flex;width:max-content;animation:marquee 34s linear infinite}
.t-track.rev{animation-direction:reverse}
.t-half{display:flex;align-items:center;padding:8px 0;flex:none}
.t-half b{font-weight:800;font-size:11px;letter-spacing:.18em;white-space:nowrap;margin-left:2.4em;text-transform:uppercase}
.t-half i{font-style:normal;color:var(--red);margin-left:2.4em;font-size:11px}
@keyframes marquee{to{transform:translateX(-50%)}}
.divider{position:relative;z-index:3;margin:14px -14px;transform:rotate(-1.3deg)}
.divider .t-track{animation-duration:40s}
.divider .t-half{padding:13px 0}
.divider .t-half b{font-family:var(--display);font-size:clamp(15px,1.9vw,21px);letter-spacing:.05em;color:var(--cream)}
.divider .t-half i{color:var(--cream);font-size:clamp(15px,1.9vw,21px)}
/* ---------- topbar ---------- */
.topbar{position:fixed;top:0;left:0;right:0;z-index:50;background:var(--cream)}
.topbar.scrolled{filter:drop-shadow(0 5px 10px rgba(35,22,16,.22))}
.header{max-width:1160px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;gap:16px;padding:11px 24px;border-bottom:3px solid var(--ink)}
.brand{display:flex;align-items:center;gap:9px;font-family:var(--display);font-size:19px;text-decoration:none;letter-spacing:.01em}
.brand svg{width:26px;height:26px;color:var(--red)}
.brand b{color:var(--red)}
.nav{display:flex;gap:26px;font-weight:800;font-size:13.5px;text-transform:uppercase;letter-spacing:.1em}
.nav a{text-decoration:none;padding:4px 2px;border-bottom:3px solid transparent}
.nav a:hover{border-bottom-color:var(--red)}
.h-actions{display:flex;align-items:center;gap:12px}
.pill{display:inline-flex;align-items:center;gap:8px;border:2.5px solid var(--ink);border-radius:999px;padding:7px 13px;font-size:10.5px;font-weight:900;letter-spacing:.14em;text-transform:uppercase;background:var(--cream2)}
.pill .dot{width:9px;height:9px;border-radius:50%;background:#a39a8d}
.pill.open{background:#DFF3D6}
.pill.open .dot{background:var(--green);animation:pulse 1.7s infinite}
@keyframes pulse{to{box-shadow:0 0 0 9px rgba(47,168,79,0)}}
.cart-btn{position:relative;display:flex;align-items:center;gap:8px;background:var(--yellow);border:3px solid var(--ink);border-radius:12px;padding:9px 13px;font-family:var(--display);font-size:13px;text-transform:uppercase;cursor:pointer;box-shadow:3px 3px 0 var(--ink);transition:transform .15s,box-shadow .15s}
.cart-btn:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 var(--ink)}
.cart-btn:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--ink)}
.badge{position:absolute;top:-11px;right:-11px;min-width:23px;height:23px;display:none;align-items:center;justify-content:center;background:var(--red);color:var(--cream);border:2.5px solid var(--ink);border-radius:99px;font-size:11px;font-weight:900;padding:0 4px}
.badge.show{display:flex}
.badge.pop{animation:pop .4s}
@keyframes pop{45%{transform:scale(1.4)}}
/* ---------- buttons ---------- */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:10px;font-family:var(--display);text-transform:uppercase;font-size:15px;letter-spacing:.02em;padding:14px 22px;border:3px solid var(--ink);border-radius:14px;background:var(--red);color:var(--cream);box-shadow:var(--shadow);cursor:pointer;text-decoration:none;transition:transform .15s,box-shadow .15s}
.btn:hover{transform:translate(-3px,-3px);box-shadow:11px 11px 0 var(--ink)}
.btn:active{transform:translate(3px,3px);box-shadow:2px 2px 0 var(--ink)}
.btn.ghost{background:var(--paper);color:var(--ink)}
.btn.ghost2{background:var(--yellow);color:var(--ink)}
/* ---------- hero ---------- */
.hero{padding:54px 24px 76px;background:var(--cream);background-image:radial-gradient(rgba(35,22,16,.05) 1.4px,transparent 1.4px);background-size:22px 22px}
.hero-inner{max-width:1160px;margin:0 auto;display:grid;grid-template-columns:1.06fr .94fr;gap:44px;align-items:center}
.chips{display:flex;gap:10px;align-items:center;font-weight:900;font-size:12px;letter-spacing:.14em;text-transform:uppercase}
.chip{border:2.5px solid var(--ink);padding:6px 12px;border-radius:999px;background:var(--yellow);box-shadow:3px 3px 0 var(--ink)}
.chip.red{background:var(--red);color:var(--cream)}
.chips .x{font-family:var(--display);font-size:15px}
.hero-title{font-size:clamp(58px,8.4vw,116px);line-height:.87;margin:22px 0 20px;text-transform:uppercase}
.hero-title .t2{display:block;color:var(--red);text-shadow:.05em .05em 0 var(--ink)}
.lede{font-size:clamp(16px,1.5vw,19.5px);line-height:1.55;max-width:46ch;color:#4a3626}
.lede b{color:var(--red-deep)}
.hero-ctas{display:flex;gap:16px;flex-wrap:wrap;margin-top:26px}
.hero-status{margin-top:16px;font-size:15px;font-weight:600}
.hero-status .open-t{color:var(--red-deep)}
.hero-status .closed-t{color:var(--ink)}
.stats{display:flex;gap:30px;margin-top:30px;flex-wrap:wrap}
.stats>div{min-width:120px}
.stats b{display:block;font-family:var(--display);font-size:30px;color:var(--red);line-height:1;margin-bottom:6px}
.stats span{font-size:12px;letter-spacing:.09em;text-transform:uppercase;font-weight:800;color:#6b5340}
.hero-art{display:flex;flex-direction:column;align-items:center}
.stage{position:relative;width:min(400px,86vw)}
.sunburst{position:absolute;left:50%;top:46%;width:99%;aspect-ratio:1;transform:translate(-50%,-50%);border-radius:50%;border:4px solid var(--ink);background:repeating-conic-gradient(var(--yellow) 0 16deg,var(--cream) 16deg 32deg);box-shadow:12px 12px 0 var(--red);animation:spin 60s linear infinite;z-index:0}
@keyframes spin{to{rotate:360deg}}
.stage.loco .sunburst{animation-duration:7s}
.taco-wrap{position:relative;z-index:1;width:100%;transition:transform .3s ease-out;filter:drop-shadow(0 0 var(--glow) rgba(226,59,46,.55))}
.stage.loco #tacoSvg{animation:wig .3s linear infinite;transform-box:fill-box;transform-origin:50% 50%}
@keyframes wig{0%,100%{transform:rotate(-1.4deg)}50%{transform:rotate(1.4deg)}}
.steam{position:absolute;top:-3%;left:24%;width:52%;height:24%;pointer-events:none;z-index:2}
.steam i{position:absolute;bottom:0;background:rgba(255,255,255,.85);border-radius:50%;opacity:0;animation:puff var(--dur) ease-out forwards}
@keyframes puff{from{opacity:.85;transform:translateY(0) scale(.7)}to{opacity:0;transform:translateY(var(--dy)) scale(1.6)}}
.flames{opacity:var(--fo);transform-box:fill-box;transform-origin:50% 100%;animation:flick .5s ease-in-out infinite alternate}
@keyframes flick{to{transform:scaleY(1.14) scaleX(.94)}}
.salsa{fill:var(--salsa)}
/* heat card */
.heat-card{margin-top:26px;width:min(400px,100%);background:var(--paper);border:3px solid var(--ink);border-radius:18px;padding:18px 20px;box-shadow:7px 7px 0 var(--red)}
.heat-head{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
.heat-head label{font-weight:900;text-transform:uppercase;font-size:13px;letter-spacing:.12em}
.heat-read{font-family:var(--display);font-size:13px;letter-spacing:.1em;border:2.5px solid var(--ink);padding:5px 13px;border-radius:999px;background:var(--salsa);transition:background .2s}
.heat-sub{font-size:14px;margin-top:12px;color:#6b5340;min-height:1.4em}
input[type=range]{-webkit-appearance:none;appearance:none;width:100%;height:30px;margin-top:12px;background:transparent;cursor:pointer}
input[type=range]::-webkit-slider-runnable-track{height:14px;border:3px solid var(--ink);border-radius:99px;background:linear-gradient(90deg,#9BC53B,#FFC53D 45%,#E23B2E 82%,#8f1210)}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:30px;height:30px;border-radius:50%;background:var(--paper);border:3px solid var(--ink);margin-top:-11px;box-shadow:3px 3px 0 var(--ink)}
input[type=range]::-moz-range-track{height:14px;border:3px solid var(--ink);border-radius:99px;background:linear-gradient(90deg,#9BC53B,#FFC53D 45%,#E23B2E 82%,#8f1210)}
input[type=range]::-moz-range-thumb{width:24px;height:24px;border-radius:50%;background:var(--paper);border:3px solid var(--ink);box-shadow:3px 3px 0 var(--ink)}
/* ---------- sections ---------- */
section{scroll-margin-top:118px}
.shead{max-width:720px;margin:0 auto;text-align:center}
.kicker{display:inline-block;background:var(--red);color:var(--cream);font-family:var(--display);font-size:12px;letter-spacing:.22em;padding:7px 14px;border:2.5px solid var(--ink);border-radius:999px;box-shadow:3px 3px 0 var(--ink);text-transform:uppercase}
.shead h2{font-size:clamp(34px,4.6vw,58px);margin:18px 0 6px}
.squig{width:220px;height:16px;margin:6px auto 14px;color:var(--red)}
.shead p{font-size:16px;color:#6b5340;font-weight:600}
/* ---------- menu ---------- */
.menu{background:var(--ink);color:var(--cream);padding:78px 24px 84px;background-image:radial-gradient(rgba(255,246,233,.07) 1.5px,transparent 1.5px);background-size:22px 22px}
.menu .shead p{color:rgba(255,246,233,.75)}
.menu .squig{color:var(--red)}
.menu-grid{max-width:1160px;margin:46px auto 0;display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:26px}
.card{position:relative;background:var(--cream);color:var(--ink);border:3px solid var(--ink);border-radius:18px;padding:24px 22px 20px;box-shadow:7px 7px 0 var(--red);transition:transform .22s,box-shadow .22s}
.card:hover{transform:translate(-4px,-4px) rotate(-.4deg);box-shadow:12px 12px 0 var(--red)}
.tag{position:absolute;top:-13px;right:14px;transform:rotate(3deg);background:var(--red);color:var(--cream);font-size:10.5px;font-weight:900;letter-spacing:.14em;padding:5px 10px;border:2.5px solid var(--ink);border-radius:8px;box-shadow:3px 3px 0 var(--ink)}
.card-top{display:grid;grid-template-columns:auto 1fr auto;gap:13px;align-items:center;margin-bottom:12px}
.dish{width:48px;height:48px;border:3px solid var(--ink);border-radius:13px;background:var(--yellow);display:grid;place-items:center;box-shadow:inset 0 0 0 2px rgba(255,255,255,.35)}
.dish svg{width:26px;height:26px}
.card-top h3{font-size:16.5px;line-height:1.12;letter-spacing:.01em}
.price{font-family:var(--display);font-size:14px;background:var(--yellow);border:2.5px solid var(--ink);padding:5px 10px;border-radius:999px;box-shadow:2px 2px 0 var(--ink);white-space:nowrap}
.desc{font-size:14px;color:#5c4a3d;margin-bottom:18px}
.card-bot{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap}
.heatcheck{display:flex;align-items:center;gap:4px;color:var(--red-deep)}
.chili{width:16px;height:16px}
.chili.off{color:#D8CBB4}
.heatcheck em{font-style:italic;font-weight:800;font-size:12px;margin-left:5px;color:#8a6f57}
.add{font-family:var(--display);font-size:13px;text-transform:uppercase;letter-spacing:.05em;background:var(--red);color:var(--cream);border:3px solid var(--ink);border-radius:11px;padding:10px 15px;box-shadow:3px 3px 0 var(--ink);cursor:pointer;transition:transform .15s,box-shadow .15s}
.add:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 var(--ink)}
.add:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--ink)}
.drinks{max-width:1160px;margin:36px auto 0;border:2px dashed rgba(255,246,233,.45);border-radius:16px;padding:18px 22px;display:flex;gap:12px 26px;flex-wrap:wrap;align-items:center;font-weight:800;letter-spacing:.06em;font-size:13.5px;text-transform:uppercase}
.drinks b{font-family:var(--display);color:var(--yellow);font-size:13px;letter-spacing:.1em}
.drinks span{color:rgba(255,246,233,.85)}
.menu-note{max-width:1160px;margin:22px auto 0;text-align:center;font-size:13px;color:rgba(255,246,233,.6);font-weight:700}
/* ---------- truck ---------- */
.truck{padding:84px 24px 92px;background:var(--cream);background-image:radial-gradient(rgba(35,22,16,.05) 1.4px,transparent 1.4px);background-size:22px 22px}
.truck .shead p{color:#6b5340}
.truck-grid{max-width:1160px;margin:42px auto 0;display:grid;grid-template-columns:1.1fr .9fr;gap:40px;align-items:start}
.status-card{display:flex;gap:16px;align-items:flex-start;background:var(--paper);border:3px solid var(--ink);border-radius:16px;padding:20px 22px;box-shadow:7px 7px 0 var(--ink);font-size:15px}
.status-card .dot{flex:none;width:16px;height:16px;border-radius:50%;background:#a39a8d;margin-top:4px}
.status-card.open .dot{background:var(--green);animation:pulse 1.7s infinite}
.status-card b{display:block;font-family:var(--display);font-size:15px;letter-spacing:.06em;margin-bottom:6px;text-transform:uppercase}
.status-card.open b{color:var(--red-deep)}
.status-card span{color:#5c4a3d;font-weight:600}
.road{margin-top:28px;background:#2A201A;border:3px solid var(--ink);border-radius:20px;padding:24px 18px 10px;position:relative;overflow:hidden}
#truckSvg{width:min(400px,92%);margin:0 auto;cursor:pointer}
.t-bob{animation:bob 1.6s ease-in-out infinite alternate}
@keyframes bob{to{transform:translateY(-3px)}}
.smoke circle{animation:rise 2.6s linear infinite}
.smoke circle:last-child{animation-delay:1.3s}
@keyframes rise{0%{transform:translate(0,0);opacity:0}25%{opacity:.7}100%{transform:translate(7px,-16px);opacity:0}}
.sign{transform-box:fill-box;transform-origin:50% 0;animation:swing 2.6s ease-in-out infinite}
@keyframes swing{0%,100%{transform:rotate(-4deg)}50%{transform:rotate(5deg)}}
.road-line{height:4px;border-radius:2px;margin:12px 6px 8px;background:repeating-linear-gradient(90deg,rgba(255,246,233,.55) 0 26px,transparent 26px 52px)}
.beep{position:absolute;left:50%;top:36%;transform:translate(-50%,-50%) rotate(-6deg);font-family:var(--display);background:var(--yellow);border:3px solid var(--ink);box-shadow:5px 5px 0 var(--ink);padding:10px 16px;border-radius:12px;animation:beep .9s ease forwards;z-index:3}
@keyframes beep{0%{opacity:0;transform:translate(-50%,-50%) rotate(-6deg) scale(.5)}25%{opacity:1;transform:translate(-50%,-50%) rotate(-6deg) scale(1.12)}70%{opacity:1}100%{opacity:0;transform:translate(-50%,-34%) rotate(-6deg) scale(1)}}
.truck-note{margin-top:18px;font-size:14px;color:#6b5340;font-weight:600}
.sched{background:var(--paper);border:3px solid var(--ink);border-radius:18px;box-shadow:7px 7px 0 var(--red);overflow:hidden}
.sched-head{font-family:var(--display);font-size:14px;letter-spacing:.16em;text-transform:uppercase;padding:16px 20px;border-bottom:3px solid var(--ink);background:var(--yellow)}
.srow{display:grid;grid-template-columns:54px 112px 1fr;gap:10px;padding:13px 18px;border-bottom:2px solid rgba(35,22,16,.12);align-items:center;position:relative}
.srow:last-child{border-bottom:0}
.srow b{font-family:var(--display);font-size:14px}
.srow>span{font-size:13px;font-weight:800;color:#5c4a3d}
.srow>em{font-style:italic;font-weight:700;font-size:13.5px;color:#8a6f57}
.srow.today{background:#FFE4BC}
.srow.today>em{color:var(--red-deep);font-weight:900}
.now{position:absolute;right:14px;font-style:normal;background:var(--red);color:var(--cream);font-size:10px;font-weight:900;letter-spacing:.16em;padding:4px 9px;border:2.5px solid var(--ink);border-radius:7px;box-shadow:2px 2px 0 var(--ink)}
#pinBtn{margin-top:22px;width:100%}
/* ---------- hype ---------- */
.hype{background:var(--red);color:var(--cream);padding:84px 24px 88px;border-top:3px solid var(--ink)}
.hype .kicker{background:var(--yellow);color:var(--ink)}
.hype .squig{color:var(--yellow)}
.quotes{display:grid;grid-template-columns:repeat(auto-fit,minmax(265px,1fr));gap:26px;max-width:1160px;margin:46px auto 0}
.q{background:var(--cream);color:var(--ink);border:3px solid var(--ink);border-radius:18px;padding:26px 24px;box-shadow:7px 7px 0 var(--ink);transition:transform .2s,box-shadow .2s}
.q:hover{transform:translate(-3px,-3px);box-shadow:10px 10px 0 var(--ink)}
.q .stars{display:flex;gap:4px;color:#EFA00B;margin-bottom:14px}
.q p{font-size:16.5px;font-weight:650;line-height:1.5}
.q b{display:block;margin-top:14px;font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--red-deep)}
.badges{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;margin-top:38px}
.badge{border:2.5px solid var(--cream);color:var(--cream);padding:9px 16px;border-radius:999px;font-weight:800;font-size:12px;letter-spacing:.12em;text-transform:uppercase}
.badge i{font-style:normal;color:var(--yellow);margin-right:8px}
/* ---------- story ---------- */
.story{background:var(--yellow);border-top:3px solid var(--ink);border-bottom:3px solid var(--ink);padding:60px 24px;background-image:radial-gradient(rgba(35,22,16,.07) 1.4px,transparent 1.4px);background-size:22px 22px}
.story-inner{max-width:880px;margin:0 auto;text-align:center}
.story .kicker{background:var(--red)}
.story p{font-size:clamp(17px,2.3vw,23px);font-weight:650;line-height:1.5;margin-top:20px}
.story p b{color:var(--red-deep)}
.founders{display:flex;gap:16px;justify-content:center;margin-top:26px;flex-wrap:wrap}
.f-chip{display:flex;align-items:center;gap:10px;background:var(--cream);border:3px solid var(--ink);border-radius:999px;padding:6px 18px 6px 8px;font-weight:900;font-size:13.5px;box-shadow:4px 4px 0 var(--ink)}
.f-chip small{display:block;font-weight:700;font-size:10.5px;color:#8a6f57;letter-spacing:.1em;text-transform:uppercase}
/* ---------- footer ---------- */
footer{background:var(--ink);color:var(--cream);padding:76px 24px 30px}
.f-big{max-width:1160px;margin:0 auto;font-family:var(--display);text-transform:uppercase;font-size:clamp(32px,5.6vw,60px);line-height:.95}
.f-big em{font-style:normal;color:var(--yellow)}
.f-grid{max-width:1160px;margin:52px auto 40px;display:grid;grid-template-columns:1.35fr .7fr .8fr .8fr;gap:34px}
.f-grid h4{font-family:var(--display);font-size:13px;letter-spacing:.16em;color:var(--yellow);margin-bottom:14px;text-transform:uppercase}
.f-grid p{font-size:14px;color:rgba(255,246,233,.75);font-weight:600}
.f-grid ul{list-style:none}
.f-grid li{font-size:14px;font-weight:700;padding:5px 0;color:rgba(255,246,233,.85)}
.f-grid a{color:var(--cream);text-decoration:none;font-weight:700;font-size:14px;display:inline-block;padding:5px 0;border-bottom:2px solid transparent}
.f-grid a:hover{border-bottom-color:var(--red)}
.news-form{display:flex;gap:12px;margin-top:16px;flex-wrap:wrap}
.news-form input{flex:1;min-width:200px;border:3px solid var(--cream);background:transparent;color:var(--cream);padding:13px 16px;border-radius:13px;font:inherit;font-weight:600}
.news-form input::placeholder{color:rgba(255,246,233,.5)}
.news-form .btn{padding:12px 20px;font-size:14px}
.socs{display:flex;flex-direction:column;gap:12px;align-items:flex-start}
.soc{display:flex;align-items:center;gap:9px;border:2.5px solid var(--cream);background:transparent;color:var(--cream);padding:10px 16px;border-radius:999px;font-weight:800;font-size:13.5px;cursor:pointer;transition:background .2s,color .2s}
.soc:hover{background:var(--cream);color:var(--ink)}
.f-bottom{max-width:1160px;margin:0 auto;border-top:2px dashed rgba(255,246,233,.35);padding-top:22px;display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;font-size:12.5px;font-weight:700;color:rgba(255,246,233,.6);letter-spacing:.04em}
/* ---------- cart ---------- */
.overlay{position:fixed;inset:0;background:rgba(35,22,16,.55);opacity:0;pointer-events:none;transition:.3s;z-index:60}
.overlay.show{opacity:1;pointer-events:auto}
.cart{position:fixed;top:0;right:0;bottom:0;width:min(400px,94vw);background:var(--cream);z-index:61;border-left:3px solid var(--ink);transform:translateX(104%);transition:transform .38s cubic-bezier(.22,1,.36,1);display:flex;flex-direction:column}
.cart.open{transform:none}
.cart-head{display:flex;justify-content:space-between;align-items:center;padding:18px 22px;border-bottom:3px solid var(--ink)}
.cart-head h3{font-size:19px;letter-spacing:.02em}
.cart-head h3 span{color:var(--red)}
#cartClose{width:38px;height:38px;border:3px solid var(--ink);border-radius:11px;background:var(--yellow);cursor:pointer;display:grid;place-items:center;box-shadow:3px 3px 0 var(--ink)}
#cartClose:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--ink)}
#cartClose svg{width:16px;height:16px}
.cart-body{flex:1;overflow-y:auto;padding:18px 22px}
.cart-foot{padding:18px 22px;border-top:3px solid var(--ink);background:var(--paper)}
.crow{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:13px 0;border-bottom:2px dashed rgba(35,22,16,.18)}
.crow:last-of-type{border-bottom:0}
.crow-l b{display:block;font-weight:900;font-size:15px;line-height:1.25}
.crow-l span{font-size:13px;color:#8a6f57;font-weight:700}
.stepper{display:flex;align-items:center;gap:9px}
.stepper span{font-weight:900;font-size:15px;min-width:16px;text-align:center}
.stepper button{width:31px;height:31px;border:2.5px solid var(--ink);background:var(--cream);border-radius:10px;font-weight:900;font-size:16px;cursor:pointer;box-shadow:2px 2px 0 var(--ink);line-height:1}
.stepper button:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--ink)}
.ctotal{display:flex;justify-content:space-between;font-weight:900;font-size:16px;margin-bottom:14px}
.fire{width:100%}
.cnote{font-size:12px;text-align:center;margin-top:12px;color:#8a6f57;font-weight:600}
.empty{text-align:center;padding:36px 12px 8px;font-weight:800;font-size:16px;line-height:1.5}
.empty2{text-align:center;padding:0 12px;font-weight:600;font-size:13.5px;color:#8a6f57}
.ticket{background:#fff;border:3px dashed var(--ink);border-radius:16px;padding:22px;font-family:ui-monospace,"SFMono-Regular","Courier New",monospace;font-size:14px;line-height:1.8}
.tk-head{letter-spacing:.3em;font-weight:700;text-align:center;color:var(--red-deep)}
.tk-num{font-weight:700;text-align:center;font-size:21px;margin:6px 0 12px}
.tk-total{font-weight:700}
.tk-line{border-top:2px dashed var(--ink);margin:12px 0}
.tk-where{font-weight:700;text-transform:uppercase}
.tk-small{font-size:12px;color:#6b5340}
/* ---------- toasts & burst ---------- */
.toasts{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);display:flex;flex-direction:column;gap:10px;z-index:70;align-items:center;max-width:92vw}
.toast{background:var(--ink);color:var(--cream);border:3px solid var(--ink);box-shadow:5px 5px 0 var(--red);padding:13px 20px;border-radius:13px;font-weight:700;font-size:14px;opacity:0;transform:translateY(14px);transition:.3s}
.toast.show{opacity:1;transform:none}
.toast b{color:var(--yellow)}
.bdot{position:fixed;width:13px;height:13px;border-radius:50%;border:2.5px solid var(--ink);z-index:80;pointer-events:none;animation:burst .85s cubic-bezier(.2,.7,.3,1) forwards}
@keyframes burst{from{transform:translate(0,0) scale(1);opacity:1}to{transform:translate(var(--tx),var(--ty)) scale(.2);opacity:0}}
/* ---------- responsive ---------- */
@media (max-width:1020px){
.hero-inner{grid-template-columns:1fr;text-align:center;gap:30px}
.chips,.hero-ctas,.stats{justify-content:center}
.stage{margin:0 auto}
.hero-ctas{margin-left:auto;margin-right:auto}
.truck-grid{grid-template-columns:1fr}
}
@media (max-width:760px){
.nav{display:none}
.f-grid{grid-template-columns:1fr 1fr}
.divider{margin:10px -8px}
}
@media (max-width:560px){
.h-actions .pill{display:none}
.stats{gap:20px}
.srow{grid-template-columns:52px 1fr}
.srow>em{grid-column:1/-1}
.f-grid{grid-template-columns:1fr}
.hero{padding-top:40px}
}
@media (prefers-reduced-motion:reduce){
*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}
}
</style>
</head>
<body id="top">
<!-- ============ icon symbols ============ -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<symbol id="i-taco" viewBox="0 0 24 24">
<path d="M3.5 12a8.5 8.5 0 0 0 17 0" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round"/>
<path d="M4.6 12c4 2.7 10.8 2.7 14.8 0" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round"/>
<circle cx="8.2" cy="9" r="1.5" fill="currentColor"/><circle cx="12" cy="8" r="1.5" fill="currentColor"/><circle cx="15.8" cy="9" r="1.5" fill="currentColor"/>
</symbol>
<symbol id="i-chili" viewBox="0 0 24 24">
<path d="M14.8 5.4c-2 .3-3.7 1.5-5.1 3.1-2.9 3.4-2.6 8.4.8 10.4 3.6 2.1 8.3.3 9.4-3.6.6-2.1-.2-4.4-2-ad" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
<path d="M14.8 5.4c.6-1.7 2.4-2.5 4.1-2" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
</symbol>
<symbol id="i-burrito" viewBox="0 0 24 24">
<rect x="5" y="7" width="14" height="10" rx="5" fill="none" stroke="currentColor" stroke-width="2"/>
<path d="M8.5 7.6l1.6 8.8M12.2 7.6v8.8M15.6 7.6l-1.1 8.8" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
</symbol>
<symbol id="i-queso" viewBox="0 0 24 24">
<path d="M19 5 5 12l14 7Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
<circle cx="14" cy="12.2" r="1.3" fill="currentColor"/><circle cx="16.3" cy="14.4" r="1.3" fill="currentColor"/>
</symbol>
<symbol id="i-fries" viewBox="0 0 24 24">
<path d="M6 13l1.5 6.5h9L18 13" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
<path d="M9.4 13V6.6M12.7 13V5.6M16 13V7.6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</symbol>
<symbol id="i-wings" viewBox="0 0 24 24">
<circle cx="9.5" cy="9.5" r="6.3" fill="none" stroke="currentColor" stroke-width="2.1"/>
<path d="M14 14l4.3 4.3" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" fill="none"/>
<circle cx="19.7" cy="16.9" r="1.8" fill="currentColor"/><circle cx="16.9" cy="19.7" r="1.8" fill="currentColor"/>
</symbol>
<symbol id="i-bag" viewBox="0 0 24 24">
<path d="M5.5 8.5h13L17 20H7z" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linejoin="round"/>
<path d="M9 8.5a3 3 0 0 1 6 0" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round"/>
</symbol>
<symbol id="i-close" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6 6 18" stroke="currentColor" stroke-width="2.6" stroke-linecap="round"/></symbol>
<symbol id="i-star" viewBox="0 0 24 24"><path d="M12 2.6l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.4l-5.9 3.2 1.2-6.5L2.5 9.5l6.6-.9z" fill="currentColor"/></symbol>
<symbol id="i-arrow" viewBox="0 0 24 24"><path d="M4 12h15M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></symbol>
<symbol id="i-pin" viewBox="0 0 24 24"><path d="M12 21s-7-5.6-7-11a7 7 0 1 1 14 0c0 5.4-7 11-7 11z" fill="none" stroke="currentColor" stroke-width="2.1"/><circle cx="12" cy="10" r="2.6" fill="none" stroke="currentColor" stroke-width="2.1"/></symbol>
</defs>
</svg>
<!-- ============ topbar ============ -->
<div class="topbar" id="topbar">
<div class="ticker"><div class="t-track" id="tickerA"></div></div>
<header class="header">
<a class="brand" href="#top"><svg aria-hidden="true"><use href="#i-taco"/></svg>KIMCHI <b>LOCO</b></a>
<nav class="nav" aria-label="Main">
<a href="#menu">Menu</a><a href="#truck">Find the truck</a><a href="#hype">Hype</a>
</nav>
<div class="h-actions">
<span class="pill" id="pillTop"><span class="dot"></span>CHECKING…</span>
<button class="cart-btn" id="cartBtn" aria-label="Open your order">
<svg class="ico" aria-hidden="true"><use href="#i-bag"/></svg>
Order
<span class="badge" id="cartCount">0</span>
</button>
</div>
</header>
</div>
<!-- ============ hero ============ -->
<section class="hero">
<div class="hero-inner">
<div class="hero-copy">
<div class="chips">
<span class="chip red">SEOUL BBQ</span><span class="x">×</span><span class="chip">MEXICO CITY STREETS</span>
</div>
<h1 class="hero-title"><span>GET</span><span class="t2">LOCO.</span></h1>
<p class="lede"><b>Bulgogi</b> in your tortilla. <b>Kimchi</b> in your quesadilla. Sesame smoke hanging over a screaming-hot plancha at 2AM. We're a silver truck with a serious flavor problem — and you're about to catch it.</p>
<div class="hero-ctas">
<a class="btn" href="#truck">Find today's truck</a>
<a class="btn ghost" href="#menu">Peep the menu <svg class="ico" aria-hidden="true"><use href="#i-arrow"/></svg></a>
</div>
<p class="hero-status" id="heroStatus"></p>
<div class="stats">
<div><b data-count="128400">0</b><span>tacos slung</span></div>
<div><b data-count="4.9" data-dec="1">0</b><span>avg · 2,140 reviews</span></div>
<div><b>'TIL 2AM</b><span>weekends, no mercy</span></div>
</div>
</div>
<div class="hero-art">
<div class="stage" id="stage">
<div class="sunburst" aria-hidden="true"></div>
<div class="steam" id="steam" aria-hidden="true"></div>
<div class="taco-wrap" id="tacoWrap">
<svg id="tacoSvg" viewBox="0 0 340 300" role="img" aria-label="A taco loaded with bulgogi, kimchi and salsa">
<g transform="translate(170,238)">
<g class="flames">
<path d="M-30 2C-34-8-32-18-26-24c2 10 8 12 10 20 4-10 10-8 12-16 6 10 4 18-2 22z" fill="#F77F00"/>
<path d="M2 4C-4-10-2-24 6-30c0 12 6 14 8 20 4-8 10-6 12-12 6 10 4 20-4 26z" fill="#F77F00"/>
<path d="M28 2c-4-8-2-16 4-20 0 8 6 10 8 16 6-8 12 0 8 6-4 4-14 2-20-2z" fill="#F77F00"/>
<path d="M-10 2c-4-6-2-12 2-16 2 8 6 8 8 14 2 4 0 6-10 2z" fill="#FFC53D"/>
<path d="M12 4c-4-8-2-16 4-20 0 8 4 8 6 14 2 6-4 8-10 6z" fill="#FFC53D"/>
</g>
</g>
<g fill="none" stroke-linecap="round">
<path d="M104 133c-3-16 2-30 12-38" stroke="#4E9F3D" stroke-width="9"/>
<path d="M118 134c4-16 12-26 22-30" stroke="#77C159" stroke-width="8"/>
<path d="M244 132c4-15 0-28-10-36" stroke="#7B4A9E" stroke-width="9"/>
<path d="M230 134c-3-14-10-24-18-28" stroke="#9A6BC4" stroke-width="8"/>
<path d="M88 128c-5-12-3-24 5-32" stroke="#77C159" stroke-width="8"/>
<path d="M256 128c5-12 3-24-5-32" stroke="#77C159" stroke-width="8"/>
</g>
<g stroke="#231610" stroke-width="3.5">
<path d="M96 140c8-14 26-16 38-10-6 8-26 14-38 10z" fill="#8E5030"/>
<path d="M206 140c10-12 28-14 40-8-6 8-28 16-40 8z" fill="#A05A32"/>
<path d="M138 148c12-10 30-12 44-8-10 10-30 14-44 8z" fill="#8E5030"/>
</g>
<g stroke="#231610" stroke-width="3">
<rect x="116" y="102" width="19" height="11" rx="4.5" fill="#E23B2E" transform="rotate(-8 126 108)"/>
<rect x="192" y="104" width="18" height="11" rx="4.5" fill="#D93A2B" transform="rotate(7 201 110)"/>
<rect x="158" y="88" width="17" height="11" rx="4.5" fill="#E23B2E" transform="rotate(-4 166 94)"/>
<rect x="140" y="114" width="15" height="10" rx="4" fill="#C9301F" transform="rotate(6 148 119)"/>
<rect x="176" y="112" width="14" height="10" rx="4" fill="#E23B2E" transform="rotate(-6 183 117)"/>
</g>
<g fill="#FFF6E9" stroke="#231610" stroke-width="2">
<ellipse cx="150" cy="100" rx="4.5" ry="2.6" transform="rotate(-18 150 100)"/>
<ellipse cx="177" cy="122" rx="4.5" ry="2.6" transform="rotate(12 177 122)"/>
<ellipse cx="207" cy="90" rx="4.5" ry="2.6" transform="rotate(24 207 90)"/>
<ellipse cx="126" cy="124" rx="4.5" ry="2.6" transform="rotate(30 126 124)"/>
</g>
<path class="salsa" d="M90 138c26 12 58 16 80 10 26-6 54-4 76 6-14 16-50 24-82 18-32-6-58-18-74-34z" stroke="#231610" stroke-width="4"/>
<g transform="translate(262 150) rotate(14)">
<circle r="16" fill="#C9E59B" stroke="#231610" stroke-width="3.5"/>
<path d="M-10 8 12-8M-8-10 8 10M0-14v28" stroke="#8FBF57" stroke-width="2.4"/>
</g>
<path d="M34 148C64 236 276 236 306 148C282 176 58 176 34 148Z" fill="#F5B849" stroke="#231610" stroke-width="5" stroke-linejoin="round"/>
<path d="M48 152c52 26 148 26 200 0" fill="none" stroke="#FBD98A" stroke-width="6" opacity=".9"/>
<g fill="#C97F2F" opacity=".75">
<ellipse cx="118" cy="192" rx="9" ry="4"/><ellipse cx="176" cy="202" rx="10" ry="4.5"/><ellipse cx="232" cy="188" rx="8" ry="4"/>
</g>
<g transform="translate(28,196)">
<path d="M0 6l7 28h40l7-28z" fill="#FFF6E9" stroke="#231610" stroke-width="4.5" stroke-linejoin="round"/>
<ellipse class="salsa" cx="27" cy="6" rx="27" ry="8" stroke="#231610" stroke-width="4.5"/>
</g>
</svg>
</div>
</div>
<div class="heat-card">
<div class="heat-head">
<label for="heatSlider">How loco do you want it?</label>
<span class="heat-read" id="heatRead">SEOUL SIZZLE</span>
</div>
<input type="range" id="heatSlider" min="0" max="100" value="30" step="1" aria-label="Spice level">
<p class="heat-sub" id="heatSub"></p>
</div>
</div>
</div>
</section>
<div class="divider"><div class="t-track rev" id="tickerB"></div></div>
<!-- ============ menu ============ -->
<section class="menu" id="menu">
<div class="shead reveal">
<span class="kicker">The Menu</span>
<h2>Hot off the plancha</h2>
<svg class="squig" viewBox="0 0 220 16" aria-hidden="true"><path d="M3 11c14-9 26-9 40 0s28 9 42 0 28-9 42 0 28 9 42 0 24-9 48 0" fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round"/></svg>
<p>Made to order, tortillas pressed while you wait, heat marked honestly.</p>
</div>
<div class="menu-grid" id="menuGrid"></div>
<div class="drinks reveal">
<b>In the cooler →</b>
<span>Yuja-ade</span><span>Watermelon soju slushy (21+)</span><span>Mexican Coke</span><span>Horchata makgeolli float</span><span>Cucumber limeade</span><span>Hot tea when it's cold out</span>
</div>
<p class="menu-note">Corn tortillas everywhere · gluten-friendly · ask us anything, we love talking about this stuff</p>
</section>
<!-- ============ truck ============ -->
<section class="truck" id="truck">
<div class="shead reveal">
<span class="kicker">Find us</span>
<h2>Where's the truck?</h2>
<svg class="squig" viewBox="0 0 220 16" aria-hidden="true"><path d="M3 11c14-9 26-9 40 0s28 9 42 0 28-9 42 0 28 9 42 0 24-9 48 0" fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round"/></svg>
<p>Same spots every week. The smell finds you before the truck does.</p>
</div>
<div class="truck-grid">
<div>
<div class="status-card reveal" id="statusCard"></div>
<div class="road reveal" id="road">
<svg id="truckSvg" viewBox="0 0 380 175" role="img" aria-label="The Kimchi Loco food truck — click to honk">
<ellipse cx="190" cy="163" rx="158" ry="8" fill="rgba(35,22,16,.18)"/>
<g class="t-bob">
<rect x="48" y="26" width="10" height="22" rx="4" fill="#231610"/>
<g class="smoke">
<circle cx="53" cy="16" r="5" fill="rgba(255,246,233,.55)"/>
<circle cx="58" cy="9" r="4" fill="rgba(255,246,233,.45)"/>
</g>
<rect x="140" y="28" width="42" height="16" rx="5" fill="#E23B2E" stroke="#231610" stroke-width="3.5"/>
<rect x="26" y="100" width="252" height="15" fill="#E23B2E"/>
<path d="M278 58h28q18 3 24 18l14 44h-66z" fill="#FFFDF6" stroke="#231610" stroke-width="4" stroke-linejoin="round"/>
<rect x="26" y="44" width="252" height="76" rx="9" fill="#FFFDF6" stroke="#231610" stroke-width="4"/>
<text x="228" y="70" font-family="'Arial Black',Arial,sans-serif" font-weight="900" font-size="19" fill="#231610" text-anchor="middle">KIMCHI</text>
<text x="228" y="92" font-family="'Arial Black',Arial,sans-serif" font-weight="900" font-size="19" fill="#B01F14" text-anchor="middle">LOCO</text>
<rect x="84" y="42" width="104" height="13" fill="#E23B2E" stroke="#231610" stroke-width="3"/>
<rect x="110" y="42" width="26" height="13" fill="#9BC53B"/>
<rect x="162" y="42" width="26" height="13" fill="#9BC53B"/>
<rect x="88" y="58" width="96" height="42" rx="8" fill="#231610"/>
<rect x="98" y="66" width="60" height="4" rx="2" fill="rgba(255,246,233,.8)"/>
<rect x="98" y="76" width="76" height="4" rx="2" fill="rgba(255,246,233,.5)"/>
<rect x="98" y="86" width="46" height="4" rx="2" fill="rgba(255,246,233,.6)"/>
<g transform="translate(136,55)">
<g class="sign">
<line x1="0" y1="0" x2="0" y2="9" stroke="#FFF6E9" stroke-width="3"/>
<rect x="-22" y="9" width="44" height="17" rx="4" fill="#FFF6E9" stroke="#231610" stroke-width="2.5"/>
<text x="0" y="21.5" font-family="Arial,sans-serif" font-weight="900" font-size="10" fill="#B01F14" text-anchor="middle">OPEN</text>
</g>
</g>
<path d="M288 64h22q10 2 14 11l6 17h-42z" fill="#3A2620"/>
<g>
<circle cx="95" cy="132" r="17" fill="#231610"/><circle cx="95" cy="132" r="7.5" fill="#FFF6E9"/><circle cx="95" cy="132" r="2.6" fill="#231610"/>
<circle cx="322" cy="132" r="17" fill="#231610"/><circle cx="322" cy="132" r="7.5" fill="#FFF6E9"/><circle cx="322" cy="132" r="2.6" fill="#231610"/>
</g>
</g>
</svg>
<div class="road-line"></div>
</div>
<p class="truck-note">We drop the exact pin on Instagram at 10AM daily. Rain or shine. If it's really coming down — look across the street, we'll be in the bar.</p>
</div>
<div>
<div class="sched reveal">
<div class="sched-head">The weekly route</div>
<div id="schedList"></div>
</div>
<button class="btn ghost2" id="pinBtn">Drop me a pin <svg class="ico" aria-hidden="true"><use href="#i-pin"/></svg></button>
</div>
</div>
</section>
<!-- ============ hype ============ -->
<section class="hype" id="hype">
<div class="shead reveal">
<span class="kicker">Hype</span>
<h2>The people are yelling (nicely)</h2>
<svg class="squig" viewBox="0 0 220 16" aria-hidden="true"><path d="M3 11c14-9 26-9 40 0s28 9 42 0 28-9 42 0 28 9 42 0 24-9 48 0" fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round"/></svg>
<p>Over 2,100 reviews and a line that moves faster than it looks.</p>
</div>
<div class="quotes">
<div class="q reveal" style="--d:0ms">
<div class="stars"><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg></div>
<p>"I planned to try one taco. That was four months ago. Send help. Don't actually send help."</p>
<b>Dana R. · regular since day one</b>
</div>
<div class="q reveal" style="--d:90ms">
<div class="stars"><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg></div>
<p>"The short rib taco might be the single best four-fifty in this city. We counted."</p>
<b>Fork & Spoon Magazine</b>
</div>
<div class="q reveal" style="--d:180ms">
<div class="stars"><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg><svg class="ico"><use href="#i-star"/></svg></div>
<p>"The buldak wings made my dad cry. He asked for the recipe. We're family with the chefs now."</p>
<b>Marcus T. · verified spice victim</b>
</div>
</div>
<div class="badges reveal">
<span class="badge"><i>★</i>Best late-night eats — City Weekly</span>
<span class="badge"><i>★</i>Top 10 food trucks — Fork & Spoon</span>
<span class="badge"><i>★</i>People's choice — Grand Central Food Park</span>
</div>
</section>
<!-- ============ story ============ -->
<section class="story">
<div class="story-inner reveal">
<span class="kicker">Est. 2019</span>
<p>"A taquero from Oaxaca and a cook from Busan shared one midnight fridge. There was <b>one fight</b> — about hot sauce — and one leftover stack of tortillas. That's it. That's the whole origin story. Now there's a truck."</p>
<div class="founders">
<span class="f-chip">
<svg width="38" height="38" viewBox="0 0 40 40" aria-hidden="true"><circle cx="20" cy="20" r="17" fill="#FFE4BC" stroke="#231610" stroke-width="3"/><circle cx="14" cy="17" r="2.2" fill="#231610"/><circle cx="26" cy="17" r="2.2" fill="#231610"/><path d="M13 27q7 6 14 0" fill="none" stroke="#231610" stroke-width="2.6" stroke-linecap="round"/></svg>
Chef Marco<small>the tortilla half</small>
</span>
<span class="f-chip">
<svg width="38" height="38" viewBox="0 0 40 40" aria-hidden="true"><circle cx="20" cy="20" r="17" fill="#FFE4BC" stroke="#231610" stroke-width="3"/><circle cx="14" cy="17" r="3.4" fill="none" stroke="#231610" stroke-width="2.2"/><circle cx="26" cy="17" r="3.4" fill="none" stroke="#231610" stroke-width="2.2"/><path d="M17.4 17h5.2" stroke="#231610" stroke-width="2.2"/><path d="M14 26q6 5 12 0" fill="none" stroke="#231610" stroke-width="2.6" stroke-linecap="round"/></svg>
Chef Soo<small>the gochujang half</small>
</span>
</div>
</div>
</section>
<!-- ============ footer ============ -->
<footer>
<div class="f-big">See you at<br><em>the window.</em></div>
<div class="f-grid">
<div>
<h4>Get the drop</h4>
<p>One email a week, max — where the truck is, what's bubbling in the pots, secret-menu stuff.</p>
<form class="news-form" id="newsForm">
<input type="email" id="newsInput" required placeholder="you@hungry.com" aria-label="Email address">
<button class="btn ghost" type="submit">Join</button>
</form>
</div>
<div>
<h4>Hours</h4>
<ul><li>Mon–Tue · 11A–9P</li><li>Wed–Thu · 5P–Late</li><li>Fri–Sat · 12P–2A</li><li>Sun · 11A–6P</li></ul>
</div>
<div>
<h4>Talk</h4>
<p style="margin-bottom:8px">Catering? Same email,<br>subject line: <b style="color:var(--yellow)">“BIG TACO ENERGY.”</b></p>
<a href="mailto:tacos@kimchiloco.food">tacos@kimchiloco.food</a><br>
<a href="tel:+15558625626">(555) 862-LOCO</a>
</div>
<div>
<h4>Follow</h4>
<div class="socs" id="socials">
<button class="soc" data-name="Instagram">Instagram <svg class="ico" aria-hidden="true"><use href="#i-arrow"/></svg></button>
<button class="soc" data-name="TikTok">TikTok <svg class="ico" aria-hidden="true"><use href="#i-arrow"/></svg></button>
</div>
</div>
</div>
<div class="f-bottom">
<span>© 2025 Kimchi Loco LLC · Seoul × Mexico City</span>
<span>Hot sauce responsibly.</span>
</div>
</footer>
<!-- ============ cart ============ -->
<div class="overlay" id="overlay"></div>
<aside class="cart" id="cartPanel" aria-label="Your order">
<div class="cart-head">
<h3>Your order <span>@ the window</span></h3>
<button id="cartClose" aria-label="Close order"><svg><use href="#i-close"/></svg></button>
</div>
<div class="cart-body" id="cartBody"></div>
<div class="cart-foot" id="cartFoot"></div>
</aside>
<div class="toasts" id="toasts"></div>
<script>
(function(){
"use strict";
var $=function(s){return document.querySelector(s)};
var $$=function(s){return Array.prototype.slice.call(document.querySelectorAll(s))};
var reduced=window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* ---------- data ---------- */
var MENU=[
{id:"bulgogi",name:"Bul-Go-Gee Taco",desc:"Three-week marinated short rib, sesame slaw, kimchi pico, lime crema.",price:4.50,heat:1,tag:"FAN FAVORITE",icon:"taco"},
{id:"gochujang",name:"Gochujang Chicken Taco",desc:"Double-fried thigh brushed with sweet-fire gochujang, pickled daikon, crushed nori.",price:4.75,heat:2,tag:null,icon:"chili"},
{id:"loco",name:"El Loco Burrito",desc:"Bulgogi + day-old fried rice + stretchy queso fresco + crispy nori shards.",price:11,heat:2,tag:"THE BIG ONE",icon:"burrito"},
{id:"queso",name:"Kimchi Quesadilla",desc:"Aged cheddar + charred kimchi, toasted till the edges shatter. Gochujang sour cream dip.",price:8,heat:1,tag:"VEGGIE",icon:"queso"},
{id:"fries",name:"Seoul Street Fries",desc:"Fries buried under bulgogi bits, mozzarella sauce, doenjang mayo, scallion snow.",price:7.50,heat:1,tag:null,icon:"fries"},
{id:"wings",name:"Buldak “Crybaby” Wings",desc:"Six wings. Soy-garlic or buldak fire. Comes with a look of genuine concern.",price:9,heat:3,tag:"SIGNATURE",icon:"wings"}
];
var HEAT_LABEL=["","mild","spicy","certified"];
var SCHED=[
{d:1,day:"Mon",start:11,end:21,spot:"Ridgeline Brewing"},
{d:2,day:"Tue",start:11,end:21,spot:"Ridgeline Brewing"},
{d:3,day:"Wed",start:17,end:23,spot:"Grand Central Food Park"},
{d:4,day:"Thu",start:17,end:24,spot:"Grand Central Food Park"},
{d:5,day:"Fri",start:12,end:26,spot:"Night Market · 8th & Main"},
{d:6,day:"Sat",start:12,end:26,spot:"Night Market · 8th & Main"},
{d:0,day:"Sun",start:11,end:18,spot:"Fowler St Farmers Market"}
];
function fmtH(h){h=((h%24)+24)%24;if(h===0)return"12AM";if(h===12)return"12PM";return h>12?(h-12)+"AM":h+"AM"}
/* ---------- tickers ---------- */
function buildTicker(id,items){
var half=items.map(function(t){return"<b>"+t+"</b><i>★</i>"}).join("");
document.getElementById(id).innerHTML=half+half;
}
buildTicker("tickerA",["BULGOGI TACOS","KIMCHI FRIES","GOCHUJANG WINGS","SOJU SLUSHIES","OPEN LATE","EL LOCO BURRITO","BULDAK WINGS","YUJA-ADE","PRESSED TO ORDER"]);
buildTicker("tickerB",["COMPLIMENTS TO THE CHEF","TACO TUESDAY? EVERY DAY.","HEAT MARKED HONESTLY","'TIL 2AM ON WEEKENDS","CORN TORTILLAS EVERYWHERE","GET LOCO"]);
/* ---------- live status ---------- */
function statusNow(){
var now=new Date(),dow=now.getDay(),t=now.getHours()+now.getMinutes()/60;
var today=null,i;
for(i=0;i<SCHED.length;i++)if(SCHED[i].d===dow)today=SCHED[i];
if(today&&t>=today.start&&t<today.end)return{open:true,spot:today.spot,until:fmtH(today.end)};
if(today&&t<today.start)return{open:false,spot:today.spot,next:"today at "+fmtH(today.start)};
for(var k=1;k<8;k++){
var s=null;
for(i=0;i<SCHED.length;i++)if(SCHED[i].d===(dow+k)%7)s=SCHED[i];
if(s)return{open:false,spot:s.spot,next:(k===1?"tomorrow":s.day)+" at "+fmtH(s.start)};
}
return{open:false,spot:"the usual corner",next:"soon"};
}
function renderStatus(){
var s=statusNow();
var pill=$("#pillTop");
pill.classList.toggle("open",s.open);
pill.innerHTML='<span class="dot"></span>'+(s.open?"OPEN NOW":"CLOSED");
$("#heroStatus").innerHTML=s.open
?"<b class='open-t'>We're slinging RIGHT NOW</b> at "+s.spot+" — griddle hot 'til "+s.until+"."
:"Griddle's off right now. <b class='closed-t'>Next stop: "+s.spot+", "+s.next+".</b>";
var sc=$("#statusCard");
sc.classList.toggle("open",s.open);
sc.innerHTML='<span class="dot"></span><div><b>'+(s.open?"OPEN — COME HUNGRY":"CLOSED — GRIDDLE NAPPING")+'</b><span>'+
(s.open?"Sliding at "+s.spot+" 'til "+s.until+". Follow the sesame smoke — it finds you first."
:"Back at "+s.spot+" "+s.next+". The kimchi is fermenting patiently until then.")+'</span></div>';
}
renderStatus();
setInterval(renderStatus,30000);
/* ---------- schedule ---------- */
var todayD=new Date().getDay();
$("#schedList").innerHTML=SCHED.map(function(s){
return '<div class="srow'+(s.d===todayD?" today":"")+'"><b>'+s.day+'</b><span>'+fmtH(s.start)+"–"+fmtH(s.end)+'</span><em>'+s.spot+'</em>'+(s.d===todayD?'<i class="now">TODAY</i>':"")+'</div>';
}).join("");
/* ---------- menu ---------- */
var menuGrid=$("#menuGrid");
menuGrid.innerHTML=MENU.map(function(m,i){
var chilis=[0,1,2].map(function(k){return '<svg class="chili'+(k<m.heat?"":" off")+'" aria-hidden="true"><use href="#i-chili"/></svg>'}).join("");
return '<article class="card reveal" style="--d:'+(i*60)+'ms">'+
(m.tag?'<span class="tag">'+m.tag+'</span>':"")+
'<div class="card-top"><span class="dish"><svg class="ico" aria-hidden="true"><use href="#i-'+m.icon+'"/></svg></span>'+
'<h3>'+m.name+'</h3><span class="price">$'+m.price.toFixed(2)+'</span></div>'+
'<p class="desc">'+m.desc+'</p>'+
'<div class="card-bot"><span class="heatcheck">'+chilis+'<em>'+HEAT_LABEL[m.heat]+'</em></span>'+
'<button class="add" data-add="'+m.id+'">Add +</button></div></article>';
}).join("");
/* ---------- heat slider ---------- */
var stage=$("#stage"),slider=$("#heatSlider"),heatRead=$("#heatRead"),heatSub=$("#heatSub");
var STOPS=[[155,196,59],[255,197,61],[247,127,31],[226,59,46],[158,18,24]];
function salsa(v){
var t=v/100*(STOPS.length-1),i=Math.min(STOPS.length-2,Math.floor(t)),f=t-i,c=[];
for(var k=0;k<3;k++)c.push(Math.round(STOPS[i][k]+(STOPS[i+1][k]-STOPS[i][k])*f));
return"rgb("+c.join(",")+")";
}
var HEATS=[
[24,"SO GENTLE","Soy-garlic energy. Abuela-approved. Halmoni-approved."],
[49,"SEOUL SIZZLE","A polite little kick. You will be fine. Probably."],
[74,"GOCHUJANG GANG","The sweet-fire paste does its thing. Napkins within reach."],
[94,"BULDAK BRAVE","Sweating at the window is a normal Tuesday here."],
[101,"EL LOCO","Sign the flavor waiver at the counter. We check ID and soul."]
];
var heat=+slider.value, userTouched=false;
function applyHeat(v){
heat=v;
var c=salsa(v);
stage.style.setProperty("--salsa",c);
stage.style.setProperty("--glow",(4+v*.20).toFixed(1)+"px");
stage.style.setProperty("--fo",Math.max(0,(v-55)/45).toFixed(2));
var L=HEATS[0];
for(var i=0;i<HEATS.length;i++)if(v<=HEATS[i][0]){L=HEATS[i];break}
heatRead.textContent=L[1];
heatRead.style.background=c;
heatRead.style.color=v>52?"#FFF6E9":"#231610";
heatSub.textContent=L[2];
stage.classList.toggle("loco",v>=95);
}
slider.addEventListener("input",function(){userTouched=true;applyHeat(+slider.value)});
slider.addEventListener("pointerdown",function(){userTouched=true});
applyHeat(heat);
/* gentle self-demo sweep until the visitor takes over */
if(!reduced){
setTimeout(function(){
if(userTouched)return;
var from=+slider.value,to=62,t0=performance.now(),dur=2400;
(function tick(t){
if(userTouched)return;
var p=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-p,3);
slider.value=from+(to-from)*e;
applyHeat(+slider.value);
if(p<1)requestAnimationFrame(tick);
})(t0);
},900);
}
/* ---------- steam ---------- */
var steamBox=$("#steam");
function puff(){
var p=document.createElement("i");
var s=(9+Math.random()*13);
p.style.left=(10+Math.random()*80)+"%";
p.style.width=s+"px";p.style.height=s+"px";
p.style.setProperty("--dy",-(60+Math.random()*70)+"px");
p.style.setProperty("--dur",(1.5+Math.random()*1.3)+"s");
p.addEventListener("animationend",function(){p.remove()});
steamBox.appendChild(p);
}
if(!reduced){(function loop(){puff();setTimeout(loop,280+Math.max(0,(100-heat)*3))})()}
/* ---------- taco tilt ---------- */
var tacoWrap=$("#tacoWrap");
if(window.matchMedia("(pointer:fine)").matches){
stage.addEventListener("pointermove",function(e){
var r=stage.getBoundingClientRect();
var dx=(e.clientX-r.left)/r.width-.5,dy=(e.clientY-r.top)/r.height-.5;
tacoWrap.style.transform="rotate("+(dx*5).toFixed(2)+"deg) translate("+(dx*10).toFixed(1)+"px,"+(dy*8).toFixed(1)+"px)";
});
stage.addEventListener("pointerleave",function(){tacoWrap.style.transform=""});
}
/* ---------- cart ---------- */
var cart={};
var cartBtn=$("#cartBtn"),cartCount=$("#cartCount"),panel=$("#cartPanel"),overlay=$("#overlay"),
cartBody=$("#cartBody"),cartFoot=$("#cartFoot"),toasts=$("#toasts");
function toast(html){
var t=document.createElement("div");
t.className="toast";t.innerHTML=html;
toasts.appendChild(t);
requestAnimationFrame(function(){t.classList.add("show")});
setTimeout(function(){t.classList.remove("show");setTimeout(function(){t.remove()},350)},2600);
}
function addToCart(id){
cart[id]=(cart[id]||0)+1;
renderCart();
var m=null;for(var i=0;i<MENU.length;i++)if(MENU[i].id===id)m=MENU[i];
toast("<b>"+m.name+"</b> added — good call.");
cartCount.classList.remove("pop");void cartCount.offsetWidth;cartCount.classList.add("pop");
}
function renderCart(){
var ids=Object.keys(cart),n=0,i;
for(i=0;i<ids.length;i++)n+=cart[ids[i]];
cartCount.textContent=n;
cartCount.classList.toggle("show",n>0);
if(!n){
cartBody.innerHTML='<p class="empty">Nothing yet.<br>The tacos can feel ignored, you know.</p><p class="empty2">Hit <b>Add +</b> on anything that sounds loud.</p>';
cartFoot.innerHTML="";
return;
}
var total=0;
var rows=ids.map(function(id){
var m=null;for(var j=0;j<MENU.length;j++)if(MENU[j].id===id)m=MENU[j];
var q=cart[id];total+=m.price*q;
return '<div class="crow"><div class="crow-l"><b>'+m.name+'</b><span>$'+(m.price*q).toFixed(2)+'</span></div>'+
'<div class="stepper"><button data-step="-1" data-id="'+id+'" aria-label="One less">−</button><span>'+q+'</span><button data-step="1" data-id="'+id+'" aria-label="One more">+</button></div></div>';
}).join("");
cartBody.innerHTML=rows;
cartFoot.innerHTML='<div class="ctotal"><span>Subtotal</span><b>$'+total.toFixed(2)+'</b></div>'+
'<button class="btn fire" id="fireBtn">Fire up my order</button>'+
'<p class="cnote">Pay at the window — cash, card, or your dignity.</p>';
}
function openCart(){panel.classList.add("open");overlay.classList.add("show")}
function closeCart(){panel.classList.remove("open");overlay.classList.remove("show")}
cartBtn.addEventListener("click",openCart);
$("#cartClose").addEventListener("click",closeCart);
overlay.addEventListener("click",closeCart);
window.addEventListener("keydown",function(e){if(e.key==="Escape")closeCart()});
cartBody.addEventListener("click",function(e){
var b=e.target.closest("[data-step]");
if(!b)return;
var id=b.dataset.id,d=+b.dataset.step;
cart[id]+=d;
if(cart[id]<=0)delete cart[id];
renderCart();
});
menuGrid.addEventListener("click",function(e){
var b=e.target.closest("[data-add]");
if(b)addToCart(b.dataset.add);
});
function burst(x,y){
var colors=["#E23B2E","#FFC53D","#9BC53B","#FFF6E9"];
for(var i=0;i<14;i++){
var d=document.createElement("i");
d.className="bdot";
var a=Math.random()*Math.PI*2,r=40+Math.random()*95;
d.style.cssText="left:"+x+"px;top:"+y+"px;background:"+colors[i%4]+";--tx:"+(Math.cos(a)*r)+"px;--ty:"+(Math.sin(a)*r-40)+"px;";
document.body.appendChild(d);
setTimeout(function(el){return function(){el.remove()}}(d),900);
}
}
function checkout(rect){
var s=statusNow(),total=0;
var lines=Object.keys(cart).map(function(id){
var m=null;for(var j=0;j<MENU.length;j++)if(MENU[j].id===id)m=MENU[j];
total+=m.price*cart[id];
return "<div>"+cart[id]+" × "+m.name+"</div>";
}).join("");
var num="LOCO-"+(100+Math.floor(Math.random()*900));
var where=s.open?s.spot:s.spot+" — "+s.next;
burst(rect.left+rect.width/2,rect.top+rect.height/2);
cartBody.innerHTML='<div class="ticket">'+
'<div class="tk-head">★ KIMCHI LOCO ★</div>'+
'<div class="tk-num">ORDER #'+num+'</div>'+
lines+
'<div class="tk-total">TOTAL $'+total.toFixed(2)+'</div>'+
'<div class="tk-line"></div>'+
'<div>READY IN ~8 MIN AT</div><div class="tk-where">'+where+'</div>'+
'<div class="tk-line"></div>'+
'<div class="tk-small">Show this at the window and yell “LOCO!” for 10% off. (Started as a joke. It stuck.)</div>'+
'</div>';
cartFoot.innerHTML='<button class="btn ghost2" id="doneBtn">Start another round</button>';
toast("<b>Order fired!</b> The griddle heard you.");
}
cartFoot.addEventListener("click",function(e){
var f=e.target.closest("#fireBtn");
if(f){checkout(f.getBoundingClientRect());return}
var d=e.target.closest("#doneBtn");
if(d){for(var k in cart)delete cart[k];renderCart();closeCart()}
});
renderCart();
/* ---------- truck honk ---------- */
var road=$("#road");
$("#truckSvg").addEventListener("click",function(){
var b=document.createElement("div");
b.className="beep";b.textContent="BEEP BEEP!";
road.appendChild(b);
setTimeout(function(){b.remove()},950);
});
$("#pinBtn").addEventListener("click",function(){
toast("<b>Pin dropped.</b> Look for the shiny silver truck and the short line that moves fast.");
});
/* ---------- socials + newsletter ---------- */
$$("#socials .soc").forEach(function(b){
b.addEventListener("click",function(){toast("See you on <b>"+b.dataset.name+"</b> — same handle, same tacos.")});
});
$("#newsForm").addEventListener("submit",function(e){
e.preventDefault();
var em=$("#newsInput");
if(!em.value)return;
toast("<b>You're in.</b> We'll slide into your inbox the next time the truck moves.");
em.value="";
});
/* ---------- reveal + counters ---------- */
var io=new IntersectionObserver(function(es){
es.forEach(function(en){
if(en.isIntersecting){en.target.classList.add("in");io.unobserve(en.target)}
});
},{threshold:.12});
$$(".reveal").forEach(function(el){io.observe(el)});
setTimeout(function(){
$$("[data-count]").forEach(function(el){
var target=parseFloat(el.dataset.count),dec=+(el.dataset.dec||0),t0=performance.now(),dur=1400;
(function tk(t){
var p=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-p,3),v=target*e;
el.textContent=dec?v.toFixed(dec):Math.round(v).toLocaleString("en-US");
if(p<1)requestAnimationFrame(tk);
})(t0);
});
},250);
/* ---------- topbar shadow ---------- */
window.addEventListener("scroll",function(){$("#topbar").classList.toggle("scrolled",window.scrollY>10)},{passive:true});
})();
</script>
</body>
</html>Reasoning trace (47,798 tokens)
The user wants a website for a Korean taco food truck called "Kimchi Loco". This is a marketing/landing page task. I need to make it vibrant, mouth-watering, energetic — something that makes people really want to come.
Let me think about the design direction:
**Brand identity: "Kimchi Loco"**
- Korean-Mexican fusion. The name itself is playful ("loco" = crazy in Spanish).
- Color palette: Korean taco trucks typically use bold reds (gochujang/kimchi red), lime greens, warm yellows/crepes. Think: hot red (#E63946 or a gochujang red like #C1272D), lime green (#8AC926 or #A7D129), cream (#FFF8EF), black for contrast. Maybe a neon-sign aesthetic for the "food truck at night" vibe.
- Typography: bold condensed display font for headlines (something like "Archivo Black" or "Anton" via Google Fonts — wait, no external network requests allowed except CDN URLs explicitly allowed by the task. The task doesn't explicitly allow any CDN. Hmm — "No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task." The task doesn't mention any allowed CDNs. So I should avoid Google Fonts too? That's risky — fonts via CDN are network requests. To be safe, use system fonts with strong fallbacks. I can use system-ui stacks and heavy weights (font-weight: 900) which render well. Actually, many of these demos use Google Fonts CDN... but the rule says "except the CDN URLs explicitly allowed by the task" — the task allows none. I'll use system fonts: "Arial Black", "Helvetica Neue", Impact-adjacent stacks. I can create a punchy look with `font-family: 'Arial Black', 'Helvetica Neue', system-ui, sans-serif` and letter-spacing. Also could use SVG-drawn text effects. Let me stay safe with system fonts.
Wait, actually — images: no external images. So all visuals must be CSS/SVG/canvas. I can draw tacos with SVG! That could be a signature element: a big illustrated taco/kimchi in the hero, drawn in SVG with bold flat colors. That gives it character without photos.
**What makes people "really want to come":**
1. Giant appetite-tempting hero with bold claim.
2. Menu with vivid dish names + descriptions + prices.
3. Location + hours (food truck needs this): "Find the truck".
4. Social proof / press quotes.
5. A fun interactive element — signature moment. Ideas:
- A "spice meter" slider that changes the taco illustration color/steam intensity.
- Live "truck status" widget: OPEN NOW / pulsing, today's location.
- A marquee ticker of menu items.
- An animated truck illustration that drives across.
- "Gochujang heat slider" — drag from Mild to Loco, background tints, taco SVG gets hotter.
- A mini "build your taco" configurator? Could be cute but heavy.
- Confetti kimchi leaves?
Signature interactive idea: **"How Loco Are You?" heat-level slider** that drives the hero taco illustration: salsa color shifts from green (mild) through orange to fiery red, steam particles increase, background glow intensifies, and the label changes ("Soy Sauce Gentle" → "Gochujang Gang" → "LOCO MODE"). That's fun and on-brand.
Also a self-playing element: since this is a website, not a game, but the demo should be alive on first paint — animations running immediately (marquee, steam, truck driving by periodically, pulsing OPEN badge). No need for a "click to start" since it's not a game... The rules say games must self-play; this is a website so it just needs to be visually alive immediately.
**Layout plan (1280×800 first impression):**
1. **Top ticker/marquee bar** — "GOCHUJANG BULGOGI • KIMCHI FRIES • OPEN TIL 2AM •" scrolling.
2. **Header/nav** — logo "KIMCHI LOCO" with a taco SVG mark, links: Menu, Find the Truck, Story, and a big "ORDER" button. Sticky maybe.
3. **Hero** — split or full: massive headline "SEOUL MEETS THE STREET" or better: "KOREAN BBQ. MEXICAN SOUL. ONE CRAZY TRUCK." Big taco SVG illustration with animated steam. Heat slider signature moment. CTA "Find Today's Truck →". Pulsing "OPEN NOW" badge.
4. **Menu section** — cards with dish illustrations (small SVG icons per item), names like "Bulgogi Taco", "Kimchi Quesadilla", "Gochujang Wings", "Dumpling Nachos(?)", prices, heat rating (chili icons). Hover effects: cards lift, tilt.
5. **The truck / find us** — schedule table by day (Mon: brewery, etc.), today highlighted, and an animated truck SVG that drives across. Maybe a live "right now" status computed from actual Date/time — that's a nice real touch: "It's Tuesday 6pm — we're at Hop County Brewing right now!" Using JS Date.
6. **Testimonials** — a few punchy quotes with stars ("I dream about these tacos", press badges).
7. **Footer** — socials, hours, newsletter signup (fake, with a nice confirmation toast on submit — no backend), big logo.
**Interactivity checklist:**
- Heat slider (drag input, updates taco SVG + colors + labels + maybe body accent hue).
- Marquee animation (CSS keyframes).
- Steam particles (CSS/JS animated).
- Live open/closed status via Date.
- Today's schedule highlight.
- Menu card hover tilt/lift.
- Clicking "add to order" buttons adds to a little cart counter with a flying-dot animation and toast ("Added: 2× Bulgogi Taco"). A simple order tally makes it feel like a real app and is satisfying. Keep it lightweight — a floating cart pill that shows count and total, click to see summary modal? Or just a mini list drawer. Let me do: each menu card has "+ Add" button; clicking adds to order with a bounce animation on the cart badge and a small toast. Cart button opens a slide-over panel listing items with quantities and total, and a "I'm on my way" button that shows a confirmation. All local, no storage APIs.
- Smooth scroll for nav links.
- Scroll reveal animations (IntersectionObserver).
- Maybe a subtle cursor-following heat glow? Might be too much. Keep it: background has a soft animated grain/pattern.
Wait — constraints: no localStorage. Fine, state in JS memory.
Also: the screenshot is taken ~3 seconds after load without interaction. So hero must be fully rendered with animations mid-flow — fine, CSS animations start on load. Menu and everything visible if scrolled? Only viewport matters at 3s — hero should fill it and look rich. I should ensure the fold (1280×800) shows hero with everything: headline, taco illustration, slider, CTA, marquee, nav.
**Color palette:**
- Base cream: #FFF4E6 / #FFF7EC
- Ink: #1A1410 or #201612 (warm black)
- Primary red: #E8352E or gochujang #D7263D... let me pick a punchy red: #E23E2B (tomatillo-red), deeper red #B01E14.
- Lime/green: #7AB648 or #55A630... lime #9BC53C? Let me use "kimchi green" #4E9F3D and bright lime accent #B5E853? Hmm. A Korean taco palette: red #E63946, cream #FFF8F0, green #2A9D8F-ish? Actually lime green #8AB93B pairs well with red and cream. Let me set:
- `--red: #E33429`
- `--red-deep: #C0231B`
- `--green: #3E9B4F` hmm, or lime `#A6C93F`? For chips/accents: `--lime: #B4E04A`? Bright lime on cream can be low contrast. Use green #2E7D32 for text accents and lime for chips backgrounds.
- `--yellow: #FFC53D` (queso/mango yellow)
- `--ink: #21130E` (warm dark brown-black)
- `--cream: #FFF6E9`
Sections alternate: cream hero, ink section for menu (dark with red), cream for truck, ink footer? Or keep mostly cream with ink and red blocks. Bold "truck wrap" style: thick borders (like hand-painted sign), drop shadows (hard offset shadows — retro diner/truck vibe). Hard shadows (box-shadow: 6px 6px 0 ink) fit the food-truck aesthetic perfectly.
**Taco SVG illustration:** Draw a decent taco:
- A folded tortilla shape (half-ellipse), slightly golden with darker speckles, with fillings poking out the top: green cabbage shreds (wavy paths), red kimchi bits, meat (brown blobs), lime wedge. Keep it stylized flat with ink outlines (2-3px strokes) — matches "hand-painted truck sign" aesthetic.
Let me sketch coordinates for a taco SVG (viewBox 0 0 320 260):
- Tortilla: path like an open smile shape: M20,140 Q160,260 300,140 then top edge back with slight curve and a rim. Simpler: an ellipse cut in half? Let's do: outer shell path: `M30 130 C 30 210, 290 210, 290 130 C 290 190 ...` hmm. A taco = half-disc with wavy top opening.
Easier approach:
- Fillings behind: several rounded blobs and zigzag strips above the shell line.
- Shell: `M28 135 C 28 208 292 208 292 135 C 292 168 246 196 160 196 C 74 196 28 168 28 135 Z`? That gives a crescent. Hmm, I want a classic taco: top is an open arc where fillings show, bottom is round.
Let me define: bottom tortilla curve from left tip (30,120) to right tip (290,120) bulging down to y=215 in the middle: `M30 120 Q160 225 290 120` then close with the top lip: `Q160 160 30 120 Z`. That's a crescent shape — fill golden #F2B24A? Tortilla color: #F5C05B or #EFA94A. Add a lighter inner band.
Fillings sit in the opening: above y≈140-160 between x=60 and 260. Draw:
- Cabbage/purple cabbage shreds: wavy vertical strips in green (#6BBE45) and purple (#8C4A9E)? Purple cabbage is on brand for Korean-Mexican.
- Bulgogi slices: brown-red blobs #8E4A2A.
- Kimchi: red-orange chunks #E04E2F.
- Sesame dots: small white circles.
- Cilantro + lime wedge peeking out sides.
I'll build these as groups of simple paths. Also a second SVG: the food truck illustration for the "find us" section — a cute truck: box body with serving window, awning stripes, wheels, smokestack, "KIMCHI LOCO" side text, little taco sign. Truck drives across the screen periodically (CSS animation translateX) with bouncing wheels.
Also small SVG icons: chili (for heat ratings), clock, pin, star, instagram-ish glyphs. I'll use inline SVG symbols.
**Heat slider mechanics:**
- Range input 0–100 (or 5 steps: Mild, Spicy, Hot, Extra Hot, LOCO).
- On input:
- Update taco salsa blob colors (interpolate green→yellow→orange→red→deep red with flame glow).
- Update background radial glow intensity around taco.
- Update label text + "flame" count.
- Add `--heat` CSS var that controls an accent gradient in the hero and steam particle count/speed.
- Body shake at max? A tiny "LOCO MODE" wiggle on the headline — fun.
- Label list: 0-19 "Gringo Mild", 20-39 "Seoul Sizzle", 40-59 "Gochujang Gang", 60-79 "Fire Noodles", 80-99 "Buldak Brave", 100 "🥇 EL LOCO" — no emoji though. Just "EL LOCO MAX". Keep the flavor text.
Also slider changes a small "salsa cup" color and the flame icons near menu? Maybe simpler: just hero taco + a "Your salsa today" chip that changes color and name.
Steam: JS-generated particles rising above the taco — spawn divs with random x, animate up with keyframes, fade. Particle count scales with heat. Also at max heat, add flames (small animated SVG flames flickering) under/behind taco.
**Live status logic:**
Schedule (hardcoded):
- Mon: 11am–9pm @ "Ridgeline Brewing"
- Tue: 11am–9pm @ "Ridgeline Brewing"
- Wed: 5pm–11pm @ "Grand Central Food Park"
- Thu: 5pm–midnight @ "Grand Central Food Park"
- Fri: 12pm–2am @ "Night Market / 8th & Main"
- Sat: 12pm–2am @ "Night Market / 8th & Main"
- Sun: 11am–6pm @ "Fowler Street Farmers Market"
Compute current day/hour; if within hours → "OPEN NOW — we're at X until Y"; else → "CLOSED — next stop: X opens at ...". Show a countdown-ish? Keep simple: status pill with pulsing dot (green if open, red if closed) and a sentence: "It's Friday night — find us at the Night Market 'til 2AM, or 4h 12m from now we fire up the griddle." Hmm, computing next opening if closed: find today's next session or next day's. Let me implement: sessions as [day, startHour, endHour] with end possibly >24 (26 = 2am). Determine if now within [start,end) on that day-of-week. If yes → open, location, closes at (end%12). If no → find next session start: either later today or upcoming days; produce "Opens Thu 5PM at Fowler St..." I'll write a compact function. Also highlight today's row in the schedule table.
**Order/cart:**
- Menu items array with id, name, desc, price, heat(0-3), tags.
- Render cards with an SVG or emoji? No emoji. I'll do small colored "chili" glyphs for heat and a dish SVG icon — maybe simpler: each card has a stylized circular badge with the dish initial or a tiny icon (taco/burrito/fries/wings/quesadilla/rice bowl). I can make 6 mini SVG icons:
1. Taco (mini version)
2. Burrito (rolled rectangle with stripes)
3. Loaded fries (box with zigzag)
4. Wings (drumstick shape)
5. Quesadilla (wedge triangle)
6. Bibimbap bowl (bowl with steam)
- "+ Add" button → cart state {id: qty}; badge bounces; toast "Bulgogi Taco added — $4".
- Cart panel slides from right: list, +/- quantity steppers, total, "Fire up my order" button → fake confirmation state ("Order #88 ready at the window in 8 min — text SENT" playful), with a fun message. Include "call it in" fake. Keep it charming: after clicking, show a ticket-style confirmation with order number, ETA, and a joke. Also a "walk-up code" LOCO10.
Menu items (6-8):
1. **Bul-Go-gee Taco** — marinated short rib, sesame-slaw, kimchi pico, lime crema. $4.50. Heat 1.
2. **Gochujang Chicken Taco** — double-fried chicken thigh, gochujang glaze, pickled daikon. $4.75. Heat 2.
3. **El Loco Burrito** — bulgogi, fried rice, mozzarella+queso fresco, crispy nori. $11. Heat 2. "Loaded" tag.
4. **Kimchi Quesadilla** — aged cheddar + toasted kimchi, gochujang sour cream dip. $8. Heat 1. Veg tag.
5. **Seoul Street Fries** — fries, bulgogi bits, mozzarella sauce, scallion, doenjang mayo. $7.50. Heat 1.
6. **Buldak Wings "Cry Baby"** — 6 wings, soy-garlic or buldak fire. $9. Heat 3. "Ghost" tag.
7. **Bibimbap Taco Bowl(?)** — maybe keep at 6 items + drinks: **Yuja-Ade / Horchata-soju?** Alcohol maybe: "Watermelon Soju Slushy $7" (21+ chip). Let me do 8 cards: add "Tteokbokki Nachos"? Hmm 6 is clean in a grid. Maybe 6 mains + a "drinks" strip. Keep 6 cards + a marquee of drinks: "Yuja-ade · Watermelon Soju Slushy · Cucumber Mexican Coke · Horchata Makgeolli Float". Fun.
**Testimonials:** 3 quotes:
- "I came for one taco and left with a loyalty punch card and a slight kimchi addiction." — Dana R.
- Press badges row: "Best Late-Night Eats — City Weekly" "10 Best Food Trucks — Fork & Spoon Mag".
- "The bulgogi quesadilla is a war crime against my diet and I don't care." — @marco.eats
**Story strip:** Short paragraph: "One truck. Two grandmas... " something: " Founded by a taquero from Oaxaca and a seondu... " Let's write: "Kimchi Loco started with a question nobody asked: what if your abuela's tortillas met your halmoni's chili paste? Chef Marco & Chef Soo's answer is a silver truck that smells like toasted sesame and charred tortillas." Add small stat chips: "12,400 tacos slung" "3.9★ avg"? Numbers ticking up — animated counters on scroll! Nice detail: counters animate (tacos served, rating, days open).
**Aesthetic details:**
- Halftone/dot pattern backgrounds? Subtle.
- Hard shadows: `box-shadow: 8px 8px 0 rgba(33,19,14,1)`.
- Sticker-style rotated labels ("NEW!", "FAN FAVORITE").
- Section headers with a red underline swash (SVG).
- Noise: no.
- Wavy section dividers between cream and ink sections (SVG scallops).
- Marquee ticker top and mid-page ("COMPLIMENTS TO THE CHEF" ticker with quotes between menu and truck? Or keep one ticker).
- Buttons with press-down effect (translate on active).
- Focus on responsiveness: 375px wide — nav collapses to logo + cart; hero stacks; menu grid 1 col; truck section stacks; schedule table scrolls within container? I'll make schedule rows as flex cards that wrap, so no horizontal scroll.
**No horizontal scrollbar** — use max-width: 100%, overflow-x hidden on body, careful with marquee (use `overflow: hidden` container with inner track wider than viewport — fine since parent hides overflow).
**Structure:**
```
<body>
ticker (top, red bg, scrolling text with ★ separators)
header (sticky): logo (taco mark + wordmark), nav links, OPEN NOW pill, cart button
main:
hero: left: eyebrow "Seoul BBQ × Mexico City Streets", H1 "GET LOCO." big + subline, CTA buttons (Find the truck / Peep the menu), status pill w/ live logic, mini stats row
right: taco SVG on a sunburst/halftone badge, steam layer, heat slider card below: "HOW LOCO DO YOU WANT IT?" range + label
marquee divider (ink bg): "KIMCHI FRIES • GOCHUJANG WINGS • ..." — actually this is the mid divider
menu section (ink bg): header "THE MENU (HOT OFF THE PLANCHA)" + cards grid + drinks line
truck section (cream): "FIND THE LOCO" + truck SVG driving animation + schedule (today highlighted) + status sentence + map-ish card? maybe a "text us" chip.
hype section (red bg): testimonials 3 cards + press badges
footer (ink): big wordmark, hours summary, socials, newsletter input + subscribe (fake confirm), credits line.
cart panel (aside fixed) + overlay
toast container
```
Hmm, that's a lot but manageable. Let me also consider: sticky header with cart button and "open now" pill duplicated is fine — keep header slim: logo | nav (Menu, Truck, Hype) | order button.
Live status computed in JS: I'll render a sentence in hero and in truck section. Both update every minute.
**JS plan (~350 lines):**
- DATA: menu items, schedule.
- renderMenu(), renderSchedule(), initCart(), updateCart(), toast(), openPanel.
- Heat slider logic: interp colors. Define salsaStops = [{t:0,color:...}] use simple lerp across [green #7CB342, yellow #FFC53D, orange #F77F00, red #E33429, deep #A6121B]... Actually the salsa blob on the taco: I'll give fill `fill="url(#salsaGrad)"`? Simpler: set the fill attribute of a group of blobs via JS interpolation. Plus flame opacity = clamp((heat-60)/40).
- Steam: setInterval spawn based on heat; particle divs in `#steam` container above taco; each animates 2.2s translateY(-90px) scale up, opacity fade, then removed.
- Loco mode (heat >= 95): add class to hero for a subtle wiggle animation on headline and red bg pulse; label "🔥 EL LOCO" — no emoji; label text "☠️"? no emoji at all to be safe. Just "EL LOCO 🌶" — no. Text only: "EL LOCO — sign the waiver".
- IntersectionObserver reveals: elements with `[data-reveal]` get `.in`.
- Counter animation for stats when visible.
- Marquee: pure CSS with duplicated content (2 tracks translateX -100%).
- Smooth scroll: `scroll-behavior: smooth` CSS + JS for offset? Use scroll-margin-top on sections.
- Newsletter submit → preventDefault → show toast "You're on the list — see you at the window." + clear input. Fine, no storage.
- Order button in panel → show ticket state with random order number & ETA, confetti? Maybe tiny taco-shower particles — could be fun but keep it small: on confirm, animate a few leaf/taco sprites. Optional. I'll add a simple burst of red/lime dots (CSS animation), 12 particles.
Also demo self-play: not a game, but I could add a subtle auto behavior — e.g., heat slider auto-sweeps once on load (animates from 30 to 65 over 3s) so the screenshot shows it mid-interaction? Careful: it shouldn't fight user input. Auto-sweep only until first user interaction with slider (pointerdown cancels). That makes the demo feel alive. Good idea: on load, after 800ms, tween the slider value 20→55 over 2.5s unless user interacts.
Console-clean: careful with all APIs. Use `requestAnimationFrame` for tween.
**Fonts:** system stack. For the big display: `font-family: "Arial Black", "Helvetica Neue", Arial, sans-serif` with uppercase and tight letter-spacing... Actually letter-spacing tight on Arial Black looks good (-0.02em). For body: `"Helvetica Neue", Arial, "Segoe UI", sans-serif`. Maybe a serif accent for quotes? Georgia italic for testimonial quotes — nice contrast. OK.
Let me be careful about the "no external requests" — no Google Fonts. Confirmed.
**Responsive:**
- Hero grid: `grid-template-columns: 1.1fr 0.9fr` → 1 col under 900px.
- Menu grid: `repeat(auto-fill, minmax(300px,1fr))` → wraps.
- Header nav links hide under 820px (keep logo + cart + a compact "menu" link? Or keep links but smaller). I'll keep 3 short links, hide the middle ones < 720px.
- Ticker fine.
- Truck svg scales via width 100%.
**The truck SVG:** side view:
- Body: rounded rect (0-260 wide, 100 tall) white/cream with red stripe and text "KIMCHI LOCO" + small taco logo.
- Cab front: to the right (driving right) — hood shape.
- Wheels: two circles with hubcaps, rotating? CSS rotate on wheel group while truck moves.
- Window: dark rounded rect with awning stripes above (red/lime scalloped).
- Chimney smoke: little circles.
- Under-shadow ellipse.
Animation: truck container `animation: drive 9s infinite` translating from -30% to 110% with slight bob (`ease` keyframes), wheels spin (rotate transform-origin center). Actually wheels rotating via SVG `animation: spin 0.6s linear infinite` on a `<g>` with transform-box: fill-box.
Simplify: truck drives across a "road" strip in the find-us section. Loop with a pause. Wheels spin, truck bobs. Cute.
**Halftone sunburst behind taco:** CSS radial-gradient repeating? A classic: `repeating-conic-gradient(#F2B24A 0 9deg, #EFA94A 9deg 18deg)`? Repeating-conic in a circle behind taco — retro sunburst, matches food truck signage. Nice: a circle div with repeating-conic-gradient in warm yellows/reds, slowly rotating (60s). Plus ring border in ink.
**Time budget** — this is a big file (~900-1100 lines). Let's write carefully, verifying:
Key correctness checks:
1. All IDs referenced by JS exist.
2. No emoji anywhere (avoid rendering issues? emojis are fine but design-wise avoid).
3. No horizontal overflow: marquee uses `width: max-content` inside `overflow:hidden` — inner track is fine.
4. `prefers-reduced-motion`: could add media query to disable big animations — nice touch, cheap: `@media (prefers-reduced-motion: reduce){ * { animation-duration: .01ms !important; } }` — but that would also stop steam spawn visuals (JS) — I'll guard steam spawning on matchMedia too? Keep simple: CSS-only guard for animations; steam checks `matchMedia('(prefers-reduced-motion: reduce)').matches` skip. Fine, low cost.
Let me now write the content copy — this matters a lot for "makes people really want to come":
Hero:
- Eyebrow: "KOREAN BBQ × MEXICO CITY STREET FOOD"
- H1: "GET LOCO." with sub: "Bulgogi in your tortilla. Kimchi in your quesadilla. Sesame smoke in the air at 2AM. The most delicious identity crisis on wheels is parked somewhere near you."
- CTAs: "Find the truck today" (red), "See the menu →" (ghost).
- Stats: "128K tacos slung", "4.9 stars avg (2,140 reviews)", "Every night 'til 2AM".
- Status pill: computed.
Heat card: "HOW LOCO DO YOU WANT IT?" + slider + dynamic label + description: e.g. Mild: "Soy-glazed. Safe for your abuela." Loco: "Buldak-level. We check your ID and your soul."
Menu header: "HOT OFF THE PLANCHA" + sub: "Everything made to order. Tortillas pressed while you wait. Heat marked honestly."
Items (with heat 0–3 chilis):
1. Bul-Go-Gee Taco — "3 weeks marinated short rib, sesame-slaw, kimchi pico, lime crema." $4.50 — [FAN FAVORITE]
2. Gochujang Chicken Taco — "Double-fried thigh brushed w/ sweet-fire gochujang, pickled daikon, crushed nori." $4.75 — heat 2
3. El Loco Burrito — "Bulgogi + day-old fried rice + stretchy queso fresco + crispy nori shards. Wrapped like a hug." $11 — [THE BIG ONE]
4. Kimchi Quesadilla — "Aged cheddar, charred kimchi toasted 'til the edges shatter. Gochujang sour cream to dip." $8 — [VEGGIE]
5. Seoul Street Fries — "Fries buried under bulgogi bits, mozz sauce, doenjang mayo, scallion snow." $7.50
6. Buldak "Crybaby" Wings — "6 wings, soy-garlic or buldak fire glaze. Comes with a look of genuine concern." $9 — heat 3 — [SIGNATURE]
Drinks ticker: "YUJA-ADE · WATERMELON SOJU SLUSHY · MEXICAN COKE · HORCHATA MAKGEOLLI FLOAT · CUCUMBER LIMEADE ·"
Truck section: "WHERE'S THE TRUCK?" — schedule + live line: "Right now: ...". Note: "We post the exact pin on Instagram at 10AM daily. If it's raining, we're still there. If we're not there, check the bar across the street."
Hype/testimonials (red bg? or yellow):
- "★★★★★ 'I planned to try one taco. That was four months ago. Send help.'" — Dana R., regular since day one
- "★★★★★ 'The short rib taco is the single best $4.50 in this city.'" — Fork & Spoon Magazine
- "★★★★★ 'Buldak wings made my father cry. He asked for the recipe. We became friends with the chefs.'" — Marcus T.
Press badges: "BEST LATE-NIGHT EATS 2024 — City Weekly" · "TOP 10 FOOD TRUCKS — Fork & Spoon" · "PEOPLE'S CHOICE — Grand Central Food Park".
Story block: "A taquero from Oaxaca. A cook from Seoul. One shared fridge at 2AM and a fight over hot sauce that turned into a truck." + stats counters.
Footer: big "SEE YOU AT THE WINDOW." + hours + socials + newsletter + tiny print "Kimchi Loco LLC · tacos@kimchilocotruck.com · (555) 862-LOCO".
That's compelling copy. Now write the code.
Let me carefully build the taco SVG. ViewBox 0 0 340 300. Center around (170, ~170).
Plan shapes:
- Fillings (drawn first, behind shell top):
- Kimchi chunks: red-orange rounded paths around y 130-165, x 90-250.
- Bulgogi: brown wavy strips.
- Cabbage shreds: green wavy paths sticking up, tips around y=95-120.
- Purple cabbage strips: purple.
- Sesame: tiny white ellipses.
- Lime wedge: small circle (green wedge with white lines) at left side, x≈80,y≈130.
- Shell: big crescent: `M35 160 Q170 265 305 160 Q170 205 35 160 Z` — hmm let me think. A taco shell from the side: left tip (35,150), right tip (305,150). Bottom edge sags: `M35 150 C 60 235, 280 235, 305 150` — bulges to ~y 218. Top edge (the opening where fillings sit): slight downward curve `C 280 175, 60 175, 35 150` wait direction: after bottom curve reaching (305,150), curve back to start: `C 270 168, 70 168, 35 150 Z`. So shell is a crescent between a deep sag and a shallow sag — fill #F2B04B, stroke ink 5. Add inner highlight line along the top edge (a lighter path #FBD07E). Also toasted spots (small darker ellipses #D9862E with low opacity? use fill #C77F2F).
Shell opening: fillings should sit inside between the two curves, around y 150-200?? Hmm — if the shell top edge dips to ~168 in middle and fillings are above the shell (y < 168) but visually inside the opening... Actually classic taco drawing: fillings poke ABOVE the shell's top rim, spilling over. So fillings occupy y 90–175 across x 80–260; shell drawn after (on top of) the fillings' lower parts, so fillings appear to be inside. Good: draw fillings first (they extend down to y~185), then shell path on top covers y 150–218 region partially, leaving filling tops visible. But my shell's top edge dips only to 168 mid — fillings visible above that.
Then the salsa blob: a red blob ON the fillings (like sauce drizzle) — its fill gets JS-interpolated by the heat slider. Also a small salsa cup next to the taco? Simpler: a big drizzle blob among the fillings + a dip cup at bottom right? Let me include a "salsa cup" in front: small cup at (285, 205)? Actually the taco occupies most; place cup bottom-left (60,210)? The shell bottom is y~218 — cup in front could overlap shell — fine, draw after shell: cup at x 45-105, y 195-230: trapezoid cup (ink outline, cream fill) with salsa top ellipse whose fill is the interpolated color. Plus a little spoon? Skip spoon.
Also heat-driven flame: behind taco bottom, small flames (SVG paths) with opacity scaled by heat — flames in the sunburst behind the taco at bottom edge. Or a flame ring around the badge — could get busy. I'll add 3 small flames behind the taco base, opacity = (heat-60)/40, with flicker animation.
Let me define the salsa gradient fill via two stops updated by JS? Simpler: one solid fill color applied to `.salsa` class elements (SVG supports CSS fill on paths). Use CSS var: `.salsa { fill: var(--salsa); }` and JS sets `--salsa` on the hero element.
Also `--salsa-glow` for a drop-shadow filter on the taco svg scaled with heat: `filter: drop-shadow(0 0 calc(var(--glow)px) ...)` — drop-shadow with CSS var works. Nice: taco gets a hot glow at high heat.
Steam: container positioned above taco, particles as divs with `background: var(--ink)`? Steam as soft gray puffs — use border-radius 50%, rgba(33,19,14,.15)? On cream bg, use white puffs with blur? Simple: circles with background `#fff` opacity 0.9, blur(2px)... but bg is sunburst — white puffs read as steam. OK.
Heat label mapping (text + sub):
- 0–24: "SO GENTLE" / "Soy-garlic vibes. Abuela-approved."
- 25–49: "SEOUL SIZZLE" / "A polite kick. You'll be fine. Probably."
- 50–74: "GOCHUJANG GANG" / "The sweet-fire paste does its thing. Napkins ready."
- 75–94: "BULDAK BRAVE" / "Sweating at the window is normal here."
- 95–100: "EL LOCO" / "Sign the flavor waiver at the counter. No refunds on pride."
Slider styling: custom track (cream with ink border) and thumb (red circle with ink border, hard shadow) via `-webkit-slider-thumb` and `-moz-range-thumb`.
Schedule data (JS):
```
const schedule = [
{d:1, day:'Mon', start:11, end:21, spot:'Ridgeline Brewing', note:'Beers + bulgogi = research'},
{d:2, day:'Tue', start:11, end:21, spot:'Ridgeline Brewing', ...},
{d:3, day:'Wed', start:17, end:23, spot:'Grand Central Food Park'},
{d:4, day:'Thu', start:17, end:24, spot:'Grand Central Food Park'},
{d:5, day:'Fri', start:12, end:26, spot:'Night Market · 8th & Main', note:'We stay 'til 2AM'},
{d:6, day:'Sat', start:12, end:26, spot:'Night Market · 8th & Main'},
{d:0, day:'Sun', start:11, end:18, spot:'Fowler St Farmers Market'},
];
```
Status function:
```
function getStatus(now=new Date()){
const dow = now.getDay();
const t = now.getHours() + now.getMinutes()/60;
const today = schedule.find(s=>s.d===dow);
if(today && t>=today.start && t<today.end) return {open:true, spot:today.spot, until: fmtHour(today.end)};
// find next opening
for(let k=0;k<8;k++){
const d=(dow+k)%7; const s=schedule.find(x=>x.d===d);
if(!s) continue;
if(k===0 && t<s.start) return {open:false, next: `${s.day} at ${fmtHour(s.start)}`, spot:s.spot};
if(k>0) return {open:false, next: `${s.day} at ${fmtHour(s.start)}`, spot:s.spot};
}
}
```
Wait if k===0 and t>=s.end, continue to next k. The `if(k===0 && t<s.start)` covers same-day upcoming; else k>0 returns future day. But careful: k===0 && t>=s.start && t<s.end already returned. Good. fmtHour: h>12? `${h-12}PM` etc; end 26 → 26-12=14 → `2AM` — fmtHour(26) should give "2AM": if h>=24 h-=24; then h>12 ? `${h-12}PM` : `${h}AM`; h===12 → '12PM'? 12PM noon ok; h===24→0 → '12AM'. Good.
Status sentence examples:
- Open: `OPEN NOW · slinging at ${spot} 'til ${until}`
- Closed: `CLOSED · next up: ${spot}, ${next}` → phrase: "The plancha fires back up ${next} at ${spot}".
Pill classes: `.open` green dot pulse / `.closed` red dot.
**Cart:**
```
let cart = {}; // id -> qty
function addItem(id){ cart[id]=(cart[id]||0)+1; renderCart(); bounce badge; toast(`${name} added`) }
```
Panel: fixed right, translateX(100%) → 0 with `.open`. Overlay dim. Items list with steppers (+/−) and remove. Subtotal. "Fire up my order" → panel content swaps to ticket confirmation: order number random 3 digits, eta "7–9 min", spot = current status spot or "the window", message: "Show this at the window. Yell 'LOCO' for 10% off. (Real thing. We started it as a joke. It stuck.)" + button "Back to menu" resets panel to list state. Ticket styled like receipt paper (cream bg, dashed border, monospace font: `ui-monospace, "Courier New"`).
**Toast:** bottom-center, slides up, auto-dismiss 2.4s, stacking (container column, gap).
**Reveal:** `.reveal` opacity 0 translateY(24px) → in: transition. IntersectionObserver threshold .12. Add to section headers, cards (staggered via `transition-delay` computed from index? use CSS `:nth-child` small delays or JS set style transitionDelay = i*60ms).
**Counters:** elements `[data-count]` animate from 0 to value over ~1.2s with rAF easing outCubic, format with commas / suffix from data attrs (e.g., "128,500", "4.9", "2AM" no). Only counters: tacos "128,400", reviews "2,140", days "312". Rating maybe static.
Header on scroll: add shadow/border after scrollY>10 — minor.
Let me also add a subtle interactive touch: hero taco tilts toward mouse (rotate ~±4deg, translate few px) — parallax on pointermove over hero. Cheap and delightful. Guard: only fine pointer.
**Now CSS architecture:** custom properties, sections. Let me estimate sizes to keep the file manageable but complete. I'll write ~700-900 lines total. Let me draft mentally then write final.
Colors final:
```
--ink:#211611;
--cream:#FFF6E9;
--cream2:#FBEED6;
--red:#E23B2E;
--red2:#B01F14;
--lime:#9BC53B?
```
Hmm lime accent: chips like "VEGGIE" in lime `#A8C836`? text ink. Green for open status `#3E8E4A`. Yellow `#FFC53D` for sunburst/accents. Purple small `#8C4A9E` cabbage. Tortilla `#F5B849`.
Font stack display: `"Arial Black","Helvetica Neue",Helvetica,Arial,sans-serif` — define `--display`. Body `--body: "Helvetica Neue", Helvetica, Arial, sans-serif`.
I must double check no `alert/confirm/prompt`, no storage — yes.
Edge: viewport meta required. `overflow-x:hidden` on html,body to be safe? Sometimes sticky header + overflow hidden issues — overflow-x: clip is safer on body only; sticky works if overflow on body? Sticky fails if an ancestor has overflow hidden... Actually sticky within body works even with `overflow-x: hidden` on html/body? Known issue: `overflow-x: hidden` on body can break `position: sticky` (because it creates a scroll container on body? In Chrome, overflow-x:hidden on body → body becomes scroll container? Historically, `overflow-x:hidden` on html or body makes sticky still work if set on html? Common guidance: use `overflow-x: clip` to keep sticky working, or put it on both). Modern Chrome supports `overflow-x: clip` — use `html,body{overflow-x:clip}` with fallback `overflow-x:hidden`. Order: `overflow-x:hidden;` then `overflow-x:clip;`? Later wins where supported. Sticky is inside body which scrolls the viewport... Actually sticky is fine when body has overflow-x hidden as long as html also scrolls? To be safe I'll use clip with hidden fallback and test logic: header is `position: fixed` instead of sticky — fixed avoids the issue entirely! Use fixed header with a spacer (padding-top on body). Simpler and robust. Yes: `header { position: fixed; top:0; left:0; right:0 }`, body padding-top: 76px + ticker height... I'll make ticker fixed too? If ticker is in normal flow above header, header fixed overlaps. Option: wrap ticker+header both in fixed container? Ticker at very top fixed, header below it fixed → content padding-top = ticker(36) + header(64) = 100px. Or simpler: ticker is part of the fixed stack: `<div class="topbar">` containing ticker + header rows. Let me do one fixed `.topbar` containing both. Height ~ 34+58 = 92px, padding-top: 100px on body. On small screens ticker text smaller.
Hmm, ticker moving at top fixed — nice.
Sections need `scroll-margin-top: 110px` for anchor links.
**Menu card design:** ink section bg (#211611), cards cream with 3px ink border + hard shadow in red (`box-shadow: 8px 8px 0 var(--red)`)? On dark bg, hard shadow red pops. Card content: top row: icon badge (44px circle cream w/ ink border, mini svg) + name + price chip. Desc. Bottom: heat chilis (small svg chili repeated with unlit ones gray) + Add button (red bg cream text, hard shadow ink). Tag sticker rotated on corner ("FAN FAVORITE" red bg white text, rotate -4deg, position absolute top -10 right 14). Hover: translate(-3px,-3px) + shadow grows to 11px; icon wiggles.
Heat chilis: inline SVG symbol reused via `<use>`. Define `<svg style="display:none"><symbol id="i-chili">...` — symbols with `<use href="#i-chili">`. Chili shape: small curved pepper: path `M7 2 C 3 4 3 9 6 11 C 9 13 13 11 14 8`? Let me just draw simple: `<path d="M10.5 1.5 C10 3 8.5 3.5 7 4.5 C3.5 7 4.5 12.5 9 13.5 C13.5 14.5 16.5 11 15.5 6.5 C15 4.5 13 3 12.5 1.5" ...>` — I'll wing a decent chili: body teardrop curved + stem. Use stroke ink fill red. Fine at 14×14.
Icons needed as symbols: chili (heat), star, pin, clock, taco-mini, burrito, fries, wings(drumstick), quesadilla(wedge), bowl, cart(bag), plus, minus, arrow-right, instagram, x(close). I'll hand-draw each simply with 2-3 paths. Keep strokes consistent (stroke-width 1.8, round caps).
Dish icons (24×24 stroke style):
- taco-mini: `M4 13 C4 18 20 18 20 13` bottom arc + shell line `M4 13 C7 15 17 15 20 13`? plus fillings bumps: small circles above. Simplify: path bottom `M4 12 A8 8 0 0 0 20 12` (half circle down), top line `M4 12 Q12 15 20 12`? and 3 dots. ok.
- burrito: rotated rounded rect with diagonal wrap lines.
- fries: box trapezoid + 3 vertical sticks poking.
- wings: drumstick: circle + tapered rect + 2 small circles bone.
- quesadilla: wedge: triangle with rounded tip + dots.
- bowl: half-circle bowl + steam curves.
All stroke-based, `fill:none stroke:currentColor`. These will look hand-drawn enough.
**Amount of JS details** — write carefully to avoid errors. Let me write the file now.
Also remember: NO text outside code block. Exactly one fenced ```html block.
Let me write out the full code:
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kimchi Loco — Korean Tacos, Big Trouble</title>
<style>
:root{
--ink:#231610;
--cream:#FFF6E9;
--cream-2:#F7E9CF;
--red:#E23B2E;
--red-deep:#B01F14;
--yellow:#FFC53D;
--green:#3E8E4A;
--lime:#9BC53B;
--tortilla:#F5B849;
--purple:#7B4A9E;
--shadow: 8px 8px 0 var(--ink);
--display:"Arial Black","Helvetica Neue",Helvetica,Arial,sans-serif;
--body:"Helvetica Neue",Helvetica,Arial,"Segoe UI",sans-serif;
}
*{margin:0;padding:0;box-sizing:border-box}
html{scroll-behavior:smooth}
html,body{overflow-x:hidden;overflow-x:clip}
body{background:var(--cream);color:var(--ink);font-family:var(--body);line-height:1.5;padding-top:96px;-webkit-font-smoothing:antialiased}
...
```
Wait: `overflow-x: clip` on html,body — clip doesn't create scroll container so sticky ok, but I'm using fixed header anyway. Also programmatic scroll fine.
Let me write everything section by section.
**Topbar** (fixed):
```html
<div class="topbar">
<div class="ticker" aria-hidden="true"><div class="ticker-track" id="tickerTrack">…</div></div>
<header>
<a class="brand" href="#top">logo svg + KIMCHI LOCO</a>
<nav>… links …</nav>
<div class="header-actions"><div class="status-pill" id="statusPillTop"></div><button class="cart-btn" id="cartBtn">…</button></div>
</header>
</div>
```
Ticker content duplicated via JS (clone innerHTML ×2) to make seamless loop; CSS animation translateX(-50%). Actually standard: track contains 2 identical spans, animate track from 0 to -50%. I'll hardcode content 2× in HTML or JS-duplicate. JS-duplicate is cleaner: `track.innerHTML += track.innerHTML;` then CSS keyframes translateX(-50%). But content width may be less than viewport — need at least 2× viewport width. I'll put the phrase repeated 4 times per half → safe. Let me create ticker items in JS from array and repeat enough times.
Header layout: flex, brand left, nav center, actions right. Height 56px.
**Hero:**
```html
<section class="hero" id="top">
<div class="hero-inner">
<div class="hero-copy">
<div class="eyebrow"><span class="flag">KOR</span> × <span class="flag">MEX</span> ...</div>
<h1>GET<br><span class="outline">LOCO.</span></h1>
<p class="lede">…</p>
<div class="hero-ctas"><a class="btn btn-red" href="#truck">Find today's truck</a><a class="btn btn-ghost" href="#menu">Peep the menu</a></div>
<div class="hero-stats">…counters…</div>
</div>
<div class="hero-art">
<div class="sunburst"></div>
<div class="taco-wrap" id="tacoWrap">
<svg id="tacoSvg">…</svg>
<div class="steam" id="steam"></div>
<svg class="flames">…</svg>
</div>
<div class="heat-card">
<div class="heat-head"><label for="heatSlider">How loco do you want it?</label><span class="heat-read" id="heatRead">SEOUL SIZZLE</span></div>
<input type="range" id="heatSlider" min="0" max="100" value="30">
<p class="heat-sub" id="heatSub">…</p>
</div>
</div>
</div>
</section>
```
Hmm, heat card inside hero-art below taco. Or place heat slider as its own strip under hero grid full width. I'll put it under the taco (right column) — it relates to the taco visually. On mobile it stacks fine.
Layout: hero grid `1.05fr .95fr`, gap 40, min-height ~ calc(100vh - 96px)? Set padding: 56px 0 64px. The sunburst behind taco: 420px circle. Hero bg cream; add faint dot pattern via radial-gradient background-image (dots): `background-image: radial-gradient(var(--cream-2) 2px, transparent 2.5px); background-size: 26px 26px;`? subtle texture on hero. Actually cream-2 dots on cream — subtle. Good.
Huge headline: clamp(3.4rem, 8vw, 7rem), font display, line-height .92, letter-spacing -.02em, uppercase. "LOCO." in red with hard text-shadow? Outline style: `-webkit-text-stroke: 3px var(--ink); color: var(--cream);`? Hmm. Let me do "GET" solid ink, "LOCO." red with ink hard shadow (`text-shadow: 6px 6px 0 var(--ink)`; color red). Nice.
Hero copy width limited. Eyebrow: small chips "SEOUL BBQ" red chip, "×", "MEXICO CITY STREET" yellow chip.
**Marquee divider** between hero and menu: red bg strip rotated? A slightly rotated (-1.5deg) red bar with scrolling text "KIMCHI FRIES · GOCHUJANG WINGS · BULGOGI TACOS ·" — rotation adds energy. With `overflow:hidden` on parent to avoid horizontal scroll from rotation... rotation of full-width bar causes corners to stick out horizontally → with body overflow-x clip it's fine. Second marquee moving opposite direction with different content? Keep one: rotated red ticker "TODAY'S HYPE: …". Also ticker at very top already scrolls; the divider one uses ink bg cream text maybe. Fine.
**Menu section (ink bg):**
Header: big "HOT OFF THE PLANCHA" cream text + red underline squiggle SVG + sub. Grid cards. After grid: drinks strip (cream-2 rounded box): "TO DRINK → Yuja-ade · Watermelon soju slushy · Mexican Coke · Horchata makgeolli float · Cucumber limeade — $4–7".
**Truck section (cream):** two-col: left: heading "WHERE'S THE TRUCK?" + status card (big, live sentence + pulse) + the road with truck animation + note. Right: schedule list (7 rows). Today highlighted (red border + "TODAY" chip). Under: "raining? we're still here" note.
**Hype (red bg):** "THE PEOPLE HAVE SPOKEN" + 3 quote cards (cream bg) + press badges row (outlined chips).
**Story strip (yellow bg?):** Hmm — maybe fold story into footer area. A compact strip: yellow bg with two founder "avatars"? Avatars as SVG circles with faces? Could be cute: two circles, one with a mustache, one with glasses... Getting heavy. Alternative: simple text strip on yellow: quote from founders. I'll do a yellow strip with the origin story (short) + counters there? Counters already in hero. Keep yellow strip short: "EST. WHEN A TAQUERO FROM OAXACA MET A COOK FROM BUSAN IN A SHARED KITCHEN..." Keep.
**Footer (ink):** giant "SEE YOU AT THE WINDOW." + newsletter form + columns of links (hours summary, contact) + tiny print.
**Cart panel + overlay + toasts.**
Now JS pieces:
```js
const $=s=>document.querySelector(s), $$=s=>document.querySelectorAll(s);
// MENU DATA
const menu=[
{id:'bulgogi',name:'Bul-Go-Gee Taco',desc:'3-week marinated short rib, sesame slaw, kimchi pico, lime crema.',price:4.5,heat:1,tag:'FAN FAVORITE',icon:'taco'},
{id:'gochujang',name:'Gochujang Chicken Taco',desc:'Double-fried thigh, sweet-fire gochujang, pickled daikon, crushed nori.',price:4.75,heat:2,icon:'taco'},
{id:'loco-burrito',name:'El Loco Burrito',desc:'Bulgogi + fried rice + stretchy queso fresco + crispy nori shards.',price:11,heat:2,tag:'THE BIG ONE',icon:'burrito'},
{id:'queso',name:'Kimchi Quesadilla',desc:'Aged cheddar + charred kimchi, toasted till edges shatter. Gochujang sour cream dip.',price:8,heat:1,tag:'VEGGIE',icon:'queso'},
{id:'fries',name:'Seoul Street Fries',desc:'Fries buried in bulgogi bits, mozz sauce, doenjang mayo, scallion snow.',price:7.5,heat:1,icon:'fries'},
{id:'wings',name:'Buldak "Crybaby" Wings',desc:'Six wings. Soy-garlic or buldak fire. Comes with a look of genuine concern.',price:9,heat:3,tag:'SIGNATURE',icon:'wings'},
];
```
Render into grid. Heat chili count: heat 0 → "no heat". Render chilis: 3 slots, lit = item.heat.
fmt price: `$${p.toFixed(2)}`.
**Status:** as planned. Update pill text + hero line + truck card. setInterval 30s.
Sentence strings:
- open: `We're SLINGING RIGHT NOW at ${spot} — griddle hot 'til ${until}.`
- closed: `Griddle's off. Next stop: ${spot}, ${next}.` plus emoji no.
Top pill: `OPEN NOW` or `CLOSED` with dot.
**Heat slider:**
```js
const stops=[[122,179,66],[255,197,61],[247,127,31],[226,59,46],[168,18,24]]; // green→yellow→orange→red→deep
function salsaColor(v){ // v 0..100
const t=v/100*(stops.length-1), i=Math.min(Math.floor(t),stops.length-2), f=t-i;
const c=stops[i].map((x,k)=>Math.round(x+(stops[i+1][k]-x)*f));
return `rgb(${c.join(',')})`;
}
```
Apply: `hero.style.setProperty('--salsa', color)`; glow: `--glow: (0.15+v/100*0.5)px`? drop-shadow blur can't be calc var? It can: `filter: drop-shadow(0 0 var(--glow) rgba(...))` with var as length works if var is e.g. `18px` string. Set `--glow: ${(6+v*0.28)}px`. Flames opacity: set style on flame svg `--flame-o: clamp((v-55)/45,0,1)` and CSS `opacity:var(--flame-o)`.
Heat label thresholds as defined. Also update slider accent? Track fill: use background gradient on input: `linear-gradient(90deg, var(--lime), var(--yellow), var(--red) ...)` static fine. Or dynamic via `--fill: v%` background: `linear-gradient(to right, var(--red) var(--fill), var(--cream-2) var(--fill))`. Set `--fill`.
Also the hero headline "LOCO" could scale-shadow? skip.
Loco mode (v>=95): add class `loco` on hero → sunburst spins faster, taco wiggle animation, background pulse. Remove when lower.
**Steam:** container `#steam` absolutely positioned over taco top area (width 140px centered, height 90px, pointer-events none). Spawn loop: every 260ms if `steamOn`, create div.puff at random left 10–90%, size 10–20px, animate via CSS keyframes with random duration/delay using inline style: `p.style.animation = puff Xs ease-out forwards` — keyframes translateY(-70px) scale(2) opacity 0. Randomize via inline custom props: `--dy: -6rem`. Simple: each puff gets `style.setProperty('--dur', ...)`, keyframes use `translateY(var(--dy))`. Set --dy random -60px..-110px. Remove after animationend. Intensity: interval count based on heat: base 500ms, at heat>70 220ms. Just compute interval via setTimeout chain.
Reduced motion: skip steam + auto-sweep.
**Taco tilt:** pointermove on `.hero-art` → rotate tacoWrap `rotate(${dx*3}deg)` translate small; pointerleave resets. Use transform with transition .3s. Combine with loco wiggle? If loco class adds animation wiggle to tacoWrap, inline transform overrides animation? Animation wins over inline style (animations override inline non-important? CSS animations override inline styles). It'd fight; wiggle only on `.taco-wrap` while tilt uses same property → conflict acceptable? I'll put loco wiggle on inner svg element instead: `#tacoSvg` gets animation, tilt on wrapper. Good separation.
**Auto-sweep:**
```js
let userTouched=false;
slider.addEventListener('pointerdown',()=>userTouched=true);
setTimeout(()=>{ if(!userTouched && !reduced) tweenHeat(slider.value, 62, 2200); },900);
function tweenHeat(from,to,dur){ const t0=performance.now(); (function f(t){ const p=Math.min(1,(t-t0)/dur), e=1-Math.pow(1-p,3); const v=from+(to-from)*e; slider.value=v; applyHeat(v); if(p<1&&!userTouched) requestAnimationFrame(f); })(performance.now()); }
```
Also add keydown input on slider: userTouched on 'input' event too.
**Cart:**
```js
const cart={};
function addToCart(id){...}
renderCartPanel(): builds rows; total; empty state text "Nothing yet. The tacos feel ignored."
```
Panel markup prebuilt in HTML with `<div id="cartPanel">` containing header (title "Your order at the window", close btn), `<div id="cartItems">`, `<div class="cart-foot">` total + fire btn. On confirm: `showTicket()`.
Ticket:
```
ORDER #<span>
3 items · $24.25
READY IN 7–9 MIN AT <spot>
Show this at the window. Yell "LOCO!" for 10% off.
```
Buttons: "Start another round" (clears cart, closes panel).
Badge bounce: add class 'pop' then remove on animationend.
Toast: `showToast(html)`.
**Reveal observer:** all `.reveal` get in-class.
**Counters:** in hero-stats: `<b data-count="128400">0</b> tacos slung` etc. Animate on load (hero visible immediately) — just run on DOMContentLoaded with rAF 1.4s. 4.9 rating: `data-count="4.9" data-dec="1"`. reviews 2140. Since screenshot at 3s, counters mid/end — fine, they'll be done ~1.5s.
**Schedule render:** rows with day, hours, spot; today gets class. Clicking a day? no need.
**Truck SVG animation:** `.road-truck { animation: drive 8s cubic-bezier(.4,0,.3,1) infinite; }` keyframes: from translateX(-40%) to translateX(calc(100% + ...))? Using percent of own width relative to road container... Simpler: truck element positioned absolute, animate `left: -260px` → `calc(100% + 260px)`? Keyframes with left are jankier but fine for demo. Better: `@keyframes drive { from{ transform: translateX(-100%);} 80%{ transform: translateX(calc(60vw));} ... }` Hmm width varies. Use JS? Nah: road has fixed height ~ 120px; truck svg width 300px; animate `margin-left` from -320px to 100% via transform translateX with `--` no...
Simplest robust: keyframes on transform: `0% { transform: translateX(-340px) } 100% { transform: translateX(calc(100vw))}` — inside a container of known width, translateX(100%) refers to element's own width (300px), not container. So `to { transform: translateX(100vw) }` — but the road container is < 100vw on desktop (max-width 1100px centered). translateX(100vw) from a truck inside centered container overshoots — fine, it just drives past beyond right edge (hidden by road overflow hidden). Overshoot beyond viewport is hidden. So: from -340px to 100vw covers any container width ≤ viewport. But bounce/pause: add a "stop" in middle: keyframes 0% -340px; 45% translateX(38vw)?? mid position varies with layout... skip the pause, just continuous drive with slight bob (nested element bob animation translateY 0→-4px). And wheels spin. Also entrance delayed. Add second layer: truck also flips? no.
Actually nicer: keep truck parked center by default (static, always visible for screenshot), and ALSO have a tiny background car? No — screenshot needs the truck visible! If truck is mid-drive at 3s it might be off-screen when screenshot happens! Critical: at t≈3s truck must be visible. Animation 8s starting at load: at 3s it's at 37.5% across — visible.
But safer: make the truck sit in the section, gently bouncing (suspension), wheels idle, with a periodic "rev" — I'll keep drive animation but ensure it's within view: drive keyframes with a long hold: 0-30%: enters to ~30% position; 30-70%: stays (bob continues); 70-100%: exits. At 3s (t=37.5%) → position ≈ 30% mark + bobbing. Visible.
```css
@keyframes drive{
0%{transform:translateX(-360px)}
30%{transform:translateX(min(24%, 200px))}?
```
Hmm translateX % is own width. Let me define stops in vw but small container... translateX(10vw) at 1440 viewport = 144px — depends on viewport. Use container width via `100%` = truck width (320). Positions: enter from -1.2× width to 0.25× container?? I'll use a wrapper approach: `.truck-lane` full width overflow hidden; truck `left:0; transform: translateX(...)`. Keyframes using `calc()` of `100%` (own 320px): from translateX(-360px) to translateX( calc( 100% + 40px ) )? That only moves it 360+320+40 = ~720px — if lane is 1100px wide it never crosses fully. Combine: `to { transform: translateX(100vw) }` — from truck's left position at 0 within lane; lane spans (screenW-2pad)/… the truck ends at 100vw from lane-left-0 which may exceed lane width → overshoot hidden by overflow hidden. And mid-hold: `45% { transform: translateX(30vw) }`? On 375px phone, lane ≈ 343px, 30vw=112px — truck(320 wide) fits mostly? truck width 320 on 343 lane — huge relative but ok scaled: I'll set truck svg width: clamp(240px, 30vw, 320px). translateX(30vw) at phone = 112px → truck spans 112..432 → clipped right, partially visible. Meh. Alternative simpler & safe: don't animate driving across; instead: truck parked, with continuous idle bounce + wheels slow spin + occasional horn? At 3s fully visible guaranteed. And add motion via "steam/smoke" from truck chimney (animated puffs) — alive but stable. I'll go parked: cleaner. Add road dashes moving (animated background-position) to imply motion? Parked truck + moving road dashes = truck driving? Contradiction — dashes moving = truck stationary while road moves implies driving illusion. Actually keep road static, truck static with bob + chimney smoke + awning sway. Fine.
Actually simplest crowd-pleaser: keep drive animation but contained: lane overflow hidden, keyframes `0%{translateX(-360px)} 35%{translateX(15%)} 65%{translateX(15%)} 100%{translateX(360px)}` where translateX(15%) = 48px (15% of 320) → truck sits at 48px..368px inside lane (lane width ≥ 343 phone): 368 > 343 slightly clipped on phone — acceptable? On phone it'd be slightly clipped. Use translateX(0%) at hold instead: truck at 0..320 within lane — fits everywhere. Enter from -360px, hold at 0, exit to 360px? Exit to +360px means truck spans 360..680 — exits right side only if lane width ≤ 360... at desktop lane 1100: truck at 360..680 still fully visible, then jumps back — visible jump-cut. Ugh.
Final: parked truck, idle animations. Add a second tiny detail: a hanging "OPEN" sign swinging. Done deciding. Parked + bob + smoke + swinging sign. Also wheels: no spin. Good.
Hmm, but "driving across" was charming... I'll add a separate micro-interaction: clicking the truck honks (visual "BEEP BEEP" burst text) + jumps. Cute. `truck.addEventListener('click', ...)` spawn absolutely positioned "BEEP BEEP!" sticker that pops. OK.
**Sunburst:**
```css
.sunburst{position:absolute; width:min(420px,80vw); aspect-ratio:1; border-radius:50%; background: repeating-conic-gradient(var(--yellow) 0deg 18deg, var(--tortilla) 18deg 36deg); border:4px solid var(--ink); box-shadow: 10px 10px 0 var(--red)? ; animation: spin 40s linear infinite; opacity:.9}
```
Colors yellow & tortilla — subtle stripes. Behind taco (z-index). Add inner cream circle? Taco sits over center. Add a red ring? Keep.
spin keyframes rotate 360.
**Section divider waves:** use SVG or `border-radius` bumps: `.wave{height:14px; background: radial-gradient(circle at 10px -6px, transparent 12px, var(--ink) 13px)...` classic scallop trick. Simpler: skip fancy dividers; use straight edges + the rotated marquee bar as divider. The rotated bar overlapping sections adds energy: `.divider{transform: rotate(-2deg); margin: -20px 0; z-index: 2; position: relative}` etc. Keep one rotated marquee between hero and menu (red bg, cream text) and one between hype and footer maybe. Fine.
**Now writing actual SVG paths — most delicate part.**
Taco SVG viewBox="0 0 340 300":
Fillings group (before shell):
- Purple cabbage: paths like `M120 100 q8 -30 3 -48` stroke 7 purple round — several at x 115-150.
- Green cabbage: same in green at x 160-200.
Let me define a helper shape: a wavy strip = path `M x y c ...` Let me just write strips as thick strokes:
```
<g stroke-linecap="round" fill="none">
<path d="M110 118 c-4 -14 -2 -26 4 -34" stroke="#7B4A9E" stroke-width="9"/>
<path d="M126 116 c6 -16 4 -30 -3 -38" stroke="#8f5ab5"? ...
```
Keep two purples (#7B4A9E, #9A6BC4), two greens (#4E9F3D, #77C159), strokes width 8-10, rising from y≈118 to y≈78.
- Kimchi chunks: fill #E23B2E rounded rects rotated: `<rect x=".." y=".." width="16" height="10" rx="4" fill="#E23B2E" stroke="#231610" stroke-width="3" transform="rotate(-8 ...)"/>` a few.
- Bulgogi: fill #8E5030 blobs: `<path d="M96 138 q10 -12 26 -6 q14 4 20 -4 q12 12 -6 18 q-24 8 -40 -8z">`-ish strips at y 128-150 with charred edges stroke ink.
- Salsa blob (class salsa): big drizzle across fillings: `<path class="salsa" d="M92 132 c30 14 60 18 78 8 c22 -12 48 -10 62 2 c-14 18 -52 24 -84 16 c-30 -7 -48 -14 -56 -26z" stroke="#231610" stroke-width="4"/>`.
- Sesame: 4 small white ellipses with ink stroke on top of fillings.
- Lime wedge at right: `<g transform="translate(246 118)"><circle r="16" fill="#B9E07A"? ...` lime: circle fill #C9E59B stroke ink + 3 lines wedge + white rind? Simple: green circle w/ lighter spokes.
Shell path (after fillings):
```
<path d="M34 148 C64 236 276 236 306 148 C288 186 230 205 170 205 C110 205 52 186 34 148 Z" fill="#F5B849" stroke="#231610" stroke-width="5"/>
```
Check: outer bottom curve from (34,148) control (64,236),(276,236) to (306,148): sags nicely to y≈210. Inner return curve from (306,148) with controls (288,186)? Wait inner edge from (306,148) to (34,148): I wrote two mid points... Let me use one cubic: `C 282 178, 58 178, 34 148` dips to ~y 173 mid. So shell = band between y~210 bottom and ~168 mid top edge at x=170: thickness ~40 at center, tapering to 0 at tips. Good taco shell.
Add toasted spots: `<ellipse cx="120" cy="190" rx="7" ry="4" fill="#C97F2F"/>` few, and a highlight along top edge: `<path d="M52 152 C 90 186 250 186 288 152" stroke="#FBD98A" stroke-width="6" fill="none" opacity=".8"/>`? Wait highlight should follow inner edge curve — approx fine.
Salsa cup (in front, left):
```
<g transform="translate(38 196)">
<path d="M4 8 L10 34 L38 34 L44 8 Z" fill="#FFF6E9" stroke="#231610" stroke-width="4"/> // trapezoid cup opening 44 wide
<ellipse cx="24" cy="8" rx="20" ry="7" fill="var(--salsa)" ...
```
Hmm var in SVG attr — use class + CSS `.salsa{fill:var(--salsa)}` where --salsa defined on :root default. Cup: salsa ellipse sits at cup top. Cute "GA" label? skip.
Flames (behind taco, at base): small flame path repeated: `<path d="M0 0 c6 -2 8 -8 5 -13 c8 2 11 10 6 18 c-4 6 -13 6 -16 0 c-2 -4 0 -8 5 -5z">` — I'll wing it: flames as `<path d="M0 24 C2 14 -6 12 -4 4 C4 8 6 12 6 16 C10 10 16 10 18 16 C22 24 14 30 8 30 C2 30 -2 28 0 24 Z"` eh. Flames flicker via CSS scale animation on group. Place 3 at bottom center behind shell (z below shell? behind whole taco svg? Put flames in separate svg positioned absolute behind). I'll place flame paths inside tacoSvg before fillings, at y~225 near base, fill var(--salsa)? Flames red/orange: fill "#F77F00" with inner "#FFC53D". opacity var(--flame-o). Add keyframes flick (scaleY 1→1.08). transform-origin bottom.
Steam container: absolute top of taco-wrap: `top:-12px; left:50%? width: 160px; left: calc(50% - 80px); height: 110px`. Puffs `position:absolute; bottom:0; background:#fff; border:2px solid var(--ink)?` — outlined puffs match sticker style! `border-radius:50%; opacity:0;` keyframes puff: `from{opacity:.85; transform:translateY(0) scale(.6)} to{opacity:0; transform:translateY(var(--dy)) scale(1.5)}`. Outlined white circles rising = comic steam.
Truck SVG (viewBox 0 0 360 160):
- Ground shadow: ellipse cx 180 cy 150 rx 150 ry 10 fill rgba ink .15.
- Body: main box `x=30 y=36 w=250 h=76 rx=10` fill cream stroke ink 4. Service window: `x=90 y=52 w=90 h=40 rx=6` fill ink (dark window) with border cream? Window shows menu board inside: small lines. Awning above window: scallops: path with 4 bumps alternating red/lime: e.g. `<path d="M88 48 q11 12 22 0 ..." >` each bump as separate rect with rounded bottom? Simpler: 4 small rects `x` width 22 height 10 with rx bottom — use `<path>` per stripe: I'll draw awning as group of rects y=42 h=8 fill alternating red/lime, and below scalloped edge via `border-radius` not available — use `<circle>`s? Keep flat striped bar + a zigzag line under: fine, retro enough.
- Taco sign on side: mini taco shape + text "KIMCHI LOCO" along body: `<text x="215" y="86" font-family="Arial Black" font-size="20" fill="#231610" text-anchor="middle" transform="rotate(0)">KIMCHI LOCO</text>` text width might overflow 250 box: font-size 20, 11 chars ≈ 130px ok centered x=195. Also add red stripe `rect y=96 h=14 fill red`.
- Cab: right side: `path d="M280 44 h34 q14 2 18 16 l8 30 h-60 z"`? Front slopes. Wheel arches: circles over wheels.
- Wheels: `<g class="wheel"><circle cx="86" cy="126" r="16" fill="#231610"/><circle cx="86" cy="126" r="7" fill="#FFF6E9"/><circle r="3" fill="#231610"/>` second at cx=250.
- Chimney: `rect x=70 y=20 w=10 h=18 fill ink rx=3` + smoke puffs (CSS animated circles in SVG: `<circle class="smoke">` with CSS animation translateY — SVG elements animate with CSS transforms fine).
- "OPEN" hanging sign in window: small rect swinging: `<g class="sign" transform-origin>`: rect 34×16 cream stroke ink, text OPEN red? small text size 10. animation swing rotate ±6deg.
Bob animation on truck group `translateY 0/-3`.
I'll implement `--` details inline; keep it decent not perfect.
**Chili symbol:** `<symbol id="i-chili" viewBox="0 0 24 24"><path d="M15.5 4.5c-1 2-3 2.5-4.5 4C6.5 12 7 18 12.5 19c6 1.2 9.5-4 7-9-1-2-3-3-4-5" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/><path d="M15.5 4.5c.8-1.5 2.5-2 4-1.5" stroke="currentColor" stroke-width="2.2" fill="none"/></symbol>` — a curved chili with stem. Unlit variant: same symbol, color gray via CSS class .chili.off{color:#cbbfa8}. Lit: color var(--red-deep).
Star symbol: standard star path `M12 2l2.9 6.3 6.8.8-5 4.7 1.3 6.8L12 17l-6 3.6 1.3-6.8-5-4.7 6.8-.8z` fill currentColor.
Cart icon: bag: `M6 8h12l-1 12H7L6 8zM9 8a3 3 0 0 1 6 0` stroke.
Plus: `M12 5v14M5 12h14` stroke.
Minus: `M5 12h14`.
Close: `M6 6l12 12M18 6L6 18`.
Pin: `M12 21s-7-5.5-7-11a7 7 0 1 1 14 0c0 5.5-7 11-7 11z` + circle.
Clock: circle + `M12 7v5l3 2`.
Arrow: `M4 12h14M13 6l6 6-6 6`.
Dish mini icons 24×24 stroke style:
- i-taco: `<path d="M4 12a8 8 0 0 0 16 0" ...>` wait taco opening up: bottom arc: `M4 13a8 8 0 0 0 16 0` draws lower semicircle from (4,13) to (20,13)?? arc sweep flags: `M4 13 A8 8 0 0 0 20 13` sweep 0 → which direction? Start (4,13) end (20,13), large-arc 0 sweep 0 → counterclockwise → goes below? For arc from left to right, sweep 0 = counter-clockwise = passes through bottom (y+8). Yes lower bump. Then top lip: `M4 13 Q12 16 20 13` fillings: three dots circles at (8,10),(12,9),(16,10) r1.5 fill currentColor stroke none. Looks taco-ish.
- i-burrito: `<rect x="7" y="5" width="15" height="9" rx="4.5" transform="rotate(20 14 10)"/>` hmm rotate makes bbox overflow? viewBox 24, rotate 20 of rect 7..22 → could clip. Alternative diagonal wraps: draw rounded rect axis-aligned + 2 diagonal lines: `<rect x="6" y="7" width="12" height="10" rx="5"/><path d="M9 7l3 10M14 7l1 6"/>`? eh fine: rect + slanted lines inside.
- i-fries: box: `M5 13l1.2 6h11.6L19 13z`? wait stroke path open trapezoid: `M5 13l1.5 6h11L19 13` + sticks `M9 13V7M12.5 13V6M16 13V8` with round caps + a dip blob `M10 5c2-1.5 5-1.5 6 0`? skip blob.
- i-wings (drumstick): `<path d="M13 4a6 6 0 0 1 4 10l-1.5 1.5L13 12l1-1a... ` simpler: leg: `M14 3a6.5 6.5 0 1 1-4 11.5L8 17l-2.5-.5L5 14l2.5-2.5"` plus bone circles `<circle cx="5" cy="14" r="1.4"/><circle cx="8" cy="17" r="1.4"/>`. I'll draw: meat blob circle-ish at (14,8) r5 + tapered to (8,14), bone at bottom-left with two knobs.
- i-queso: wedge: `<path d="M3 16c0-6 5-9 9-9s9 3 9 9H3z"/>`? that's half-leaf shape... quesadilla quarter: `M4 18 L18 4 q3 3 3 8 H4`? Simplify: triangle with rounded corners + dots: `<path d="M20 5c2 2 2 12-4 15-5-8-2-14 4-15z"/>`? I'll just do: slice: `M4 16 L16 5 c3 3 4 7 1 11 L4 16z`... honestly a simple "wedge with dots": `<path d="M19 4c2.5 2.5 2.5 10-3 15L4 16C10 8 14 5 19 4z"/>` + dots. OK whatever — icons just need to be charming-abstract.
- i-bowl: `M4 10h16c0 5-3 8-8 8s-8-3-8-8z` wait that's filled stroke bowl: `M4 10c0 5 3.5 8 8 8s8-3 8-8"` open path + steam `M9 7c0-2 1.5-2 1.5-4M14 7c0-2 1.5-2 1.5-4`? steam above bowl going up from y7 while bowl rim at 10: steam curls at y1-7? bowl occupies 10-18, steam 1-9. ok.
These icons are rough but stroke-consistent — good enough.
Actually simpler idea for menu cards: skip per-item icons and instead use a big number badge ("01".."06") in display font — typographic menus look classy and avoid janky icons. But little food doodles add warmth... Compromise: use the mini dish icons but drawn carefully. I'll include them; if one looks off, it still reads as hand-drawn charm. Keep both: icon in colored circle + heat chilis.
**Press badges row:** chips with border.
Let me also double-check contrast: ink on cream good. Red buttons with cream text: #E23B2E vs #FFF6E9 — contrast ~3.6:1 at large bold text ok. Body text red on cream avoid.
**Time to write the final file.** I'll aim for clean, organized CSS. Also add `::selection` styling (red bg) — fun detail.
Also `<html lang="en">`, title, meta description.
One more constraint check: "Something meaningful visible on first paint" — yes hero. "no hidden behind high score" — n/a. Keyboard: website links work; slider keyboard-usable. Add Esc closes cart. "Games must start in self-playing demo mode" — not a game, but the auto heat-sweep covers liveliness.
Newsletter form: `<form id="newsForm"><input type="email" required placeholder="taco@lover.com"><button>Get the drop</button></form>` → toast.
Footer socials: SVG icons (instagram: rounded rect + circle; tiktok skip; use "IG", "TikTok" text chips?) — use simple glyphs: instagram rect+dot, X, facebook f? I'll do IG + X (Twitter) minimal paths, as chips with labels.
Total counts of JS features: menu render, schedule render + status, heat slider + tween + steam, cart + panel + ticket + toasts, reveal, counters, ticker build, truck beep, tilt, nav shadow, form. ~400 lines JS. Let's write.
CSS: ~600 lines. Let's go. I need to be careful to write valid CSS/JS in one pass.
Details on hero stats:
```
<div class="stats">
<div><b class="count" data-count="128400">0</b><span>tacos slung since '19</span></div>
<div><b class="count" data-count="2140" >0</b><span>5-star reviews</span></div>?
```
Wait 4.9 rating: `<b data-count="4.9" data-dec="1">` shows 4.9. Counters: 128,400 tacos; 4.9★ avg; "'TIL 2AM" static chip "open late". 3 stats.
Counter format: if dec → toFixed(1), else Math.round(v).toLocaleString('en-US').
Schedule spot details:
- Mon: Ridgeline Brewing — 11a–9p
- Tue: Ridgeline Brewing — 11a–9p
- Wed: Grand Central Food Park — 5p–11p
- Thu: Grand Central Food Park — 5p–Midnight
- Fri: Night Market · 8th & Main — 12p–2a
- Sat: Night Market · 8th & Main — 12p–2a
- Sun: Fowler St Farmers Market — 11a–6p
Note under schedule: "Rain or shine. The pin drops on Instagram every morning at 10."
fmtHour(24)→'12AM' (midnight). fmtHour(26)→'2AM'. fmtHour(23)→'11PM'. fmtHour(11)→'11AM'. fmtHour(17)→'5PM'. fmtHour(12)→'12PM'. Good.
Status sentence in hero: separate element `#heroStatus`: if open: `Sliding at ${spot} right now — 'til ${until}.` else `Griddle's cooling. Next stop: ${spot}, ${next}.`
Truck section big card `#statusBig` with pulse dot + sentence + "Get directions"-ish fake chip? A button "Drop a pin (opens maps)" — no external nav... could link to google maps? External network request? A link is fine but might not work in sandbox; keep button → toast "Pin dropped! … kidding, we're a demo. But we ARE at Fowler St."
Hmm keep playful but not "demo"-breaking the fantasy. Toast: "Pin copied — see you at the window." Fine.
Now — write CSS carefully.
Key classes:
- .topbar (fixed), .ticker, .ticker-track (animation marquee 18s linear infinite), .header-inner
- .hero, .hero-inner (grid), .eyebrow, .chips, h1 styles, .lede, .btn variants, .stats
- .hero-art, .sunburst, .taco-wrap, #tacoSvg (width ~ min(430px, 78vw)), .steam, .heat-card
- .divider (rotated marquee)
- .menu (ink), .section-head, .menu-grid, .card
- .truck-section, .status-card, .road, .truck svg, .sched
- .hype (red), .quote-card, .badges
- .story (yellow strip)
- footer
- .cart-btn, .cart-panel, .overlay, .toast-stack
- reveal classes, media queries.
Buttons: `.btn{font-family:var(--display); text-transform:uppercase; border:3px solid var(--ink); padding:14px 22px; box-shadow: var(--shadow); background:var(--red); color:var(--cream); ...}` hover translate(-2,-2) shadow 10px; active translate(2,2) shadow 3px.
Menu card heat row + add btn small.
Tag sticker: absolute top -12, right 10, rotate(-3deg), bg yellow or red, border ink 3px, shadow 4px.
OK writing now. Also remember body padding-top for topbar ~ 92px.
Ticker: I'll build content in JS: phrases array joined with separator spans "★"? no emoji — star SVG inline small or just "•". Use "◆"? A plain "★" char is not emoji (U+2605 text glyph) — acceptable but rule "no emoji"? ★ is a dingbat, renders as text glyph not emoji in Chrome... U+2605 is not in emoji presentation set (✦? Actually ★ U+2605 no emoji style). It's fine, but I'll use a middot "·" or custom SVG? Ticker is tiny text — use " ✕ "? I'll use "•" bullet char, safe. Actually let me use small inline separators "★"? Hmm risk of emoji rendering? U+2605 BLACK STAR is not emoji (U+2B50 is). Chrome renders U+2605 as font glyph. Safe. I'll use "★" in ticker & press badges.
Toppings ticker content: ['BULGOGI TACOS','KIMCHI FRIES','GOCHUJANG WINGS','SOJU SLUSHIES','OPEN LATE','EL LOCO BURRITO'].
JS ticker builder: create span items, duplicate set twice into track, CSS animation translateX(-50%). Track must be display:inline-flex width:max-content.
The rotated divider uses same builder with different id + reverse direction (animation-direction: reverse).
Now cart panel details:
```html
<div class="overlay" id="overlay"></div>
<aside class="cart" id="cartPanel" aria-label="Your order">
<div class="cart-head"><h3>At the window</h3><button id="cartClose">×icon</button></div>
<div class="cart-body" id="cartBody"></div>
<div class="cart-foot" id="cartFoot"></div>
</aside>
```
Render function builds rows & footer; empty state includes "Go grab something →" closing panel.
Ticket replaces cartBody+foot content.
Checkout button id "fireBtn" — attach via event delegation on panel (since re-rendered): use `cartFoot.addEventListener('click', e=> e.target.id==='fireBtn' && checkout())`.
Also +/- buttons data-id & data-delta — delegate on cartBody click.
Toast container `#toasts` fixed bottom center; toast div with border+shadow.
Order burst on checkout: spawn 14 dots absolutely at panel button? Simple screen-center burst divs with random vectors via inline CSS vars & keyframe. I'll do small: `burst(x,y)` creates container of 12 span dots animating outward translate via --tx/--ty. Colors alternate red/lime/yellow. Put container at click position (fixed, pointer-events none).
Where does the burst keyframes land: `@keyframes pop { from{transform:translate(0,0); opacity:1} to{transform:translate(var(--tx),var(--ty)); opacity:0} }`.
Alright. Also Escape key closes cart. Overlay click closes.
Reveal stagger: in observer callback, `el.style.transitionDelay = el.dataset.delay||''`... simpler: CSS `.reveal{transition: opacity .6s, transform .6s}` and JS adds `.in`. For grid children add `--d: i*70ms` via inline style at render time: `style="--d:${i*70}ms"` and CSS `transition-delay: var(--d, 0ms)`.
Counters run on load (hero). OK.
Also — headline "GET LOCO." maybe better as one line "GET LOCO." with big size; two lines: "KOREAN BBQ / MEETS / EL STREET"? Hmm. Final: H1 lines: "SSEOUL" no. Let me finalize hero H1:
```
<h1 class="hero-title">
<span class="t1">GET</span>
<span class="t2">LOCO</span>
</h1>
```
Big "GET LOCO" with LOCO in red w/ hard ink shadow. Under: lede paragraph. That's punchy and clear. Sub above eyebrow: chip "KOREAN BBQ × MEXICO CITY STREETS".
Lede: "Bulgogi in your tortilla. Kimchi in your quesadilla. Sesame smoke hanging over a hot plancha at 2AM. We are a silver truck with a serious flavor problem — and you're about to have one too."
CTA buttons + status line under.
Let me finalize heat labels array:
```js
const HEAT=[
{max:24,name:'SO GENTLE',sub:'Soy-garlic energy. Abuela-approved, grandma-approved.'},
{max:49,name:'SEOUL SIZZLE',sub:'A polite little kick. You will be fine. Probably.'},
{max:74,name:'GOCHUJANG GANG',sub:'The sweet-fire paste does its thing. Napkins within reach.'},
{max:94,name:'BULDAK BRAVE',sub:'Sweating at the window is a normal Tuesday here.'},
{max:101,name:'EL LOCO',sub:'Sign the flavor waiver. We check ID and soul.'},
];
```
And salsa stops as above. Also heat read chip color = salsa color with ink text? chip bg = var(--salsa) — text ink; at deep red bg ink text low contrast... use cream text when heat > 70: set class. Manage: `heatRead.style.background = color; heatRead.style.color = v>55?'#FFF6E9':'#231610'`.
Slider CSS: custom.
```css
input[type=range]{-webkit-appearance:none;width:100%;height:26px;background:transparent}
input[type=range]::-webkit-slider-runnable-track{height:12px;border:3px solid var(--ink);border-radius:99px;background:linear-gradient(90deg,var(--lime),var(--yellow) 40%,var(--red));}
input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:30px;height:30px;border-radius:50%;background:var(--cream);border:3px solid var(--ink);margin-top:-13px;box-shadow:3px 3px 0 var(--ink);cursor:grab}
input[type=range]::-moz-range-track{...}
input[type=range]::-moz-range-thumb{...}
```
Track gradient static (don't need --fill). Fine.
Heat card: cream bg? Hero is cream; heat card white-ish (cream brighter #FFFDF7?) with ink border + shadow red. Put inside hero-art below taco.
Sunburst behind taco may crowd heat card — layout: `.hero-art{position:relative}` sunburst absolute centered behind; taco-wrap relative z1; heat card below margin-top. Sunburst size 400-460. Taco svg width 105% of sunburst? Let me: `.hero-art{display:flex;flex-direction:column;align-items:center}` `.hero-stage{position:relative;width:min(420px,86vw)}` sunburst absolute inset 0 rotate; svg width 100% relative z1; steam absolute.
Flames: inside svg.
I think I'm ready. One more: prevent `Enter` key on slider from scrolling etc — default ok.
Newsletter + "Get the drop" + toast on submit.
Also honor "no console errors": double-check every getElementById matches. I'll be systematic at the end.
Rough final copy check for truck section heading: "WHERE'S THE TRUCK?" + status card "RIGHT NOW" + sentence. Big red "OPEN" state styling.
Footer big text "SEE YOU AT THE WINDOW." huge display cream on ink, then form row + columns (HOURS/CONTACT/FOLLOW) + bottom bar "© 2025 Kimchi Loco · Hot sauce responsibly".
Let me now also design the "story" yellow strip content:
```
<section class="story">
<div>
<div class="story-kicker">HOW IT STARTED</div>
<p>"A taquero from Oaxaca and a cook from Busan shared a midnight fridge. There was one fight — about hot sauce — and one leftover stack of tortillas. That's it. That's the whole origin story. Now there's a truck."</p>
— Marco & Soo, founders
```
Add two little SVG face circles? I'll add simple founder chips: circles with glasses/mustache doodles. Quick paths: face circle cream stroke ink; glasses: two circles + bridge; mustache: `M ... q`. Cute, cheap. Do it.
Alright — WRITE THE FILE. I'll keep it tight but complete. Also final QA pass mentally afterward.
One more consideration: total length — likely ~1000 lines; that's fine.
Media queries: 980px (hero stack), 720px (nav links hide, stats wrap), 400s fine-tune. Ensure menu grid `repeat(auto-fill,minmax(270px,1fr))`.
Font sizes: hero title clamp(56px, 9vw, 118px)? At 1280 → 9vw=115px. Two lines "GET" "LOCO" — total ~240px + rest; fits 800 height with slider? Hero might exceed viewport → scroll needed; screenshot shows top part only. Keep hero compact: title clamp(64px,7.5vw,104px), stats small. Should be OK: eyebrow 20 + title 220 + lede 90 + ctas 60 + stats 70 + status 30 ≈ 490 + paddings. Art column: sunburst 400 + heat card 130 ≈ 560. Fits in ~660 available (800-96-56). Tight but OK. Mobile stacks → taller, fine.
Let me write out everything now, carefully.
Also note: `body{padding-top:96px}` then hero pad top 40. Topbar: ticker 32px + header 60px = 92 → padding 100.
Ticker font-size 12px letter-spacing 1px.
Write JS constants and DOM refs after DOM (script at end of body, no defer needed).
Cart button in header: bag icon + count badge (hidden when 0).
Edge case: `cart` name conflicts with panel ids? Fine.
Let me make sure `--salsa` default in `:root{--salsa:#E23B2E}` so SVG shows color pre-JS.
Flames opacity: default 0.
Steam spawn positions: container width 180px, but hero art width varies; container centered over taco top. puffs random left 15-85%, bottom 0.
Also handle visibilitychange to avoid runaway timers? Minor, skip.
Double-check no forbidden APIs. Using Date, setInterval, rAF — all fine.
Writing final code now — will do one continuous pass with attention.
After writing, I'll verify: IDs: tickerTrack, tickerTrack2(divider), statusPillTop, statusTextTop? Let me define IDs list as I write and cross-check at end:
- topbar ids: ticker, tickerTrack
- cartBtn, cartCount
- heroStatus (sentence),
- heatSlider, heatRead, heatSub, heatCard? , heroArt, tacoWrap, tacoSvg, steam
- menuGrid
- schedList, statusBig (big sentence), statusDot? build inner html
- newsForm, newsInput
- cartPanel, overlay, cartClose, cartBody, cartFoot
- toasts
- truckSvg (click beep) — use class .truck-honk container id truckWrap
- stats counters via [data-count]
- reveals via [data-reveal]
Go.
Also small: `@media (prefers-reduced-motion){ .ticker-track, .sunburst, ... {animation:none} }` and JS `reduced` flag.
Draft the HTML body structure fully, then CSS, then JS. Final answer = single code block. Let me be meticulous with the SVGs.
TACO SVG final paths (I'll go with my plan):
```html
<svg id="tacoSvg" viewBox="0 0 340 300" role="img" aria-label="A bulgogi taco loaded with kimchi and salsa">
<!-- flames -->
<g class="flames" style="--fo:0">
<path class="fl" d="M84 232c-8-4-10-12-4-18-8 1-13 8-10 17 4 12 22 14 30 4-6 3-12-3-16-3z" ...
```
Hmm flames are fiddly; simpler flame: teardrop with inner notch:
`M0 0 C -6 -8, -2 -18, 6 -20 C 2 -12, 6 -10, 8 -14 C 14 -8, 12 2, 6 6 C 2 9 -3 6 0 0 Z` — whatever, stylized. I'll write 3 flames via path reuse with transform translate/scale. Let me define:
```
<path id="flame" d="M7 -34C-1 -26 -3 -16 1 -9c-4 1 -6 4 -5 8 2 8 12 10 17 4 5 -5 4 -14 -2 -21 -3 -4-4-9-10-12z"/>
```
Too improvised. Alternative: skip flames, instead when loco: show fire emoji? no. Simpler "LOCO MODE" visual: at high heat the sunburst spins faster + taco wiggle + red glow — flames optional. I'll include simple flames: three teardrops: `M0 0 c-7 -6 -8 -16 -2 -22 c1 6 4 8 8 9 c6 -8 8 -2 7 4 c-1 8 -7 12 -13 9z` — eh.
You know what — easiest readable flame:
```
<path d="M12 2c5 8 9 10 7 17-2 8-11 11-17 6-5-4-4-11 1-13-1-4 1-7 3-10z"/>
```
can't visualize. FINE — different approach: use three small chili-ish "fire tongues": rounded triangles. `<path d="M0 0 C-4 -8 -4 -16 0 -22 C4 -16 4 -8 0 0 Z">` = simple flame spike (pointing down? from 0,0 up to -22). Three spikes of varying width/height, fill #F77F00, inner smaller spike #FFC53D. That reads as flames! Do:
```
<g class="flames">
<g transform="translate(150,238)">
<path d="M-10 0C-14 -10 -12 -20 -8 -26 C-6 -18 -2 -16 -1 -24 C4 -14 6 -6 4 0Z" fill="#F77F00"/>
...
```
whatever, three overlapping spikes look flamey enough with flicker scale animation. Good.
Then fillings, shell, salsa, cup as planned.
Let me carefully finalize shell + filling coordinates:
viewBox 0 0 340 300. Taco center x=170.
Fillings region: y from ~70 (tips) to ~150 (base into shell). x from 85 to 255.
Shreds (strokes) from base y≈135 upward:
- green: `M100 132 C 98 112 104 96 116 88` w8 #4E9F3D; second `M112 134 C 112 116 120 104 128 98` w8 #77C159
- purple: `M232 132 C 236 112 230 98 218 90` w8 #7B4A9E; `M222 134 C 220 118 212 106 202 100` w8 #9A6BC4
- another green on right `M150 130 C 146 108 152 92 166 84` w9 #4E9F3D? Let me add 5-6 strips total varying.
Bulgogi strips (filled paths):
- `M96 140 C 104 126 122 124 134 130 C 128 138 108 146 96 140 Z` fill #8E5030 stroke ink 3.5
- `M204 140 C 214 128 232 126 244 132 C 238 140 216 146 204 140 Z` fill #A05A32 stroke ink 3.5
- middle one partially hidden: `M138 146 C 150 136 168 134 182 138 C 172 148 152 152 138 146 Z` fill #8E5030 stroke ink 3.5
Kimchi chunks:
- `<rect x="118" y="104" width="18" height="11" rx="4" fill="#E23B2E" stroke="#231610" stroke-width="3" transform="rotate(-10 127 110)"/>`
- `<rect x="196" y="106" width="17" height="10" rx="4" fill="#E23B29"... rotate(8)>`
- `<rect x="160" y="92" width="16" height="10" rx="4" ... rotate(-4)>`
- `<rect x="140" y="112" width="14" height="9" rx="4" fill="#C9301F" rotate(6)>`
Sesame seeds: ellipses w4 h2.5 fill #FFF6E9 stroke ink 2 at (150,100),(176,120),(206,88)? A few: `<ellipse cx="150" cy="101" rx="4" ry="2.6" transform="rotate(-20 ...)"/>`.
Salsa drizzle (class salsa, fill var): `M92 136 C 116 148 148 152 170 146 C 196 140 224 142 246 152 C 232 168 196 174 162 168 C 128 162 100 152 92 136 Z` stroke ink 4. This sits across fillings at y 136-174 — above shell top edge (which dips to ~168 mid): drizzle bottom ~174 slightly below shell inner edge → shell covers a bit — fine, looks like sauce behind the rim. Good.
Cilantro sprigs? skip.
Lime wedge right side: `<g transform="translate(258 150) rotate(12)"><circle r="17" fill="#C9E59B" stroke="#231610" stroke-width="3.5"/><path d="M-12 6 L14 -8 M-8 -10 L10 12 M0 -16 L0 16" stroke="#8FBF57" stroke-width="2.5"/></g>` — hmm lines across circle: spokes. Wait lime wedge usually has rind ring: outer circle fill #B9E07A, inner circle #D9F0A8? Keep simple: circle + spokes.
Shell:
`<path d="M34 148 C 64 236 276 236 306 148 C 282 176 58 176 34 148 Z" fill="#F5B849" stroke="#231610" stroke-width="5" stroke-linejoin="round"/>`
Wait cubic from (306,148) back to (34,148) needs explicit endpoint syntax: `C 282 176, 58 176, 34 148 Z`. Yes include endpoint. Mid of inner curve ≈ y ~169 at center. Bottom mid: controls (64,236),(276,236) → mid y ≈ 148*? Bezier mid = (P0+3P1+3P2+P3)/8 y = (148+3*236+3*236+148)/8 = (296+1416)/8=1712/8=214. Bottom dips to 214, inner edge mid = (148+3*176+3*176+148)/8 = (296+1056)/8=169. Shell band 169..214 center.
Toasted spots: ellipses at (120,190),(178,200),(236,186) rx8 ry4 fill #D98E33 opacity .8, plus tiny circles.
Rim highlight: `<path d="M48 152 C 100 178 240 178 292 152" stroke="#FBD98A" stroke-width="7" fill="none" opacity=".85"/>` hmm that's ON the shell below inner edge — inner edge mid 169, highlight at ~176-178 → on shell surface. ok.
Also add shell "notch" lines near tips to suggest fold: small strokes at (44,155),(290,155)? skip.
Salsa cup front-left:
```
<g transform="translate(30,196)">
<path d="M0 6 L7 34 L47 34 L54 6 Z" fill="#FFF6E9" stroke="#231610" stroke-width="4.5" stroke-linejoin="round"/>
<ellipse class="salsa" cx="27" cy="6" rx="27" ry="8" stroke="#231610" stroke-width="4.5"/>
<text ...? skip
</g>
```
Cup at x 30..84, y 196..238 area — overlaps shell bottom-left (shell bottom curve at x~34-84 spans y 148..~200) — cup drawn after shell → in front. Good. Add label "SALSA" tiny? skip.
Flames behind at base y≈235: three spikes as discussed, centered x 170:
```
<g class="flames" transform="translate(170,236)">
<path d="M-26 2 C-30 -8 -28 -18 -22 -24 C-20 -14 -14 -12 -12 -20 C-8 -10 -8 -2 -12 2 Z" fill="#F77F00"/>
<path d="M2 4 C-4 -10 -2 -24 6 -30 C 8 -18 14 -16 16 -24 C 22 -12 20 -2 12 4 Z" fill="#F77F00"/>
<path d="M30 2 C 26 -8 28 -16 34 -20 C 34 -12 40 -12 40 -18 C 44 -8 42 0 36 2 Z" fill="#F77F00"/>
<path d="M-8 2 C-12 -6 -10 -14 -6 -18 C-4 -10 0 -8 2 -14 C 6 -6 4 0 0 2 Z" fill="#FFC53D"/>
<path d="M14 4 C 10 -6 12 -16 18 -20 C 18 -10 24 -8 24 -14 C 28 -4 24 2 18 4 Z" fill="#FFC53D"/>
</g>
```
Rough but plausible. Flicker: CSS `.flames{animation: flick .5s steps(2) infinite alternate; transform-origin: 170px 236px; opacity: var(--fo)}` — SVG CSS transform on g with translate attr + animation transform conflicts (animation transform overrides attribute transform!). CSS `transform` overrides the SVG attribute presentation → would move flames to origin. So wrap: outer `<g transform="translate(170,236)">` (attribute), inner `<g class="flames">` gets CSS scale animation only. But transform-origin issues in SVG for CSS: use `transform-box: fill-box; transform-origin: 50% 100%`. OK.
Steam puffs: over top of fillings: container top area. tacoWrap position relative; steam `position:absolute; top:-6%; left:20%; width:60%; height:26%` puffs inside.
Tilt: JS sets tacoWrap.style.transform rotate — but sunburst separate element, tilt taco only. `#tacoWrap{transition:transform .25s}`.
Loco wiggle on `#tacoSvg.loco svg? ` class on svg: `#tacoSvg.loco{animation: wiggle .35s infinite}` with transform-box fill-box.
Sunburst spin: `.sunburst{animation: spin 60s linear infinite}` `.loco .sunburst{animation-duration: 6s}` — put class on `.hero-stage` parent: `stage.classList.toggle('loco', v>=95)`.
Heat glow: `#tacoWrap{filter: drop-shadow(0 0 var(--glow) rgba(226,59,46,.55));}` JS sets `--glow` px on stage? filter on wrap. At low heat 6px subtle. But drop-shadow on outlined art may look like a cheap glow... keep small (4→20px) — it will read as heat aura. OK.
Now the TRUCK SVG (viewBox 0 0 380 170):
```
<svg id="truckSvg" viewBox="0 0 380 170">
<ellipse cx="190" cy="158" rx="165" ry="9" fill="rgba(35,22,16,.18)"/> <!-- shadow -->
<g class="truck-bob">
<!-- cab (front, right side) -->
<path d="M300 52 h22 q16 3 22 18 l12 42 h-56 z" fill="#FFF6E9" stroke="#231610" stroke-width="4" stroke-linejoin="round"/>
<path d="M310 58 h12 q9 3 13 13 l8 26 h-33 z" fill="#3A2620"/>? windshield...
```
Hmm cab shape: truck front slanting: I'll draw cab as: body box from x=30..300 (the cargo box), cab from 300..370. Cab: `M300 46 h24 q14 2 20 14 l14 52 h-58 Z`? then front bumper. Wheel at cx=330? wheels: rear at 100, front at 330, cy=132 r=17.
Let me structure:
- cargo box: `rect x=28 y=40 width=250 height=76 rx=8` cream, stroke 4.
- red stripe: `rect x=28 y=98 width=250 height=18` fill red, stroke? no stroke, rx0 — but rect base rx… stripe inside box: y 98..116 within 40..116 → box bottom edge at 116 exactly touches; make box height 78 → bottom 118, stripe y=96 h=14.
- window: `rect x=96 y=54 width=94 height=44 rx=8` fill #231610 (dark) — inside it small menu lines cream: `<rect x=106 y=64 width="74" height="4" rx="2" fill="#FFF6E9" opacity=".7"/>` ×3? and a tiny taco icon? keep 3 lines.
- awning: above window y=42..54: stripes: rects x=92 w=25.33 alternating red/lime y=40 h=14, stroke ink 3 around whole: draw as one group with outline rect. scallop: skip.
- text "KIMCHI LOCO" on box right area: `<text x="224" y="78" text-anchor="middle" font-size="21" font-weight="900" fill="#231610" font-family="Arial Black, Arial">KIMCHI</text>`? Box free area: x 200..278 above stripe (y 40..96). "KIMCHI LOCO" 11 chars at 21px Arial Black ≈ 11*13 = 143px — fits x 200..278 (78px)? no! Too wide. Rotate? Put text vertically? Reduce to size 14 → ~95px, area 78px wide still tight. Alternative: text on the box BELOW window: x 96..190 y 110..116 too short. Hmm. Move window smaller: window x=88 w=70; text area x 170..270 (100px): "KIMCHI LOCO" font 15 Arial Black ≈ 11*8.7=96 fits. Or stack: "KIMCHI" (size 20, ~62px wide) newline "LOCO" — stacked 2 lines at x 224. Let me:
```
<text x="228" y="66" font-size="19" font-family="Arial Black, Arial, sans-serif" font-weight="900" fill="#231610" text-anchor="middle">KIMCHI</text>
<text x="228" y="88" font-size="19" ...>LOCO</text>
```
Width ~19*0.75*6≈86 fits x 185..271. Good, and red stripe below gives the wrap look. Add tiny taco icon next to text? skip.
- cab: `path d="M278 50 h26 q16 3 22 16 l14 46 h-62 z"` wait box ends at x=278 (28+250). Cab attaches right: from x=278 to 370, top y≈50 slanting windshield. Cab fill cream stroke ink 4. Cab window: `path d="M288 56 h20 q10 2 14 10 l6 20 h-40 z" fill="#3A2620" stroke="#231610" stroke-width="3"`? Simplify: windshield slant: `M282 54 L330 54 L344 78 L282 78 Z`? I'll write cab: `M278 54 h30 q14 2 20 14 l12 44 h-62 Z`; windshield: `M286 60 h22 q9 2 12 9 l6 18 h-40 Z` fill #3A2620.
- front bumper: `rect x=330 y=120 width=40 height=8 rx=3` hmm wheels cover.
- wheels: rear cx=95 cy=132 r=17: outer circle fill #231610, inner r=7 cream, inner-inner r=2 ink. front cx=328 cy=132 r=17 same. Wheel arch: box bottom at 118; wheels stick below to 149 — truck floats: add wheel cutout look fine.
- exhaust stack: `rect x=52 y=18 width=9 height=24 rx=3 fill=#231610` + smoke: `<circle class="smoke" cx="56" cy="14" r="5" fill="rgba(35,22,16,.25)"/>` ×2 with animation delay rising (CSS translateY + fade). SVG circle CSS animation transform ok.
- side mirror / AC unit on top: `rect x=120 y=28 width=34 height=14 rx=4 fill=#E23B2E stroke ink 3` (rooftop unit).
- "OPEN" sign hanging from window top: `<g class="sign" transform="translate(143 100)"><line x1=0 y1=0 x2=0 y2=8 stroke=#231610 stroke-width=3/><rect x=-22 y=8 width=44 height=18 rx=4 fill="#FFF6E9" stroke="#231610" stroke-width="3"/><text x=0 y=21 font-size=10 font-weight=900 text-anchor=middle fill="#B01F14">OPEN</text></g>` positioned over stripe — swing animation rotate ±8 at top. Sign at y ~100-126 overlaps stripe (y 96-110) — reposition: window bottom 98; sign hangs below window: translate(143,98), rect y 8..26 → 106..124 — over red stripe/cream? box bottom 118, so sign extends below box → fine visually (hangs over edge). Move sign INTO window: translate(143,58)? window y 54..98. sign line from y=56? Let me hang from awning: translate(141,44)? Actually simplest: sign hangs from awning bottom center (143, 56): line 56→62, rect 62..80 (inside window, visible against dark) — cute! OPEN text red on cream. swing rotate origin at (141,56). CSS: `.sign{transform-box:? }` transform-box fill-box uses bounding box; set transform-origin: 50% 0% with transform-box:fill-box.
Wait — CSS animation transform overrides presentation attribute `transform` on that `<g class="sign">`? Yes — conflict again. So wrap sign in outer `<g transform="translate(...)">` and inner `<g class="sign">` with box origin top.
Wheels: no spin (parked). Bob animation on whole truck g: `translateY(0/-3px)` 1.6s alternate — CSS transform on g without attribute transform: `.truck-bob{animation: bob 1.4s ease-in-out infinite alternate}` fine since no attr transform on that g (bob is its own).
Smoke circles: `.smoke{animation: rise 2.2s linear infinite}` keyframes translate(0,0)→(−6px,−18px) opacity fade. With delays per circle. transform-box fill-box fine.
Honk burst on click: overlay div in road container: "BEEP BEEP!" sticker rotate pop.
That's the truck.
Also honk keyframes pop: scale 0→1.15→1 fade.
Alright — schedule card & status:
```html
<div class="status-card" id="statusCard">
<span class="dot"></span>
<div>
<b id="statusWord">CHECKING…</b>
<span id="statusLine">…</span>
</div>
</div>
```
statusWord OPEN/CLOSED. dot green pulsing when open.
Schedule list: rows built by JS into `#schedList` as `.srow` (day | hours | spot). Today row gets `.today` with a "TODAY" sticker.
Truck section layout: grid 2 cols: left col: heading + status card + road/truck + note; right col: schedule panel + "pin" button + insta note. On mobile stack.
The hype section: red bg (#E23B2E) with cream cards. Heading "THE PEOPLE ARE YELLING (nicely)". Quotes:
1. "I planned to try one taco. That was four months ago. Send help." — Dana R. · regular since day 1
2. "The short rib taco might be the single best $4.50 in this city." — Fork & Spoon Magazine
3. "The buldak wings made my dad cry. He asked for the recipe. We're family now." — Marcus T. · verified spice victim
Badges: "★ BEST LATE-NIGHT EATS — CITY WEEKLY", "★ TOP 10 TRUCKS — FORK&SPOON", "★ PEOPLE'S CHOICE — GRAND CENTRAL PARK".
Footer ink: giant "SEE YOU AT THE WINDOW." + newsletter + links: HOURS (Mon–Sun summary), CONTACT: tacos@kimchiloco.food, (555) 862-LOCO, FOLLOW: IG/TikTok chips (links '#' with... href="#" jump top — fine, or toast "See you on IG"). Use buttons with toast.
Bottom: "© 2025 Kimchi Loco LLC · Seoul × Mexico City · Hot sauce responsibly."
Menu ink section: heading "HOT OFF THE PLANCHA" + squiggle underline svg + sub "Made to order, tortillas pressed while you wait, heat marked honestly."
Also small note in menu: "QR? We have a QR. It just goes to this page." skip.
Drinks strip: "IN THE COOLER — Yuja-ade · Watermelon soju slushy (21+) · Mexican Coke · Horchata makgeolli float · Cucumber limeade"
Now finalize JS numbers formatting: prices like `$4.50`. toFixed(2).
Cart total & count.
Toast content examples: `Bul-Go-Gee Taco — added. Good call.`
Ticket:
```
ORDER #LOCO-<rand 2 digits>
<items summary>
READY IN ~8 MIN AT <spot or 'the window if closed'>
```
If closed: ticket says "We'll have it hot & ready the second the griddle's back on (<next>)". Cute — use status.
OK also keyboard: Esc close. Space/keys scroll fine.
Now REALLY write it. I'll write the code in order and try to be complete and consistent. Let me draft the JS functions signatures to keep consistent:
```js
(()=>{
"use strict";
const $=(s,c=document)=>c.querySelector(s);
const $$=(s,c=document)=>[...c.querySelectorAll(s)];
const reduced=matchMedia('(prefers-reduced-motion: reduce)').matches;
// ---------- data
const MENU=[...];
const SCHED=[...]; // ordered Mon..Sun
const fmtH=h=>{h%=24;if(h===0)return'12AM';if(h===12)return'12PM';return h>12?(h-12)+'PM':h+'AM'};
// ---------- ticker builder
function buildTicker(track, items){ ... track.innerHTML = html.repeat? ... }
// CSS marquee: .ticker-track{display:flex;gap:2.5em;width:max-content;animation:marquee var(--ms,26s) linear infinite}
// @keyframes marquee{to{transform:translateX(-50%)}}
// Build: track.innerHTML = [...items,...items,...items,...items].map(t=>`<span>${t}</span><i>★</i>`).join('');
// width max-content, translate -50% needs exactly 2 copies: 2 groups. So: const group=items.map(...).join(''); track.innerHTML = group+group; each group width equal → -50% seamless. Need group width ≥ viewport: items×(text+gap): phrases ~6 items * ~160px = 960 < 1280 hmm. Repeat phrases 3× inside group: items.concat(items).concat(items)? I'll pass arrays already repeated: buildTicker(track, [...phrases,...phrases,...phrases]) then group = that + same → total 6×… fine.
// ---------- status
function statusNow(){...}
function renderStatus(){ updates pills & lines }
// ---------- menu render
// ---------- schedule render
// ---------- heat
// ---------- steam
// ---------- tilt
// ---------- cart + panel + ticket
// ---------- toasts
// ---------- reveal + counters
// ---------- misc listeners
})();
```
Ticker separator: use `<em>★</em>` styled red/yellow.
Careful: innerHTML with template strings containing `$` fine.
Counters:
```js
function countUp(el){const target=parseFloat(el.dataset.count),dec=+ (el.dataset.dec||0);const t0=performance.now(),dur=1300;
(function tick(t){const p=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-p,3);const v=target*e;el.textContent=dec?v.toFixed(dec):Math.round(v).toLocaleString('en-US');if(p<1)requestAnimationFrame(tick)})(t0);}
```
Run after 300ms.
Reveal observer:
```js
const io=new IntersectionObserver(es=>es.forEach(e=>{if(e.isIntersecting){e.target.classList.add('in');io.unobserve(e.target)}}),{threshold:.15});
$$('.reveal').forEach(el=>io.observe(el));
```
Cart render:
```js
function renderCart(){
const ids=Object.keys(cart);
cartCount.textContent=Object.values(cart).reduce((a,b)=>a+b,0);
cartCount.classList.toggle('show', !!...);
cartCount pop animation: cartCount.classList.remove('pop'); void cartCount.offsetWidth; cartCount.classList.add('pop');
if(!ids.length){cartBody.innerHTML=`<p class="cart-empty">Nothing yet...</p>`;cartFoot.innerHTML=``;return}
rows + foot with total + fire button.
}
```
Checkout:
```js
function checkout(){
const items=Object.entries(cart); const total=...;
const s=statusNow();
const loc=s.open? s.spot : `next stop: ${s.spot} (${s.next})`;
const num='LOCO-'+(10+Math.floor(Math.random()*89));
cartBody.innerHTML=`<div class="ticket">...`;cartFoot.innerHTML=`<button ...>Done — back to browsing</button>`;
fire burst at button pos.
}
```
Ticket uses monospace.
Escape:
```js
addEventListener('keydown',e=>{if(e.key==='Escape')closeCart()})
```
Truck beep:
```js
truckSvg.addEventListener('click',e=>{ spawn sticker in road: beep() })
```
beep: create div in #road with text "BEEP BEEP!", class pop-beep, positioned center-ish, remove after 1s. Also truck jumps: add class 'honk' (translateY -6px quick). Simple.
Tilt:
```js
const stage=$('#heroStage');
stage.addEventListener('pointermove',e=>{if(matchMedia('(pointer:fine)').matches){const r=stage.getBoundingClientRect();const dx=(e.clientX-r.left)/r.width-0.5,dy=(e.clientY-r.top)/r.height-0.5;tacoWrap.style.transform=`rotate(${dx*6}deg) translate(${dx*10}px,${dy*6}px)`}});
stage.addEventListener('pointerleave',()=>tacoWrap.style.transform='');
```
Steam:
```js
let steamOn=!reduced;
function puff(){const p=document.createElement('i');p.style.left=(12+Math.random()*76)+'%';p.style.setProperty('--dy',-(60+Math.random()*60)+'px');p.style.setProperty('--dur',(1.6+Math.random()*1.2)+'s');p.style.width=p.style.height=(9+Math.random()*13)+'px';steamBox.appendChild(p);p.addEventListener('animationend',()=>p.remove());}
setInterval(()=>{if(document.visibilityState==='visible')puff()},heat>75?260:520);
```
Set interval dynamically? Simpler: schedule via recursive setTimeout reading current heat.
```js
(function loop(){const wait=reduced?4000: (320 - heat*2.4); setTimeout(()=>{puff();loop()},Math.max(160,wait))})();
```
heat variable updated by applyHeat.
applyHeat(v):
```js
let heat=+slider.value;
function applyHeat(v){heat=v; const c=salsaColor(v); stage.style.setProperty('--salsa',c); stage.style.setProperty('--glow',(4+v*.22)+'px'); stage.style.setProperty('--fo',Math.max(0,(v-55)/45).toFixed(2));
const L=HEAT.find(h=>v<=h.max); heatRead.textContent=L.name; heatRead.style.background=c; heatRead.style.color= v>55?'#FFF6E9':'#231610'; heatSub.textContent=L.sub;
stage.classList.toggle('loco', v>=95);
slider.style.setProperty('--fill', v+'%'); // if using fill gradient... using static gradient, skip
}
```
`--fo` used by flames: `.flames{opacity:var(--fo,0)}` on stage-scoped: define `.flames{opacity:var(--fo,0)}` since stage inherits var. Good. Also glow: `#tacoWrap{filter:drop-shadow(0 0 var(--glow,6px) rgba(226,59,46,.5))}` — inherits var from stage. OK.
Sunburst behind: z-index: stage relative; sunburst absolute z:0; tacoWrap z:1; steam z:2.
Careful: `--glow` in filter drop-shadow: `drop-shadow(0 0 var(--glow) color)` — var must resolve to length; setting `(4+v*.22)+'px'` ok.
salsaColor stops: [[155,196,59],[255,197,61],[247,127,31],[226,59,46],[168,18,24]].
Menu card HTML:
```js
menuGrid.innerHTML=MENU.map((m,i)=>`
<article class="card reveal" style="--d:${i*70}ms" data-id="${m.id}">
${m.tag?`<span class="tag">${m.tag}</span>`:''}
<div class="card-top">
<span class="dish-ico"><svg><use href="#i-${m.icon}"/></svg></span>
<h3>${m.name}</h3>
<span class="price">$${m.price.toFixed(2)}</span>
</div>
<p>${m.desc}</p>
<div class="card-bot">
<span class="heat-row">${heatChilis(m.heat)}<em>${HEATNAMES[m.heat]}</em></span>
<button class="add" data-add="${m.id}">Add +</button>
</div>
</article>`).join('');
```
HEATNAMES=['no heat','mild','medium-hot','certified'] maybe ['','mild','hot','loco']? heat 1 = "gentle", 2 = "spicy", 3="fire". chilis: `<svg class="chili ${k<m.heat?'':'off'}">` ×3 for heat>0; if 0: "0"?? All items have ≥1 so always 3 chili slots. heatChilis: `[0,1,2].map(k=>`<svg class="chili${k<m.heat?'':' off'}" aria-hidden="true"><use href="#i-chili"/></svg>`).join('')` plus label text.
Note `<use href>` works in Chrome (SVG2). Include xlink? modern fine.
Delegate: menuGrid.addEventListener('click', e=>{const b=e.target.closest('[data-add]'); if(b){addItem(b.dataset.add); toast(...)}}).
addItem: cart[id]++, renderCart, bounce.
Panel rows:
```js
cartBody.innerHTML=ids.map(...`<div class="crow"><div><b>${m.name}</b><span>$${(m.price*q).toFixed(2)}</span></div><div class="stepper"><button data-step="-1" data-id="${id}">−</button><span>${q}</span><button data-step="1" data-id="${id}">+</button></div></div>`)
cartFoot.innerHTML=`<div class="ctotal"><span>Total</span><b>$${total.toFixed(2)}</b></div><button class="btn btn-red" id="fireBtn">Fire up my order</button><p class="cnote">Pay at the window — cash, card, or your dignity.</p>`
```
stepper buttons content use minus svg or char '−' (minus sign U+2212 fine text).
Delegation on cartBody for step buttons; if qty→0 delete.
OK. Toast fn:
```js
function toast(msg){const t=document.createElement('div');t.className='toast';t.innerHTML=msg;toasts.appendChild(t);requestAnimationFrame(()=>t.classList.add('show'));setTimeout(()=>{t.classList.remove('show');setTimeout(()=>t.remove(),300)},2600);}
```
Newsletter:
```js
newsForm.addEventListener('submit',e=>{e.preventDefault();toast(`<b>You're in.</b> We'll slide into your inbox when the truck moves.`,?); newsInput.value='';});
```
Pin button: `pinBtn` → toast "Pin dropped — 8th & Main, look for the shiny silver truck."
IG chips → toast "See you on the grid (@kimchilocotruck)."
Status:
```js
function statusNow(){
const now=new Date(), dow=now.getDay(), t=now.getHours()+now.getMinutes()/60;
const today=SCHED.find(s=>s.d===dow);
if(today&&t>=today.start&&t<today.end)return{open:true,spot:today.spot,until:fmtH(today.end)};
for(let k=0;k<7;k++){const d=(dow+k)%7;const s=SCHED.find(x=>x.d===d);
if(k===0){ if(s&&t<s.start) return{open:false,spot:s.spot,next:`today at ${fmtH(s.start)}`}; }
else if(s) return{open:false,spot:s.spot,next:`${s.day} at ${fmtH(s.start)}`};}
return{open:false,spot:'somewhere',next:'soon'};}
```
SCHED includes all 7 days so fine.
renderStatus():
```js
const s=statusNow();
$('#statusPill').innerHTML=`<span class="dot ${s.open?'on':''}"></span>${s.open?'OPEN NOW':'CLOSED'}`;
heroStatus: s.open? `Right now: <b>${s.spot}</b> — griddle hot 'til ${s.until}.` : `Griddle's off — next stop <b>${s.spot}</b>, ${s.next}.`
statusWord/#statusBig similar; statusCard classList toggle 'open'.
```
setInterval(renderStatus, 30000); renderStatus() on load.
Schedule render:
```js
schedList.innerHTML=SCHED.map(s=>`<div class="srow${s.d===todayD?' today':''}"><b>${s.day}</b><span>${fmtH(s.start)}–${fmtH(s.end)}</span><em>${s.spot}</em>${s.d===todayD?'<i class="now">TODAY</i>':''}</div>`).join('');
```
todayD computed at render.
Nav shadow: `addEventListener('scroll',()=>{topbar.classList.toggle('scrolled',scrollY>8)})` minor.
Auto tween: as planned. Also 'input' listener sets userTouched=true? If user drags later after tween done, fine; userTouched only stops tween.
slider 'input' → applyHeat(+value). Also keydown arrows (input event fires).
Now CSS specifics. Let me define section paddings: `.wrap{max-width:1160px;margin:0 auto;padding:0 24px}`.
Hero: `padding: 48px 0 72px`.
Title: `font-size:clamp(3.5rem,7.4vw,7rem); line-height:.9; letter-spacing:-.015em`. "LOCO" red with `text-shadow:0.07em 0.07em 0 var(--ink)`. Hmm text-shadow em-based good.
Eyebrow chips: red chip white text "SEOUL BBQ", "×", yellow chip ink text "MEXICO CITY STREETS".
Lede: font-size clamp(1.05rem,1.4vw,1.25rem); max-width 46ch; margin.
Buttons row + status pill inline.
Stats: flex gap; each: display font big red number + label small.
Divider marquee: `.divider{background:var(--red);transform:rotate(-1.6deg);border-top/bottom:3px solid ink;}` inner track cream text, font display, font-size ~ clamp(1rem,2vw,1.4rem); padding 10px 0; margin: -8px 0? To overlap sections place z-index 2, position relative, width 104%, left -2%? Rotation makes corners exceed → body overflow clip handles.
Actually simpler: normal-flow divider with rotate, margins negative vertical to overlap ink menu section top? Menu section below has straight top; rotated red bar over it looks nice: `.divider{margin: 6px 0 -28px; z-index:2; position:relative}` and menu section padding-top accounts. Let me just do margin: 12px 0 -26px; menu padding-top 90px.
Menu section ink: `background:var(--ink); color:var(--cream)`. Header center. Cards cream text ink. Hard shadows red: `box-shadow:7px 7px 0 var(--red)`.
Section head style: `h2{font-family:display;font-size:clamp(2rem,4vw,3.2rem);text-transform:uppercase}` with `.kicker` small chip "THE MENU".
Squiggle underline: inline svg red path in header.
Drinks strip: cream2 box with dashed ink border, display font list.
Truck section: cream bg. Grid `1.15fr .85fr` gap 36.
Road: `background:var(--ink);border-radius:14px;border:3px solid var(--ink)`? A road strip: dark asphalt rounded box height ~ 200px with dashed center line (repeating-linear-gradient) and truck svg inside width min(420px, 80%). Plus a little "street food" sign? Keep: asphalt `#3A2E24`? Road: bg #2B201A, dashes cream. Truck on it.
Status card: cream2 bg, ink border 3, shadow, padding 18, flex, dot 14px green pulse or gray/red. Big sentence 1.1rem.
Schedule panel: cream bg, ink border, rows with borders between; today row bg #FFE1B8? or red-tinted #FFD9CF + "TODAY" sticker chip red.
Hype: red bg, cream text; heading cream; quote cards cream bg ink text with big quote mark; stars yellow svg row; badges: transparent cream border chips.
Story: yellow bg (#FFC53D) ink text: founder quote + founder chips (two circles svg faces + names "Chef Marco" "Chef Soo"). Face SVGs:
```
<svg viewBox="0 0 60 60"><circle cx="30" cy="30" r="26" fill="#FFF6E9" stroke="#231610" stroke-width="3"/>
<path d="M20 34 q10 8 20 0" .../> mustache for Marco: <path d="M22 36 q4 -4 8 0 q4 -4 8 0" stroke ink fill none/>
eyes: dots. Soo: glasses circles.
```
Simple: Marco: eyes dots + mustache; Soo: glasses + smile. Good.
Footer: ink bg cream text: big display line clamp(2.2rem,5vw,4rem) "SEE YOU AT THE WINDOW." Then grid: form + 3 cols. Form: cream input + red button. Social buttons chips.
Cart panel CSS: fixed right top bottom, width min(400px, 92vw), bg cream, border-left 3 ink? panel z 60; transform translateX(105%) transition .35s cubic; .open → 0. Overlay: fixed inset 0 rgba(35,22,16,.45) opacity 0 pointer-events none; .show → 1 + events.
Toasts: fixed bottom 20 center; toast: bg ink color cream border? shadow red; transform translateY(12px) opacity 0 → show.
Cart badge: absolute -6 -6 red circle cream text w18 h18 display:none → .show flex. pop keyframes scale 1→1.3→1.
Fine. Also `.btn` focus-visible outline for a11y: outline 3px dashed red offset 3.
Now responsive:
@media (max-width: 980px): hero grid → 1 col; hero art order? copy first then art — art after copy. center text? keep left. stats wrap. truck grid 1 col. footer cols wrap.
@media (max-width: 680px): nav links display none; header height same; title bigger relative ok; menu grid minmax 100%? auto-fill minmax(260px,1fr) → 1 col at 375? 375-48=327 >260 so maybe 1 col since 260*2=520>327 → yes 1 col. good. schedule rows: grid-template columns 1fr with wrap? Rows: `display:grid;grid-template-columns:48px 1fr auto` may cramp 375: day | hours | spot. At 375: 48+100+~150 tight; make spot full width: media 520px: srow → block/stack with flex wrap: day+hours inline, spot full line. I'll set `.srow{grid-template-columns:56px 110px 1fr}` and at small `grid-template-columns:56px 1fr; em spans 2`. Fine details.
Hero at 375: title 3.5rem ok.
Cart panel width 92vw.
Let me also add `#top` id on body top for brand link.
I think structure is settled. Now — WRITE. I'll produce the final file. Length will be big; that's fine. Let me draft final code text now, carefully, in order. I'll be extra careful with JS syntax and matching braces/quotes.
For the mini dish icons (symbols):
```html
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<defs>
<symbol id="i-taco" viewBox="0 0 24 24">
<path d="M3.5 12a8.5 8.5 0 0 0 17 0" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M4.5 12c4 2.6 11 2.6 15 0" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<circle cx="8" cy="9.4" r="1.4" fill="currentColor"/><circle cx="12" cy="8.4" r="1.4" fill="currentColor"/><circle cx="16" cy="9.4" r="1.4" fill="currentColor"/>
</symbol>
```
Hmm path `M3.5 12a8.5 8.5 0 0 0 17 0`: arc from (3.5,12) to (20.5,12) rx ry 8.5 sweep 0 → CCW → below. Yes lower semicircle. Then inner lip curve from (4.5,12) to (19.5,12) with quad to center — draw `M4.5 12c4 2.6 11 2.6 15 0` → cubic: end x=19.5 ✓ dips ~y+2.6→ shell. dots above at y 8-9.4 between arc top (12-8.5=3.5) and lip — dots at y 8-9 sit inside opening ✓.
- i-burrito:
```
<symbol id="i-burrito" viewBox="0 0 24 24">
<rect x="5" y="7" width="14" height="9.5" rx="4.75" fill="none" stroke="currentColor" stroke-width="2"/>
<path d="M8.5 7.5l1.5 8.5M12 7.5v8.5M15.5 7.5l-1 8.5" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
</symbol>
```
rect rx 4.75 height 9.5 → pill; stripes diagonal-ish vertical wraps. OK.
- i-fries:
```
<symbol id="i-fries" viewBox="0 0 24 24">
<path d="M6 13l1.5 6.5h9L18 13z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
<path d="M9.5 13V6.5M12.5 13V5.5M15.5 13V7.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</symbol>
```
Box path: M6 13 → l1.5 6.5 → h9 → L18 13 → z: trapezoid ✓.
- i-wings (drumstick):
```
<symbol id="i-wings" viewBox="0 0 24 24">
<path d="M14.5 3.5a6.5 6.5 0 1 1-4 11.6l-3.2 3.2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<circle cx="6.2" cy="17.8" r="2.2" fill="none" stroke="currentColor" stroke-width="2"/>
<circle cx="9" cy="15" r="2.2" fill="none" stroke="currentColor" stroke-width="2"/>?
```
Hmm two bone knobs + meat arc. Meat: circle-ish blob at (14.5,9) r6.5 via arc path... Let me do: `<path d="M8.5 13.5A7 7 0 1 1 18 4c-1 3-3.5 5-7 6.5z"`? getting complex. Simpler drumstick: `<path d="M15 4a6.2 6.2 0 1 1-4.5 10.9L7 18.4a1.8 1.8 0 1 1-2.5-2.5L7.9 12" ...>` one path meat+leg; knobs as circles r1.6 fill at (5.5,17)(7.5,19). Let me write:
```
<path d="M15.5 4a6.3 6.3 0 1 1-5 10l-2.6 2.6a1.9 1.9 0 1 1-2.7-2.7l2.6-2.6A6.3 6.3 0 0 1 15.5 4Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
```
Arc with "A... 0 1 1" from (15.5,4)?? I wrote start M then `a` relative... Let me not over-engineer: it's a decorative circle-ish icon; I'll use: `<circle cx="14.5" cy="9" r="5.8" .../><path d="M10.4 13.2 6.8 16.8"/><circle cx="6" cy="17.6" r="1.7" .../><circle cx="8.4" cy="15.2" r="1.7"/>` — meat circle + angled line + 2 bone knobs overlapping = drumstick-ish. The two knob circles + line = bone; ok fine.
Wait `<circle>` in stroke-only symbols: fill none. knobs should be filled to look like bones: fill currentColor? knob fill: `fill:currentColor; stroke:none`? As separate circles fill currentColor — fine.
- i-queso (wedge):
```
<symbol id="i-queso" viewBox="0 0 24 24">
<path d="M3 17c3-8 8-12.5 15-13-.5 7-5 12.5-13 15z"?
```
Hmm wedge (quesadilla slice): triangle with curved hypotenuse: `M20 4C11 6 6.5 11 4.5 19c8-2 13-6.5 15.5-15z`? Meh. Let me do quarter circle wedge: `<path d="M20 4a16 16 0 0 0-16 16h16z" ...>` that's a big quarter — scale: viewBox 24: `M20 4 A16 16 0 0 0 4 20 L20 20 Z` = quarter pie with radius16 → too big? Points (20,4)→(4,20) radius 16 chord ~22.6 radius 16 ok — quarter disc filling 24 box roughly. Then inner arc smaller for rind: `M20 7A13 13 0 0 0 7 20`. dots inside. Actually a quesadilla wedge pointing... this reads as pie slice. Fine: pie-slice + 2 dots = quesadilla wedge. Use:
```
<path d="M20 4A16 16 0 0 0 4 20L20 20Z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/>
<circle cx="13" cy="14" r="1.3" fill="currentColor"/><circle cx="16" cy="17" r="1.3" fill="currentColor"/>
```
Hmm that quarter disc is huge (from 4..20). OK acceptable.
Actually flip wedge to corner look nicer rotated 45? skip.
- i-bowl:
```
<symbol id="i-bowl" viewBox="0 0 24 24">
<path d="M4 11c0 5 3.6 8 8 8s8-3 8-8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M4 11h16" ...? bowl rim: path M4 11h16 stroke.
<path d="M9 8c0-2 .5-2 .5-4M14 8c0-2 .5-2 .5-4M11.5 8c0-2 .5-2 .5-4" steam
```
bowl: semicircle down: `M4 11a8 8 0 0 0 16 0` + rim line `M4 11h16`... rim + arc creates closed bowl look with fill? fill none. steam curls above at y4-8. ok.
- i-chili (as defined):
```
<symbol id="i-chili" viewBox="0 0 24 24">
<path d="M14.8 5.2c-2 .3-3.6 1.4-5 3-2.8 3.3-2.5 8.3.8 10.3 3.6 2.1 8.2.3 9.3-3.6.6-2.1-.2-4.3-1.9-5.7" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
<path d="M14.8 5.2c.6-1.6 2.3-2.4 4-1.9" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
</symbol>
```
Hmm the body path: starts (14.8,5.2) curves... c-2 .3-3.6 1.4-5 3 → to (9.8,8.2); then -2.8 3.3 -2.5 8.3 .8 10.3 → to (10.6,18.5); then arcs to right... then 0.6 -2.1 -.2 -4.3 -1.9 -5.7 → ends at (14.8+... let me not verify to nth; it forms a hooked chili blob roughly. It'll look like a curved pepper. Accept.
Also i-star, i-bag, i-plus, i-close, i-arrow:
```
<symbol id="i-star" viewBox="0 0 24 24"><path d="M12 2.5l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.3l-5.9 3.2 1.2-6.5L2.5 9.4l6.6-.9z" fill="currentColor"/></symbol>
<symbol id="i-bag" viewBox="0 0 24 24"><path d="M5.5 8.5h13L17 20H7z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/><path d="M9 8.5a3 3 0 0 1 6 0" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></symbol>
<symbol id="i-close" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></symbol>
<symbol id="i-arrow" viewBox="0 0 24 24"><path d="M4 12h15M13 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></symbol>
```
Use `<svg class="ico"><use href="#i-star"/></svg>` — width/height via CSS.
Alright, final copywriting for ticket & toasts is set. Now write everything. I'll be careful about CSS var usage inside SVG (fill via class).
For the taco svg, salsa elements: `class="salsa"` → CSS `.salsa{fill:var(--salsa,#E23B2E)}`. Also cup ellipse class salsa. Drizzle path class salsa.
Steam container inside stage (not wrap, so tilt won't move steam — good, steam rises independently).
One more: title outline "LOCO" — I'll style `.t2{color:var(--red); text-shadow: .06em .06em 0 var(--ink)}` and maybe `paint-order`? fine.
Wait — I said "GET LOCO" on 2 lines: `GET` then `LOCO.` — with period? "LOCO." with red period? Just LOCO red. Add small rotated sticker near title: "HOT & FRESH" sticker? Hero eyebrow chips cover it.
Also add "★ 4.9" chip in header status area? Keep header clean: brand, nav (Menu · Truck · Hype), pill OPEN/CLOSED, cart.
Pill in header too redundant with hero status — keep small pill anyway (it's the real "makes people want to come" info!). Keep.
Time to produce final output. Writing now. (Estimating ~950 lines.)
Also double-check: `header nav a` links: #menu #truck #hype — sections have those ids with scroll-margin-top:104px.
Font-family display for buttons uppercase already.
For `--font` in SVG text (truck KIMCHI/LOCO) use attr font-family="Arial Black, Arial, sans-serif".
Let me handle "menu-grid" heading etc. Also add data-reveal to section heads & schedule & quotes.
Final check on constraint "no horizontal scrollbar": rotated divider width 100% + rotate → horizontal extent grows: width 100% rotated 1.6° → horizontal extent = w*cos+h*sin ≈ w + 3% * h small; with body overflow-x clip it's safe (clip prevents scrollbar). ✓.
Alright, writing the final answer now.
One more thought — the auto tween start value: slider initial value attribute 30; tween 30→62 over 2.4s starting at 900ms → at screenshot (t=3s) it's done at 3.3s — mid-tween at ~87%. Good, shows motion maybe blurred mid — applyHeat continuous so label settles "GOCHUJANG GANG" at 62. Nice.
Counters done by ~1.6s. Ticker running. Steam puffs visible periodically. Status computed instantly. Cart empty state hidden (badge hidden). All good.
Let me write out the code.
Actually, also — one subtle bug risk: `heatRead` initial text "SEOUL SIZZLE" — applyHeat runs on load so fine.
Another: the `pop` animation on cartCount uses class toggle each add — include `@keyframes pop`.
For the "loc" class on stage: `.stage.loco .sunburst{animation-duration:6s}` etc.
Writing!
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>KIMCHI LOCO — Korean Tacos · Seoul × Mexico City</title>
<meta name="description" content="Korean BBQ meets Mexico City street food. Find the truck, get the heat, get loco.">
<style>
...
</style>
</head>
<body id="top">
<svg width="0" height="0" ...symbols...>
<div class="topbar" id="topbar">
<div class="ticker"><div class="t-track" id="tickerA"></div></div>
<header class="header">
<a class="brand" href="#top">[taco mini svg] KIMCHI<b>LOCO</b></a>
<nav class="nav">
<a href="#menu">Menu</a><a href="#truck">Find the truck</a><a href="#hype">Hype</a>
</nav>
<div class="h-actions">
<span class="pill" id="pillTop">…</span>
<button class="cart-btn" id="cartBtn"><svg><use #i-bag></svg><span class="badge" id="cartCount">0</span></button>
</div>
</header>
</div>
...
```
Brand mini taco svg inline small (use i-taco symbol in brand with current color).
OK writing everything for real now. I'll produce it in final answer directly, keeping mental QA as I go.
Let me pre-write tricky CSS pieces:
```css
.ticker{overflow:hidden;background:var(--ink);color:var(--cream);border-bottom:3px solid var(--ink)}
.t-track{display:flex;width:max-content;animation:marquee 30s linear infinite}
.t-track>*{display:flex;align-items:center;gap:2.4em;padding-right:2.4em;font-size:11px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;white-space:nowrap}
```
Hmm build: each group is a span containing items? Let me build track children as: for seamless -50%: track has 2 identical halves. I'll create: `track.innerHTML = half+half` where `half = <span class="t-half">…items…</span>` each half display:flex. Then animate track translateX(-50%). Each half shrink-0.
```css
.t-half{display:flex;flex:none;align-items:center}
.t-half b{...}` items as <b> with star separators <i>
```
Item markup: `<b>BULGOGI TACOS</b><i>★</i>` repeated. star i color red? On ink bg red stars pop.
Divider ticker: red bg, cream text, reverse direction (`animation-direction:reverse`), font display bigger.
Marquee keyframes: `@keyframes marquee{to{transform:translateX(-50%)}}`.
Track width = 2 halves exactly → -50% seamless ✓.
Header:
```css
.header{max-width:1160px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;padding:10px 24px;background:var(--cream);border-bottom:3px solid var(--ink)}
```
Wait topbar fixed full width: bg cream needed behind header. `.topbar{position:fixed;inset:0 0 auto 0;z-index:50;background:var(--cream);}` ticker inside topbar too. Topbar bg cream; ticker has ink bg strip full width. Header inside max-width. Good.
body padding-top: 34+? compute: ticker ~26px + header 58 → 84 → padding-top 96.
Hero:
```css
.hero{padding:56px 24px 80px;background:radial-gradient(circle at 12% 8%, rgba(255,197,61,.35), transparent 34%), radial-gradient(circle at 88% 90%, rgba(226,59,46,.12), transparent 40%), var(--cream)}
```
plus dots pattern? maybe pattern via body. Keep hero bg cream with soft radial tints — tasteful.
Hmm dots: I'll add on menu ink section a subtle pattern? `background-image: radial-gradient(rgba(255,246,233,.07) 1.5px, transparent 1.5px); background-size: 22px 22px;` nice texture on ink.
Grid: `.hero-inner{max-width:1160px;margin:0 auto;display:grid;grid-template-columns:1.05fr .95fr;gap:48px;align-items:center}`.
Title:
```css
.hero-title{font-family:var(--display);text-transform:uppercase;line-height:.88;letter-spacing:-.01em;font-size:clamp(64px,8.2vw,120px);margin:18px 0 22px}
.hero-title .t2{color:var(--red);text-shadow:.05em .05em 0 var(--ink);display:block}
```
Eyebrow chips:
```css
.chips{display:flex;gap:10px;align-items:center;font-weight:900;font-size:12px;letter-spacing:.12em}
.chip{border:2.5px solid var(--ink);padding:6px 12px;border-radius:999px;background:var(--yellow);box-shadow:3px 3px 0 var(--ink)}
.chip.red{background:var(--red);color:var(--cream)}
```
Buttons:
```css
.btn{display:inline-flex;align-items:center;gap:10px;font-family:var(--display);text-transform:uppercase;letter-spacing:.02em;font-size:15px;padding:14px 22px;border:3px solid var(--ink);border-radius:12px;background:var(--red);color:var(--cream);box-shadow:var(--shadow);cursor:pointer;text-decoration:none;transition:transform .15s, box-shadow .15s}
.btn:hover{transform:translate(-2px,-2px);box-shadow:10px 10px 0 var(--ink)}
.btn:active{transform:translate(3px,3px);box-shadow:2px 2px 0 var(--ink)}
.btn.ghost{background:var(--cream);color:var(--ink)}
```
Stats:
```css
.stats{display:flex;gap:26px;margin-top:30px;flex-wrap:wrap}
.stats b{display:block;font-family:var(--display);font-size:30px;color:var(--red);line-height:1}
.stats span{font-size:12.5px;letter-spacing:.05em;text-transform:uppercase;font-weight:700}
```
Hero status line: `.hero-status{margin-top:8px;font-size:15px}` with `.dot` inline.
Stage:
```css
.stage{position:relative;width:min(430px,86vw);margin:0 auto}
.sunburst{position:absolute;left:50%;top:47%;width:96%;aspect-ratio:1;transform:translate(-50%,-50%);border-radius:50%;background:repeating-conic-gradient(var(--yellow) 0deg 15deg, #F8C64B? ...)}
```
Hmm sunburst colors: yellow & tortilla (#FFC53D and #F5B849 similar). Better: cream & yellow: `repeating-conic-gradient(var(--yellow) 0 18deg, var(--cream) 18deg 36deg)` — bold rays on cream hero. With ink border 4px + shadow red 10px. rotate slow. Yes.
`.taco-wrap{position:relative;z-index:2;width:100%;transition:transform .3s ease-out}` svg display block width 100%.
Heat card:
```css
.heat-card{margin-top:22px;background:#FFFDF6;border:3px solid var(--ink);border-radius:16px;padding:18px 20px;box-shadow:7px 7px 0 var(--red)}
.heat-head{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px}
.heat-head label{font-weight:900;text-transform:uppercase;font-size:13px;letter-spacing:.1em}
.heat-read{font-family:var(--display);font-size:13px;letter-spacing:.08em;border:2.5px solid var(--ink);padding:5px 12px;border-radius:999px;background:var(--lime)? no dynamic}
.heat-sub{font-size:14px;margin-top:10px;min-height:1.4em;color:#5c4a3d}
```
Menu section:
```css
.menu{background:var(--ink);color:var(--cream);padding:96px 24px 90px;background-image:radial-gradient(rgba(255,246,233,.08) 1.5px,transparent 1.5px);background-size:22px 22px}
.menu-grid{max-width:1160px;margin:44px auto 0;display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:26px}
.card{position:relative;background:var(--cream);color:var(--ink);border:3px solid var(--ink);border-radius:16px;padding:22px;box-shadow:7px 7px 0 var(--red);transition:transform .2s, box-shadow .2s}
.card:hover{transform:translate(-4px,-4px);box-shadow:11px 11px 0 var(--red)}
```
Card top: flex: icon badge 46px circle yellow border ink; name font display 17px; price chip red bg cream. desc 14px #5c4a3d. bottom: heat chilis (svg 16px) + label; add button small red.
Tag: absolute -top rotated sticker.
Drinks strip:
```css
.drinks{max-width:1160px;margin:34px auto 0;background:var(--cream2)? use #2A1B14 dark or cream? On ink bg: box with dashed border cream/50: background:#2E1e15? Use transparent with dashed border: border:2px dashed rgba(255,246,233,.45);border-radius:14px;padding:16px 20px;display:flex;gap:14px;flex-wrap:wrap;align-items:center
```
Text: "IN THE COOLER:" label red display + items list cream.
Truck:
```css
.truck{padding:88px 24px;background:var(--cream)}
.truck-grid{max-width:1160px;margin:0 auto;display:grid;grid-template-columns:1.12fr .88fr;gap:40px;align-items:start}
.road{margin-top:26px;background:#2A201A;border:3px solid var(--ink);border-radius:18px;padding:26px 18px 12px;position:relative;overflow:hidden}
.road-line{height:4px;background:repeating-linear-gradient(90deg, rgba(255,246,233,.5) 0 26px, transparent 26px 52px);margin:14px 0 6px;border-radius:2px}
```
Truck svg width min(400px, 86%) display block margin auto.
Status card: cream2 bg... it's on cream; use #FFFDF6 white-ish + shadow ink.
Schedule:
```css
.sched{background:#FFFDF6;border:3px solid var(--ink);border-radius:16px;box-shadow:7px 7px 0 var(--ink);overflow:hidden}
.srow{display:grid;grid-template-columns:52px 118px 1fr;gap:8px;padding:13px 16px;border-bottom:2px solid rgba(35,22,16,.12);align-items:center;position:relative}
.srow:last-child{border-bottom:0}
.srow b{font-family:var(--display);font-size:14px}
.srow.today{background:#FFE8C7}
.srow .now{position:absolute;right:12px;top:-9px;transform:rotate(-4deg);background:var(--red);color:var(--cream);font-size:10px;font-weight:900;letter-spacing:.1em;padding:4px 8px;border:2px solid var(--ink);border-radius:6px}
```
Hmm "TODAY" sticker overlapping row top — with overflow hidden on .sched it clips at top for first row; today row is rarely first (Mon=first, Sunday today → last). Today mid-list fine.
Wait .now absolute top -9 clipped by .sched overflow hidden — only matters for first row. Acceptable.
Hype:
```css
.hype{background:var(--red);color:var(--cream);padding:88px 24px;border-top:3px solid var(--ink);border-bottom:3px solid var(--ink);background-image:radial-gradient(rgba(35,22,16,.06)...) skip}
.quotes{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:24px;max-width:1160px;margin:44px auto 0}
.q{background:var(--cream);color:var(--ink);border:3px solid var(--ink);border-radius:16px;padding:24px;box-shadow:7px 7px 0 var(--ink);}
.q .stars{color:var(--yellow)? stars on cream: color #E8A70F? use var(--red)? Yellow on cream low contrast — use red stars or ink. I'll use #E8A70F dark-ish yellow? Just var(--yellow) might be low contrast; use red for stars. Actually classic: yellow stars on cream card — #F5A623? I'll use var(--yellow) with stroke? Keep `#EFA00B`. fine.
```
Badges row: chips with cream border on red bg.
Story:
```css
.story{background:var(--yellow);border-top:3px solid var(--ink);padding:64px 24px}
.story-inner{max-width:900px;margin:0 auto;text-align:center}
.story p{font-size:clamp(18px,2.4vw,24px);font-weight:600;line-height:1.45}
.founders{display:flex;gap:14px;justify-content:center;margin-top:22px}
.f-chip{display:flex;align-items:center;gap:8px;background:var(--cream);border:3px solid var(--ink);border-radius:999px;padding:6px 16px 6px 6px;font-weight:900}
```
Footer:
```css
footer{background:var(--ink);color:var(--cream);padding:80px 24px 28px;border-top:3px solid var(--ink)}
.f-big{font-family:var(--display);text-transform:uppercase;font-size:clamp(34px,6vw,64px);line-height:.95;color:var(--cream)}
.f-big em{color:var(--yellow);font-style:normal}
.f-grid{max-width:1160px;margin:48px auto 0;display:grid;grid-template-columns:1.3fr .7fr .7fr .9fr;gap:30px}
```
newsletter col + hours col + contact col + follow col.
Cart:
```css
.overlay{position:fixed;inset:0;background:rgba(35,22,16,.5);opacity:0;pointer-events:none;transition:.3s;z-index:60}
.overlay.show{opacity:1;pointer-events:auto}
.cart{position:fixed;top:0;right:0;bottom:0;width:min(400px,94vw);background:var(--cream);z-index:61;border-left:3px solid var(--ink);transform:translateX(103%);transition:transform .35s cubic-bezier(.22,1,.36,1);display:flex;flex-direction:column}
.cart.open{transform:none}
.cart-head{display:flex;justify-content:space-between;align-items:center;padding:18px 20px;border-bottom:3px solid var(--ink)}
.cart-body{flex:1;overflow-y:auto;padding:16px 20px}
.cart-foot{padding:18px 20px;border-top:3px solid var(--ink);background:#FFFDF6}
```
Ticket:
```css
.ticket{font-family:ui-monospace,"Courier New",monospace;background:#fff;border:3px dashed var(--ink);border-radius:12px;padding:18px;font-size:14px;line-height:1.7}
```
Toast:
```css
.toasts{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);display:flex;flex-direction:column;gap:10px;z-index:70;align-items:center;width:max-content;max-width:92vw}
.toast{background:var(--ink);color:var(--cream);border:3px solid var(--ink);box-shadow:5px 5px 0 var(--red);padding:12px 18px;border-radius:12px;font-weight:700;font-size:14px;opacity:0;transform:translateY(14px);transition:.3s}
.toast.show{opacity:1;transform:none}
.toast b{color:var(--yellow)}
```
Beep:
```css
.beep{position:absolute;left:50%;top:34%;transform:translate(-50%,-50%) rotate(-6deg);font-family:var(--display);background:var(--yellow);border:3px solid var(--ink);box-shadow:5px 5px 0 var(--ink);padding:10px 16px;border-radius:12px;animation:beep .9s ease forwards;z-index:3}
@keyframes beep{0%{opacity:0;transform:translate(-50%,-50%) rotate(-6deg) scale(.6)}25%{opacity:1;transform:translate(-50%,-50%) rotate(-6deg) scale(1.08)}70%{opacity:1}100%{opacity:0;transform:translate(-50%,-30%) rotate(-6deg) scale(1)}}
```
remove after 950ms.
Burst dots:
```css
.burst-dot{position:fixed;width:12px;height:12px;border-radius:50%;z-index:65;pointer-events:none;animation:burst .8s ease-out forwards}
@keyframes burst{from{transform:translate(0,0) scale(1);opacity:1}to{transform:translate(var(--tx),var(--ty)) scale(.3);opacity:0}}
```
Now, JS build details — I'll write it in the file directly. Final answer coming. Also ensure `use` elements: `<use href="#i-taco"></use>`.
For SVG icons inside buttons sized via CSS: `svg.ico{width:18px;height:18px;flex:none}` and `.chili{width:15px;height:15px}` etc.
Note `fill:none stroke:currentColor` symbols inherit color from CSS.
Let me also not forget: `.pill` in header:
```css
.pill{display:inline-flex;align-items:center;gap:8px;border:2.5px solid var(--ink);border-radius:999px;padding:6px 12px;font-size:11px;font-weight:900;letter-spacing:.12em;text-transform:uppercase;background:var(--cream2)}
.pill .dot{width:9px;height:9px;border-radius:50%;background:#9aa0a6}
.pill.open{background:#DFF3D8}
.pill.open .dot{background:#2FA84F;box-shadow:0 0 0 0 rgba(47,168,79,.6);animation:pulse 1.6s infinite}
@keyframes pulse{to{box-shadow:0 0 0 9px rgba(47,168,79,0)}}
```
Status card in truck section reuses similar.
Also hero status sentence `#heroStatus` with red underline text.
Alright, compose the final file. I must keep it coherent; let me write it fully now and double check as I go. I'll write the JS last.
For heat labels on cards:
HEAT_LABELS = ['no heat','gentle','spicy','certified loco'] index by m.heat (1..3).
Menu icons mapping: bulgogi→i-taco, gochujang→i-taco? two tacos same icon meh: gochujang chicken taco → i-taco too... acceptable but variety better. Options: bulgogi taco→i-taco; chicken taco→i-wings? No — tacos: use taco for both, differentiate via nothing. Eh fine: bulgogi→i-taco, burrito→i-burrito, quesadilla→i-queso, fries→i-fries, wings→i-wings, chicken taco→i-bowl? no. Give chicken taco the chili icon! "Gochujang chicken" → i-chili. Cute. bulgogi→i-taco, gochujang→i-chili, burrito→i-burrito, queso→i-queso, fries→i-fries, wings→i-wings.
Schedule data check with days mapping: JS getDay(): 0 Sun..6 Sat. SCHED entries d:1 Mon... d:0 Sun. Order in list Mon→Sun: order array Mon(1),Tue(2),Wed(3),Thu(4),Fri(5),Sat(6),Sun(0). ✓
fmtH(26): 26%24=2 →2PM?? Bug! 26 → '2AM' needed: 26%24=2 → h=2 → h>12 false → '2AM' ✓. fmtH(24): 24%24=0 → '12AM' ✓. fmtH(21)= 9PM ✓.
Status "until" for end 26 → fmtH(26)='2AM' ✓.
Also display hours strings: use fmtH(start)+'–'+fmtH(end): Fri '12PM–2AM' ✓.
Truck svg coordinates — let me finalize numbers:
viewBox="0 0 380 175".
- shadow ellipse cx190 cy162 rx160 ry8 fill rgba(35,22,16,.16)
- bob group contains everything except shadow:
- cargo box: rect x=26 y=44 w=252 h=76 rx=9 fill #FFFDF6 stroke ink sw4
- red stripe: rect x=26 y=100 w=252 h=20 fill var(--red) — but rect corners rx on box only; stripe square edges stick out over rounded corners? stripe y100 h20 → bottom at 120 = box bottom(44+76=120) — at corners box curves inward at rx 9; stripe would poke outside corners slightly. Set stripe x=26 w=252 but add stroke none; corner overlap negligible (rx 9, stripe full height corners? box bottom corners rounded at bottom: stripe bottom aligned to box bottom line — stripe square corners vs box rounded → stripe pokes 9px triangle at corners. Fix: stripe y=100 h=17 → 117, keep bottom inside (box bottom edge 120, corners rounding starts 111). Stripe at y100..117: at x edges 26..35 box side is straight until y111 (44+67). So stripe corner region y111..117 at x=26..? box side x=26 straight to y=111 then curves to bottom. At y117, box edge x ≈ 26+9-... circle center (35,111) r9: at y117, dx=sqrt(81-36)=6.7 → edge x=28.3. Stripe left edge 26 < 28.3 → pokes 2.3px. Invisible-ish at small scale — fine, or inset stripe: x=30 w=244 h=16 y=100 → bottom 116 edge… whatever, accept minor.
Simpler: make stripe x=26 w=252 h=14 y=100 (bottom 114): at y111+ curve: at y114: dx=sqrt(81-9)=8.49→edge x=26.5? center(35,111): y114 → dx = sqrt(81-9)=8.49 → x_edge=35-8.49=26.51. Stripe at x=26 → pokes 0.5px. Fine — invisible.
- text KIMCHI / LOCO at x=214 (between window right edge and box right edge): window x=88 w=96 → right edge 184. Text area 190..278: center ~230. font 19 Arial Black: "KIMCHI" ~6 chars*13.5=81 → 190..271 ✓. y: 68 and 90 (above stripe 100). ✓ Add taco mini icon? skip.
- window: rect x=88 y=58 w=96 h=42 rx=8 fill #231610 (ink) stroke ink? just fill ink. Menu lines inside: rects cream at x=98 y=68/76/84 w varying.
- awning: above window y=40..56: rect x=84 y=42 w=104 h=13 stroke ink fill #FFF6E9? Stripes: 4 rects inside w=13 each alternating red/lime at x=84,97,110,123... wait 104/4=26: stripes w26 at x84,110,136,162 h13 fill alternating var(--red), var(--lime) — then outline rect over. Add scallop bumps: circles? skip.
Wait awning overlaps box top (y44)? Awning at y42..55 sticks above box top 44 → nice 3D. But then roofline: fine.
Actually stripe colors: use --red and --yellow (lime odd on red truck? awning red+lime is very taco-truck. keep red/lime).
- OPEN sign: hangs from awning: outer g translate(136,55): line 55→63? sign rect x-20 y63 w40 h17 rx4 cream stroke3, text OPEN size 9 red x0 y75 anchor middle. Inner swing g class sign: contains line+rect+text with transform-origin top.
Hmm sign y 63..80 inside window area (58..100) ✓ visible against ink window. x 116..156 ✓.
- rooftop unit: rect x=140 y=28 w=40 h=16 rx=5 fill var(--red) stroke ink 3.5
- chimney: rect x=48 y=26 w=10 h=20 rx=3 fill #231610; smoke circles at (53,18) r5 & (57,10) r4 fill rgba(255,246,233,.35)? smoke cream-ish on cream hero bg — use ink .25. class smoke rise anim.
- cab: path `M278 58 h28 q18 3 24 18 l14 44 h-66 z` fill #FFFDF6 stroke ink 4 linejoin round → cab from x278 to 344 at bottom, top y58.. windshield: path `M286 64 h22 q11 2 15 11 l5 17 h-42 z` fill #23 6110? fill #3A2620 stroke ink 3.
check cab shape: M278 58 → h28 (→306,58) q18 3 24 18 → (330,76) l14 44 → (344,120) h-66 → (278,120) z. Box bottom at 120 ✓ aligned. Cab covers box right edge x278 (box ends 278) ✓ seamless.
- bumper: rect x=322 y=120 w=40? no. Skip.
- wheels: cy=132? box bottom 120, wheels r=17 cy=132 → top of wheel 115 inside box, bottom 149. shadow at y162 ✓. rear wheel cx=95, front cx=322 (under cab). Wheel: outer circle r17 fill #231610; hub circle r7.5 fill var(--cream); dot r2.5 fill ink.
- arch hints: skip.
Truck svg width in road: min(400px, 90%).
Beep sticker near truck: appended to .road.
Right — smoke anim: `.smoke{animation:rise 2.4s linear infinite}` `@keyframes rise{0%{transform:translate(0,0);opacity:0}20%{opacity:.5}100%{transform:translate(6px,-14px);opacity:0}}` with second circle delay 1.2s. SVG circle transform CSS fine.
Bob: `.t-bob{animation:bob 1.6s ease-in-out infinite alternate}@keyframes bob{to{transform:translateY(-3px)}}` — group wraps all except shadow.
Sign swing: `.sign{transform-box:fill-box;transform-origin:50% 0;animation:swing 2.8s ease-in-out infinite}@keyframes swing{0%,100%{transform:rotate(-4deg)}50%{transform:rotate(4deg)}}` — but sign g has no attr transform (parent g has translate) ✓.
Now the hero taco svg final markup — assemble everything discussed. One change: add drop shadow filter? CSS drop-shadow via --glow on wrap ✓.
Flames: wrap `<g transform="translate(170,238)">` then inner `<g class="flames">` paths. CSS `.flames{opacity:var(--fo,0);transform-box:fill-box;transform-origin:50% 100%;animation:flick .45s ease-in-out infinite alternate}` `@keyframes flick{from{transform:scaleY(1)}to{transform:scaleY(1.12) scaleX(.94)}}`. --fo set on stage → inherits ✓.
Now, is `.flames` CSS transform overriding needed attrs? inner g has none ✓.
Steam box: `.steam{position:absolute;top:-4%;left:22%;width:56%;height:24%;pointer-events:none;z-index:3}` puffs `i{position:absolute;bottom:0;background:#fff;border:2.5px solid var(--ink);border-radius:50%;opacity:0;animation:puff var(--dur) ease-out forwards}` `@keyframes puff{from{opacity:.85;transform:translateY(0) scale(.7)}to{opacity:0;transform:translateY(var(--dy)) scale(1.5)}}`.
Hmm outlined white puffs may read as bubbles; use borderless rgba(255,255,255,.85)? On sunburst bg, soft white circles = steam ✓ better borderless with slight blur? blur filter costs perf minor. Use plain white 80% opacity circles — good.
OK — also `#tacoWrap` tilt transform vs animation conflicts: none (animations are on stage svg inner loco class). ✓
Loco: `.stage.loco #tacoSvg{animation:wig .3s linear infinite}` transform-box fill-box origin center: `@keyframes wig{0%,100%{transform:rotate(-1.5deg)}50%{transform:rotate(1.5deg)}}`.
Sunburst: `.stage.loco .sunburst{animation-duration:8s}` default 60s.
Headline loco? keep to stage only.
Now the JS — final full write:
```js
<script>
(function(){
"use strict";
const $=(s)=>document.querySelector(s);
const $$=(s)=>[...document.querySelectorAll(s)];
const reduced=matchMedia("(prefers-reduced-motion: reduce)").matches;
/* DATA */
const MENU=[
{id:"bulgogi",name:"Bul-Go-Gee Taco",desc:"Three-week marinated short rib, sesame slaw, kimchi pico, lime crema.",price:4.50,heat:1,tag:"FAN FAVORITE",icon:"taco"},
{id:"gochujang",name:"Gochujang Chicken Taco",desc:"Double-fried thigh brushed with sweet-fire gochujang, pickled daikon, crushed nori.",price:4.75,heat:2,tag:null,icon:"chili"},
{id:"loco",name:"El Loco Burrito",desc:"Bulgogi + day-old fried rice + stretchy queso fresco + crispy nori shards.",price:11,heat:2,tag:"THE BIG ONE",icon:"burrito"},
{id:"queso",name:"Kimchi Quesadilla",desc:"Aged cheddar + charred kimchi, toasted till the edges shatter. Gochujang sour cream dip.",price:8,heat:1,tag:"VEGGIE",icon:"queso"},
{id:"fries",name:"Seoul Street Fries",desc:"Fries buried under bulgogi bits, mozzarella sauce, doenjang mayo, scallion snow.",price:7.50,heat:1,tag:null,icon:"fries"},
{id:"wings",name:"Buldak 'Crybaby' Wings",desc:"Six wings. Soy-garlic or buldak fire. Comes with a look of genuine concern.",price:9,heat:3,tag:"SIGNATURE",icon:"wings"}
];
const HEAT_LABEL=["","mild","spicy","certified"];
const SCHED=[
{d:1,day:"Mon",start:11,end:21,spot:"Ridgeline Brewing"},
{d:2,day:"Tue",start:11,end:21,spot:"Ridgeline Brewing"},
{d:3,day:"Wed",start:17,end:23,spot:"Grand Central Food Park"},
{d:4,day:"Thu",start:17,end:24,spot:"Grand Central Food Park"},
{d:5,day:"Fri",start:12,end:26,spot:"Night Market · 8th & Main"},
{d:6,day:"Sat",start:12,end:26,spot:"Night Market · 8th & Main"},
{d:0,day:"Sun",start:11,end:18,spot:"Fowler St Farmers Market"}
];
const fmtH=(h)=>{h=((h%24)+24)%24; if(h===0)return"12AM"; if(h===12)return"12PM"; return h>12?(h-12)+"AM":h+"AM";};
```
wait h>12 → PM: h=13 →1PM ✓; h<12 → AM ✓.
Hmm h=26 → 26%24=2 → 2AM ✓.
```js
/* TICKERS */
function fillTicker(el,items,sepClass){...}
```
Implement:
```js
function buildTicker(trackId, items){
const track=document.getElementById(trackId);
const half=items.map(t=>`<b>${t}</b><i>★</i>`).join("");
track.innerHTML=half+half;
}
buildTicker("tickerA",["BULGOGI TACOS","KIMCHI FRIES","GOCHUJANG WINGS","SOJU SLUSHIES","OPEN LATE","EL LOCO BURRITO","BULDAK WINGS","YUJA-ADE"]);
buildTicker("tickerB",["COMPLIMENTS TO THE CHEF","TACO TUESDAY? EVERY DAY","HEAT MARKED HONESTLY","PRESSED TO ORDER","'TIL 2AM FRIDAY","GET LOCO"]);
```
CSS for both: `.t-track{...}` items `b{...}` star `i{color:var(--red)}` on ink ticker; divider ticker red bg → star cream.
Need each half wide enough: 8 items * ~150px ≈ 1200 ✓ for A; B 6 items ~1100? "COMPLIMENTS TO THE CHEF" long... ok ~1200. Fine.
/* STATUS */
```js
function statusNow(){
const now=new Date(),dow=now.getDay(),t=now.getHours()+now.getMinutes()/60;
const today=SCHED.find(s=>s.d===dow);
if(today&&t>=today.start&&t<today.end)return{open:true,spot:today.spot,until:fmtH(today.end)};
for(let k=1;k<8;k++){
const s=SCHED.find(x=>x.d===(dow+k)%7);
if(s)return{open:false,spot:s.spot,next:(k===1?"tomorrow ":s.day+" ")+fmtH(s.start)};
}
return{open:false,spot:"the usual corner",next:"soon"};
}
```
Wait k=1 tomorrow but if today's session already passed... but also "later today" case: if today t < today.start → "today at X". Add before loop:
```js
if(today && t<today.start) return{open:false,spot:today.spot,next:"today "+fmtH(today.start)};
```
And loop starts k=1 → tomorrow... k from 1: (dow+k)%7 tomorrow's session ✓. Phrase: k===1 → "tomorrow at X", else "on Day at X": `${k===1?"tomorrow":s.day} at ${fmtH(s.start)}`. But k could hit a day with no session — all days have sessions ✓ so first k=1 always returns. Simplify loop to k=1 only... but keep loop anyway robust.
```js
function renderStatus(){
const s=statusNow();
const pill=$("#pillTop");
pill.classList.toggle("open",s.open);
pill.innerHTML=`<span class="dot"></span>${s.open?"OPEN NOW":"CLOSED"}`;
$("#heroStatus").innerHTML=s.open
?`<b class="open-t">We're slinging RIGHT NOW</b> at ${s.spot} — griddle hot 'til ${s.until}.`
:`Griddle's off right now. <b class="closed-t">Next stop: ${s.spot}, ${s.next}.</b>`;
const sc=$("#statusCard");
sc.classList.toggle("open",s.open);
sc.innerHTML=`<span class="dot big"></span><div><b>${s.open?"OPEN — COME HUNGRY":"CLOSED — NAPPING THE GRIDDLE"}</b>
<span>${s.open?`Sliding at ${s.spot} 'til ${s.until}. The smell finds you before the truck does.`:`Back at ${s.spot} ${s.next}. The kimchi is fermenting patiently in the meantime.`}</span></div>`;
}
```
/* MENU */
```js
const menuGrid=$("#menuGrid");
menuGrid.innerHTML=MENU.map((m,i)=>`
<article class="card reveal" style="--d:${i*60}ms">
${m.tag?`<span class="tag">${m.tag}</span>`:""}
<div class="card-top">
<span class="dish"><svg class="ico" aria-hidden="true"><use href="#i-${m.icon}"/></svg></span>
<h3>${m.name}</h3><span class="price">$${m.price.toFixed(2)}</span>
</div>
<p class="desc">${m.desc}</p>
<div class="card-bot">
<span class="heatcheck">${[0,1,2].map(k=>`<svg class="chili${k<m.heat?"":" off"}" aria-hidden="true"><use href="#i-chili"/></svg>`).join("")}<em>${HEAT_LABEL[m.heat]}</em></span>
<button class="add" data-add="${m.id}">Add +</button>
</div>
</article>`).join("");
```
/* SCHEDULE */
```js
const todayD=new Date().getDay();
$("#schedList").innerHTML=SCHED.map(s=>`
<div class="srow${s.d===todayD?" today":""}">
<b>${s.day}</b><span>${fmtH(s.start)}–${fmtH(s.end)}</span><em>${s.spot}</em>
${s.d===todayD?'<i class="now">TODAY</i>':""}
</div>`).join("");
```
/* HEAT */
```js
const stage=$("#stage"),slider=$("#heatSlider"),heatRead=$("#heatRead"),heatSub=$("#heatSub");
const STOPS=[[155,196,59],[255,197,61],[247,127,31],[226,59,46],[158,18,24]];
function salsa(v){const t=v/100*(STOPS.length-1),i=Math.min(STOPS.length-2,Math.floor(t)),f=t-i;
const c=STOPS[i].map((x,k)=>Math.round(x+(STOPS[i+1][k]-x)*f));return"rgb("+c.join(",")+")";}
const HEATS=[[24,"SO GENTLE","Soy-garlic energy. Abuela-approved. Halmoni-approved."],
[49,"SEOUL SIZZLE","A polite little kick. You will be fine. Probably."],
[74,"GOCHUJANG GANG","The sweet-fire paste does its thing. Napkins within reach."],
[94,"BULDAK BRAVE","Sweating at the window is a normal Tuesday here."],
[101,"EL LOCO","Sign the flavor waiver at the counter. We check ID and soul."]];
let heat=+slider.value;
function applyHeat(v){
heat=v;
const c=salsa(v);
stage.style.setProperty("--salsa",c);
stage.style.setProperty("--glow",(4+v*.20).toFixed(1)+"px");
stage.style.setProperty("--fo",Math.max(0,(v-55)/45).toFixed(2));
const L=HEATS.find(h=>v<=h[0]);
heatRead.textContent=L[1];
heatRead.style.background=c;
heatRead.style.color=v>52?"#FFF6E9":"#231610";
heatSub.textContent=L[2];
stage.classList.toggle("loco",v>=95);
}
slider.addEventListener("input",()=>{userTouched=true;applyHeat(+slider.value)});
let userTouched=false;
applyHeat(+slider.value);
```
Order: userTouched declared before listener (hoist let issue — listener callback references later-declared let fine since executed after). But applyHeat(heat) before tween. Add `let userTouched=false;` before listener assignment. I'll organize: define userTouched first.
Auto tween:
```js
if(!reduced){
setTimeout(()=>{ if(userTouched)return;
const from=+slider.value,to=62,t0=performance.now(),dur=2200;
const tick=(t)=>{ if(userTouched)return; const p=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-p,3);
slider.value=from+(to-from)*e; applyHeat(+slider.value);
if(p<1)requestAnimationFrame(tick);};
requestAnimationFrame(tick);
},900);
}
```
Also pointerdown sets userTouched:
```js
slider.addEventListener("pointerdown",()=>userTouched=true);
```
/* STEAM */
```js
const steamBox=$("#steam");
function puff(){
const p=document.createElement("i");
p.style.left=(10+Math.random()*80)+"%";
p.style.width=p.style.height=(9+Math.random()*13)+"px";
p.style.setProperty("--dy",-(60+Math.random()*70)+"px");
p.style.setProperty("--dur",(1.5+Math.random()*1.3)+"s");
p.addEventListener("animationend",()=>p.remove());
steamBox.appendChild(p);
}
if(!reduced){ (function loop(){ puff(); setTimeout(loop, 300+Math.max(0,(100-heat)*3)); })(); }
```
Wait heat increases → faster: interval = 300 + (100-heat)*3 → heat 100 → 300ms; heat 0 → 600ms. Reasonable. Hmm reversed niceness fine.
Animation end name: the animation shorthand uses var --dur in animation property? CSS `animation: puff var(--dur) ease-out forwards` — var in animation duration OK.
/* TILT */
```js
const tacoWrap=$("#tacoWrap");
if(matchMedia("(pointer:fine)").matches){
stage.addEventListener("pointermove",e=>{
const r=stage.getBoundingClientRect();
const dx=(e.clientX-r.left)/r.width-.5,dy=(e.clientY-r.top)/r.height-.5;
tacoWrap.style.transform=`rotate(${(dx*5).toFixed(2)}deg) translate(${(dx*10).toFixed(1)}px,${(dy*8).toFixed(1)}px)`;
});
stage.addEventListener("pointerleave",()=>tacoWrap.style.transform="");
}
```
/* CART */
```js
const cart={};
const cartBtn=$("#cartBtn"),cartCount=$("#cartCount"),panel=$("#cartPanel"),overlay=$("#overlay"),cartBody=$("#cartBody"),cartFoot=$("#cartFoot"),toasts=$("#toasts");
function toast(html){const t=document.createElement("div");t.className="toast";t.innerHTML=html;toasts.appendChild(t);requestAnimationFrame(()=>t.classList.add("show"));setTimeout(()=>{t.classList.remove("show");setTimeout(()=>t.remove(),350)},2600);}
function addToCart(id){
cart[id]=(cart[id]||0)+1;
renderCart();
const m=MENU.find(x=>x.id===id);
toast(`<b>${m.name}</b> added — good call.`);
cartCount.classList.remove("pop");void cartCount.offsetWidth;cartCount.classList.add("pop");
}
function renderCart(){
const ids=Object.keys(cart);
const n=ids.reduce((a,id)=>a+cart[id],0);
cartCount.textContent=n;
cartCount.classList.toggle("show",n>0);
if(!n){cartBody.innerHTML=`<p class="empty">Nothing yet.<br>The tacos can feel ignored, you know.</p><p class="empty2">Hit <b>Add +</b> on anything that sounds loud.</p>`;cartFoot.innerHTML="";return;}
let total=0;
const rows=ids.map(id=>{const m=MENU.find(x=>x.id===id),q=cart[id];total+=m.price*q;
return `<div class="crow"><div class="crow-l"><b>${m.name}</b><span>$${(m.price*q).toFixed(2)}</span></div>
<div class="stepper"><button data-step="-1" data-id="${id}" aria-label="less">−</button><span>${q}</span><button data-step="1" data-id="${id}" aria-label="more">+</button></div></div>`;}).join("");
cartBody.innerHTML=rows;
cartFoot.innerHTML=`<div class="ctotal"><span>Subtotal</span><b>$${total.toFixed(2)}</b></div>
<button class="btn fire" id="fireBtn">Fire up my order</button>
<p class="cnote">Pay at the window — cash, card, or your dignity.</p>`;
}
function openCart(){panel.classList.add("open");overlay.classList.add("show");}
function closeCart(){panel.classList.remove("open");overlay.classList.remove("show");}
cartBtn.addEventListener("click",openCart);
$("#cartClose").addEventListener("click",closeCart);
overlay.addEventListener("click",closeCart);
addEventListener("keydown",e=>{if(e.key==="Escape")closeCart();});
cartBody.addEventListener("click",e=>{
const b=e.target.closest("[data-step]");if(!b)return;
const id=b.dataset.id,d=+b.dataset.step;
cart[id]+=d; if(cart[id]<=0)delete cart[id];
renderCart();
});
menuGrid.addEventListener("click",e=>{
const b=e.target.closest("[data-add]");if(b)addToCart(b.dataset.add);
});
```
Ticket state: renderCart is called after steppers; but if ticket shown and user modifies... ticket only via fireBtn; after ticket, cartBody replaced; renderCart would overwrite if called later — cart unchanged until "Done" resets. Fine.
```js
cartFoot.addEventListener("click",e=>{
if(e.target.closest("#fireBtn"))checkout();
if(e.target.closest("#doneBtn")){for(const k in cart)delete cart[k];renderCart();closeCart();}
});
function burst(x,y,colors){
for(let i=0;i<14;i++){
const d=document.createElement("i");d.className="bdot";
const a2=Math.random()*Math.PI*2,r=40+Math.random()*90;
d.style.cssText=`left:${x}px;top:${y}px;background:${colors[i%colors.length]};--tx:${Math.cos(a2)*r}px;--ty:${Math.sin(a2)*r-40}px;`;
document.body.appendChild(d);setTimeout(()=>d.remove(),900);
}
}
function checkout(){
const items=Object.keys(cart),s=statusNow();
let total=0;const lines=items.map(id=>{const m=MENU.find(x=>x.id===id),q=cart[id];total+=m.price*q;return `${q} × ${m.name}`;});
const num="LOCO-"+(100+Math.floor(Math.random()*900));
const where=s.open?s.spot:`${s.spot} (${s.next})`;
cartBody.innerHTML=`<div class="ticket">
<div class="tk-head">★ KIMCHI LOCO ★</div>
<div class="tk-num">ORDER #${num}</div>
${lines.map(l=>`<div>${l}</div>`).join("")}
<div class="tk-total">TOTAL $${total.toFixed(2)}</div>
<div class="tk-line"></div>
<div>READY IN ~8 MIN AT</div><div class="tk-where">${where}</div>
<div class="tk-line"></div>
<div class="tk-small">Show this at the window. Yell “LOCO!” for 10% off. (Started as a joke. It stuck.)</div>
</div>`;
cartFoot.innerHTML=`<button class="btn ghost2" id="doneBtn">Start another round</button>`;
const r=$("#fireBtn")?.getBoundingClientRect(); // fireBtn no longer exists after foot replaced...
```
Oops — after replacing foot HTML, fireBtn gone. Capture rect BEFORE replacing:
```js
const r=(e.target closest rect)...
```
In checkout I don't have event. Pass rect from click handler: `cartFoot.addEventListener('click',e=>{const f=e.target.closest('#fireBtn'); if(f)checkout(f.getBoundingClientRect()); ...})`.
Then `function checkout(rect){ ... burst(rect.left+rect.width/2, rect.top+rect.height/2, ["#E23B2E","#FFC53D","#9BC53B","#FFF6E9"]); }` place burst before innerHTML changes — order irrelevant since fixed positioning. But burst coordinates viewport fixed ✓.
bdot CSS:
```css
.bdot{position:fixed;width:13px;height:13px;border-radius:50%;border:2px solid var(--ink);z-index:80;pointer-events:none;animation:burst .8s cubic-bezier(.2,.7,.3,1) forwards}
@keyframes burst{from{transform:translate(0,0) scale(1);opacity:1}to{transform:translate(var(--tx),var(--ty)) scale(.2);opacity:0}}
```
/* Newsletter */
```js
$("#newsForm").addEventListener("submit",e=>{e.preventDefault();
const em=$("#newsInput"); if(!em.value)return;
toast(`<b>${escape?}</b>`) — avoid quoting issues; use textContent-ish: toast(`<b>You're in.</b> We'll slide into your inbox the next time the truck moves.`);
em.value="";});
```
Include email? skip.
/* misc buttons */
```js
$("#pinBtn").addEventListener("click",()=>toast(`<b>Pin dropped.</b> Look for the shiny silver truck & the short line that moves fast.`));
$$("#socials button").forEach(b=>b.addEventListener("click",()=>toast(`See you on <b>${b.dataset.name}</b> — same handle, same tacos.`)));
```
Hmm `$$` helper defined. b.dataset.name = "Instagram" etc.
/* reveal & counters */
```js
const io=new IntersectionObserver(es=>es.forEach(en=>{if(en.isIntersecting){en.target.classList.add("in");io.unobserve(en.target)}}),{threshold:.12});
$$(".reveal").forEach(el=>io.observe(el));
setTimeout(()=>{$$("[data-count]").forEach(el=>{
const target=parseFloat(el.dataset.count),dec=+(el.dataset.dec||0),t0=performance.now(),dur=1400;
(function tk(t){const p=Math.min(1,(t-t0)/dur),e=1-Math.pow(1-p,3),v=target*e;
el.textContent=dec?v.toFixed(dec):Math.round(v).toLocaleString("en-US");
if(p<1)requestAnimationFrame(tk);})(t0);
});},250);
```
/* topbar scroll + truck beep */
```js
addEventListener("scroll",()=>{$("#topbar").classList.toggle("scrolled",scrollY>10)},{passive:true});
const road=$("#road");
$("#truckSvg").addEventListener("click",()=>{
const b=document.createElement("div");b.className="beep";b.textContent="BEEP BEEP!";
road.appendChild(b);setTimeout(()=>b.remove(),950);
toast(`<b>Honk received.</b> Truck honors all honks with 10% more flavor.`);
});
```
Maybe toast every click annoying; alternate: only beep sticker no toast. Keep toast out — just beep.
/* status interval */
```js
renderStatus();setInterval(renderStatus,30000);
renderCart(); // initial
```
I need `#fireBtn` initial foot — renderCart handles empty.
Also initial `#pillTop` markup replaced by renderStatus ✓. `#statusCard` in truck section markup replaced ✓.
Double-check HTML ids present: topbar, tickerA, tickerB, pillTop, cartBtn, cartCount, heroStatus, stage, heatSlider, heatRead, heatSub, tacoWrap, steam, tacoSvg, menuGrid, schedList, statusCard, road, truckSvg, pinBtn, newsForm, newsInput, cartPanel, overlay, cartClose, cartBody, cartFoot, toasts, socials.
Also "hype" section id, "menu", "truck".
Reveal on: section heads, cards, quotes, sched, status card, story, etc. Add class reveal + optional --d inline.
CSS reveal:
```css
.reveal{opacity:0;transform:translateY(26px);transition:opacity .6s ease var(--d,0ms),transform .6s ease var(--d,0ms)}
.reveal.in{opacity:1;transform:none}
```
But cards have hover transform — after .in transform:none conflicts with hover translate? `.card:hover` transform overrides .reveal.in ✓ (specificity: .card:hover vs .reveal.in — both 0,2,0? .card:hover = class+pseudo-class = (0,2,0); .reveal.in = two classes (0,2,0) — later in stylesheet wins. Order: define .reveal rules BEFORE .card:hover? Then hover wins ✓. Put reveal CSS early. Also transition on card transform .2s vs reveal .6s: transition property conflict — card sets `transition: transform .2s, box-shadow .2s` overriding reveal's transition → reveal animates with .2s opacity?? `transition: transform .2s, box-shadow .2s` doesn't include opacity → opacity jumps. Hmm. Fix: card uses `transition: opacity .6s var(--d), transform .2s, box-shadow .2s`? Combine manually on .card:
```css
.card{transition:opacity .55s ease var(--d,0ms), transform .25s ease, box-shadow .25s ease}
```
And .reveal base uses opacity 0/translate; when .in added, transform transitions .25s (fast) — acceptable. Or simpler: don't put .reveal on cards themselves; wrap? Use only opacity for cards: `.reveal{opacity:0;transition:opacity...}` and translate via a separate class for heads. Let me define `.reveal{opacity:0;translate:0 26px;transition:opacity .6s var(--d),translate .6s var(--d)}` — using `translate` property (not transform) avoids conflicts with transform transitions entirely! `translate` property supported in Chrome 104+. And card:hover uses transform:translate(-4px,-4px) — independent of `translate` ✓.
So:
```css
.reveal{opacity:0;translate:0 24px;transition:opacity .6s ease var(--d,0ms),translate .6s ease var(--d,0ms)}
.reveal.in{opacity:1;translate:0 0}
```
Clean. Cards keep their own transition for transform/box-shadow.
Hero elements visible immediately (no reveal on hero) ✓ screenshot safe. Menu cards need scroll → they get reveal; fine.
Now heading styles + squiggle. Squiggle svg in headers:
```html
<svg class="squig" viewBox="0 0 220 14"><path d="M3 10c14-8 26-8 40 0s28 8 42 0 28-8 42 0 28 8 42 0 26-8 48 0" fill="none" stroke="var(--red)" stroke-width="5" stroke-linecap="round"/></svg>
```
Wait var() in SVG attr? As presentation attr stroke="var(--red)" — CSS var in presentation attribute doesn't work... Actually presentation attributes don't support var(); but style="stroke:var(--red)" does. Use `style` attr or CSS class. I'll set `stroke="currentColor"` and wrap CSS `color:var(--red)`. Fine.
Squiggle path: M3 10 then repeated cubic bumps: `c 14 -8 26 -8 40 0` bump up? ends each at +40 x. 5 segments → 3+200=203 ✓.
Section head markup:
```html
<div class="shead reveal"><span class="kicker">THE MENU</span><h2>Hot off the plancha</h2><svg class="squig">…</svg><p>Made to order...</p></div>
```
Kicker: red chip.
OK also decide `.h-actions .pill` hidden on very small? keep.
Now — is everything consistent for 1280×800 screenshot: topbar 96 + hero fits? Hero grid align-items center with padding 56/80: content ~ available 800-96=704 - paddings 136 → 568px height. Art column: sunburst 430 + heat card ~170 = 600 > 568 → slight overflow → hero grows taller than viewport → menu partially cut. Acceptable (scroll exists), but nicer if fold complete: reduce sunburst to min(360px, 42vw)? Let me size: stage width min(380px,86vw) → taco svg 380 tall ~ 380*(300/340)=335 + heat card ~150 → 485 ✓ fits 568. Set stage width 380. Title column similar height ✓. Good: hero fully in first viewport at 800h.
At 1280 hero grid: 1160 max, columns ~ 545/545. 380px stage centered in column — some whitespace, fine (sunburst adds). Maybe stage 400. ok 400.
Let me also add hero decorative absolute stickers: a rotated "EST. 2019" badge or "HOT!" starburst on stage corner: `★ GET IT ★` badge svg — skip, sunburst enough. Maybe add floating "since 2019" rotating text circle? Skip — complexity budget reached.
Footer newsletter: heading "GET THE DROP" text "One email a week max: where the truck is + what's bubbling."
Hours col: "Mon–Tue 11a–9p / Wed–Thu 5p–late / Fri–Sat 12p–2a / Sun 11a–6p".
Contact: tacos@kimchiloco.food · (555) 862-LOCO.
Follow: buttons IG/TikTok.
Bottom: "© 2025 Kimchi Loco LLC · Seoul × Mexico City · No tacos were harmed."
Now finalize responsive CSS:
```css
@media (max-width:1020px){.hero-inner{grid-template-columns:1fr;text-align:center;gap:34px}.chips{justify-content:center}.stats{justify-content:center}.hero-ctas{justify-content:center}.stage{margin:0 auto}}
@media (max-width:760px){.nav{display:none}.truck-grid{grid-template-columns:1fr}.f-grid{grid-template-columns:1fr 1fr}.quotes{...}}
@media (max-width:560px){.h-actions .pill{display:none}.srow{grid-template-columns:44px 1fr}.srow em{grid-column:2}.f-grid{grid-template-columns:1fr}.stats{gap:18px}}
```
Wait srow default columns 52px 118px 1fr; small: `grid-template-columns:52px 1fr;` and em (spot) `grid-column:1/-1`? Let me: default `.srow{grid-template-columns:52px 116px 1fr}` at ≤560: `.srow{grid-template-columns:52px 1fr} .srow em{grid-column:2}`? then spot under hours col. Actually make spot wrap: `grid-column:1/-1;`. With .now sticker absolute right. Let me define:
```css
.srow{display:grid;grid-template-columns:56px 118px 1fr;gap:10px;...}
@media(max-width:560px){.srow{grid-template-columns:56px 1fr}.srow>em{grid-column:1/-1;font-style:normal}}
```
spot full width second line ✓.
Hype quotes grid: repeat(auto-fit,minmax(250px,1fr)) ✓.
Menu grid minmax(290px,1fr) gap 26.
Cart rows styles:
```css
.crow{display:flex;justify-content:space-between;align-items:center;gap:12px;padding:12px 0;border-bottom:2px dashed rgba(35,22,16,.15)}
.crow-l b{display:block;font-weight:900;font-size:15px}
.crow-l span{font-size:13px;color:#8a6f57}
.stepper{display:flex;align-items:center;gap:8px}
.stepper button{width:30px;height:30px;border:2.5px solid var(--ink);background:var(--cream);border-radius:10px;font-weight:900;font-size:15px;cursor:pointer;box-shadow:2px 2px 0 var(--ink)}
.stepper button:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--ink)}
.ctotal{display:flex;justify-content:space-between;font-weight:900;margin-bottom:12px}
.fire{width:100%;justify-content:center}
.cnote{font-size:12px;text-align:center;margin-top:10px;color:#8a6f57}
.empty{text-align:center;padding:40px 10px;font-weight:700}
.empty2{...}
```
Ticket CSS:
```css
.ticket{background:#FFFFFF;border:3px dashed var(--ink);border-radius:14px;padding:20px;font-family:ui-monospace,"SFMono-Regular","Courier New",monospace;font-size:14px;line-height:1.8}
.tk-head{letter-spacing:.3em;font-weight:700;text-align:center}
.tk-num{font-weight:700;text-align:center;font-size:20px;margin:6px 0 10px}
.tk-total{font-weight:700}
.tk-line{border-top:2px dashed var(--ink);margin:10px 0}
.tk-where{font-weight:700;text-transform:uppercase}
.tk-small{font-size:12px;color:#6b5340}
```
OK. The `.btn.fire` white-ish? red default ✓ ghost2 for done: cream bg.
Heat card position: below stage within hero-art column: hero art = `<div class="hero-art"><div class="stage" id="stage">…</div><div class="heat-card">…</div></div>`. `.hero-art{display:flex;flex-direction:column;align-items:center;gap:0}` heat-card width 100%? width min(380px,100%). Good.
Sunburst & taco & steam inside stage. Stage margin auto.
Also `--salsa` default: `:root{--salsa:#E23B2E;--glow:10px;--fo:0}` to avoid unstyled flash before JS (JS runs immediately though — inline script at end). Set defaults in :root anyway ✓.
Flames inside svg get `opacity:var(--fo)` — var inherits from stage? --fo set on stage element style; svg is descendant → inherits ✓. But initial :root --fo:0 ✓.
Alright — also the divider `.divider` between hero & menu and maybe another before footer with reverse direction.
`.divider{background:var(--red);transform:rotate(-1.4deg);position:relative;z-index:3;margin:0 -14px;padding:12px 0;border-top:3px solid var(--ink);border-bottom:3px solid var(--ink);overflow:hidden}` margin negative pulls edges beyond → covers rounded corner gap. body overflow clip ✓.
Divider track: `.t-track.rev{animation-direction:reverse}` and for divider: bigger font (display), cream color, star ink? stars cream.
Ticker A (top, ink bg): font 11px.
Now assemble final HTML. Also `html{scroll-padding-top}`: use `scroll-margin-top:110px` on sections: menu, truck, hype.
Also `:focus-visible` styles.
Let me finalize the heat slider CSS with both webkit & moz.
Also `.add` button:
```css
.add{font-family:var(--display);font-size:13px;text-transform:uppercase;letter-spacing:.06em;background:var(--red);color:var(--cream);border:3px solid var(--ink);border-radius:10px;padding:9px 14px;box-shadow:3px 3px 0 var(--ink);cursor:pointer;transition:transform .15s,box-shadow .15s}
.add:hover{transform:translate(-2px,-2px);box-shadow:5px 5px 0 var(--ink)}
.add:active{transform:translate(2px,2px);box-shadow:0 0 0 var(--ink)}
```
heatcheck: `display:flex;align-items:center;gap:4px;color:var(--red-deep)` `.chili.off{color:#D8CBB4}` `em{font-style:italic;font-weight:800;font-size:12px;margin-left:6px;color:#8a6f57}`.
price chip: `background:var(--yellow);border:2.5px solid var(--ink);padding:4px 10px;border-radius:999px;font-weight:900;font-family:var(--display);font-size:14px;box-shadow:2px 2px 0 var(--ink)`.
dish icon: `width:46px;height:46px;border:3px solid var(--ink);border-radius:12px;background:var(--yellow);display:grid;place-items:center` svg 24px ink.
card-top: `display:grid;grid-template-columns:auto 1fr auto;gap:12px;align-items:center` name font display 16px lh 1.15. name might wrap 2 lines ✓.
tag: `.tag{position:absolute;top:-13px;right:14px;transform:rotate(3deg);background:var(--red);color:var(--cream);font-size:11px;font-weight:900;letter-spacing:.1em;padding:5px 10px;border:2.5px solid var(--ink);border-radius:8px;box-shadow:3px 3px 0 var(--ink)}`.
Quote cards: `.q{...}` `.q .stars{display:flex;gap:3px;color:#EFA00B;margin-bottom:10px}` svg 16. `.q p{font-size:16.5px;font-weight:600;line-height:1.5}` `.q b{display:block;margin-top:12px;font-size:12.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--red-deep)}`.
Badges: `.badges{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-top:34px}` `.badge{border:2.5px solid var(--cream);color:var(--cream);padding:8px 14px;border-radius:999px;font-weight:800;font-size:12px;letter-spacing:.1em;text-transform:uppercase}`.
Story faces svg inline (two small svgs 40px).
OK final: menu "IN THE COOLER" strip content:
"YUJA-ADE · WATERMELON SOJU SLUSHY (21+) · MEXICAN COKE · HORCHATA MAKGEOLLI FLOAT · CUCUMBER LIMEADE · HOT TEA WHEN IT'S COLD"
Now let me also decide hero right side order in DOM: after copy column. For 375px stacking: copy then art ✓.
Also hero eyebrow chips: "SEOUL BBQ" (red chip) "×" (ink) "MEXICO CITY STREETS" (yellow chip).
hero title: GET / LOCO — maybe with sticker inline after LOCO: small rotated chip "since '19"? skip.
Lede text final: "Bulgogi in your tortilla. Kimchi in your quesadilla. Sesame smoke over a screaming-hot plancha at 2AM. We're a silver truck with a serious flavor problem — and you're about to catch it."
CTA: [Find today's truck] [Peep the menu →]. Status line under CTAs.
Stats: 128,400 tacos slung · 4.9 avg from 2,140 reviews · 'til 2AM on weekends.
Data-count values: 128400 (int), 4.9 dec1, 2140. Labels: "tacos slung", "stars, 2,140 reviews" hmm: stat 2: b=4.9 span="avg ★, 2,140 reviews" — text has ★ char fine.
Third stat static b="'TIL 2AM" span="weekends". Make b font display 30 — "'TIL 2AM" fits.
Truck section left heading: kicker "FIND US" h2 "Where's the truck?" squiggle + status card + road + note "We post the exact pin on Instagram at 10AM daily. Rain or shine. If it's really coming down, look across the street — we'll be in the bar."
pinBtn in right column under schedule: full-width btn "Drop me a pin".
Right column also heading? Schedule panel top label: "THE WEEKLY ROUT" small display + rows.
Hype: kicker "HYPE" h2 "The people are yelling (nicely)".
Menu: kicker "THE MENU" h2 "Hot off the plancha".
OK, writing final HTML now. Also remember to include symbols svg block. And `#i-...` symbols: taco, chili, burrito, queso, fries, wings, bag, close, arrow, star.
Double check burrito/wings/queso symbol paths once more... they're fine-ish. For wings simplify to meat circle + bone:
```
<symbol id="i-wings" viewBox="0 0 24 24">
<path d="M17.5 13.5 11 20" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" fill="none"/>
<circle cx="19" cy="16.5" r="1.6" fill="currentColor"/><circle cx="16.8" cy="18.7" r="1.6" fill="currentColor"/>
<path d="M9 4.5a6 6 0 1 1-3.5 10.8A6 6 0 0 1 9 4.5Z" fill="none" stroke="currentColor" stroke-width="2.2"/>
</symbol>
```
Hmm meat path weird. Use circle cx=9 cy=9 r=6 stroke; bone line from (13.2,13.2)→(19,19)? diagonal to bottom-right, knobs at (19.5,17.5),(17.5,19.5). Let me: `<circle cx="9.5" cy="9.5" r="6.3" .../> <path d="M14 14l4.2 4.2"/> <circle cx="19.6" cy="16.8" r="1.7"/><circle cx="16.8" cy="19.6" r="1.7"/>` → drumstick ✓ (meat circle top-left, bone bottom-right).
Queso: quarter-pie wedge rotated? Use `<path d="M18.5 5.5A13 13 0 0 0 5.5 18.5L18.5 18.5Z">` — arc from (18.5,5.5) to (5.5,18.5) r13 sweep 0 → bulging left-down (CCW): chord length = sqrt(13²+13²)=18.4 ≤ 2r=26 ✓ valid arc, sweep 0 → arc bends away... which side? From start to end CCW → passes upper-left region? Center of circle... ugh. For A rx ry 0 0 0 with two points, sweep 0 = counterclockwise → arc on the left side of chord direction (chord from (18.5,5.5)→(5.5,18.5) pointing down-left; CCW arc → on the lower-right side? I keep flip-flopping. Test known: `M3.5 12a8.5 8.5 0 0 0 17 0` taco bottom arc — sweep 0 gave bottom (I asserted). Points (3.5,12)→(20.5,12), chord →right; CCW from start going right = arc below? Counterclockwise around circle center above chord → arc sweeps below? Center above chord: going CCW (negative angle direction in SVG y-down is... ) — SVG sweep 0 = "negative angle direction" (counterclockwise in standard math but y-axis flipped → visually counterclockwise? In SVG, sweep 1 = clockwise visually (positive rotation). For chord left→right: sweep 0 arc bulges DOWN? Let me verify with known example: rainbow arc `M4 12 a8 8 0 0 1 16 0` with sweep 1 draws upper arc (common in icons). Yes! Known: `M6 18 A 8 8 0 0 1 22 18`? Hmm commonly upward arc uses sweep 1 when going left→right... e.g. feather "cloud" `M17.5 19a4.5...` not helpful. Let me reason math: SVG arc sweep-flag=1 means arc drawn in "positive angle" direction, where positive = clockwise in screen coords (y down). From (3.5,12) to (20.5,12) r8.5: two possible centers: (12, 12±?) centers at (12,12) both? distance between pts=17=2r → only one center (12,12) — semicircle either top or bottom; sweep decides direction: sweep 1 → clockwise from start: starting (3.5,12) going clockwise around (12,12) → moves up first → top semicircle? Clockwise in screen: at angle 180° (left), clockwise motion (angle increasing in screen CW = angle decreasing math) → next point moves to bottom? Parametrize: point angle θ measured screen-CCW... I'll trust known example: the "smile" path `M2 8 a8 8 0 0 0 16 0`? Hmm MDN example `M 10 315 L 110 215 A 30 50 0 0 1 162.55 162.45`...
Simplest: avoid relying on direction — test with quarter circle for queso using `L` lines + rounded join: wedge as two straight lines + arc? Instead: make quesadilla icon a half-circle (filled? stroke): path `M4 12 a8 8 0 0 0 16 0 z` — semicircle bottom (taco-like again!) — conflict with taco icon. Make queso icon = triangle wedge: `M19 5 5 12l14 7z` with dots — flat pie slice pointing left. `<path d="M19 5 5 12l14 7Z" fill="none" stroke...>` triangle (19,5)-(5,12)-(19,19) ✓ rounded joins. Plus 2 dots. Good — no arcs.
Similarly taco icon arcs — direction uncertainty affects taco & bowl & star ok? Star is polygon lines ✓. Taco: if sweep 0 gives TOP semicircle instead of bottom, icon looks like a bowl — need certainty. Reason definitively: SVG spec: sweep-flag=1 → arc drawn in "positive-angle" direction. In SVG coordinate system (y down), positive angle = clockwise visually?? Rotation positive = CCW math but screen-flipped... Positive angle direction in SVG is from +x axis toward +y axis. +x → +y (y down) = downward rotation = clockwise on screen. So sweep 1 = clockwise visually. Chord left→right (3.5,12)→(20.5,12), semicircle, sweep 1 (clockwise): starting at (3.5,12) moving clockwise around center (12,12): initial direction at angle 180°: clockwise tangent = (sin180, -cos180)*? tangent for clockwise (positive) direction at angle θ: derivative of (cx + r cosθ, cy + r sinθ) w/ θ increasing = (-sinθ, cosθ). At θ=180°: (-0, -1) → moving up. So sweep 1 from left point moves UP → top arc. Therefore sweep 0 moves DOWN → bottom arc ✓ my taco path `a8.5 8.5 0 0 0 17 0` = bottom semicircle ✓ (I want taco shell bottom ✓).
Bowl icon `M4 11a8 8 0 0 0 16 0` bottom arc ✓ then rim line h16 ✓ steam curls ✓.
Burrito rect ✓.
Use angle logic validated.
Also main taco SVG shell uses cubic beziers only ✓ no arcs.
Wing circles + line ✓.
Star polygon ✓.
Chili: cubic-ish blob + stem — direction-agnostic ✓.
Now — one concern: symbol default viewBox content scaled by CSS width/height on `<svg class="ico">` — symbols have their own viewBox; `<use>` inside outer svg without viewBox — symbol's viewBox applies at use ✓ standard pattern works.
Alright, final assembly time. Also add `aria-hidden="true"` to decorative svgs and labels where useful. Keep lang="en".
Small extra: `<meta name="theme-color" content="#E23B2E">`? fine.
Let me make sure the header cart-btn styling:
```css
.cart-btn{position:relative;display:flex;align-items:center;gap:8px;background:var(--yellow);border:3px solid var(--ink);border-radius:12px;padding:9px 13px;font-family:var(--display);font-size:13px;cursor:pointer;box-shadow:3px 3px 0 var(--ink)}
.badge{position:absolute;top:-10px;right:-10px;min-width:22px;height:22px;display:none;align-items:center;justify-content:center;background:var(--red);color:var(--cream);border:2.5px solid var(--ink);border-radius:99px;font-size:11px;font-weight:900}
.badge.show{display:flex}
.badge.pop{animation:pop .4s}
@keyframes pop{40%{transform:scale(1.35)}}
```
Header pill id pillTop styled `.pill`.
Brand: `font-family:var(--display)` "KIMCHI LOCO" with LOCO red. Add mini taco svg 22px before.
Topbar scrolled adds shadow: `.topbar.scrolled .header{border-bottom-width:0;box-shadow:0 2px 0 rgba(35,22,16,.12)}` meh skip complexity — keep header border static. Actually keep the scroll listener but apply: `.topbar.scrolled{filter:drop-shadow(0 4px 8px rgba(35,22,16,.18))}` cheap ✓.
Time to write final. I'll now produce the complete file in the answer. During writing, maintain quality & correctness.
Word budget: file will be long; that's expected.
One more decision: `body{padding-top:96px}` and anchor scroll-margin 120px.
Ticker bar height: font 11px padding 8px → ~30px; header 58 → 88 total ≈ padding 96 ok.
Divider B uses same buildTicker with 6 long items — each half must exceed viewport: e.g. 6 items avg 220px = 1300 ✓.
Ticker CSS specifics:
```css
.t-track{display:flex;width:max-content;animation:marquee var(--ms,32s) linear infinite}
.t-track>*{flex:none}
.t-half{display:flex;align-items:center;padding:8px 0}
.t-half b{font-weight:800;font-size:11px;letter-spacing:.16em;white-space:nowrap;margin-left:2.2em}
.t-half i{font-style:normal;color:var(--red);margin-left:2.2em;font-size:11px}
```
Hmm star separators: pattern items: text★text★... build half as: items.map(`<b>${t}</b><i>★</i>`).join('') ✓ trailing star before next half's first b — margin consistent ✓.
Divider variant:
```css
.divider .t-track{--ms:36s}
.divider .t-half{padding:12px 0}
.divider .t-half b{font-family:var(--display);font-size:clamp(15px,2vw,21px);letter-spacing:.04em}
.divider .t-half i{color:var(--cream);font-size:clamp(15px,2vw,21px)}
```
Star cream on red ✓ text cream ✓.
Reverse: `.t-track.rev{animation-direction:reverse}` — apply to divider's track? Both scroll same direction is fine; make divider rev for variety ✓.
Wait — the divider id tickerB gets class too: `<div class="divider"><div class="t-track rev" id="tickerB"></div></div>` ✓.
Animation-timing: constant speed ✓.
prefers-reduced-motion: `.t-track{animation-duration:...}` set `animation:none` inside media query: `@media (prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}` — this nukes marquee (content still visible? track at 0 → first half visible ✓ readable). Steam JS skipped via reduced ✓ puff loop guarded ✓. tween guarded ✓.
Also with reduced, ticker shows only half content — fine.
Now, hero background pattern: add subtle dots to cream:
```css
.hero{background:var(--cream);background-image:radial-gradient(rgba(35,22,16,.05) 1.4px,transparent 1.4px);background-size:22px 22px}
```
Hmm dots over whole hero — subtle texture ✓ nice print vibe.
Truck section same dots? keep cream flat, road provides contrast. Add dots too, consistent. Use shared class? minor, just re-declare.
Footer f-grid content:
Newsletter col: `<h4>Get the drop</h4><p>One email a week max — where the truck is, what's bubbling, secret menu stuff.</p><form id="newsForm"><input id="newsInput" type="email" required placeholder="you@hungry.com"><button class="btn">Join</button></form>`
Hours col: `<h4>Hours</h4> <ul><li>Mon–Tue · 11A–9P</li><li>Wed–Thu · 5P–Late</li><li>Fri–Sat · 12P–2A</li><li>Sun · 11A–6P</li></ul>`
Contact: `<h4>Talk</h4><a href="mailto:...">tacos@kimchiloco.food</a><a href="tel:...">(555) 862-LOCO</a><p>Catering? Same email, subject line “BIG TACO ENERGY”.</p>`
Follow: `<h4>Follow</h4><div id="socials"><button data-name="Instagram">Instagram ↗(arrow svg)</button><button data-name="TikTok">TikTok ↗</button><button data-name="the group chat">…</button></div>` — keep 2-3 chips.
f-big: "SEE YOU AT <em>THE WINDOW.</em>"
Bottom bar: `© 2025 Kimchi Loco LLC · Seoul × Mexico City · hot sauce responsibly.`
Alright. Also add "menu" section extras: after drinks strip add small line: "GLUTEN-FRIEND? CORN TORTILLAS EVERYWHERE. ASK US ANYTHING — WE LOVE TALKING." — small centered note. Nice.
Final check list against my earlier plan complete. Write it.
Actually one more time sanity: JS reference `$("#road")` — road id ✓. `#truckSvg` inside road ✓.
The `stars` svg row in quotes: `<svg class="ico star"><use href="#i-star"/></svg>` ×5.
Write now. Also careful: template literal inside JS containing `</script>`? No.
In buildTicker I use innerHTML with `<i>★</i>` fine.
Also `heatRead` initial content "SEOUL SIZZLE" replaced on load ✓.
Let me write the truck SVG block carefully as final markup:
```html
<svg id="truckSvg" viewBox="0 0 380 175" role="img" aria-label="The Kimchi Loco food truck">
<ellipse cx="190" cy="163" rx="158" ry="8" fill="rgba(35,22,16,.18)"/>
<g class="t-bob">
<path d="M278 58h28q18 3 24 18l14 44h-66z" fill="#FFFDF6" stroke="#231610" stroke-width="4" stroke-linejoin="round"/>
<rect x="48" y="26" width="10" height="22" rx="4" fill="#231610"/>
<g class="smoke">
<circle cx="53" cy="16" r="5" fill="rgba(255,246,233,.5)"/>
<circle cx="58" cy="8" r="4" fill="rgba(255,246,233,.4)"/>
</g>
<rect x="140" y="28" width="42" height="16" rx="5" fill="#E23B2E" stroke="#231610" stroke-width="3.5"/>
<rect x="26" y="44" width="252" height="76" rx="9" fill="#FFFDF6" stroke="#231610" stroke-width="4"/>
<rect x="26" y="100" width="252" height="15" fill="#E23B2E"/>
<text x="228" y="70" font-family="'Arial Black',Arial,sans-serif" font-weight="900" font-size="19" fill="#231610" text-anchor="middle">KIMCHI</text>
<text x="228" y="92" font-family="'Arial Black',Arial,sans-serif" font-weight="900" font-size="19" fill="#B01F14" text-anchor="middle">LOCO</text>
<g>
<rect x="84" y="42" width="104" height="13" fill="#E23B2E" stroke="#231610" stroke-width="3"/>
<rect x="110" y="42" width="26" height="13" fill="#9BC53B"/>
<rect x="162" y="42" width="26" height="13" fill="#9BC53B"/>
</g>
<rect x="88" y="58" width="96" height="42" rx="8" fill="#231610"/>
<rect x="98" y="66" width="60" height="4" rx="2" fill="rgba(255,246,233,.8)"/>
<rect x="98" y="76" width="76" height="4" rx="2" fill="rgba(255,246,233,.5)"/>
<rect x="98" y="86" width="46" height="4" rx="2" fill="rgba(255,246,233,.6)"/>
<g transform="translate(136,55)">
<g class="sign">
<line x1="0" y1="0" x2="0" y2="9" stroke="#FFF6E9" stroke-width="3"/>
<rect x="-22" y="9" width="44" height="17" rx="4" fill="#FFF6E9" stroke="#231610" stroke-width="2.5"/>
<text x="0" y="21.5" font-family="Arial,sans-serif" font-weight="900" font-size="10" fill="#B01F14" text-anchor="middle">OPEN</text>
</g>
</g>
<path d="M288 64h22q10 2 14 11l6 17h-42z" fill="#3A2620"/>
<g>
<circle cx="95" cy="132" r="17" fill="#231610"/>
<circle cx="95" cy="132" r="7.5" fill="#FFF6E9"/>
<circle cx="95" cy="132" r="2.6" fill="#231610"/>
<circle cx="322" cy="132" r="17" fill="#231610"/>
<circle cx="322" cy="132" r="7.5" fill="#FFF6E9"/>
<circle cx="322" cy="132" r="2.6" fill="#231610"/>
</g>
</g>
</svg>
```
Wait awning y=42 height 13 → covers box top edge (44) region 42-55, sits just above box — good. But box top at 44 — awning floats at 42..55 overlapping top border ✓ looks attached. Window 58..100 under awning ✓.
Wait stripes rect x84..188: 84 + 26 + 26 = 136? stripes: base red rect 84..188 (w104); lime overlays at 110..136 and 162..188 ✓ leaves red 84..110 and 136..162 ✓ alternating ✓.
Text: KIMCHI at x228 y70: awning ends 188; window ends 184; text area starts 190 → "KIMCHI" width ~ 19*0.72*6 ≈ 82 → 228±41 = 187..269 — overlaps awning right edge 188 barely ✓ fine, and LOCO y92 above stripe 100 ✓.
Cab: path M278 58 h28 →(306,58) q18 3 24 18 → (330,76) l14 44 → (344,120) h-66 → (278,120) z ✓. Windshield `M288 64h22q10 2 14 11l6 17h-42z`: (288,64)→(310,64)→q→(324,75)→l→(330,92)→h-42→(288,92) ✓ inside cab ✓.
Chimney at x48 y26..48 — box top 44 → chimney sticks above ✓.
Wheels: rear 95 within box (26..278) ✓ front 322 under cab (278..344) ✓.
Front wheel vs cab bottom: cab bottom y120, wheel top 115 → overlaps ✓.
Truck svg width: min(400px,92%) inside road; height auto.
Road content: padding 20px; truck margin: 6px auto 2px; road-line under truck.
Also add in road a "location sign"? skip.
Beep positioned at truck center: `.beep{left:50%;top:38%}` ✓.
For hover cursor on truck: `#truckSvg{cursor:pointer}`.
Also add hover wiggle on truck? cursor + beep enough.
Now hero taco svg final:
```html
<div class="stage" id="stage">
<div class="sunburst" aria-hidden="true"></div>
<div class="steam" id="steam" aria-hidden="true"></div>
<div class="taco-wrap" id="tacoWrap">
<svg id="tacoSvg" viewBox="0 0 340 300" role="img" aria-label="A taco loaded with bulgogi, kimchi and salsa">
<g transform="translate(170,238)">
<g class="flames">
<path d="M-30 2C-34-8-32-18-26-24c2 10 8 12 10 20 4-10 10-8 12-16 6 10 4 18-2 22z" fill="#F77F00"/>
<path d="M2 4C-4-10-2-24 6-30c0 12 6 14 8 20 4-8 10-6 12-12 6 10 4 20-4 26z" fill="#F77F00"/>
<path d="M28 2c-4-8-2-16 4-20 0 8 6 10 8 16 6-8 12 0 8 6-4 4-14 2-20-2z" fill="#F77F00"/>
<path d="M-10 2c-4-6-2-12 2-16 2 8 6 8 8 14 2 4 0 6-10 2z" fill="#FFC53D"/>
<path d="M12 4c-4-8-2-16 4-20 0 8 4 8 6 14 2 6-4 8-10 6z" fill="#FFC53D"/>
</g>
</g>
<!-- fillings -->
<g fill="none" stroke-linecap="round">
<path d="M104 133c-3-16 2-30 12-38" stroke="#4E9F3D" stroke-width="9"/>
<path d="M118 134c4-16 12-26 22-30" stroke="#77C159" stroke-width="8"/>
<path d="M244 132c4-15 0-28-10-36" stroke="#7B4A9E" stroke-width="9"/>
<path d="M230 134c-3-14-10-24-18-28" stroke="#9A6BC4" stroke-width="8"/>
<path d="M86 128c-5-12-3-24 5-32" stroke="#77C159" stroke-width="8"/>
<path d="M258 128c5-12 3-24-5-32" stroke="#77C159" stroke-width="8"/>
</g>
<g stroke="#231610" stroke-width="3.5">
<path d="M96 140c8-14 26-16 38-10-6 8-26 14-38 10z" fill="#8E5030"/>
<path d="M206 140c10-12 28-14 40-8-6 8-28 16-40 8z" fill="#A05A32"/>
<path d="M138 148c12-10 30-12 44-8-10 10-30 14-44 8z" fill="#8E5030"/>
</g>
<g stroke="#231610" stroke-width="3">
<rect x="116" y="102" width="19" height="11" rx="4.5" fill="#E23B2E" transform="rotate(-8 126 108)"/>
<rect x="192" y="104" width="18" height="11" rx="4.5" fill="#D93A2B" transform="rotate(7 201 110)"/>
<rect x="158" y="88" width="17" height="11" rx="4.5" fill="#E23B2E" transform="rotate(-4 166 94)"/>
<rect x="140" y="114" width="15" height="10" rx="4" fill="#C9301F" transform="rotate(6 148 119)"/>
<rect x="176" y="112" width="14" height="10" rx="4" fill="#E23B2E" transform="rotate(-6 183 117)"/>
</g>
<g fill="#FFF6E9" stroke="#231610" stroke-width="2">
<ellipse cx="150" cy="100" rx="4.5" ry="2.6" transform="rotate(-18 150 100)"/>
<ellipse cx="177" cy="122" rx="4.5" ry="2.6" transform="rotate(12 177 122)"/>
<ellipse cx="207" cy="90" rx="4.5" ry="2.6" transform="rotate(24 207 90)"/>
<ellipse cx="126" cy="124" rx="4.5" ry="2.6" transform="rotate(30 126 124)"/>
</g>
<path class="salsa" d="M90 138c26 12 58 16 80 10 26-6 54-4 76 6-14 16-50 24-82 18-32-6-58-18-74-34z" stroke="#231610" stroke-width="4"/>
<!-- lime -->
<g transform="translate(262 150) rotate(14)">
<circle r="16" fill="#C9E59B" stroke="#231610" stroke-width="3.5"/>
<path d="M-10 8L12-8M-8-10L8 10M0-14v28" stroke="#8FBF57" stroke-width="2.4"/>
</g>
<!-- shell -->
<path d="M34 148C64 236 276 236 306 148C282 176 58 176 34 148Z" fill="#F5B849" stroke="#231610" stroke-width="5" stroke-linejoin="round"/>
<path d="M48 152c52 26 148 26 200 0" fill="none" stroke="#FBD98A" stroke-width="6" opacity=".9"/>
<g fill="#C97F2F" opacity=".75">
<ellipse cx="118" cy="192" rx="9" ry="4"/>
<ellipse cx="176" cy="202" rx="10" ry="4.5"/>
<ellipse cx="232" cy="188" rx="8" ry="4"/>
</g>
<!-- salsa cup -->
<g transform="translate(28,196)">
<path d="M0 6l7 28h40l7-28z" fill="#FFF6E9" stroke="#231610" stroke-width="4.5" stroke-linejoin="round"/>
<ellipse class="salsa" cx="27" cy="6" rx="27" ry="8" stroke="#231610" stroke-width="4.5"/>
</g>
</svg>
</div>
</div>
```
Check salsa path: starts (90,138) c26 12 58 16 80 10 → ends (170,148); then 26-6 54-4 76 6 → ends (246,154); then -14 16-50 24-82 18 → (164,172); then -32-6-58-18-74-34 → (90,138) ✓ closes. Drizzle band across x90..246 y138..172 ✓ above shell inner edge (~169 mid) — some overlap, shell drawn AFTER covers below-rim parts — drizzle mid-bottom (164,172) hidden behind shell inner edge... shell top edge at x164 ≈ y169: drizzle point 172 slightly below → covered. Visually salsa tucks into shell ✓.
Cup at translate(28,196): cup spans x28..82, y6→34 → 202..230. Shell bottom curve near x28..82: bottom edge y from 148 (tip) sagging to ~200 at x80: cup overlaps lower-left shell front — cup drawn last → in front ✓ good. Might cover shell tip (34,148)? no, cup starts y196 ✓.
Lime at (262,150): circle r16 → 246..278 x — inside? shell right tip 306 — lime above shell right region overlapping shreds — drawn after fillings before shell: shell covers its lower part maybe partially. OK.
Steam container placement: `.steam{top:-2%;left:26%;width:48%;height:22%}` over the top of fillings area (fillings top ~88 → relative: svg height 300 mapped to stage width 400 → svg rendered height 400*300/340 ≈ 353px? aspect keeps: width 400 → height 353. steam box top -2% ≈ -7px, height 22% ≈ 78px, left 26% w 48% → covers x 104..296 in svg coords → centered over fillings ✓.
Actually tacoWrap contains svg; steam is sibling positioned vs stage — same box roughly ✓.
Now heat-card markup:
```html
<div class="heat-card">
<div class="heat-head">
<label for="heatSlider">How loco do you want it?</label>
<span class="heat-read" id="heatRead">SEOUL SIZZLE</span>
</div>
<input type="range" id="heatSlider" min="0" max="100" value="30" step="1" aria-label="Spice level">
<p class="heat-sub" id="heatSub">A polite little kick. You will be fine. Probably.</p>
</div>
```
Slider track gradient: `linear-gradient(90deg,#9BC53B,#FFC53D 45%,#E23B2E 80%,#8f1210)`.
OK. Compose whole file. GO. I'll count on careful serialization.
Also — last: `#hype` anchor is section with id="hype". Nav has 3 links.
Also add `::selection{background:var(--red);color:var(--cream)}`.
Focus: `:focus-visible{outline:3px dashed var(--red);outline-offset:3px}`.
Scrollbar styling optional skip.
Write final now.
Wait — one potential issue: `input[type=range]` styling with `--webkit-slider-runnable-track` margin-top for thumb alignment: track height 12px, thumb 30px → margin-top = (12-30)/2 = -9 minus border? track includes border? I'll set track height 14px with box-sizing... Standard recipe: runnable-track height 14px; thumb margin-top: -10px (for 30px thumb with 3px borders = 30 outer? width/height 30 includes border with box-sizing default content-box → content 24 + border 6 = 30 actual). margin-top -9 ≈ align. I'll eyeball -9px. Firefox uses ::-moz-range-thumb natively centers ✓.
Also `background` of input transparent.
Alright, writing the final file — for real this time. I'll produce it directly as the answer. Keep coherent order: CSS sections then HTML then JS.
Additional minor: `.hero-ctas` flex wrap gap 14. `.hero-status` mt 14, font 15. `.open-t` color var(--red-deep).
For `#heroStatus` initial markup: `<p class="hero-status" id="heroStatus"></p>` filled by JS ✓ (screenshot at 3s: filled ✓).
`.menu a` nav etc.
Let me also include the small "menu note" and "gluten" line.
Footer form input styles:
```css
.news-form{display:flex;gap:10px;margin-top:14px}
.news-form input{flex:1;min-width:0;border:3px solid var(--cream);background:#2b1c14? no on ink bg: use transparent bg with cream border; color cream; padding 12px 14px; border-radius:12px; font:inherit}
```
focus outline yellow.
Footer headings h4 display font letter-spacing yellow? cream. `.f-grid h4{font-family:var(--display);font-size:14px;letter-spacing:.14em;color:var(--yellow);margin-bottom:14px;text-transform:uppercase}`.
Links cream underline on hover.
Social buttons: `.soc{display:flex;align-items:center;gap:8px;border:2.5px solid var(--cream);background:transparent;color:var(--cream);padding:9px 14px;border-radius:999px;font-weight:800;font-size:13px;cursor:pointer}` hover invert.
Enough planning — WRITE.
Actually, let me reconsider one thing quickly: fonts — "Arial Black" widely available on Windows/macOS ✓ Chrome on Linux falls back to system sans bold — acceptable.
Also numbers: toLocaleString('en-US') → "128,400" ✓.
Final ticket when cart empty prevented (fire only in foot when items) ✓.
Write the answer now. During writing I'll keep double-checking tag closure & JS braces. The response must be ONLY the code block.
One more small thing: hero "GET LOCO." period style: "LOCO" red + ink shadow — with the final period maybe cream chip? Just text.
Also body background cream — done.
Let me also make `main` wrapper? not needed.
GO.