Qwen3.8-27B-Abliterated
Other · llama-swap · every run of this model · uwu · 2026-10-31
Race it: run Qwen3.8-27B-Abliterated on your computer and see the two side by side.
- Score
- 2,125 Silver
- Tasks passed
- 42/44
- Points
- 60/64Harder tasks count 3
- Tokens
- 230,69122,407 read, 208,284 written, as the runner counted them
- Speed
- 22.7 tok/sAs the runner measured it; the site cannot check this.
tok/s is tokens a second: how fast the model writes. A token is about three quarters of a word. How the score works.
Not on the leaderboard: it was posted under an earlier version of the task suite. Leaderboard
- Summarizing 5/5
- Extracting 5/5
- Following instructions 5/5
- Reasoning 5/5
- Reading code 4/4
- Using tools 5/5
- Building pages 4/5
- Building games 1/2
- Writing code 3/3
- Fixing code 2/2
- Planning and data 3/3
Run details
Flag this run
Failed 2
A small websiteNo <footer> with opening hours
31,243 tokens: 163 read, 31,080 written · 23 min 37 s · 21.9 tok/s
Passes when A <header> with an <h1> naming the bakery, a <nav> with exactly 3 links, a <main> with <section id="menu"> holding at least 4 list items, and a <footer> with opening hours. Nothing loaded from another site and no network requests.
Build a one-page website for a bakery called "Crumb & Co". It must have a <header> containing an <h1> with the bakery's name; a <nav> with exactly 3 links; a <main> with a <section id="menu"> listing at least 4 items in a list; and a <footer> with the opening hours. Output one complete HTML document and nothing else. Put all CSS and JavaScript inline. Do not load anything from another site: no external scripts, stylesheets, fonts or images.
Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content="Crumb & Co. — wood-fired sourdough, croissants and slow bakes on Alder Row, Portland. Open from 7."> <title>Crumb & Co. — Bakery, Alder Row</title> <style> :root{ --paper:#f5eddc; --paper-2:#ece0c6; --card:#fbf5e8; --ink:#2b180b; --cocoa:#4b2d15; --butter:#e9b44c; --caramel:#c47a2c; --crust:#8f4f1d; --cream-txt:#f2e5c8; --serif:"Didot","Bodoni MT","Bodoni 72","Playfair Display","Big Caslon",Georgia,"Times New Roman",serif; --sans:"Avenir Next",Avenir,"Segoe UI","Trebuchet MS",Verdana,sans-serif; --mono:"SF Mono","Cascadia Code",Menlo,Consolas,"DejaVu Sans Mono",monospace; } *{box-sizing:border-box;margin:0;padding:0} html{scroll-behavior:smooth;scroll-padding-top:5.5rem} body{ background: radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,.5), transparent 55%), var(--paper); color:var(--ink); font:1rem/1.6 var(--sans); overflow-x:hidden; } ::selection{background:var(--butter);color:var(--ink)} :focus-visible{outline:3px solid var(--crust);outline-offset:3px;border-radius:2px} img,svg{display:block;max-width:100%} .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap} .grain{position:fixed;inset:0;z-index:90;pointer-events:none;opacity:.06;mix-blend-mode:multiply; background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)' opacity='0.55'/></svg>"); } /* ---------- top ticker ---------- */ .ticker{background:var(--ink);color:var(--cream-txt);overflow:hidden;border-bottom:2px solid var(--ink)} .ticker-track{display:flex;width:max-content;animation:tick 32s linear infinite} .ticker-track span{font:600 .68rem/1 var(--mono);letter-spacing:.24em;text-transform:uppercase;padding:.7rem 0;white-space:nowrap} .ticker-track b{color:var(--butter);font-weight:600;padding:0 .4rem} @keyframes tick{to{transform:translateX(-50%)}} .wrap{width:min(1120px,92%);margin:0 auto} /* ---------- header ---------- */ header{border-bottom:2px solid var(--ink)} .topbar{display:flex;justify-content:space-between;align-items:center;gap:1rem;padding:.6rem 0;border-bottom:1.5px dotted rgba(43,24,11,.4);font:600 .68rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa)} #oven-chip{color:var(--crust)} @media(max-width:640px){.topbar .tb-mid{display:none}} .navbar{position:sticky;top:0;z-index:50;background:var(--paper);border-bottom:2px solid var(--ink)} .navbar .wrap{display:flex;align-items:center;justify-content:space-between;gap:1rem} .brandmark{font:700 .72rem var(--mono);letter-spacing:.1em;border:2px solid var(--ink);background:var(--butter);padding:.45rem .6rem;border-radius:50%;box-shadow:2px 2px 0 var(--ink)} nav ul{display:flex;list-style:none;gap:1.6rem} .navbar a{position:relative;font:600 .78rem var(--sans);letter-spacing:.16em;text-transform:uppercase;color:var(--ink);text-decoration:none;padding:.95rem .2rem} .navbar a::after{content:"";position:absolute;left:.2rem;right:.2rem;bottom:.6rem;height:2px;background:var(--caramel);transform:scaleX(0);transform-origin:left;transition:transform .3s ease} .navbar a:hover::after,.navbar a.active::after{transform:scaleX(1)} .navbar a.active{color:var(--crust)} .nav-note{font:500 .66rem var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--cocoa)} @media(max-width:720px){.nav-note{display:none}nav ul{gap:1rem}} .hero{padding:3.4rem 0 3rem} .hero-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:3rem;align-items:center} @media(max-width:900px){.hero-grid{grid-template-columns:1fr}} .kicker{font:600 .7rem var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--caramel);margin-bottom:1.1rem} .kicker::before{content:"— ";color:var(--crust)} h1.wordmark{font-family:var(--serif);font-weight:400;font-size:clamp(3.4rem,10vw,7rem);line-height:.94;letter-spacing:-.015em} .wm-line{display:block;overflow:hidden;padding-bottom:.04em} .wm-in{display:inline-block;transform:translateY(112%);animation:rise .95s cubic-bezier(.22,1,.36,1) forwards} .wm-in.d2{animation-delay:.14s} @keyframes rise{to{transform:translateY(0)}} .wordmark em{font-style:italic;color:var(--caramel);font-size:1.02em} .wheat{width:.8em;height:1.35em;color:var(--caramel);vertical-align:-.24em;margin-left:.18em;transform-origin:bottom center;animation:sway 4.5s ease-in-out infinite alternate} @keyframes sway{from{transform:rotate(-4deg)}to{transform:rotate(4deg)}} .tagline{max-width:46ch;margin:1.4rem 0 1.5rem;font-size:1.05rem;color:var(--cocoa)} .chips{display:flex;flex-wrap:wrap;gap:.6rem} .chips li{list-style:none;font:600 .66rem var(--mono);letter-spacing:.14em;text-transform:uppercase;border:1.5px solid var(--cocoa);background:var(--card);padding:.5rem .75rem;border-radius:999px;box-shadow:2px 2px 0 rgba(43,24,11,.18);transition:transform .2s,background .2s} .chips li:hover{transform:translateY(-3px);background:var(--butter)} .crust-card{position:relative;border:2px solid var(--ink);background:var(--card);box-shadow:9px 9px 0 var(--butter);padding:1.6rem; background-image:repeating-radial-gradient(circle at 50% 55%, transparent 0 20px, rgba(43,24,11,.05) 20px 21px)} #bread{transform-origin:150px 150px;transition:transform .5s cubic-bezier(.22,1,.36,1);cursor:pointer} .crust-art:hover #bread{transform:translateY(-7px) rotate(-1.5deg)} #bread.pop{animation:crustpop .55s ease} @keyframes crustpop{30%{transform:scale(1.06) rotate(-2.5deg)}60%{transform:scale(.97)}100%{transform:scale(1)}} .steam path{animation:steam 2.8s ease-in-out infinite;opacity:0} .steam path:nth-child(2){animation-delay:.9s} .steam path:nth-child(3){animation-delay:1.7s} @keyframes steam{0%{opacity:0;transform:translateY(9px)}30%{opacity:.8}100%{opacity:0;transform:translateY(-16px)}} .crust-cap{margin-top:1rem;text-align:center;font:500 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--cocoa)} .stamp{position:absolute;top:-34px;right:-26px;width:112px;height:112px;color:var(--cocoa);animation:spin 26s linear infinite} @keyframes spin{to{transform:rotate(360deg)}} .stamp text{font:700 11.5px var(--mono);letter-spacing:2.5px;fill:currentColor} .meta-strip{display:flex;flex-wrap:wrap;gap:.9rem 2.6rem;margin-top:2.8rem;padding-top:1.4rem;border-top:2px dotted rgba(43,24,11,.35)} .meta-strip p{font:500 .72rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa)} .meta-strip b{font-family:var(--serif);font-weight:400;font-size:1.45rem;letter-spacing:0;color:var(--ink);margin-right:.35rem} /* ---------- sections ---------- */ .section{padding:5.2rem 0 5.6rem} .section + .section{border-top:2px dotted rgba(43,24,11,.28)} h2{font-family:var(--serif);font-weight:400;font-size:clamp(2.1rem,4.6vw,3.3rem);line-height:1.05;letter-spacing:-.01em;margin-bottom:.4rem} .sec-lead{color:var(--cocoa);max-width:52ch} .crumb-line{height:16px;background:radial-gradient(circle,var(--crust) 1.6px,transparent 1.7px) 0 50%/26px 16px;opacity:.4} /* ---------- menu ---------- */ .menu-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:1.4rem;margin-bottom:2rem} .tabs{display:flex;flex-wrap:wrap;gap:.55rem} .tab{font:700 .68rem var(--mono);letter-spacing:.16em;text-transform:uppercase;padding:.6rem 1rem;border:2px solid var(--ink);background:transparent;color:var(--ink);cursor:pointer;border-radius:999px;transition:transform .15s,background .2s,color .2s,box-shadow .15s} .tab:hover{transform:translateY(-2px)} .tab[aria-pressed="true"]{background:var(--ink);color:var(--paper);box-shadow:3px 3px 0 var(--butter)} .menu-count{font:500 .7rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa);margin-bottom:.8rem} .menu-list{list-style:none;columns:2;column-gap:2.6rem;border:2px solid var(--ink);background:var(--card);box-shadow:9px 9px 0 rgba(43,24,11,.12);padding:1.2rem 1.4rem} @media(max-width:820px){.menu-list{columns:1}} .menu-item{break-inside:avoid;display:grid;grid-template-columns:1fr;gap:.3rem;padding:1.15rem .7rem;border-bottom:1.5px dotted rgba(43,24,11,.4);transition:background .25s,transform .25s} .menu-item:last-child{border-bottom:none} .menu-item:hover{background:#fff9ec;transform:translateX(6px)} .menu-item.off{display:none} .mi-top{display:flex;align-items:baseline;gap:.7rem} .mi-name{font-family:var(--serif);font-weight:400;font-size:1.4rem;line-height:1.15;transition:color .2s} .menu-item:hover .mi-name{color:var(--crust)} .mi-dots{flex:1;border-bottom:2px dotted var(--caramel);transform:translateY(-5px);opacity:.65} .mi-price{font:700 .95rem var(--mono);color:var(--crust)} .mi-desc{font-size:.92rem;color:var(--cocoa);max-width:52ch} .mi-tags{list-style:none;display:flex;gap:.5rem;flex-wrap:wrap} .mi-tags li{font:600 .6rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa);border:1.5px dashed rgba(75,45,21,.5);padding:.22rem .5rem;border-radius:999px} .menu-note{margin-top:1rem;font:500 .74rem var(--mono);letter-spacing:.06em;color:var(--cocoa)} /* ---------- oven band ---------- */ .oven-band{background:#26140a;color:var(--cream-txt);padding:4.6rem 0 5rem;border-top:6px solid var(--butter)} .oven-band .kicker{color:var(--butter)} .oven-band .kicker::before{color:var(--caramel)} .oven-band h2{color:var(--cream-txt)} .oven-grid{display:grid;grid-template-columns:1fr 1.1fr 1fr;gap:1.4rem;margin-top:2.4rem} @media(max-width:900px){.oven-grid{grid-template-columns:1fr}} .ov-card{border:1.5px solid rgba(242,229,200,.28);background:rgba(242,229,200,.04);padding:1.5rem;transition:border-color .3s,transform .3s} .ov-card:hover{border-color:var(--butter);transform:translateY(-4px)} .ov-label{font:700 .66rem var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--butter);margin-bottom:1.1rem;display:flex;justify-content:space-between;gap:.5rem} .ov-label span{color:rgba(242,229,200,.5);font-weight:500} .ov-ring{position:relative;width:172px;margin:0 auto 1.2rem} .ring circle{fill:none;stroke-width:7} .ring-bg{stroke:rgba(242,229,200,.14)} .ring-fg{stroke:var(--butter);stroke-linecap:round;transform:rotate(-90deg);transform-origin:center} .ov-center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.2rem;text-align:center} #ov-count{font:700 1.55rem var(--mono);letter-spacing:.04em} .ov-sub{font:500 .62rem var(--mono);letter-spacing:.2em;text-transform:uppercase;color:rgba(242,229,200,.55)} .ov-next{font-size:.92rem;color:rgba(242,229,200,.85);text-align:center} .ov-next strong{color:var(--cream-txt)} .ov-status{margin-top:.7rem;text-align:center;font:700 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--caramel)} .box-row{display:flex;align-items:center;gap:.8rem;padding:.85rem .4rem;border-bottom:1.5px dashed rgba(242,229,200,.2);cursor:pointer;transition:background .2s,transform .2s;font-size:.95rem} .box-row:hover{background:rgba(233,180,76,.09);transform:translateX(5px)} .box-row input{accent-color:var(--butter);width:17px;height:17px;flex:none} .box-name{flex:1} .box-name small{font:500 .7rem var(--mono);color:rgba(242,229,200,.5);margin-left:.4rem} .box-price{font:700 .85rem var(--mono);color:var(--butter)} .box-total{display:flex;justify-content:space-between;align-items:baseline;margin:1.1rem 0 .9rem} .box-total span{font:500 .7rem var(--mono);letter-spacing:.2em;text-transform:uppercase;color:rgba(242,229,200,.6)} #box-total{font-family:var(--serif);font-size:1.9rem;color:var(--cream-txt)} #box-total.pop{animation:crustpop .45s ease} #box-btn{width:100%;font:700 .74rem var(--mono);letter-spacing:.18em;text-transform:uppercase;padding:.95rem;border:2px solid var(--butter);background:var(--butter);color:var(--ink);cursor:pointer;box-shadow:4px 4px 0 rgba(0,0,0,.35);transition:transform .15s,box-shadow .15s,background .2s} #box-btn:hover:not(:disabled){transform:translate(-2px,-2px);box-shadow:6px 6px 0 rgba(0,0,0,.35)} #box-btn:active:not(:disabled){transform:translate(3px,3px);box-shadow:0 0 0 rgba(0,0,0,0)} #box-btn:disabled{opacity:.45;cursor:not-allowed} #box-btn.held{background:var(--cream-txt)} #box-note{margin-top:.8rem;font-size:.82rem;color:var(--butter)} .sold-list{list-style:none;display:grid;gap:.85rem} .sold-list li{display:flex;justify-content:space-between;gap:1rem;font-size:.95rem} .sold-list li span{text-decoration:line-through;text-decoration-color:var(--caramel);opacity:.6} .sold-list time{font:600 .72rem var(--mono);color:rgba(242,229,200,.55);white-space:nowrap} .sold-list .back span{text-decoration:none;opacity:.95} .sold-list .back time{color:var(--butter)} .sold-foot{margin-top:1.1rem;font-size:.85rem;font-style:italic;color:rgba(242,229,200,.6)} /* ---------- story ---------- */ .story-grid{display:grid;grid-template-columns:.9fr 1.1fr;gap:3.5rem;align-items:start} @media(max-width:880px){.story-grid{grid-template-columns:1fr}} .story-left{position:sticky;top:5.5rem} @media(max-width:880px){.story-left{position:static}} .stats{display:flex;flex-wrap:wrap;gap:2.2rem;margin-top:2.2rem;padding-top:1.6rem;border-top:2px dotted rgba(43,24,11,.35)} .stats b{display:block;font-family:var(--serif);font-weight:400;font-size:2rem;line-height:1;color:var(--crust)} .stats span{font:600 .64rem var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--cocoa)} .story-right p{color:var(--cocoa);margin-bottom:1.3rem;max-width:60ch} blockquote{margin:2.2rem 0;padding:1rem 0 1rem 1.6rem;border-left:4px solid var(--butter);font-family:var(--serif);font-style:italic;font-size:1.5rem;line-height:1.35;color:var(--ink)} blockquote footer{margin-top:.8rem;font:600 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;font-style:normal;color:var(--caramel)} .timeline{list-style:none;margin-top:2.6rem;border-left:2px dotted var(--caramel);padding-left:1.6rem} .timeline li{position:relative;padding-bottom:1.9rem} .timeline li:last-child{padding-bottom:.4rem} .timeline li::before{content:"";position:absolute;left:calc(-1.6rem - 7px);top:.5rem;width:12px;height:12px;border-radius:50%;background:var(--butter);border:2.5px solid var(--ink)} .tl-year{display:inline-block;font:700 .7rem var(--mono);letter-spacing:.16em;background:var(--butter);border:1.5px solid var(--ink);padding:.25rem .55rem;margin-bottom:.45rem} .timeline h3{font-family:var(--serif);font-weight:400;font-size:1.35rem;margin-bottom:.25rem} .timeline p{font-size:.94rem;max-width:52ch} /* ---------- visit ---------- */ .visit-grid{display:grid;grid-template-columns:1fr 1fr;gap:3rem;align-items:start;margin-top:2.2rem} @media(max-width:880px){.visit-grid{grid-template-columns:1fr}} .open-pill{display:inline-block;font:700 .68rem var(--mono);letter-spacing:.16em;text-transform:uppercase;padding:.55rem .85rem;border:2px solid var(--ink);margin-bottom:1.2rem} .open-pill.now{background:var(--butter);box-shadow:3px 3px 0 var(--ink)} .open-pill.later{background:var(--card);color:var(--cocoa);border-style:dashed} table.hours{width:100%;border-collapse:collapse;border:2px solid var(--ink);background:var(--card);box-shadow:7px 7px 0 rgba(43,24,11,.12)} .hours tr{border-bottom:1.5px dotted rgba(43,24,11,.35);transition:background .3s} .hours tr:last-child{border-bottom:none} .hours th,.hours td{padding:.85rem 1.1rem;text-align:left;font-size:.95rem} .hours th{font-family:var(--serif);font-weight:400;font-size:1.05rem;width:34%} .hours td{font:600 .82rem var(--mono);letter-spacing:.04em;color:var(--cocoa)} .hours td small{display:block;font:italic 500 .72rem var(--sans);letter-spacing:0;color:var(--crust)} .hours tr.today{background:#f7e7c0} .today-chip{display:inline-block;margin-left:.6rem;font:700 .58rem var(--mono);letter-spacing:.16em;text-transform:uppercase;background:var(--ink);color:var(--butter);padding:.2rem .45rem;border-radius:999px;vertical-align:middle} .visit-addr{margin-top:1.4rem;font:500 .85rem/1.9 var(--mono);color:var(--cocoa)} .visit-addr b{color:var(--ink)} figure.map-fig{border:2px solid var(--ink);background:var(--card);box-shadow:9px 9px 0 var(--butter);padding:1.1rem} .map-fig figcaption{margin-top:.9rem;font:500 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--cocoa);text-align:right} /* ---------- footer ---------- */ footer{background:#20110a;color:#f0e2c6;border-top:6px solid var(--butter)} .foot-grid{display:grid;grid-template-columns:1.1fr 1fr 1fr;gap:2.6rem;padding:3.6rem 0 2.6rem} @media(max-width:820px){.foot-grid{grid-template-columns:1fr}} .foot-word{font-family:var(--serif);font-size:2.1rem;line-height:1.1} .foot-tag{margin-top:.6rem;font-size:.9rem;color:rgba(240,226,198,.65)} .foot-h{font:700 .66rem var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--butter);margin-bottom:1rem} .hours-mini{list-style:none;display:grid;gap:.55rem} .hours-mini li{display:flex;justify-content:space-between;gap:1rem;border-bottom:1.5px dashed rgba(240,226,198,.22);padding-bottom:.55rem;font-size:.92rem} .hours-mini li span:last-child{font:600 .8rem var(--mono);letter-spacing:.04em} footer address{font-style:normal;font:500 .84rem/2 var(--mono);color:rgba(240,226,198,.85)} .foot-note{margin-top:.9rem;font-size:.8rem;font-style:italic;color:rgba(240,226,198,.55)} .foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:.8rem;padding:1.2rem 0 1.6rem;border-top:1.5px solid rgba(240,226,198,.2);font:500 .68rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:rgba(240,226,198,.55)} /* ---------- reveals ---------- */ .reveal{opacity:0;transform:translateY(26px);transition:opacity .7s ease,transform .7s cubic-bezier(.22,1,.36,1);transition-delay:var(--d,0s)} .reveal.in{opacity:1;transform:none} @media (prefers-reduced-motion: reduce){ html{scroll-behavior:auto} .ticker-track,.stamp,.wheat,.steam path,#bread,.menu-item,.box-row,.ov-card,.chips li{animation:none !important;transition:none !important} .steam path{opacity:.6} .wm-in{animation:none;transform:none} .reveal{opacity:1;transform:none;transition:none} } </style> </head> <body> <div class="grain" aria-hidden="true"></div> <div class="ticker" aria-hidden="true"> <div class="ticker-track"> <span>Fresh at 4:00 AM <b>✳</b> stone-milled wheat <b>✳</b> 48-hour ferment <b>✳</b> wood-fired since 2003 <b>✳</b> real butter, always <b>✳</b> no shortcuts, no freezer <b>✳</b> Alder Row & Fourth <b>✳</b> </span> </div> </div> <header id="top"> <div class="wrap"> <div class="topbar"> <span>Est. 1994 · Portland, OR</span> <span class="tb-mid">Flour · water · salt · time</span> <span id="oven-chip">Oven · fires soon</span> </div> </div> <div class="navbar"> <div class="wrap"> <span class="brandmark" aria-hidden="true">C&C</span> <nav aria-label="Main"> <ul> <li><a href="#menu">Menu</a></li> <li><a href="#story">Our story</a></li> <li><a href="#visit">Visit</a></li> </ul> </nav> <span class="nav-note">The case closes when it empties</span> </div> </div> <div class="wrap hero"> <div class="hero-grid"> <div> <p class="kicker">Boulangerie & bakery — the slow corner of Alder</p> <h1 class="wordmark" aria-label="Crumb & Co."> <span class="wm-line"><span class="wm-in d1">Crumb<svg class="wheat" viewBox="0 0 40 64" aria-hidden="true"><path d="M20 60V14" stroke="currentColor" stroke-width="3" fill="none" stroke-linecap="round"/><g fill="currentColor"><ellipse cx="20" cy="9" rx="4" ry="7"/><ellipse cx="11" cy="19" rx="4" ry="7" transform="rotate(-32 11 19)"/><ellipse cx="29" cy="19" rx="4" ry="7" transform="rotate(32 29 19)"/><ellipse cx="11" cy="31" rx="4" ry="7" transform="rotate(-32 11 31)"/><ellipse cx="29" cy="31" rx="4" ry="7" transform="rotate(32 29 31)"/><ellipse cx="11" cy="43" rx="4" ry="7" transform="rotate(-32 11 43)"/><ellipse cx="29" cy="43" rx="4" ry="7" transform="rotate(32 29 43)"/></g></svg></span></span> <span class="wm-line"><span class="wm-in d2"><em>&</em> Co.</span></span> </h1> <p class="tagline">Two generations, one stone mill, and a schedule that starts at 4 a.m. — the good crusts come out of the oven before most of the neighborhood is awake.</p> <ul class="chips"> <li>48-hr ferment</li> <li>Stone-milled rye</li> <li>Cherry-oak fire</li> <li>Real butter, always</li> </ul> </div> <figure class="crust-card reveal"> <svg class="stamp" viewBox="0 0 120 120" aria-hidden="true"> <defs><path id="circ" d="M60,60 m-44,0 a44,44 0 1,1 88,0 a44,44 0 1,1 -88,0"/></defs> <circle cx="60" cy="60" r="30" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="3 4"/> <circle cx="60" cy="60" r="6" fill="currentColor"/> <text><textPath href="#circ">FRESH BAKED · SMALL BATCH · SINCE 1994 · </textPath></text> </svg> <svg class="crust-art" viewBox="0 0 300 260" role="img" aria-label="A warm, scored boule of bread with steam rising"> <ellipse cx="150" cy="222" rx="120" ry="14" fill="rgba(43,24,11,.14)"/> <g class="steam" stroke="#7a5a35" stroke-width="5" fill="none" stroke-linecap="round"> <path d="M108 108c7-9-7-15 0-26s-7-17 0-26"/> <path d="M150 100c7-9-7-15 0-26s-7-17 0-26"/> <path d="M192 108c7-9-7-15 0-26s-7-17 0-26"/> </g> <g id="bread"> <ellipse cx="150" cy="152" rx="112" ry="86" fill="url(#dough)"/> <path d="M66 128c14-26 48-44 84-44s70 18 84 44c-22-16-52-25-84-25s-62 9-84 25z" fill="#f0d9a8" opacity=".85"/> <path d="M92 138q58-26 116 0" stroke="#f7efdf" stroke-width="7" fill="none" stroke-linecap="round" opacity=".9"/> <path d="M104 166q46-20 92 0" stroke="#f7efdf" stroke-width="7" fill="none" stroke-linecap="round" opacity=".75"/> <path d="M120 192q30-13 60 0" stroke="#f7efdf" stroke-width="6" fill="none" stroke-linecap="round" opacity=".55"/> <g fill="#f7efdf" opacity=".4"> <circle cx="110" cy="120" r="2.4"/><circle cx="178" cy="112" r="2"/><circle cx="140" cy="178" r="2.2"/><circle cx="196" cy="170" r="1.8"/><circle cx="128" cy="148" r="1.6"/> </g> </g> <defs> <radialGradient id="dough" cx="42%" cy="32%" r="80%"> <stop offset="0%" stop-color="#eec489"/> <stop offset="55%" stop-color="#cf8d3f"/> <stop offset="100%" stop-color="#9c5a24"/> </radialGradient> </defs> </svg> <figcaption class="crust-cap">Today's boule · scored three times · 41 min in the fire</figcaption> </figure> </div> <div class="meta-strip reveal"> <p><b>4:00</b> AM first fire</p> <p><b>38</b> loaves an hour</p> <p><b>27</b> layers in a croissant</p> <p><b>0</b> shortcuts</p> </div> </div> </header> <main> <!-- ============ MENU ============ --> <section id="menu" class="section" aria-labelledby="menu-h"> <div class="wrap"> <div class="menu-head"> <div class="reveal"> <p class="kicker">No. 01 — The case</p> <h2 id="menu-h">From this morning's bake</h2> </div> <div class="tabs reveal" role="group" aria-label="Filter the menu"> <button class="tab" type="button" data-filter="all" aria-pressed="true">All</button> <button class="tab" type="button" data-filter="breads" aria-pressed="false">Breads</button> <button class="tab" type="button" data-filter="pastries" aria-pressed="false">Pastries</button> <button class="tab" type="button" data-filter="sweets" aria-pressed="false">Sweets</button> </div> </div> <p class="menu-count" id="menu-count">9 bakes on the case</p> <ul class="menu-list" id="menu-list"> <li class="menu-item reveal" data-cat="breads"> <div class="mi-top"><h3 class="mi-name">Country sourdough</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$8.00</span></div> <p class="mi-desc">48-hour cold ferment, stone-milled bread flour, a blistered, crackly crust. Half loaves available.</p> <ul class="mi-tags"><li>whole grain</li><li>vegan</li></ul> </li> <li class="menu-item reveal" data-cat="breads"> <div class="mi-top"><h3 class="mi-name">Seeded rye</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$7.50</span></div> <p class="mi-desc">Our own-milled rye, caraway, sunflower and pumpkin seed. Dense enough for real butter.</p> <ul class="mi-tags"><li>vegan</li><li>keep 5 days</li></ul> </li> <li class="menu-item reveal" data-cat="breads"> <div class="mi-top"><h3 class="mi-name">Olive & rosemary focaccia</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$9.00</span></div> <p class="mi-desc">Dimpled deep, coarse sea salt, a generous pour of extra-virgin. Sold by the slice after noon.</p> <ul class="mi-tags"><li>vegan</li><li>sells out</li></ul> </li> <li class="menu-item reveal" data-cat="pastries"> <div class="mi-top"><h3 class="mi-name">Butter croissant</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$4.50</span></div> <p class="mi-desc">Twenty-seven layers of Normandy butter, laminated over three days. Still warm until about 10.</p> <ul class="mi-tags"><li>27 layers</li></ul> </li> <li class="menu-item reveal" data-cat="pastries"> <div class="mi-top"><h3 class="mi-name">Almond-cardamom bun</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$6.00</span></div> <p class="mi-desc">Brown-butter glaze, ground cardamom, a broken-almond crown. The one people cross town for.</p> <ul class="mi-tags"><li>house glaze</li></ul> </li> <li class="menu-item reveal" data-cat="pastries"> <div class="mi-top"><h3 class="mi-name">Chocolate babka, sliced</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$5.50</span></div> <p class="mi-desc">A brioche braided with 70% dark chocolate, brushed with demerara. Slice or whole (by order).</p> <ul class="mi-tags"><li>whole loaf: $22</li></ul> </li> <li class="menu-item reveal" data-cat="sweets"> <div class="mi-top"><h3 class="mi-name">Seasonal fruit galette</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$7.00</span></div> <p class="mi-desc">Whatever the orchard sent this week — ask at the counter. Folded crust, coarse sugar, no fuss.</p> <ul class="mi-tags"><li>ask what's in</li></ul> </li> <li class="menu-item reveal" data-cat="sweets"> <div class="mi-top"><h3 class="mi-name">Cinnamon knot</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$5.00</span></div> <p class="mi-desc">Ceylon cinnamon and a brown-sugar syrup that pools in the wrapper. Best eaten standing at the counter.</p> <ul class="mi-tags"><li>warm until 2</li></ul> </li> <li class="menu-item reveal" data-cat="sweets"> <div class="mi-top"><h3 class="mi-name">Oat & honey cookie</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$3.50</span></div> <p class="mi-desc">Thick, chewy middle, burnt edges, local wildflower honey. Three to a sleeve, always.</p> <ul class="mi-tags"><li>local honey</li></ul> </li> </ul> <p class="menu-note">* When the case is empty, the case is empty — the 4 a.m. tray goes first. Prices include the bag.</p> </div> </section> <!-- ============ OVEN / COUNTER ============ --> <section class="oven-band" aria-labelledby="counter-h"> <div class="wrap"> <p class="kicker">Live — the counter</p> <h2 id="counter-h">Today at the window</h2> <div class="oven-grid"> <div class="ov-card reveal"> <p class="ov-label">Oven status</p> <div class="ov-ring"> <svg class="ring" viewBox="0 0 120 120" aria-hidden="true"> <circle class="ring-bg" cx="60" cy="60" r="52"/> <circle class="ring-fg" id="ring-fg" cx="60" cy="60" r="52"/> </svg> <div class="ov-center"> <span id="ov-count" role="timer">--:--:--</span> <span class="ov-sub">to next tray</span> </div> </div> <p class="ov-next">Next out: <strong id="ov-name">—</strong> · <span id="ov-time">—</span></p> <p class="ov-status" id="ov-status">Firing the oven…</p> </div> <div class="ov-card reveal" style="--d:.08s"> <p class="ov-label">Build a box <span>pickup at 4 PM</span></p> <label class="box-row"><input type="checkbox" data-price="27" data-name="Croissants"><span class="box-name">Croissants <small>×6</small></span><span class="box-price">$27</span></label> <label class="box-row"><input type="checkbox" data-price="15" data-name="Baguettes"><span class="box-name">Thin baguettes <small>×3</small></span><span class="box-price">$15</span></label> <label class="box-row"><input type="checkbox" data-price="24" data-name="Cardamom buns"><span class="box-name">Cardamom buns <small>×4</small></span><span class="box-price">$24</span></label> <label class="box-row"><input type="checkbox" data-price="14" data-name="Galette halves"><span class="box-name">Galette, cut in half <small>×2</small></span><span class="box-price">$14</span></label> <div class="box-total"><span>Your box</span><strong id="box-total">$0.00</strong></div> <button id="box-btn" type="button" disabled>Hold at the counter</button> <p id="box-note" hidden>Held under “Guest” until 6 PM — show this page at pickup.</p> </div> <div class="ov-card reveal" style="--d:.16s"> <p class="ov-label">Sold out <span>today, sadly</span></p> <ul class="sold-list"> <li><span>Seeded rye</span><time>1:20 PM</time></li> <li><span>Olive focaccia</span><time>12:05 PM</time></li> <li><span>Cinnamon knots</span><time>11:58 AM</time></li> <li class="back"><span>Apple galette</span><time>back 3:00 PM</time></li> </ul> <p class="sold-foot">We bake to the case, not the clock. If it's gone, it was loved.</p> </div> </div> </div> </section> <!-- ============ STORY ============ --> <section id="story" class="section" aria-labelledby="story-h"> <div class="wrap story-grid"> <div class="story-left reveal"> <p class="kicker">No. 02 — Our story</p> <h2 id="story-h">Thirty years,<br>still on the corner.</h2> <div class="stats"> <div><b>1994</b><span>first bake</span></div> <div><b>4,100</b><span>loaves a week</span></div> <div><b>3</b><span>ovens, one timer</span></div> </div> </div> <div class="story-right"> <p class="reveal">Crumb & Co. started in a garage: Maren Kessler, a borrowed deck oven, and a sourdough starter she named Arthur, on this very corner of Alder Row. The first Saturday, the line wrapped past the mailbox. Arthur has never gone quiet since.</p> <p class="reveal" style="--d:.08s">We still mill a little of our own rye, proof everything overnight, and fire the cherry-oak oven at 4 a.m. sharp. Nothing is frozen, nothing is rushed, and the croissant still takes twenty-seven layers — that's the math, not the marketing.</p> <blockquote class="reveal" style="--d:.12s"> “A good loaf tells you when it's ready. We've just learned to listen.” <footer>— Maren K., first baker</footer> </blockquote> <ol class="timeline"> <li class="reveal"><span class="tl-year">1994</span><h3>The garage</h3><p>Two proofing baskets, one borrowed oven, a starter named Arthur.</p></li> <li class="reveal"><span class="tl-year">2003</span><h3>The wood oven</h3><p>Cherry oak, built by the Alders next door. We never went back to gas.</p></li> <li class="reveal"><span class="tl-year">2011</span><h3>The stone mill</h3><p>We started milling our own rye and spelt a block away, every Thursday.</p></li> <li class="reveal"><span class="tl-year">2019</span><h3>The apprentices</h3><p>Three paid seats a year. Flour on the uniform is encouraged, in fact required.</p></li> <li class="reveal"><span class="tl-year">2025</span><h3>The second floor</h3><p>A small coffee bar above the case. Same ovens, same 4 a.m. alarm.</p></li> </ol> </div> </div> </section> <!-- ============ VISIT ============ --> <section id="visit" class="section" aria-labelledby="visit-h"> <div class="wrap"> <div class="reveal"> <p class="kicker">No. 03 — Visit</p> <h2 id="visit-h">Follow the smell of butter.</h2> <p class="sec-lead">We're on the corner of Alder Row and Fourth, under the blue awning. No reservations and no Wi-Fi at the counter — just the trays and whatever the oven decided to do today.</p> </div> <div class="visit-grid"> <div class="reveal"> <span class="open-pill" id="open-pill">Checking the door…</span> <table class="hours"> <caption class="visually-hidden">Opening hours</caption> <tbody> <tr data-day="1"><th scope="row">Monday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="2"><th scope="row">Tuesday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="3"><th scope="row">Wednesday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="4"><th scope="row">Thursday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="5"><th scope="row">Friday</th><td>7:00 AM – 3:00 PM<small>pastries fly by 1</small></td></tr> <tr data-day="6"><th scope="row">Saturday</th><td>8:00 AM – 4:00 PM</td></tr> <tr data-day="0"><th scope="row">Sunday</th><td>8:00 AM – 1:00 PM<small>loaves gone by 12</small></td></tr> </tbody> </table> <p class="visit-addr"> <b>214 Alder Row, Portland, OR 97204</b><br> (503) 555-0119 · hello@crumbandco.bake<br> No reservations — the line moves fast, the tray moves faster. </p> </div> <figure class="map-fig reveal" style="--d:.1s"> <svg viewBox="0 0 440 320" role="img" aria-label="Hand-drawn map: Crumb and Co. sits at the corner of Alder Row and Fourth, north of the river"> <rect width="440" height="320" fill="#efe4cb"/> <path d="M-10 262 C 90 218, 160 306, 268 262 S 420 236, 460 278" stroke="#c9d4b8" stroke-width="26" fill="none"/> <g stroke="#dccba6" stroke-width="11"> <line x1="0" y1="70" x2="440" y2="70"/> <line x1="0" y1="150" x2="440" y2="150"/> <line x1="0" y1="230" x2="440" y2="230"/> <line x1="90" y1="0" x2="90" y2="320"/> <line x1="200" y1="0" x2="200" y2="320"/> <line x1="310" y1="0" x2="310" y2="320"/> </g> <g fill="#e9dcba"> <rect x="104" y="84" width="82" height="52"/> <rect x="214" y="84" width="82" height="52"/> <rect x="104" y="164" width="82" height="52"/> <rect x="324" y="84" width="102" height="52"/> </g> <path d="M90 230 L 200 150" stroke="#c47a2c" stroke-width="2.5" stroke-dasharray="6 6" fill="none"/> <circle cx="90" cy="230" r="5" fill="#c47a2c"/> <text x="36" y="256" font-family="monospace" font-size="11" fill="#8f4f1d">you, ideally at 7 AM</text> <circle cx="200" cy="150" r="17" fill="#e9b44c" stroke="#2b180b" stroke-width="3"/> <text x="200" y="155" text-anchor="middle" font-family="monospace" font-size="12" font-weight="bold" fill="#2b180b">C</text> <text x="222" y="134" font-family="monospace" font-size="11" font-weight="bold" fill="#2b180b">CRUMB & CO.</text> <text x="322" y="146" font-family="monospace" font-size="10" letter-spacing="3" fill="#8f7a52">ALDER ROW</text> <text x="196" y="212" font-family="monospace" font-size="10" letter-spacing="3" fill="#8f7a52" transform="rotate(-90 196 212)">4TH AVE</text> </svg> <figcaption>— not to scale; to butter</figcaption> </figure> </div> </div> </section> <div class="crumb-line" aria-hidden="true"></div> </main> <footer> <div class="wrap foot-grid"> <div> <p class="foot-word">Crumb & Co.</p> <p class="foot-tag">Baked the slow way since 1994, on the slow corner of Alder Row, Portland, Oregon. Come early; the trays are the truth.</p> </div> <div> <p class="foot-h">Opening hours</p> <ul class="hours-mini"> <li><span>Monday – Friday</span><span>7:00 AM – 3:00 PM</span></li> <li><span>Saturday</span><span>8:00 AM – 4:00 PM</span></li> <li><span>Sunday</span><span>8:00 AM – 1:00 PM</span></li> </ul> <p class="foot-note">Closed on major holidays. Last order fifteen minutes before close.</p> </div> <div> <p class="foot-h">Find us</p> <address>214 Alder Row, Portland, OR 97204<br>(503) 555-0119<br>hello@crumbandco.bake</address> <p class="foot-note">Follow the smell of butter, then the blue awning.</p> </div> </div> <div class="wrap foot-base"> <span>© <span id="yr">2025</span> Crumb & Co. — all crumbs reserved.</span> <span>Flour · water · salt · time</span> </div> </footer> <script> (function(){ "use strict"; var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; /* seamless ticker */ document.querySelectorAll(".ticker-track").forEach(function(t){ t.innerHTML += t.innerHTML; }); /* reveal on scroll */ var revealEls = document.querySelectorAll(".reveal"); if (reduceMotion || !("IntersectionObserver" in window)) { revealEls.forEach(function(el){ el.classList.add("in"); }); } else { var io = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }); }, { threshold:.12, rootMargin:"0px 0px -8%" }); revealEls.forEach(function(el){ io.observe(el); }); } /* gentle stagger inside the menu list */ document.querySelectorAll("#menu-list .menu-item").forEach(function(li,i){ li.style.setProperty("--d", ((i % 6) * 0.05) + "s"); }); /* scrollspy for the 3 nav links */ var links = Array.prototype.slice.call(document.querySelectorAll(".navbar a")); if ("IntersectionObserver" in window) { var spy = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if (e.isIntersecting) { var id = "#" + e.target.id; links.forEach(function(a){ a.classList.toggle("active", a.getAttribute("href") === id); }); } }); }, { rootMargin:"-30% 0px -60% 0px" }); links.forEach(function(a){ var s = document.querySelector(a.getAttribute("href")); if (s) spy.observe(s); }); } /* ----- oven countdown ----- */ var SCHEDULE = [ ["8:00","Country sourdough"], ["11:30","Butter croissants"], ["14:00","Focaccia & knots"], ["16:30","Babka & galettes"] ]; function toSec(t){ var p = t.split(":"); return (+p[0])*3600 + (+p[1])*60; } function pad(n){ return (n < 10 ? "0" : "") + n; } var C = 2 * Math.PI * 52; var ring = document.getElementById("ring-fg"); ring.style.strokeDasharray = C; var ovCount = document.getElementById("ov-count"), ovName = document.getElementById("ov-name"), ovTime = document.getElementById("ov-time"), ovStat = document.getElementById("ov-status"), chip = document.getElementById("oven-chip"); function ovenTick(){ var now = new Date(); var n = now.getHours()*3600 + now.getMinutes()*60 + now.getSeconds(); var idx = -1; for (var i = 0; i < SCHEDULE.length; i++) { if (toSec(SCHEDULE[i][0]) > n) { idx = i; break; } } var next, prev, tomorrow = false; if (idx === -1) { idx = 0; next = toSec(SCHEDULE[0][0]) + 86400; prev = toSec(SCHEDULE[3][0]); tomorrow = true; } else { next = toSec(SCHEDULE[idx][0]); prev = idx > 0 ? toSec(SCHEDULE[idx-1][0]) : 0; } var rem = next - n; var h = Math.floor(rem/3600), m = Math.floor((rem%3600)/60), s = rem%60; ovCount.textContent = pad(h)+":"+pad(m)+":"+pad(s); var prog = Math.min(1, Math.max(0, (n - prev) / (next - prev))); ring.style.strokeDashoffset = C * (1 - prog); ovName.textContent = SCHEDULE[idx][1]; ovTime.textContent = SCHEDULE[idx][0] + (tomorrow ? " (tomorrow)" : ""); ovStat.textContent = rem > 1800 ? "Proofing & shaping" : rem > 900 ? "Firing the oven" : rem > 300 ? "Loading the trays" : rem > 45 ? "Out the door any second" : "Fresh — grab a tray"; chip.textContent = "Oven · fires " + SCHEDULE[idx][0] + (tomorrow ? " +1d" : ""); } ovenTick(); setInterval(ovenTick, 1000); /* ----- menu filter ----- */ var tabs = document.querySelectorAll(".tab"), items = Array.prototype.slice.call(document.querySelectorAll("#menu-list .menu-item")), count = document.getElementById("menu-count"); tabs.forEach(function(btn){ btn.addEventListener("click", function(){ tabs.forEach(function(b){ b.setAttribute("aria-pressed", b === btn ? "true" : "false"); }); var f = btn.getAttribute("data-filter"), shown = 0; items.forEach(function(li){ var show = f === "all" || li.getAttribute("data-cat") === f; li.classList.toggle("off", !show); if (show) shown++; }); count.textContent = shown + (shown === 1 ? " bake" : " bakes") + " on the case" + (f !== "all" ? " · " + f : ""); }); }); /* ----- build a box ----- */ var boxInputs = Array.prototype.slice.call(document.querySelectorAll(".box-row input")), boxTotal = document.getElementById("box-total"), boxBtn = document.getElementById("box-btn"), boxNote = document.getElementById("box-note"); function pop(el){ el.classList.remove("pop"); void el.offsetWidth; el.classList.add("pop"); } function boxUpdate(){ var t = 0, any = false; boxInputs.forEach(function(i){ if (i.checked) { t += +i.getAttribute("data-price"); any = true; } }); boxTotal.textContent = "$" + t.toFixed(2); boxBtn.disabled = !any; if (!any) { boxNote.hidden = true; boxBtn.classList.remove("held"); boxBtn.textContent = "Hold at the counter"; } pop(boxTotal); } boxInputs.forEach(function(i){ i.addEventListener("change", function(){ boxBtn.classList.remove("held"); boxBtn.textContent = "Hold at the counter"; boxNote.hidden = true; boxUpdate(); }); }); boxBtn.addEventListener("click", function(){ if (boxBtn.disabled) return; boxBtn.classList.add("held"); boxBtn.textContent = "Held ✓ — see you soon"; boxNote.hidden = false; }); /* ----- croissant (boule) easter egg ----- */ var bread = document.getElementById("bread"); if (bread) bread.addEventListener("click", function(){ if (reduceMotion) return; bread.classList.remove("pop"); void bread.getBoundingClientRect(); bread.classList.add("pop"); }); /* ----- open / closed + today's row ----- */ var HOURS = [[480,780],[420,900],[420,900],[420,900],[420,900],[420,900],[480,960]]; /* Sun..Sat */ var DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; function fmt(mins){ var h = Math.floor(mins/60), m = mins%60, ap = h >= 12 ? "PM" : "AM"; var h12 = h % 12 === 0 ? 12 : h % 12; return h12 + ":" + pad(m) + " " + ap; } (function openStatus(){ var now = new Date(), d = now.getDay(), nowM = now.getHours()*60 + now.getMinutes(); var row = document.querySelector('.hours tr[data-day="' + d + '"]'); if (row) { var th = row.querySelector("th"); var chipEl = document.createElement("span"); chipEl.className = "today-chip"; chipEl.textContent = "today"; th.appendChild(chipEl); } var o = HOURS[d][0], c = HOURS[d][1], pill = document.getElementById("open-pill"); var msg, open = nowM >= o && nowM < c; if (open) msg = "Open now — closes " + fmt(c); else if (nowM < o) msg = "Closed — opens today at " + fmt(o); else { var nd = d, found = false; for (var k = 1; k <= 7; k++) { nd = (d + k) % 7; if (HOURS[nd][0] !== HOURS[nd][1]) { msg = "Closed — opens " + (k === 1 ? "tomorrow" : DAYS[nd]) + " at " + fmt(HOURS[nd][0]); found = true; break; } } if (!found) msg = "Closed"; } pill.textContent = msg; pill.classList.add(open ? "now" : "later"); })(); document.getElementById("yr").textContent = new Date().getFullYear(); })(); </script> </body> </html>Build SnakeFailed
37,624 tokens: 497 read, 37,127 written · 27 min 47 s · 22.3 tok/s
Build Snake as one web page on a 10 by 10 grid, playable with the arrow keys (one key press, one step), with the grid drawn on the page. In <script id="game">, define createGame() returning an object with step(dir) and state(). dir is "up", "down", "left" or "right". state() returns {snake, food, score, over}: snake is a list of [x, y] cells, head first; food is [x, y]; score is a number; over is a boolean. x is the column (0 to 9, left to right), y the row (0 to 9, top to bottom); up is y - 1. The rules, exactly: 1. The game starts with snake [[3, 3], [4, 3], [5, 3]], moving left, food at [0, 2], score 0. 2. step(dir): if dir is the opposite of the current direction it is ignored and the snake keeps going; otherwise dir becomes the current direction. The head moves one cell that way. 3. If the new head cell holds the food, the snake grows: its tail stays. Otherwise the tail cell is freed first. 4. If the new head is off the grid or on a cell of the snake (after rule 3), the game is over: over becomes true and nothing else changes. Once over, step does nothing. 5. Eating adds 1 to the score, and the next food goes to the first empty cell in reading order (row by row from the top, left to right) after the new head's cell, wrapping from [9, 9] to [0, 0]. Output one complete HTML document and nothing else, with all CSS and JavaScript inline and nothing loaded from another site. Put the game logic, and only it, in <script id="game">: it must not touch document, window or timers, because the site runs that script on its own and plays the game through createGame(). Wire the page in another <script>.Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Snake · 10 × 10</title> <style> :root{ --bg:#0a110d; --line:#223b2d; --line-soft:#182b20; --cell:#12241b; --cell-alt:#0f1f17; --gridline:#0b150f; --grass:#3fe08d; --amber:#ffb454; --cream:#e9f5ec; --dim:#8fae9d; --display: Impact, "Arial Black", "Franklin Gothic Medium", "Trebuchet MS", sans-serif; --body: "Trebuchet MS", "Segoe UI", Verdana, system-ui, sans-serif; --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, "Courier New", monospace; } *{ box-sizing:border-box; margin:0; padding:0; } html{ color-scheme:dark; } ::selection{ background:rgba(63,224,141,.3); } body{ min-height:100vh; font-family:var(--body); color:var(--cream); padding:28px clamp(16px,4vw,44px) 40px; overflow-x:hidden; background: radial-gradient(1000px 620px at 10% -12%, rgba(63,224,141,.10), transparent 60%), radial-gradient(820px 520px at 108% 12%, rgba(255,180,84,.07), transparent 55%), radial-gradient(900px 700px at 50% 118%, rgba(63,224,141,.07), transparent 60%), var(--bg); } body::before{ content:""; position:fixed; inset:-60px; z-index:-1; background-image: linear-gradient(rgba(122,224,170,.05) 1px, transparent 1px), linear-gradient(90deg, rgba(122,224,170,.05) 1px, transparent 1px); background-size:34px 34px; animation:drift 26s linear infinite; } body::after{ content:""; position:fixed; inset:0; z-index:-1; pointer-events:none; background: radial-gradient(120% 90% at 50% 40%, transparent 55%, rgba(2,6,4,.55) 100%); } @keyframes drift{ to{ transform:translate3d(34px,34px,0); } } /* ---------- header ---------- */ .top{ display:flex; align-items:flex-end; justify-content:space-between; gap:20px; flex-wrap:wrap; max-width:1080px; margin:0 auto 26px; } .brand{ display:flex; gap:18px; align-items:center; } .mark{ width:58px; height:58px; flex:none; filter:drop-shadow(0 0 14px rgba(63,224,141,.35)); animation:markbob 4s ease-in-out infinite; } @keyframes markbob{ 50%{ transform:translateY(-4px) rotate(-3deg); } } .overline{ font-family:var(--mono); font-size:11px; letter-spacing:.32em; text-transform:uppercase; color:var(--dim); margin-bottom:8px; } h1{ font-family:var(--display); font-weight:400; font-size:clamp(46px,7vw,76px); line-height:.85; letter-spacing:.015em; color:#f1faf4; text-shadow:.05em .05em 0 #14402b, .1em .1em 0 rgba(14,46,30,.6), 0 0 34px rgba(63,224,141,.28); } h1 .dot{ color:var(--amber); text-shadow:.05em .05em 0 #52350f, 0 0 22px rgba(255,180,84,.55); } .badges{ display:flex; flex-direction:column; gap:8px; align-items:flex-end; } .chip{ font-family:var(--mono); font-size:11px; letter-spacing:.18em; color:#bfe8cf; border:1px solid var(--line); background:rgba(63,224,141,.05); padding:7px 12px; border-radius:4px; } .chip:last-child{ border-color:rgba(255,180,84,.35); color:#ffd9a3; background:rgba(255,180,84,.06); } /* ---------- layout ---------- */ .layout{ display:grid; grid-template-columns:minmax(0,600px) minmax(250px,340px); gap:26px; max-width:1080px; margin:0 auto; align-items:start; } @media (max-width:920px){ .layout{ grid-template-columns:minmax(0,600px); justify-content:center; } .panel{ max-width:600px; width:100%; margin:0 auto; } .badges{ flex-direction:row; align-items:center; } } .card{ background:linear-gradient(180deg,#111d15,#0d1710); border:1px solid var(--line); border-radius:16px; padding:16px; position:relative; box-shadow:0 24px 60px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.04); } .board-top{ display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px; padding:2px 4px 12px; } .status{ display:flex; align-items:center; gap:9px; font-family:var(--mono); font-size:11.5px; letter-spacing:.22em; text-transform:uppercase; color:#9fe8c2; } .dot-live{ width:9px; height:9px; border-radius:50%; background:var(--grass); box-shadow:0 0 10px var(--grass); animation:blink 1.4s ease-in-out infinite; } .status.is-over{ color:#ff9d8a; } .status.is-over .dot-live{ background:#ff6a4f; box-shadow:0 0 10px #ff6a4f; animation:none; } @keyframes blink{ 50%{ opacity:.35; transform:scale(.8); } } .hud-mini{ font-family:var(--mono); font-size:12px; color:var(--dim); letter-spacing:.08em; } .hud-mini b{ color:var(--amber); font-size:13px; } /* ---------- the grid ---------- */ .gridwrap{ position:relative; } .grid{ display:grid; grid-template-columns:repeat(10,1fr); grid-template-rows:repeat(10,1fr); gap:3px; padding:6px; position:relative; aspect-ratio:1; background:var(--gridline); border:1px solid var(--line-soft); border-radius:12px; transition:filter .5s; } .grid.dead{ filter:saturate(.45) brightness(.82); animation:shake .55s cubic-bezier(.36,.07,.19,.97); } @keyframes shake{ 10%{ transform:translate(-3px,1px); } 30%{ transform:translate(3px,-2px); } 50%{ transform:translate(-3px,-1px); } 70%{ transform:translate(2px,2px); } 90%{ transform:translate(-1px,1px); } } .cell{ position:relative; background:var(--cell); border-radius:5px; transition:background .18s ease; } .cell.alt{ background:var(--cell-alt); } @media (hover:hover){ .cell:hover{ background:#173022; } } .cell.food{ background:#1c2517; } .cell.food::before{ content:""; position:absolute; inset:17%; border-radius:50%; background:radial-gradient(circle at 34% 30%, #ffc2ad, #ff6a4f 48%, #d43a26 78%, #9c2416); box-shadow:0 0 12px rgba(255,106,79,.5), 0 0 26px rgba(255,106,79,.22); animation:bob 1.1s ease-in-out infinite; } .cell.food::after{ content:""; position:absolute; top:11%; left:55%; width:22%; height:13%; background:#8fe06e; border-radius:0 70% 0 70%; transform:rotate(-30deg); box-shadow:0 0 8px rgba(143,224,110,.45); animation:swaying 1.1s ease-in-out infinite; } @keyframes bob{ 50%{ transform:scale(1.14) translateY(-5%); } } @keyframes swaying{ 50%{ transform:rotate(-22deg) translate(1px,-1px); } } .cell.body::before{ content:""; position:absolute; inset:9%; border-radius:32%; background:linear-gradient(145deg, #37cf85, #1d9a5e 60%, #127144); box-shadow:inset 0 -3px 5px rgba(0,40,22,.4), 0 0 6px rgba(47,182,106,.25); opacity:max(.3, calc(1 - var(--i,0) * .03)); transform:scale(calc(1 - var(--i,0) * .012)); } .cell.head{ z-index:2; } .cell.head::before{ content:""; position:absolute; inset:3%; border-radius:36%; background:linear-gradient(150deg, #8cf7bb, #34d489 55%, #17a363); box-shadow:0 0 14px rgba(63,224,141,.55), 0 0 34px rgba(63,224,141,.2), inset 0 -4px 8px rgba(0,50,26,.4); animation:breathe 1.5s ease-in-out infinite; } @keyframes breathe{ 50%{ transform:scale(1.05); } } .face{ position:absolute; inset:0; z-index:3; pointer-events:none; transition:transform .13s ease; } .cell.head.dir-down .face{ transform:rotate(90deg); } .cell.head.dir-left .face{ transform:rotate(180deg); } .cell.head.dir-up .face{ transform:rotate(270deg); } .eyes{ position:absolute; right:19%; top:25%; width:26%; height:50%; display:none; } .cell.head .eyes{ display:block; } .eyes i{ position:absolute; right:0; width:64%; aspect-ratio:1/1; border-radius:50%; background:#052617; box-shadow:inset 0 -1px 1px rgba(255,255,255,.25); transition:all .2s; } .eyes i:first-child{ top:0; } .eyes i:last-child{ bottom:0; } .grid.dead .eyes i{ border-radius:3px; aspect-ratio:auto; width:100%; height:24%; } /* ---------- overlay ---------- */ .overlay{ position:absolute; inset:0; z-index:10; display:grid; place-items:center; border-radius:12px; background:radial-gradient(90% 90% at 50% 45%, rgba(8,15,11,.82), rgba(5,10,8,.94)); opacity:0; pointer-events:none; transition:opacity .35s ease; } .overlay.show{ opacity:1; pointer-events:auto; } .over-box{ text-align:center; padding:20px; } .overlay.show .over-box{ animation:popin .45s cubic-bezier(.2,1.4,.4,1); } @keyframes popin{ from{ transform:scale(.8); opacity:0; } } .over-kicker{ font-family:var(--mono); font-size:11px; letter-spacing:.3em; text-transform:uppercase; color:var(--dim); margin-bottom:10px; } .over-box h2{ font-family:var(--display); font-weight:400; font-size:clamp(34px,5vw,52px); letter-spacing:.02em; color:#ff8a70; margin-bottom:8px; text-shadow:.06em .06em 0 #4a160c, 0 0 30px rgba(255,106,79,.4); } .over-score{ font-family:var(--mono); color:var(--dim); font-size:14px; margin-bottom:18px; } .over-score b{ color:var(--amber); font-size:20px; } .over-hint{ margin-top:14px; font-size:12px; color:var(--dim); } kbd{ font-family:var(--mono); font-size:11px; background:#15251c; border:1px solid #2c4736; border-bottom-width:3px; border-radius:5px; padding:2px 7px; color:#cfe9da; } .plus{ position:absolute; z-index:15; transform:translate(-50%,-50%); font-family:var(--mono); font-weight:700; font-size:15px; color:var(--amber); pointer-events:none; text-shadow:0 0 10px rgba(255,180,84,.9), 0 2px 3px rgba(0,0,0,.6); animation:rise .85s ease-out forwards; } @keyframes rise{ 0%{ opacity:0; margin-top:6px; } 18%{ opacity:1; } 100%{ opacity:0; margin-top:-30px; } } /* ---------- dpad ---------- */ .dpad{ display:grid; grid-template-columns:repeat(3,54px); grid-auto-rows:46px; gap:9px; justify-content:center; margin-top:16px; user-select:none; } .key{ font-family:var(--mono); font-size:17px; color:#a9dcc0; cursor:pointer; background:linear-gradient(180deg,#15271d,#0e1c14); border:1px solid #27452f; border-bottom-width:4px; border-radius:9px; transition:transform .08s, box-shadow .15s, color .15s, border-color .15s, background .15s; } .key:hover{ color:#e5fbef; border-color:rgba(63,224,141,.4); box-shadow:0 0 14px rgba(63,224,141,.18); } .key.lit, .key:active{ transform:translateY(3px); border-bottom-width:1px; color:#8cf7bb; background:linear-gradient(180deg,#1d3a2b,#14281d); border-color:#3fe08d; box-shadow:inset 0 0 18px rgba(63,224,141,.3); } .key:focus-visible{ outline:2px solid var(--amber); outline-offset:2px; } .key[data-dir="up"]{ grid-column:2; grid-row:1; } .key[data-dir="left"]{ grid-column:1; grid-row:2; } .key[data-dir="down"]{ grid-column:2; grid-row:2; } .key[data-dir="right"]{ grid-column:3; grid-row:2; } /* ---------- side panel ---------- */ .panel{ background:linear-gradient(180deg,#101c15,#0c150f); border:1px solid var(--line); border-radius:16px; padding:20px; display:flex; flex-direction:column; gap:16px; box-shadow:0 24px 60px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.04); } .stat-hero{ background:radial-gradient(120% 140% at 20% 0%, rgba(63,224,141,.10), transparent 60%), #0d1a12; border:1px solid var(--line); border-radius:12px; padding:14px 18px 12px; } .label{ display:block; font-family:var(--mono); font-size:10.5px; letter-spacing:.3em; text-transform:uppercase; color:var(--dim); margin-bottom:4px; } .value{ display:inline-block; font-family:var(--mono); font-weight:700; font-size:52px; line-height:1; color:#eafff3; text-shadow:0 0 24px rgba(63,224,141,.35); } .value.pop{ animation:scorepop .45s ease; } @keyframes scorepop{ 30%{ transform:scale(1.28); color:var(--amber); text-shadow:0 0 30px rgba(255,180,84,.7); } } .mini-stats{ display:grid; grid-template-columns:1fr 1fr; gap:10px; } .mini-stats .stat{ background:#0d1a12; border:1px solid var(--line-soft); border-radius:10px; padding:10px 14px; } .mid{ font-family:var(--mono); font-size:22px; font-weight:700; color:#c9f5dd; } .rule{ border:none; border-top:1px dashed #24402f; } .how-title{ font-family:var(--mono); font-size:11px; letter-spacing:.28em; text-transform:uppercase; color:#7fae94; } .how{ list-style:none; display:flex; flex-direction:column; gap:10px; font-size:13.5px; color:#b9d4c4; line-height:1.45; } .how li{ padding-left:16px; position:relative; } .how li::before{ content:"▸"; position:absolute; left:0; color:var(--grass); } .kbdmini{ font-family:var(--mono); font-size:11px; color:#9fe8c2; background:#132319; border:1px solid #2c4736; border-radius:4px; padding:1px 6px; } .apple-dot{ display:inline-block; width:10px; height:10px; border-radius:50%; vertical-align:-1px; background:radial-gradient(circle at 34% 30%, #ffc2ad, #ff6a4f 60%, #a52a1c); box-shadow:0 0 8px rgba(255,106,79,.6); } .btn{ font-family:var(--body); font-weight:700; font-size:15px; letter-spacing:.03em; border:none; cursor:pointer; border-radius:10px; padding:12px 22px; color:#05261a; background:linear-gradient(180deg,#58e99c,#22b371); box-shadow:0 5px 0 #0d5c37, 0 12px 24px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.4); transition:transform .12s ease, box-shadow .12s ease, filter .2s; } .btn:hover{ transform:translateY(-2px); filter:brightness(1.05); box-shadow:0 7px 0 #0d5c37, 0 16px 30px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.4); } .btn:active{ transform:translateY(3px); box-shadow:0 2px 0 #0d5c37, 0 6px 14px rgba(0,0,0,.35); } .btn:focus-visible{ outline:2px solid var(--amber); outline-offset:3px; } .btn.ghost{ background:transparent; color:#9fe8c2; box-shadow:inset 0 0 0 1.5px #2c4a38; } .btn.ghost:hover{ color:#c9f5dd; transform:translateY(-1px); box-shadow:inset 0 0 0 1.5px #3fe08d, 0 0 18px rgba(63,224,141,.15); } .btn.ghost:active{ transform:translateY(1px); } #restartBtn{ width:100%; } .foot{ max-width:1080px; margin:26px auto 0; text-align:center; color:#5f7a6b; font-size:12.5px; font-family:var(--mono); letter-spacing:.04em; } .foot code{ color:#8fd9b3; } @media (prefers-reduced-motion: reduce){ *, *::before, *::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } } </style> </head> <body> <header class="top"> <div class="brand"> <svg class="mark" viewBox="0 0 64 64" aria-hidden="true"> <defs> <linearGradient id="sg" x1="0" y1="0" x2="1" y2="1"> <stop offset="0" stop-color="#58e99c"/><stop offset="1" stop-color="#157a4a"/> </linearGradient> </defs> <path d="M42 12 C42 30 18 30 18 42 C18 54 46 54 46 42 C46 33 30 32 26 38" fill="none" stroke="url(#sg)" stroke-width="8" stroke-linecap="round"/> <circle cx="42" cy="12" r="8" fill="#5ce99d"/> <circle cx="44.5" cy="9.5" r="1.8" fill="#06281a"/> <path d="M49 7 L56 3 M49 7 L57 9" stroke="#ff6a4f" stroke-width="2" stroke-linecap="round"/> </svg> <div> <p class="overline">Classic arcade · ten by ten · one press, one step</p> <h1>SNAKE<span class="dot">.</span></h1> </div> </div> <div class="badges"> <span class="chip">10 × 10 GRID</span> <span class="chip">ARROWS ONLY</span> </div> </header> <main class="layout"> <section class="card" aria-label="Snake board"> <div class="board-top"> <span class="status" id="statusWrap" aria-live="polite"><i class="dot-live"></i><span id="status">Running</span></span> <span class="hud-mini">SCORE <b id="scoreMini">0</b> · STEP <b id="steps">0</b></span> </div> <div class="gridwrap"> <div id="board" class="grid" aria-label="10 by 10 game grid"></div> <div class="overlay" id="overlay"> <div class="over-box"> <p class="over-kicker">The wall won this time</p> <h2>GAME OVER</h2> <p class="over-score">final score <b id="finalScore">0</b></p> <button class="btn" id="againBtn">Play again ↻</button> <p class="over-hint">or press <kbd>Enter</kbd></p> </div> </div> </div> <div class="dpad" aria-label="On-screen controls"> <button class="key" data-dir="up" aria-label="Move up">↑</button> <button class="key" data-dir="left" aria-label="Move left">←</button> <button class="key" data-dir="down" aria-label="Move down">↓</button> <button class="key" data-dir="right" aria-label="Move right">→</button> </div> </section> <aside class="panel" aria-label="Scoreboard and rules"> <div class="stat-hero"> <span class="label">Score</span> <span class="value" id="score">0</span> </div> <div class="mini-stats"> <div class="stat"><span class="label">Best</span><span class="mid" id="best">0</span></div> <div class="stat"><span class="label">Length</span><span class="mid" id="len">3</span></div> </div> <hr class="rule"> <h3 class="how-title">Rules of the run</h3> <ul class="how"> <li><span class="kbdmini">↑ ↓ ← →</span> One key press moves the snake <b>one cell</b>.</li> <li>Steer into the <span class="apple-dot"></span> apple to grow — +1 point each.</li> <li>The next apple lands on the first free cell after your head, reading left → right, top → down.</li> <li>Hitting a wall or your own body ends the run.</li> <li>Pressing the opposite of your heading is ignored — you keep going straight.</li> </ul> <button class="btn ghost" id="restartBtn">⟳ Restart run</button> </aside> </main> <footer class="foot"> <p>Grid logic lives in <code><script id="game"></code> — pure functions, no DOM, no timers. The rest of this page just draws it.</p> </footer> <script id="game"> // Pure game logic. No DOM, no window, no timers. function createGame() { var SIZE = 10; var CELLS = SIZE * SIZE; var OPPOSITE = { up: "down", down: "up", left: "right", right: "left" }; var DELTA = { up: [0, -1], down: [0, 1], left: [-1, 0], right: [1, 0] }; var snake = [[3, 3], [4, 3], [5, 3]]; // head first var dir = "left"; var food = [0, 2]; var score = 0; var over = false; function placeFood(afterX, afterY) { // First empty cell in reading order after [afterX, afterY], wrapping at [9,9]. var start = afterY * SIZE + afterX; var taken = {}; for (var i = 0; i < snake.length; i++) { taken[snake[i][1] * SIZE + snake[i][0]] = true; } for (var k = 1; k <= CELLS; k++) { var cell = (start + k) % CELLS; if (!taken[cell]) { food = [cell % SIZE, Math.floor(cell / SIZE)]; return; } } // Board completely full: no empty cell exists; leave food as-is. } function step(input) { if (over) return; if (OPPOSITE[input] === undefined) return; // Opposite of the current direction is ignored; otherwise it becomes the heading. if (input !== OPPOSITE[dir]) dir = input; var d = DELTA[dir]; var head = snake[0]; var nx = head[0] + d[0]; var ny = head[1] + d[1]; var eating = (nx === food[0] && ny === food[1]); if (!eating) snake.pop(); // tail cell freed first var inGrid = nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE; var hitSelf = false; if (inGrid) { for (var i = 0; i < snake.length; i++) { if (snake[i][0] === nx && snake[i][1] === ny) { hitSelf = true; break; } } } if (!inGrid || hitSelf) { over = true; // nothing else changes return; } snake.unshift([nx, ny]); if (eating) { score += 1; placeFood(nx, ny); } } function state() { var s = []; for (var i = 0; i < snake.length; i++) s.push([snake[i][0], snake[i][1]]); return { snake: s, food: [food[0], food[1]], score: score, over: over }; } return { step: step, state: state }; } </script> <script> (function () { "use strict"; var SIZE = 10; var gridEl = document.getElementById("board"); var scoreEl = document.getElementById("score"); var scoreMiniEl = document.getElementById("scoreMini"); var bestEl = document.getElementById("best"); var lenEl = document.getElementById("len"); var stepsEl = document.getElementById("steps"); var statusEl = document.getElementById("status"); var statusWrap = document.getElementById("statusWrap"); var overlay = document.getElementById("overlay"); var finalScoreEl = document.getElementById("finalScore"); var cells = []; // Build the 10 x 10 board once. for (var y = 0; y < SIZE; y++) { for (var x = 0; x < SIZE; x++) { var c = document.createElement("div"); c.className = "cell" + (((x + y) % 2) ? " alt" : ""); var face = document.createElement("span"); face.className = "face"; face.innerHTML = '<span class="eyes"><i></i><i></i></span>'; c.appendChild(face); gridEl.appendChild(c); cells.push(c); } } function loadBest() { try { return Math.max(0, parseInt(localStorage.getItem("snake10.best"), 10) || 0); } catch (e) { return 0; } } function saveBest(v) { try { localStorage.setItem("snake10.best", String(v)); } catch (e) {} } var game = createGame(); var lastHead = null; var lastDir = "left"; // matches the starting heading var lastScore = 0; var steps = 0; var best = loadBest(); var DIR_KEYS = { ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right" }; var keyBtns = {}; var keyList = document.querySelectorAll(".key[data-dir]"); for (var i = 0; i < keyList.length; i++) keyBtns[keyList[i].getAttribute("data-dir")] = keyList[i]; function flashKey(d) { var b = keyBtns[d]; if (!b) return; b.classList.add("lit"); clearTimeout(b._t); b._t = setTimeout(function () { b.classList.remove("lit"); }, 160); } function dirBetween(a, b) { var dx = b[0] - a[0], dy = b[1] - a[1]; if (dx === 1 && dy === 0) return "right"; if (dx === -1 && dy === 0) return "left"; if (dx === 0 && dy === 1) return "down"; if (dx === 0 && dy === -1) return "up"; return null; } function popEl(el) { el.classList.remove("pop"); void el.offsetWidth; // restart the animation el.classList.add("pop"); } function plusOne(px, py) { var c = cells[py * SIZE + px]; var el = document.createElement("span"); el.className = "plus"; el.textContent = "+1"; el.style.left = (c.offsetLeft + c.offsetWidth / 2) + "px"; el.style.top = (c.offsetTop + c.offsetHeight / 2) + "px"; gridEl.appendChild(el); el.addEventListener("animationend", function () { el.remove(); }); } function render() { var s = game.state(); var head = s.snake[0]; if (lastHead && (lastHead[0] !== head[0] || lastHead[1] !== head[1])) { var moved = dirBetween(lastHead, head); if (moved) lastDir = moved; } lastHead = [head[0], head[1]]; var occupied = new Int16Array(SIZE * SIZE).fill(-1); for (var i = 0; i < s.snake.length; i++) { occupied[s.snake[i][1] * SIZE + s.snake[i][0]] = i; } for (var y2 = 0; y2 < SIZE; y2++) { for (var x2 = 0; x2 < SIZE; x2++) { var idx = y2 * SIZE + x2; var cell = cells[idx]; var cls = "cell" + (((x2 + y2) % 2) ? " alt" : ""); var si = occupied[idx]; if (si === 0) cls += " head dir-" + lastDir; else if (si > 0) { cls += " body"; cell.style.setProperty("--i", si); } if (s.food && s.food[0] === x2 && s.food[1] === y2) cls += " food"; cell.className = cls; } } if (s.score !== lastScore) { if (s.score > lastScore) { popEl(scoreEl); plusOne(head[0], head[1]); } lastScore = s.score; } scoreEl.textContent = s.score; scoreMiniEl.textContent = s.score; lenEl.textContent = s.snake.length; stepsEl.textContent = steps; if (s.score > best) { best = s.score; saveBest(best); } bestEl.textContent = best; if (s.over && !gridEl.classList.contains("dead")) { gridEl.classList.add("dead"); statusWrap.classList.add("is-over"); finalScoreEl.textContent = s.score; overlay.classList.add("show"); } statusEl.textContent = s.over ? "Game over" : "Running"; } function play(d) { if (game.state().over) return; steps++; flashKey(d); game.step(d); render(); } function restart() { game = createGame(); lastHead = null; lastDir = "left"; lastScore = 0; steps = 0; gridEl.classList.remove("dead"); statusWrap.classList.remove("is-over"); overlay.classList.remove("show"); render(); } window.addEventListener("keydown", function (e) { var d = DIR_KEYS[e.key]; if (d) { e.preventDefault(); play(d); return; } if (game.state().over && (e.key === "Enter" || e.key === " " || e.key === "r" || e.key === "R")) { e.preventDefault(); restart(); } }); for (var k = 0; k < keyList.length; k++) { (function (btn) { btn.addEventListener("click", function () { play(btn.getAttribute("data-dir")); }); })(keyList[k]); } document.getElementById("againBtn").addEventListener("click", restart); document.getElementById("restartBtn").addEventListener("click", restart); bestEl.textContent = best; render(); })(); </script> </body> </html>
What it built
Pages and games, as the model wrote them, each in a sandbox. On a computer, click a game and use the arrow keys.
-
Build 2048Keeps a game's rules across many moves.Passed · 37k tokens · 22.6 tok/s
-
Build SnakeRuns a game where a snake grows and the game ends on a crash.Failed · 38k tokens · 22.3 tok/s
-
A small websiteLays out a small website with a menu and a footer.Failed · 31k tokens · 21.9 tok/s
-
A pricing table with a toggleSwitches every price between monthly and yearly with one button.Passed · 14k tokens · 22.6 tok/s
-
An accessible sign-up formChecks a sign-up form and labels each of its fields.Passed · 16k tokens · 23.5 tok/s
-
A to-do app that remembersKeeps data across a reload.Passed · 22k tokens · 23 tok/s
-
A bar chart in SVGDraws data to scale.Passed · 4.5k tokens · 24.6 tok/s
Scored tasks
Summarizing 5 of 5
One-sentence summaryPassed
777 tokens: 142 read, 635 written · 59 s · 26.1 tok/s
Passes when One sentence of 5 to 30 words saying the library opens earlier (8 a.m.) on weekdays.
Summarize this in one sentence of at most 30 words. Starting March 3, the Millbrook Public Library will open at 8 a.m. instead of 10 a.m. on weekdays. The change follows a survey in which 62% of respondents asked for earlier hours. Weekend hours stay the same. The library will hire two part-time staff to cover the extra time, paid for by a state grant.
Following a survey, Millbrook Public Library will open weekdays at 8 a.m. starting March 3, adding two grant-funded part-time staff, while weekend hours remain unchanged.
Meeting notes in three bulletsPassed
889 tokens: 236 read, 653 written · 26 s · 25.9 tok/s
Passes when Exactly three bullet points and nothing else, each at most 20 words, covering the launch moving to June 12, the approved marketing budget and Priya owning testing, and leaving out the webhook fix and the coffee machine.
Summarize these meeting notes as exactly three bullet points, each at most 20 words. Keep only the decisions and ownership changes that matter. Output only the bullet points. Weekly sync, product team. Present: Priya, Omar, Jules, Sam. - Omar reported that the payment provider fixed the webhook bug; no action needed. - The launch date moves from June 5 to June 12 because the app store review is slow. - Jules showed the new onboarding screens. Everyone liked them. Small copy changes only. - The marketing budget of $40,000 was approved by finance on Monday. - Priya will own end-to-end testing from now on; Sam hands over the test plan by Friday. - Next sync is moved to Thursday at 10:00. - Coffee machine on floor 3 is broken again.
- Launch date moved from June 5 to June 12 due to slow app store review. - Finance approved the $40,000 marketing budget on Monday. - Priya now owns end-to-end testing; Sam hands over the test plan by Friday.
Review sentiment and complaintPassed
279 tokens: 162 read, 117 written · 5 s · 25.3 tok/s
Passes when Both lines. Sentiment is mixed: the reviewer loves the battery and sound but is annoyed. The complaint names the charger or support in at most 12 words.
Read this product review and answer in exactly this format: Sentiment: <positive, negative or mixed> Complaint: <the main complaint in a few words> I've used this speaker every day for a month. The battery is honestly amazing, easily two full days, and it sounds far better than its size suggests. But the charger that came in the box stopped working after a week, and support took nine days to reply. I bought a replacement cable myself. Would I buy it again? Probably, but I'm annoyed.
Sentiment: mixed Complaint: defective charger and slow support
Faithful summary of a studyPassed
2,021 tokens: 177 read, 1,844 written · 1 min 10 s · 26.9 tok/s
Passes when At most 50 words with the 240 participants, the 12-week length and the 3.1-day result, and nothing the text does not say (no approval, cure, children or recommendation).
Summarize this in at most 50 words. Keep the key numbers. Do not add anything the text does not say. In a randomized trial, 240 adults with chronic migraine took either the new drug or a placebo for 12 weeks. Those on the drug had 3.1 fewer headache days per month on average, compared with 1.9 fewer days for placebo. Nausea was the most common side effect, reported by 9% of the drug group. The authors note the trial lasted only 12 weeks and call for longer studies before any recommendation is made.
In a 12-week randomized trial, 240 adults with chronic migraine received the new drug or placebo. Drug users averaged 3.1 fewer headache days per month than placebo’s 1.9. Nausea affected 9% of the drug group. Authors say longer studies are needed before recommendations.
Action items from an email threadPassed
627 tokens: 256 read, 371 written · 15 s · 27 tok/s
Passes when Exactly four lines of the form "- owner: action": Dana sends the contract, Luis books the venue, Mei updates the budget sheet, Dana emails the caterer. Tom's declined offer, the logo and the invitations are left out.
List every action item in this email thread that someone has committed to do, as lines of the form "- <owner>: <action>". Leave out offers that were declined and things that turned out not to be needed. Output only the list. From: Dana Hi all, quick recap of what's still open for the June offsite. I'll send the revised contract to the hotel by Wednesday. From: Tom I can book the venue for the dinner if that helps? From: Luis Thanks Tom, but I'll book the venue myself since I already spoke to them. Also, no need to change the logo on the badges, it's fine as is. From: Mei I'll update the budget sheet with the new hotel price tonight. From: Dana Great. Oh, and I'll email the caterer about the vegetarian options. Tom, could you just double-check the date on the invitations? Actually never mind, I checked them this morning, they're correct.
- Dana: Send the revised contract to the hotel by Wednesday - Luis: Book the venue for the dinner - Mei: Update the budget sheet with the new hotel price - Dana: Email the caterer about the vegetarian options
Extracting 5 of 5
Fields from an email as JSONPassed
356 tokens: 160 read, 196 written · 8 s · 27.6 tok/s
Passes when Only a JSON object with exactly name "Laura Chen", company "Northwind Traders", meeting_date "2025-05-14" and attendees the number 4.
Extract these fields from the email as a JSON object with exactly these keys: "name" (the sender's full name), "company", "meeting_date" (YYYY-MM-DD) and "attendees" (a number). Output only the JSON. Hello! This is Laura Chen from Northwind Traders. We'd love to visit your workshop. Could we meet on the 14th of May 2025, any time after lunch? We'll be four people in total, including me. Best, Laura
{ "name": "Laura Chen", "company": "Northwind Traders", "meeting_date": "2025-05-14", "attendees": 4 }Every email address, oncePassed
452 tokens: 181 read, 271 written · 11 s · 27.3 tok/s
Passes when Exactly four addresses, one per line, in order: info@harbor-events.org, speakers@harbor-events.org, mia.k@studio-nine.io, accounts@harbor-events.org. "jo at harbor dot org" is not written out in full, and its inbox is closed.
List every email address written out in full in this text (like name@example.com), one per line, in the order they first appear, without duplicates. Leave out any inbox the text says is closed. Output only the addresses. Contacts for the conference: write to info@harbor-events.org for general questions, or to speakers@harbor-events.org if you are presenting. Our designer (mia.k@studio-nine.io) made the poster. Billing questions: accounts@harbor-events.org. Again, presenters use speakers@harbor-events.org. Please don't write to jo at harbor dot org, that inbox is closed.
info@harbor-events.org speakers@harbor-events.org mia.k@studio-nine.io accounts@harbor-events.org
A list as CSVPassed
336 tokens: 140 read, 196 written · 8 s · 26.5 tok/s
Passes when The header item,qty,price, then the four items with whole-number quantities and prices as plain numbers (no $).
Convert this shopping list to CSV with the header row item,qty,price. Write prices as plain numbers without a currency sign. Output only the CSV. Apples - 12 @ $0.50 Bread loaf - 2 @ $3.25 Olive oil (1 l) - 1 @ $8.90 Eggs - 18 @ $0.30
item,qty,price Apples,12,0.50 Bread loaf,2,3.25 Olive oil (1 l),1,8.90 Eggs,18,0.30
The real deadlinePassed
267 tokens: 141 read, 126 written · 5 s · 26.9 tok/s
Passes when Only 2025-02-07, the extended deadline.
What is the final submission deadline? Answer with the date only, in YYYY-MM-DD format. The call for papers opened on 2 January 2025. Drafts were originally due January 20, but the committee extended the final submission deadline to 7 February 2025. Reviews are due by 28 February, and results will be announced on March 10, 2025.
2025-02-07
Warnings and errors from a logPassed
686 tokens: 318 read, 368 written · 15 s · 27.2 tok/s
Passes when Only a JSON array of the WARN line and the two ERROR lines, in order, with time, level and message copied exactly.
From this log, return a JSON array with one object per WARN or ERROR line, in order, each with the keys "time" (HH:MM:SS), "level" and "message". Output only the JSON. 2025-03-04 09:12:01 INFO server started on port 8080 2025-03-04 09:12:07 WARN config key "timeout" is deprecated 2025-03-04 09:13:44 INFO user alice logged in 2025-03-04 09:15:02 ERROR database connection lost 2025-03-04 09:15:03 INFO retrying in 5s 2025-03-04 09:15:08 INFO database reconnected 2025-03-04 09:20:30 DEBUG cache hit ratio 0.93 2025-03-04 09:31:12 ERROR payment webhook returned 502
[ { "time": "09:12:07", "level": "WARN", "message": "config key \"timeout\" is deprecated" }, { "time": "09:15:02", "level": "ERROR", "message": "database connection lost" }, { "time": "09:31:12", "level": "ERROR", "message": "payment webhook returned 502" } ]
Following instructions 5 of 5
Exactly one wordPassed
84 tokens: 62 read, 22 written · 1 s · 24.9 tok/s
Passes when Exactly OK, with nothing else, not even a full stop.
Reply with exactly the word OK and nothing else.
OK
Three lowercase linesPassed
199 tokens: 84 read, 115 written · 5 s · 23.8 tok/s
Passes when Exactly three lines about the sea, all lowercase, the third ending with a question mark.
Write exactly three lines about the sea. Every line must be entirely lowercase, and the third line must end with a question mark. Output only the three lines.
the sea breathes silver under the moon waves fold and whisper on the sand are you listening to the deep?
Exact JSON shapePassed
214 tokens: 88 read, 126 written · 5 s · 28.3 tok/s
Passes when Only JSON with exactly two keys: colors (three lowercase color names) and count (the number 3).
Return a JSON object with exactly two keys: "colors", a list of exactly three color names in lowercase, and "count", the number 3. Output only the JSON.
{ "colors": [ "red", "green", "blue" ], "count": 3 }Sort a listPassed
309 tokens: 81 read, 228 written · 10 s · 24.1 tok/s
Passes when One line: apple, apricot, banana, cherry, pear (any capitalization).
Sort these words alphabetically, ignoring case, and output them on one line separated by commas: pear, Apple, banana, cherry, apricot
Apple, apricot, banana, cherry, pear
A name with three rulesPassed
289 tokens: 88 read, 201 written · 9 s · 24.2 tok/s
Passes when Two words, both capitalized, with no letter e anywhere.
Suggest a product name for a solar-powered lamp. Rules: exactly two words, both capitalized, and the letter "e" must not appear anywhere. Output only the name.
Sun Glow
Reasoning 5 of 5
Unit pricePassed
288 tokens: 94 read, 194 written · 7 s · 27.9 tok/s
A shop sells pencils at 3 for $1.50. How much do 19 pencils cost, in dollars? Work it out, then end with a line "ANSWER: <number>".
3 pencils cost $1.50, so 1 pencil costs $1.50 ÷ 3 = $0.50. 19 pencils cost 19 × $0.50 = $9.50. ANSWER: 9.50
Days of the weekPassed
267 tokens: 88 read, 179 written · 7 s · 26 tok/s
Today is Monday. What day of the week will it be 321 days from today? Work it out, then end with a line "ANSWER: <day>".
321 divided by 7 leaves a remainder of 6, so 321 days from Monday is 6 days later: Sunday. ANSWER: Sunday
Who owns the catPassed
222 tokens: 109 read, 113 written · 5 s · 26.5 tok/s
Ivy, Cal and Eli each own exactly one pet: a cat, a dog or a fish, all different. Cal does not own the dog. Ivy owns the fish. Who owns the cat? Work it out, then end with a line "ANSWER: <name>".
Ivy owns the fish, so Cal and Eli must own the cat and dog. Since Cal does not own the dog, Cal owns the cat. ANSWER: Cal
Arrival timePassed
235 tokens: 106 read, 129 written · 5 s · 27.1 tok/s
A train leaves at 18:15 and the trip takes 4 hours 35 minutes. At what time does it arrive? Work it out, then end with a line "ANSWER: <HH:MM>" in 24-hour time.
18:15 + 4 hours = 22:15 22:15 + 35 minutes = 22:50 ANSWER: 22:50
Filling a tankPassed
547 tokens: 132 read, 415 written · 16 s · 26.5 tok/s
A tank holds 120 liters. Pipe A alone fills it in 15 hours, pipe B alone fills it in 15 hours, and a drain alone empties a full tank in 15 hours. With all three open and the tank starting empty, how many hours until it is full? Work it out, then end with a line "ANSWER: <number>".
Pipe A fills at \(120 \div 15 = 8\) liters/hour. Pipe B fills at \(120 \div 15 = 8\) liters/hour. The drain empties at \(120 \div 15 = 8\) liters/hour. With all three open, the net filling rate is: \[ 8 + 8 - 8 = 8 \text{ liters/hour} \] Time to fill 120 liters: \[ 120 \div 8 = 15 \text{ hours} \] ANSWER: 15
Reading code 4 of 4
What does it printPassed
272 tokens: 117 read, 155 written · 6 s · 26.3 tok/s
What does this Python program print? Work it out, then end with a line "ANSWER: <output>". def f(xs): return sorted(xs)[::-1][:2] print(f([6, 40, 48, 22, 15, 25]))ANSWER: [48, 40]
Trace a loopPassed
680 tokens: 135 read, 545 written · 21 s · 27 tok/s
What does this Python program print? Work it out, then end with a line "ANSWER: <output>". def g(n): total = 0 for i in range(1, n + 1): if i % 3 == 0 or i % 7 == 0: total += i return total print(g(28))The program sums all numbers from 1 to 28 that are divisible by 3 or 7: - Multiples of 3: 3, 6, 9, 12, 15, 18, 21, 24, 27 → sum = 135 - Multiples of 7: 7, 14, 21, 28 → sum = 70 - Remove double-counted multiple: 21 Total: 135 + 70 - 21 = 184 ANSWER: 184
Find the bugPassed
343 tokens: 148 read, 195 written · 8 s · 26.4 tok/s
Passes when A final line ANSWER: 6 (it divides by one less than the count).
This function should return the mean of a non-empty list. Which line has the bug? Work it out, then end with a line "ANSWER: <line number>". 1 def average(numbers): 2 """Return the mean of a non-empty list of numbers.""" 3 total = 0 4 for n in numbers: 5 total += n 6 count = len(numbers) - 1 7 return total / count
The bug is that `count` should be the full length of the list, not `len(numbers) - 1`. ANSWER: 6
Run a query in your headPassed
354 tokens: 185 read, 169 written · 7 s · 27 tok/s
What rows does this SQL query return? Output only the rows, one per line, as customer,total in the query's order. Table orders: id | customer | amount 1 | ben | 145 2 | ana | 25 3 | cal | 40 4 | ana | 25 5 | ben | 30 6 | cal | 30 Query: SELECT customer, SUM(amount) AS total FROM orders GROUP BY customer HAVING SUM(amount) > 100 ORDER BY total DESC;
ben,175
Using tools 5 of 5
Call one toolPassed
903 tokens: 783 read, 120 written · 7 s · 26.8 tok/s
Passes when One get_weather call for Lisbon in celsius, then an answer giving the 21 °C the tool returned.
What's the weather in Lisbon right now, in celsius?
- user
What's the weather in Lisbon right now, in celsius?
- assistant
get_weather({"city":"Lisbon","unit":"celsius"}) - tool
{"city": "Lisbon", "conditions": "sunny", "temperature": 21, "unit": "celsius"} - assistant
It's currently **21°C and sunny** in Lisbon. ☀️
- user
Don't call a tool when none is neededPassed
486 tokens: 455 read, 31 written · 4 s · 25.3 tok/s
Passes when No tool call, and the answer is 144.
What is 12 times 12?
- user
What is 12 times 12?
- assistant
12 × 12 = **144**
- user
Two calls for two answersPassed
1,165 tokens: 906 read, 259 written · 12 s · 28 tok/s
Passes when Converts 250 EUR to USD and to JPY in at most three calls, then gives both amounts: 270.50 USD and 40,125 JPY.
Convert 250 EUR to USD and to JPY.
- user
Convert 250 EUR to USD and to JPY.
- assistant
convert_currency({"amount":250,"from":"EUR","to":"USD"})convert_currency({"amount":250,"from":"EUR","to":"JPY"}) - tool
{"amount": 270.5, "currency": "USD"} - tool
{"amount": 40125, "currency": "JPY"} - assistant
Here are the conversions for 250 EUR at today's rates: - **USD:** $270.50 - **JPY:** ¥40,125
- user
Chain two toolsPassed
1,590 tokens: 1,374 read, 216 written · 11 s · 27.3 tok/s
Passes when Looks up the customer first, asks for C-1042's orders, then reports A-77 shipped and A-80 processing.
What is the status of each order for the customer with the email kim@example.com?
- user
What is the status of each order for the customer with the email kim@example.com?
- assistant
find_customer({"email":"kim@example.com"}) - tool
{"customer_id": "C-1042", "name": "Kim Park"} - assistant
get_orders({"customer_id":"C-1042"}) - tool
{"orders": [{"carrier": "DHL", "order_id": "A-77", "status": "shipped"}, {"order_id": "A-80", "status": "processing"}]} - assistant
Here are the order statuses for Kim Park (kim@example.com): - **Order A-77**: Shipped (via DHL) - **Order A-80**: Processing
- user
Handle a failed call honestlyPassed
1,070 tokens: 821 read, 249 written · 12 s · 26.6 tok/s
Passes when Tries 19:00 for 2 people, never books another time without asking, does not claim the booking succeeded, and offers 18:30 or 20:30.
Book a table for 2 people at Luigi's tonight at 19:00.
- user
Book a table for 2 people at Luigi's tonight at 19:00.
- assistant
book_table({"restaurant":"Luigi's","people":2,"time":"19:00"}) - tool
{"available_times": ["18:30", "20:30"], "error": "fully booked at that time"} - assistant
Luigi's is fully booked at 19:00 tonight. The nearest available times are **18:30** and **20:30**. Would you like me to book one of those instead?
- user
Building pages 4 of 5
A small websiteFailed
31,243 tokens: 163 read, 31,080 written · 23 min 37 s · 21.9 tok/s
Passes when A <header> with an <h1> naming the bakery, a <nav> with exactly 3 links, a <main> with <section id="menu"> holding at least 4 list items, and a <footer> with opening hours. Nothing loaded from another site and no network requests.
Build a one-page website for a bakery called "Crumb & Co". It must have a <header> containing an <h1> with the bakery's name; a <nav> with exactly 3 links; a <main> with a <section id="menu"> listing at least 4 items in a list; and a <footer> with the opening hours. Output one complete HTML document and nothing else. Put all CSS and JavaScript inline. Do not load anything from another site: no external scripts, stylesheets, fonts or images.
Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content="Crumb & Co. — wood-fired sourdough, croissants and slow bakes on Alder Row, Portland. Open from 7."> <title>Crumb & Co. — Bakery, Alder Row</title> <style> :root{ --paper:#f5eddc; --paper-2:#ece0c6; --card:#fbf5e8; --ink:#2b180b; --cocoa:#4b2d15; --butter:#e9b44c; --caramel:#c47a2c; --crust:#8f4f1d; --cream-txt:#f2e5c8; --serif:"Didot","Bodoni MT","Bodoni 72","Playfair Display","Big Caslon",Georgia,"Times New Roman",serif; --sans:"Avenir Next",Avenir,"Segoe UI","Trebuchet MS",Verdana,sans-serif; --mono:"SF Mono","Cascadia Code",Menlo,Consolas,"DejaVu Sans Mono",monospace; } *{box-sizing:border-box;margin:0;padding:0} html{scroll-behavior:smooth;scroll-padding-top:5.5rem} body{ background: radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,.5), transparent 55%), var(--paper); color:var(--ink); font:1rem/1.6 var(--sans); overflow-x:hidden; } ::selection{background:var(--butter);color:var(--ink)} :focus-visible{outline:3px solid var(--crust);outline-offset:3px;border-radius:2px} img,svg{display:block;max-width:100%} .visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap} .grain{position:fixed;inset:0;z-index:90;pointer-events:none;opacity:.06;mix-blend-mode:multiply; background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)' opacity='0.55'/></svg>"); } /* ---------- top ticker ---------- */ .ticker{background:var(--ink);color:var(--cream-txt);overflow:hidden;border-bottom:2px solid var(--ink)} .ticker-track{display:flex;width:max-content;animation:tick 32s linear infinite} .ticker-track span{font:600 .68rem/1 var(--mono);letter-spacing:.24em;text-transform:uppercase;padding:.7rem 0;white-space:nowrap} .ticker-track b{color:var(--butter);font-weight:600;padding:0 .4rem} @keyframes tick{to{transform:translateX(-50%)}} .wrap{width:min(1120px,92%);margin:0 auto} /* ---------- header ---------- */ header{border-bottom:2px solid var(--ink)} .topbar{display:flex;justify-content:space-between;align-items:center;gap:1rem;padding:.6rem 0;border-bottom:1.5px dotted rgba(43,24,11,.4);font:600 .68rem/1 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa)} #oven-chip{color:var(--crust)} @media(max-width:640px){.topbar .tb-mid{display:none}} .navbar{position:sticky;top:0;z-index:50;background:var(--paper);border-bottom:2px solid var(--ink)} .navbar .wrap{display:flex;align-items:center;justify-content:space-between;gap:1rem} .brandmark{font:700 .72rem var(--mono);letter-spacing:.1em;border:2px solid var(--ink);background:var(--butter);padding:.45rem .6rem;border-radius:50%;box-shadow:2px 2px 0 var(--ink)} nav ul{display:flex;list-style:none;gap:1.6rem} .navbar a{position:relative;font:600 .78rem var(--sans);letter-spacing:.16em;text-transform:uppercase;color:var(--ink);text-decoration:none;padding:.95rem .2rem} .navbar a::after{content:"";position:absolute;left:.2rem;right:.2rem;bottom:.6rem;height:2px;background:var(--caramel);transform:scaleX(0);transform-origin:left;transition:transform .3s ease} .navbar a:hover::after,.navbar a.active::after{transform:scaleX(1)} .navbar a.active{color:var(--crust)} .nav-note{font:500 .66rem var(--mono);letter-spacing:.12em;text-transform:uppercase;color:var(--cocoa)} @media(max-width:720px){.nav-note{display:none}nav ul{gap:1rem}} .hero{padding:3.4rem 0 3rem} .hero-grid{display:grid;grid-template-columns:1.15fr .85fr;gap:3rem;align-items:center} @media(max-width:900px){.hero-grid{grid-template-columns:1fr}} .kicker{font:600 .7rem var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--caramel);margin-bottom:1.1rem} .kicker::before{content:"— ";color:var(--crust)} h1.wordmark{font-family:var(--serif);font-weight:400;font-size:clamp(3.4rem,10vw,7rem);line-height:.94;letter-spacing:-.015em} .wm-line{display:block;overflow:hidden;padding-bottom:.04em} .wm-in{display:inline-block;transform:translateY(112%);animation:rise .95s cubic-bezier(.22,1,.36,1) forwards} .wm-in.d2{animation-delay:.14s} @keyframes rise{to{transform:translateY(0)}} .wordmark em{font-style:italic;color:var(--caramel);font-size:1.02em} .wheat{width:.8em;height:1.35em;color:var(--caramel);vertical-align:-.24em;margin-left:.18em;transform-origin:bottom center;animation:sway 4.5s ease-in-out infinite alternate} @keyframes sway{from{transform:rotate(-4deg)}to{transform:rotate(4deg)}} .tagline{max-width:46ch;margin:1.4rem 0 1.5rem;font-size:1.05rem;color:var(--cocoa)} .chips{display:flex;flex-wrap:wrap;gap:.6rem} .chips li{list-style:none;font:600 .66rem var(--mono);letter-spacing:.14em;text-transform:uppercase;border:1.5px solid var(--cocoa);background:var(--card);padding:.5rem .75rem;border-radius:999px;box-shadow:2px 2px 0 rgba(43,24,11,.18);transition:transform .2s,background .2s} .chips li:hover{transform:translateY(-3px);background:var(--butter)} .crust-card{position:relative;border:2px solid var(--ink);background:var(--card);box-shadow:9px 9px 0 var(--butter);padding:1.6rem; background-image:repeating-radial-gradient(circle at 50% 55%, transparent 0 20px, rgba(43,24,11,.05) 20px 21px)} #bread{transform-origin:150px 150px;transition:transform .5s cubic-bezier(.22,1,.36,1);cursor:pointer} .crust-art:hover #bread{transform:translateY(-7px) rotate(-1.5deg)} #bread.pop{animation:crustpop .55s ease} @keyframes crustpop{30%{transform:scale(1.06) rotate(-2.5deg)}60%{transform:scale(.97)}100%{transform:scale(1)}} .steam path{animation:steam 2.8s ease-in-out infinite;opacity:0} .steam path:nth-child(2){animation-delay:.9s} .steam path:nth-child(3){animation-delay:1.7s} @keyframes steam{0%{opacity:0;transform:translateY(9px)}30%{opacity:.8}100%{opacity:0;transform:translateY(-16px)}} .crust-cap{margin-top:1rem;text-align:center;font:500 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--cocoa)} .stamp{position:absolute;top:-34px;right:-26px;width:112px;height:112px;color:var(--cocoa);animation:spin 26s linear infinite} @keyframes spin{to{transform:rotate(360deg)}} .stamp text{font:700 11.5px var(--mono);letter-spacing:2.5px;fill:currentColor} .meta-strip{display:flex;flex-wrap:wrap;gap:.9rem 2.6rem;margin-top:2.8rem;padding-top:1.4rem;border-top:2px dotted rgba(43,24,11,.35)} .meta-strip p{font:500 .72rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa)} .meta-strip b{font-family:var(--serif);font-weight:400;font-size:1.45rem;letter-spacing:0;color:var(--ink);margin-right:.35rem} /* ---------- sections ---------- */ .section{padding:5.2rem 0 5.6rem} .section + .section{border-top:2px dotted rgba(43,24,11,.28)} h2{font-family:var(--serif);font-weight:400;font-size:clamp(2.1rem,4.6vw,3.3rem);line-height:1.05;letter-spacing:-.01em;margin-bottom:.4rem} .sec-lead{color:var(--cocoa);max-width:52ch} .crumb-line{height:16px;background:radial-gradient(circle,var(--crust) 1.6px,transparent 1.7px) 0 50%/26px 16px;opacity:.4} /* ---------- menu ---------- */ .menu-head{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:1.4rem;margin-bottom:2rem} .tabs{display:flex;flex-wrap:wrap;gap:.55rem} .tab{font:700 .68rem var(--mono);letter-spacing:.16em;text-transform:uppercase;padding:.6rem 1rem;border:2px solid var(--ink);background:transparent;color:var(--ink);cursor:pointer;border-radius:999px;transition:transform .15s,background .2s,color .2s,box-shadow .15s} .tab:hover{transform:translateY(-2px)} .tab[aria-pressed="true"]{background:var(--ink);color:var(--paper);box-shadow:3px 3px 0 var(--butter)} .menu-count{font:500 .7rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa);margin-bottom:.8rem} .menu-list{list-style:none;columns:2;column-gap:2.6rem;border:2px solid var(--ink);background:var(--card);box-shadow:9px 9px 0 rgba(43,24,11,.12);padding:1.2rem 1.4rem} @media(max-width:820px){.menu-list{columns:1}} .menu-item{break-inside:avoid;display:grid;grid-template-columns:1fr;gap:.3rem;padding:1.15rem .7rem;border-bottom:1.5px dotted rgba(43,24,11,.4);transition:background .25s,transform .25s} .menu-item:last-child{border-bottom:none} .menu-item:hover{background:#fff9ec;transform:translateX(6px)} .menu-item.off{display:none} .mi-top{display:flex;align-items:baseline;gap:.7rem} .mi-name{font-family:var(--serif);font-weight:400;font-size:1.4rem;line-height:1.15;transition:color .2s} .menu-item:hover .mi-name{color:var(--crust)} .mi-dots{flex:1;border-bottom:2px dotted var(--caramel);transform:translateY(-5px);opacity:.65} .mi-price{font:700 .95rem var(--mono);color:var(--crust)} .mi-desc{font-size:.92rem;color:var(--cocoa);max-width:52ch} .mi-tags{list-style:none;display:flex;gap:.5rem;flex-wrap:wrap} .mi-tags li{font:600 .6rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--cocoa);border:1.5px dashed rgba(75,45,21,.5);padding:.22rem .5rem;border-radius:999px} .menu-note{margin-top:1rem;font:500 .74rem var(--mono);letter-spacing:.06em;color:var(--cocoa)} /* ---------- oven band ---------- */ .oven-band{background:#26140a;color:var(--cream-txt);padding:4.6rem 0 5rem;border-top:6px solid var(--butter)} .oven-band .kicker{color:var(--butter)} .oven-band .kicker::before{color:var(--caramel)} .oven-band h2{color:var(--cream-txt)} .oven-grid{display:grid;grid-template-columns:1fr 1.1fr 1fr;gap:1.4rem;margin-top:2.4rem} @media(max-width:900px){.oven-grid{grid-template-columns:1fr}} .ov-card{border:1.5px solid rgba(242,229,200,.28);background:rgba(242,229,200,.04);padding:1.5rem;transition:border-color .3s,transform .3s} .ov-card:hover{border-color:var(--butter);transform:translateY(-4px)} .ov-label{font:700 .66rem var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--butter);margin-bottom:1.1rem;display:flex;justify-content:space-between;gap:.5rem} .ov-label span{color:rgba(242,229,200,.5);font-weight:500} .ov-ring{position:relative;width:172px;margin:0 auto 1.2rem} .ring circle{fill:none;stroke-width:7} .ring-bg{stroke:rgba(242,229,200,.14)} .ring-fg{stroke:var(--butter);stroke-linecap:round;transform:rotate(-90deg);transform-origin:center} .ov-center{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.2rem;text-align:center} #ov-count{font:700 1.55rem var(--mono);letter-spacing:.04em} .ov-sub{font:500 .62rem var(--mono);letter-spacing:.2em;text-transform:uppercase;color:rgba(242,229,200,.55)} .ov-next{font-size:.92rem;color:rgba(242,229,200,.85);text-align:center} .ov-next strong{color:var(--cream-txt)} .ov-status{margin-top:.7rem;text-align:center;font:700 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--caramel)} .box-row{display:flex;align-items:center;gap:.8rem;padding:.85rem .4rem;border-bottom:1.5px dashed rgba(242,229,200,.2);cursor:pointer;transition:background .2s,transform .2s;font-size:.95rem} .box-row:hover{background:rgba(233,180,76,.09);transform:translateX(5px)} .box-row input{accent-color:var(--butter);width:17px;height:17px;flex:none} .box-name{flex:1} .box-name small{font:500 .7rem var(--mono);color:rgba(242,229,200,.5);margin-left:.4rem} .box-price{font:700 .85rem var(--mono);color:var(--butter)} .box-total{display:flex;justify-content:space-between;align-items:baseline;margin:1.1rem 0 .9rem} .box-total span{font:500 .7rem var(--mono);letter-spacing:.2em;text-transform:uppercase;color:rgba(242,229,200,.6)} #box-total{font-family:var(--serif);font-size:1.9rem;color:var(--cream-txt)} #box-total.pop{animation:crustpop .45s ease} #box-btn{width:100%;font:700 .74rem var(--mono);letter-spacing:.18em;text-transform:uppercase;padding:.95rem;border:2px solid var(--butter);background:var(--butter);color:var(--ink);cursor:pointer;box-shadow:4px 4px 0 rgba(0,0,0,.35);transition:transform .15s,box-shadow .15s,background .2s} #box-btn:hover:not(:disabled){transform:translate(-2px,-2px);box-shadow:6px 6px 0 rgba(0,0,0,.35)} #box-btn:active:not(:disabled){transform:translate(3px,3px);box-shadow:0 0 0 rgba(0,0,0,0)} #box-btn:disabled{opacity:.45;cursor:not-allowed} #box-btn.held{background:var(--cream-txt)} #box-note{margin-top:.8rem;font-size:.82rem;color:var(--butter)} .sold-list{list-style:none;display:grid;gap:.85rem} .sold-list li{display:flex;justify-content:space-between;gap:1rem;font-size:.95rem} .sold-list li span{text-decoration:line-through;text-decoration-color:var(--caramel);opacity:.6} .sold-list time{font:600 .72rem var(--mono);color:rgba(242,229,200,.55);white-space:nowrap} .sold-list .back span{text-decoration:none;opacity:.95} .sold-list .back time{color:var(--butter)} .sold-foot{margin-top:1.1rem;font-size:.85rem;font-style:italic;color:rgba(242,229,200,.6)} /* ---------- story ---------- */ .story-grid{display:grid;grid-template-columns:.9fr 1.1fr;gap:3.5rem;align-items:start} @media(max-width:880px){.story-grid{grid-template-columns:1fr}} .story-left{position:sticky;top:5.5rem} @media(max-width:880px){.story-left{position:static}} .stats{display:flex;flex-wrap:wrap;gap:2.2rem;margin-top:2.2rem;padding-top:1.6rem;border-top:2px dotted rgba(43,24,11,.35)} .stats b{display:block;font-family:var(--serif);font-weight:400;font-size:2rem;line-height:1;color:var(--crust)} .stats span{font:600 .64rem var(--mono);letter-spacing:.16em;text-transform:uppercase;color:var(--cocoa)} .story-right p{color:var(--cocoa);margin-bottom:1.3rem;max-width:60ch} blockquote{margin:2.2rem 0;padding:1rem 0 1rem 1.6rem;border-left:4px solid var(--butter);font-family:var(--serif);font-style:italic;font-size:1.5rem;line-height:1.35;color:var(--ink)} blockquote footer{margin-top:.8rem;font:600 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;font-style:normal;color:var(--caramel)} .timeline{list-style:none;margin-top:2.6rem;border-left:2px dotted var(--caramel);padding-left:1.6rem} .timeline li{position:relative;padding-bottom:1.9rem} .timeline li:last-child{padding-bottom:.4rem} .timeline li::before{content:"";position:absolute;left:calc(-1.6rem - 7px);top:.5rem;width:12px;height:12px;border-radius:50%;background:var(--butter);border:2.5px solid var(--ink)} .tl-year{display:inline-block;font:700 .7rem var(--mono);letter-spacing:.16em;background:var(--butter);border:1.5px solid var(--ink);padding:.25rem .55rem;margin-bottom:.45rem} .timeline h3{font-family:var(--serif);font-weight:400;font-size:1.35rem;margin-bottom:.25rem} .timeline p{font-size:.94rem;max-width:52ch} /* ---------- visit ---------- */ .visit-grid{display:grid;grid-template-columns:1fr 1fr;gap:3rem;align-items:start;margin-top:2.2rem} @media(max-width:880px){.visit-grid{grid-template-columns:1fr}} .open-pill{display:inline-block;font:700 .68rem var(--mono);letter-spacing:.16em;text-transform:uppercase;padding:.55rem .85rem;border:2px solid var(--ink);margin-bottom:1.2rem} .open-pill.now{background:var(--butter);box-shadow:3px 3px 0 var(--ink)} .open-pill.later{background:var(--card);color:var(--cocoa);border-style:dashed} table.hours{width:100%;border-collapse:collapse;border:2px solid var(--ink);background:var(--card);box-shadow:7px 7px 0 rgba(43,24,11,.12)} .hours tr{border-bottom:1.5px dotted rgba(43,24,11,.35);transition:background .3s} .hours tr:last-child{border-bottom:none} .hours th,.hours td{padding:.85rem 1.1rem;text-align:left;font-size:.95rem} .hours th{font-family:var(--serif);font-weight:400;font-size:1.05rem;width:34%} .hours td{font:600 .82rem var(--mono);letter-spacing:.04em;color:var(--cocoa)} .hours td small{display:block;font:italic 500 .72rem var(--sans);letter-spacing:0;color:var(--crust)} .hours tr.today{background:#f7e7c0} .today-chip{display:inline-block;margin-left:.6rem;font:700 .58rem var(--mono);letter-spacing:.16em;text-transform:uppercase;background:var(--ink);color:var(--butter);padding:.2rem .45rem;border-radius:999px;vertical-align:middle} .visit-addr{margin-top:1.4rem;font:500 .85rem/1.9 var(--mono);color:var(--cocoa)} .visit-addr b{color:var(--ink)} figure.map-fig{border:2px solid var(--ink);background:var(--card);box-shadow:9px 9px 0 var(--butter);padding:1.1rem} .map-fig figcaption{margin-top:.9rem;font:500 .68rem var(--mono);letter-spacing:.18em;text-transform:uppercase;color:var(--cocoa);text-align:right} /* ---------- footer ---------- */ footer{background:#20110a;color:#f0e2c6;border-top:6px solid var(--butter)} .foot-grid{display:grid;grid-template-columns:1.1fr 1fr 1fr;gap:2.6rem;padding:3.6rem 0 2.6rem} @media(max-width:820px){.foot-grid{grid-template-columns:1fr}} .foot-word{font-family:var(--serif);font-size:2.1rem;line-height:1.1} .foot-tag{margin-top:.6rem;font-size:.9rem;color:rgba(240,226,198,.65)} .foot-h{font:700 .66rem var(--mono);letter-spacing:.22em;text-transform:uppercase;color:var(--butter);margin-bottom:1rem} .hours-mini{list-style:none;display:grid;gap:.55rem} .hours-mini li{display:flex;justify-content:space-between;gap:1rem;border-bottom:1.5px dashed rgba(240,226,198,.22);padding-bottom:.55rem;font-size:.92rem} .hours-mini li span:last-child{font:600 .8rem var(--mono);letter-spacing:.04em} footer address{font-style:normal;font:500 .84rem/2 var(--mono);color:rgba(240,226,198,.85)} .foot-note{margin-top:.9rem;font-size:.8rem;font-style:italic;color:rgba(240,226,198,.55)} .foot-base{display:flex;flex-wrap:wrap;justify-content:space-between;gap:.8rem;padding:1.2rem 0 1.6rem;border-top:1.5px solid rgba(240,226,198,.2);font:500 .68rem var(--mono);letter-spacing:.14em;text-transform:uppercase;color:rgba(240,226,198,.55)} /* ---------- reveals ---------- */ .reveal{opacity:0;transform:translateY(26px);transition:opacity .7s ease,transform .7s cubic-bezier(.22,1,.36,1);transition-delay:var(--d,0s)} .reveal.in{opacity:1;transform:none} @media (prefers-reduced-motion: reduce){ html{scroll-behavior:auto} .ticker-track,.stamp,.wheat,.steam path,#bread,.menu-item,.box-row,.ov-card,.chips li{animation:none !important;transition:none !important} .steam path{opacity:.6} .wm-in{animation:none;transform:none} .reveal{opacity:1;transform:none;transition:none} } </style> </head> <body> <div class="grain" aria-hidden="true"></div> <div class="ticker" aria-hidden="true"> <div class="ticker-track"> <span>Fresh at 4:00 AM <b>✳</b> stone-milled wheat <b>✳</b> 48-hour ferment <b>✳</b> wood-fired since 2003 <b>✳</b> real butter, always <b>✳</b> no shortcuts, no freezer <b>✳</b> Alder Row & Fourth <b>✳</b> </span> </div> </div> <header id="top"> <div class="wrap"> <div class="topbar"> <span>Est. 1994 · Portland, OR</span> <span class="tb-mid">Flour · water · salt · time</span> <span id="oven-chip">Oven · fires soon</span> </div> </div> <div class="navbar"> <div class="wrap"> <span class="brandmark" aria-hidden="true">C&C</span> <nav aria-label="Main"> <ul> <li><a href="#menu">Menu</a></li> <li><a href="#story">Our story</a></li> <li><a href="#visit">Visit</a></li> </ul> </nav> <span class="nav-note">The case closes when it empties</span> </div> </div> <div class="wrap hero"> <div class="hero-grid"> <div> <p class="kicker">Boulangerie & bakery — the slow corner of Alder</p> <h1 class="wordmark" aria-label="Crumb & Co."> <span class="wm-line"><span class="wm-in d1">Crumb<svg class="wheat" viewBox="0 0 40 64" aria-hidden="true"><path d="M20 60V14" stroke="currentColor" stroke-width="3" fill="none" stroke-linecap="round"/><g fill="currentColor"><ellipse cx="20" cy="9" rx="4" ry="7"/><ellipse cx="11" cy="19" rx="4" ry="7" transform="rotate(-32 11 19)"/><ellipse cx="29" cy="19" rx="4" ry="7" transform="rotate(32 29 19)"/><ellipse cx="11" cy="31" rx="4" ry="7" transform="rotate(-32 11 31)"/><ellipse cx="29" cy="31" rx="4" ry="7" transform="rotate(32 29 31)"/><ellipse cx="11" cy="43" rx="4" ry="7" transform="rotate(-32 11 43)"/><ellipse cx="29" cy="43" rx="4" ry="7" transform="rotate(32 29 43)"/></g></svg></span></span> <span class="wm-line"><span class="wm-in d2"><em>&</em> Co.</span></span> </h1> <p class="tagline">Two generations, one stone mill, and a schedule that starts at 4 a.m. — the good crusts come out of the oven before most of the neighborhood is awake.</p> <ul class="chips"> <li>48-hr ferment</li> <li>Stone-milled rye</li> <li>Cherry-oak fire</li> <li>Real butter, always</li> </ul> </div> <figure class="crust-card reveal"> <svg class="stamp" viewBox="0 0 120 120" aria-hidden="true"> <defs><path id="circ" d="M60,60 m-44,0 a44,44 0 1,1 88,0 a44,44 0 1,1 -88,0"/></defs> <circle cx="60" cy="60" r="30" fill="none" stroke="currentColor" stroke-width="1.5" stroke-dasharray="3 4"/> <circle cx="60" cy="60" r="6" fill="currentColor"/> <text><textPath href="#circ">FRESH BAKED · SMALL BATCH · SINCE 1994 · </textPath></text> </svg> <svg class="crust-art" viewBox="0 0 300 260" role="img" aria-label="A warm, scored boule of bread with steam rising"> <ellipse cx="150" cy="222" rx="120" ry="14" fill="rgba(43,24,11,.14)"/> <g class="steam" stroke="#7a5a35" stroke-width="5" fill="none" stroke-linecap="round"> <path d="M108 108c7-9-7-15 0-26s-7-17 0-26"/> <path d="M150 100c7-9-7-15 0-26s-7-17 0-26"/> <path d="M192 108c7-9-7-15 0-26s-7-17 0-26"/> </g> <g id="bread"> <ellipse cx="150" cy="152" rx="112" ry="86" fill="url(#dough)"/> <path d="M66 128c14-26 48-44 84-44s70 18 84 44c-22-16-52-25-84-25s-62 9-84 25z" fill="#f0d9a8" opacity=".85"/> <path d="M92 138q58-26 116 0" stroke="#f7efdf" stroke-width="7" fill="none" stroke-linecap="round" opacity=".9"/> <path d="M104 166q46-20 92 0" stroke="#f7efdf" stroke-width="7" fill="none" stroke-linecap="round" opacity=".75"/> <path d="M120 192q30-13 60 0" stroke="#f7efdf" stroke-width="6" fill="none" stroke-linecap="round" opacity=".55"/> <g fill="#f7efdf" opacity=".4"> <circle cx="110" cy="120" r="2.4"/><circle cx="178" cy="112" r="2"/><circle cx="140" cy="178" r="2.2"/><circle cx="196" cy="170" r="1.8"/><circle cx="128" cy="148" r="1.6"/> </g> </g> <defs> <radialGradient id="dough" cx="42%" cy="32%" r="80%"> <stop offset="0%" stop-color="#eec489"/> <stop offset="55%" stop-color="#cf8d3f"/> <stop offset="100%" stop-color="#9c5a24"/> </radialGradient> </defs> </svg> <figcaption class="crust-cap">Today's boule · scored three times · 41 min in the fire</figcaption> </figure> </div> <div class="meta-strip reveal"> <p><b>4:00</b> AM first fire</p> <p><b>38</b> loaves an hour</p> <p><b>27</b> layers in a croissant</p> <p><b>0</b> shortcuts</p> </div> </div> </header> <main> <!-- ============ MENU ============ --> <section id="menu" class="section" aria-labelledby="menu-h"> <div class="wrap"> <div class="menu-head"> <div class="reveal"> <p class="kicker">No. 01 — The case</p> <h2 id="menu-h">From this morning's bake</h2> </div> <div class="tabs reveal" role="group" aria-label="Filter the menu"> <button class="tab" type="button" data-filter="all" aria-pressed="true">All</button> <button class="tab" type="button" data-filter="breads" aria-pressed="false">Breads</button> <button class="tab" type="button" data-filter="pastries" aria-pressed="false">Pastries</button> <button class="tab" type="button" data-filter="sweets" aria-pressed="false">Sweets</button> </div> </div> <p class="menu-count" id="menu-count">9 bakes on the case</p> <ul class="menu-list" id="menu-list"> <li class="menu-item reveal" data-cat="breads"> <div class="mi-top"><h3 class="mi-name">Country sourdough</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$8.00</span></div> <p class="mi-desc">48-hour cold ferment, stone-milled bread flour, a blistered, crackly crust. Half loaves available.</p> <ul class="mi-tags"><li>whole grain</li><li>vegan</li></ul> </li> <li class="menu-item reveal" data-cat="breads"> <div class="mi-top"><h3 class="mi-name">Seeded rye</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$7.50</span></div> <p class="mi-desc">Our own-milled rye, caraway, sunflower and pumpkin seed. Dense enough for real butter.</p> <ul class="mi-tags"><li>vegan</li><li>keep 5 days</li></ul> </li> <li class="menu-item reveal" data-cat="breads"> <div class="mi-top"><h3 class="mi-name">Olive & rosemary focaccia</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$9.00</span></div> <p class="mi-desc">Dimpled deep, coarse sea salt, a generous pour of extra-virgin. Sold by the slice after noon.</p> <ul class="mi-tags"><li>vegan</li><li>sells out</li></ul> </li> <li class="menu-item reveal" data-cat="pastries"> <div class="mi-top"><h3 class="mi-name">Butter croissant</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$4.50</span></div> <p class="mi-desc">Twenty-seven layers of Normandy butter, laminated over three days. Still warm until about 10.</p> <ul class="mi-tags"><li>27 layers</li></ul> </li> <li class="menu-item reveal" data-cat="pastries"> <div class="mi-top"><h3 class="mi-name">Almond-cardamom bun</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$6.00</span></div> <p class="mi-desc">Brown-butter glaze, ground cardamom, a broken-almond crown. The one people cross town for.</p> <ul class="mi-tags"><li>house glaze</li></ul> </li> <li class="menu-item reveal" data-cat="pastries"> <div class="mi-top"><h3 class="mi-name">Chocolate babka, sliced</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$5.50</span></div> <p class="mi-desc">A brioche braided with 70% dark chocolate, brushed with demerara. Slice or whole (by order).</p> <ul class="mi-tags"><li>whole loaf: $22</li></ul> </li> <li class="menu-item reveal" data-cat="sweets"> <div class="mi-top"><h3 class="mi-name">Seasonal fruit galette</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$7.00</span></div> <p class="mi-desc">Whatever the orchard sent this week — ask at the counter. Folded crust, coarse sugar, no fuss.</p> <ul class="mi-tags"><li>ask what's in</li></ul> </li> <li class="menu-item reveal" data-cat="sweets"> <div class="mi-top"><h3 class="mi-name">Cinnamon knot</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$5.00</span></div> <p class="mi-desc">Ceylon cinnamon and a brown-sugar syrup that pools in the wrapper. Best eaten standing at the counter.</p> <ul class="mi-tags"><li>warm until 2</li></ul> </li> <li class="menu-item reveal" data-cat="sweets"> <div class="mi-top"><h3 class="mi-name">Oat & honey cookie</h3><span class="mi-dots" aria-hidden="true"></span><span class="mi-price">$3.50</span></div> <p class="mi-desc">Thick, chewy middle, burnt edges, local wildflower honey. Three to a sleeve, always.</p> <ul class="mi-tags"><li>local honey</li></ul> </li> </ul> <p class="menu-note">* When the case is empty, the case is empty — the 4 a.m. tray goes first. Prices include the bag.</p> </div> </section> <!-- ============ OVEN / COUNTER ============ --> <section class="oven-band" aria-labelledby="counter-h"> <div class="wrap"> <p class="kicker">Live — the counter</p> <h2 id="counter-h">Today at the window</h2> <div class="oven-grid"> <div class="ov-card reveal"> <p class="ov-label">Oven status</p> <div class="ov-ring"> <svg class="ring" viewBox="0 0 120 120" aria-hidden="true"> <circle class="ring-bg" cx="60" cy="60" r="52"/> <circle class="ring-fg" id="ring-fg" cx="60" cy="60" r="52"/> </svg> <div class="ov-center"> <span id="ov-count" role="timer">--:--:--</span> <span class="ov-sub">to next tray</span> </div> </div> <p class="ov-next">Next out: <strong id="ov-name">—</strong> · <span id="ov-time">—</span></p> <p class="ov-status" id="ov-status">Firing the oven…</p> </div> <div class="ov-card reveal" style="--d:.08s"> <p class="ov-label">Build a box <span>pickup at 4 PM</span></p> <label class="box-row"><input type="checkbox" data-price="27" data-name="Croissants"><span class="box-name">Croissants <small>×6</small></span><span class="box-price">$27</span></label> <label class="box-row"><input type="checkbox" data-price="15" data-name="Baguettes"><span class="box-name">Thin baguettes <small>×3</small></span><span class="box-price">$15</span></label> <label class="box-row"><input type="checkbox" data-price="24" data-name="Cardamom buns"><span class="box-name">Cardamom buns <small>×4</small></span><span class="box-price">$24</span></label> <label class="box-row"><input type="checkbox" data-price="14" data-name="Galette halves"><span class="box-name">Galette, cut in half <small>×2</small></span><span class="box-price">$14</span></label> <div class="box-total"><span>Your box</span><strong id="box-total">$0.00</strong></div> <button id="box-btn" type="button" disabled>Hold at the counter</button> <p id="box-note" hidden>Held under “Guest” until 6 PM — show this page at pickup.</p> </div> <div class="ov-card reveal" style="--d:.16s"> <p class="ov-label">Sold out <span>today, sadly</span></p> <ul class="sold-list"> <li><span>Seeded rye</span><time>1:20 PM</time></li> <li><span>Olive focaccia</span><time>12:05 PM</time></li> <li><span>Cinnamon knots</span><time>11:58 AM</time></li> <li class="back"><span>Apple galette</span><time>back 3:00 PM</time></li> </ul> <p class="sold-foot">We bake to the case, not the clock. If it's gone, it was loved.</p> </div> </div> </div> </section> <!-- ============ STORY ============ --> <section id="story" class="section" aria-labelledby="story-h"> <div class="wrap story-grid"> <div class="story-left reveal"> <p class="kicker">No. 02 — Our story</p> <h2 id="story-h">Thirty years,<br>still on the corner.</h2> <div class="stats"> <div><b>1994</b><span>first bake</span></div> <div><b>4,100</b><span>loaves a week</span></div> <div><b>3</b><span>ovens, one timer</span></div> </div> </div> <div class="story-right"> <p class="reveal">Crumb & Co. started in a garage: Maren Kessler, a borrowed deck oven, and a sourdough starter she named Arthur, on this very corner of Alder Row. The first Saturday, the line wrapped past the mailbox. Arthur has never gone quiet since.</p> <p class="reveal" style="--d:.08s">We still mill a little of our own rye, proof everything overnight, and fire the cherry-oak oven at 4 a.m. sharp. Nothing is frozen, nothing is rushed, and the croissant still takes twenty-seven layers — that's the math, not the marketing.</p> <blockquote class="reveal" style="--d:.12s"> “A good loaf tells you when it's ready. We've just learned to listen.” <footer>— Maren K., first baker</footer> </blockquote> <ol class="timeline"> <li class="reveal"><span class="tl-year">1994</span><h3>The garage</h3><p>Two proofing baskets, one borrowed oven, a starter named Arthur.</p></li> <li class="reveal"><span class="tl-year">2003</span><h3>The wood oven</h3><p>Cherry oak, built by the Alders next door. We never went back to gas.</p></li> <li class="reveal"><span class="tl-year">2011</span><h3>The stone mill</h3><p>We started milling our own rye and spelt a block away, every Thursday.</p></li> <li class="reveal"><span class="tl-year">2019</span><h3>The apprentices</h3><p>Three paid seats a year. Flour on the uniform is encouraged, in fact required.</p></li> <li class="reveal"><span class="tl-year">2025</span><h3>The second floor</h3><p>A small coffee bar above the case. Same ovens, same 4 a.m. alarm.</p></li> </ol> </div> </div> </section> <!-- ============ VISIT ============ --> <section id="visit" class="section" aria-labelledby="visit-h"> <div class="wrap"> <div class="reveal"> <p class="kicker">No. 03 — Visit</p> <h2 id="visit-h">Follow the smell of butter.</h2> <p class="sec-lead">We're on the corner of Alder Row and Fourth, under the blue awning. No reservations and no Wi-Fi at the counter — just the trays and whatever the oven decided to do today.</p> </div> <div class="visit-grid"> <div class="reveal"> <span class="open-pill" id="open-pill">Checking the door…</span> <table class="hours"> <caption class="visually-hidden">Opening hours</caption> <tbody> <tr data-day="1"><th scope="row">Monday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="2"><th scope="row">Tuesday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="3"><th scope="row">Wednesday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="4"><th scope="row">Thursday</th><td>7:00 AM – 3:00 PM</td></tr> <tr data-day="5"><th scope="row">Friday</th><td>7:00 AM – 3:00 PM<small>pastries fly by 1</small></td></tr> <tr data-day="6"><th scope="row">Saturday</th><td>8:00 AM – 4:00 PM</td></tr> <tr data-day="0"><th scope="row">Sunday</th><td>8:00 AM – 1:00 PM<small>loaves gone by 12</small></td></tr> </tbody> </table> <p class="visit-addr"> <b>214 Alder Row, Portland, OR 97204</b><br> (503) 555-0119 · hello@crumbandco.bake<br> No reservations — the line moves fast, the tray moves faster. </p> </div> <figure class="map-fig reveal" style="--d:.1s"> <svg viewBox="0 0 440 320" role="img" aria-label="Hand-drawn map: Crumb and Co. sits at the corner of Alder Row and Fourth, north of the river"> <rect width="440" height="320" fill="#efe4cb"/> <path d="M-10 262 C 90 218, 160 306, 268 262 S 420 236, 460 278" stroke="#c9d4b8" stroke-width="26" fill="none"/> <g stroke="#dccba6" stroke-width="11"> <line x1="0" y1="70" x2="440" y2="70"/> <line x1="0" y1="150" x2="440" y2="150"/> <line x1="0" y1="230" x2="440" y2="230"/> <line x1="90" y1="0" x2="90" y2="320"/> <line x1="200" y1="0" x2="200" y2="320"/> <line x1="310" y1="0" x2="310" y2="320"/> </g> <g fill="#e9dcba"> <rect x="104" y="84" width="82" height="52"/> <rect x="214" y="84" width="82" height="52"/> <rect x="104" y="164" width="82" height="52"/> <rect x="324" y="84" width="102" height="52"/> </g> <path d="M90 230 L 200 150" stroke="#c47a2c" stroke-width="2.5" stroke-dasharray="6 6" fill="none"/> <circle cx="90" cy="230" r="5" fill="#c47a2c"/> <text x="36" y="256" font-family="monospace" font-size="11" fill="#8f4f1d">you, ideally at 7 AM</text> <circle cx="200" cy="150" r="17" fill="#e9b44c" stroke="#2b180b" stroke-width="3"/> <text x="200" y="155" text-anchor="middle" font-family="monospace" font-size="12" font-weight="bold" fill="#2b180b">C</text> <text x="222" y="134" font-family="monospace" font-size="11" font-weight="bold" fill="#2b180b">CRUMB & CO.</text> <text x="322" y="146" font-family="monospace" font-size="10" letter-spacing="3" fill="#8f7a52">ALDER ROW</text> <text x="196" y="212" font-family="monospace" font-size="10" letter-spacing="3" fill="#8f7a52" transform="rotate(-90 196 212)">4TH AVE</text> </svg> <figcaption>— not to scale; to butter</figcaption> </figure> </div> </div> </section> <div class="crumb-line" aria-hidden="true"></div> </main> <footer> <div class="wrap foot-grid"> <div> <p class="foot-word">Crumb & Co.</p> <p class="foot-tag">Baked the slow way since 1994, on the slow corner of Alder Row, Portland, Oregon. Come early; the trays are the truth.</p> </div> <div> <p class="foot-h">Opening hours</p> <ul class="hours-mini"> <li><span>Monday – Friday</span><span>7:00 AM – 3:00 PM</span></li> <li><span>Saturday</span><span>8:00 AM – 4:00 PM</span></li> <li><span>Sunday</span><span>8:00 AM – 1:00 PM</span></li> </ul> <p class="foot-note">Closed on major holidays. Last order fifteen minutes before close.</p> </div> <div> <p class="foot-h">Find us</p> <address>214 Alder Row, Portland, OR 97204<br>(503) 555-0119<br>hello@crumbandco.bake</address> <p class="foot-note">Follow the smell of butter, then the blue awning.</p> </div> </div> <div class="wrap foot-base"> <span>© <span id="yr">2025</span> Crumb & Co. — all crumbs reserved.</span> <span>Flour · water · salt · time</span> </div> </footer> <script> (function(){ "use strict"; var reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; /* seamless ticker */ document.querySelectorAll(".ticker-track").forEach(function(t){ t.innerHTML += t.innerHTML; }); /* reveal on scroll */ var revealEls = document.querySelectorAll(".reveal"); if (reduceMotion || !("IntersectionObserver" in window)) { revealEls.forEach(function(el){ el.classList.add("in"); }); } else { var io = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } }); }, { threshold:.12, rootMargin:"0px 0px -8%" }); revealEls.forEach(function(el){ io.observe(el); }); } /* gentle stagger inside the menu list */ document.querySelectorAll("#menu-list .menu-item").forEach(function(li,i){ li.style.setProperty("--d", ((i % 6) * 0.05) + "s"); }); /* scrollspy for the 3 nav links */ var links = Array.prototype.slice.call(document.querySelectorAll(".navbar a")); if ("IntersectionObserver" in window) { var spy = new IntersectionObserver(function(entries){ entries.forEach(function(e){ if (e.isIntersecting) { var id = "#" + e.target.id; links.forEach(function(a){ a.classList.toggle("active", a.getAttribute("href") === id); }); } }); }, { rootMargin:"-30% 0px -60% 0px" }); links.forEach(function(a){ var s = document.querySelector(a.getAttribute("href")); if (s) spy.observe(s); }); } /* ----- oven countdown ----- */ var SCHEDULE = [ ["8:00","Country sourdough"], ["11:30","Butter croissants"], ["14:00","Focaccia & knots"], ["16:30","Babka & galettes"] ]; function toSec(t){ var p = t.split(":"); return (+p[0])*3600 + (+p[1])*60; } function pad(n){ return (n < 10 ? "0" : "") + n; } var C = 2 * Math.PI * 52; var ring = document.getElementById("ring-fg"); ring.style.strokeDasharray = C; var ovCount = document.getElementById("ov-count"), ovName = document.getElementById("ov-name"), ovTime = document.getElementById("ov-time"), ovStat = document.getElementById("ov-status"), chip = document.getElementById("oven-chip"); function ovenTick(){ var now = new Date(); var n = now.getHours()*3600 + now.getMinutes()*60 + now.getSeconds(); var idx = -1; for (var i = 0; i < SCHEDULE.length; i++) { if (toSec(SCHEDULE[i][0]) > n) { idx = i; break; } } var next, prev, tomorrow = false; if (idx === -1) { idx = 0; next = toSec(SCHEDULE[0][0]) + 86400; prev = toSec(SCHEDULE[3][0]); tomorrow = true; } else { next = toSec(SCHEDULE[idx][0]); prev = idx > 0 ? toSec(SCHEDULE[idx-1][0]) : 0; } var rem = next - n; var h = Math.floor(rem/3600), m = Math.floor((rem%3600)/60), s = rem%60; ovCount.textContent = pad(h)+":"+pad(m)+":"+pad(s); var prog = Math.min(1, Math.max(0, (n - prev) / (next - prev))); ring.style.strokeDashoffset = C * (1 - prog); ovName.textContent = SCHEDULE[idx][1]; ovTime.textContent = SCHEDULE[idx][0] + (tomorrow ? " (tomorrow)" : ""); ovStat.textContent = rem > 1800 ? "Proofing & shaping" : rem > 900 ? "Firing the oven" : rem > 300 ? "Loading the trays" : rem > 45 ? "Out the door any second" : "Fresh — grab a tray"; chip.textContent = "Oven · fires " + SCHEDULE[idx][0] + (tomorrow ? " +1d" : ""); } ovenTick(); setInterval(ovenTick, 1000); /* ----- menu filter ----- */ var tabs = document.querySelectorAll(".tab"), items = Array.prototype.slice.call(document.querySelectorAll("#menu-list .menu-item")), count = document.getElementById("menu-count"); tabs.forEach(function(btn){ btn.addEventListener("click", function(){ tabs.forEach(function(b){ b.setAttribute("aria-pressed", b === btn ? "true" : "false"); }); var f = btn.getAttribute("data-filter"), shown = 0; items.forEach(function(li){ var show = f === "all" || li.getAttribute("data-cat") === f; li.classList.toggle("off", !show); if (show) shown++; }); count.textContent = shown + (shown === 1 ? " bake" : " bakes") + " on the case" + (f !== "all" ? " · " + f : ""); }); }); /* ----- build a box ----- */ var boxInputs = Array.prototype.slice.call(document.querySelectorAll(".box-row input")), boxTotal = document.getElementById("box-total"), boxBtn = document.getElementById("box-btn"), boxNote = document.getElementById("box-note"); function pop(el){ el.classList.remove("pop"); void el.offsetWidth; el.classList.add("pop"); } function boxUpdate(){ var t = 0, any = false; boxInputs.forEach(function(i){ if (i.checked) { t += +i.getAttribute("data-price"); any = true; } }); boxTotal.textContent = "$" + t.toFixed(2); boxBtn.disabled = !any; if (!any) { boxNote.hidden = true; boxBtn.classList.remove("held"); boxBtn.textContent = "Hold at the counter"; } pop(boxTotal); } boxInputs.forEach(function(i){ i.addEventListener("change", function(){ boxBtn.classList.remove("held"); boxBtn.textContent = "Hold at the counter"; boxNote.hidden = true; boxUpdate(); }); }); boxBtn.addEventListener("click", function(){ if (boxBtn.disabled) return; boxBtn.classList.add("held"); boxBtn.textContent = "Held ✓ — see you soon"; boxNote.hidden = false; }); /* ----- croissant (boule) easter egg ----- */ var bread = document.getElementById("bread"); if (bread) bread.addEventListener("click", function(){ if (reduceMotion) return; bread.classList.remove("pop"); void bread.getBoundingClientRect(); bread.classList.add("pop"); }); /* ----- open / closed + today's row ----- */ var HOURS = [[480,780],[420,900],[420,900],[420,900],[420,900],[420,900],[480,960]]; /* Sun..Sat */ var DAYS = ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; function fmt(mins){ var h = Math.floor(mins/60), m = mins%60, ap = h >= 12 ? "PM" : "AM"; var h12 = h % 12 === 0 ? 12 : h % 12; return h12 + ":" + pad(m) + " " + ap; } (function openStatus(){ var now = new Date(), d = now.getDay(), nowM = now.getHours()*60 + now.getMinutes(); var row = document.querySelector('.hours tr[data-day="' + d + '"]'); if (row) { var th = row.querySelector("th"); var chipEl = document.createElement("span"); chipEl.className = "today-chip"; chipEl.textContent = "today"; th.appendChild(chipEl); } var o = HOURS[d][0], c = HOURS[d][1], pill = document.getElementById("open-pill"); var msg, open = nowM >= o && nowM < c; if (open) msg = "Open now — closes " + fmt(c); else if (nowM < o) msg = "Closed — opens today at " + fmt(o); else { var nd = d, found = false; for (var k = 1; k <= 7; k++) { nd = (d + k) % 7; if (HOURS[nd][0] !== HOURS[nd][1]) { msg = "Closed — opens " + (k === 1 ? "tomorrow" : DAYS[nd]) + " at " + fmt(HOURS[nd][0]); found = true; break; } } if (!found) msg = "Closed"; } pill.textContent = msg; pill.classList.add(open ? "now" : "later"); })(); document.getElementById("yr").textContent = new Date().getFullYear(); })(); </script> </body> </html>A pricing table with a togglePassed
13,898 tokens: 134 read, 13,764 written · 10 min 11 s · 22.6 tok/s
Passes when Three elements with class plan, each holding a price, and a <button id="billing-toggle"> handled by script; the page switches between monthly and yearly. Nothing loaded from another site and no network requests.
Build a pricing page with three plans. Each plan is an element with class "plan" containing an element with class "price". Add a <button id="billing-toggle"> that switches every price between monthly and yearly billing when clicked. Output one complete HTML document and nothing else. Put all CSS and JavaScript inline. Do not load anything from another site: no external scripts, stylesheets, fonts or images.
Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Timberline Outdoor Club — Rate Card</title> <style> :root{ --pine: #0e241b; --pine-2: #143126; --pine-3: #1b3f30; --cream: #f4efe0; --cream-2: #ece4cf; --ink: #1b2620; --muted: #a8bfae; --marigold: #f2b31c; --tang: #ff6b35; --moss: #7fa08b; --font-display: "Superclarendon","Rockwell","Rockwell Nova","Roboto Slab","Bookman Old Style",Georgia,serif; --font-body: "Avenir Next",Avenir,"Segoe UI","Helvetica Neue","Trebuchet MS",sans-serif; } *{ margin:0; padding:0; box-sizing:border-box; } html{ scroll-behavior:smooth; } body{ background: var(--pine); color: #e9e4d3; font-family: var(--font-body); font-size: 16px; line-height: 1.6; min-height: 100vh; position: relative; background-image: linear-gradient(rgba(244,239,224,.035) 1px, transparent 1px), linear-gradient(90deg, rgba(244,239,224,.035) 1px, transparent 1px); background-size: 44px 44px; } body::after{ /* soft vignette */ content:""; position:fixed; inset:0; pointer-events:none; z-index:0; background: radial-gradient(120% 90% at 50% 0%, transparent 55%, rgba(0,0,0,.45)); } ::selection{ background: var(--marigold); color: var(--ink); } /* ---------- grain ---------- */ .grain{ position:fixed; inset:0; z-index:60; pointer-events:none; opacity:.07; background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>"); } /* ---------- ticker ---------- */ .ticker{ position:relative; z-index:2; overflow:hidden; background: var(--marigold); color: var(--ink); border-bottom: 2px solid var(--ink); font-family: var(--font-display); font-size: .72rem; letter-spacing:.22em; text-transform:uppercase; } .ticker__track{ display:flex; width:max-content; animation: tick 28s linear infinite; } .ticker__group{ white-space:nowrap; padding:.55rem 0; } .ticker__group b{ font-weight:inherit; margin:0 1.4rem; } @keyframes tick{ to{ transform: translateX(-50%); } } .wrap{ position:relative; z-index:1; max-width:1140px; margin:0 auto; padding:0 28px; } /* ---------- masthead ---------- */ .masthead{ display:flex; align-items:flex-end; justify-content:space-between; gap:32px; padding: 64px 0 40px; animation: rise .7s cubic-bezier(.2,.8,.2,1) both; } .kicker{ font-family: var(--font-display); font-size:.74rem; letter-spacing:.34em; text-transform:uppercase; color: var(--marigold); margin-bottom:18px; } h1.display{ font-family: var(--font-display); font-size: clamp(2.7rem, 6.4vw, 4.7rem); line-height:1.02; letter-spacing:-.01em; color: var(--cream); min-height: 1.02em; } h1 .dot{ color: var(--tang); } .lede{ max-width: 52ch; margin-top:18px; color: var(--muted); font-size:1.02rem; } .stamp{ flex:0 0 auto; transform: rotate(6deg); border: 3px double var(--cream); color: var(--cream); padding: 14px 20px; text-align:center; font-family: var(--font-display); letter-spacing:.28em; text-transform:uppercase; font-size:.66rem; line-height:1.9; opacity:.85; } .stamp strong{ display:block; font-size:1.5rem; letter-spacing:.12em; } /* ---------- billing bar ---------- */ .billing{ display:flex; align-items:center; flex-wrap:wrap; gap:22px; border:2px dashed rgba(244,239,224,.28); padding: 18px 24px; margin: 8px 0 56px; animation: rise .7s .12s cubic-bezier(.2,.8,.2,1) both; } .billing__label{ font-family: var(--font-display); font-size:.72rem; letter-spacing:.3em; text-transform:uppercase; color: var(--muted); } .billing__hint{ margin-left:auto; font-size:.85rem; color: var(--muted); font-style: italic; max-width: 22ch; } #billing-toggle{ position:relative; border:2px solid var(--cream); background: var(--pine-2); display:flex; align-items:stretch; cursor:pointer; font-family: var(--font-display); font-size:.86rem; letter-spacing:.08em; padding:4px; border-radius: 999px; transition: border-color .25s; } #billing-toggle:hover{ border-color: var(--marigold); } #billing-toggle:focus-visible{ outline:3px dashed var(--marigold); outline-offset:4px; } .toggle__thumb{ position:absolute; top:4px; bottom:4px; left:4px; width: calc(50% - 4px); background: var(--marigold); border-radius:999px; transition: transform .45s cubic-bezier(.34,1.56,.64,1); z-index:0; } #billing-toggle[data-mode="yearly"] .toggle__thumb{ transform: translateX(100%); } .toggle__option{ position:relative; z-index:1; display:flex; align-items:center; gap:.5em; padding: 10px 22px; color: var(--muted); transition: color .3s; font-weight:700; } #billing-toggle[data-mode="monthly"] .toggle__option:nth-child(1), #billing-toggle[data-mode="yearly"] .toggle__option:nth-child(2){ color: var(--ink); } .save{ font-style:normal; font-size:.62rem; letter-spacing:.14em; text-transform:uppercase; background: var(--tang); color:var(--cream); padding:2px 7px; border-radius:999px; } #billing-toggle[data-mode="yearly"] .save{ background: var(--ink); } /* ---------- plans ---------- */ .plans{ display:grid; grid-template-columns: repeat(3, 1fr); gap: 30px; align-items:stretch; padding-bottom: 72px; } .plan{ position:relative; display:flex; flex-direction:column; background: var(--cream); color: var(--ink); border: 2px solid var(--ink); border-radius: 14px 14px 14px 4px; padding: 30px 28px 28px; border-top: 8px solid var(--moss); box-shadow: 9px 9px 0 rgba(0,0,0,.38); transition: transform .3s cubic-bezier(.2,.8,.2,1), box-shadow .3s; animation: rise .7s cubic-bezier(.2,.8,.2,1) both; } .plan:nth-child(1){ animation-delay:.18s; } .plan:nth-child(2){ animation-delay:.30s; } .plan:nth-child(3){ animation-delay:.42s; } .plan:hover{ transform: translateY(-7px); box-shadow: 14px 15px 0 rgba(0,0,0,.42); } .plan--featured{ border-top-color: var(--marigold); transform: translateY(-16px); box-shadow: 11px 12px 0 var(--pine-3), 11px 12px 0 2px rgba(242,179,28,.55); } .plan--featured:hover{ transform: translateY(-23px); box-shadow: 15px 17px 0 var(--pine-3), 15px 17px 0 2px var(--marigold); } .plan--atlas{ border-top-color: var(--tang); } .popular{ position:absolute; top:-18px; right:22px; transform: rotate(-7deg); background: var(--ink); color: var(--marigold); font-family: var(--font-display); font-size:.66rem; letter-spacing:.26em; text-transform:uppercase; padding: 8px 14px; border:2px solid var(--marigold); animation: bob 3.4s ease-in-out infinite; } @keyframes bob{ 0%,100%{ transform: rotate(-7deg) translateY(0);} 50%{ transform: rotate(-5deg) translateY(-4px);} } .plan__name{ font-family: var(--font-display); font-size:1.6rem; letter-spacing:.01em; } .plan__tag{ font-size:.82rem; color:#5c6b5f; font-style:italic; margin-top:2px; } .plan__pricebox{ position:relative; margin-top: 22px; padding-bottom: 22px; border-bottom: 2px dashed rgba(27,38,32,.35); display:flex; align-items:baseline; gap:6px; flex-wrap:wrap; } .plan__pricebox::before, .plan__pricebox::after{ content:""; position:absolute; top:100%; width:18px; height:18px; border-radius:50%; background: var(--pine); transform: translateY(-50%); border: 2px solid var(--ink); } .plan__pricebox::before{ left:-33px; } .plan__pricebox::after{ right:-33px; } .cur{ font-family: var(--font-display); font-size:1.3rem; align-self:flex-start; margin-top:8px; } .price{ font-family: var(--font-display); font-weight:700; font-size: clamp(3rem, 4vw, 3.6rem); line-height:1; letter-spacing:-.02em; display:inline-block; min-width:1.4ch; transition: color .3s; } .price.tick{ color: var(--tang); transform: scale(1.06); } .per{ font-family: var(--font-display); font-size:.95rem; color:#5c6b5f; } .billed{ flex-basis:100%; font-size:.8rem; color:#5c6b5f; letter-spacing:.04em; margin-top: 8px; transition: opacity .16s, transform .16s; } .billed.swap{ opacity:0; transform: translateY(5px); } .plan__save{ display:inline-block; margin-left:10px; font-size:.62rem; letter-spacing:.14em; text-transform:uppercase; background: var(--marigold); border:1px solid var(--ink); padding: 2px 8px; border-radius:999px; opacity:0; transform: scale(.7); transition: opacity .35s, transform .35s cubic-bezier(.34,1.56,.64,1); } body.is-yearly .plan__save{ opacity:1; transform: scale(1); } .plan__feats{ list-style:none; margin: 24px 0 26px; flex:1; display:flex; flex-direction:column; gap: 11px; font-size:.92rem; } .plan__feats li{ position:relative; padding-left: 26px; transition: transform .2s; } .plan__feats li::before{ content:"✓"; position:absolute; left:0; top:0; font-family: var(--font-display); font-weight:700; color: var(--tang); } .plan__feats li:hover{ transform: translateX(4px); } .plan__feats li:hover::before{ color: var(--ink); } .plan__feats li.sub{ color:#75836f; font-size:.84rem; } .plan__feats li.sub::before{ content:"·"; top:-6px; color:#a3ac97; } .plan__cta{ display:block; text-align:center; text-decoration:none; font-family: var(--font-display); font-size:.9rem; letter-spacing:.14em; text-transform:uppercase; color: var(--ink); border:2px solid var(--ink); padding: 14px 10px; border-radius: 10px; transition: background .25s, color .25s, box-shadow .25s, transform .15s; } .plan__cta .arr{ display:inline-block; transition: transform .25s; margin-left:6px; } .plan__cta:hover{ background: var(--marigold); box-shadow: 4px 4px 0 var(--ink); } .plan__cta:hover .arr{ transform: translateX(5px); } .plan__cta:active{ transform: translate(2px,2px); box-shadow: 2px 2px 0 var(--ink); } .plan--featured .plan__cta{ background: var(--ink); color: var(--cream); } .plan--featured .plan__cta:hover{ background: var(--tang); border-color: var(--tang); color: var(--ink); box-shadow: 4px 4px 0 var(--ink); } .plan__cta:focus-visible{ outline:3px dashed var(--tang); outline-offset:3px; } /* ---------- fine print ---------- */ .fineprint{ display:grid; grid-template-columns: repeat(3, 1fr); gap: 34px; border-top: 2px dashed rgba(244,239,224,.28); padding: 44px 0 56px; opacity:0; transform: translateY(22px); transition: opacity .7s .1s, transform .7s .1s cubic-bezier(.2,.8,.2,1); } .fineprint.in{ opacity:1; transform:none; } .fineprint h3{ font-family: var(--font-display); font-size:.72rem; letter-spacing:.3em; text-transform:uppercase; color: var(--marigold); margin-bottom:10px; } .fineprint p{ font-size:.84rem; color: var(--muted); } .fineprint p b{ color: var(--cream); font-weight:600; } footer.foot{ position:relative; z-index:1; border-top: 2px dashed rgba(244,239,224,.2); background: var(--pine-2); padding: 22px 28px; display:flex; justify-content:space-between; align-items:center; gap:16px; flex-wrap:wrap; font-size:.78rem; letter-spacing:.06em; color: var(--muted); } footer a{ color: var(--marigold); text-decoration:none; font-family: var(--font-display); letter-spacing:.18em; text-transform:uppercase; font-size:.72rem; border-bottom: 1px dashed var(--marigold); padding-bottom:2px; transition: color .2s, border-color .2s; } footer a:hover{ color: var(--tang); border-color: var(--tang); } @keyframes rise{ from{ opacity:0; transform: translateY(26px); } to{ opacity:1; transform: translateY(0);} } @media (max-width: 980px){ .plans{ grid-template-columns: 1fr; max-width: 520px; margin: 0 auto; } .plan--featured{ transform:none; } .plan--featured:hover{ transform: translateY(-7px); } .masthead{ flex-direction:column; align-items:flex-start; padding-top:44px; } .stamp{ align-self:flex-end; margin-top:-20px; } .billing__hint{ margin-left:0; max-width:none; } .fineprint{ grid-template-columns: 1fr; gap:26px; } .plan__pricebox::before{ left:-33px; } } @media (prefers-reduced-motion: reduce){ *, *::before, *::after{ animation: none !important; transition: none !important; } .ticker__track{ transform:none; } .fineprint{ opacity:1; transform:none; } html{ scroll-behavior:auto; } } </style> </head> <body> <div class="grain" aria-hidden="true"></div> <div class="ticker" aria-hidden="true"> <div class="ticker__track"> <span class="ticker__group">No hidden fees<b>★</b>Cancel any season<b>★</b>30-day summit guarantee<b>★</b>Prices locked 12 months<b>★</b>Gear rental credits included<b>★</b>Ranger chat, open at dawn<b>★</b></span> <span class="ticker__group">No hidden fees<b>★</b>Cancel any season<b>★</b>30-day summit guarantee<b>★</b>Prices locked 12 months<b>★</b>Gear rental credits included<b>★</b>Ranger chat, open at dawn<b>★</b></span> </div> </div> <main class="wrap"> <header class="masthead"> <div> <p class="kicker">Timberline Outdoor Club — Member Rate Card</p> <h1 class="display" id="headline">Choose your altitude<span class="dot">.</span></h1> <p class="lede">One membership, every trail in the system. Switch billing cycles whenever the weather turns — your mileage and hut credits carry over either way.</p> </div> <div class="stamp" aria-hidden="true"> Est.<strong>1987</strong>Rate card · Rev. 04 </div> </header> <section class="billing" aria-label="Billing cycle"> <span class="billing__label">Billing cycle</span> <button id="billing-toggle" type="button" aria-pressed="false" data-mode="monthly" aria-label="Switch billing between monthly and yearly"> <span class="toggle__thumb" aria-hidden="true"></span> <span class="toggle__option">Monthly</span> <span class="toggle__option">Yearly <em class="save">2 mo free</em></span> </button> <p class="billing__hint" id="billing-status" aria-live="polite">Prices per member, per month — billed monthly.</p> </section> <section class="plans" aria-label="Membership plans"> <article class="plan" data-monthly="9" data-yearly="7" data-yearly-total="84"> <h2 class="plan__name">Trailhead</h2> <p class="plan__tag">For weekend wanderers</p> <div class="plan__pricebox"> <span class="cur" aria-hidden="true">$</span> <span class="price" data-value="9">9</span> <span class="per">/mo</span> <p class="billed">billed monthly <span class="plan__save">save $24 / yr</span></p> </div> <ul class="plan__feats"> <li>20 guided routes each month</li> <li>Three offline topo maps</li> <li>Community fire rings & bunk maps</li> <li>Gear locker — 2 items</li> <li>Monthly trail-notes digest</li> <li class="sub">Email support, replies within 48 h</li> </ul> <a class="plan__cta" href="#join">Join Trailhead <span class="arr">→</span></a> </article> <article class="plan plan--featured" data-monthly="19" data-yearly="15" data-yearly-total="180"> <span class="popular" aria-hidden="true">Most popular</span> <h2 class="plan__name">Voyager</h2> <p class="plan__tag">For the long way around</p> <div class="plan__pricebox"> <span class="cur" aria-hidden="true">$</span> <span class="price" data-value="19">19</span> <span class="per">/mo</span> <p class="billed">billed monthly <span class="plan__save">save $48 / yr</span></p> </div> <ul class="plan__feats"> <li>60 guided routes each month</li> <li>Unlimited offline topo maps</li> <li>Priority fire-ring booking</li> <li>Gear locker — 8 items</li> <li>Hut passes — 2 per season</li> <li>Live ranger chat, replies within 6 h</li> <li class="sub">30-day summit guarantee</li> </ul> <a class="plan__cta" href="#join">Join Voyager <span class="arr">→</span></a> </article> <article class="plan plan--atlas" data-monthly="39" data-yearly="31" data-yearly-total="372"> <h2 class="plan__name">Atlas</h2> <p class="plan__tag">For base-camp residents</p> <div class="plan__pricebox"> <span class="cur" aria-hidden="true">$</span> <span class="price" data-value="39">39</span> <span class="per">/mo</span> <p class="billed">billed monthly <span class="plan__save">save $96 / yr</span></p> </div> <ul class="plan__feats"> <li>Unlimited routes, all elevation bands</li> <li>First call on every fire ring</li> <li>Offline maps + quarterly paper copies</li> <li>Gear locker — unlimited</li> <li>Hut passes — 8 per season</li> <li>Dedicated ranger, replies within 2 h</li> <li>Two guest passes each month</li> <li class="sub">60-day summit guarantee</li> </ul> <a class="plan__cta" href="#join">Join Atlas <span class="arr">→</span></a> </article> </section> <section class="fineprint" id="fineprint"> <div> <h3>§ Switching cycles</h3> <p>Move from monthly to yearly any time; the remaining monthly days are <b>credited pro-rata</b> against the annual total. No penalty, no small print ambush.</p> </div> <div> <h3>§ The summit guarantee</h3> <p>Miss a hut booking, a gear drop, or a route window that we caused? Your next month is <b>on us</b> — automatically applied, no forms to file.</p> </div> <div> <h3>§ Leaving the range</h3> <p>Cancel before your next billing date and you keep access through the end of the paid period. <b>Maps you download stay yours</b>, forever, offline.</p> </div> </section> </main> <footer class="foot"> <p>Timberline Outdoor Club — printed on evergreen, not pixel-perfect.</p> <a href="#ranger">Talk to a ranger ↗</a> </footer> <script> (function(){ "use strict"; var reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches; /* ---------- scramble-decode headline ---------- */ var headline = document.getElementById("headline"); var finalHTML = headline.innerHTML; if (!reduced && headline){ var glyphs = "▲△/\\|—*+·"; var text = "Choose your altitude."; var total = 34, frame = 0; var dotSpan = '<span class="dot">.</span>'; function step(){ frame++; var out = ""; for (var i = 0; i < text.length; i++){ var ch = text[i]; if (ch === " "){ out += " "; continue; } var lock = (i + 1) * (total / text.length); out += frame > lock ? ch : glyphs[Math.floor(Math.random() * glyphs.length)]; } headline.innerHTML = out + dotSpan; if (frame < total){ requestAnimationFrame(step); } else { headline.innerHTML = finalHTML; } } requestAnimationFrame(step); } /* ---------- billing toggle ---------- */ var toggle = document.getElementById("billing-toggle"); var status = document.getElementById("billing-status"); var plans = Array.prototype.slice.call(document.querySelectorAll(".plan")); var yearly = false; function animatePrice(el, to){ var from = (el._val !== undefined) ? el._val : parseFloat(el.getAttribute("data-value")); el._val = to; if (reduced || from === to){ el.textContent = to; el.setAttribute("data-value", to); return; } el.classList.add("tick"); var dur = 560, t0 = null; function frame(ts){ if (t0 === null) t0 = ts; var p = Math.min(1, (ts - t0) / dur); var e = 1 - Math.pow(1 - p, 3); /* easeOutCubic */ el.textContent = Math.round(from + (to - from) * e); if (p < 1){ requestAnimationFrame(frame); } else { el.setAttribute("data-value", to); setTimeout(function(){ el.classList.remove("tick"); }, 260); } } requestAnimationFrame(frame); } toggle.addEventListener("click", function(){ yearly = !yearly; toggle.setAttribute("data-mode", yearly ? "yearly" : "monthly"); toggle.setAttribute("aria-pressed", String(yearly)); document.body.classList.toggle("is-yearly", yearly); plans.forEach(function(plan){ var target = yearly ? parseInt(plan.getAttribute("data-yearly"), 10) : parseInt(plan.getAttribute("data-monthly"), 10); animatePrice(plan.querySelector(".price"), target); var billed = plan.querySelector(".billed"); var chip = billed.querySelector(".plan__save"); var next = yearly ? "billed yearly — $" + plan.getAttribute("data-yearly-total") : "billed monthly"; function apply(){ billed.textContent = next; if (chip) billed.appendChild(chip); billed.classList.remove("swap"); } if (reduced){ apply(); } else { billed.classList.add("swap"); setTimeout(apply, 170); } }); status.textContent = yearly ? "Yearly billing — two months free on every plan." : "Prices per member, per month — billed monthly."; }); /* ---------- fine print reveal ---------- */ var fp = document.getElementById("fineprint"); if (fp && "IntersectionObserver" in window && !reduced){ var io = new IntersectionObserver(function(entries){ entries.forEach(function(en){ if (en.isIntersecting){ en.target.classList.add("in"); io.disconnect(); } }); }, { threshold: 0.2 }); io.observe(fp); } else if (fp){ fp.classList.add("in"); } })(); </script> </body> </html>An accessible sign-up formPassed
15,754 tokens: 169 read, 15,585 written · 11 min 4 s · 23.5 tok/s
Passes when <form id="signup"> with name, email (type email) and password (type password, minlength 8 or more) inputs, each with a <label for>; a submit button; an element id="status" with role="status"; and script that stops the submit and writes to the status. Nothing loaded from another site and no network requests.
Build a sign-up form: <form id="signup"> with inputs id="name", id="email" (type email) and id="password" (type password, minlength at least 8), each with a <label for>, and a submit button. When the form is submitted and valid, prevent the real submission and show a confirmation in an element with id="status" and role="status". Output one complete HTML document and nothing else. Put all CSS and JavaScript inline. Do not load anything from another site: no external scripts, stylesheets, fonts or images.
Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Northlight — Sign up</title> <style> :root{ --bg:#0c1d18; --card:#15332b; --card-deep:#0f2620; --form-bg-top:#122e27; --form-bg-bot:#0f2822; --line:#2a584a; --line-soft:#1d453a; --ink:#eaf6ef; --muted:#8fb3a4; --faint:#5f8577; --amber:#ffb454; --leaf:#5fd694; --coral:#ff7d6e; --display:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif; --mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,"Liberation Mono",monospace; --sans:"Avenir Next",Avenir,"Segoe UI","Helvetica Neue",Helvetica,sans-serif; } *{box-sizing:border-box} html,body{margin:0;padding:0} body{ min-height:100svh; background:var(--bg); color:var(--ink); font-family:var(--sans); -webkit-font-smoothing:antialiased; } /* ---------- ambient scene ---------- */ .scene{position:fixed;inset:0;z-index:0;overflow:hidden; background: radial-gradient(1100px 750px at 72% -12%, #164034 0%, transparent 60%), radial-gradient(900px 700px at -8% 112%, #123630 0%, transparent 55%), var(--bg); } .scene .gridlayer{position:absolute;inset:0; background-image: linear-gradient(rgba(234,246,239,.04) 1px, transparent 1px), linear-gradient(90deg, rgba(234,246,239,.04) 1px, transparent 1px); background-size:36px 36px; -webkit-mask-image:radial-gradient(ellipse at 50% 38%, black 25%, transparent 75%); mask-image:radial-gradient(ellipse at 50% 38%, black 25%, transparent 75%); } .scene .dots{position:absolute;inset:0;opacity:.45; background-image:radial-gradient(rgba(234,246,239,.12) 1px, transparent 1.4px); background-size:110px 110px; animation:pan 70s linear infinite; } @keyframes pan{to{background-position:110px 220px}} .glow{position:absolute;border-radius:50%;filter:blur(70px)} .glow-a{width:440px;height:440px;top:-120px;left:6%; background:radial-gradient(circle, rgba(255,180,84,.17), transparent 65%); animation:drift 19s ease-in-out infinite alternate; } .glow-b{width:520px;height:520px;bottom:-180px;right:2%; background:radial-gradient(circle, rgba(95,214,148,.14), transparent 65%); animation:drift 24s ease-in-out infinite alternate-reverse; } @keyframes drift{to{transform:translate(38px,26px)}} /* ---------- layout ---------- */ .wrap{position:relative;z-index:1;min-height:100svh;display:grid;place-items:center;padding:44px 20px} .stage{width:min(940px,100%)} .foot{margin:22px 0 0;text-align:center;font:600 10.5px/1 var(--mono); letter-spacing:.18em;text-transform:uppercase;color:var(--faint)} /* ---------- the card (a punched ticket) ---------- */ .card{position:relative;display:grid;grid-template-columns:0.86fr 1fr; border:1px solid var(--line);border-radius:22px;overflow:hidden; background:linear-gradient(180deg,#173830,#122b24); box-shadow:0 44px 90px -34px rgba(0,0,0,.65); transition:box-shadow .5s ease; } .card::before{content:"";position:absolute;top:0;left:0;right:0;height:5px; background:linear-gradient(90deg,var(--amber),var(--leaf));} .card.success{box-shadow:0 44px 90px -34px rgba(0,0,0,.65), 0 0 0 3px rgba(95,214,148,.22)} /* ---------- left: pass panel ---------- */ .pass{position:relative;padding:46px 42px 40px; background: repeating-linear-gradient(45deg, rgba(255,255,255,.018) 0 2px, transparent 2px 9px), linear-gradient(165deg,#1a3d33,#102822); } .pass-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:30px} .kicker{font:700 10px/1 var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--faint)} .pass-no{font:700 11px/1 var(--mono);letter-spacing:.12em;color:var(--amber); border:1px solid rgba(255,180,84,.35);border-radius:999px;padding:5px 10px} .badge{position:relative;width:128px;height:128px;margin:0 0 22px} .badge svg{width:100%;height:100%;overflow:visible} .badge .spin{transform-origin:60px 60px;animation:rot 30s linear infinite} .card.success .badge .spin{animation-duration:5s} @keyframes rot{to{transform:rotate(360deg)}} .ring-text{font:700 9.5px var(--mono);letter-spacing:2.6px;fill:var(--muted)} .ring-inner{fill:none;stroke:var(--line);stroke-dasharray:3 6} .ring-core{font-size:32px;fill:var(--amber);animation:breathe 3.4s ease-in-out infinite} @keyframes breathe{0%,100%{opacity:.75}50%{opacity:1}} h1{margin:6px 0 12px;font-family:var(--display);font-weight:600; font-size:clamp(27px,3.4vw,38px);line-height:1.1;letter-spacing:-.01em} h1 em{color:var(--amber)} .pass-copy{margin:0;font-size:14px;line-height:1.65;color:var(--muted)} .perks{list-style:none;margin:22px 0 0;padding:0;display:grid;gap:10px} .perks li{display:flex;gap:11px;align-items:baseline;font-size:13.5px;color:var(--muted); transition:color .2s, transform .2s} .perks li:hover{color:var(--ink);transform:translateX(3px)} .perks li::before{content:"✚";font:700 11px var(--mono);color:var(--leaf)} /* ---------- right: the form ---------- */ form{position:relative;padding:44px 46px 38px; background:linear-gradient(180deg,var(--form-bg-top),var(--form-bg-bot)); border-left:2px dashed rgba(143,179,164,.28); display:flex;flex-direction:column; } .notch{position:absolute;width:22px;height:22px;border-radius:50%} .notch.t{top:-11px;left:-11px;background:var(--form-bg-top)} .notch.b{bottom:-11px;left:-11px;background:var(--form-bg-bot)} .form-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px} .form-head .tag{font:700 10.5px/1 var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--muted)} .live{display:flex;align-items:center;gap:7px;font:700 9.5px/1 var(--mono); letter-spacing:.14em;text-transform:uppercase;color:var(--faint)} .live-dot{width:7px;height:7px;border-radius:50%;background:var(--leaf); animation:beat 2.2s ease-out infinite} @keyframes beat{0%{box-shadow:0 0 0 0 rgba(95,214,148,.55)}100%{box-shadow:0 0 0 10px rgba(95,214,148,0)}} .field{margin-bottom:20px} .frow{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px} label{font:700 11px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase; color:var(--muted);transition:color .2s} .field:focus-within label{color:var(--amber)} .fno{font:700 10px/1 var(--mono);letter-spacing:.1em;color:var(--faint)} input{width:100%;padding:13px 15px;border-radius:11px;color:var(--ink); background:var(--card-deep);border:1px solid var(--line-soft); font:400 16px/1.35 var(--sans); transition:border-color .2s, box-shadow .2s, background .2s} input::placeholder{color:var(--faint);opacity:.75} input:hover{border-color:var(--line)} input:focus{outline:none;border-color:var(--amber);background:#12312a; box-shadow:0 0 0 4px rgba(255,180,84,.13)} .form.nudge input:invalid{border-color:var(--coral);box-shadow:0 0 0 4px rgba(255,125,110,.1)} @keyframes shake{0%,100%{transform:translateX(0)}20%{transform:translateX(-7px)} 40%{transform:translateX(6px)}60%{transform:translateX(-4px)}80%{transform:translateX(3px)}} form.nudge{animation:shake .45s ease} .pw-wrap{position:relative} .pw-wrap input{padding-right:72px} .pw-toggle{position:absolute;right:7px;top:50%;transform:translateY(-50%); background:transparent;border:1px solid transparent;border-radius:8px;cursor:pointer; color:var(--faint);font:700 10px/1 var(--mono);letter-spacing:.14em;padding:8px 10px; transition:color .2s, border-color .2s, background .2s} .pw-toggle:hover{color:var(--amber);border-color:var(--line);background:rgba(255,180,84,.07)} .meter-row{display:flex;align-items:center;gap:10px;margin-top:8px} .meter{display:flex;gap:4px;flex:1;max-width:120px} .meter i{height:4px;flex:1;border-radius:2px;background:var(--line-soft);transition:background .3s} .meter[data-lvl="1"] i:nth-child(1){background:var(--coral)} .meter[data-lvl="2"] i:nth-child(-n+2){background:var(--amber)} .meter[data-lvl="3"] i{background:var(--leaf)} .meter-word{font:700 10px/1 var(--mono);letter-spacing:.1em;text-transform:uppercase; color:var(--faint);transition:color .3s} .meter-word.w1{color:var(--coral)} .meter-word.w2{color:var(--amber)} .meter-word.w3{color:var(--leaf)} .submit{position:relative;overflow:hidden;margin-top:6px;padding:15px 18px;border:0;cursor:pointer; border-radius:12px;background:linear-gradient(180deg,#ffc670,#ef9a2b);color:#241608; font:700 12.5px/1 var(--mono);letter-spacing:.16em;text-transform:uppercase; transition:transform .16s ease, box-shadow .16s ease, background .3s, filter .3s} .submit:hover{transform:translateY(-2px);box-shadow:0 12px 26px -8px rgba(255,180,84,.45)} .submit:active{transform:translateY(0)} .submit:focus-visible,.pw-toggle:focus-visible{outline:2px solid var(--amber);outline-offset:3px} .submit::before{content:"";position:absolute;top:0;bottom:0;left:-45%;width:35%; background:linear-gradient(100deg,transparent,rgba(255,255,255,.4),transparent); transform:skewX(-18deg);transition:left .55s ease} .submit:hover::before{left:130%} .arr{display:inline-block;margin-left:8px;transition:transform .2s} .submit:hover .arr{transform:translateX(5px)} form.done .submit{background:linear-gradient(180deg,#84e5b1,#46bd7c);color:#0a2a19;cursor:default} form.done .submit:hover{transform:none;box-shadow:none} form.done input{opacity:.55} #status{min-height:1.5em;margin:20px 0 0;font-size:14px;line-height:1.5; display:grid;gap:5px} #status .s-line{display:flex;align-items:center;gap:11px;font-weight:600;animation:rise .5s ease both} #status .s-sub{padding-left:37px;font-size:12.5px;color:var(--muted);animation:rise .5s .08s ease both} @keyframes rise{from{opacity:0;transform:translateY(7px)}} .stamp{flex:none;display:grid;place-items:center;width:27px;height:27px;border-radius:50%; border:2px solid var(--leaf);color:var(--leaf);font:700 13px/1 var(--mono); transform:rotate(-8deg);animation:stampIn .45s cubic-bezier(.2,1.7,.4,1) both} @keyframes stampIn{from{transform:rotate(-8deg) scale(1.9);opacity:0}} /* ---------- responsive ---------- */ @media (max-width:780px){ .card{grid-template-columns:1fr} .pass{padding:38px 28px 30px} .badge{width:96px;height:96px;margin-bottom:16px} form{border-left:none;border-top:2px dashed rgba(143,179,164,.28);padding:34px 26px 30px} .notch.t{left:auto;right:-11px} .notch.b{top:-11px;bottom:auto;left:-11px;background:var(--form-bg-top)} .notch.t{background:var(--form-bg-top)} } @media (max-width:420px){ .wrap{padding:26px 12px} form{padding:30px 20px 26px} .pass{padding:32px 22px 26px} } @media (prefers-reduced-motion:reduce){ *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important} } </style> </head> <body> <div class="scene" aria-hidden="true"> <div class="gridlayer"></div> <div class="dots"></div> <div class="glow glow-a"></div> <div class="glow glow-b"></div> </div> <main class="wrap"> <div class="stage"> <section class="card" id="card" aria-labelledby="signupTitle"> <aside class="pass"> <div class="pass-top"> <span class="kicker">Entry ledger · Northlight</span> <span class="pass-no">№ 0042</span> </div> <div class="badge" aria-hidden="true"> <svg viewBox="0 0 120 120"> <defs> <path id="ringPath" d="M60 60 m-45 0 a45 45 0 1 1 90 0 a45 45 0 1 1 -90 0"/> </defs> <g class="spin"> <text class="ring-text"><textPath href="#ringPath">OPEN LEDGER · NEW ENTRY · OPEN LEDGER ·</textPath></text> </g> <circle class="ring-inner" cx="60" cy="60" r="33"/> <text class="ring-core" x="60" y="60" text-anchor="middle" dominant-baseline="central">✳</text> </svg> </div> <span class="kicker">Application form</span> <h1 id="signupTitle">Sign up for <em>Northlight</em></h1> <p class="pass-copy">The shared studio for small, stubborn teams. Three fields stand between you and your desk — the ledger takes it from there.</p> <ul class="perks"> <li>A desk in every room, invite included</li> <li>Monospace-grade audit trail, always honest</li> <li>Cancel any time — no fine print</li> </ul> </aside> <form id="signup" novalidate=""> <span class="notch t" aria-hidden="true"></span> <span class="notch b" aria-hidden="true"></span> <div class="form-head"> <span class="tag">03 · The fine fields</span> <span class="live"><span class="live-dot"></span>Accepting entries</span> </div> <div class="field"> <div class="frow"> <label for="name">Full name</label> <span class="fno" aria-hidden="true">01</span> </div> <input id="name" name="name" type="text" required autocomplete="name" placeholder="Ada Lovelace"> </div> <div class="field"> <div class="frow"> <label for="email">Email address</label> <span class="fno" aria-hidden="true">02</span> </div> <input id="email" name="email" type="email" required autocomplete="email" placeholder="ada@studio.co"> </div> <div class="field"> <div class="frow"> <label for="password">Password</label> <span class="fno" aria-hidden="true">03</span> </div> <div class="pw-wrap"> <input id="password" name="password" type="password" required minlength="8" autocomplete="new-password" placeholder="At least 8 characters"> <button type="button" class="pw-toggle" id="pwToggle" aria-pressed="false" aria-label="Show password">SHOW</button> </div> <div class="meter-row"> <div class="meter" id="meter" data-lvl="0" aria-hidden="true"><i></i><i></i><i></i></div> <span class="meter-word" id="meterWord"></span> </div> </div> <button type="submit" class="submit" id="submitBtn">Create account<span class="arr">→</span></button> <p id="status" class="status" role="status" aria-live="polite"></p> </form> </section> <p class="foot">Local form · nothing leaves this page</p> </div> </main> <script> (function(){ "use strict"; var form = document.getElementById("signup"); var nameI = document.getElementById("name"); var emailI = document.getElementById("email"); var passI = document.getElementById("password"); var status = document.getElementById("status"); var meter = document.getElementById("meter"); var meterW = document.getElementById("meterWord"); var toggle = document.getElementById("pwToggle"); var btn = document.getElementById("submitBtn"); var card = document.getElementById("card"); /* ---- live password strength ---- */ function refreshMeter(){ var v = passI.value, len = v.length, lvl, word, cls; if (len === 0){ lvl = 0; word = ""; cls = ""; } else if (len < 8){ lvl = 1; word = "8+ characters needed"; cls = "w1"; } else if (len < 12 || !/[A-Z]/.test(v) || !/[a-z]/.test(v) || !/\d/.test(v)){ lvl = 2; word = "Good"; cls = "w2"; } else { lvl = 3; word = "Strong"; cls = "w3"; } meter.setAttribute("data-lvl", String(lvl)); meterW.textContent = word; meterW.className = "meter-word" + (cls ? " " + cls : ""); } passI.addEventListener("input", refreshMeter); /* ---- show / hide password ---- */ toggle.addEventListener("click", function(){ var show = passI.type === "password"; passI.type = show ? "text" : "password"; toggle.textContent = show ? "HIDE" : "SHOW"; toggle.setAttribute("aria-pressed", String(show)); toggle.setAttribute("aria-label", show ? "Hide password" : "Show password"); passI.focus({preventScroll:true}); }); /* ---- submit ---- */ form.addEventListener("submit", function(e){ e.preventDefault(); if (form.classList.contains("done")) return; if (!form.checkValidity()){ form.classList.remove("nudge"); void form.offsetWidth; /* restart the shake */ form.classList.add("nudge"); form.reportValidity(); status.textContent = ""; setTimeout(function(){ form.classList.remove("nudge"); }, 600); return; } var name = nameI.value.trim(); var email = emailI.value.trim(); var first = (name.split(/\s+/)[0] || "friend").replace(/^\w/, function(c){ return c.toUpperCase(); }); /* confirmation, built with text nodes (no injection) */ status.textContent = ""; status.className = "status ok"; var line = document.createElement("span"); line.className = "s-line"; var stamp = document.createElement("span"); stamp.className = "stamp"; stamp.setAttribute("aria-hidden", "true"); stamp.textContent = "✓"; line.appendChild(stamp); line.appendChild(document.createTextNode("Welcome, " + first + " — your account is confirmed.")); var sub = document.createElement("span"); sub.className = "s-sub"; sub.textContent = "A pass for " + email + " has been added to the ledger."; status.appendChild(line); status.appendChild(sub); /* success state */ form.classList.add("done"); card.classList.add("success"); btn.disabled = true; btn.textContent = "Account created ✓"; [nameI, emailI, passI].forEach(function(i){ i.disabled = true; }); }); })(); </script> </body> </html>A to-do app that remembersPassed
21,946 tokens: 157 read, 21,789 written · 15 min 47 s · 23 tok/s
Passes when The input, add button and list with the given ids, and script that saves to localStorage under "todos" and adds list items with a remove control. Nothing loaded from another site and no network requests.
Build a to-do list app with an <input id="new-task">, a <button id="add"> and a <ul id="tasks">. Clicking add appends the text as an <li> with its own remove button. Save the list in localStorage under the key "todos" and load it again when the page opens. Output one complete HTML document and nothing else. Put all CSS and JavaScript inline. Do not load anything from another site: no external scripts, stylesheets, fonts or images.
Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>The Day's List — a little to-do paper</title> <style> :root{ --desk:#16232b; --paper-hi:#f9f4e9; --paper-lo:#f3ecdc; --ink:#253239; --ink-soft:#4c5b63; --pencil:#6d7a80; --rule:rgba(37,50,57,.16); --red:#d5482e; --red-soft:rgba(213,72,46,.14); --green:#2f7d5a; --display:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif; --body:"Avenir Next",Avenir,"Segoe UI","Helvetica Neue",Arial,sans-serif; --mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,monospace; } *{box-sizing:border-box} html{background:var(--desk)} body{ margin:0; font-family:var(--body); color:var(--ink); -webkit-font-smoothing:antialiased; } ::selection{background:rgba(213,72,46,.25)} .desk{ position:relative; min-height:100svh; display:grid; place-items:center; padding:56px 18px; } .desk::before{ content:""; position:fixed; inset:-20%; background: radial-gradient(52rem 34rem at 78% 6%, rgba(56,132,138,.35), transparent 65%), radial-gradient(42rem 30rem at 6% 98%, rgba(176,84,52,.26), transparent 60%); animation:drift 26s ease-in-out infinite alternate; pointer-events:none; } .desk::after{ content:""; position:fixed; inset:0; background-image:radial-gradient(rgba(255,255,255,.05) 1px, transparent 1.4px); background-size:24px 24px; pointer-events:none; } @keyframes drift{to{transform:translate(3%,-2%) scale(1.04)}} /* ---------- the sheet ---------- */ .paper{ position:relative; z-index:1; width:min(640px,94vw); padding:42px 40px 26px 66px; background:linear-gradient(160deg,var(--paper-hi),var(--paper-lo)); border-radius:3px; box-shadow: 0 24px 50px -12px rgba(0,0,0,.55), 0 6px 16px rgba(0,0,0,.35); transform:rotate(-.4deg); } .paper::before{ /* red margin rule */ content:""; position:absolute; top:-12px; bottom:-12px; left:44px; width:1.5px; background:linear-gradient(180deg,transparent,rgba(213,72,46,.42) 8%,rgba(213,72,46,.42) 92%,transparent); } .tape{ position:absolute; top:-13px; width:112px; height:32px; box-shadow:0 3px 8px rgba(0,0,0,.22); background-image:repeating-linear-gradient(45deg,rgba(255,255,255,.09) 0 5px,transparent 5px 10px); } .tape-l{left:32px; transform:rotate(-7deg); background-color:rgba(122,178,196,.45);} .tape-r{right:34px; transform:rotate(5deg); background-color:rgba(216,166,92,.42);} .kicker{ display:flex; justify-content:space-between; align-items:center; gap:12px; font-family:var(--mono); font-size:.66rem; letter-spacing:.16em; text-transform:uppercase; color:var(--pencil); } .kicker-right{display:inline-flex; align-items:center; gap:14px;} .save{display:inline-flex; align-items:center; gap:6px;} .save i{ width:7px; height:7px; border-radius:50%; background:var(--green); transition:transform .2s; } .save.ping i{animation:ping .6s ease} @keyframes ping{35%{transform:scale(1.8); box-shadow:0 0 0 5px rgba(47,125,90,.22)}} h1{ margin:8px 0 14px; font-family:var(--display); font-style:italic; font-weight:700; font-size:clamp(1.9rem,4.5vw,2.55rem); letter-spacing:-.01em; line-height:1.05; display:inline-block; padding-bottom:14px; background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='6' viewBox='0 0 120 6'%3E%3Cpath d='M0 3 Q 10 0 20 3 T 40 3 T 60 3 T 80 3 T 100 3 T 120 3' fill='none' stroke='%23d5482e' stroke-width='2' stroke-linecap='round' opacity='0.65'/%3E%3C/svg%3E") repeat-x left bottom / 96px 6px; } .status{ display:flex; align-items:center; gap:14px; margin:0 0 18px; } #count{ display:inline-block; font-family:var(--mono); font-size:.76rem; letter-spacing:.04em; color:var(--ink-soft); white-space:nowrap; } #count.bump{animation:bump .35s ease} @keyframes bump{40%{transform:scale(1.15)}} .progress{ flex:1; height:6px; border-radius:3px; overflow:hidden; background:rgba(37,50,57,.12); } .progress i{ display:block; height:100%; width:0; border-radius:3px; background:var(--green); transition:width .55s cubic-bezier(.22,1,.36,1), background .4s; } .progress i.full{background:linear-gradient(90deg,#2f7d5a,#4fa07a)} /* ---------- add row ---------- */ .add-row{ display:flex; align-items:flex-end; gap:14px; margin:2px 0 10px; } #new-task{ flex:1; font-family:var(--body); font-size:1.05rem; color:var(--ink); background:transparent; border:0; border-bottom:2px dashed rgba(37,50,57,.35); padding:8px 2px 7px; transition:border-color .2s; } #new-task::placeholder{color:#9aa59f; font-style:italic} #new-task:focus{outline:none; border-bottom-color:var(--red)} #new-task.nudge{animation:nudge .3s ease; border-bottom-color:var(--red)} @keyframes nudge{ 0%,100%{transform:translateX(0)} 25%{transform:translateX(-5px)} 50%{transform:translateX(5px)} 75%{transform:translateX(-3px)} } #add{ font-family:var(--body); font-weight:600; font-size:.95rem; letter-spacing:.02em; color:#f6f2e7; background:var(--ink); border:0; border-radius:8px; padding:10px 18px; cursor:pointer; box-shadow:0 2px 0 rgba(0,0,0,.25), 0 8px 18px -8px rgba(0,0,0,.5); transition:transform .15s ease, background .2s, box-shadow .15s; } #add .enter{opacity:.55; font-size:.85em; margin-left:2px} #add:hover{background:#33454f; transform:translateY(-1px); box-shadow:0 3px 0 rgba(0,0,0,.25), 0 12px 22px -8px rgba(0,0,0,.55)} #add:active{transform:translateY(1px)} #add.stamp{animation:stamp .35s ease} @keyframes stamp{35%{transform:scale(.93) rotate(-2.5deg)}70%{transform:scale(1.04)}} /* ---------- the list ---------- */ #tasks{list-style:none; margin:0; padding:0} #tasks li{ position:relative; display:grid; grid-template-columns:24px 1fr auto; align-items:center; gap:13px; padding:11px 0; border-bottom:1px solid var(--rule); animation:drop .38s ease both; } #tasks li:first-child{padding-top:4px} @keyframes drop{from{opacity:0; transform:translateY(-8px)}} #tasks li.fresh{animation:drop .38s ease both, fresh 1.1s ease .05s both} @keyframes fresh{from{background:rgba(47,125,90,.15)} to{background:transparent}} #tasks li.out{ transition:height .3s ease, opacity .25s ease, transform .3s ease, padding .3s ease, border-color .3s ease; height:0 !important; opacity:0; transform:translateX(26px); padding-top:0 !important; padding-bottom:0 !important; overflow:hidden; border-bottom-color:transparent; } .check{position:relative; display:inline-flex; cursor:pointer} .check input{position:absolute; inset:0; width:100%; height:100%; margin:0; opacity:0; cursor:pointer} .box{ width:22px; height:22px; border-radius:6px; border:2px solid var(--ink-soft); background:#fffdf6; display:grid; place-items:center; transition:border-color .2s, background .2s, transform .12s; } .check:active .box{transform:scale(.9)} .check input:focus-visible + .box{outline:2px dashed var(--red); outline-offset:3px} .box svg{width:16px; height:16px; display:block; margin-left:-1px} .box svg path{ fill:none; stroke:var(--green); stroke-width:3.2; stroke-linecap:round; stroke-linejoin:round; stroke-dasharray:24; stroke-dashoffset:24; transition:stroke-dashoffset .28s ease .04s; } #tasks li.done .box{border-color:var(--green); background:rgba(47,125,90,.08)} #tasks li.done .box svg path{stroke-dashoffset:0} .txt{ position:relative; font-size:1.02rem; line-height:1.45; color:var(--ink); word-break:break-word; transition:color .25s; } .txt::after{ content:""; position:absolute; left:0; top:calc(50% - 1px); height:2px; width:0; border-radius:2px; background:var(--ink-soft); transition:width .32s cubic-bezier(.4,0,.2,1); } #tasks li.done .txt{color:var(--pencil)} #tasks li.done .txt::after{width:100%} .rm{ width:28px; height:28px; border:0; border-radius:50%; background:transparent; color:var(--ink-soft); font-size:19px; line-height:1; display:grid; place-items:center; cursor:pointer; opacity:0; transform:translateX(4px); transition:opacity .22s, transform .22s, background .2s, color .2s; } #tasks li:hover .rm, #tasks li:focus-within .rm{opacity:1; transform:none} .rm:hover{background:var(--red-soft); color:var(--red); transform:rotate(90deg) scale(1.05)} .rm:active{transform:rotate(90deg) scale(.9)} @media (hover:none){.rm{opacity:1; transform:none}} .empty{ margin:16px 0 10px; padding:22px 18px; border:1.5px dashed rgba(37,50,57,.28); border-radius:10px; text-align:center; font-family:var(--display); font-style:italic; font-size:1rem; color:var(--pencil); } [hidden]{display:none !important} .foot{ display:flex; justify-content:space-between; align-items:center; gap:12px; margin-top:16px; padding-top:12px; border-top:1px solid var(--rule); font-family:var(--mono); font-size:.68rem; letter-spacing:.08em; color:var(--pencil); } #clear-done{ font:inherit; letter-spacing:inherit; background:none; border:0; cursor:pointer; color:var(--pencil); text-decoration:underline dashed; text-underline-offset:3px; transition:color .2s; } #clear-done:hover{color:var(--red)} :focus-visible{outline:2px dashed var(--red); outline-offset:3px} @media (max-width:600px){ .desk{padding:40px 10px} .paper{padding:34px 20px 20px 46px} .paper::before{left:26px} .tape{width:76px; height:26px; top:-11px} .tape-l{left:16px} .tape-r{right:16px} #clock{display:none} } @media (prefers-reduced-motion:reduce){ *,*::before,*::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } .desk::before{animation:none} } </style> </head> <body> <main class="desk"> <section class="paper" aria-label="To-do list"> <span class="tape tape-l" aria-hidden="true"></span> <span class="tape tape-r" aria-hidden="true"></span> <header> <div class="kicker"> <span id="date">—</span> <span class="kicker-right"> <span id="clock">--:--:--</span> <span class="save" id="save-wrap"><i></i><span>saved</span></span> </span> </div> <h1>The Day's List</h1> <div class="status"> <span id="count">0 tasks</span> <div class="progress" role="progressbar" aria-label="Tasks completed"><i id="bar"></i></div> </div> </header> <form id="add-form" class="add-row" autocomplete="off"> <input id="new-task" type="text" maxlength="140" placeholder="Write a task, press Enter…" aria-label="New task"> <button id="add" type="submit">Add<span class="enter">↵</span></button> </form> <ul id="tasks"></ul> <p id="empty" class="empty">Nothing here yet — add your first task above.</p> <footer class="foot"> <span>autosaves to this browser</span> <button id="clear-done" type="button" hidden>clear done</button> </footer> </section> </main> <script> (function(){ 'use strict'; var KEY = 'todos'; var list = document.getElementById('tasks'); var form = document.getElementById('add-form'); var input = document.getElementById('new-task'); var addBtn = document.getElementById('add'); var countEl = document.getElementById('count'); var bar = document.getElementById('bar'); var emptyEl = document.getElementById('empty'); var clearBtn = document.getElementById('clear-done'); var saveWrap = document.getElementById('save-wrap'); var clockEl = document.getElementById('clock'); var dateEl = document.getElementById('date'); var RM = window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches; function uid(){ return Date.now().toString(36) + Math.random().toString(36).slice(2,7); } function pingSave(){ saveWrap.classList.remove('ping'); void saveWrap.offsetWidth; saveWrap.classList.add('ping'); } function save(quiet){ try{ localStorage.setItem(KEY, JSON.stringify(items.map(function(it){ return { t: it.t, d: it.d }; }))); }catch(e){} if(!quiet) pingSave(); } function load(){ var raw = null; try{ raw = localStorage.getItem(KEY); }catch(e){} if(raw === null){ var seed = [ { t: 'Sketch the poster idea', d: false }, { t: "Reply to Sam's email", d: false }, { t: 'Water the plants', d: true } ]; try{ localStorage.setItem(KEY, JSON.stringify(seed)); }catch(e){} return seed; } var data; try{ data = JSON.parse(raw); }catch(e){ return []; } if(!Array.isArray(data)) return []; return data.map(function(x){ if(typeof x === 'string') return { t: x, d: false }; return { t: String((x && x.t) || ''), d: !!(x && x.d) }; }).filter(function(x){ return x.t.length; }); } var items = load().map(function(it){ it.id = uid(); return it; }); var lastOpen = null; function buildLi(item, idx, isNew){ var li = document.createElement('li'); li.dataset.id = item.id; if(item.d) li.className = 'done'; if(isNew) li.classList.add('fresh'); else li.style.animationDelay = Math.min(idx * 45, 450) + 'ms'; var lab = document.createElement('label'); lab.className = 'check'; var cb = document.createElement('input'); cb.type = 'checkbox'; cb.checked = item.d; cb.setAttribute('aria-label', 'Mark task done'); var box = document.createElement('span'); box.className = 'box'; box.innerHTML = "<svg viewBox='0 0 24 24' aria-hidden='true'><path d='M4.5 12.8l5 5L19.5 7' pathLength='24'/></svg>"; lab.appendChild(cb); lab.appendChild(box); var txt = document.createElement('span'); txt.className = 'txt'; txt.textContent = item.t; var rm = document.createElement('button'); rm.type = 'button'; rm.className = 'rm'; rm.textContent = '\u00d7'; rm.setAttribute('aria-label', 'Delete: ' + item.t); li.appendChild(lab); li.appendChild(txt); li.appendChild(rm); cb.addEventListener('change', function(){ var it = null; for(var k = 0; k < items.length; k++) if(items[k].id === item.id){ it = items[k]; break; } if(!it) return; it.d = cb.checked; li.classList.toggle('done', it.d); save(); updateUI(); }); rm.addEventListener('click', function(){ removeLi(li, item.id); }); list.appendChild(li); return li; } function removeLi(li, id){ if(li.dataset.removing) return; li.dataset.removing = '1'; var finished = false; function finalize(){ if(finished) return; finished = true; var i = -1; for(var k = 0; k < items.length; k++) if(items[k].id === id){ i = k; break; } if(i > -1) items.splice(i, 1); if(li.parentNode) li.parentNode.removeChild(li); save(); updateUI(); } if(RM){ finalize(); return; } li.style.height = li.offsetHeight + 'px'; void li.offsetHeight; li.classList.add('out'); li.addEventListener('transitionend', function h(e){ if(e.propertyName === 'height'){ li.removeEventListener('transitionend', h); finalize(); } }); setTimeout(finalize, 500); /* fallback */ } function updateUI(){ var total = items.length; var done = items.filter(function(x){ return x.d; }).length; var open = total - done; bar.style.width = total ? (done / total * 100) + '%' : '0%'; bar.classList.toggle('full', total > 0 && done === total); countEl.textContent = !total ? '0 tasks' : !open ? 'all ' + total + ' done \u2726' : open + ' open' + (done ? ' \u00b7 ' + done + ' done' : ''); emptyEl.hidden = total > 0; clearBtn.hidden = done === 0; if(lastOpen !== null && lastOpen !== open){ countEl.classList.remove('bump'); void countEl.offsetWidth; countEl.classList.add('bump'); } lastOpen = open; } form.addEventListener('submit', function(e){ e.preventDefault(); var t = input.value.trim(); if(!t){ input.classList.remove('nudge'); void input.offsetWidth; input.classList.add('nudge'); input.focus(); return; } var item = { id: uid(), t: t, d: false }; items.push(item); save(); buildLi(item, items.length - 1, true); updateUI(); input.value = ''; input.focus(); addBtn.classList.remove('stamp'); void addBtn.offsetWidth; addBtn.classList.add('stamp'); }); input.addEventListener('keydown', function(e){ if(e.key === 'Escape') input.value = ''; }); input.addEventListener('animationend', function(){ input.classList.remove('nudge'); }); addBtn.addEventListener('animationend', function(){ addBtn.classList.remove('stamp'); }); countEl.addEventListener('animationend', function(){ countEl.classList.remove('bump'); }); clearBtn.addEventListener('click', function(){ var lis = list.querySelectorAll('li.done'); var i = 0; Array.prototype.forEach.call(lis, function(li){ var delay = RM ? 0 : i++ * 70; setTimeout(function(){ removeLi(li, li.dataset.id); }, delay); }); }); /* live clock + date */ function pad(n){ return (n < 10 ? '0' : '') + n; } function tick(){ var d = new Date(); clockEl.textContent = pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds()); } dateEl.textContent = new Date().toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' }); tick(); setInterval(tick, 1000); /* initial paint */ items.forEach(function(it, i){ buildLi(it, i, false); }); updateUI(); })(); </script> </body> </html> ```A bar chart in SVGPassed
4,473 tokens: 182 read, 4,291 written · 2 min 55 s · 24.6 tok/s
Passes when Five <rect class="bar"> in an <svg>, written in the markup, with heights proportional to 12, 30, 18, 24 and 6 in that order, labelled Mon to Fri in <text>, and a <title> or aria-label. Nothing loaded from another site and no network requests.
Draw an inline SVG bar chart of coffee sold: Mon 12, Tue 30, Wed 18, Thu 24, Fri 6. Each bar is a <rect class="bar"> whose height attribute is proportional to its value, each bar is labelled with its day in a <text>, and the <svg> has a <title> or an aria-label. Write the bars and labels directly in the SVG markup, not with JavaScript. Output one complete HTML document and nothing else. Put all CSS and JavaScript inline. Do not load anything from another site: no external scripts, stylesheets, fonts or images.
Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Coffee Sold Bar Chart</title> <style> body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: #f7f4ef; font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; color: #302a25; } figure { margin: 0; padding: 24px; background: #fff; border: 1px solid #e5ddd2; border-radius: 12px; box-shadow: 0 10px 24px rgba(0, 0, 0, 0.08); max-width: 520px; width: min(100%, 520px); } svg { display: block; width: 100%; height: auto; } .bar { fill: #6f4a2f; transition: fill 120ms ease; } .bar:hover { fill: #8a5f3d; } .axis { stroke: #8c847b; stroke-width: 2; stroke-linecap: round; } .grid { stroke: #e8e2d8; stroke-width: 1; } .day-label { font-size: 14px; font-weight: 600; fill: #4a4139; } .value-label { font-size: 12px; fill: #6b6157; } figcaption { margin-top: 12px; text-align: center; font-size: 14px; color: #5e554b; } </style> </head> <body> <figure> <svg viewBox="0 0 420 300" role="img" aria-label="Bar chart of coffee sold by day: Monday 12, Tuesday 30, Wednesday 18, Thursday 24, Friday 6" > <title>Coffee sold by day</title> <line class="grid" x1="30" y1="50" x2="390" y2="50"></line> <line class="grid" x1="30" y1="90" x2="390" y2="90"></line> <line class="grid" x1="30" y1="130" x2="390" y2="130"></line> <line class="grid" x1="30" y1="170" x2="390" y2="170"></line> <line class="grid" x1="30" y1="210" x2="390" y2="210"></line> <line class="axis" x1="30" y1="250" x2="390" y2="250"></line> <rect class="bar" x="50" y="170" width="40" height="80"></rect> <rect class="bar" x="130" y="50" width="40" height="200"></rect> <rect class="bar" x="210" y="130" width="40" height="120"></rect> <rect class="bar" x="290" y="90" width="40" height="160"></rect> <rect class="bar" x="350" y="210" width="40" height="40"></rect> <text class="value-label" x="70" y="162" text-anchor="middle">12</text> <text class="value-label" x="150" y="42" text-anchor="middle">30</text> <text class="value-label" x="230" y="122" text-anchor="middle">18</text> <text class="value-label" x="310" y="82" text-anchor="middle">24</text> <text class="value-label" x="370" y="202" text-anchor="middle">6</text> <text class="day-label" x="70" y="272" text-anchor="middle">Mon</text> <text class="day-label" x="150" y="272" text-anchor="middle">Tue</text> <text class="day-label" x="230" y="272" text-anchor="middle">Wed</text> <text class="day-label" x="310" y="272" text-anchor="middle">Thu</text> <text class="day-label" x="370" y="272" text-anchor="middle">Fri</text> </svg> <figcaption>Cups of coffee sold each day</figcaption> </figure> </body> </html>
Building games 1 of 2
Build 2048Passed
37,342 tokens: 502 read, 36,840 written · 27 min 13 s · 22.6 tok/s
Build the game 2048 as one web page, playable with the arrow keys, with the board drawn on the page. In <script id="game">, define createGame() returning an object with move(dir) and state(). dir is "up", "down", "left" or "right". state() returns {board, score, over}: board is 4 rows of 4 numbers, top row first (0 is an empty cell), score is a number, over is a boolean. The rules, exactly: 1. The board starts as this one, then a 2 is put in each of the first two empty cells in reading order (row by row from the top, left to right). The score starts at 0. [16, 0, 4, 8] [2, 0, 0, 0] [0, 32, 4, 32] [4, 0, 0, 0] 2. A move slides every tile as far as it goes toward dir. Two tiles of the same value that meet merge into one tile of their sum, nearest the side they move toward first: moving left, [2,2,2,2] becomes [4,4,0,0] and [2,2,2,0] becomes [4,2,0,0]. A tile made by a merge does not merge again in the same move. 3. Each merge adds the new tile's value to the score. 4. If a move changed the board, a 2 is put in the first empty cell in reading order. A move that changes nothing does nothing. 5. over is true when no move would change the board; then moves do nothing. Output one complete HTML document and nothing else, with all CSS and JavaScript inline and nothing loaded from another site. Put the game logic, and only it, in <script id="game">: it must not touch document, window or timers, because the site runs that script on its own and plays the game through createGame(). Wire the page in another <script>.Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>2048</title> <style> :root { --font-display: ui-rounded, "Avenir Next Rounded", "Arial Rounded MT Bold", "Trebuchet MS", "Segoe UI", system-ui, sans-serif; --font-body: "Avenir Next", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif; --ink: #e7f2ef; --muted: #7fa39f; --amber: #f2b13e; } * { box-sizing: border-box; } html, body { margin: 0; } body { min-height: 100vh; font-family: var(--font-body); color: var(--ink); background: #0c1a20; overflow-x: hidden; } /* ---- ambient background ---- */ .bg { position: fixed; inset: 0; z-index: -1; overflow: hidden; background: radial-gradient(1100px 750px at 85% 115%, rgba(242,177,62,.08), transparent 60%), radial-gradient(950px 680px at -10% -12%, rgba(47,158,143,.13), transparent 60%), #0c1a20; } .glow { position: absolute; border-radius: 50%; } .g1 { width: 55vmax; height: 55vmax; top: -18vmax; left: -12vmax; background: radial-gradient(circle, rgba(47,158,143,.17), transparent 65%); animation: drift1 28s ease-in-out infinite alternate; } .g2 { width: 48vmax; height: 48vmax; bottom: -16vmax; right: -10vmax; background: radial-gradient(circle, rgba(242,177,62,.12), transparent 65%); animation: drift2 34s ease-in-out infinite alternate; } @keyframes drift1 { to { transform: translate(6vmax, 4vmax); } } @keyframes drift2 { to { transform: translate(-5vmax, -3vmax); } } .dots { position: absolute; inset: 0; background-image: radial-gradient(rgba(231,242,239,.06) 1px, transparent 1.3px); background-size: 26px 26px; -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 78%); mask-image: radial-gradient(ellipse 90% 70% at 50% 40%, #000 30%, transparent 78%); } /* ---- layout ---- */ .wrap { max-width: 620px; margin: 0 auto; padding: 34px 16px 44px; } .top { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 24px; user-select: none; } .logo { display: flex; gap: 8px; margin: 0 0 10px; } .lt { width: 50px; height: 50px; border-radius: 11px; display: grid; place-items: center; font-family: var(--font-display); font-weight: 800; font-size: 27px; box-shadow: inset 0 1px 0 rgba(255,255,255,.3), 0 10px 20px -10px rgba(0,0,0,.6); animation: bob 4.5s ease-in-out infinite; } .lt:nth-child(2) { animation-delay: -1.1s; } .lt:nth-child(3) { animation-delay: -2.3s; } .lt:nth-child(4) { animation-delay: -3.4s; } @keyframes bob { 50% { transform: translateY(-6px); } } .lt2 { background: #dcefe6; color: #3a5a4e; } .lt0 { background: #1b3d49; color: #7fa39f; box-shadow: inset 0 2px 6px rgba(0,0,0,.4); } .lt4 { background: #92d8c0; color: #17453a; } .lt8 { background: #59c0a4; color: #0c352c; } .tag { margin: 0; font-size: 14px; color: var(--muted); } .tag b { color: var(--amber); font-weight: 800; } .scores { display: flex; gap: 10px; } .chip { position: relative; min-width: 96px; padding: 8px 15px 10px; border-radius: 12px; background: #122730; text-align: right; box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 10px 22px -12px rgba(0,0,0,.65); } .chip-label { display: block; font-size: 10.5px; letter-spacing: .15em; text-transform: uppercase; color: var(--muted); } .chip-val { display: block; font-family: var(--font-display); font-weight: 800; font-size: 25px; line-height: 1.1; font-variant-numeric: tabular-nums; } .chip-val.bump { animation: bump .35s ease; } @keyframes bump { 40% { transform: scale(1.24); color: #ffd95e; } } .gain { position: absolute; left: 50%; top: -4px; transform: translateX(-50%); font-family: var(--font-display); font-weight: 800; font-size: 17px; color: #ffd95e; pointer-events: none; animation: rise .95s ease-out forwards; } @keyframes rise { to { transform: translate(-50%, -36px); opacity: 0; } } .board-zone { display: grid; justify-items: center; } /* ---- board ---- */ .board { --gap: clamp(7px, 1.9vmin, 11px); --tile: calc((min(88vw, 400px) - 5 * var(--gap)) / 4); position: relative; width: min(88vw, 400px); aspect-ratio: 1 / 1; border-radius: 18px; background: linear-gradient(160deg, #123039, #0e2530); box-shadow: 0 32px 70px -24px rgba(0,0,0,.8), inset 0 1px 0 rgba(255,255,255,.05); touch-action: none; user-select: none; } .board.nudge { animation: nudge .18s ease-out; } @keyframes nudge { 35% { transform: translate(var(--nx, 0px), var(--ny, 0px)); } } .cells { position: absolute; inset: 0; display: grid; grid-template-rows: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr); gap: var(--gap); padding: var(--gap); } .cell { background: #1b3d49; border-radius: 12px; box-shadow: inset 0 2px 6px rgba(0,0,0,.35); } .tiles { position: absolute; inset: 0; } .tile { position: absolute; top: 0; left: 0; width: var(--tile); height: var(--tile); border-radius: 12px; display: grid; place-items: center; font-family: var(--font-display); font-weight: 800; line-height: 1; letter-spacing: -.02em; transform: translate( calc(var(--gap) + var(--c) * (var(--tile) + var(--gap))), calc(var(--gap) + var(--r) * (var(--tile) + var(--gap))) ); transition: transform 120ms cubic-bezier(.25,.8,.35,1); box-shadow: inset 0 1px 0 rgba(255,255,255,.28), 0 4px 10px -4px rgba(0,0,0,.45); will-change: transform; } .tile.spawn { animation: spawn .22s cubic-bezier(.34,1.4,.5,1) backwards; } @keyframes spawn { 0% { scale: .2; opacity: 0; } 70% { scale: 1.06; opacity: 1; } 100% { scale: 1; opacity: 1; } } .tile.merge { animation: merge .24s cubic-bezier(.34,1.45,.5,1) backwards; } @keyframes merge { 0% { scale: .3; opacity: 0; } 60% { scale: 1.18; opacity: 1; } 100% { scale: 1; opacity: 1; } } .f1 { font-size: calc(var(--tile) * .44); } .f2 { font-size: calc(var(--tile) * .35); } .f3 { font-size: calc(var(--tile) * .28); } .f4 { font-size: calc(var(--tile) * .21); } .t-2 { background: #dcefe6; color: #3a5a4e; } .t-4 { background: #c1e6d3; color: #33564a; } .t-8 { background: #92d8c0; color: #17453a; } .t-16 { background: #59c0a4; color: #0c352c; } .t-32 { background: #2f9e8f; color: #f2fbf8; } .t-64 { background: #f4d35e; color: #5c4a12; } .t-128 { background: #f2b13e; color: #4a3407; } .t-256 { background: #ef8b2d; color: #fff; } .t-512 { background: #e9642c; color: #fff; } .t-1024 { background: #d63b2f; color: #fff; } .t-2048 { background: linear-gradient(135deg, #ffd95e, #f2b13e); color: #4a3403; box-shadow: inset 0 1px 0 rgba(255,255,255,.4), 0 0 22px rgba(244,197,66,.55); } .t-more { background: #4a3a8f; color: #fff; } .overlay { position: absolute; inset: 0; z-index: 5; border-radius: inherit; display: grid; place-items: center; background: rgba(8,20,26,.78); backdrop-filter: blur(2px); opacity: 0; pointer-events: none; transition: opacity .35s ease; } .overlay.show { opacity: 1; pointer-events: auto; } .overlay-card { text-align: center; padding: 20px; } .overlay.show .overlay-card { animation: cardIn .45s cubic-bezier(.34,1.56,.64,1) both; } @keyframes cardIn { from { transform: translateY(18px) scale(.92); opacity: 0; } } .overlay-card h2 { font-family: var(--font-display); font-size: 34px; margin: 0 0 6px; } .overlay-card p { color: var(--muted); margin: 0 0 18px; font-size: 14.5px; } .btn { font-family: var(--font-display); font-weight: 800; font-size: 15.5px; color: #3d2c05; border: 0; border-radius: 12px; padding: 11px 20px; cursor: pointer; background: linear-gradient(180deg, #ffd95e, #f2b13e); box-shadow: 0 6px 0 #a8711c, 0 16px 26px -12px rgba(242,177,62,.55); transition: transform .12s ease, box-shadow .12s ease, filter .12s ease; } .btn:hover { transform: translateY(-2px); box-shadow: 0 8px 0 #a8711c, 0 20px 30px -12px rgba(242,177,62,.6); filter: brightness(1.04); } .btn:active { transform: translateY(2px); box-shadow: 0 3px 0 #a8711c, 0 8px 16px -8px rgba(242,177,62,.4); } .btn:focus-visible { outline: 3px solid #7fd8cf; outline-offset: 3px; } .btn-ghost { background: transparent; color: #cfe6e1; border: 2px solid #2c4a54; box-shadow: none; } .btn-ghost:hover { border-color: #4a707d; background: rgba(127,216,207,.08); box-shadow: none; filter: none; } .btn-ghost:active { box-shadow: none; } .foot { display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; margin-top: 24px; user-select: none; } .hint { margin: 0; color: var(--muted); font-size: 13.5px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; } .sep { opacity: .5; margin: 0 5px; } kbd { font-family: var(--font-body); font-size: 11.5px; color: #cfe6e1; background: #122730; border: 1px solid #27454f; border-bottom-width: 3px; border-radius: 7px; padding: 3px 7px; } .toast { position: fixed; left: 50%; bottom: 26px; z-index: 50; transform: translate(-50%, 20px); background: linear-gradient(135deg, #ffd95e, #f2b13e); color: #4a3403; font-family: var(--font-display); font-weight: 800; font-size: 17px; padding: 12px 24px; border-radius: 14px; box-shadow: 0 18px 44px -12px rgba(242,177,62,.6); opacity: 0; pointer-events: none; transition: opacity .3s ease, transform .3s cubic-bezier(.34,1.56,.64,1); } .toast.show { opacity: 1; transform: translate(-50%, 0); } @media (max-width: 480px) { .lt { width: 42px; height: 42px; font-size: 22px; } .chip { min-width: 80px; padding: 7px 12px 9px; } .chip-val { font-size: 21px; } } @media (prefers-reduced-motion: reduce) { .g1, .g2, .lt { animation: none; } .tile { transition-duration: 1ms; } } </style> </head> <body> <div class="bg" aria-hidden="true"> <div class="glow g1"></div> <div class="glow g2"></div> <div class="dots"></div> </div> <main class="wrap"> <header class="top"> <div class="brand"> <h1 class="logo" aria-label="2048"> <span class="lt lt2">2</span><span class="lt lt0">0</span><span class="lt lt4">4</span><span class="lt lt8">8</span> </h1> <p class="tag">Slide, merge, and build the big one — <b>2048</b>.</p> </div> <div class="scores"> <div class="chip"><span class="chip-label">Score</span><span id="score" class="chip-val" aria-live="polite">0</span></div> <div class="chip"><span class="chip-label">Best</span><span id="best" class="chip-val">0</span></div> </div> </header> <section class="board-zone"> <div id="board" class="board" aria-label="2048 board"> <div class="cells" id="cells"></div> <div class="tiles" id="tiles"></div> <div id="overlay" class="overlay" aria-hidden="true"> <div class="overlay-card"> <h2>Game over</h2> <p id="over-sub"></p> <button id="again" class="btn">Try again</button> </div> </div> </div> </section> <footer class="foot"> <p class="hint"> <kbd>←</kbd><kbd>↑</kbd><kbd>↓</kbd><kbd>→</kbd> move tiles <span class="sep">·</span> <kbd>R</kbd> new game <span class="sep">·</span> swipe works too </p> <button id="new" class="btn btn-ghost">New game</button> </footer> </main> <div id="toast" class="toast" role="status">2048 — incredible! Keep going…</div> <noscript><p style="text-align:center">2048 needs JavaScript to run.</p></noscript> <script id="game"> function createGame() { var START = [ [16, 0, 4, 8], [2, 0, 0, 0], [0, 32, 4, 32], [4, 0, 0, 0] ]; var board = START.map(function (row) { return row.slice(); }); var score = 0; var over = false; // Rule 1: place a 2 in each of the first two empty cells in reading order. (function seed() { var placed = 0; for (var r = 0; r < 4 && placed < 2; r++) { for (var c = 0; c < 4 && placed < 2; c++) { if (board[r][c] === 0) { board[r][c] = 2; placed++; } } } })(); // Read a 4-cell line with index 0 at the edge tiles move toward. function pull(dir, anchor) { var line = [0, 0, 0, 0]; for (var i = 0; i < 4; i++) { var r, c; if (dir === 'up' || dir === 'down') { c = anchor; r = (dir === 'up') ? i : 3 - i; } else { r = anchor; c = (dir === 'left') ? i : 3 - i; } line[i] = board[r][c]; } return line; } function push(dir, anchor, line) { for (var i = 0; i < 4; i++) { var r, c; if (dir === 'up' || dir === 'down') { c = anchor; r = (dir === 'up') ? i : 3 - i; } else { r = anchor; c = (dir === 'left') ? i : 3 - i; } board[r][c] = line[i]; } } // Rule 2: slide toward index 0; equal neighbors merge once, nearest the edge first. function slide(line) { var slots = []; for (var i = 0; i < 4; i++) { var v = line[i]; if (v === 0) continue; var last = slots[slots.length - 1]; if (last && !last.merged && last.value === v) { last.value = v * 2; last.merged = true; } else { slots.push({ value: v, merged: false }); } } var out = [0, 0, 0, 0]; var gained = 0; for (var k = 0; k < slots.length; k++) { out[k] = slots[k].value; if (slots[k].merged) gained += slots[k].value; } return { line: out, gained: gained }; } // Rule 4: a 2 goes in the first empty cell in reading order. function spawnOne() { for (var r = 0; r < 4; r++) { for (var c = 0; c < 4; c++) { if (board[r][c] === 0) { board[r][c] = 2; return; } } } } // Rule 5: game is over when no move could change the board. function anyMovePossible() { for (var r = 0; r < 4; r++) { for (var c = 0; c < 4; c++) { var v = board[r][c]; if (v === 0) return true; if (r < 3 && board[r + 1][c] === v) return true; if (c < 3 && board[r][c + 1] === v) return true; } } return false; } function move(dir) { if (over) return; if (dir !== 'up' && dir !== 'down' && dir !== 'left' && dir !== 'right') return; var before = board.map(function (row) { return row.slice(); }); var gained = 0; for (var i = 0; i < 4; i++) { var res = slide(pull(dir, i)); gained += res.gained; push(dir, i, res.line); } var changed = false; for (var r = 0; r < 4 && !changed; r++) { for (var c = 0; c < 4; c++) { if (board[r][c] !== before[r][c]) { changed = true; break; } } } if (!changed) return; score += gained; // Rule 3 spawnOne(); // Rule 4 if (!anyMovePossible()) over = true; } function state() { return { board: board.map(function (row) { return row.slice(); }), score: score, over: over }; } return { move: move, state: state }; } </script> <script> (function () { 'use strict'; const boardEl = document.getElementById('board'); const cellsEl = document.getElementById('cells'); const layerEl = document.getElementById('tiles'); const scoreEl = document.getElementById('score'); const bestEl = document.getElementById('best'); const overlay = document.getElementById('overlay'); const overSub = document.getElementById('over-sub'); const toastEl = document.getElementById('toast'); const SLIDE = 120; for (let i = 0; i < 16; i++) { const d = document.createElement('div'); d.className = 'cell'; cellsEl.appendChild(d); } let best = 0; try { best = parseInt(localStorage.getItem('best2048'), 10) || 0; } catch (e) {} bestEl.textContent = best; let game = null; let tilesAt = emptyGrid(); let busy = false; let wonShown = false; let toastTimer = null; function emptyGrid() { return [0, 1, 2, 3].map(() => [0, 1, 2, 3].map(() => null)); } function valueClass(v) { return v <= 2048 ? 't-' + v : 't-more'; } function sizeClass(v) { const n = String(v).length; if (n >= 5) return 'f4'; if (n === 4) return 'f3'; if (n === 3) return 'f2'; return 'f1'; } function makeTile(v, r, c) { const el = document.createElement('div'); el.className = 'tile ' + valueClass(v) + ' ' + sizeClass(v); el.textContent = v; el.style.setProperty('--r', r); el.style.setProperty('--c', c); return el; } function setPos(el, r, c) { if (!el) return; el.style.setProperty('--r', r); el.style.setProperty('--c', c); } function sameBoard(a, b) { for (let r = 0; r < 4; r++) for (let c = 0; c < 4; c++) if (a[r][c] !== b[r][c]) return false; return true; } // Mirror of the game's slide, used only for animation: // which source cells each resulting tile came from. function trace(board, dir) { const result = []; for (let i = 0; i < 4; i++) { const cells = [0, 1, 2, 3].map(k => { let r, c; if (dir === 'left') { r = i; c = k; } else if (dir === 'right') { r = i; c = 3 - k; } else if (dir === 'up') { r = k; c = i; } else { r = 3 - k; c = i; } return [r, c]; }); const slots = []; for (const [r, c] of cells) { const v = board[r][c]; if (!v) continue; const last = slots[slots.length - 1]; if (last && !last.merged && last.value === v) { last.merged = true; last.value *= 2; last.froms.push([r, c]); } else { slots.push({ value: v, merged: false, froms: [[r, c]] }); } } slots.forEach((s, k) => result.push({ to: cells[k], value: s.value, merged: s.merged, froms: s.froms })); } return result; } function playMove(traced, after) { const newAt = emptyGrid(); for (const m of traced) { const [r, c] = m.to; const els = m.froms.map(([fr, fc]) => tilesAt[fr][fc]).filter(Boolean); els.forEach(el => setPos(el, r, c)); if (m.merged) { const merged = makeTile(m.value, r, c); merged.classList.add('merge'); merged.style.animationDelay = SLIDE + 'ms'; layerEl.appendChild(merged); newAt[r][c] = merged; setTimeout(() => { els.forEach(el => { if (el.parentNode) el.parentNode.removeChild(el); }); }, SLIDE + 15); } else { newAt[r][c] = els[0] || null; } } // The one cell that gained a value without a tile landing there is the spawn. for (let r = 0; r < 4; r++) { for (let c = 0; c < 4; c++) { const v = after[r][c]; if (v && !newAt[r][c]) { const el = makeTile(v, r, c); el.classList.add('spawn'); el.style.animationDelay = SLIDE + 'ms'; layerEl.appendChild(el); newAt[r][c] = el; } } } tilesAt = newAt; } function paintScore(v, bump) { scoreEl.textContent = v; if (bump) { scoreEl.classList.remove('bump'); void scoreEl.offsetWidth; scoreEl.classList.add('bump'); } } function floatGain(amount) { const chip = scoreEl.parentElement; const s = document.createElement('span'); s.className = 'gain'; s.textContent = '+' + amount; chip.appendChild(s); setTimeout(() => s.remove(), 950); } function nudge(dir) { const x = dir === 'left' ? '-12px' : dir === 'right' ? '12px' : '0px'; const y = dir === 'up' ? '-12px' : dir === 'down' ? '12px' : '0px'; boardEl.style.setProperty('--nx', x); boardEl.style.setProperty('--ny', y); boardEl.classList.remove('nudge'); void boardEl.offsetWidth; boardEl.classList.add('nudge'); } function showToast() { toastEl.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(() => toastEl.classList.remove('show'), 2600); } function showOverlay(score) { overSub.textContent = (score > 0 && score >= best) ? 'Final score ' + score + ' — new best!' : 'Final score ' + score + ' · best ' + best; overlay.classList.add('show'); overlay.setAttribute('aria-hidden', 'false'); } function newGame() { game = createGame(); wonShown = false; busy = false; overlay.classList.remove('show'); overlay.setAttribute('aria-hidden', 'true'); layerEl.innerHTML = ''; tilesAt = emptyGrid(); const st = game.state(); let k = 0; for (let r = 0; r < 4; r++) for (let c = 0; c < 4; c++) { const v = st.board[r][c]; if (!v) continue; const el = makeTile(v, r, c); el.classList.add('spawn'); el.style.animationDelay = (k * 35) + 'ms'; layerEl.appendChild(el); tilesAt[r][c] = el; k++; } paintScore(st.score, false); } function handle(dir) { if (!game || busy) return; const before = game.state(); if (before.over) return; const traced = trace(before.board, dir); game.move(dir); const after = game.state(); if (sameBoard(before.board, after.board)) { nudge(dir); return; } busy = true; playMove(traced, after.board); const g = game; setTimeout(() => { if (game === g) busy = false; }, SLIDE + 40); const gained = after.score - before.score; if (gained > 0) floatGain(gained); paintScore(after.score, true); if (after.score > best) { best = after.score; bestEl.textContent = best; try { localStorage.setItem('best2048', String(best)); } catch (e) {} } if (!wonShown && after.board.some(row => row.some(v => v >= 2048))) { wonShown = true; setTimeout(() => showToast(), SLIDE + 200); } if (after.over) { setTimeout(() => { if (game === g) showOverlay(g.state().score); }, SLIDE + 500); } } const KEYS = { ArrowLeft: 'left', ArrowRight: 'right', ArrowUp: 'up', ArrowDown: 'down', a: 'left', d: 'right', w: 'up', s: 'down', A: 'left', D: 'right', W: 'up', S: 'down' }; window.addEventListener('keydown', e => { if (e.metaKey || e.ctrlKey || e.altKey) return; const dir = KEYS[e.key]; if (dir) { e.preventDefault(); handle(dir); } else if (e.key === 'r' || e.key === 'R') newGame(); }); let touchStart = null; boardEl.addEventListener('touchstart', e => { const t = e.changedTouches[0]; touchStart = { x: t.clientX, y: t.clientY }; }, { passive: true }); boardEl.addEventListener('touchend', e => { if (!touchStart) return; const t = e.changedTouches[0]; const dx = t.clientX - touchStart.x; const dy = t.clientY - touchStart.y; touchStart = null; if (Math.max(Math.abs(dx), Math.abs(dy)) < 24) return; if (Math.abs(dx) > Math.abs(dy)) handle(dx > 0 ? 'right' : 'left'); else handle(dy > 0 ? 'down' : 'up'); }, { passive: true }); document.getElementById('new').addEventListener('click', newGame); document.getElementById('again').addEventListener('click', newGame); newGame(); })(); </script> </body> </html> ```Build SnakeFailed
37,624 tokens: 497 read, 37,127 written · 27 min 47 s · 22.3 tok/s
Build Snake as one web page on a 10 by 10 grid, playable with the arrow keys (one key press, one step), with the grid drawn on the page. In <script id="game">, define createGame() returning an object with step(dir) and state(). dir is "up", "down", "left" or "right". state() returns {snake, food, score, over}: snake is a list of [x, y] cells, head first; food is [x, y]; score is a number; over is a boolean. x is the column (0 to 9, left to right), y the row (0 to 9, top to bottom); up is y - 1. The rules, exactly: 1. The game starts with snake [[3, 3], [4, 3], [5, 3]], moving left, food at [0, 2], score 0. 2. step(dir): if dir is the opposite of the current direction it is ignored and the snake keeps going; otherwise dir becomes the current direction. The head moves one cell that way. 3. If the new head cell holds the food, the snake grows: its tail stays. Otherwise the tail cell is freed first. 4. If the new head is off the grid or on a cell of the snake (after rule 3), the game is over: over becomes true and nothing else changes. Once over, step does nothing. 5. Eating adds 1 to the score, and the next food goes to the first empty cell in reading order (row by row from the top, left to right) after the new head's cell, wrapping from [9, 9] to [0, 0]. Output one complete HTML document and nothing else, with all CSS and JavaScript inline and nothing loaded from another site. Put the game logic, and only it, in <script id="game">: it must not touch document, window or timers, because the site runs that script on its own and plays the game through createGame(). Wire the page in another <script>.Open the page on its own. It runs in a sandbox: no network, no access to this site.
Page source
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Snake · 10 × 10</title> <style> :root{ --bg:#0a110d; --line:#223b2d; --line-soft:#182b20; --cell:#12241b; --cell-alt:#0f1f17; --gridline:#0b150f; --grass:#3fe08d; --amber:#ffb454; --cream:#e9f5ec; --dim:#8fae9d; --display: Impact, "Arial Black", "Franklin Gothic Medium", "Trebuchet MS", sans-serif; --body: "Trebuchet MS", "Segoe UI", Verdana, system-ui, sans-serif; --mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, "Courier New", monospace; } *{ box-sizing:border-box; margin:0; padding:0; } html{ color-scheme:dark; } ::selection{ background:rgba(63,224,141,.3); } body{ min-height:100vh; font-family:var(--body); color:var(--cream); padding:28px clamp(16px,4vw,44px) 40px; overflow-x:hidden; background: radial-gradient(1000px 620px at 10% -12%, rgba(63,224,141,.10), transparent 60%), radial-gradient(820px 520px at 108% 12%, rgba(255,180,84,.07), transparent 55%), radial-gradient(900px 700px at 50% 118%, rgba(63,224,141,.07), transparent 60%), var(--bg); } body::before{ content:""; position:fixed; inset:-60px; z-index:-1; background-image: linear-gradient(rgba(122,224,170,.05) 1px, transparent 1px), linear-gradient(90deg, rgba(122,224,170,.05) 1px, transparent 1px); background-size:34px 34px; animation:drift 26s linear infinite; } body::after{ content:""; position:fixed; inset:0; z-index:-1; pointer-events:none; background: radial-gradient(120% 90% at 50% 40%, transparent 55%, rgba(2,6,4,.55) 100%); } @keyframes drift{ to{ transform:translate3d(34px,34px,0); } } /* ---------- header ---------- */ .top{ display:flex; align-items:flex-end; justify-content:space-between; gap:20px; flex-wrap:wrap; max-width:1080px; margin:0 auto 26px; } .brand{ display:flex; gap:18px; align-items:center; } .mark{ width:58px; height:58px; flex:none; filter:drop-shadow(0 0 14px rgba(63,224,141,.35)); animation:markbob 4s ease-in-out infinite; } @keyframes markbob{ 50%{ transform:translateY(-4px) rotate(-3deg); } } .overline{ font-family:var(--mono); font-size:11px; letter-spacing:.32em; text-transform:uppercase; color:var(--dim); margin-bottom:8px; } h1{ font-family:var(--display); font-weight:400; font-size:clamp(46px,7vw,76px); line-height:.85; letter-spacing:.015em; color:#f1faf4; text-shadow:.05em .05em 0 #14402b, .1em .1em 0 rgba(14,46,30,.6), 0 0 34px rgba(63,224,141,.28); } h1 .dot{ color:var(--amber); text-shadow:.05em .05em 0 #52350f, 0 0 22px rgba(255,180,84,.55); } .badges{ display:flex; flex-direction:column; gap:8px; align-items:flex-end; } .chip{ font-family:var(--mono); font-size:11px; letter-spacing:.18em; color:#bfe8cf; border:1px solid var(--line); background:rgba(63,224,141,.05); padding:7px 12px; border-radius:4px; } .chip:last-child{ border-color:rgba(255,180,84,.35); color:#ffd9a3; background:rgba(255,180,84,.06); } /* ---------- layout ---------- */ .layout{ display:grid; grid-template-columns:minmax(0,600px) minmax(250px,340px); gap:26px; max-width:1080px; margin:0 auto; align-items:start; } @media (max-width:920px){ .layout{ grid-template-columns:minmax(0,600px); justify-content:center; } .panel{ max-width:600px; width:100%; margin:0 auto; } .badges{ flex-direction:row; align-items:center; } } .card{ background:linear-gradient(180deg,#111d15,#0d1710); border:1px solid var(--line); border-radius:16px; padding:16px; position:relative; box-shadow:0 24px 60px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.04); } .board-top{ display:flex; justify-content:space-between; align-items:center; flex-wrap:wrap; gap:8px; padding:2px 4px 12px; } .status{ display:flex; align-items:center; gap:9px; font-family:var(--mono); font-size:11.5px; letter-spacing:.22em; text-transform:uppercase; color:#9fe8c2; } .dot-live{ width:9px; height:9px; border-radius:50%; background:var(--grass); box-shadow:0 0 10px var(--grass); animation:blink 1.4s ease-in-out infinite; } .status.is-over{ color:#ff9d8a; } .status.is-over .dot-live{ background:#ff6a4f; box-shadow:0 0 10px #ff6a4f; animation:none; } @keyframes blink{ 50%{ opacity:.35; transform:scale(.8); } } .hud-mini{ font-family:var(--mono); font-size:12px; color:var(--dim); letter-spacing:.08em; } .hud-mini b{ color:var(--amber); font-size:13px; } /* ---------- the grid ---------- */ .gridwrap{ position:relative; } .grid{ display:grid; grid-template-columns:repeat(10,1fr); grid-template-rows:repeat(10,1fr); gap:3px; padding:6px; position:relative; aspect-ratio:1; background:var(--gridline); border:1px solid var(--line-soft); border-radius:12px; transition:filter .5s; } .grid.dead{ filter:saturate(.45) brightness(.82); animation:shake .55s cubic-bezier(.36,.07,.19,.97); } @keyframes shake{ 10%{ transform:translate(-3px,1px); } 30%{ transform:translate(3px,-2px); } 50%{ transform:translate(-3px,-1px); } 70%{ transform:translate(2px,2px); } 90%{ transform:translate(-1px,1px); } } .cell{ position:relative; background:var(--cell); border-radius:5px; transition:background .18s ease; } .cell.alt{ background:var(--cell-alt); } @media (hover:hover){ .cell:hover{ background:#173022; } } .cell.food{ background:#1c2517; } .cell.food::before{ content:""; position:absolute; inset:17%; border-radius:50%; background:radial-gradient(circle at 34% 30%, #ffc2ad, #ff6a4f 48%, #d43a26 78%, #9c2416); box-shadow:0 0 12px rgba(255,106,79,.5), 0 0 26px rgba(255,106,79,.22); animation:bob 1.1s ease-in-out infinite; } .cell.food::after{ content:""; position:absolute; top:11%; left:55%; width:22%; height:13%; background:#8fe06e; border-radius:0 70% 0 70%; transform:rotate(-30deg); box-shadow:0 0 8px rgba(143,224,110,.45); animation:swaying 1.1s ease-in-out infinite; } @keyframes bob{ 50%{ transform:scale(1.14) translateY(-5%); } } @keyframes swaying{ 50%{ transform:rotate(-22deg) translate(1px,-1px); } } .cell.body::before{ content:""; position:absolute; inset:9%; border-radius:32%; background:linear-gradient(145deg, #37cf85, #1d9a5e 60%, #127144); box-shadow:inset 0 -3px 5px rgba(0,40,22,.4), 0 0 6px rgba(47,182,106,.25); opacity:max(.3, calc(1 - var(--i,0) * .03)); transform:scale(calc(1 - var(--i,0) * .012)); } .cell.head{ z-index:2; } .cell.head::before{ content:""; position:absolute; inset:3%; border-radius:36%; background:linear-gradient(150deg, #8cf7bb, #34d489 55%, #17a363); box-shadow:0 0 14px rgba(63,224,141,.55), 0 0 34px rgba(63,224,141,.2), inset 0 -4px 8px rgba(0,50,26,.4); animation:breathe 1.5s ease-in-out infinite; } @keyframes breathe{ 50%{ transform:scale(1.05); } } .face{ position:absolute; inset:0; z-index:3; pointer-events:none; transition:transform .13s ease; } .cell.head.dir-down .face{ transform:rotate(90deg); } .cell.head.dir-left .face{ transform:rotate(180deg); } .cell.head.dir-up .face{ transform:rotate(270deg); } .eyes{ position:absolute; right:19%; top:25%; width:26%; height:50%; display:none; } .cell.head .eyes{ display:block; } .eyes i{ position:absolute; right:0; width:64%; aspect-ratio:1/1; border-radius:50%; background:#052617; box-shadow:inset 0 -1px 1px rgba(255,255,255,.25); transition:all .2s; } .eyes i:first-child{ top:0; } .eyes i:last-child{ bottom:0; } .grid.dead .eyes i{ border-radius:3px; aspect-ratio:auto; width:100%; height:24%; } /* ---------- overlay ---------- */ .overlay{ position:absolute; inset:0; z-index:10; display:grid; place-items:center; border-radius:12px; background:radial-gradient(90% 90% at 50% 45%, rgba(8,15,11,.82), rgba(5,10,8,.94)); opacity:0; pointer-events:none; transition:opacity .35s ease; } .overlay.show{ opacity:1; pointer-events:auto; } .over-box{ text-align:center; padding:20px; } .overlay.show .over-box{ animation:popin .45s cubic-bezier(.2,1.4,.4,1); } @keyframes popin{ from{ transform:scale(.8); opacity:0; } } .over-kicker{ font-family:var(--mono); font-size:11px; letter-spacing:.3em; text-transform:uppercase; color:var(--dim); margin-bottom:10px; } .over-box h2{ font-family:var(--display); font-weight:400; font-size:clamp(34px,5vw,52px); letter-spacing:.02em; color:#ff8a70; margin-bottom:8px; text-shadow:.06em .06em 0 #4a160c, 0 0 30px rgba(255,106,79,.4); } .over-score{ font-family:var(--mono); color:var(--dim); font-size:14px; margin-bottom:18px; } .over-score b{ color:var(--amber); font-size:20px; } .over-hint{ margin-top:14px; font-size:12px; color:var(--dim); } kbd{ font-family:var(--mono); font-size:11px; background:#15251c; border:1px solid #2c4736; border-bottom-width:3px; border-radius:5px; padding:2px 7px; color:#cfe9da; } .plus{ position:absolute; z-index:15; transform:translate(-50%,-50%); font-family:var(--mono); font-weight:700; font-size:15px; color:var(--amber); pointer-events:none; text-shadow:0 0 10px rgba(255,180,84,.9), 0 2px 3px rgba(0,0,0,.6); animation:rise .85s ease-out forwards; } @keyframes rise{ 0%{ opacity:0; margin-top:6px; } 18%{ opacity:1; } 100%{ opacity:0; margin-top:-30px; } } /* ---------- dpad ---------- */ .dpad{ display:grid; grid-template-columns:repeat(3,54px); grid-auto-rows:46px; gap:9px; justify-content:center; margin-top:16px; user-select:none; } .key{ font-family:var(--mono); font-size:17px; color:#a9dcc0; cursor:pointer; background:linear-gradient(180deg,#15271d,#0e1c14); border:1px solid #27452f; border-bottom-width:4px; border-radius:9px; transition:transform .08s, box-shadow .15s, color .15s, border-color .15s, background .15s; } .key:hover{ color:#e5fbef; border-color:rgba(63,224,141,.4); box-shadow:0 0 14px rgba(63,224,141,.18); } .key.lit, .key:active{ transform:translateY(3px); border-bottom-width:1px; color:#8cf7bb; background:linear-gradient(180deg,#1d3a2b,#14281d); border-color:#3fe08d; box-shadow:inset 0 0 18px rgba(63,224,141,.3); } .key:focus-visible{ outline:2px solid var(--amber); outline-offset:2px; } .key[data-dir="up"]{ grid-column:2; grid-row:1; } .key[data-dir="left"]{ grid-column:1; grid-row:2; } .key[data-dir="down"]{ grid-column:2; grid-row:2; } .key[data-dir="right"]{ grid-column:3; grid-row:2; } /* ---------- side panel ---------- */ .panel{ background:linear-gradient(180deg,#101c15,#0c150f); border:1px solid var(--line); border-radius:16px; padding:20px; display:flex; flex-direction:column; gap:16px; box-shadow:0 24px 60px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.04); } .stat-hero{ background:radial-gradient(120% 140% at 20% 0%, rgba(63,224,141,.10), transparent 60%), #0d1a12; border:1px solid var(--line); border-radius:12px; padding:14px 18px 12px; } .label{ display:block; font-family:var(--mono); font-size:10.5px; letter-spacing:.3em; text-transform:uppercase; color:var(--dim); margin-bottom:4px; } .value{ display:inline-block; font-family:var(--mono); font-weight:700; font-size:52px; line-height:1; color:#eafff3; text-shadow:0 0 24px rgba(63,224,141,.35); } .value.pop{ animation:scorepop .45s ease; } @keyframes scorepop{ 30%{ transform:scale(1.28); color:var(--amber); text-shadow:0 0 30px rgba(255,180,84,.7); } } .mini-stats{ display:grid; grid-template-columns:1fr 1fr; gap:10px; } .mini-stats .stat{ background:#0d1a12; border:1px solid var(--line-soft); border-radius:10px; padding:10px 14px; } .mid{ font-family:var(--mono); font-size:22px; font-weight:700; color:#c9f5dd; } .rule{ border:none; border-top:1px dashed #24402f; } .how-title{ font-family:var(--mono); font-size:11px; letter-spacing:.28em; text-transform:uppercase; color:#7fae94; } .how{ list-style:none; display:flex; flex-direction:column; gap:10px; font-size:13.5px; color:#b9d4c4; line-height:1.45; } .how li{ padding-left:16px; position:relative; } .how li::before{ content:"▸"; position:absolute; left:0; color:var(--grass); } .kbdmini{ font-family:var(--mono); font-size:11px; color:#9fe8c2; background:#132319; border:1px solid #2c4736; border-radius:4px; padding:1px 6px; } .apple-dot{ display:inline-block; width:10px; height:10px; border-radius:50%; vertical-align:-1px; background:radial-gradient(circle at 34% 30%, #ffc2ad, #ff6a4f 60%, #a52a1c); box-shadow:0 0 8px rgba(255,106,79,.6); } .btn{ font-family:var(--body); font-weight:700; font-size:15px; letter-spacing:.03em; border:none; cursor:pointer; border-radius:10px; padding:12px 22px; color:#05261a; background:linear-gradient(180deg,#58e99c,#22b371); box-shadow:0 5px 0 #0d5c37, 0 12px 24px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.4); transition:transform .12s ease, box-shadow .12s ease, filter .2s; } .btn:hover{ transform:translateY(-2px); filter:brightness(1.05); box-shadow:0 7px 0 #0d5c37, 0 16px 30px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.4); } .btn:active{ transform:translateY(3px); box-shadow:0 2px 0 #0d5c37, 0 6px 14px rgba(0,0,0,.35); } .btn:focus-visible{ outline:2px solid var(--amber); outline-offset:3px; } .btn.ghost{ background:transparent; color:#9fe8c2; box-shadow:inset 0 0 0 1.5px #2c4a38; } .btn.ghost:hover{ color:#c9f5dd; transform:translateY(-1px); box-shadow:inset 0 0 0 1.5px #3fe08d, 0 0 18px rgba(63,224,141,.15); } .btn.ghost:active{ transform:translateY(1px); } #restartBtn{ width:100%; } .foot{ max-width:1080px; margin:26px auto 0; text-align:center; color:#5f7a6b; font-size:12.5px; font-family:var(--mono); letter-spacing:.04em; } .foot code{ color:#8fd9b3; } @media (prefers-reduced-motion: reduce){ *, *::before, *::after{ animation-duration:.01ms !important; animation-iteration-count:1 !important; transition-duration:.01ms !important; } } </style> </head> <body> <header class="top"> <div class="brand"> <svg class="mark" viewBox="0 0 64 64" aria-hidden="true"> <defs> <linearGradient id="sg" x1="0" y1="0" x2="1" y2="1"> <stop offset="0" stop-color="#58e99c"/><stop offset="1" stop-color="#157a4a"/> </linearGradient> </defs> <path d="M42 12 C42 30 18 30 18 42 C18 54 46 54 46 42 C46 33 30 32 26 38" fill="none" stroke="url(#sg)" stroke-width="8" stroke-linecap="round"/> <circle cx="42" cy="12" r="8" fill="#5ce99d"/> <circle cx="44.5" cy="9.5" r="1.8" fill="#06281a"/> <path d="M49 7 L56 3 M49 7 L57 9" stroke="#ff6a4f" stroke-width="2" stroke-linecap="round"/> </svg> <div> <p class="overline">Classic arcade · ten by ten · one press, one step</p> <h1>SNAKE<span class="dot">.</span></h1> </div> </div> <div class="badges"> <span class="chip">10 × 10 GRID</span> <span class="chip">ARROWS ONLY</span> </div> </header> <main class="layout"> <section class="card" aria-label="Snake board"> <div class="board-top"> <span class="status" id="statusWrap" aria-live="polite"><i class="dot-live"></i><span id="status">Running</span></span> <span class="hud-mini">SCORE <b id="scoreMini">0</b> · STEP <b id="steps">0</b></span> </div> <div class="gridwrap"> <div id="board" class="grid" aria-label="10 by 10 game grid"></div> <div class="overlay" id="overlay"> <div class="over-box"> <p class="over-kicker">The wall won this time</p> <h2>GAME OVER</h2> <p class="over-score">final score <b id="finalScore">0</b></p> <button class="btn" id="againBtn">Play again ↻</button> <p class="over-hint">or press <kbd>Enter</kbd></p> </div> </div> </div> <div class="dpad" aria-label="On-screen controls"> <button class="key" data-dir="up" aria-label="Move up">↑</button> <button class="key" data-dir="left" aria-label="Move left">←</button> <button class="key" data-dir="down" aria-label="Move down">↓</button> <button class="key" data-dir="right" aria-label="Move right">→</button> </div> </section> <aside class="panel" aria-label="Scoreboard and rules"> <div class="stat-hero"> <span class="label">Score</span> <span class="value" id="score">0</span> </div> <div class="mini-stats"> <div class="stat"><span class="label">Best</span><span class="mid" id="best">0</span></div> <div class="stat"><span class="label">Length</span><span class="mid" id="len">3</span></div> </div> <hr class="rule"> <h3 class="how-title">Rules of the run</h3> <ul class="how"> <li><span class="kbdmini">↑ ↓ ← →</span> One key press moves the snake <b>one cell</b>.</li> <li>Steer into the <span class="apple-dot"></span> apple to grow — +1 point each.</li> <li>The next apple lands on the first free cell after your head, reading left → right, top → down.</li> <li>Hitting a wall or your own body ends the run.</li> <li>Pressing the opposite of your heading is ignored — you keep going straight.</li> </ul> <button class="btn ghost" id="restartBtn">⟳ Restart run</button> </aside> </main> <footer class="foot"> <p>Grid logic lives in <code><script id="game"></code> — pure functions, no DOM, no timers. The rest of this page just draws it.</p> </footer> <script id="game"> // Pure game logic. No DOM, no window, no timers. function createGame() { var SIZE = 10; var CELLS = SIZE * SIZE; var OPPOSITE = { up: "down", down: "up", left: "right", right: "left" }; var DELTA = { up: [0, -1], down: [0, 1], left: [-1, 0], right: [1, 0] }; var snake = [[3, 3], [4, 3], [5, 3]]; // head first var dir = "left"; var food = [0, 2]; var score = 0; var over = false; function placeFood(afterX, afterY) { // First empty cell in reading order after [afterX, afterY], wrapping at [9,9]. var start = afterY * SIZE + afterX; var taken = {}; for (var i = 0; i < snake.length; i++) { taken[snake[i][1] * SIZE + snake[i][0]] = true; } for (var k = 1; k <= CELLS; k++) { var cell = (start + k) % CELLS; if (!taken[cell]) { food = [cell % SIZE, Math.floor(cell / SIZE)]; return; } } // Board completely full: no empty cell exists; leave food as-is. } function step(input) { if (over) return; if (OPPOSITE[input] === undefined) return; // Opposite of the current direction is ignored; otherwise it becomes the heading. if (input !== OPPOSITE[dir]) dir = input; var d = DELTA[dir]; var head = snake[0]; var nx = head[0] + d[0]; var ny = head[1] + d[1]; var eating = (nx === food[0] && ny === food[1]); if (!eating) snake.pop(); // tail cell freed first var inGrid = nx >= 0 && nx < SIZE && ny >= 0 && ny < SIZE; var hitSelf = false; if (inGrid) { for (var i = 0; i < snake.length; i++) { if (snake[i][0] === nx && snake[i][1] === ny) { hitSelf = true; break; } } } if (!inGrid || hitSelf) { over = true; // nothing else changes return; } snake.unshift([nx, ny]); if (eating) { score += 1; placeFood(nx, ny); } } function state() { var s = []; for (var i = 0; i < snake.length; i++) s.push([snake[i][0], snake[i][1]]); return { snake: s, food: [food[0], food[1]], score: score, over: over }; } return { step: step, state: state }; } </script> <script> (function () { "use strict"; var SIZE = 10; var gridEl = document.getElementById("board"); var scoreEl = document.getElementById("score"); var scoreMiniEl = document.getElementById("scoreMini"); var bestEl = document.getElementById("best"); var lenEl = document.getElementById("len"); var stepsEl = document.getElementById("steps"); var statusEl = document.getElementById("status"); var statusWrap = document.getElementById("statusWrap"); var overlay = document.getElementById("overlay"); var finalScoreEl = document.getElementById("finalScore"); var cells = []; // Build the 10 x 10 board once. for (var y = 0; y < SIZE; y++) { for (var x = 0; x < SIZE; x++) { var c = document.createElement("div"); c.className = "cell" + (((x + y) % 2) ? " alt" : ""); var face = document.createElement("span"); face.className = "face"; face.innerHTML = '<span class="eyes"><i></i><i></i></span>'; c.appendChild(face); gridEl.appendChild(c); cells.push(c); } } function loadBest() { try { return Math.max(0, parseInt(localStorage.getItem("snake10.best"), 10) || 0); } catch (e) { return 0; } } function saveBest(v) { try { localStorage.setItem("snake10.best", String(v)); } catch (e) {} } var game = createGame(); var lastHead = null; var lastDir = "left"; // matches the starting heading var lastScore = 0; var steps = 0; var best = loadBest(); var DIR_KEYS = { ArrowUp: "up", ArrowDown: "down", ArrowLeft: "left", ArrowRight: "right" }; var keyBtns = {}; var keyList = document.querySelectorAll(".key[data-dir]"); for (var i = 0; i < keyList.length; i++) keyBtns[keyList[i].getAttribute("data-dir")] = keyList[i]; function flashKey(d) { var b = keyBtns[d]; if (!b) return; b.classList.add("lit"); clearTimeout(b._t); b._t = setTimeout(function () { b.classList.remove("lit"); }, 160); } function dirBetween(a, b) { var dx = b[0] - a[0], dy = b[1] - a[1]; if (dx === 1 && dy === 0) return "right"; if (dx === -1 && dy === 0) return "left"; if (dx === 0 && dy === 1) return "down"; if (dx === 0 && dy === -1) return "up"; return null; } function popEl(el) { el.classList.remove("pop"); void el.offsetWidth; // restart the animation el.classList.add("pop"); } function plusOne(px, py) { var c = cells[py * SIZE + px]; var el = document.createElement("span"); el.className = "plus"; el.textContent = "+1"; el.style.left = (c.offsetLeft + c.offsetWidth / 2) + "px"; el.style.top = (c.offsetTop + c.offsetHeight / 2) + "px"; gridEl.appendChild(el); el.addEventListener("animationend", function () { el.remove(); }); } function render() { var s = game.state(); var head = s.snake[0]; if (lastHead && (lastHead[0] !== head[0] || lastHead[1] !== head[1])) { var moved = dirBetween(lastHead, head); if (moved) lastDir = moved; } lastHead = [head[0], head[1]]; var occupied = new Int16Array(SIZE * SIZE).fill(-1); for (var i = 0; i < s.snake.length; i++) { occupied[s.snake[i][1] * SIZE + s.snake[i][0]] = i; } for (var y2 = 0; y2 < SIZE; y2++) { for (var x2 = 0; x2 < SIZE; x2++) { var idx = y2 * SIZE + x2; var cell = cells[idx]; var cls = "cell" + (((x2 + y2) % 2) ? " alt" : ""); var si = occupied[idx]; if (si === 0) cls += " head dir-" + lastDir; else if (si > 0) { cls += " body"; cell.style.setProperty("--i", si); } if (s.food && s.food[0] === x2 && s.food[1] === y2) cls += " food"; cell.className = cls; } } if (s.score !== lastScore) { if (s.score > lastScore) { popEl(scoreEl); plusOne(head[0], head[1]); } lastScore = s.score; } scoreEl.textContent = s.score; scoreMiniEl.textContent = s.score; lenEl.textContent = s.snake.length; stepsEl.textContent = steps; if (s.score > best) { best = s.score; saveBest(best); } bestEl.textContent = best; if (s.over && !gridEl.classList.contains("dead")) { gridEl.classList.add("dead"); statusWrap.classList.add("is-over"); finalScoreEl.textContent = s.score; overlay.classList.add("show"); } statusEl.textContent = s.over ? "Game over" : "Running"; } function play(d) { if (game.state().over) return; steps++; flashKey(d); game.step(d); render(); } function restart() { game = createGame(); lastHead = null; lastDir = "left"; lastScore = 0; steps = 0; gridEl.classList.remove("dead"); statusWrap.classList.remove("is-over"); overlay.classList.remove("show"); render(); } window.addEventListener("keydown", function (e) { var d = DIR_KEYS[e.key]; if (d) { e.preventDefault(); play(d); return; } if (game.state().over && (e.key === "Enter" || e.key === " " || e.key === "r" || e.key === "R")) { e.preventDefault(); restart(); } }); for (var k = 0; k < keyList.length; k++) { (function (btn) { btn.addEventListener("click", function () { play(btn.getAttribute("data-dir")); }); })(keyList[k]); } document.getElementById("againBtn").addEventListener("click", restart); document.getElementById("restartBtn").addEventListener("click", restart); bestEl.textContent = best; render(); })(); </script> </body> </html>
Writing code 3 of 3
Merge intervalsPassed
2,313 tokens: 200 read, 2,113 written · 1 min 33 s · 22.9 tok/s
Write a JavaScript function mergeIntervals(intervals). intervals is an array of [start, end] pairs of integers with start <= end, in any order, possibly empty. Return a new array of the merged intervals sorted by start: intervals that overlap or touch (one ends where the next starts) become one. Do not change the input array or its pairs. mergeIntervals([[8, 10], [1, 3], [2, 6], [6, 7]]) returns [[1, 7], [8, 10]]. Reply with one ```js code block holding plain JavaScript (no imports, no exports, no DOM, no timers). The site runs it with hidden tests.
```js function mergeIntervals(intervals) { if (!Array.isArray(intervals) || intervals.length === 0) { return []; } const sorted = intervals .map(([start, end]) => [start, end]) .sort((a, b) => a[0] - b[0] || a[1] - b[1]); const merged = []; let currentStart = sorted[0][0]; let currentEnd = sorted[0][1]; for (let i = 1; i < sorted.length; i++) { const start = sorted[i][0]; const end = sorted[i][1]; if (start <= currentEnd) { if (end > currentEnd) { currentEnd = end; } } else { merged.push([currentStart, currentEnd]); currentStart = start; currentEnd = end; } } merged.push([currentStart, currentEnd]); return merged; } ```An LRU cachePassed
1,097 tokens: 210 read, 887 written · 40 s · 22.8 tok/s
Write a JavaScript class LRUCache: a least-recently-used cache of integer keys and values. - new LRUCache(capacity): capacity is an integer of at least 1. - get(key) returns the value stored for key, or -1 if there is none. A get that finds the key makes it the most recently used. - put(key, value) stores the value (replacing any value already there) and makes key the most recently used. If that leaves more than capacity keys, the least recently used key is removed. get and put should take constant time on average. Reply with one ```js code block holding plain JavaScript (no imports, no exports, no DOM, no timers). The site runs it with hidden tests.
```js class LRUCache { constructor(capacity) { this.capacity = capacity; this.cache = new Map(); } get(key) { if (!this.cache.has(key)) { return -1; } const value = this.cache.get(key); // Move key to the end so it becomes the most recently used. this.cache.delete(key); this.cache.set(key, value); return value; } put(key, value) { if (this.cache.has(key)) { // Update value and move key to the end as most recently used. this.cache.delete(key); } else if (this.cache.size >= this.capacity) { // Remove the least recently used key, which is the first key in the Map. const leastRecentlyUsed = this.cache.keys().next().value; this.cache.delete(leastRecentlyUsed); } this.cache.set(key, value); } } ```Evaluate arithmeticPassed
22,663 tokens: 236 read, 22,427 written · 17 min 5 s · 21.9 tok/s
Write a JavaScript function evaluate(expr) that returns the value of an arithmetic expression given as a string. The expression has non-negative integers, the operators + - * /, parentheses, unary minus (as in -3, - -3, 2 * -3 or -(1 + 2)) and spaces anywhere between tokens. * and / bind tighter than + and -, and operators of the same kind group from the left. / is integer division that truncates toward zero: 7 / 2 is 3 and -7 / 2 is -3. Dividing by zero must throw an Error. Do not use eval or Function. evaluate("2 + 3 * (4 - 6) / 4") returns 1. Reply with one ```js code block holding plain JavaScript (no imports, no exports, no DOM, no timers). The site runs it with hidden tests.```js function evaluate(expr) { var s = String(expr); var pos = 0; var n = s.length; function skipSpaces() { while (pos < n) { var c = s[pos]; if (c === ' ' || c === '\t' || c === '\n' || c === '\r') { pos++; } else { break; } } } function isFiniteInteger(x) { return typeof x === 'number' && isFinite(x) && x === Math.floor(x); } function divide(a, b) { if (b === 0) { throw new Error('Division by zero'); } if (typeof BigInt === 'function' && isFiniteInteger(a) && isFiniteInteger(b)) { return Number(BigInt(a) / BigInt(b)); } var negative = (a < 0) !== (b < 0); var q = Math.floor(Math.abs(a) / Math.abs(b)); return (negative && q !== 0) ? -q : q; } function parseExpression() { var value = parseTerm(); for (;;) { skipSpaces(); var c = s[pos]; if (c === '+') { pos++; value += parseTerm(); } else if (c === '-') { pos++; value -= parseTerm(); } else { break; } } return value; } function parseTerm() { var value = parseFactor(); for (;;) { skipSpaces(); var c = s[pos]; if (c === '*') { pos++; value *= parseFactor(); } else if (c === '/') { pos++; value = divide(value, parseFactor()); } else { break; } } return value; } function parseFactor() { skipSpaces(); var negate = false; for (;;) { var c = s[pos]; if (c === '-') { negate = !negate; pos++; skipSpaces(); } else if (c === '+') { pos++; skipSpaces(); } else { break; } } var value = parsePrimary(); return negate ? -value : value; } function parsePrimary() { skipSpaces(); var c = s[pos]; if (c === '(') { pos++; var value = parseExpression(); skipSpaces(); if (s[pos] !== ')') { throw new Error('Expected closing parenthesis'); } pos++; return value; } if (c >= '0' && c <= '9') { var start = pos; while (pos < n) { var d = s[pos]; if (d >= '0' && d <= '9') { pos++; } else { break; } } return Number(s.substring(start, pos)); } throw new Error('Unexpected character'); } var result = parseExpression(); skipSpaces(); if (pos !== n) { throw new Error('Unexpected character'); } return result; } ```
Fixing code 2 of 2
Fix the bookings modulePassed
1,526 tokens: 708 read, 818 written · 35 s · 25.3 tok/s
This JavaScript module has one bug. Guests are charged the weekend rate for Sunday nights and the weekday rate for Friday nights. Find it and fix it without changing anything else. ```js // Bookings for one holiday flat. A booking is { guest, checkIn, checkOut } with dates as "YYYY-MM-DD". // A guest sleeps the nights from checkIn up to, but not including, checkOut: checkOut is the day they leave, // and another guest may check in that same day. function dayNumber(date) { const [y, m, d] = date.split('-').map(Number); return Date.UTC(y, m - 1, d) / 86400000; } function toDate(day) { return new Date(day * 86400000).toISOString().slice(0, 10); } function nights(booking) { return dayNumber(booking.checkOut) - dayNumber(booking.checkIn); } function overlaps(a, b) { return dayNumber(a.checkIn) < dayNumber(b.checkOut) && dayNumber(b.checkIn) < dayNumber(a.checkOut); } function canBook(bookings, request) { if (nights(request) < 1) return false; for (const booking of bookings) { if (overlaps(booking, request)) return false; } return true; } function isBooked(bookings, day) { return bookings.some((b) => dayNumber(b.checkIn) <= day && day < dayNumber(b.checkOut)); } // The nights from `from` up to, but not including, `to` that someone has booked. function bookedNights(bookings, from, to) { let count = 0; for (let day = dayNumber(from); day < dayNumber(to); day++) { if (isBooked(bookings, day)) count++; } return count; } // The first night on or after `from` that nobody has booked, as "YYYY-MM-DD". function firstFreeNight(bookings, from) { let day = dayNumber(from); while (isBooked(bookings, day)) day++; return toDate(day); } // The price in cents: Friday and Saturday nights at rates.weekend, every other night at rates.weekday. function price(booking, rates) { let total = 0; for (let day = dayNumber(booking.checkIn); day < dayNumber(booking.checkOut); day++) { const weekday = new Date(day * 86400000).getUTCDay(); total += weekday === 6 || weekday === 0 ? rates.weekend : rates.weekday; } return total; } ``` Reply with the whole corrected module in one ```js code block, with the same function names.```js // Bookings for one holiday flat. A booking is { guest, checkIn, checkOut } with dates as "YYYY-MM-DD". // A guest sleeps the nights from checkIn up to, but not including, checkOut: checkOut is the day they leave, // and another guest may check in that same day. function dayNumber(date) { const [y, m, d] = date.split('-').map(Number); return Date.UTC(y, m - 1, d) / 86400000; } function toDate(day) { return new Date(day * 86400000).toISOString().slice(0, 10); } function nights(booking) { return dayNumber(booking.checkOut) - dayNumber(booking.checkIn); } function overlaps(a, b) { return dayNumber(a.checkIn) < dayNumber(b.checkOut) && dayNumber(b.checkIn) < dayNumber(a.checkOut); } function canBook(bookings, request) { if (nights(request) < 1) return false; for (const booking of bookings) { if (overlaps(booking, request)) return false; } return true; } function isBooked(bookings, day) { return bookings.some((b) => dayNumber(b.checkIn) <= day && day < dayNumber(b.checkOut)); } // The nights from `from` up to, but not including, `to` that someone has booked. function bookedNights(bookings, from, to) { let count = 0; for (let day = dayNumber(from); day < dayNumber(to); day++) { if (isBooked(bookings, day)) count++; } return count; } // The first night on or after `from` that nobody has booked, as "YYYY-MM-DD". function firstFreeNight(bookings, from) { let day = dayNumber(from); while (isBooked(bookings, day)) day++; return toDate(day); } // The price in cents: Friday and Saturday nights at rates.weekend, every other night at rates.weekday. function price(booking, rates) { let total = 0; for (let day = dayNumber(booking.checkIn); day < dayNumber(booking.checkOut); day++) { const weekday = new Date(day * 86400000).getUTCDay(); total += weekday === 5 || weekday === 6 ? rates.weekend : rates.weekday; } return total; } ```Fix the CSV readerPassed
2,118 tokens: 558 read, 1,560 written · 1 min 10 s · 23.1 tok/s
This JavaScript module has one bug. A line that ends with a comma loses its last, empty field, so the next column's values shift. Find it and fix it without changing anything else. ```js // Reads CSV the way spreadsheets write it: fields are separated by commas, a field may be wrapped in // double quotes so it can hold commas, and "" inside a quoted field stands for one double quote. // Fields are not trimmed. Empty lines are skipped. function parseLine(line) { const fields = []; let field = ''; let quoted = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (quoted) { if (ch === '"') { if (line[i + 1] === '"') { field += '"'; i++; } else { quoted = false; } } else { field += ch; } } else if (ch === '"') { quoted = true; } else if (ch === ',') { fields.push(field); field = ''; } else { field += ch; } } if (field !== '') fields.push(field); return fields; } function parseCSV(text) { return text.split(/\r?\n/).filter((line) => line !== '').map(parseLine); } // Each row after the header as an object keyed by the header's names; a missing field is ''. function toObjects(text) { const [header, ...rows] = parseCSV(text); return rows.map((row) => { const record = {}; header.forEach((name, i) => { record[name] = i < row.length ? row[i] : ''; }); return record; }); } // The sum of a column's numbers; fields that are not numbers count as 0. function sumColumn(text, name) { return toObjects(text).reduce((total, record) => total + (Number(record[name]) || 0), 0); } ``` Reply with the whole corrected module in one ```js code block, with the same function names.```js // Reads CSV the way spreadsheets write it: fields are separated by commas, a field may be wrapped in // double quotes so it can hold commas, and "" inside a quoted field stands for one double quote. // Fields are not trimmed. Empty lines are skipped. function parseLine(line) { const fields = []; let field = ''; let quoted = false; for (let i = 0; i < line.length; i++) { const ch = line[i]; if (quoted) { if (ch === '"') { if (line[i + 1] === '"') { field += '"'; i++; } else { quoted = false; } } else { field += ch; } } else if (ch === '"') { quoted = true; } else if (ch === ',') { fields.push(field); field = ''; } else { field += ch; } } fields.push(field); return fields; } function parseCSV(text) { return text.split(/\r?\n/).filter((line) => line !== '').map(parseLine); } // Each row after the header as an object keyed by the header's names; a missing field is ''. function toObjects(text) { const [header, ...rows] = parseCSV(text); return rows.map((row) => { const record = {}; header.forEach((name, i) => { record[name] = i < row.length ? row[i] : ''; }); return record; }); } // The sum of a column's numbers; fields that are not numbers count as 0. function sumColumn(text, name) { return toObjects(text).reduce((total, record) => total + (Number(record[name]) || 0), 0); } ```
Planning and data 3 of 3
A query over hidden dataPassed
879 tokens: 274 read, 605 written · 27 s · 23.7 tok/s
Write one SQLite query that answers the question below. The site runs it on its own copy of these tables, filled with rows you have not seen. ```sql CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT NOT NULL, city TEXT NOT NULL); CREATE TABLE orders (id INTEGER PRIMARY KEY, customer_id INTEGER NOT NULL REFERENCES customers(id), placed TEXT NOT NULL, -- 'YYYY-MM-DD' status TEXT NOT NULL); -- 'delivered', 'cancelled' or 'pending' CREATE TABLE items (order_id INTEGER NOT NULL REFERENCES orders(id), product TEXT NOT NULL, quantity INTEGER NOT NULL, price_cents INTEGER NOT NULL); -- price_cents: for one unit ``` Question: For each product, how many different customers bought it in a delivered order. Columns: product, customers. Only products with at least 2 such customers. Most customers first, then product name A to Z. Reply with only the query, in one ```sql code block: one SELECT statement (WITH is fine), nothing that changes the database.```sql SELECT i.product AS product, COUNT(DISTINCT c.id) AS customers FROM items AS i JOIN orders AS o ON o.id = i.order_id JOIN customers AS c ON c.id = o.customer_id WHERE o.status = 'delivered' GROUP BY i.product HAVING COUNT(DISTINCT c.id) >= 2 ORDER BY customers DESC, product ASC; ```Schedule five meetingsPassed
10,445 tokens: 330 read, 10,115 written · 7 min 32 s · 22.4 tok/s
Five people each have one meeting today: Cleo, Fay, Ivy, Max, Noa. The five meetings are at different times, 09:00, 10:00, 11:00, 13:00, 14:00 (in that order: 10:00 is right after 09:00, 13:00 right after 11:00), and in different rooms. The list of rooms, in order: Cedar, Elm, Oak, Pine, Willow. 1. Max is not in Willow. 2. Whoever is in Cedar meets earlier than Max. 3. Noa meets earlier than Max. 4. Whoever is in Willow meets earlier than Fay. 5. Max is not in Oak. 6. Fay's and Noa's rooms are next to each other on the list of rooms. 7. Whoever is in Pine meets earlier than Noa. 8. Cleo meets neither first nor last. 9. Cleo's and Max's rooms are not next to each other on the list of rooms. Exactly one schedule fits every clue. Work it out, then end with exactly five lines, one per person, in this form: Ana: 09:00, Oak
Cleo: 10:00, Willow Fay: 11:00, Pine Ivy: 09:00, Cedar Max: 14:00, Elm Noa: 13:00, Oak
A replacement in ten calls or fewerPassed
11,163 tokens: 10,318 read, 845 written · 43 s · 25.2 tok/s
Passes when Looks up the account, opens order R-5530 (the kettle), checks north then south and stops, reserves one steel kettle at south for R-5530, opens a damage claim (a return, if tried, is refused), makes at most 10 calls, and tells the customer claim CL-2207 and that the replacement ships on 3 November, without claiming a return label.
Hi, it's ana.silva@example.com. My kettle arrived with a cracked lid. Can you sort it out?
- system
You are the support agent of Tidewell Home, working through its tools. When an item arrives damaged: confirm which order it came in, send a replacement from the north warehouse if it has the item, else from south, else from east (check each in that order, and only as far as needed), and take the damaged item back with a return. Then reply to the customer with what you did, giving any reference numbers and the date the replacement ships.
- user
Hi, it's ana.silva@example.com. My kettle arrived with a cracked lid. Can you sort it out?
- assistant
lookup_account({"email":"ana.silva@example.com"}) - tool
{"account_id": "AC-311", "name": "Ana Silva"} - assistant
list_orders({"account_id":"AC-311"}) - tool
{"orders": [{"order_id": "R-5521", "placed": "2026-10-02", "status": "delivered"}, {"order_id": "R-5530", "placed": "2026-10-09", "status": "delivered"}, {"order_id": "R-5544", "placed": "2026-10-20", "status": "in transit"}]} - assistant
get_order({"order_id":"R-5530"})get_order({"order_id":"R-5521"}) - tool
{"items": [{"name": "Steel kettle", "quantity": 1, "sku": "kettle-steel"}, {"name": "Grey mug", "quantity": 2, "sku": "mug-grey"}], "order_id": "R-5530"} - tool
{"items": [{"name": "Oak table lamp", "quantity": 1, "sku": "lamp-oak"}], "order_id": "R-5521"} - assistant
check_stock({"sku":"kettle-steel","warehouse":"north"}) - tool
{"available": 0, "sku": "kettle-steel", "warehouse": "north"} - assistant
check_stock({"sku":"kettle-steel","warehouse":"south"}) - tool
{"available": 4, "sku": "kettle-steel", "warehouse": "south"} - assistant
reserve_item({"sku":"kettle-steel","warehouse":"south","order_id":"R-5530"})create_return({"order_id":"R-5530","reason":"Kettle arrived with a cracked lid"}) - tool
{"reservation_id": "RS-4410", "ships_on": "2026-11-03"} - tool
{"error": "damaged items are not returned: open a damage claim with open_claim instead"} - assistant
open_claim({"order_id":"R-5530","reason":"Steel kettle arrived with a cracked lid"}) - tool
{"claim_id": "CL-2207", "order_id": "R-5530", "status": "open"} - assistant
Hi Ana, Sorry about the cracked lid! I've sorted this out for you: - **Order identified:** Your kettle came in order **R-5530** (Steel kettle, delivered). - **Replacement:** The north warehouse was out of stock, so I reserved a replacement from the south warehouse — **reservation RS-4410**. It ships to your address on **3 November 2026**. - **Damaged item:** A damage claim has been opened (ref **CL-2207**) so we can collect the cracked kettle back from you — you'll hear how that works once the claim is processed. Is there anything else I can help with?
- system
Comments
Sign in or create an account to comment.