Qwen3.8-27B

M4 Max 128 GB · llama.cpp llama-swap (version not exposed) · every run of this model · uwu · 2026-11-05

Race this run

Race it: run Qwen3.8-27B on your computer and see the two side by side.

Score
1,950 Silver
Tasks passed
41/44
Points
57/64Harder tasks count 3
Tokens
270,90222,578 read, 248,324 written, as the runner counted them
Speed
21.9 tok/sAs the runner measured it; the site cannot check this.
Writing speed
21.9 tok/sPrompt reading left out. As 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.

This setup, checked

No other runs of Qwen3.8-27B on M4 Max 128 GB yet; the next one will be compared with this one.

Qwen3.8-27B on M4 Max 128 GB: 41 of 44 tasks in 271k tokens at 21.9 tok/s, a Silver score of 1,950, in the resyn benchmark. https://resyn.cloud/suite/3c7bae2473d3b40e4fd292b3f2783b85Compare another runComment

Not on the leaderboard: it was posted under an earlier version of the task suite. Leaderboard

Run details
Model file
Not a listed file
Where it ran
Other
How
Run through the model's API, by OpenCode (resyn-bench 2)
Model the API reported
Qwen3.8-27B-UD-Q8_K_XL
Time
3 h 10 min
Settings
seed=42, temperature=0, top_p=1
Suite
resyn-tasks@5
Flag this run

Hides the run from public pages until an admin checks it.

Failed 3

  • A bar chart in SVGThe chart has no accessible name

    28,523 tokens: 182 read, 28,341 written · 21 min 3 s · 22.5 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>The Daily Grind · Weekly Coffee Ledger</title>
    <style>
      :root{
        --bg:#170f09;
        --board1:#2a1b11; --board2:#221510;
        --line:#4a3320;
        --cream:#f4e7d0;
        --dim:#b3926b;
        --faint:#8a6c4c;
        --acc:#e8a24c;
        --serif:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
        --mono:"Courier New",ui-monospace,Consolas,Menlo,monospace;
        --sans:system-ui,"Segoe UI",Helvetica,Arial,sans-serif;
        color-scheme:dark;
      }
      *{box-sizing:border-box}
      html,body{margin:0}
      body{
        min-height:100vh;
        display:flex; align-items:center; justify-content:center;
        padding:40px 18px;
        font-family:var(--sans);
        color:var(--cream);
        background-color:var(--bg);
        background-image:
          radial-gradient(900px 520px at 10% -10%, rgba(232,162,76,.10), transparent 60%),
          radial-gradient(760px 540px at 112% 116%, rgba(150,70,25,.22), transparent 62%),
          radial-gradient(rgba(244,231,208,.03) 1px, transparent 1.4px);
        background-size:auto,auto,26px 26px;
        overflow-x:hidden;
      }
    
      /* drifting beans */
      .bean{position:fixed; z-index:0; pointer-events:none; opacity:.12;
        animation:drift ease-in-out infinite alternate}
      .bean svg{display:block; width:100%; height:auto}
      .b1{top:7%;  left:5%;   width:60px; animation-duration:15s}
      .b2{top:16%; right:6%;  width:44px; animation-duration:12s; animation-delay:-4s}
      .b3{bottom:10%; left:10%; width:52px; animation-duration:17s; animation-delay:-8s}
      .b4{bottom:6%; right:14%; width:70px; animation-duration:13s; animation-delay:-2s}
      .b5{top:52%; left:2%;  width:36px; animation-duration:19s; animation-delay:-11s}
      @keyframes drift{
        from{transform:translateY(-14px) rotate(-16deg)}
        to{transform:translateY(16px) rotate(12deg)}
      }
    
      .wrap{width:min(720px,100%); position:relative; z-index:1}
    
      /* header */
      .topbar{display:flex; justify-content:space-between; align-items:flex-end;
        gap:16px; flex-wrap:wrap; margin-bottom:20px;
        animation:fadeDown .7s ease both}
      .brand{display:flex; align-items:center; gap:14px}
      .cup{width:46px; height:46px; flex:none}
      .cup-steam{fill:none; stroke:var(--acc); stroke-width:2.4; stroke-linecap:round;
        opacity:0; animation:puff 2.6s ease-in-out infinite}
      .s2{animation-delay:1.3s}
      @keyframes puff{
        0%{opacity:0; transform:translateY(4px)}
        35%{opacity:.9}
        75%,100%{opacity:0; transform:translateY(-6px)}
      }
      .eyebrow{display:inline-flex; align-items:center; gap:8px;
        font:600 11px/1 var(--sans); letter-spacing:.22em; text-transform:uppercase;
        color:var(--acc)}
      .open-dot{width:7px; height:7px; border-radius:50%; background:var(--acc);
        animation:blink 2.2s ease-in-out infinite}
      @keyframes blink{
        0%,100%{opacity:1; box-shadow:0 0 0 0 rgba(232,162,76,.5)}
        50%{opacity:.5; box-shadow:0 0 0 5px rgba(232,162,76,0)}
      }
      h1{margin:7px 0 0; font:italic 400 clamp(30px,6vw,42px)/1 var(--serif)}
      .stat{text-align:right; font-family:var(--mono)}
      .stat-num{display:block; font-size:42px; font-weight:700; line-height:1; color:var(--acc)}
      .stat-cap{font-size:11px; letter-spacing:.18em; text-transform:uppercase; color:var(--dim)}
    
      /* board */
      .board{
        position:relative;
        background:linear-gradient(180deg,var(--board1),var(--board2) 60%);
        border:1px solid var(--line); border-radius:6px;
        box-shadow:0 34px 60px -30px rgba(0,0,0,.85), inset 0 1px 0 rgba(244,231,208,.06);
        padding:20px 20px 10px;
        animation:fadeUp .8s .12s ease both;
      }
      .board::before{content:""; position:absolute; inset:9px;
        border:1px dashed rgba(244,231,208,.13); border-radius:4px; pointer-events:none}
      .board-head{display:flex; justify-content:space-between; align-items:baseline;
        gap:12px; padding:6px 8px 0; flex-wrap:wrap}
      h2{margin:0; font:700 12px/1 var(--mono); letter-spacing:.24em;
        text-transform:uppercase; color:var(--dim)}
      .scale{font:11px var(--mono); color:var(--faint); letter-spacing:.05em}
    
      /* chart */
      .chart{display:block; width:100%; height:auto}
      .grid line{stroke:var(--line); stroke-dasharray:3 6}
      .vaxis{stroke:#5b3f27; stroke-width:1.5}
      .axis{stroke:#7a5433; stroke-width:2}
      .y-lab{fill:var(--faint); font:11px var(--mono); text-anchor:end}
    
      .col{cursor:pointer; outline:none}
      .grow{transform-box:fill-box; transform-origin:50% 100%;
        animation:rise 1s cubic-bezier(.19,1,.22,1) backwards}
      .g1{animation-delay:.15s}.g2{animation-delay:.30s}.g3{animation-delay:.45s}
      .g4{animation-delay:.60s}.g5{animation-delay:.75s}
      @keyframes rise{from{transform:scaleY(0)}}
    
      .bar{fill:url(#brew); transition:filter .25s ease}
      .crema{fill:#f6cf92; opacity:.85; transform-box:fill-box; transform-origin:50% 50%;
        transition:transform .2s ease, opacity .3s ease}
      .value{fill:var(--cream); font:700 17px var(--mono); text-anchor:middle;
        transform-box:fill-box; transform-origin:50% 100%;
        transition:transform .25s ease, fill .25s ease;
        animation:pop .5s ease backwards}
      .v1{animation-delay:.80s}.v2{animation-delay:.95s}.v3{animation-delay:1.10s}
      .v4{animation-delay:1.25s}.v5{animation-delay:1.40s}
      .hot{fill:var(--acc); font-size:19px}
      .day{fill:#c9ab84; font:italic 15px var(--serif); text-anchor:middle;
        animation:fadein .6s ease .1s backwards; transition:fill .25s ease}
      @keyframes pop{from{opacity:0; transform:translateY(10px)}}
      @keyframes fadein{from{opacity:0}}
    
      .steam{animation:fadein 1s ease .9s backwards}
      .steam path{fill:none; stroke:var(--acc); stroke-width:2; stroke-linecap:round;
        opacity:0; animation:wispy 3s ease-in-out infinite}
      .steam .s2{animation-delay:1.5s}
      @keyframes wispy{
        0%{opacity:0; transform:translateY(6px)}
        30%{opacity:.75}
        65%{opacity:.3}
        100%{opacity:0; transform:translateY(-9px)}
      }
    
      .col:hover .bar,.col:focus-visible .bar{filter:brightness(1.2) saturate(1.15)}
      .col:focus-visible .bar{stroke:var(--acc); stroke-width:2}
      .col:hover .value{transform:translateY(-4px); fill:var(--acc)}
      .col:hover .day{fill:var(--cream)}
      .col.sip .value{transform:translateY(-9px)}
      .col.sip .crema{transform:scaleX(1.28); opacity:.5}
    
      .readout{margin:2px 8px 12px; min-height:1.3em; font:12px var(--mono);
        color:var(--dim); letter-spacing:.03em}
      .readout b{color:var(--acc); font-weight:700}
    
      /* receipt tape */
      .tape{
        position:relative; margin-top:16px; padding:12px 18px 13px;
        background:#f1e3c6; color:#33200f;
        border-top:2px dashed rgba(51,32,15,.3);
        font:600 11px/1.4 var(--mono); letter-spacing:.05em;
        display:flex; flex-wrap:wrap; gap:6px 20px; align-items:baseline;
        box-shadow:0 12px 26px -14px rgba(0,0,0,.7);
        animation:fadeUp .8s .5s ease both;
      }
      .tape::after{
        content:""; position:absolute; left:0; right:0; top:100%; height:7px;
        background:
          linear-gradient(45deg,#f1e3c6 3.5px,transparent 0) 0 0/7px 7px repeat-x,
          linear-gradient(-45deg,#f1e3c6 3.5px,transparent 0) 0 0/7px 7px repeat-x;
      }
      .ti{display:inline-flex; align-items:baseline; gap:8px; white-space:nowrap}
      .ti i{flex:1 1 16px; min-width:12px; border-bottom:2px dotted #9a7a4f;
        transform:translateY(-3px)}
      .total{margin-left:auto; color:#8a3d12}
    
      @keyframes fadeUp{from{opacity:0; transform:translateY(14px)}}
      @keyframes fadeDown{from{opacity:0; transform:translateY(-10px)}}
    
      @media (max-width:520px){
        .stat{text-align:left}
        .stat-num{font-size:32px}
        .total{margin-left:0; width:100%}
      }
      @media (prefers-reduced-motion:reduce){
        *,*::before,*::after{animation:none!important; transition:none!important}
      }
    </style>
    </head>
    <body>
    
      <div class="bean b1" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#b97a3e"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b2" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#a96d34"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b3" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#b97a3e"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b4" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#9c6230"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b5" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#b97a3e"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
    
      <div class="wrap">
    
        <header class="topbar">
          <div class="brand">
            <svg class="cup" viewBox="0 0 48 48" aria-hidden="true">
              <path class="cup-steam" d="M19 15c-3-4 3-6 0-11"/>
              <path class="cup-steam s2" d="M28 15c-3-4 3-6 0-11"/>
              <path d="M10 22h26v6a13 10 0 0 1-26 0z" fill="none" stroke="#f4e7d0" stroke-width="2.4" stroke-linejoin="round"/>
              <path d="M36 24h4a5 5 0 0 1 0 10h-5" fill="none" stroke="#f4e7d0" stroke-width="2.4" stroke-linecap="round"/>
              <path d="M8 42h32" stroke="#b3926b" stroke-width="2.4" stroke-linecap="round"/>
            </svg>
            <div>
              <span class="eyebrow"><span class="open-dot"></span> Café ledger — week 32</span>
              <h1>The Daily Grind</h1>
            </div>
          </div>
          <div class="stat">
            <span class="stat-num">90</span>
            <span class="stat-cap">cups poured</span>
          </div>
        </header>
    
        <section class="board">
          <div class="board-head">
            <h2>Weekly pour</h2>
            <span class="scale">scale · 1 cup = 10 px</span>
          </div>
    
          <svg class="chart" viewBox="0 0 640 440" role="img"
               aria-label="Bar chart of coffee sold this week: Monday 12, Tuesday 30, Wednesday 18, Thursday 24, Friday 6.">
            <title>Coffee sold per day, Monday through Friday</title>
            <defs>
              <linearGradient id="brew" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stop-color="#f2b95f"/>
                <stop offset=".32" stop-color="#d98a35"/>
                <stop offset="1" stop-color="#5c2d12"/>
              </linearGradient>
            </defs>
    
            <g class="grid">
              <line x1="60" x2="620" y1="60"  y2="60"/>
              <line x1="60" x2="620" y1="120" y2="120"/>
              <line x1="60" x2="620" y1="180" y2="180"/>
              <line x1="60" x2="620" y1="240" y2="240"/>
              <line x1="60" x2="620" y1="300" y2="300"/>
            </g>
            <line class="vaxis" x1="60" x2="60" y1="54" y2="360"/>
    
            <g>
              <text class="y-lab" x="48" y="64">30</text>
              <text class="y-lab" x="48" y="124">24</text>
              <text class="y-lab" x="48" y="184">18</text>
              <text class="y-lab" x="48" y="244">12</text>
              <text class="y-lab" x="48" y="304">6</text>
              <text class="y-lab" x="48" y="364">0</text>
            </g>
    
            <g class="col" data-day="Monday" data-cups="12" tabindex="0">
              <g class="grow g1">
                <rect class="bar" x="87" y="240" width="56" height="120" rx="3"/>
                <rect class="crema" x="87" y="240" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v1" x="115" y="226">12</text>
              <text class="day" x="115" y="392">Monday</text>
            </g>
    
            <g class="col" data-day="Tuesday" data-cups="30" tabindex="0">
              <g class="grow g2">
                <rect class="bar" x="197" y="60" width="56" height="300" rx="3"/>
                <rect class="crema" x="197" y="60" width="56" height="7" rx="3.5"/>
              </g>
              <g class="steam" aria-hidden="true">
                <path d="M210 52c-5-9 5-13 0-24"/>
                <path class="s2" d="M240 52c5-9 -5-13 0-24"/>
              </g>
              <text class="value v2 hot" x="225" y="46">30</text>
              <text class="day" x="225" y="392">Tuesday</text>
            </g>
    
            <g class="col" data-day="Wednesday" data-cups="18" tabindex="0">
              <g class="grow g3">
                <rect class="bar" x="307" y="180" width="56" height="180" rx="3"/>
                <rect class="crema" x="307" y="180" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v3" x="335" y="166">18</text>
              <text class="day" x="335" y="392">Wednesday</text>
            </g>
    
            <g class="col" data-day="Thursday" data-cups="24" tabindex="0">
              <g class="grow g4">
                <rect class="bar" x="417" y="120" width="56" height="240" rx="3"/>
                <rect class="crema" x="417" y="120" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v4" x="445" y="106">24</text>
              <text class="day" x="445" y="392">Thursday</text>
            </g>
    
            <g class="col" data-day="Friday" data-cups="6" tabindex="0">
              <g class="grow g5">
                <rect class="bar" x="527" y="300" width="56" height="60" rx="3"/>
                <rect class="crema" x="527" y="300" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v5" x="555" y="286">6</text>
              <text class="day" x="555" y="392">Friday</text>
            </g>
    
            <line class="axis" x1="60" x2="620" y1="360" y2="360"/>
          </svg>
    
          <p class="readout" id="readout" aria-live="polite"></p>
        </section>
    
        <footer class="tape" aria-hidden="true">
          <span class="ti">MON<i></i><b>12</b></span>
          <span class="ti">TUE<i></i><b>30</b></span>
          <span class="ti">WED<i></i><b>18</b></span>
          <span class="ti">THU<i></i><b>24</b></span>
          <span class="ti">FRI<i></i><b>06</b></span>
          <span class="ti total">TOTAL<i></i><b>90</b></span>
        </footer>
    
      </div>
    
      <script>
        (function () {
          var cols = Array.prototype.slice.call(document.querySelectorAll('.col'));
          var total = cols.reduce(function (s, c) {
            return s + +c.getAttribute('data-cups');
          }, 0);
          var readout = document.getElementById('readout');
          var DEFAULT = 'Peak pour — <b>Tuesday</b> · 30 cups · 33% of the week\u2019s ' + total;
          readout.innerHTML = DEFAULT;
          var timers = {};
    
          cols.forEach(function (col, i) {
            var day = col.getAttribute('data-day');
            var cups = +col.getAttribute('data-cups');
            var pct = Math.round(cups / total * 100);
            var msg = '<b>' + day + ':</b> ' + cups + ' cup' + (cups === 1 ? '' : 's') +
                      ' \u00b7 ' + pct + '% of the week';
    
            function show()  { readout.innerHTML = msg; }
            function reset() { readout.innerHTML = DEFAULT; }
            function sip() {
              col.classList.add('sip');
              clearTimeout(timers[i]);
              timers[i] = setTimeout(function () { col.classList.remove('sip'); }, 240);
            }
    
            col.addEventListener('mouseenter', show);
            col.addEventListener('focus', show);
            col.addEventListener('mouseleave', reset);
            col.addEventListener('blur', reset);
            col.addEventListener('click', sip);
            col.addEventListener('keydown', function (e) {
              if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); sip(); }
            });
          });
        })();
      </script>
    </body>
    </html>
  • Build 2048Failed

    54,311 tokens: 502 read, 53,809 written · 41 min 17 s · 21.7 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.
    [0, 0, 0, 0]
    [0, 0, 0, 0]
    [0, 32, 2, 2]
    [32, 16, 4, 4]
    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="color-scheme" content="dark">
    <title>2048 — slide · merge · climb</title>
    <style>
      * { box-sizing: border-box; }
      html { color-scheme: dark; }
      html, body { margin: 0; padding: 0; }
    
      :root {
        --bg: #0e1421;
        --panel: #171f30;
        --board: #1b2437;
        --line: #2d3a58;
        --ink: #ecf1fb;
        --muted: #8ea0c0;
        --accent: #f3a83c;
        --accent-deep: #c97f1c;
        --gap: clamp(8px, 2.2vw, 13px);
        --r: 14px;
        --sans: "Segoe UI", "Helvetica Neue", system-ui, -apple-system, sans-serif;
        --display: "Arial Black", "Avenir Next", Futura, "Segoe UI", system-ui, sans-serif;
        --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
      }
    
      body {
        min-height: 100vh;
        background: var(--bg);
        color: var(--ink);
        font-family: var(--sans);
        overflow-x: hidden;
      }
      body::before,
      body::after {
        content: "";
        position: fixed;
        inset: 0;
        pointer-events: none;
        z-index: 0;
      }
      body::before {
        inset: -12%;
        background:
          radial-gradient(46% 38% at 16% 10%, rgba(243, 168, 60, .16), transparent 70%),
          radial-gradient(42% 36% at 86% 88%, rgba(84, 148, 255, .13), transparent 70%),
          radial-gradient(28% 24% at 78% 14%, rgba(255, 110, 64, .09), transparent 70%);
        animation: drift 26s ease-in-out infinite alternate;
      }
      body::after {
        background-image: radial-gradient(rgba(236, 241, 251, .05) 1px, transparent 1.4px);
        background-size: 26px 26px;
        -webkit-mask-image: radial-gradient(75% 68% at 50% 42%, #000 32%, transparent 100%);
        mask-image: radial-gradient(75% 68% at 50% 42%, #000 32%, transparent 100%);
      }
      @keyframes drift {
        from { transform: translate3d(-1.4%, -1%, 0); }
        to   { transform: translate3d(1.4%, 1.2%, 0); }
      }
    
      .stage {
        position: relative;
        z-index: 1;
        max-width: 620px;
        margin: 0 auto;
        padding: clamp(18px, 4vh, 42px) clamp(14px, 4vw, 28px) 46px;
        display: flex;
        flex-direction: column;
        gap: clamp(14px, 3vh, 22px);
      }
    
      /* ---------- header ---------- */
      .top {
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        gap: 14px 20px;
        flex-wrap: wrap;
        animation: rise .55s ease both;
      }
      .mark {
        margin: 0;
        font-family: var(--display);
        font-weight: 900;
        font-size: clamp(2.7rem, 9vw, 4.3rem);
        line-height: .85;
        letter-spacing: -0.04em;
        text-shadow: 0 3px 0 rgba(0, 0, 0, .4), 0 14px 34px rgba(243, 168, 60, .14);
      }
      .mark .hot { color: var(--accent); }
      .tag { margin: 8px 0 0; color: var(--muted); font-size: .95rem; }
      .tag b { color: var(--accent); }
    
      .hud { display: flex; align-items: stretch; gap: 10px; animation: rise .55s .1s ease both; }
      .stat {
        position: relative;
        min-width: 92px;
        padding: 8px 14px 10px;
        text-align: center;
        background: var(--panel);
        border: 1px solid var(--line);
        border-radius: 11px;
        box-shadow: 0 6px 16px rgba(0, 0, 0, .28);
      }
      .stat-label {
        display: block;
        font-size: .6rem;
        font-weight: 700;
        letter-spacing: .16em;
        text-transform: uppercase;
        color: var(--muted);
        margin-bottom: 2px;
      }
      .stat-value {
        display: block;
        font-family: var(--mono);
        font-weight: 700;
        font-size: 1.3rem;
        font-variant-numeric: tabular-nums;
        transition: color .35s ease;
      }
      .stat.flash .stat-value { color: var(--accent); }
      .float-gain {
        position: absolute;
        left: 50%;
        top: 26%;
        font-family: var(--mono);
        font-weight: 700;
        color: var(--accent);
        text-shadow: 0 2px 10px rgba(0, 0, 0, .55);
        pointer-events: none;
        animation: float-up .75s ease-out forwards;
      }
      @keyframes float-up {
        0%   { opacity: 0; transform: translate(-50%, 8px) scale(.8); }
        18%  { opacity: 1; }
        100% { opacity: 0; transform: translate(-50%, -38px) scale(1.12); }
      }
    
      .btn {
        font-family: var(--sans);
        font-size: .92rem;
        font-weight: 800;
        letter-spacing: .02em;
        color: #261602;
        background: linear-gradient(180deg, #ffca62, var(--accent));
        border: 0;
        border-radius: 11px;
        padding: 10px 18px;
        cursor: pointer;
        box-shadow: 0 3px 0 var(--accent-deep), 0 10px 20px rgba(243, 168, 60, .22);
        transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
      }
      .btn:hover { transform: translateY(-2px); filter: brightness(1.06); box-shadow: 0 5px 0 var(--accent-deep), 0 14px 26px rgba(243, 168, 60, .3); }
      .btn:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--accent-deep), 0 6px 12px rgba(243, 168, 60, .22); }
      .btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
    
      /* ---------- board ---------- */
      .board-zone {
        position: relative;
        width: min(100%, 520px);
        margin: 0 auto;
        animation: rise .6s .18s cubic-bezier(.22, 1, .36, 1) both;
      }
      .board {
        position: relative;
        width: 100%;
        aspect-ratio: 1;
        background: linear-gradient(180deg, #202b44, var(--board));
        border: 1px solid var(--line);
        border-radius: var(--r);
        box-shadow: 0 28px 64px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .06);
        container-type: size;
        touch-action: none;
        user-select: none;
        -webkit-user-select: none;
        transition: transform .1s ease;
      }
      .board.pushed { transform: scale(.992); }
      .cells, .tiles { position: absolute; inset: var(--gap); }
      .cells {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        grid-template-rows: repeat(4, 1fr);
        gap: var(--gap);
      }
      .cells div {
        background: rgba(8, 12, 21, .5);
        border-radius: 9px;
        box-shadow: inset 0 2px 7px rgba(0, 0, 0, .42), inset 0 -1px 0 rgba(255, 255, 255, .04);
      }
    
      .tile {
        position: absolute;
        width: calc(25% - 0.75 * var(--gap));
        height: calc(25% - 0.75 * var(--gap));
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 9px;
        font-weight: 800;
        line-height: 1;
        font-size: 1.9rem;
        font-size: 10.5cqw;
        transition: left .12s ease-out, top .12s ease-out;
        box-shadow: 0 3px 8px rgba(0, 0, 0, .35), inset 0 1px 0 rgba(255, 255, 255, .22);
        will-change: left, top;
        z-index: 1;
      }
      .tile.d3 { font-size: 8.4cqw; }
      .tile.d4 { font-size: 6.6cqw; }
      .tile.d5 { font-size: 5.2cqw; }
      .tile.spawn { animation: tile-in .2s cubic-bezier(.34, 1.4, .64, 1) both; z-index: 2; }
      .tile.merge-pop { animation: merge-pop .22s ease-out; z-index: 3; }
      @keyframes tile-in {
        from { transform: scale(0); opacity: 0; }
        to   { transform: scale(1); opacity: 1; }
      }
      @keyframes merge-pop {
        0%   { transform: scale(1); }
        45%  { transform: scale(1.18); }
        100% { transform: scale(1); }
      }
    
      .t2    { background: #e9dfca; color: #6f5f47; }
      .t4    { background: #e2cfa8; color: #6b573b; }
      .t8    { background: #e7a63c; color: #4c3405; }
      .t16   { background: #e28a2c; color: #fff5e3; }
      .t32   { background: #dc6c28; color: #fff5e3; }
      .t64   { background: #d64a33; color: #ffeee4; }
      .t128  { background: #f6c344; color: #57400a; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 18px rgba(246,195,68,.32); }
      .t256  { background: #f4ae35; color: #533c08; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 24px rgba(244,174,53,.42); }
      .t512  { background: #ef982a; color: #4a2c04; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 28px rgba(239,152,42,.48); }
      .t1024 { background: #fb8a25; color: #3d2003; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 32px rgba(251,138,37,.55); }
      .t2048 {
        background: #ffd042; color: #634404;
        box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 30px rgba(255,208,66,.6);
        animation: breathe 2.4s ease-in-out infinite;
      }
      .tmax  { background: #fff1a8; color: #6d5308; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 46px rgba(255,222,110,.75); }
      @keyframes breathe {
        0%, 100% { box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 24px rgba(255,208,66,.5); }
        50%      { box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 48px rgba(255,208,66,.85); }
      }
    
      /* ---------- overlay & toast ---------- */
      .overlay {
        position: absolute;
        inset: 0;
        z-index: 5;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: var(--r);
        background: rgba(9, 13, 22, .74);
        backdrop-filter: blur(3px);
        -webkit-backdrop-filter: blur(3px);
        animation: fade-in .28s ease both;
      }
      .overlay[hidden] { display: none; }
      .card { text-align: center; padding: 26px 38px; animation: rise .4s .05s ease both; }
      .card h2 {
        margin: 0 0 8px;
        font-family: var(--display);
        font-weight: 900;
        font-size: clamp(1.6rem, 6vw, 2.2rem);
        letter-spacing: -0.02em;
      }
      .card p { margin: 0 0 20px; color: var(--muted); }
      .card b { font-family: var(--mono); font-size: 1.25rem; color: var(--ink); }
      @keyframes fade-in { from { opacity: 0; } }
      @keyframes rise {
        from { opacity: 0; transform: translateY(14px) scale(.97); }
        to   { opacity: 1; transform: none; }
      }
    
      .toast {
        position: absolute;
        top: -16px;
        left: 50%;
        transform: translate(-50%, -100%);
        z-index: 6;
        white-space: nowrap;
        padding: 10px 22px;
        border-radius: 999px;
        font-weight: 800;
        font-size: .95rem;
        color: #2a1802;
        background: linear-gradient(180deg, #ffd062, var(--accent));
        box-shadow: 0 12px 30px rgba(243, 168, 60, .4);
        animation: toast-in .32s cubic-bezier(.34, 1.56, .64, 1) both;
      }
      .toast.out { animation: toast-out .32s ease both; }
      .toast[hidden] { display: none; }
      @keyframes toast-in {
        from { opacity: 0; transform: translate(-50%, -70%) scale(.8); }
        to   { opacity: 1; transform: translate(-50%, -100%) scale(1); }
      }
      @keyframes toast-out {
        to { opacity: 0; transform: translate(-50%, -190%) scale(.9); }
      }
    
      /* ---------- footer ---------- */
      .hint {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 8px 16px;
        flex-wrap: wrap;
        color: var(--muted);
        font-size: .84rem;
        animation: rise .55s .32s ease both;
      }
      kbd {
        display: inline-block;
        margin: 0 1px;
        padding: 3px 7px;
        font-family: var(--mono);
        font-size: .72rem;
        color: var(--ink);
        background: #1a2338;
        border: 1px solid #344260;
        border-bottom-width: 2px;
        border-radius: 6px;
      }
      @media (hover: none) and (pointer: coarse) { .keys { display: none; } }
      @media (hover: hover) { .swipe-hint { display: none; } }
    
      .sr-only {
        position: absolute;
        width: 1px; height: 1px;
        margin: -1px; padding: 0;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
        border: 0;
      }
    
      @media (max-width: 470px) {
        .top { flex-direction: column; align-items: stretch; }
        .hud { width: 100%; }
        .stat { flex: 1; }
        .btn { flex: 1.2; }
      }
      @media (prefers-reduced-motion: reduce) {
        *, *::before, *::after {
          animation-duration: .01ms !important;
          animation-iteration-count: 1 !important;
          transition-duration: .01ms !important;
        }
      }
    </style>
    </head>
    <body>
    <main class="stage">
      <header class="top">
        <div class="brand">
          <h1 class="mark">20<span class="hot">48</span></h1>
          <p class="tag">Slide the tiles. Merge the twins. Climb to <b>2048</b>.</p>
        </div>
        <div class="hud">
          <div class="stat" id="score-stat">
            <span class="stat-label">Score</span>
            <span class="stat-value" id="score">0</span>
          </div>
          <div class="stat" id="best-stat">
            <span class="stat-label">Best</span>
            <span class="stat-value" id="best">0</span>
          </div>
          <button class="btn" id="new-game" type="button">New&nbsp;game</button>
        </div>
      </header>
    
      <section class="board-zone" id="board-zone">
        <div class="board" id="board" role="img" aria-label="2048 board">
          <div class="cells" id="cells"></div>
          <div class="tiles" id="tiles"></div>
        </div>
        <div class="overlay" id="overlay" hidden>
          <div class="card">
            <h2>Game over</h2>
            <p>Final score <b id="final-score">0</b></p>
            <button class="btn" id="retry" type="button">Play again</button>
          </div>
        </div>
        <div class="toast" id="toast" hidden></div>
      </section>
    
      <footer class="hint">
        <span class="keys"><kbd>←</kbd><kbd>↑</kbd><kbd>↓</kbd><kbd>→</kbd>&nbsp;or&nbsp;<kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd></span>
        <span class="swipe-hint">swipe to move</span>
      </footer>
      <p class="sr-only" id="live" aria-live="polite"></p>
    </main>
    
    <script id="game">
    function createGame() {
      "use strict";
    
      const START = [
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 32, 2, 2],
        [32, 16, 4, 4]
      ];
    
      function zeroBoard() {
        return [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
      }
    
      function cloneBoard(b) {
        return b.map(function (row) { return row.slice(); });
      }
    
      function same(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;
      }
    
      // Rule 1 / 4: drop a 2 into each of the first `count` empty cells, reading order.
      function place(count, b) {
        let placed = 0;
        for (let r = 0; r < 4 && placed < count; r++)
          for (let c = 0; c < 4 && placed < count; c++)
            if (b[r][c] === 0) { b[r][c] = 2; placed++; }
      }
    
      // Rule 2: slide one row/column toward index 0; equal neighbors merge once,
      // nearest the target side first; a merge result never merges again.
      function slideLine(line) {
        const tiles = line.filter(function (v) { return v !== 0; });
        const out = [];
        let gained = 0;
        let i = 0;
        while (i < tiles.length) {
          if (i + 1 < tiles.length && tiles[i] === tiles[i + 1]) {
            const v = tiles[i] * 2;
            out.push(v);
            gained += v;
            i += 2;
          } else {
            out.push(tiles[i]);
            i += 1;
          }
        }
        while (out.length < 4) out.push(0);
        return { out: out, gained: gained };
      }
    
      const DIRS = ["left", "right", "up", "down"];
    
      function apply(b, dir) {
        const nb = zeroBoard();
        let gained = 0;
        for (let i = 0; i < 4; i++) {
          let line;
          if (dir === "left")        line = [b[i][0], b[i][1], b[i][2], b[i][3]];
          else if (dir === "right")  line = [b[i][3], b[i][2], b[i][1], b[i][0]];
          else if (dir === "up")     line = [b[0][i], b[1][i], b[2][i], b[3][i]];
          else                       line = [b[3][i], b[2][i], b[1][i], b[0][i]];
          const res = slideLine(line);
          gained += res.gained;
          for (let j = 0; j < 4; j++) {
            if (dir === "left")        nb[i][j] = res.out[j];
            else if (dir === "right")  nb[i][3 - j] = res.out[j];
            else if (dir === "up")     nb[j][i] = res.out[j];
            else                       nb[3 - j][i] = res.out[j];
          }
        }
        return { board: nb, gained: gained };
      }
    
      function movable(b) {
        return DIRS.some(function (d) { return !same(b, apply(b, d).board); });
      }
    
      const board = cloneBoard(START);
      place(2, board);            // rule 1
      let score = 0;
      let over = !movable(board); // rule 5
    
      function move(dir) {
        if (over || DIRS.indexOf(dir) === -1) return;
        const res = apply(board, dir);
        if (same(res.board, board)) return; // rule 4: no change -> nothing happens
        board = res.board;
        score += res.gained;               // rule 3
        place(1, board);                   // rule 4
        over = !movable(board);            // rule 5
      }
    
      return {
        move: move,
        state: function () {
          return { board: cloneBoard(board), score: score, over: over };
        }
      };
    }
    </script>
    
    <script>
    (function () {
      "use strict";
    
      const cellsEl   = document.getElementById("cells");
      const tilesEl   = document.getElementById("tiles");
      const boardEl   = document.getElementById("board");
      const zoneEl    = document.getElementById("board-zone");
      const scoreEl   = document.getElementById("score");
      const bestEl    = document.getElementById("best");
      const scoreStat = document.getElementById("score-stat");
      const bestStat  = document.getElementById("best-stat");
      const overlayEl = document.getElementById("overlay");
      const finalEl   = document.getElementById("final-score");
      const retryEl   = document.getElementById("retry");
      const newBtn    = document.getElementById("new-game");
      const toastEl   = document.getElementById("toast");
      const liveEl    = document.getElementById("live");
    
      for (let i = 0; i < 16; i++) cellsEl.appendChild(document.createElement("div"));
    
      let game    = createGame();
      let state   = game.state();
      let shown   = state.board.map(function (r) { return r.slice(); });
      let tiles   = new Map();   // "r,c" -> tile element
      let victims = [];          // merged-away elements awaiting removal
      let winShown = false;
      let toastTimers = [];
    
      const BEST_KEY = "ink2048.best";
      let best = 0;
      try { best = Math.max(0, parseInt(localStorage.getItem(BEST_KEY), 10) || 0); } catch (e) {}
      bestEl.textContent = best;
    
      const key = function (r, c) { return r + "," + c; };
    
      function pos(el, r, c) {
        el.style.left = "calc(" + c + " * (25% + 0.25 * var(--gap)))";
        el.style.top  = "calc(" + r + " * (25% + 0.25 * var(--gap)))";
      }
    
      function digitClass(v) {
        if (v >= 10000) return " d5";
        if (v >= 1000) return " d4";
        if (v >= 100) return " d3";
        return "";
      }
      function tileClasses(v, extra) {
        return "tile " + (v <= 2048 ? "t" + v : "tmax") + digitClass(v) + (extra ? " " + extra : "");
      }
      function makeTile(r, c, v, extra) {
        const el = document.createElement("div");
        el.className = tileClasses(v, extra);
        el.textContent = v;
        pos(el, r, c);
        tilesEl.appendChild(el);
        return el;
      }
      function clearTiles() {
        tiles.forEach(function (el) { el.remove(); });
        victims.forEach(function (el) { el.remove(); });
        tiles = new Map();
        victims = [];
      }
    
      // Presentation only: trace where each existing tile ends up so slides can be animated.
      function lineCoord(dir, i, j) {
        if (dir === "left")  return [i, j];
        if (dir === "right") return [i, 3 - j];
        if (dir === "up")    return [j, i];
        return [3 - j, i];
      }
      function planMove(board, dir) {
        const moves = [], victims = [], targets = new Set();
        for (let i = 0; i < 4; i++) {
          const seq = [0, 1, 2, 3].map(function (j) { return lineCoord(dir, i, j); });
          const path = seq.filter(function (rc) { return board[rc[0]][rc[1]] !== 0; });
          let out = 0, k = 0;
          while (k < path.length) {
            const v = board[path[k][0]][path[k][1]];
            let value = v, victim = null;
            if (k + 1 < path.length && board[path[k + 1][0]][path[k + 1][1]] === v) {
              value = v * 2;
              victim = path[k + 1];
            }
            const dest = seq[out];
            moves.push({ r: path[k][0], c: path[k][1], tr: dest[0], tc: dest[1], value: value });
            if (victim) victims.push({ r: victim[0], c: victim[1], tr: dest[0], tc: dest[1] });
            targets.add(dest[0] + "," + dest[1]);
            k += victim ? 2 : 1;
            out += 1;
          }
        }
        return { moves: moves, victims: victims, targets: targets };
      }
    
      function animateMove(dir, next) {
        victims.forEach(function (el) { el.remove(); });
        victims = [];
    
        const plan = planMove(shown, dir);
        const fresh = new Map();
    
        plan.moves.forEach(function (m) {
          const el = tiles.get(key(m.r, m.c));
          if (!el) return;
          pos(el, m.tr, m.tc);
          if (m.value !== shown[m.r][m.c]) {
            el.className = tileClasses(m.value, "merge-pop");
            el.textContent = m.value;
            el.addEventListener("animationend", function h(ev) {
              if (ev.animationName === "merge-pop") {
                el.classList.remove("merge-pop");
                el.removeEventListener("animationend", h);
              }
            });
          }
          fresh.set(key(m.tr, m.tc), el);
        });
    
        plan.victims.forEach(function (v) {
          const el = tiles.get(key(v.r, v.c));
          if (!el) return;
          pos(el, v.tr, v.tc);
          victims.push(el);
          setTimeout(function () {
            const i = victims.indexOf(el);
            if (i > -1) victims.splice(i, 1);
            el.remove();
          }, 170);
        });
    
        next.board.forEach(function (row, r) {
          row.forEach(function (v, c) {
            if (v && !plan.targets.has(key(r, c))) {
              const el = makeTile(r, c, v, "spawn");
              el.style.animationDelay = "90ms";
              fresh.set(key(r, c), el);
            }
          });
        });
    
        tiles = fresh;
        shown = next.board.map(function (row) { return row.slice(); });
      }
    
      let scoreRaf = 0;
      function tickScore(from, to) {
        cancelAnimationFrame(scoreRaf);
        const t0 = performance.now(), dur = 260;
        const step = function (t) {
          const p = Math.min(1, (t - t0) / dur);
          scoreEl.textContent = Math.round(from + (to - from) * (1 - Math.pow(1 - p, 3)));
          if (p < 1) scoreRaf = requestAnimationFrame(step);
        };
        scoreRaf = requestAnimationFrame(step);
      }
      function floatGain(amount) {
        const chip = document.createElement("span");
        chip.className = "float-gain";
        chip.textContent = "+" + amount;
        scoreStat.appendChild(chip);
        chip.addEventListener("animationend", function () { chip.remove(); });
      }
    
      function hideToast() {
        toastTimers.forEach(clearTimeout);
        toastTimers = [];
        toastEl.hidden = true;
        toastEl.classList.remove("out");
      }
      function showToast(msg) {
        hideToast();
        toastEl.textContent = msg;
        toastEl.hidden = false;
        toastEl.style.animation = "none";
        void toastEl.offsetWidth;
        toastEl.style.animation = "";
        toastTimers.push(setTimeout(function () {
          toastEl.classList.add("out");
          toastTimers.push(setTimeout(hideToast, 340));
        }, 2600));
      }
    
      function doMove(dir) {
        if (state.over) return;
        const before = state;
        game.move(dir);
        const next = game.state();
        let changed = false;
        for (let r = 0; r < 4 && !changed; r++)
          for (let c = 0; c < 4; c++)
            if (next.board[r][c] !== before.board[r][c]) { changed = true; break; }
        if (!changed) return;
    
        animateMove(dir, next);
    
        boardEl.classList.add("pushed");
        setTimeout(function () { boardEl.classList.remove("pushed"); }, 110);
    
        if (next.score > before.score) {
          tickScore(before.score, next.score);
          floatGain(next.score - before.score);
        } else {
          scoreEl.textContent = next.score;
        }
        if (next.score > best) {
          best = next.score;
          try { localStorage.setItem(BEST_KEY, String(best)); } catch (e) {}
          bestEl.textContent = best;
          bestStat.classList.add("flash");
          setTimeout(function () { bestStat.classList.remove("flash"); }, 700);
        }
    
        state = next;
        boardEl.setAttribute("aria-label", "2048 board. Score " + state.score + ".");
    
        if (!winShown && !next.over &&
            next.board.some(function (row) { return row.some(function (v) { return v >= 2048; }); })) {
          winShown = true;
          showToast("2048 tile! Keep climbing.");
          liveEl.textContent = "You made a 2048 tile. Keep playing.";
        }
        if (next.over) {
          liveEl.textContent = "Game over. No moves left.";
          finalEl.textContent = next.score;
          setTimeout(function () {
            overlayEl.hidden = false;
            retryEl.focus({ preventScroll: true });
          }, 450);
        }
      }
    
      function newGame() {
        game = createGame();
        state = game.state();
        winShown = false;
        overlayEl.hidden = true;
        hideToast();
        scoreEl.textContent = state.score;
        clearTiles();
        let i = 0;
        state.board.forEach(function (row, r) {
          row.forEach(function (v, c) {
            if (v) {
              const el = makeTile(r, c, v, "spawn");
              el.style.animationDelay = (i++ * 34) + "ms";
              tiles.set(key(r, c), el);
            }
          });
        });
        shown = state.board.map(function (row) { return row.slice(); });
        boardEl.setAttribute("aria-label", "2048 board. Score " + state.score + ".");
        liveEl.textContent = "New game. Score " + state.score + ".";
      }
    
      const KEYDIR = {
        arrowup: "up",     w: "up",
        arrowdown: "down", s: "down",
        arrowleft: "left", a: "left",
        arrowright: "right", d: "right"
      };
      window.addEventListener("keydown", function (e) {
        if (e.metaKey || e.ctrlKey || e.altKey) return;
        const dir = KEYDIR[e.key.toLowerCase()];
        if (!dir) return;
        e.preventDefault();
        doMove(dir);
      });
    
      let touchStart = null;
      zoneEl.addEventListener("touchstart", function (e) {
        touchStart = e.touches.length === 1
          ? { x: e.touches[0].clientX, y: e.touches[0].clientY }
          : null;
      }, { passive: true });
      zoneEl.addEventListener("touchend", function (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;
        doMove(Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? "right" : "left")
                                           : (dy > 0 ? "down" : "up"));
      }, { passive: true });
    
      newBtn.addEventListener("click", newGame);
      retryEl.addEventListener("click", newGame);
    
      newGame();
    })();
    </script>
    </body>
    </html>
  • Build SnakeFailed

    16,830 tokens: 497 read, 16,333 written · 12 min 12 s · 22.4 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, 5], [2, 5], [1, 5]], moving right, food at [5, 9], 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</title>
      <style>
        :root {
          --bg: #101614;
          --panel: #1b2421;
          --line: #2a3833;
          --text: #e7f2ee;
          --muted: #9db4ab;
          --snake: #43d17a;
          --head: #a5f3c0;
          --food: #ff5d5d;
        }
    
        * {
          box-sizing: border-box;
        }
    
        body {
          min-height: 100vh;
          margin: 0;
          display: grid;
          place-items: center;
          background: radial-gradient(circle at top, #17231f, var(--bg));
          color: var(--text);
          font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
        }
    
        main {
          width: min(92vw, 540px);
        }
    
        .hud {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 1rem;
          margin-bottom: 0.75rem;
        }
    
        h1 {
          margin: 0;
          font-size: 1.5rem;
          letter-spacing: 0.04em;
        }
    
        .score {
          font-size: 1.1rem;
          color: var(--muted);
        }
    
        .score strong {
          color: var(--text);
        }
    
        button {
          font: inherit;
          border: 1px solid var(--line);
          background: #22302b;
          color: var(--text);
          padding: 0.45rem 0.8rem;
          border-radius: 8px;
          cursor: pointer;
        }
    
        button:hover {
          background: #2a3b35;
        }
    
        .board {
          display: grid;
          grid-template-columns: repeat(10, 1fr);
          grid-template-rows: repeat(10, 1fr);
          gap: 3px;
          width: 100%;
          aspect-ratio: 1;
          padding: 8px;
          background: var(--panel);
          border: 1px solid var(--line);
          border-radius: 12px;
        }
    
        .cell {
          border-radius: 5px;
          background: #212d29;
          box-shadow: inset 0 0 0 1px #293832;
        }
    
        .cell.snake {
          background: var(--snake);
          box-shadow: inset 0 0 0 2px #2a8f55;
        }
    
        .cell.head {
          background: var(--head);
          box-shadow: inset 0 0 0 2px #43d17a;
        }
    
        .cell.food {
          background: var(--food);
          border-radius: 50%;
          box-shadow: inset 0 0 0 2px #b33;
        }
    
        .status {
          margin: 0.75rem 0 0;
          color: var(--muted);
          min-height: 1.4em;
          text-align: center;
        }
      </style>
    </head>
    <body>
      <main>
        <div class="hud">
          <h1>Snake</h1>
          <div class="score">Score: <strong id="score">0</strong></div>
          <button id="restart" type="button">New Game</button>
        </div>
    
        <div id="board" class="board" aria-label="Snake game board"></div>
    
        <p id="status" class="status"></p>
      </main>
    
      <script id="game">
    function createGame() {
      var SIZE = 10;
      var snake = [[3, 5], [2, 5], [1, 5]];
      var food = [5, 9];
      var score = 0;
      var over = false;
      var dir = "right";
    
      var vectors = {
        up: [0, -1],
        down: [0, 1],
        left: [-1, 0],
        right: [1, 0]
      };
    
      var opposites = {
        up: "down",
        down: "up",
        left: "right",
        right: "left"
      };
    
      function cellIn(list, x, y) {
        for (var i = 0; i < list.length; i++) {
          if (list[i][0] === x && list[i][1] === y) {
            return true;
          }
        }
        return false;
      }
    
      function nextFood(headX, headY) {
        var total = SIZE * SIZE;
        var start = (headY * SIZE + headX + 1) % total;
    
        for (var i = 0; i < total; i++) {
          var idx = (start + i) % total;
          var x = idx % SIZE;
          var y = Math.floor(idx / SIZE);
    
          if (!cellIn(snake, x, y)) {
            return [x, y];
          }
        }
    
        return [headX, headY];
      }
    
      function step(nextDir) {
        if (over) {
          return;
        }
    
        if (!vectors[nextDir]) {
          return;
        }
    
        if (nextDir !== opposites[dir]) {
          dir = nextDir;
        }
    
        var vx = vectors[dir][0];
        var vy = vectors[dir][1];
        var head = snake[0];
        var nx = head[0] + vx;
        var ny = head[1] + vy;
    
        if (nx < 0 || nx >= SIZE || ny < 0 || ny >= SIZE) {
          over = true;
          return;
        }
    
        if (nx === food[0] && ny === food[1]) {
          if (cellIn(snake, nx, ny)) {
            over = true;
            return;
          }
    
          snake.unshift([nx, ny]);
          score = score + 1;
    
          var newFood = nextFood(nx, ny);
          food[0] = newFood[0];
          food[1] = newFood[1];
        } else {
          var body = snake.slice(0, -1);
    
          if (cellIn(body, nx, ny)) {
            over = true;
            return;
          }
    
          snake.shift();
          snake.unshift([nx, ny]);
        }
      }
    
      function state() {
        var resultSnake = [];
    
        for (var i = 0; i < snake.length; i++) {
          resultSnake.push([snake[i][0], snake[i][1]]);
        }
    
        return {
          snake: resultSnake,
          food: [food[0], food[1]],
          score: score,
          over: over
        };
      }
    
      return {
        step: step,
        state: state
      };
    }
      </script>
    
      <script>
      (function () {
        var board = document.getElementById("board");
        var scoreEl = document.getElementById("score");
        var statusEl = document.getElementById("status");
        var restartBtn = document.getElementById("restart");
    
        var game = createGame();
        var cells = [];
    
        for (var y = 0; y < 10; y++) {
          for (var x = 0; x < 10; x++) {
            var cell = document.createElement("div");
            cell.className = "cell";
            board.appendChild(cell);
            cells.push(cell);
          }
        }
    
        function render() {
          var s = game.state();
    
          scoreEl.textContent = String(s.score);
          statusEl.textContent = s.over
            ? "Game over. Click New Game to play again."
            : "Use the arrow keys. Each press moves the snake one cell.";
    
          var snakeSet = {};
          for (var i = 0; i < s.snake.length; i++) {
            snakeSet[s.snake[i][1] * 10 + s.snake[i][0]] = true;
          }
    
          var headIndex = s.snake.length ? s.snake[0][1] * 10 + s.snake[0][0] : -1;
          var foodIndex = s.food ? s.food[1] * 10 + s.food[0] : -1;
    
          for (var c = 0; c < cells.length; c++) {
            var cls = "cell";
    
            if (snakeSet[c]) {
              cls += " snake";
            }
    
            if (c === headIndex) {
              cls += " head";
            }
    
            if (c === foodIndex && !snakeSet[c]) {
              cls += " food";
            }
    
            cells[c].className = cls;
          }
        }
    
        function dirForKey(key) {
          if (key === "ArrowUp") return "up";
          if (key === "ArrowDown") return "down";
          if (key === "ArrowLeft") return "left";
          if (key === "ArrowRight") return "right";
          return null;
        }
    
        function onKeydown(event) {
          var dir = dirForKey(event.key);
    
          if (!dir) {
            return;
          }
    
          if (event.repeat) {
            return;
          }
    
          event.preventDefault();
          game.step(dir);
          render();
        }
    
        function onRestart() {
          game = createGame();
          render();
        }
    
        window.addEventListener("keydown", onKeydown);
        restartBtn.addEventListener("click", onRestart);
    
        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.

Scored tasks

Summarizing 5 of 5
  • One-sentence summaryPassed

    1,176 tokens: 142 read, 1,034 written · 1 min 23 s · 23.9 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.
    Starting March 3, Millbrook Public Library will open weekdays at 8 a.m. after 62% requested earlier hours; weekend hours stay unchanged, funded by a state grant for staff.
  • Meeting notes in three bulletsPassed

    615 tokens: 236 read, 379 written · 21 s · 18.8 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 moved to June 12 due slow app store review.
    - Finance approved the $40,000 marketing budget.
    - Priya owns end-to-end testing; Sam hands over test plan by Friday.
  • Review sentiment and complaintPassed

    283 tokens: 162 read, 121 written · 9 s · 14.7 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: faulty charger and slow support
  • Faithful summary of a studyPassed

    3,009 tokens: 177 read, 2,832 written · 2 min 24 s · 19.8 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 randomized trial, 240 adults with chronic migraine took the new drug or placebo for 12 weeks. The drug had 3.1 fewer headache days per month on average, versus 1.9 for placebo. Nausea was reported by 9% of the drug group. Authors call for longer studies before any recommendation.
  • Action items from an email threadPassed

    743 tokens: 256 read, 487 written · 22 s · 23.1 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

    285 tokens: 160 read, 125 written · 6 s · 24.5 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

    480 tokens: 181 read, 299 written · 14 s · 22.5 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

    313 tokens: 140 read, 173 written · 12 s · 14.9 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

    265 tokens: 141 read, 124 written · 9 s · 16 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

    693 tokens: 318 read, 375 written · 26 s · 15.7 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

    227 tokens: 62 read, 165 written · 8 s · 21.8 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

    212 tokens: 84 read, 128 written · 6 s · 22.7 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 is calm
    waves whisper secrets
    will the tide return?
  • Exact JSON shapePassed

    204 tokens: 88 read, 116 written · 5 s · 24.7 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

    201 tokens: 81 read, 120 written · 6 s · 23.8 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

    265 tokens: 88 read, 177 written · 8 s · 23.3 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.
    Solar Glow
Reasoning 5 of 5
  • Unit pricePassed

    288 tokens: 94 read, 194 written · 9 s · 23.6 tok/s

    A shop sells pencils at 2 for $0.40. How much do 12 pencils cost, in dollars? Work it out, then end with a line "ANSWER: <number>".
    2 pencils cost $0.40, so 1 pencil costs $0.20.  
    12 pencils cost 12 × $0.20 = $2.40.
    
    ANSWER: 2.40
  • Days of the weekPassed

    253 tokens: 88 read, 165 written · 7 s · 24.2 tok/s

    Today is Tuesday. What day of the week will it be 291 days from today? Work it out, then end with a line "ANSWER: <day>".
    291 ÷ 7 = 41 remainder 4, so 291 days is the same as 4 days later.
    
    Tuesday + 4 days = Saturday.
    
    ANSWER: Saturday
  • Who owns the catPassed

    214 tokens: 109 read, 105 written · 5 s · 23.1 tok/s

    Dee, Jon and Kim each own exactly one pet: a cat, a dog or a fish, all different. Dee does not own the dog. Jon owns the fish. Who owns the cat? Work it out, then end with a line "ANSWER: <name>".
    Jon owns the fish, so the cat and dog belong to Dee and Kim. Dee does not own the dog, so Dee must own the cat.
    
    ANSWER: Dee
  • Arrival timePassed

    162 tokens: 105 read, 57 written · 3 s · 25.2 tok/s

    A train leaves at 12:50 and the trip takes 1 hours 5 minutes. At what time does it arrive? Work it out, then end with a line "ANSWER: <HH:MM>" in 24-hour time.
    ANSWER: 13:55
  • Filling a tankPassed

    459 tokens: 129 read, 330 written · 14 s · 24.8 tok/s

    A tank holds 120 liters. Pipe A alone fills it in 6 hours, pipe B alone fills it in 4 hours, and a drain alone empties a full tank in 4 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’s rate: \(120 \div 6 = 20\) liters per hour.  
    Pipe B’s rate: \(120 \div 4 = 30\) liters per hour.  
    Drain’s rate: \(120 \div 4 = 30\) liters per hour, but it removes water, so \(-30\) liters per hour.
    
    Net rate with all three open:
    
    \[
    20 + 30 - 30 = 20 \text{ liters per hour}
    \]
    
    Time to fill 120 liters:
    
    \[
    120 \div 20 = 6 \text{ hours}
    \]
    
    ANSWER: 6
Reading code 4 of 4
  • What does it printPassed

    288 tokens: 117 read, 171 written · 8 s · 24.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([23, 1, 33, 35, 12, 14]))
    ANSWER: [35, 33]
  • Trace a loopPassed

    545 tokens: 135 read, 410 written · 17 s · 25 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 % 4 == 0 or i % 5 == 0:
                total += i
        return total
    
    print(g(24))
    The program sums all numbers from 1 to 24 that are divisible by 4 or 5.
    
    - Multiples of 4: 4, 8, 12, 16, 20, 24 → sum = 84
    - Multiples of 5: 5, 10, 15, 20 → sum = 50
    - 20 is counted twice, so subtract it once: 84 + 50 - 20 = 114
    
    ANSWER: 114
  • Find the bugPassed

    246 tokens: 148 read, 98 written · 5 s · 23.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 one less.
    
    ANSWER: 6
  • Run a query in your headPassed

    382 tokens: 186 read, 196 written · 9 s · 25.1 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  | ana      | 20
    2  | cal      | 55
    3  | ana      | 95
    4  | cal      | 30
    5  | ben      | 130
    6  | ben      | 130
    
    Query:
    SELECT customer, SUM(amount) AS total
    FROM orders
    GROUP BY customer
    HAVING SUM(amount) > 100
    ORDER BY total DESC;
    ben,260
    ana,115
Using tools 5 of 5
  • Call one toolPassed

    939 tokens: 783 read, 156 written · 9 s · 23.6 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?
    1. user
      What's the weather in Lisbon right now, in celsius?
    2. assistant
      get_weather({"city":"Lisbon","unit":"celsius"})
    3. tool
      {"city": "Lisbon", "conditions": "sunny", "temperature": 21, "unit": "celsius"}
    4. assistant
      It's currently **sunny** in Lisbon with a temperature of **21°C**. A lovely day out there! ☀️
  • Don't call a tool when none is neededPassed

    483 tokens: 455 read, 28 written · 3 s · 23.4 tok/s

    Passes when No tool call, and the answer is 144.

    What is 12 times 12?
    1. user
      What is 12 times 12?
    2. assistant
      12 times 12 is 144.
  • Two calls for two answersPassed

    1,158 tokens: 906 read, 252 written · 19 s · 16.2 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.
    1. user
      Convert 250 EUR to USD and to JPY.
    2. assistant
      convert_currency({"amount":250,"from":"EUR","to":"USD"})
      convert_currency({"amount":250,"from":"EUR","to":"JPY"})
    3. tool
      {"amount": 270.5, "currency": "USD"}
    4. tool
      {"amount": 40125, "currency": "JPY"}
    5. assistant
      Here are the conversions at today's rates:
      
      - **250 EUR = 270.50 USD**
      - **250 EUR = 40,125 JPY**
  • Chain two toolsPassed

    1,568 tokens: 1,374 read, 194 written · 16 s · 15.8 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?
    1. user
      What is the status of each order for the customer with the email kim@example.com?
    2. assistant
      find_customer({"email":"kim@example.com"})
    3. tool
      {"customer_id": "C-1042", "name": "Kim Park"}
    4. assistant
      get_orders({"customer_id":"C-1042"})
    5. tool
      {"orders": [{"carrier": "DHL", "order_id": "A-77", "status": "shipped"}, {"order_id": "A-80", "status": "processing"}]}
    6. assistant
      Kim Park (kim@example.com) has two orders:
      
      - **A-77** – Shipped (carrier: DHL)
      - **A-80** – Processing
  • Handle a failed call honestlyPassed

    1,061 tokens: 821 read, 240 written · 16 s · 18.8 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.
    1. user
      Book a table for 2 people at Luigi's tonight at 19:00.
    2. assistant
      book_table({"restaurant":"Luigi's","people":2,"time":"19:00"})
    3. tool
      {"available_times": ["18:30", "20:30"], "error": "fully booked at that time"}
    4. assistant
      Unfortunately, Luigi's is fully booked at 19:00. The nearest available times are:
      
      - **18:30**
      - **20:30**
      
      Would you like me to book either of those for 2 people?
Building pages 4 of 5
  • A small websitePassed

    30,229 tokens: 163 read, 30,066 written · 22 min 24 s · 22.4 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.0">
    <meta name="description" content="Crumb & Co. — a small bakery. Three bakes a day, stone-milled flour, 18-hour ferments. Sold till gone.">
    <title>Crumb &amp; Co. — Baked at dawn, sold till gone</title>
    <script>document.documentElement.classList.add('js');</script>
    <style>
      :root{
        --paper:#f5ecdc;
        --paper-2:#eee0c6;
        --card:#fbf5e8;
        --ink:#2b1a10;
        --ink-soft:#6a523c;
        --caramel:#c1682a;
        --butter:#e3a63b;
        --green:#3f7a4e;
        --rose:#b8543f;
        --board:#2b190d;
        --foot:#1e1108;
        --chalk:#f3e7d3;
        --display:"Didot","Bodoni MT","Playfair Display",Georgia,"Times New Roman",serif;
        --body:"Avenir Next",Avenir,"Segoe UI","Helvetica Neue",Helvetica,system-ui,sans-serif;
        --mono:"IBM Plex Mono","SF Mono",SFMono-Regular,Menlo,Consolas,"Courier New",monospace;
      }
      *{box-sizing:border-box}
      html{scroll-behavior:smooth}
      body{
        margin:0;background:var(--paper);color:var(--ink);
        font:400 1.02rem/1.65 var(--body);
        -webkit-font-smoothing:antialiased;overflow-x:hidden;
      }
      /* flour-grain noise over everything */
      body::after{
        content:"";position:fixed;inset:0;pointer-events:none;z-index:90;opacity:.05;
        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)'/></svg>");
      }
      ::selection{background:var(--butter);color:var(--ink)}
      a{color:inherit}
      :focus-visible{outline:2px dashed var(--caramel);outline-offset:3px}
      .wrap{max-width:1120px;margin:0 auto;padding-left:clamp(20px,4vw,48px);padding-right:clamp(20px,4vw,48px)}
      .mono{font-family:var(--mono)}
      section{scroll-margin-top:24px}
    
      /* ============ HEADER ============ */
      .site-head{
        padding:clamp(16px,2.6vw,26px) 0 clamp(30px,4.5vw,50px);
        background-image:radial-gradient(rgba(43,26,16,.16) 1px,transparent 1.4px);
        background-size:26px 26px;
        -webkit-mask-image:linear-gradient(#000 55%,transparent 96%);
        mask-image:linear-gradient(#000 55%,transparent 96%);
      }
      .head-top{
        display:flex;justify-content:space-between;align-items:center;gap:16px;flex-wrap:wrap;
        font:600 .7rem/1.5 var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-soft);
      }
      .bag{
        border:1.5px solid var(--ink);padding:8px 16px;border-radius:99px;background:var(--card);color:var(--ink);
        font:700 .72rem var(--mono);letter-spacing:.1em;display:inline-flex;gap:8px;align-items:center;
        box-shadow:3px 3px 0 rgba(43,26,16,.18);
      }
      #bagCount{display:inline-block;min-width:1ch;text-align:center}
      #bagCount.bump{animation:bump .35s ease}
      @keyframes bump{45%{transform:scale(1.7) rotate(8deg);color:var(--caramel)}}
      .wordmark-row{
        display:flex;align-items:flex-end;justify-content:space-between;gap:clamp(18px,4vw,40px);
        margin-top:clamp(22px,3.6vw,40px);
      }
      .wordmark{
        margin:0;font:600 clamp(3.1rem,10.5vw,7.6rem)/0.98 var(--display);
        letter-spacing:-.02em;color:var(--ink);
      }
      .lm{display:inline-block;overflow:hidden;vertical-align:bottom;padding-bottom:.06em}
      .lm>span{display:inline-block;transform:translateY(112%);animation:rise 1s cubic-bezier(.19,.8,.22,1) forwards}
      .l2>span{animation-delay:.14s}
      @keyframes rise{to{transform:translateY(0)}}
      .amp{
        display:inline-block;font-style:italic;color:var(--caramel);font-size:.82em;margin:0 .06em;
        transform:rotate(-6deg) translateY(-.04em);
      }
      .loaf{width:clamp(150px,22vw,235px);flex:none;color:var(--ink)}
      .steam{stroke:var(--ink-soft);fill:none;stroke-width:3;stroke-linecap:round;opacity:.5;animation:steam 3.4s ease-in-out infinite}
      .s2{animation-delay:1.1s}.s3{animation-delay:2.2s}
      @keyframes steam{0%,100%{transform:translateY(0);opacity:.2}50%{transform:translateY(-6px);opacity:.85}}
      @media(max-width:760px){
        .wordmark-row{flex-direction:column;align-items:flex-start}
        .loaf{width:180px}
      }
      .scramble-line{
        margin:clamp(18px,2.6vw,26px) 0 0;font:700 .72rem var(--mono);letter-spacing:.22em;
        text-transform:uppercase;color:var(--caramel);min-height:1.2em;
      }
      .head-sub{margin:.7rem 0 0;max-width:52ch;color:var(--ink-soft);font-size:1.08rem}
      .site-nav{display:flex;gap:clamp(20px,4vw,44px);margin-top:clamp(24px,3.4vw,38px);flex-wrap:wrap}
      .site-nav a{
        position:relative;text-decoration:none;font:700 .78rem/1 var(--mono);
        letter-spacing:.18em;text-transform:uppercase;padding:8px 2px;
      }
      .site-nav a::after{
        content:"";position:absolute;left:0;bottom:0;width:100%;height:2px;background:var(--caramel);
        transform:scaleX(0);transform-origin:left;transition:transform .35s cubic-bezier(.2,.7,.2,1);
      }
      .site-nav a:hover::after{transform:scaleX(1)}
    
      /* ============ TICKER ============ */
      .ticker{background:var(--ink);color:var(--paper);overflow:hidden}
      .ticker-track{display:flex;width:max-content;animation:tick 32s linear infinite}
      .ticker-track span{
        font:700 .68rem/1 var(--mono);letter-spacing:.26em;text-transform:uppercase;
        padding:11px 0;white-space:nowrap;
      }
      @keyframes tick{to{transform:translateX(-50%)}}
    
      /* ============ SECTION HEADS ============ */
      .sec{padding:clamp(60px,8.5vw,104px) 0}
      .eyebrow{
        font:700 .72rem var(--mono);letter-spacing:.24em;text-transform:uppercase;color:var(--caramel);
        display:flex;align-items:center;gap:12px;margin:0 0 14px;
      }
      .eyebrow::before{content:"";width:28px;height:2px;background:currentColor;flex:none}
      h2{
        margin:0;font:600 clamp(2.1rem,4.6vw,3.4rem)/1.06 var(--display);
        letter-spacing:-.015em;
      }
      h2 em{font-style:italic;color:var(--caramel)}
      .sec-head{
        display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:20px;
        margin-bottom:clamp(30px,4.5vw,52px);
      }
      .sec-head p{margin:.8rem 0 0;color:var(--ink-soft);max-width:44ch}
      .bake-chip{
        display:inline-flex;align-items:center;gap:10px;border:1.5px dashed var(--ink);
        padding:12px 16px;font:700 .72rem var(--mono);letter-spacing:.1em;color:var(--ink-soft);
        background:var(--card);
      }
      .bake-chip b{font-size:.98rem;color:var(--caramel);letter-spacing:.06em}
      .oven-dot{width:9px;height:9px;border-radius:50%;background:var(--caramel);animation:oven 1.6s ease-in-out infinite}
      @keyframes oven{50%{box-shadow:0 0 0 6px rgba(193,104,42,.18)}}
    
      /* ============ MENU ============ */
      .menu-sec{position:relative}
      .stamp{
        position:absolute;top:clamp(14px,2vw,26px);right:4%;width:112px;color:var(--caramel);
        animation:spin 26s linear infinite;pointer-events:none;
      }
      @keyframes spin{to{transform:rotate(360deg)}}
      .stamp-text{font:700 10px var(--mono);letter-spacing:1.6px;fill:currentColor}
      .stamp-center{font-size:26px;fill:currentColor}
      @media(max-width:600px){.stamp{display:none}}
      .receipt{
        background:var(--card);border:1.5px solid var(--ink);
        box-shadow:9px 11px 0 rgba(43,26,16,.12);
        padding:clamp(20px,3.4vw,40px) clamp(16px,3vw,36px);
        transform:rotate(-.4deg);position:relative;
      }
      .receipt::before{
        content:"CRUMB & CO — TODAY'S BAKE";position:absolute;top:-11px;left:26px;
        background:var(--paper);padding:0 12px;font:700 .66rem var(--mono);
        letter-spacing:.22em;color:var(--caramel);
      }
      .menu-list{list-style:none;margin:0;padding:0}
      .mi{
        display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
        padding:15px 10px;border-bottom:1.5px dashed rgba(43,26,16,.28);
        transition:background .25s,transform .25s;
      }
      .mi:last-child{border-bottom:0}
      .mi:hover{background:rgba(193,104,42,.08);transform:translateX(5px)}
      .mi-body{flex:1 1 250px;min-width:0}
      .mi-name{display:block;font:600 1.18rem/1.25 var(--display);letter-spacing:.005em}
      .mi-name.soldout{color:var(--ink-soft);text-decoration:line-through;text-decoration-color:var(--rose);text-decoration-thickness:2px}
      .mi-desc{display:block;color:var(--ink-soft);font-size:.92rem;margin-top:3px;max-width:56ch}
      .lead{flex:1;min-width:34px;border-bottom:2px dotted rgba(43,26,16,.45);transform:translateY(-5px)}
      .mi-price{font:700 1rem var(--mono);white-space:nowrap}
      .mi.sold .mi-price{color:var(--rose)}
      .tag{
        font:700 .6rem var(--mono);letter-spacing:.14em;text-transform:uppercase;
        border:1px solid currentColor;border-radius:99px;padding:2.5px 8px;margin-left:9px;
        vertical-align:middle;white-space:nowrap;
      }
      .tag--star{color:var(--caramel)}
      .tag--vegan{color:var(--green)}
      .tag--sold{color:var(--rose)}
      .add{
        width:30px;height:30px;flex:none;border-radius:50%;border:1.5px solid var(--ink);
        background:transparent;color:var(--ink);font:700 1.05rem/1 var(--mono);cursor:pointer;
        transition:background .25s,border-color .25s,color .25s,transform .25s;
      }
      .add:hover{background:var(--caramel);border-color:var(--caramel);color:var(--paper);transform:rotate(90deg)}
      .add:active{transform:scale(.8)}
      .add[disabled]{opacity:.3;cursor:not-allowed}
      .add[disabled]:hover{background:transparent;border-color:var(--ink);color:var(--ink);transform:none}
      .fine{margin:22px 4px 0;font-size:.85rem;color:var(--ink-soft)}
      .fine b{font-family:var(--mono);color:var(--caramel)}
    
      /* ============ SCALLOPS ============ */
      .scallop{
        --c:var(--paper);height:22px;
        background:radial-gradient(circle at 11px -5px,var(--c) 11px,transparent 12px);
        background-size:22px 22px;background-repeat:repeat-x;
      }
    
      /* ============ STORY ============ */
      .story-sec{background:var(--paper-2)}
      .story-grid{display:grid;grid-template-columns:1fr 1.15fr;gap:clamp(34px,6vw,80px);align-items:start}
      .story-left{position:sticky;top:88px}
      .stats{list-style:none;margin:30px 0 0;padding:0;display:grid;gap:12px;max-width:380px}
      .stats li{
        display:flex;justify-content:space-between;gap:16px;align-items:baseline;
        border-bottom:1.5px dashed rgba(43,26,16,.35);padding-bottom:8px;font-size:.95rem;color:var(--ink-soft);
      }
      .stats b{font:700 .95rem var(--mono);color:var(--ink);white-space:nowrap}
      .step{
        display:grid;grid-template-columns:auto 1fr;gap:20px;align-items:start;
        background:var(--card);border:1.5px solid var(--ink);box-shadow:6px 7px 0 rgba(43,26,16,.1);
        padding:24px 26px;margin-bottom:22px;transition:transform .3s,box-shadow .3s;
      }
      .step:hover{transform:translate(-3px,-3px);box-shadow:10px 11px 0 rgba(43,26,16,.14)}
      .step:nth-child(even){transform:rotate(.5deg)}
      .step:nth-child(even):hover{transform:rotate(.5deg) translate(-3px,-3px)}
      .step .num{font:600 2.6rem/1 var(--display);color:var(--caramel)}
      .step h3{margin:0 0 6px;font:600 1.25rem var(--display)}
      .step p{margin:0;color:var(--ink-soft);font-size:.95rem}
      .timeline{
        display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:44px;
        border-top:2px solid var(--ink);padding-top:24px;
      }
      .tl-item .year{display:block;font:700 1rem var(--mono);color:var(--caramel);margin-bottom:4px}
      .tl-item p{margin:0;font-size:.88rem;color:var(--ink-soft)}
      @media(max-width:900px){
        .story-grid{grid-template-columns:1fr}
        .story-left{position:static}
        .timeline{grid-template-columns:repeat(2,1fr)}
      }
    
      /* ============ BAKE BOARD ============ */
      .board-sec{background:var(--board);color:var(--chalk);padding-bottom:clamp(70px,9vw,110px)}
      .board-sec .eyebrow{color:var(--butter)}
      .board-sec h2 em{color:var(--butter)}
      .board-sec .sec-head p{color:rgba(243,231,211,.7)}
      .board-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;padding-bottom:6px}
      .board-grid{min-width:780px;display:grid;grid-template-columns:repeat(7,1fr);gap:12px}
      .day{
        border:1px dashed rgba(243,231,211,.32);background:rgba(255,255,255,.025);
        padding:16px 14px;transition:transform .3s,background .3s;
      }
      .day:hover{transform:translateY(-4px);background:rgba(255,255,255,.05)}
      .day h3{
        margin:0 0 12px;font:700 .7rem var(--mono);letter-spacing:.18em;
        text-transform:uppercase;color:rgba(243,231,211,.65);
      }
      .day ul{list-style:none;margin:0;padding:0}
      .day li{
        font-size:.86rem;padding:5px 0;border-bottom:1px dotted rgba(243,231,211,.22);color:rgba(243,231,211,.88);
      }
      .day li:last-child{border-bottom:0}
      .day--today{border:1.5px solid var(--butter);background:rgba(227,166,59,.12)}
      .day--today h3{color:var(--butter)}
      .day-flag{
        background:var(--butter);color:var(--ink);font:700 .58rem var(--mono);letter-spacing:.14em;
        text-transform:uppercase;padding:2.5px 8px;border-radius:99px;margin-left:8px;vertical-align:middle;
      }
      .board-quote{
        margin:clamp(40px,6vw,64px) auto 0;max-width:640px;text-align:center;
      }
      .board-quote blockquote{margin:0;font:italic 600 clamp(1.25rem,2.4vw,1.7rem)/1.4 var(--display)}
      .board-quote cite{display:block;margin-top:14px;font:700 .7rem var(--mono);letter-spacing:.2em;text-transform:uppercase;color:var(--butter);font-style:normal}
    
      /* ============ FOOTER ============ */
      .site-foot{background:var(--foot);color:#efe3cd;padding-bottom:28px}
      .foot-grid{
        display:grid;grid-template-columns:1.15fr 1fr 1fr;gap:clamp(30px,5vw,60px);
        padding-top:clamp(44px,6vw,70px);
      }
      .foot-title{margin:0 0 16px;font:600 clamp(1.9rem,3.4vw,2.6rem)/1 var(--display)}
      .foot-title em{font-style:italic;color:var(--butter)}
      .foot-col p{margin:.5rem 0;color:rgba(239,227,205,.78);font-size:.95rem}
      .foot-h{
        margin:0 0 16px;font:700 .7rem var(--mono);letter-spacing:.24em;
        text-transform:uppercase;color:var(--butter);
      }
      .hours{margin:0;display:grid;gap:11px}
      .hours div{
        display:flex;justify-content:space-between;gap:14px;align-items:baseline;
        border-bottom:1px dashed rgba(243,231,211,.28);padding-bottom:9px;
      }
      .hours dt{font-weight:600;font-size:.95rem}
      .hours dd{margin:0;font:600 .92rem var(--mono);color:var(--butter);white-space:nowrap}
      .hours-note{margin-top:16px !important;font:600 .7rem var(--mono) !important;letter-spacing:.12em;color:rgba(239,227,205,.55) !important;text-transform:uppercase}
      .open-status{
        margin-top:22px !important;display:flex;align-items:center;gap:10px;
        font:700 .78rem var(--mono);letter-spacing:.06em;color:#efe3cd;
        border:1px dashed rgba(243,231,211,.4);padding:11px 14px;width:fit-content;
      }
      .dot{width:10px;height:10px;border-radius:50%;flex:none;background:var(--rose)}
      .dot.open{background:var(--green);animation:pulse 2s ease-in-out infinite}
      @keyframes pulse{50%{box-shadow:0 0 0 6px rgba(63,122,78,.2)}}
      .foot-bottom{
        display:flex;justify-content:space-between;gap:14px;flex-wrap:wrap;margin-top:clamp(40px,5vw,60px);
        padding-top:20px;border-top:1px dashed rgba(243,231,211,.25);
        font:600 .68rem var(--mono);letter-spacing:.16em;text-transform:uppercase;color:rgba(239,227,205,.55);
      }
      @media(max-width:820px){.foot-grid{grid-template-columns:1fr}}
    
      /* ============ TOAST ============ */
      .toast{
        position:fixed;left:50%;bottom:28px;transform:translate(-50%,90px);z-index:95;
        background:var(--ink);color:var(--paper);border:1px solid rgba(245,236,220,.25);
        font:700 .78rem var(--mono);letter-spacing:.06em;padding:13px 22px;border-radius:99px;
        opacity:0;transition:transform .45s cubic-bezier(.2,.8,.2,1),opacity .45s;pointer-events:none;
        box-shadow:0 14px 34px rgba(0,0,0,.3);max-width:88vw;text-align:center;
      }
      .toast.show{transform:translate(-50%,0);opacity:1}
    
      /* ============ REVEALS ============ */
      .js .reveal{opacity:0;transform:translateY(28px);transition:opacity .7s ease var(--d,0ms),transform .7s cubic-bezier(.2,.7,.2,1) var(--d,0ms)}
      .js .reveal.in{opacity:1;transform:none}
      .js .reveal-tilt{transform:translateY(28px) rotate(-.4deg)}
      .js .reveal-tilt.in{transform:rotate(-.4deg)}
    
      @media (prefers-reduced-motion: reduce){
        html{scroll-behavior:auto}
        *,*::before,*::after{
          animation-duration:.001s !important;animation-delay:0s !important;
          animation-iteration-count:1 !important;transition-duration:.001s !important;
        }
        .ticker-track{animation:none;transform:none}
        .lm>span{transform:none;animation:none}
        .js .reveal,.js .reveal-tilt{opacity:1;transform:none}
      }
    </style>
    </head>
    <body>
    
    <header class="site-head">
      <div class="wrap">
        <div class="head-top">
          <span>Est. 2012 · 14 Millrace Lane, Old Mill District</span>
          <span class="bag" id="bag" aria-live="polite">Your bag · <b id="bagCount">0</b></span>
        </div>
    
        <div class="wordmark-row">
          <h1 class="wordmark">
            <span class="lm l1"><span>Crumb</span></span><span class="amp">&amp;</span><span class="lm l2"><span>Co.</span></span>
          </h1>
          <svg class="loaf" viewBox="0 0 260 170" fill="none" aria-hidden="true">
            <path class="steam"    d="M98 46c-7-9 7-13 0-24"/>
            <path class="steam s2" d="M136 42c-7-9 7-13 0-24"/>
            <path class="steam s3" d="M174 46c-7-9 7-13 0-24"/>
            <path d="M30 118c0-40 34-66 100-66s100 26 100 66c0 14-10 22-25 22H55c-15 0-25-8-25-22Z"
                  fill="#f0ddba" stroke="currentColor" stroke-width="3.5" stroke-linejoin="round"/>
            <path d="M80 98c9-17 25-27 45-31" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"/>
            <path d="M122 101c9-17 25-27 45-31" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"/>
            <path d="M164 101c8-15 20-24 37-28" stroke="currentColor" stroke-width="3.5" stroke-linecap="round"/>
            <path d="M24 152h212" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-dasharray="1 12"/>
          </svg>
        </div>
    
        <p class="scramble-line" id="scramble" data-text="FLOUR · WATER · SALT · TIME — FIRST BAKE 7:30 AM">FLOUR · WATER · SALT · TIME — FIRST BAKE 7:30 AM</p>
        <p class="head-sub">A small bakery that takes fermentation personally. Three ovens, one mill, and a shelf that is almost never full at noon.</p>
    
        <nav class="site-nav" aria-label="Primary">
          <a href="#menu">Menu</a>
          <a href="#story">Our story</a>
          <a href="#visit">Visit</a>
        </nav>
      </div>
    </header>
    
    <div class="ticker" aria-hidden="true">
      <div class="ticker-track">
        <span>Fresh daily&nbsp;&nbsp;✳&nbsp;&nbsp;Baked at dawn&nbsp;&nbsp;✳&nbsp;&nbsp;Stone-milled flour&nbsp;&nbsp;✳&nbsp;&nbsp;18-hour ferment&nbsp;&nbsp;✳&nbsp;&nbsp;Three ovens, no shortcuts&nbsp;&nbsp;✳&nbsp;&nbsp;Sold till gone&nbsp;&nbsp;✳&nbsp;&nbsp;Fresh daily&nbsp;&nbsp;✳&nbsp;&nbsp;Baked at dawn&nbsp;&nbsp;✳&nbsp;&nbsp;Stone-milled flour&nbsp;&nbsp;✳&nbsp;&nbsp;18-hour ferment&nbsp;&nbsp;✳&nbsp;&nbsp;Three ovens, no shortcuts&nbsp;&nbsp;✳&nbsp;&nbsp;Sold till gone&nbsp;&nbsp;✳&nbsp;&nbsp;</span>
        <span>Fresh daily&nbsp;&nbsp;✳&nbsp;&nbsp;Baked at dawn&nbsp;&nbsp;✳&nbsp;&nbsp;Stone-milled flour&nbsp;&nbsp;✳&nbsp;&nbsp;18-hour ferment&nbsp;&nbsp;✳&nbsp;&nbsp;Three ovens, no shortcuts&nbsp;&nbsp;✳&nbsp;&nbsp;Sold till gone&nbsp;&nbsp;✳&nbsp;&nbsp;Fresh daily&nbsp;&nbsp;✳&nbsp;&nbsp;Baked at dawn&nbsp;&nbsp;✳&nbsp;&nbsp;Stone-milled flour&nbsp;&nbsp;✳&nbsp;&nbsp;18-hour ferment&nbsp;&nbsp;✳&nbsp;&nbsp;Three ovens, no shortcuts&nbsp;&nbsp;✳&nbsp;&nbsp;Sold till gone&nbsp;&nbsp;✳&nbsp;&nbsp;</span>
      </div>
    </div>
    
    <main>
    
      <!-- ================= MENU ================= -->
      <section id="menu" class="sec menu-sec">
        <svg class="stamp" viewBox="0 0 140 140" aria-hidden="true">
          <defs><path id="stampPath" d="M70 70 m -52 0 a52 52 0 1 1 104 0 a52 52 0 1 1 -104 0"/></defs>
          <text class="stamp-text"><textPath href="#stampPath">BAKED DAILY ✳ CRUMB &amp; CO ✳ EST. 2012 ✳</textPath></text>
          <circle cx="70" cy="70" r="31" fill="none" stroke="currentColor" stroke-width="1.5"/>
          <text x="70" y="79" text-anchor="middle" class="stamp-center">✳</text>
        </svg>
    
        <div class="wrap">
          <div class="sec-head">
            <div>
              <p class="eyebrow">The counter</p>
              <h2>This morning's <em>menu</em></h2>
              <p>Everything below left the oven this morning. When a loaf is gone, it's gone — the ovens are already making more.</p>
            </div>
            <div class="bake-chip"><span class="oven-dot" aria-hidden="true"></span> next bake in <b id="bakeTimer">--:--:--</b></div>
          </div>
    
          <div class="receipt reveal reveal-tilt">
            <ul class="menu-list">
              <li class="mi reveal" data-d="60">
                <div class="mi-body">
                  <span class="mi-name">Butter Croissant <span class="tag tag--star">Best seller</span></span>
                  <span class="mi-desc">Sixteen layers of cultured butter, laminated over three patient days.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$4.50</span>
                <button class="add" data-name="Butter Croissant" aria-label="Add Butter Croissant to your bag">+</button>
              </li>
              <li class="mi reveal" data-d="120">
                <div class="mi-body">
                  <span class="mi-name">Country Sourdough <span class="tag tag--star">Half loaf</span></span>
                  <span class="mi-desc">Eighteen-hour cold ferment, blistered crust, wide open crumb.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$8.00</span>
                <button class="add" data-name="Country Sourdough" aria-label="Add Country Sourdough to your bag">+</button>
              </li>
              <li class="mi reveal" data-d="180">
                <div class="mi-body">
                  <span class="mi-name">Baguette Tradition</span>
                  <span class="mi-desc">In at 320°C. Shattering crust outside, cloud inside, no apologies.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$4.00</span>
                <button class="add" data-name="Baguette Tradition" aria-label="Add Baguette Tradition to your bag">+</button>
              </li>
              <li class="mi reveal" data-d="240">
                <div class="mi-body">
                  <span class="mi-name">Morning Bun</span>
                  <span class="mi-desc">Laminated dough, brown butter, orange zest and a scatter of flaky salt.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$5.25</span>
                <button class="add" data-name="Morning Bun" aria-label="Add Morning Bun to your bag">+</button>
              </li>
              <li class="mi reveal" data-d="300">
                <div class="mi-body">
                  <span class="mi-name">Cardamom Knot</span>
                  <span class="mi-desc">Ground cardamom, demerara and a whisper of rose water. Genuinely dangerous.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$4.75</span>
                <button class="add" data-name="Cardamom Knot" aria-label="Add Cardamom Knot to your bag">+</button>
              </li>
              <li class="mi reveal" data-d="360">
                <div class="mi-body">
                  <span class="mi-name">Seeded Rye <span class="tag tag--vegan">V</span></span>
                  <span class="mi-desc">Caraway, flax and sunflower. Keeps for a week, never survives a day.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$8.50</span>
                <button class="add" data-name="Seeded Rye" aria-label="Add Seeded Rye to your bag">+</button>
              </li>
              <li class="mi reveal sold" data-d="420">
                <div class="mi-body">
                  <span class="mi-name"><span class="mi-name-sold mi-name" style="display:inline">Chocolate Babka</span> <span class="tag tag--sold">Sold out · back 3:30</span></span>
                  <span class="mi-desc">Two braided doughs, 60% dark chocolate and salted caramel in the middle.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$12.00</span>
                <button class="add" data-name="Chocolate Babka" disabled aria-label="Chocolate Babka is sold out">—</button>
              </li>
              <li class="mi reveal" data-d="480">
                <div class="mi-body">
                  <span class="mi-name">Seasonal Galette <span class="tag tag--vegan">V</span></span>
                  <span class="mi-desc">Whichever fruit the market is shouting about this morning.</span>
                </div>
                <span class="lead" aria-hidden="true"></span>
                <span class="mi-price">$6.00</span>
                <button class="add" data-name="Seasonal Galette" aria-label="Add Seasonal Galette to your bag">+</button>
              </li>
            </ul>
          </div>
          <p class="fine reveal"><b>*</b> All prices include our unreasonable enthusiasm. Sold out simply means the ovens are already busy making more.</p>
        </div>
      </section>
    
      <!-- ================= STORY ================= -->
      <section id="story" class="story-sec">
        <div class="scallop" style="--c:var(--paper)" aria-hidden="true"></div>
        <div class="wrap sec">
          <div class="story-grid">
            <div class="story-left">
              <p class="eyebrow reveal">Our story</p>
              <h2 class="reveal">Slow by <em>design</em></h2>
              <p class="reveal" style="color:var(--ink-soft);margin-top:18px">
                Crumb &amp; Co. started as a street cart with one kettle and a stubborn idea: bread should
                taste like the week it was made. Fifteen years later the cart is gone, but the idea
                hasn't moved an inch. Everything is mixed by hand before sunrise, rested overnight,
                and scored, baked and cooled under the same brick roof.
              </p>
              <ul class="stats">
                <li class="reveal" data-d="60"><span>Bakes per day</span><b>3</b></li>
                <li class="reveal" data-d="120"><span>Cold ferment</span><b>18 hrs</b></li>
                <li class="reveal" data-d="180"><span>Flour</span><b>Stone-milled</b></li>
                <li class="reveal" data-d="240"><span>Preservatives</span><b>Zero</b></li>
              </ul>
            </div>
    
            <div>
              <div class="step reveal">
                <span class="num" aria-hidden="true">01</span>
                <div>
                  <h3>Mix at 5 a.m.</h3>
                  <p>Flour, water, salt, a fistful of yesterday's starter. Hands in the bowl until the dough starts holding its own shape — no later, no earlier.</p>
                </div>
              </div>
              <div class="step reveal" data-d="80">
                <span class="num" aria-hidden="true">02</span>
                <div>
                  <h3>Rest for 18 hours</h3>
                  <p>Cold, dark, patient. The ferment does the heavy lifting all night: flavour, structure, that faint wild tang that can't be rushed or faked.</p>
                </div>
              </div>
              <div class="step reveal" data-d="160">
                <span class="num" aria-hidden="true">03</span>
                <div>
                  <h3>Score &amp; steam</h3>
                  <p>Three ovens, loaded in order of stubbornness. The score decides how each loaf will bloom — the part that looks accidental and never is.</p>
                </div>
              </div>
              <div class="step reveal" data-d="240">
                <span class="num" aria-hidden="true">04</span>
                <div>
                  <h3>Cool on the wire</h3>
                  <p>Crust sets, crumb finishes baking inside. By the time the doors open, the whole street can tell we've been here all morning.</p>
                </div>
              </div>
    
              <div class="timeline">
                <div class="tl-item reveal" data-d="60"><span class="year">2012</span><p>One cart, one kettle, six croissants a day on Millrace Lane.</p></div>
                <div class="tl-item reveal" data-d="120"><span class="year">2015</span><p>The first brick oven goes up — 900°C and very particular.</p></div>
                <div class="tl-item reveal" data-d="180"><span class="year">2019</span><p>We start our own stone mill. The flour never leaves the block.</p></div>
                <div class="tl-item reveal" data-d="240"><span class="year">Today</span><p>Still three bakes a day. Still sold out by eleven on Saturdays.</p></div>
              </div>
            </div>
          </div>
        </div>
      </section>
    
      <!-- ================= BAKE BOARD ================= -->
      <section id="board" class="board-sec">
        <div class="scallop" style="--c:var(--paper-2)" aria-hidden="true"></div>
        <div class="wrap sec" style="padding-bottom:clamp(20px,3vw,40px)">
          <div class="sec-head">
            <div>
              <p class="eyebrow reveal">The bake board</p>
              <h2 class="reveal">What's in the <em>ovens</em> this week</h2>
              <p class="reveal">Loaves go in 45 minutes before the doors open. Today's column is lit up.</p>
            </div>
          </div>
    
          <div class="board-scroll reveal">
            <div class="board-grid">
              <div class="day" data-day="1"><h3>Mon</h3><ul><li>Country Loaf</li><li>Baguette</li><li>Seeded Rye</li></ul></div>
              <div class="day" data-day="2"><h3>Tue</h3><ul><li>Croissant</li><li>Cardamom Knot</li><li>Seeded Rye</li></ul></div>
              <div class="day" data-day="3"><h3>Wed</h3><ul><li>Country Loaf</li><li>Baguette</li><li>Morning Bun</li></ul></div>
              <div class="day" data-day="4"><h3>Thu</h3><ul><li>Croissant</li><li>Morning Bun</li><li>Chocolate Babka</li></ul></div>
              <div class="day" data-day="5"><h3>Fri</h3><ul><li>Chocolate Babka</li><li>Galette</li><li>Croissant</li><li>Baguette</li></ul></div>
              <div class="day" data-day="6"><h3>Sat</h3><ul><li>Country Loaf</li><li>Baguette</li><li>Galette</li><li>Cardamom Knot</li><li>Croissant</li></ul></div>
              <div class="day" data-day="0"><h3>Sun</h3><ul><li>Country Loaf</li><li>Baguette</li><li>Seasonal Galette</li><li>Croissant</li></ul></div>
            </div>
          </div>
    
          <div class="board-quote reveal">
            <blockquote>“We never sell out on purpose. It simply happens — usually by eleven on a Saturday, right on schedule.”</blockquote>
            <cite>Margaux &amp; Theo — head bakers</cite>
          </div>
        </div>
      </section>
    </main>
    
    <!-- ================= FOOTER ================= -->
    <footer id="visit" class="site-foot">
      <div class="scallop" style="--c:var(--board)" aria-hidden="true"></div>
      <div class="wrap">
        <div class="foot-grid">
          <div class="foot-col reveal">
            <h2 class="foot-title">Come by<span style="color:var(--butter)">.</span></h2>
            <p>14 Millrace Lane<br>Old Mill District<br>Portland, OR 97204</p>
            <p class="mono" style="font-size:.9rem;color:var(--butter)">(503) 555-0114</p>
            <p class="open-status"><span class="dot" id="openDot" aria-hidden="true"></span><span id="openText">Checking the oven…</span></p>
          </div>
          <div class="foot-col reveal" data-d="100">
            <h3 class="foot-h">Opening hours</h3>
            <dl class="hours">
              <div><dt>Monday – Friday</dt><dd>7:00 am – 3:00 pm</dd></div>
              <div><dt>Saturday</dt><dd>8:00 am – 4:00 pm</dd></div>
              <div><dt>Sunday</dt><dd>8:00 am – 1:00 pm</dd></div>
            </dl>
            <p class="hours-note">Oven batches at 7:30 · 12:00 · 3:30</p>
          </div>
          <div class="foot-col reveal" data-d="200">
            <h3 class="foot-h">The fine print</h3>
            <p>Everything is baked the morning you buy it. If the shelf is empty, the ovens are full — come back for the 3:30 bake.</p>
            <p>We take cash and card, and we'll happily wait for your change while a loaf cools.</p>
            <p class="mono" style="font-size:.7rem;letter-spacing:.14em;color:rgba(239,227,205,.55);text-transform:uppercase">No preservatives · No rush</p>
          </div>
        </div>
        <div class="foot-bottom">
          <span>© 2024 Crumb &amp; Co.</span>
          <span>Baked with patience</span>
        </div>
      </div>
    </footer>
    
    <div class="toast" id="toast" role="status" aria-live="polite"></div>
    
    <script>
    (function(){
      "use strict";
      var RM = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    
      /* ---------- scramble-decode line ---------- */
      (function(){
        var el = document.getElementById("scramble");
        var finalText = el.getAttribute("data-text");
        if (RM){ el.textContent = finalText; return; }
        var glyphs = "ABCEHKMNORSTUVZ0123456789✳*·—";
        var start = null, dur = 1400;
        function step(t){
          if (!start) start = t;
          var p = Math.min(1, (t - start) / dur);
          var lock = Math.floor(p * finalText.length);
          var out = "";
          for (var i = 0; i < finalText.length; i++){
            if (i < lock || finalText[i] === " "){ out += finalText[i]; }
            else { out += glyphs[Math.floor(Math.random() * glyphs.length)]; }
          }
          el.textContent = out;
          if (p < 1){ requestAnimationFrame(step); } else { el.textContent = finalText; }
        }
        setTimeout(function(){ requestAnimationFrame(step); }, 350);
      })();
    
      /* ---------- scroll reveals ---------- */
      (function(){
        var els = document.querySelectorAll(".reveal");
        if (RM || !("IntersectionObserver" in window)){
          els.forEach(function(el){ el.classList.add("in"); });
          return;
        }
        var io = new IntersectionObserver(function(entries){
          entries.forEach(function(e){
            if (e.isIntersecting){ e.target.classList.add("in"); io.unobserve(e.target); }
          });
        }, { threshold: 0.12, rootMargin: "0px 0px -6% 0px" });
        els.forEach(function(el){
          var d = el.getAttribute("data-d");
          if (d) el.style.setProperty("--d", d + "ms");
          io.observe(el);
        });
      })();
    
      /* ---------- bag + toast ---------- */
      (function(){
        var count = 0,
            bagCount = document.getElementById("bagCount"),
            toast = document.getElementById("toast"),
            toastTimer = null;
        function showToast(msg){
          toast.textContent = msg;
          toast.classList.add("show");
          clearTimeout(toastTimer);
          toastTimer = setTimeout(function(){ toast.classList.remove("show"); }, 2400);
        }
        document.querySelectorAll(".add").forEach(function(btn){
          btn.addEventListener("click", function(){
            if (btn.disabled) return;
            count++;
            bagCount.textContent = count;
            bagCount.classList.remove("bump");
            void bagCount.offsetWidth;
            bagCount.classList.add("bump");
            showToast(btn.getAttribute("data-name") + " — in the bag. Warm in ~15 min.");
          });
        });
      })();
    
      /* ---------- countdown to next bake ---------- */
      (function(){
        var el = document.getElementById("bakeTimer");
        var bakes = [[7,30],[12,0],[15,30]];
        function pad(n){ return String(n).padStart(2, "0"); }
        function tick(){
          var now = new Date();
          var target = null;
          for (var i = 0; i < bakes.length; i++){
            var t = new Date(now);
            t.setHours(bakes[i][0], bakes[i][1], 0, 0);
            if (t > now){ target = t; break; }
          }
          if (!target){
            target = new Date(now);
            target.setDate(target.getDate() + 1);
            target.setHours(7, 30, 0, 0);
          }
          var s = Math.max(0, Math.floor((target - now) / 1000));
          el.textContent = pad(Math.floor(s/3600)) + ":" + pad(Math.floor(s%3600/60)) + ":" + pad(s%60);
        }
        tick();
        setInterval(tick, 1000);
      })();
    
      /* ---------- highlight today on the bake board ---------- */
      (function(){
        var idx = (new Date().getDay() + 6) % 7; /* DOM order Mon..Sun */
        var day = document.querySelector('.day[data-day="' + String((idx+1)%7).replace(String((idx+1)%7), String((idx+1)%7)) + '"]');
        /* simpler: find the .day at position idx */
        var days = Array.prototype.slice.call(document.querySelectorAll(".board-grid .day"));
        if (days[idx]){
          days[idx].classList.add("day--today");
          var flag = document.createElement("span");
          flag.className = "day-flag";
          flag.textContent = "Today";
          days[idx].querySelector("h3").appendChild(flag);
        }
      })();
    
      /* ---------- open / closed status ---------- */
      (function(){
        var HOURS = [ [480,780], [420,900], [420,900], [420,900], [420,900], [420,900], [480,960] ]; /* Sun..Sat, minutes */
        var dot = document.getElementById("openDot"),
            txt = document.getElementById("openText");
        function fmt(mins){
          var h = Math.floor(mins/60), m = mins % 60, ap = h >= 12 ? "pm" : "am";
          h = h % 12 || 12;
          return (m ? h + ":" + String(m).padStart(2,"0") : h + ":00") + " " + ap;
        }
        function update(){
          var now = new Date(), d = now.getDay(), cur = now.getHours()*60 + now.getMinutes();
          var o = HOURS[d][0], c = HOURS[d][1];
          if (cur >= o && cur < c){
            dot.className = "dot open";
            txt.textContent = "Open now — closes at " + fmt(c);
          } else if (cur < o){
            dot.className = "dot closed";
            txt.textContent = "Closed — doors open at " + fmt(o);
          } else {
            dot.className = "dot closed";
            txt.textContent = "Closed — opens tomorrow at " + fmt(HOURS[(d+1)%7][0]);
          }
        }
        update();
        setInterval(update, 30000);
      })();
    })();
    </script>
    </body>
    </html>
  • A pricing table with a togglePassed

    42,757 tokens: 134 read, 42,623 written · 34 min 34 s · 20.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">
    <meta name="color-scheme" content="light">
    <title>Ledgerline — Pricing, itemized</title>
    <style>
      :root{
        --paper:#F3ECDE;
        --paper2:#EBE0C9;
        --ink:#182B21;
        --ink2:#55655B;
        --line:rgba(24,43,33,.16);
        --line2:rgba(24,43,33,.34);
        --accent:#C2491D;
        --accent-hi:#D15527;
        --green:#1E6E4E;
        --green-hi:#5FBE93;
        --dark:#101C15;
        --cream:#F3ECDE;
        --display:"Didot","Bodoni MT","Playfair Display","Iowan Old Style",Georgia,serif;
        --body:"Avenir Next",Avenir,"Segoe UI","Helvetica Neue",Helvetica,Arial,sans-serif;
        --mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,"Liberation Mono",monospace;
        --ease:cubic-bezier(.22,.9,.24,1);
      }
      *{box-sizing:border-box}
      html{scroll-behavior:smooth; scroll-padding-top:64px}
      body{
        margin:0; color:var(--ink); font-family:var(--body); line-height:1.5;
        background:repeating-linear-gradient(180deg, transparent 0 39px, rgba(24,43,33,.045) 39px 40px), var(--paper);
        -webkit-font-smoothing:antialiased;
      }
      @media (min-width:900px){
        body{background:
          linear-gradient(90deg, transparent 0 96px, rgba(194,73,29,.20) 96px 98px, transparent 98px),
          repeating-linear-gradient(180deg, transparent 0 39px, rgba(24,43,33,.045) 39px 40px),
          var(--paper);}
      }
      /* paper grain */
      body::after{
        content:""; position:fixed; inset:0; z-index:80; pointer-events:none; opacity:.055; 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.85' numOctaves='2' stitchTiles='stitch'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
      }
      ::selection{background:var(--accent); color:var(--cream)}
      :focus-visible{outline:2px dashed var(--accent); outline-offset:3px; border-radius:2px}
      button{font-family:inherit; cursor:pointer}
      .wrap{max-width:1180px; margin:0 auto; padding:0 clamp(20px,4vw,48px)}
      .sr-only{position:absolute; width:1px; height:1px; margin:-1px; padding:0; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0}
    
      /* ── ticker ─────────────────────────────── */
      .ticker{position:sticky; top:0; z-index:60; background:var(--ink); color:var(--cream); overflow:hidden}
      .ticker-track{display:flex; width:max-content; animation:tick 34s linear infinite}
      .ticker:hover .ticker-track{animation-play-state:paused}
      .t-half{display:flex}
      .t-half span{font:600 10.5px/2.6 var(--mono); letter-spacing:.2em; text-transform:uppercase; white-space:nowrap; padding-right:2.6em}
      @keyframes tick{to{transform:translateX(-50%)}}
    
      /* ── masthead ───────────────────────────── */
      .masthead{position:relative; overflow:hidden; padding:clamp(54px,8vw,104px) 0 clamp(44px,6vw,84px)}
      .mast-grid{position:relative; z-index:2; display:grid; grid-template-columns:1.15fr .85fr; gap:clamp(28px,5vw,64px); align-items:end}
      .kicker{display:flex; align-items:center; gap:.9em; margin:0 0 20px; font:600 11px var(--mono); letter-spacing:.24em; text-transform:uppercase; color:var(--accent)}
      .kicker::before{content:""; width:36px; height:2px; background:var(--accent)}
      h1{margin:0 0 .24em; font-family:var(--display); font-weight:400; font-size:clamp(54px,8.6vw,116px); line-height:.95; letter-spacing:-.015em}
      h1 em{font-style:italic; text-decoration-line:underline; text-decoration-style:wavy; text-decoration-color:var(--accent); text-decoration-thickness:2px; text-underline-offset:.14em}
      .mast-sub{margin:0; max-width:52ch; font-size:clamp(16px,1.6vw,18px); color:var(--ink2)}
      .mast-meta{display:flex; flex-wrap:wrap; gap:6px 14px; margin:26px 0 0; padding:0; list-style:none; font:600 11px var(--mono); letter-spacing:.14em; text-transform:uppercase; color:var(--ink2)}
      .mast-meta li:not(:first-child)::before{content:"✱"; color:var(--accent); margin-right:14px}
      .watermark{
        position:absolute; right:-.05em; top:5%; z-index:1; line-height:1; white-space:nowrap; user-select:none; pointer-events:none;
        font-family:var(--display); font-weight:700; font-size:clamp(96px,19vw,250px); letter-spacing:-.02em;
        color:rgba(24,43,33,.05); -webkit-text-stroke:1.5px rgba(24,43,33,.16); will-change:transform;
      }
    
      /* toggle */
      .toggle-card{
        width:100%; max-width:430px; justify-self:end; padding:22px 22px 18px;
        background:var(--paper2); border:2px solid var(--ink); border-radius:10px;
        box-shadow:9px 9px 0 rgba(24,43,33,.10); transform:rotate(-1.2deg);
        transition:transform .5s var(--ease);
      }
      .toggle-card:hover{transform:rotate(0deg)}
      .t-label{margin:0; font:700 11px var(--mono); letter-spacing:.24em; text-transform:uppercase; color:var(--ink)}
      .toggle{
        position:relative; display:grid; grid-template-columns:1fr 1fr; width:100%;
        margin:12px 0 10px; padding:5px; border:2px solid var(--ink); border-radius:999px;
        background:var(--paper); font-family:var(--body);
      }
      .t-knob{
        position:absolute; top:5px; bottom:5px; left:5px; width:calc(50% - 5px);
        background:var(--ink); border-radius:999px;
        transition:transform .55s cubic-bezier(.34,1.35,.4,1);
      }
      body.billing-yearly .t-knob{transform:translateX(100%)}
      .t-opt{position:relative; z-index:1; display:flex; align-items:center; justify-content:center; gap:.55em; padding:11px 6px; font-weight:600; font-size:15px; color:var(--ink2); transition:color .35s}
      body:not(.billing-yearly) .t-mo, body.billing-yearly .t-yr{color:var(--cream)}
      .t-badge{font:700 9.5px var(--mono); letter-spacing:.08em; text-transform:uppercase; background:var(--accent); color:var(--cream); padding:3px 7px; border-radius:999px}
      .toggle-note{margin:0; min-height:3em; font:500 11.5px/1.6 var(--mono); letter-spacing:.02em; color:var(--ink2)}
      .when-yr{display:none}
      body.billing-yearly .when-yr{display:inline}
      body.billing-yearly .when-mo{display:none}
    
      /* ── section heads ──────────────────────── */
      .section-head{display:grid; grid-template-columns:auto 1fr; grid-template-areas:"i h" ". l"; gap:10px 24px; align-items:start; margin-bottom:clamp(30px,4vw,52px)}
      .index{grid-area:i; align-self:start; padding-top:.7em; font:600 12px var(--mono); letter-spacing:.22em; color:var(--accent)}
      .section-head h2{grid-area:h; margin:0; font-family:var(--display); font-weight:400; font-size:clamp(34px,4.6vw,58px); line-height:1.05}
      .section-head .lede{grid-area:l; margin:0; max-width:58ch; font-size:16.5px; color:var(--ink2)}
      .mask{overflow:hidden; padding-bottom:.1em}
      .mask-in{display:inline-block; transform:translateY(115%); transition:transform .9s var(--ease) .08s}
      [data-reveal].in .mask-in{transform:translateY(0)}
    
      [data-reveal]{opacity:0; transform:translateY(26px); transition:opacity .7s ease var(--d,0s), transform .7s var(--ease) var(--d,0s)}
      [data-reveal].in{opacity:1; transform:none}
    
      .rates-note{
        display:flex; justify-content:space-between; flex-wrap:wrap; gap:6px 20px; margin:-14px 0 26px; padding-top:12px;
        border-top:2px solid var(--ink); font:600 11px var(--mono); letter-spacing:.16em; text-transform:uppercase; color:var(--ink2);
      }
      .rates-note strong{color:var(--accent); letter-spacing:.2em}
      .swap{display:inline-block; animation:swapIn .5s var(--ease)}
      @keyframes swapIn{from{opacity:0; transform:translateY(.45em)} to{opacity:1; transform:none}}
    
      /* ── plans ──────────────────────────────── */
      .plans{padding:clamp(46px,7vw,88px) 0}
      .plan-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(18px,2.6vw,30px); align-items:stretch}
      .plan{
        position:relative; display:flex; flex-direction:column; gap:16px;
        padding:28px 24px 22px; background:var(--paper); border:2px solid var(--ink); border-radius:10px;
        box-shadow:7px 7px 0 rgba(24,43,33,.10); opacity:0; transform:translateY(30px);
      }
      .plan[data-reveal].in{opacity:1; transform:none; transition:opacity .8s ease var(--d,0s), transform .8s var(--ease) var(--d,0s), box-shadow .5s ease}
      .plan[data-reveal].in:hover{transform:translateY(-8px); box-shadow:11px 14px 0 rgba(24,43,33,.13); transition:transform .4s var(--ease), box-shadow .4s ease}
      .plan.featured{background:var(--dark); color:var(--cream); border-color:var(--dark); box-shadow:8px 12px 0 rgba(24,43,33,.22); padding:34px 26px 26px}
      .plan.featured[data-reveal].in:hover{transform:translateY(-8px); box-shadow:12px 18px 0 rgba(24,43,33,.26)}
      @media (min-width:1000px){.plan.featured{margin-top:-16px}}
      .ribbon{
        position:absolute; top:-15px; right:18px; padding:7px 13px 6px; transform:rotate(2deg);
        background:var(--accent); color:var(--cream); border:2px solid var(--ink);
        font:700 10.5px var(--mono); letter-spacing:.16em; text-transform:uppercase; box-shadow:3px 3px 0 rgba(24,43,33,.25);
      }
      .plan-top h3{margin:0; font-family:var(--display); font-weight:400; font-size:31px}
      .plan-sub{margin:4px 0 0; font:600 11px var(--mono); letter-spacing:.18em; text-transform:uppercase; color:var(--ink2)}
      .plan.featured .plan-sub{color:rgba(243,236,222,.7)}
    
      .price-row{display:flex; align-items:baseline; gap:8px; font-family:var(--mono)}
      .cur{font-size:clamp(22px,2vw,28px); font-weight:600}
      .price{position:relative; font-size:clamp(44px,4.2vw,56px); font-weight:700; line-height:1; letter-spacing:-.02em}
      .per{font-size:13px; letter-spacing:.04em; color:var(--ink2)}
      .plan.featured .per{color:rgba(243,236,222,.7)}
      .digits{display:inline-flex; white-space:nowrap}
      .slot{display:inline-block; width:1ch; height:1em; overflow:hidden; transition:width .45s var(--ease), opacity .3s}
      .slot.hid{width:0; opacity:0}
      .reel{display:block; transition:transform .9s cubic-bezier(.16,.84,.28,1)}
      .reel span{display:block; height:1em; line-height:1; text-align:center}
      .digits.no-anim .reel{transition:none}
      .billed{margin:0; min-height:1.8em; font:500 12px/1.6 var(--mono); letter-spacing:.02em; color:var(--ink2)}
      .plan.featured .billed{color:rgba(243,236,222,.68)}
      .save-tag{
        align-self:flex-start; padding:6px 11px; color:var(--accent);
        border:2px dashed var(--accent); border-radius:4px;
        font:700 11px var(--mono); letter-spacing:.14em; text-transform:uppercase;
        opacity:0; transform:rotate(-2deg) scale(.55);
        transition:opacity .4s, transform .45s cubic-bezier(.34,1.56,.64,1);
      }
      body.billing-yearly .save-tag{opacity:1; transform:rotate(-2deg) scale(1)}
      .cta{
        margin-top:auto; width:100%; padding:14px 16px; border-radius:8px;
        border:2px solid var(--ink); background:transparent; color:var(--ink);
        font-weight:600; font-size:15px; letter-spacing:.02em;
        box-shadow:4px 4px 0 rgba(24,43,33,.14); transition:transform .25s var(--ease), box-shadow .25s, background .25s, color .25s;
      }
      .cta:hover{transform:translate(-1px,-2px); box-shadow:6px 7px 0 rgba(24,43,33,.16); background:var(--ink); color:var(--cream)}
      .cta:active{transform:translate(2px,2px); box-shadow:0 0 0 rgba(0,0,0,0)}
      .plan.featured .cta{background:var(--accent); border-color:var(--accent); color:var(--cream); box-shadow:4px 4px 0 rgba(0,0,0,.35)}
      .plan.featured .cta:hover{background:var(--accent-hi)}
      .ledger{list-style:none; margin:4px 0 0; padding:16px 0 0; display:flex; flex-direction:column; gap:10px; border-top:2px dashed var(--line2); font-size:14px}
      .plan.featured .ledger{border-color:rgba(243,236,222,.3)}
      .ledger li{display:flex; align-items:baseline; gap:2px}
      .ledger .k{color:var(--ink2); font-weight:500}
      .plan.featured .ledger .k{color:rgba(243,236,222,.72)}
      .dots{flex:1; min-width:10px; margin:0 .6em .4em; border-bottom:2px dotted var(--line2)}
      .plan.featured .dots{border-color:rgba(243,236,222,.32)}
      .ledger .v{font-family:var(--mono); font-size:12.5px; font-weight:700; letter-spacing:.02em}
      .v.no{opacity:.45}
      .v.check{color:var(--green)}
      .plan.featured .v.check{color:var(--green-hi)}
      .plan-foot{margin-top:12px; padding-top:12px; border-top:1px dashed var(--line2); font:500 10.5px var(--mono); letter-spacing:.06em; opacity:.65}
      .plan.featured .plan-foot{border-color:rgba(243,236,222,.3)}
    
      /* ── matrix ─────────────────────────────── */
      .matrix{padding:clamp(36px,5vw,72px) 0}
      .table-scroll{overflow-x:auto; background:var(--paper); border:2px solid var(--ink); border-radius:10px}
      table{width:100%; min-width:640px; border-collapse:collapse; font-family:var(--mono); font-size:12.5px}
      thead th{padding:14px 18px; background:var(--paper2); border-bottom:2px solid var(--ink); text-align:left; font:700 11px var(--mono); letter-spacing:.14em; text-transform:uppercase}
      thead th:not(:first-child){text-align:center}
      thead th:nth-child(3){color:var(--accent)}
      tbody td{padding:12px 18px; border-bottom:1px dashed var(--line2)}
      tbody tr:last-child td{border-bottom:none}
      tbody td:not(:first-child){text-align:center}
      tbody td:first-child{font-family:var(--body); font-size:14px; font-weight:600}
      tbody td:nth-child(3), thead th:nth-child(3){background:rgba(194,73,29,.05)}
      .yes{color:var(--green); font-weight:700; font-size:15px}
      .no{color:var(--ink2); opacity:.55}
      tbody tr{transition:background .2s}
      tbody tr:hover{background:rgba(24,43,33,.045)}
    
      /* ── add-ons receipt ────────────────────── */
      .addons{display:grid; grid-template-columns:1fr minmax(320px,440px); gap:clamp(28px,5vw,70px); align-items:start; padding:clamp(36px,5vw,72px) 0}
      .addons-copy .lede{margin:0; max-width:46ch; color:var(--ink2); font-size:16.5px}
      .addons-copy p.small{margin:18px 0 0; font:500 11.5px var(--mono); letter-spacing:.06em; color:var(--ink2)}
      .receipt{
        position:relative; padding:28px 24px 34px; background:var(--paper2); border:2px solid var(--ink); border-radius:4px;
        font-family:var(--mono); box-shadow:10px 12px 0 rgba(24,43,33,.10);
        transform:translateY(30px) rotate(1.2deg); opacity:0;
        transition:opacity .8s ease var(--d,0s), transform .8s var(--ease) var(--d,0s);
      }
      .receipt[data-reveal].in{opacity:1; transform:rotate(1.2deg)}
      .receipt[data-reveal].in:hover{transform:rotate(0deg) translateY(-4px); transition:transform .5s var(--ease)}
      .receipt::before{ /* punched top edge */
        content:""; position:absolute; top:-11px; left:22px; right:22px; height:10px;
        background-image:radial-gradient(circle 4.5px at 9px 5px, var(--paper) 4px, transparent 4.6px);
        background-size:22px 10px; background-repeat:repeat-x;
      }
      .receipt::after{ /* torn bottom edge */
        content:""; position:absolute; left:0; right:0; bottom:-13px; height:13px;
        background:linear-gradient(-45deg, var(--paper2) 13px, transparent 0), linear-gradient(45deg, var(--paper2) 13px, transparent 0);
        background-size:26px 100%; background-repeat:repeat-x;
      }
      .r-head{margin:0 0 12px; font-size:12px; font-weight:700; letter-spacing:.2em; text-transform:uppercase}
      .rule{border-top:3px double var(--line2); margin:10px 0 4px}
      .rrow{display:flex; align-items:baseline; gap:2px; padding:10px 0; font-size:13px}
      .rl{min-width:0}
      .rk{font-weight:700}
      .rd{display:block; margin-top:2px; font-size:10.5px; color:var(--ink2)}
      .rv{text-align:right; white-space:nowrap}
      .rv b{font-size:13.5px}
      .ap{margin-left:2px; font-size:10px; color:var(--ink2)}
      .r-note{margin:12px 0 0; font-size:10.5px; letter-spacing:.04em; color:var(--ink2)}
      .barcode{margin-top:18px; height:34px; background:repeating-linear-gradient(90deg, var(--ink) 0 2px, transparent 2px 4px, var(--ink) 4px 7px, transparent 7px 9px, var(--ink) 9px 10px, transparent 10px 15px)}
      .r-code{margin:8px 0 0; font-size:10px; letter-spacing:.3em; color:var(--ink2)}
      .stamp{
        position:absolute; right:18px; top:44px; width:104px; aspect-ratio:1; padding:8px; border-radius:50%;
        border:2px solid var(--accent); outline:2px solid var(--accent); outline-offset:3px;
        display:grid; place-items:center; text-align:center; color:var(--accent);
        font:700 9px/1.5 var(--mono); letter-spacing:.14em; text-transform:uppercase;
        transform:rotate(-12deg); opacity:.85;
      }
    
      /* ── faq ────────────────────────────────── */
      .faq{padding:clamp(40px,6vw,84px) 0}
      .faq-list{max-width:840px}
      details{border-top:2px solid var(--ink)}
      details:last-child{border-bottom:2px solid var(--ink)}
      summary{display:flex; align-items:baseline; gap:18px; padding:18px 4px; cursor:pointer; list-style:none; font-family:var(--display); font-size:clamp(19px,2.2vw,24px); transition:color .25s}
      summary::-webkit-details-marker{display:none}
      summary:hover{color:var(--accent)}
      .q-i{font:600 11px var(--mono); letter-spacing:.15em; color:var(--accent); padding-top:.4em}
      .q-x{margin-left:auto; font-family:var(--body); font-weight:300; font-size:24px; transition:transform .35s var(--ease)}
      details[open] .q-x{transform:rotate(45deg)}
      details p{margin:0; padding:0 4px 22px 42px; max-width:62ch; color:var(--ink2)}
    
      /* ── closing ────────────────────────────── */
      .closing{position:relative; overflow:hidden; margin-top:clamp(30px,5vw,70px); padding:clamp(64px,9vw,130px) 0; background:var(--ink); color:var(--cream)}
      .ghost-star{position:absolute; top:-18%; right:-4%; z-index:0; font-size:44vw; line-height:1; color:rgba(243,236,222,.05); animation:spin 70s linear infinite}
      @keyframes spin{to{transform:rotate(360deg)}}
      .closing .wrap{position:relative; z-index:1}
      .kicker-light{color:var(--cream)}
      .kicker-light::before{background:var(--accent)}
      .closing h2{margin:0 0 .2em; font-family:var(--display); font-weight:400; font-size:clamp(42px,7vw,86px); line-height:1}
      .lede-light{max-width:52ch; color:rgba(243,236,222,.78); font-size:clamp(16px,1.6vw,18px); margin:0}
      .closing-actions{display:flex; align-items:center; flex-wrap:wrap; gap:18px 28px; margin-top:36px}
      .cta-light{width:auto; padding:15px 32px; background:var(--cream); color:var(--ink); border-color:var(--cream); box-shadow:5px 5px 0 rgba(243,236,222,.22)}
      .cta-light:hover{background:#FBF5E7; box-shadow:7px 8px 0 rgba(243,236,222,.26)}
      .closing-note{font:600 10.5px var(--mono); letter-spacing:.14em; text-transform:uppercase; color:rgba(243,236,222,.55)}
    
      footer{background:var(--ink); color:rgba(243,236,222,.6); border-top:1px solid rgba(243,236,222,.2); padding:22px 0}
      .foot{display:flex; justify-content:space-between; align-items:baseline; flex-wrap:wrap; gap:10px 24px; font:600 10.5px var(--mono); letter-spacing:.16em; text-transform:uppercase}
      footer a{color:var(--cream); text-decoration:none; border-bottom:1px dotted rgba(243,236,222,.5); transition:color .2s, border-color .2s}
      footer a:hover{color:var(--accent-hi); border-color:var(--accent-hi)}
    
      /* billing chip */
      .billing-chip{
        position:fixed; right:18px; bottom:18px; z-index:90; display:flex; align-items:center; gap:10px;
        padding:11px 17px; background:var(--ink); color:var(--cream); text-decoration:none; border-radius:999px;
        border:1px solid rgba(243,236,222,.35); box-shadow:5px 6px 0 rgba(24,43,33,.25);
        font:600 11px var(--mono); letter-spacing:.14em; text-transform:uppercase;
        opacity:0; transform:translateY(16px); pointer-events:none; transition:opacity .4s, transform .4s var(--ease), background .2s;
      }
      .billing-chip.show{opacity:1; transform:none; pointer-events:auto}
      .billing-chip:hover{background:#0D1712}
      .chip-dot{width:8px; height:8px; border-radius:50%; background:var(--accent)}
      .chip-sep{opacity:.5}
    
      /* ── responsive ─────────────────────────── */
      @media (max-width:1000px){
        .plan-grid{grid-template-columns:1fr; max-width:560px; margin:0 auto}
        .addons{grid-template-columns:1fr}
        .receipt{max-width:480px}
      }
      @media (max-width:880px){
        .mast-grid{grid-template-columns:1fr; align-items:start}
        .toggle-card{justify-self:start; max-width:100%}
        .watermark{font-size:22vw; top:1%}
        .billing-chip{right:12px; bottom:12px}
      }
    
      /* ── reduced motion ─────────────────────── */
      @media (prefers-reduced-motion: reduce){
        *,*::before,*::after{animation-duration:.001s !important; animation-iteration-count:1 !important; transition-duration:.001s !important}
        html{scroll-behavior:auto}
        .ticker-track,.ghost-star{animation:none !important}
        [data-reveal],.plan,.receipt{opacity:1 !important; transform:none !important; transition:none !important}
        .plan.featured{transform:none}
        .mask-in{transform:none !important}
        .receipt[data-reveal].in, .receipt[data-reveal].in:hover{transform:rotate(0deg)}
        .save-tag{transition:none}
      }
    </style>
    </head>
    <body>
    
      <!-- top ticker -->
      <div class="ticker" aria-hidden="true">
        <div class="ticker-track">
          <div class="t-half">
            <span>Ledgerline</span><span>✱</span><span>Price list Nº 07</span><span>✱</span><span>14-day trial · no card</span><span>✱</span><span>2 months free on yearly</span><span>✱</span><span>Prorated upgrades</span><span>✱</span><span>Cancel any Tuesday</span><span>✱</span>
          </div>
          <div class="t-half">
            <span>Ledgerline</span><span>✱</span><span>Price list Nº 07</span><span>✱</span><span>14-day trial · no card</span><span>✱</span><span>2 months free on yearly</span><span>✱</span><span>Prorated upgrades</span><span>✱</span><span>Cancel any Tuesday</span><span>✱</span>
          </div>
        </div>
      </div>
    
      <!-- masthead -->
      <header class="masthead">
        <div class="watermark" id="watermark" aria-hidden="true">MONTHLY</div>
        <div class="wrap mast-grid">
          <div class="mast-left" data-reveal>
            <p class="kicker">Ledgerline · Price list Nº 07 · Rev. 2025</p>
            <h1>Every cent,<br><em>itemized.</em></h1>
            <p class="mast-sub">Subscription plumbing for people who ship. Three plans, one toggle — and every number on this page answers to it, so nothing up here surprises you down there.</p>
            <ul class="mast-meta">
              <li>14-day trial · no card</li>
              <li>Prorated upgrades</li>
              <li>Cancel any Tuesday</li>
            </ul>
          </div>
          <div class="mast-right" data-reveal style="--d:.12s">
            <div class="toggle-card">
              <p class="t-label">Billed every —</p>
              <button id="billing-toggle" class="toggle" type="button" aria-pressed="false" aria-label="Switch between monthly and yearly billing">
                <span class="t-knob" aria-hidden="true"></span>
                <span class="t-opt t-mo">Monthly</span>
                <span class="t-opt t-yr">Yearly <i class="t-badge">2 mo free</i></span>
              </button>
              <p class="toggle-note">
                <span class="when-mo">Billed monthly — walk away whenever the ledger says stop.</span>
                <span class="when-yr">Billed yearly — twelve months for the price of ten.</span>
              </p>
            </div>
          </div>
        </div>
      </header>
    
      <!-- 01 · plans -->
      <section class="plans">
        <div class="wrap">
          <header class="section-head" data-reveal>
            <span class="index">01</span>
            <h2 class="mask"><span class="mask-in">The three plans</span></h2>
            <p class="lede">Field for the solo shop, Atelier for the crew, Foundry for the org with an auditor nearby. Flip the toggle up top; the numbers below roll to match.</p>
          </header>
    
          <div class="rates-note" data-reveal>
            <span>Showing <strong data-mo="MONTHLY" data-yr="YEARLY">MONTHLY</strong> rates</span>
            <span data-mo="Yearly works out to 12 for the price of 10" data-yr="Monthly is still on the table for every plan">Yearly works out to 12 for the price of 10</span>
          </div>
    
          <div class="plan-grid">
    
            <!-- Field -->
            <article class="plan" data-reveal style="--d:0s">
              <header class="plan-top">
                <h3>Field</h3>
                <p class="plan-sub">The one-person shop</p>
              </header>
              <div class="price-row">
                <span class="cur">$</span>
                <span class="price" data-monthly="12" data-yearly="119">
                  <span class="digits" aria-hidden="true"></span>
                  <span class="sr-only" data-mo="$12 per month" data-yr="$119 per year, billed annually">$12 per month</span>
                </span>
                <span class="per" data-mo="per month" data-yr="per year">per month</span>
              </div>
              <p class="billed"><span data-mo="Billed monthly · cancel any Tuesday." data-yr="Billed once a year · ≈ $9.92/mo.">Billed monthly · cancel any Tuesday.</span></p>
              <span class="save-tag" aria-hidden="true">2 months free</span>
              <button class="cta" type="button">Start Field</button>
              <ul class="ledger">
                <li><span class="k">Seats</span><span class="dots"></span><span class="v">1</span></li>
                <li><span class="k">Projects</span><span class="dots"></span><span class="v">3</span></li>
                <li><span class="k">Billing history</span><span class="dots"></span><span class="v">90 days</span></li>
                <li><span class="k">Exports</span><span class="dots"></span><span class="v">PDF</span></li>
                <li><span class="k">Support</span><span class="dots"></span><span class="v">Email</span></li>
                <li><span class="k">API access</span><span class="dots"></span><span class="v no">—</span></li>
              </ul>
              <footer class="plan-foot">Downgrades to free whenever — your ledger is yours.</footer>
            </article>
    
            <!-- Atelier -->
            <article class="plan featured" data-reveal style="--d:.1s">
              <span class="ribbon">Most booked</span>
              <header class="plan-top">
                <h3>Atelier</h3>
                <p class="plan-sub">Small teams, careful invoices</p>
              </header>
              <div class="price-row">
                <span class="cur">$</span>
                <span class="price" data-monthly="24" data-yearly="239">
                  <span class="digits" aria-hidden="true"></span>
                  <span class="sr-only" data-mo="$24 per month" data-yr="$239 per year, billed annually">$24 per month</span>
                </span>
                <span class="per" data-mo="per month" data-yr="per year">per month</span>
              </div>
              <p class="billed"><span data-mo="Billed monthly · cancel any Tuesday." data-yr="Billed once a year · ≈ $19.92/mo.">Billed monthly · cancel any Tuesday.</span></p>
              <span class="save-tag" aria-hidden="true">2 months free</span>
              <button class="cta" type="button">Start Atelier</button>
              <ul class="ledger">
                <li><span class="k">Seats</span><span class="dots"></span><span class="v">5</span></li>
                <li><span class="k">Projects</span><span class="dots"></span><span class="v">Unlimited</span></li>
                <li><span class="k">Billing history</span><span class="dots"></span><span class="v">2 years</span></li>
                <li><span class="k">Exports</span><span class="dots"></span><span class="v">PDF · CSV</span></li>
                <li><span class="k">Support</span><span class="dots"></span><span class="v">Priority email</span></li>
                <li><span class="k">API access</span><span class="dots"></span><span class="v check">✓</span></li>
              </ul>
              <footer class="plan-foot">92% of ledgers live here. The sensible default.</footer>
            </article>
    
            <!-- Foundry -->
            <article class="plan" data-reveal style="--d:.2s">
              <header class="plan-top">
                <h3>Foundry</h3>
                <p class="plan-sub">Orgs with auditors nearby</p>
              </header>
              <div class="price-row">
                <span class="cur">$</span>
                <span class="price" data-monthly="49" data-yearly="479">
                  <span class="digits" aria-hidden="true"></span>
                  <span class="sr-only" data-mo="$49 per month" data-yr="$479 per year, billed annually">$49 per month</span>
                </span>
                <span class="per" data-mo="per month" data-yr="per year">per month</span>
              </div>
              <p class="billed"><span data-mo="Billed monthly · cancel any Tuesday." data-yr="Billed once a year · ≈ $39.92/mo.">Billed monthly · cancel any Tuesday.</span></p>
              <span class="save-tag" aria-hidden="true">2 months free</span>
              <button class="cta" type="button">Start Foundry</button>
              <ul class="ledger">
                <li><span class="k">Seats</span><span class="dots"></span><span class="v">25</span></li>
                <li><span class="k">Projects</span><span class="dots"></span><span class="v">Unlimited</span></li>
                <li><span class="k">Billing history</span><span class="dots"></span><span class="v">7 years</span></li>
                <li><span class="k">Exports</span><span class="dots"></span><span class="v">PDF · CSV · XLSX</span></li>
                <li><span class="k">Support</span><span class="dots"></span><span class="v">Shared inbox</span></li>
                <li><span class="k">SSO / SAML</span><span class="dots"></span><span class="v check">✓</span></li>
                <li><span class="k">Audit log</span><span class="dots"></span><span class="v check">✓</span></li>
              </ul>
              <footer class="plan-foot">Invoices accept PO numbers. Naturally.</footer>
            </article>
    
          </div>
        </div>
      </section>
    
      <!-- 02 · comparison -->
      <section class="matrix">
        <div class="wrap">
          <header class="section-head" data-reveal>
            <span class="index">02</span>
            <h2 class="mask"><span class="mask-in">Fine print, large type</span></h2>
            <p class="lede">The whole matrix in one glance — no hovering required, no "enterprise" asterisk that means "call us and find out."</p>
          </header>
          <div class="table-scroll" data-reveal>
            <table>
              <thead>
                <tr><th>Line item</th><th>Field</th><th>Atelier</th><th>Foundry</th></tr>
              </thead>
              <tbody>
                <tr><td>Unlimited projects</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td></tr>
                <tr><td>CSV exports</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td></tr>
                <tr><td>API &amp; webhooks</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td></tr>
                <tr><td>Priority support</td><td class="no">—</td><td class="yes">✓</td><td class="yes">✓</td></tr>
                <tr><td>SSO / SAML</td><td class="no">—</td><td class="no">—</td><td class="yes">✓</td></tr>
                <tr><td>Audit log</td><td class="no">—</td><td class="no">—</td><td class="yes">✓</td></tr>
                <tr><td>PO-numbered invoices</td><td class="no">—</td><td class="no">—</td><td class="yes">✓</td></tr>
                <tr><td>Dedicated onboarding</td><td class="no">—</td><td class="no">—</td><td class="yes">✓</td></tr>
              </tbody>
            </table>
          </div>
        </div>
      </section>
    
      <!-- 03 · add-ons -->
      <section class="addons">
        <div class="wrap" style="display:contents">
          <div class="addons-copy" data-reveal>
            <header class="section-head">
              <span class="index">03</span>
              <h2 class="mask"><span class="mask-in">À la carte</span></h2>
            </header>
            <p class="lede">Every add-on is billed with your plan, prorated per line item, and can be dropped the same day. They follow the toggle up top — check the ticket.</p>
            <p class="small">No setup fees · No minimum term · No “contact sales” on any of these.</p>
          </div>
          <aside class="receipt" data-reveal style="--d:.12s" aria-label="Add-on price ticket">
            <span class="stamp" aria-hidden="true">Ledgerline<br>Office of<br>Pricing ✱</span>
            <p class="r-head">Add-ons — ticket</p>
            <div class="rule"></div>
            <div class="rrow">
              <div class="rl"><span class="rk">Vaulted history</span><span class="rd">Records past your plan’s window</span></div>
              <span class="dots"></span>
              <div class="rv"><b data-mo="$4" data-yr="$39">$4</b><span class="ap" data-mo="/mo" data-yr="/yr">/mo</span></div>
            </div>
            <div class="rrow">
              <div class="rl"><span class="rk">White-label invoices</span><span class="rd">Your logo on every PDF</span></div>
              <span class="dots"></span>
              <div class="rv"><b data-mo="$9" data-yr="$89">$9</b><span class="ap" data-mo="/mo" data-yr="/yr">/mo</span></div>
            </div>
            <div class="rrow">
              <div class="rl"><span class="rk">Fast-lane support</span><span class="rd">Four-hour first reply, humans only</span></div>
              <span class="dots"></span>
              <div class="rv"><b data-mo="$15" data-yr="$149">$15</b><span class="ap" data-mo="/mo" data-yr="/yr">/mo</span></div>
            </div>
            <div class="rrow">
              <div class="rl"><span class="rk">EU data residency</span><span class="rd">Stay where your ledger lives</span></div>
              <span class="dots"></span>
              <div class="rv"><b data-mo="$12" data-yr="$119">$12</b><span class="ap" data-mo="/mo" data-yr="/yr">/mo</span></div>
            </div>
            <div class="rule"></div>
            <p class="r-note">Billed with your plan · prorate per line item · drop any time</p>
            <div class="barcode" aria-hidden="true"></div>
            <p class="r-code">7 770114 0007 · REV 2025</p>
          </aside>
        </div>
      </section>
    
      <!-- 04 · faq -->
      <section class="faq">
        <div class="wrap">
          <header class="section-head" data-reveal>
            <span class="index">04</span>
            <h2 class="mask"><span class="mask-in">Asked, answered</span></h2>
            <p class="lede">The five questions that arrive in every onboarding call, answered before the kettle boils.</p>
          </header>
          <div class="faq-list" data-reveal>
            <details>
              <summary><span class="q-i">Q.01</span> Can I switch plans mid-cycle? <span class="q-x" aria-hidden="true">+</span></summary>
              <p>Any time. Upgrades prorate to the day and start immediately; downgrades credit the remainder against your next bill. Nothing is rounded against you — we checked.</p>
            </details>
            <details>
              <summary><span class="q-i">Q.02</span> What exactly are the two free months? <span class="q-x" aria-hidden="true">+</span></summary>
              <p>Yearly costs ten months of the monthly rate: twelve for the price of ten. No math quiz at checkout, and the toggle up top shows you the arithmetic.</p>
            </details>
            <details>
              <summary><span class="q-i">Q.03</span> Is there really no card for the trial? <span class="q-x" aria-hidden="true">+</span></summary>
              <p>Fourteen days of Atelier, no card on file. We ask before billing ever begins; decline gracefully any time and your CSV export link stays alive for ninety days.</p>
            </details>
            <details>
              <summary><span class="q-i">Q.04</span> What happens to my data if I cancel? <span class="q-x" aria-hidden="true">+</span></summary>
              <p>Export everything — CSV and XLSX, all history, all projects — for ninety days after cancellation. Then it is gone, verifiably, and the certificate of deletion is a nice little receipt too.</p>
            </details>
            <details>
              <summary><span class="q-i">Q.05</span> Do you discount for nonprofits or classrooms? <span class="q-x" aria-hidden="true">+</span></summary>
              <p>Fifty percent off any plan, plus a human onboarding call. Write to us with paperwork, or with a very convincing letter. Both work; we keep the letters.</p>
            </details>
          </div>
        </div>
      </section>
    
      <!-- 05 · closing -->
      <section class="closing">
        <span class="ghost-star" aria-hidden="true">✱</span>
        <div class="wrap" data-reveal>
          <p class="kicker kicker-light">05 · Total</p>
          <h2 class="mask"><span class="mask-in">Keep your receipts.</span></h2>
          <p class="lede-light">Start the fourteen-day ledger on Atelier — upgrade, downgrade, or walk away with your CSV. The money was always yours to spend carefully.</p>
          <div class="closing-actions">
            <button class="cta cta-light" type="button">Start the free ledger</button>
            <span class="closing-note">30-day refunds · prorated everything · export forever</span>
          </div>
        </div>
      </section>
    
      <footer>
        <div class="wrap foot">
          <span>Ledgerline ✱ Price list Nº 07</span>
          <span>© 2025 · Updated quarterly, always mid-quarter</span>
          <span><a href="#billing-toggle">Billing</a> · <a href="#plans">Plans</a> · <a href="#plans">Terms</a></span>
        </div>
      </footer>
    
      <a class="billing-chip" href="#billing-toggle" aria-label="Jump to the billing toggle">
        <span class="chip-dot" aria-hidden="true"></span>
        <span data-mo="Monthly" data-yr="Yearly">Monthly</span>
        <span class="chip-sep">·</span>
        switch
      </a>
    
    <script>
    (function(){
      "use strict";
      var prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    
      /* ── odometer price reels ─────────────────────── */
      function setPrice(p, yearly){
        var v = String(yearly ? +p.dataset.yearly : +p.dataset.monthly);
        var max = p._slots.length;
        var hide = max - v.length;
        p._slots.forEach(function(slot, i){
          slot.classList.toggle("hid", i < hide);
          var reel = slot.firstChild;
          var d = (i < hide) ? 0 : (+v[i] || 0);
          reel.style.transitionDelay = prefersReduced ? "0s" : ((max - 1 - i) * 0.06) + "s";
          reel.style.transform = "translateY(" + (-d) + "em)";
        });
      }
    
      document.querySelectorAll(".price[data-monthly]").forEach(function(p){
        var m = +p.dataset.monthly, y = +p.dataset.yearly;
        var max = Math.max(String(m).length, String(y).length);
        var wrap = p.querySelector(".digits") || p;
        for (var i = 0; i < max; i++){
          var slot = document.createElement("span"); slot.className = "slot";
          var reel = document.createElement("span"); reel.className = "reel";
          for (var d = 0; d < 10; d++){
            var n = document.createElement("span"); n.textContent = d; reel.appendChild(n);
          }
          slot.appendChild(reel); wrap.appendChild(slot);
        }
        p._slots = Array.prototype.slice.call(wrap.children);
        setPrice(p, false);
        wrap.classList.add("no-anim");
        requestAnimationFrame(function(){ requestAnimationFrame(function(){ wrap.classList.remove("no-anim"); }); });
      });
    
      /* ── scramble decode ──────────────────────────── */
      var GLYPHS = "#$%&/=?+*";
      function scramble(el, text){
        if (!el) return;
        if (prefersReduced || !el.isConnected){ el.textContent = text; return; }
        if (el._raf) cancelAnimationFrame(el._raf);
        var f = 0, len = text.length;
        (function tick(){
          f++;
          var done = Math.floor(f * 0.55);
          el.textContent = text.split("").map(function(c, i){
            return i < done ? c : GLYPHS[(Math.random() * GLYPHS.length) | 0];
          }).join("");
          if (done < len) el._raf = requestAnimationFrame(tick);
          else { el.textContent = text; el._raf = null; }
        })();
      }
    
      /* ── billing toggle ───────────────────────────── */
      var toggle = document.getElementById("billing-toggle");
      var watermark = document.getElementById("watermark");
      var yearly = false;
    
      toggle.addEventListener("click", function(){
        yearly = !yearly;
        toggle.setAttribute("aria-pressed", String(yearly));
        document.body.classList.toggle("billing-yearly", yearly);
        document.querySelectorAll(".price[data-monthly]").forEach(function(p){ setPrice(p, yearly); });
        document.querySelectorAll("[data-mo]").forEach(function(el){
          var t = yearly ? el.dataset.yr : el.dataset.mo;
          if (el.textContent !== t){
            el.textContent = t;
            if (!prefersReduced){ el.classList.remove("swap"); void el.offsetWidth; el.classList.add("swap"); }
          }
        });
        scramble(watermark, yearly ? "YEARLY" : "MONTHLY");
      });
    
      setTimeout(function(){ scramble(watermark, "MONTHLY"); }, 380);
    
      /* ── cta press ────────────────────────────────── */
      document.querySelectorAll(".cta").forEach(function(b){
        b.addEventListener("click", function(){
          if (b._lock) return;
          var orig = b.textContent;
          b.textContent = "Noted ✎";
          b._lock = true;
          setTimeout(function(){ b.textContent = orig; b._lock = false; }, 1100);
        });
      });
    
      /* ── scroll reveals ───────────────────────────── */
      var io = new IntersectionObserver(function(entries){
        entries.forEach(function(e){
          if (e.isIntersecting){ e.target.classList.add("in"); io.unobserve(e.target); }
        });
      }, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" });
      document.querySelectorAll("[data-reveal]").forEach(function(el){ io.observe(el); });
    
      /* ── floating billing chip ────────────────────── */
      var chip = document.querySelector(".billing-chip");
      var mast = document.querySelector(".masthead");
      if (chip && mast && "IntersectionObserver" in window){
        var io2 = new IntersectionObserver(function(es){
          chip.classList.toggle("show", !es[0].isIntersecting);
        }, { threshold: 0 });
        io2.observe(mast);
      }
    
      /* ── watermark drift ──────────────────────────── */
      if (!prefersReduced && watermark){
        var ticking = false;
        window.addEventListener("scroll", function(){
          if (ticking) return;
          ticking = true;
          requestAnimationFrame(function(){
            var y = window.scrollY;
            if (y < 1400) watermark.style.transform = "translateY(" + (y * 0.22) + "px)";
            ticking = false;
          });
        }, { passive: true });
      }
    })();
    </script>
    </body>
    </html>
  • An accessible sign-up formPassed

    12,496 tokens: 169 read, 12,327 written · 8 min 53 s · 23.2 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>Fieldpass — Sign up</title>
    <style>
      :root{
        --paper:#EDF1E6;
        --card:#F8FBF3;
        --ink:#182019;
        --ink-soft:#54614F;
        --line:#C7D1BE;
        --green:#0E6B4A;
        --green-deep:#0A4A34;
        --pass:#10382A;
        --amber:#F6A81C;
        --amber-ink:#8A5B00;
        --red:#D6482B;
        --display:"Avenir Next Condensed","Arial Narrow","Roboto Condensed","Liberation Sans Narrow","Segoe UI",sans-serif;
        --serif:Georgia,"Times New Roman",serif;
        --mono:ui-monospace,"SF Mono","Cascadia Code",Menlo,Consolas,"Liberation Mono",monospace;
      }
      *{box-sizing:border-box;margin:0;padding:0}
      html{-webkit-text-size-adjust:100%}
      body{
        min-height:100vh;
        font-family:var(--serif);
        color:var(--ink);
        background:
          radial-gradient(1100px 560px at 88% -12%, rgba(246,168,28,.16), transparent 62%),
          radial-gradient(900px 520px at -8% 112%, rgba(14,107,74,.14), transparent 60%),
          var(--paper);
        overflow-x:hidden;
      }
      /* ---------- ambient layers ---------- */
      .grid-bg{
        position:fixed;inset:0;pointer-events:none;z-index:0;
        background:
          repeating-linear-gradient(0deg, transparent 0 34px, rgba(24,32,25,.045) 34px 35px),
          repeating-linear-gradient(90deg, transparent 0 34px, rgba(24,32,25,.045) 34px 35px);
      }
      .ring{
        position:fixed;border-radius:50%;pointer-events:none;z-index:0;
        animation:spin 70s linear infinite;
      }
      .ring-a{width:360px;height:360px;right:-130px;top:-130px;border:2px dashed rgba(14,107,74,.22)}
      .ring-b{width:220px;height:220px;left:-90px;bottom:-70px;border:2px dashed rgba(246,168,28,.35);animation-duration:48s;animation-direction:reverse}
      .speck{position:fixed;z-index:0;pointer-events:none;opacity:.5}
      .speck-1{width:12px;height:12px;background:var(--amber);left:12%;top:22%;animation:drift 9s ease-in-out infinite alternate}
      .speck-2{width:9px;height:9px;border:2px solid var(--green);border-radius:50%;right:18%;top:64%;animation:drift 12s ease-in-out infinite alternate-reverse}
      .speck-3{width:7px;height:7px;background:var(--green);right:9%;top:18%;animation:drift 14s ease-in-out infinite alternate}
      @keyframes spin{to{transform:rotate(360deg)}}
      @keyframes drift{from{transform:translate(0,0)}to{transform:translate(14px,-22px)}}
    
      /* ---------- layout ---------- */
      .wrap{position:relative;z-index:1;max-width:1060px;margin:clamp(18px,4.5vw,60px) auto;padding:0 20px}
      .panel{
        display:grid;grid-template-columns:400px 1fr;
        background:var(--card);
        border:2px solid var(--ink);border-radius:16px;overflow:hidden;
        box-shadow:10px 10px 0 rgba(24,32,25,.14);
        animation:bob 7s ease-in-out infinite;
      }
      @keyframes bob{50%{transform:translateY(-5px)}}
    
      /* ---------- the pass (left) ---------- */
      .pass{
        position:relative;color:#EDF3E6;padding:28px 26px 34px;
        background:
          repeating-linear-gradient(135deg, rgba(255,255,255,.035) 0 12px, transparent 12px 26px),
          var(--pass);
        border-right:2px dashed rgba(248,251,243,.4);
        display:flex;flex-direction:column;
      }
      .notch{position:absolute;width:26px;height:26px;border-radius:50%;background:var(--card);border:2px solid var(--ink)}
      .notch-t{top:-15px;left:100%}
      .notch-b{bottom:-15px;left:100%}
      .brand{display:flex;align-items:center;justify-content:space-between;font-family:var(--mono);font-size:11px;letter-spacing:.28em;color:#BFD3C0}
      .brand b{color:#fff;font-weight:700;letter-spacing:.34em}
      .brand svg{display:block}
      .pass-title{
        margin-top:34px;font-family:var(--display);font-weight:800;text-transform:uppercase;
        font-size:clamp(46px,5.4vw,60px);line-height:.92;letter-spacing:.015em;color:#fff;
      }
      .pass-title span{color:var(--amber)}
      @supports (-webkit-text-stroke:1px #000){
        .pass-title span{color:transparent;-webkit-text-stroke:2px var(--amber)}
      }
      .pass-meta{margin-top:26px;font-family:var(--mono);font-size:11px;letter-spacing:.14em;color:#9DB6A2}
      .pass-meta strong{display:block;margin-top:6px;font-size:26px;letter-spacing:.1em;color:var(--amber);font-weight:700}
      .pass-meta strong.bump{animation:bump .5s cubic-bezier(.2,1.6,.4,1)}
      @keyframes bump{40%{transform:scale(1.22);color:#fff}}
      .barcode{
        position:relative;margin-top:auto;height:46px;overflow:hidden;border-radius:3px;
        background:
          repeating-linear-gradient(90deg,#E9F0E2 0 2px,transparent 2px 6px,#E9F0E2 6px 7px,transparent 7px 12px,#E9F0E2 12px 15px,transparent 15px 17px),
          repeating-linear-gradient(90deg,#E9F0E2 0 1px,transparent 1px 4px);
      }
      .barcode::after{
        content:"";position:absolute;top:0;bottom:0;width:22px;left:-20%;
        background:linear-gradient(90deg,transparent,rgba(246,168,28,.85),transparent);
        animation:scan 3.4s ease-in-out infinite alternate;
      }
      @keyframes scan{to{left:105%}}
      .barcode-no{margin-top:8px;font-family:var(--mono);font-size:10px;letter-spacing:.42em;color:#8FAA94}
      .admit{
        margin-top:22px;text-align:center;font-family:var(--mono);font-size:10px;letter-spacing:.5em;
        color:#9DB6A2;border-top:1px solid rgba(248,251,243,.2);padding-top:14px;
      }
      .stamp{
        position:absolute;left:24px;bottom:70px;padding:9px 16px;border-radius:10px;
        border:3px solid var(--amber);outline:2px solid var(--amber);outline-offset:3px;
        color:var(--amber);font-family:var(--display);font-weight:800;font-size:23px;
        letter-spacing:.14em;text-transform:uppercase;white-space:nowrap;
        opacity:0;transform:rotate(-12deg) scale(2.2);mix-blend-mode:screen;pointer-events:none;
      }
      .panel.approved .stamp{animation:stampIn .55s cubic-bezier(.2,1.5,.4,1) forwards}
      @keyframes stampIn{
        60%{opacity:1;transform:rotate(-12deg) scale(.94)}
        80%{transform:rotate(-12deg) scale(1.06)}
        100%{opacity:1;transform:rotate(-12deg) scale(1)}
      }
      .panel.approved .barcode{filter:brightness(1.15) sepia(.35) saturate(2)}
    
      /* ---------- the form (right) ---------- */
      .form-side{position:relative;padding:clamp(28px,5vw,58px) clamp(24px,5vw,60px)}
      .eyebrow{
        display:flex;align-items:center;gap:12px;
        font-family:var(--mono);font-size:11px;letter-spacing:.26em;color:var(--amber-ink);
      }
      .eyebrow::after{content:"";flex:1;height:2px;background:rgba(24,32,25,.18)}
      h1{
        margin-top:14px;font-family:var(--display);font-weight:800;text-transform:uppercase;
        font-size:clamp(48px,7vw,76px);line-height:.88;letter-spacing:.01em;
      }
      h1 em{font-style:normal;color:var(--green)}
      .lede{margin-top:14px;font-size:15.5px;line-height:1.55;color:var(--ink-soft);max-width:44ch;font-style:italic}
      form{margin-top:34px}
      .field{position:relative;margin-bottom:24px}
      .field::after{
        content:"";position:absolute;left:14px;right:14px;bottom:-3px;height:3px;border-radius:2px;
        background:var(--green);transform:scaleX(0);transform-origin:left;transition:transform .35s ease;
      }
      .field:focus-within::after{transform:scaleX(1)}
      label{
        display:block;margin-bottom:8px;font-family:var(--mono);font-size:11px;font-weight:700;
        letter-spacing:.18em;text-transform:uppercase;color:var(--ink-soft);
      }
      label .idx{color:var(--amber-ink);margin-right:8px}
      input{
        width:100%;padding:13px 15px;font-family:var(--serif);font-size:17px;color:var(--ink);
        background:#fff;border:1.5px solid var(--line);border-radius:10px;
        transition:border-color .2s, box-shadow .2s, background .2s;
      }
      input::placeholder{color:#A7B29D;font-style:italic}
      input:hover{border-color:#9FB096}
      input:focus{outline:none;border-color:var(--green);background:#FDFEFC;box-shadow:0 0 0 4px rgba(14,107,74,.14)}
      input:user-invalid{border-color:var(--red);box-shadow:0 0 0 4px rgba(214,72,43,.12);animation:nudge .3s ease}
      input:disabled{background:#EFF3E9;color:#7C8A78;cursor:not-allowed}
      @keyframes nudge{25%{transform:translateX(-4px)}75%{transform:translateX(4px)}}
    
      .pw-wrap{position:relative}
      .pw-wrap input{padding-right:56px}
      .pw-toggle{
        position:absolute;right:9px;top:50%;transform:translateY(-50%);
        width:34px;height:34px;display:grid;place-items:center;cursor:pointer;
        background:#F2F6EC;border:1.5px solid var(--line);border-radius:8px;color:var(--ink-soft);
        transition:background .2s, color .2s, transform .15s;
      }
      .pw-toggle:hover{background:#E4EBDA;color:var(--green);transform:translateY(-50%) scale(1.06)}
      .pw-toggle:active{transform:translateY(-50%) scale(.94)}
      .pw-toggle:focus-visible{outline:3px solid var(--amber);outline-offset:2px}
      .hidden{display:none}
      .pw-hint{margin-top:7px;font-family:var(--mono);font-size:10px;letter-spacing:.14em;color:#8A957F}
      .meter{display:flex;gap:5px;margin-top:9px}
      .meter span{flex:1;height:5px;border-radius:3px;background:#DDE5D2;transition:background .3s}
      .meter-label{margin-top:6px;font-family:var(--mono);font-size:10px;letter-spacing:.22em;color:#8A957F;min-height:1em}
      .meter[data-score="1"] span:nth-child(-n+1){background:var(--red)}
      .meter[data-score="2"] span:nth-child(-n+2){background:var(--amber)}
      .meter[data-score="3"] span:nth-child(-n+3){background:var(--green)}
      .meter[data-score="4"] span:nth-child(-n+4){background:var(--green-deep)}
    
      #status{
        min-height:1.5em;margin:0 0 18px;font-family:var(--mono);font-size:13px;line-height:1.6;
        color:var(--green-deep);letter-spacing:.02em;
      }
      #status.ok{
        background:#E2F2E6;border:1.5px dashed var(--green);border-radius:10px;padding:11px 14px;
        animation:rise .45s ease;
      }
      @keyframes rise{from{opacity:0;transform:translateY(8px)}}
    
      .btn{
        font-family:var(--display);font-weight:800;text-transform:uppercase;
        font-size:20px;letter-spacing:.1em;color:#F2F7EE;cursor:pointer;
        background:var(--green);border:2px solid var(--ink);border-radius:12px;
        padding:15px 30px;box-shadow:5px 5px 0 var(--ink);
        transition:transform .15s, box-shadow .15s, background .25s;
      }
      .btn:hover:not(:disabled){transform:translate(-2px,-2px);box-shadow:8px 8px 0 var(--ink);background:#107A57}
      .btn:active:not(:disabled){transform:translate(2px,2px);box-shadow:2px 2px 0 var(--ink)}
      .btn:focus-visible{outline:3px solid var(--amber);outline-offset:3px}
      .btn:disabled{background:var(--green-deep);cursor:default;box-shadow:2px 2px 0 var(--ink);opacity:.92}
    
      .fineprint{
        margin-top:22px;font-family:var(--mono);font-size:10px;letter-spacing:.2em;
        text-transform:uppercase;color:#8A957F;
      }
      .fineprint i{font-style:normal;color:var(--amber-ink)}
    
      /* ---------- responsive ---------- */
      @media (max-width:880px){
        .panel{grid-template-columns:1fr}
        .pass{
          border-right:none;border-bottom:2px dashed rgba(248,251,243,.4);
          padding-bottom:26px;min-height:210px;
        }
        .pass-title{margin-top:22px;font-size:44px}
        .notch-t{top:100%;left:16px}
        .notch-b{top:100%;bottom:auto;right:16px}
        .stamp{bottom:34px}
      }
      @media (prefers-reduced-motion:reduce){
        *,*::before,*::after{animation:none !important;transition:none !important}
      }
    </style>
    </head>
    <body>
      <div class="grid-bg" aria-hidden="true"></div>
      <div class="ring ring-a" aria-hidden="true"></div>
      <div class="ring ring-b" aria-hidden="true"></div>
      <div class="speck speck-1" aria-hidden="true"></div>
      <div class="speck speck-2" aria-hidden="true"></div>
      <div class="speck speck-3" aria-hidden="true"></div>
    
      <main class="wrap">
        <section class="panel" id="panel">
    
          <aside class="pass" aria-hidden="true">
            <div class="notch notch-t"></div>
            <div class="notch notch-b"></div>
            <div class="brand">
              <span style="display:flex;align-items:center;gap:10px">
                <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#F6A81C" stroke-width="2">
                  <path d="M3 9a2 2 0 0 0 0 6v3a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-3a2 2 0 0 1 0-6V6a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1z"/>
                  <path d="M14 5v14" stroke-dasharray="2 3"/>
                </svg>
                <b>FIELDPASS</b>
              </span>
              <span>EST. 2019</span>
            </div>
    
            <h2 class="pass-title">Access<br><span>Application</span></h2>
    
            <div class="pass-meta">
              MEMBER Nº
              <strong id="memberNo">04,213</strong>
            </div>
    
            <div class="barcode"></div>
            <div class="barcode-no">FP·2025·04213</div>
    
            <div class="admit">★ ADMIT ONE ★ ADMIT ONE ★</div>
            <div class="stamp">Approved ✓</div>
          </aside>
    
          <div class="form-side">
            <p class="eyebrow">FORM 07 · JOIN THE CLUB</p>
            <h1>Sign <em>up</em></h1>
            <p class="lede">Ten seconds, no spam, no fine-print traps. Your pass stamps itself the moment you're in.</p>
    
            <form id="signup">
              <div class="field">
                <label for="name"><span class="idx">01</span>Full name</label>
                <input type="text" id="name" name="name" placeholder="Ada Fielding" autocomplete="name" required>
              </div>
    
              <div class="field">
                <label for="email"><span class="idx">02</span>Email address</label>
                <input type="email" id="email" name="email" placeholder="you@somewhere.com" autocomplete="email" required>
              </div>
    
              <div class="field">
                <label for="password"><span class="idx">03</span>Password</label>
                <div class="pw-wrap">
                  <input type="password" id="password" name="password" minlength="8" placeholder="At least 8 characters" autocomplete="new-password" required>
                  <button type="button" class="pw-toggle" id="pwToggle" aria-label="Show password" aria-pressed="false">
                    <svg id="iconEye" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                      <path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/>
                    </svg>
                    <svg id="iconEyeOff" class="hidden" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
                      <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19"/>
                      <line x1="1" y1="1" x2="23" y2="23"/>
                    </svg>
                  </button>
                </div>
                <div class="meter" id="meter" data-score="0" aria-hidden="true">
                  <span></span><span></span><span></span><span></span>
                </div>
                <p class="meter-label" id="meterLabel"></p>
                <p class="pw-hint">MIN 8 · MIX CASE, NUMBERS &amp; SYMBOLS FOR MAX</p>
              </div>
    
              <p id="status" role="status"></p>
    
              <button type="submit" class="btn" id="submitBtn">Request access</button>
    
              <p class="fineprint">House rules: <i>show up</i> · <i>be kind</i> · <i>no spoilers</i></p>
            </form>
          </div>
    
        </section>
      </main>
    
    <script>
    (function(){
      var form   = document.getElementById('signup');
      var nameEl = document.getElementById('name');
      var emailEl= document.getElementById('email');
      var pwEl   = document.getElementById('password');
      var status = document.getElementById('status');
      var btn    = document.getElementById('submitBtn');
      var panel  = document.getElementById('panel');
      var noEl   = document.getElementById('memberNo');
      var meter  = document.getElementById('meter');
      var mLabel = document.getElementById('meterLabel');
      var toggle = document.getElementById('pwToggle');
      var eye    = document.getElementById('iconEye');
      var eyeOff = document.getElementById('iconEyeOff');
      var done   = false;
      var nextNo = 4214;
    
      /* --- show / hide password --- */
      toggle.addEventListener('click', function(){
        var show = pwEl.type === 'password';
        pwEl.type = show ? 'text' : 'password';
        eye.classList.toggle('hidden', show);
        eyeOff.classList.toggle('hidden', !show);
        toggle.setAttribute('aria-pressed', show ? 'true' : 'false');
        toggle.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
        if (!done) pwEl.focus();
      });
    
      /* --- live strength meter --- */
      var LABELS = ['','TOO SHORT','SHORT','GOOD','STRONG'];
      function score(p){
        if (!p) return 0;
        var s = 0;
        if (p.length >= 8)  s++;
        if (p.length >= 12) s++;
        if (/[a-z]/.test(p) && /[A-Z]/.test(p)) s++;
        if (/\d/.test(p))   s++;
        if (/[^A-Za-z0-9]/.test(p)) s++;
        return Math.min(4, Math.max(s, 1));
      }
      pwEl.addEventListener('input', function(){
        var s = score(pwEl.value);
        meter.setAttribute('data-score', s);
        mLabel.textContent = s ? 'STRENGTH: ' + LABELS[s] : '';
      });
    
      /* --- submit --- */
      form.addEventListener('submit', function(e){
        e.preventDefault();
        if (done) return;
        if (!form.checkValidity()) { form.reportValidity(); return; }
    
        done = true;
        var nm = nameEl.value.trim();
        var em = emailEl.value.trim();
        var no = String(nextNo).padStart(5, '0');
        no = no.slice(0,2) + ',' + no.slice(2);
    
        status.textContent = '✓ Pass ' + no + ' issued to ' + nm + ' — confirmation is on its way to ' + em + '.';
        status.classList.add('ok');
    
        noEl.textContent = no;
        noEl.classList.add('bump');
        panel.classList.add('approved');
    
        btn.textContent = '✓ Member created';
        btn.disabled = true;
        nameEl.disabled = emailEl.disabled = pwEl.disabled = true;
        toggle.disabled = true;
        meter.setAttribute('data-score', 4);
        mLabel.textContent = 'STRENGTH: LOCKED IN';
      });
    })();
    </script>
    </body>
    </html>
  • A to-do app that remembersPassed

    23,271 tokens: 157 read, 23,114 written · 16 min 54 s · 22.8 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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>To-Do</title>
    <style>
      :root{
        --ink:#1b2330; --paper:#eef1f5; --line:#d9e0ea; --coral:#ff5a3c;
        --gold:#ffc531; --gold2:#ffdd7a; --green:#0f8a63; --muted:#8b95a7;
        --mono:ui-monospace,"SF Mono","Cascadia Mono","Segoe UI Mono",Menlo,Consolas,monospace;
        --sans:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
      }
      *{box-sizing:border-box}
      [hidden]{display:none!important}
      html,body{margin:0}
      body{
        min-height:100vh; font-family:var(--sans); color:var(--ink);
        background:var(--paper); display:flex; justify-content:center;
        padding:52px 18px 70px; position:relative;
      }
      body::before{
        content:""; position:fixed; inset:0; z-index:0; pointer-events:none;
        background-image:radial-gradient(rgba(27,35,48,.12) 1px, transparent 1.4px);
        background-size:22px 22px;
      }
      body::after{
        content:""; position:fixed; inset:-12%; z-index:0; pointer-events:none;
        background:
          radial-gradient(52% 46% at 88% 6%, rgba(255,197,49,.38), transparent 70%),
          radial-gradient(46% 42% at 4% 96%, rgba(15,138,99,.20), transparent 70%),
          radial-gradient(40% 36% at 104% 82%, rgba(255,90,60,.16), transparent 70%);
        animation:drift 16s ease-in-out infinite alternate;
      }
      @keyframes drift{from{transform:translate(-1.5%,-1%)}to{transform:translate(1.5%,1.5%)}}
      ::selection{background:var(--gold); color:var(--ink)}
    
      .app{width:min(620px,100%); position:relative; z-index:1}
    
      .rise{animation:rise .55s cubic-bezier(.22,.9,.3,1) backwards}
      @keyframes rise{from{opacity:0; transform:translateY(16px)}}
      .d1{animation-delay:.05s}.d2{animation-delay:.16s}.d3{animation-delay:.27s}.d4{animation-delay:.42s}
    
      .top{display:flex; justify-content:space-between; align-items:flex-end; gap:22px; margin-bottom:24px}
      h1{margin:0; font-size:clamp(2.5rem,7vw,3.6rem); font-weight:900;
         letter-spacing:-.02em; line-height:.95; text-transform:uppercase}
      .hl{position:relative; z-index:0; display:inline-block}
      .hl::before{
        content:""; position:absolute; z-index:-1; left:-3%; right:-3%; top:56%; bottom:8%;
        background:var(--gold); border-radius:3px;
        transform:skewX(-8deg) scaleX(1); transform-origin:left center;
        animation:swipe .5s .5s cubic-bezier(.6,0,.2,1) backwards;
      }
      @keyframes swipe{from{transform:skewX(-8deg) scaleX(0)}}
      .chip{
        display:inline-block; margin-top:12px; font:700 11px/1 var(--mono); letter-spacing:.14em;
        border:2px solid var(--ink); border-radius:999px; background:#fff; padding:7px 13px;
        box-shadow:3px 3px 0 rgba(27,35,48,.14); transform:rotate(-1.2deg); transition:transform .35s;
      }
      .chip:hover{transform:rotate(0deg) translateY(-2px)}
    
      .mono{font-family:var(--mono)}
      .progress{width:150px; flex:none}
      .prog-top{display:flex; align-items:baseline; justify-content:flex-end; gap:8px}
      #countLabel{font:800 1.15rem/1 var(--mono); display:inline-block}
      #countLabel.tick{animation:tick .35s ease}
      @keyframes tick{40%{transform:scale(1.3)}}
      .prog-top .dim{font:700 10px/1 var(--mono); letter-spacing:.14em; color:var(--muted)}
      .bar{margin-top:8px; height:12px; border:2px solid var(--ink); border-radius:99px; background:#fff; overflow:hidden}
      .fill{
        height:100%; width:0; border-radius:inherit;
        background:repeating-linear-gradient(45deg, var(--gold) 0 7px, var(--gold2) 7px 14px);
        transition:width .5s cubic-bezier(.4,0,.2,1);
      }
    
      .entry{display:flex; gap:12px; margin-bottom:24px}
      #new-task{
        flex:1; min-width:0; height:52px; padding:0 16px; font:500 1rem var(--sans); color:var(--ink);
        border:2px solid var(--ink); border-radius:10px; background:#fff; outline:none;
        box-shadow:4px 4px 0 rgba(27,35,48,.14);
        transition:border-color .2s, box-shadow .2s;
      }
      #new-task::placeholder{font:400 .85rem var(--mono); color:#9aa4b5; letter-spacing:.02em}
      #new-task:focus{border-color:var(--coral); box-shadow:4px 4px 0 rgba(255,90,60,.35)}
      #new-task.shake{animation:shake .4s ease}
      @keyframes shake{20%{transform:translateX(-4px)}40%{transform:translateX(4px)}60%{transform:translateX(-3px)}80%{transform:translateX(3px)}}
      #add{
        flex:none; height:52px; padding:0 22px; font:800 1rem var(--sans); letter-spacing:.03em;
        color:var(--ink); background:var(--gold); border:2px solid var(--ink); border-radius:10px;
        box-shadow:4px 4px 0 var(--ink); cursor:pointer;
        transition:transform .15s, box-shadow .15s, background .2s;
      }
      #add::before{content:"+"; font-weight:900; margin-right:7px}
      #add:hover{background:var(--gold2); transform:translate(-1px,-1px); box-shadow:5px 5px 0 var(--ink)}
      #add:active{transform:translate(3px,3px); box-shadow:1px 1px 0 var(--ink)}
      #add.stamp{animation:stamp .35s ease}
      @keyframes stamp{40%{transform:scale(.93)}70%{transform:scale(1.04)}}
    
      .cardwrap{position:relative}
      .card{
        background:#fff; border:2px solid var(--ink); border-radius:14px; overflow:hidden;
        box-shadow:9px 10px 0 rgba(27,35,48,.13);
        transform:rotate(-.35deg); transition:transform .45s ease;
      }
      .card:hover{transform:rotate(0deg)}
      .sticker{
        position:absolute; top:-15px; right:-9px; width:44px; z-index:2;
        filter:drop-shadow(3px 3px 0 rgba(27,35,48,.18));
        animation:bob 4.5s ease-in-out infinite alternate;
      }
      @keyframes bob{from{transform:rotate(14deg) translateY(0)}to{transform:rotate(26deg) translateY(7px)}}
    
      .sheet{
        background:
          linear-gradient(90deg, transparent 0 44px, rgba(255,90,60,.55) 44px 46px, transparent 46px),
          repeating-linear-gradient(to bottom, transparent 0 35px, var(--line) 35px 36px);
      }
      .row{
        display:grid; grid-template-columns:34px minmax(0,1fr) auto;
        align-items:center; column-gap:14px; height:36px; padding:0 14px 0 12px;
      }
      .head{font:700 10px/1 var(--mono); letter-spacing:.16em; color:#97a1b3; user-select:none}
      #tasks{list-style:none; margin:0; padding:0}
      .task{
        overflow:hidden; animation:taskIn .35s ease backwards;
        transition:height .3s ease, opacity .25s ease, transform .3s ease;
      }
      @keyframes taskIn{from{opacity:0; transform:translateX(-14px) scale(.98)}}
      .task.removing{height:0 !important; opacity:0; transform:translateX(26px) scale(.97)}
      .check{
        width:22px; height:22px; justify-self:start; display:grid; place-items:center; padding:0;
        background:#fff; border:2px solid var(--ink); border-radius:6px; cursor:pointer;
        transition:background .2s, transform .15s;
      }
      .check svg{width:12px; height:10px; display:block}
      .check path{
        fill:none; stroke:#fff; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round;
        stroke-dasharray:14; stroke-dashoffset:14; transition:stroke-dashoffset .25s .05s ease;
      }
      .check:hover{transform:scale(1.12)}
      .check:active{transform:scale(.9)}
      .done .check{background:var(--green)}
      .done .check path{stroke-dashoffset:0}
      .txt{
        font-size:1rem; font-weight:500; color:var(--ink); cursor:pointer;
        white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
        background:linear-gradient(currentColor,currentColor) no-repeat 0 60%/0% 2px;
        transition:background-size .35s ease, color .3s ease;
      }
      .done .txt{color:var(--muted); background-size:100% 2px}
      .del{
        width:26px; height:26px; display:grid; place-items:center;
        background:#fff; border:1.5px solid var(--ink); border-radius:50%; color:var(--ink);
        font:700 15px/1 var(--mono); cursor:pointer; opacity:.3;
        transition:opacity .2s, background .2s, color .2s, transform .25s;
      }
      .task:hover .del, .del:focus-visible{opacity:1}
      .del:hover{background:var(--coral); color:#fff; transform:rotate(90deg) scale(1.1)}
      @media (hover:none){.del{opacity:.75}}
    
      .empty{
        display:none; margin:8px 14px 18px; padding:20px 14px; text-align:center;
        border:2px dashed #b9c3d4; border-radius:10px; background:#fff;
        font:700 11px/1.9 var(--mono); letter-spacing:.12em; color:var(--muted);
      }
      .empty.show{display:block; animation:rise .4s ease backwards}
    
      .foot{
        display:flex; justify-content:space-between; align-items:center; gap:10px;
        border-top:2px solid var(--ink); padding:10px 14px;
        font:700 10px/1.6 var(--mono); letter-spacing:.1em; color:var(--muted);
      }
      #clear{
        background:none; border:none; padding:4px 7px; border-radius:6px; cursor:pointer;
        color:var(--coral); font:800 10px/1.6 var(--mono); letter-spacing:.1em;
        transition:background .2s;
      }
      #clear:hover{background:rgba(255,90,60,.12)}
    
      .meta{margin:20px 4px 0; font:700 10px/1.6 var(--mono); letter-spacing:.14em; color:#9aa4b5}
    
      .sr-only{
        position:absolute; width:1px; height:1px; margin:-1px; padding:0;
        overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
      }
      :focus-visible{outline:3px solid rgba(255,90,60,.55); outline-offset:2px}
    
      @media (max-width:540px){
        .top{flex-direction:column; align-items:flex-start; gap:18px}
        .progress{width:100%}
        .prog-top{justify-content:flex-start}
        #add{padding:0 16px}
        .foot{flex-wrap:wrap}
      }
      @media (prefers-reduced-motion:reduce){
        *,*::before,*::after{animation:none!important; transition:none!important}
      }
    </style>
    </head>
    <body>
      <main class="app">
    
        <header class="top rise d1">
          <div>
            <h1><span class="hl">To-Do</span></h1>
            <span class="chip mono" id="date"></span>
          </div>
          <div class="progress">
            <div class="prog-top">
              <span id="countLabel">0/0</span>
              <span class="dim">DONE</span>
            </div>
            <div class="bar"><div class="fill" id="fill"></div></div>
          </div>
        </header>
    
        <div class="entry rise d2">
          <input id="new-task" type="text" maxlength="200" placeholder="What needs doing?"
                 autocomplete="off" aria-label="New task">
          <button id="add" type="button">ADD</button>
        </div>
    
        <div class="cardwrap rise d3">
          <svg class="sticker" viewBox="0 0 40 40" aria-hidden="true">
            <path d="M20 3 L24.1 14.3 L36.2 14.8 L26.7 22.2 L30 33.8 L20 27
                     L10 33.8 L13.3 22.2 L3.8 14.8 L15.9 14.3 Z"
                  fill="#ffc531" stroke="#1b2330" stroke-width="2" stroke-linejoin="round"/>
          </svg>
          <section class="card">
            <div class="sheet">
              <div class="row head" aria-hidden="true">
                <span>&#10003;</span><span>TASK</span><span>&#10005;</span>
              </div>
              <ul id="tasks" aria-label="Task list"></ul>
              <div class="empty" id="empty">NOTHING ON THE LIST<br>ADD YOUR FIRST TASK ABOVE &uarr;</div>
            </div>
            <div class="foot">
              <span>&#8629; ADD &nbsp;&middot;&nbsp; CLICK TO CHECK &nbsp;&middot;&nbsp; &#215; TO DELETE</span>
              <button id="clear" type="button" hidden></button>
            </div>
          </section>
        </div>
    
        <p class="meta rise d4">STORED IN THIS BROWSER &middot; LOCALSTORAGE KEY &ldquo;TODOS&rdquo;</p>
      </main>
    
      <span class="sr-only" id="live" aria-live="polite"></span>
      <noscript><p class="meta">THIS LIST NEEDS JAVASCRIPT TO SAVE TASKS.</p></noscript>
    
    <script>
    (function(){
      var KEY = 'todos';
      var input   = document.getElementById('new-task'),
          addBtn  = document.getElementById('add'),
          ul      = document.getElementById('tasks'),
          empty   = document.getElementById('empty'),
          fill    = document.getElementById('fill'),
          countEl = document.getElementById('countLabel'),
          clearBtn= document.getElementById('clear'),
          live    = document.getElementById('live'),
          dateEl  = document.getElementById('date');
    
      var tasks = load();
    
      function load(){
        try{
          var raw = localStorage.getItem(KEY);
          var arr = raw ? JSON.parse(raw) : [];
          if(!Array.isArray(arr)) return [];
          return arr
            .filter(function(x){ return x && typeof x.t === 'string' && x.t.trim() !== ''; })
            .map(function(x){ return { t:String(x.t), d:!!x.d }; });
        }catch(e){ return []; }
      }
      function save(){
        try{ localStorage.setItem(KEY, JSON.stringify(tasks)); }catch(e){}
      }
    
      function setCheckState(check, done){
        check.setAttribute('aria-pressed', done ? 'true' : 'false');
        check.setAttribute('aria-label', done ? 'Mark as not done' : 'Mark as done');
      }
    
      function makeLi(t, delay){
        var li = document.createElement('li');
        li.className = 'row task' + (t.d ? ' done' : '');
        li.style.animationDelay = (delay || 0) + 'ms';
    
        var check = document.createElement('button');
        check.type = 'button';
        check.className = 'check';
        setCheckState(check, t.d);
        check.innerHTML = '<svg viewBox="0 0 12 10" aria-hidden="true"><path d="M1.4 5.4 4.3 8.3 10.6 1.8"/></svg>';
    
        var txt = document.createElement('span');
        txt.className = 'txt';
        txt.textContent = t.t;
        txt.title = t.t;
    
        var del = document.createElement('button');
        del.type = 'button';
        del.className = 'del';
        del.innerHTML = '&#215;';
        del.setAttribute('aria-label', 'Delete task: ' + t.t);
    
        li.appendChild(check); li.appendChild(txt); li.appendChild(del);
    
        function doToggle(){
          if (window.getSelection && window.getSelection().toString()) return;
          t.d = !t.d;
          li.classList.toggle('done', t.d);
          setCheckState(check, t.d);
          save(); stats();
        }
        check.addEventListener('click', doToggle);
        txt.addEventListener('click', doToggle);
    
        del.addEventListener('click', function(){
          if (li.dataset.removing) return;
          li.dataset.removing = '1';
          li.style.height = li.offsetHeight + 'px';
          void li.offsetWidth;
          li.classList.add('removing');
          setTimeout(function(){
            if (li.parentNode) li.remove();
            var i = tasks.indexOf(t);
            if (i > -1) tasks.splice(i, 1);
            save(); stats();
          }, 340);
        });
    
        return li;
      }
    
      function collapse(li){
        li.style.height = li.offsetHeight + 'px';
        void li.offsetWidth;
        li.classList.add('removing');
      }
    
      function stats(){
        var total = tasks.length,
            done  = tasks.filter(function(t){ return t.d; }).length,
            left  = total - done;
        countEl.textContent = done + '/' + total;
        fill.style.width = total ? (done / total * 100) + '%' : '0%';
        empty.classList.toggle('show', total === 0);
        if (done > 0){
          clearBtn.hidden = false;
          clearBtn.textContent = 'CLEAR DONE (' + done + ')';
        } else {
          clearBtn.hidden = true;
        }
        live.textContent = total === 0 ? 'All clear' : left + ' task' + (left === 1 ? '' : 's') + ' left';
        countEl.classList.remove('tick');
        void countEl.offsetWidth;
        countEl.classList.add('tick');
      }
    
      function renderAll(){
        ul.innerHTML = '';
        tasks.forEach(function(t, i){
          ul.appendChild(makeLi(t, Math.min(i, 12) * 45));
        });
        stats();
      }
    
      function addTask(){
        var v = input.value.trim();
        if (!v){
          input.classList.remove('shake');
          void input.offsetWidth;
          input.classList.add('shake');
          input.focus();
          return;
        }
        var t = { t: v.slice(0, 200), d: false };
        tasks.push(t);
        ul.appendChild(makeLi(t, 0));
        save(); stats();
        input.value = '';
        input.focus();
        addBtn.classList.remove('stamp');
        void addBtn.offsetWidth;
        addBtn.classList.add('stamp');
      }
    
      addBtn.addEventListener('click', addTask);
      input.addEventListener('keydown', function(e){
        if (e.key === 'Enter') addTask();
      });
    
      clearBtn.addEventListener('click', function(){
        var doneLis = Array.prototype.slice.call(ul.querySelectorAll('.task.done'));
        if (!doneLis.length) return;
        doneLis.forEach(function(li, i){
          setTimeout(function(){
            if (li.isConnected) collapse(li);
          }, i * 70);
        });
        setTimeout(function(){
          tasks = tasks.filter(function(t){ return !t.d; });
          save(); renderAll();
        }, doneLis.length * 70 + 340);
      });
    
      dateEl.textContent = new Date()
        .toLocaleDateString(undefined, { weekday:'long', month:'long', day:'numeric' })
        .toUpperCase();
    
      renderAll();
    })();
    </script>
    </body>
    </html>
  • A bar chart in SVGFailed

    28,523 tokens: 182 read, 28,341 written · 21 min 3 s · 22.5 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>The Daily Grind · Weekly Coffee Ledger</title>
    <style>
      :root{
        --bg:#170f09;
        --board1:#2a1b11; --board2:#221510;
        --line:#4a3320;
        --cream:#f4e7d0;
        --dim:#b3926b;
        --faint:#8a6c4c;
        --acc:#e8a24c;
        --serif:"Iowan Old Style","Palatino Linotype",Palatino,Georgia,serif;
        --mono:"Courier New",ui-monospace,Consolas,Menlo,monospace;
        --sans:system-ui,"Segoe UI",Helvetica,Arial,sans-serif;
        color-scheme:dark;
      }
      *{box-sizing:border-box}
      html,body{margin:0}
      body{
        min-height:100vh;
        display:flex; align-items:center; justify-content:center;
        padding:40px 18px;
        font-family:var(--sans);
        color:var(--cream);
        background-color:var(--bg);
        background-image:
          radial-gradient(900px 520px at 10% -10%, rgba(232,162,76,.10), transparent 60%),
          radial-gradient(760px 540px at 112% 116%, rgba(150,70,25,.22), transparent 62%),
          radial-gradient(rgba(244,231,208,.03) 1px, transparent 1.4px);
        background-size:auto,auto,26px 26px;
        overflow-x:hidden;
      }
    
      /* drifting beans */
      .bean{position:fixed; z-index:0; pointer-events:none; opacity:.12;
        animation:drift ease-in-out infinite alternate}
      .bean svg{display:block; width:100%; height:auto}
      .b1{top:7%;  left:5%;   width:60px; animation-duration:15s}
      .b2{top:16%; right:6%;  width:44px; animation-duration:12s; animation-delay:-4s}
      .b3{bottom:10%; left:10%; width:52px; animation-duration:17s; animation-delay:-8s}
      .b4{bottom:6%; right:14%; width:70px; animation-duration:13s; animation-delay:-2s}
      .b5{top:52%; left:2%;  width:36px; animation-duration:19s; animation-delay:-11s}
      @keyframes drift{
        from{transform:translateY(-14px) rotate(-16deg)}
        to{transform:translateY(16px) rotate(12deg)}
      }
    
      .wrap{width:min(720px,100%); position:relative; z-index:1}
    
      /* header */
      .topbar{display:flex; justify-content:space-between; align-items:flex-end;
        gap:16px; flex-wrap:wrap; margin-bottom:20px;
        animation:fadeDown .7s ease both}
      .brand{display:flex; align-items:center; gap:14px}
      .cup{width:46px; height:46px; flex:none}
      .cup-steam{fill:none; stroke:var(--acc); stroke-width:2.4; stroke-linecap:round;
        opacity:0; animation:puff 2.6s ease-in-out infinite}
      .s2{animation-delay:1.3s}
      @keyframes puff{
        0%{opacity:0; transform:translateY(4px)}
        35%{opacity:.9}
        75%,100%{opacity:0; transform:translateY(-6px)}
      }
      .eyebrow{display:inline-flex; align-items:center; gap:8px;
        font:600 11px/1 var(--sans); letter-spacing:.22em; text-transform:uppercase;
        color:var(--acc)}
      .open-dot{width:7px; height:7px; border-radius:50%; background:var(--acc);
        animation:blink 2.2s ease-in-out infinite}
      @keyframes blink{
        0%,100%{opacity:1; box-shadow:0 0 0 0 rgba(232,162,76,.5)}
        50%{opacity:.5; box-shadow:0 0 0 5px rgba(232,162,76,0)}
      }
      h1{margin:7px 0 0; font:italic 400 clamp(30px,6vw,42px)/1 var(--serif)}
      .stat{text-align:right; font-family:var(--mono)}
      .stat-num{display:block; font-size:42px; font-weight:700; line-height:1; color:var(--acc)}
      .stat-cap{font-size:11px; letter-spacing:.18em; text-transform:uppercase; color:var(--dim)}
    
      /* board */
      .board{
        position:relative;
        background:linear-gradient(180deg,var(--board1),var(--board2) 60%);
        border:1px solid var(--line); border-radius:6px;
        box-shadow:0 34px 60px -30px rgba(0,0,0,.85), inset 0 1px 0 rgba(244,231,208,.06);
        padding:20px 20px 10px;
        animation:fadeUp .8s .12s ease both;
      }
      .board::before{content:""; position:absolute; inset:9px;
        border:1px dashed rgba(244,231,208,.13); border-radius:4px; pointer-events:none}
      .board-head{display:flex; justify-content:space-between; align-items:baseline;
        gap:12px; padding:6px 8px 0; flex-wrap:wrap}
      h2{margin:0; font:700 12px/1 var(--mono); letter-spacing:.24em;
        text-transform:uppercase; color:var(--dim)}
      .scale{font:11px var(--mono); color:var(--faint); letter-spacing:.05em}
    
      /* chart */
      .chart{display:block; width:100%; height:auto}
      .grid line{stroke:var(--line); stroke-dasharray:3 6}
      .vaxis{stroke:#5b3f27; stroke-width:1.5}
      .axis{stroke:#7a5433; stroke-width:2}
      .y-lab{fill:var(--faint); font:11px var(--mono); text-anchor:end}
    
      .col{cursor:pointer; outline:none}
      .grow{transform-box:fill-box; transform-origin:50% 100%;
        animation:rise 1s cubic-bezier(.19,1,.22,1) backwards}
      .g1{animation-delay:.15s}.g2{animation-delay:.30s}.g3{animation-delay:.45s}
      .g4{animation-delay:.60s}.g5{animation-delay:.75s}
      @keyframes rise{from{transform:scaleY(0)}}
    
      .bar{fill:url(#brew); transition:filter .25s ease}
      .crema{fill:#f6cf92; opacity:.85; transform-box:fill-box; transform-origin:50% 50%;
        transition:transform .2s ease, opacity .3s ease}
      .value{fill:var(--cream); font:700 17px var(--mono); text-anchor:middle;
        transform-box:fill-box; transform-origin:50% 100%;
        transition:transform .25s ease, fill .25s ease;
        animation:pop .5s ease backwards}
      .v1{animation-delay:.80s}.v2{animation-delay:.95s}.v3{animation-delay:1.10s}
      .v4{animation-delay:1.25s}.v5{animation-delay:1.40s}
      .hot{fill:var(--acc); font-size:19px}
      .day{fill:#c9ab84; font:italic 15px var(--serif); text-anchor:middle;
        animation:fadein .6s ease .1s backwards; transition:fill .25s ease}
      @keyframes pop{from{opacity:0; transform:translateY(10px)}}
      @keyframes fadein{from{opacity:0}}
    
      .steam{animation:fadein 1s ease .9s backwards}
      .steam path{fill:none; stroke:var(--acc); stroke-width:2; stroke-linecap:round;
        opacity:0; animation:wispy 3s ease-in-out infinite}
      .steam .s2{animation-delay:1.5s}
      @keyframes wispy{
        0%{opacity:0; transform:translateY(6px)}
        30%{opacity:.75}
        65%{opacity:.3}
        100%{opacity:0; transform:translateY(-9px)}
      }
    
      .col:hover .bar,.col:focus-visible .bar{filter:brightness(1.2) saturate(1.15)}
      .col:focus-visible .bar{stroke:var(--acc); stroke-width:2}
      .col:hover .value{transform:translateY(-4px); fill:var(--acc)}
      .col:hover .day{fill:var(--cream)}
      .col.sip .value{transform:translateY(-9px)}
      .col.sip .crema{transform:scaleX(1.28); opacity:.5}
    
      .readout{margin:2px 8px 12px; min-height:1.3em; font:12px var(--mono);
        color:var(--dim); letter-spacing:.03em}
      .readout b{color:var(--acc); font-weight:700}
    
      /* receipt tape */
      .tape{
        position:relative; margin-top:16px; padding:12px 18px 13px;
        background:#f1e3c6; color:#33200f;
        border-top:2px dashed rgba(51,32,15,.3);
        font:600 11px/1.4 var(--mono); letter-spacing:.05em;
        display:flex; flex-wrap:wrap; gap:6px 20px; align-items:baseline;
        box-shadow:0 12px 26px -14px rgba(0,0,0,.7);
        animation:fadeUp .8s .5s ease both;
      }
      .tape::after{
        content:""; position:absolute; left:0; right:0; top:100%; height:7px;
        background:
          linear-gradient(45deg,#f1e3c6 3.5px,transparent 0) 0 0/7px 7px repeat-x,
          linear-gradient(-45deg,#f1e3c6 3.5px,transparent 0) 0 0/7px 7px repeat-x;
      }
      .ti{display:inline-flex; align-items:baseline; gap:8px; white-space:nowrap}
      .ti i{flex:1 1 16px; min-width:12px; border-bottom:2px dotted #9a7a4f;
        transform:translateY(-3px)}
      .total{margin-left:auto; color:#8a3d12}
    
      @keyframes fadeUp{from{opacity:0; transform:translateY(14px)}}
      @keyframes fadeDown{from{opacity:0; transform:translateY(-10px)}}
    
      @media (max-width:520px){
        .stat{text-align:left}
        .stat-num{font-size:32px}
        .total{margin-left:0; width:100%}
      }
      @media (prefers-reduced-motion:reduce){
        *,*::before,*::after{animation:none!important; transition:none!important}
      }
    </style>
    </head>
    <body>
    
      <div class="bean b1" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#b97a3e"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b2" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#a96d34"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b3" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#b97a3e"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b4" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#9c6230"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
      <div class="bean b5" aria-hidden="true"><svg viewBox="0 0 64 40"><ellipse cx="32" cy="20" rx="28" ry="18" fill="#b97a3e"/><path d="M8 24 C22 8, 42 32, 56 16" stroke="#1d120a" stroke-width="5" fill="none" stroke-linecap="round"/></svg></div>
    
      <div class="wrap">
    
        <header class="topbar">
          <div class="brand">
            <svg class="cup" viewBox="0 0 48 48" aria-hidden="true">
              <path class="cup-steam" d="M19 15c-3-4 3-6 0-11"/>
              <path class="cup-steam s2" d="M28 15c-3-4 3-6 0-11"/>
              <path d="M10 22h26v6a13 10 0 0 1-26 0z" fill="none" stroke="#f4e7d0" stroke-width="2.4" stroke-linejoin="round"/>
              <path d="M36 24h4a5 5 0 0 1 0 10h-5" fill="none" stroke="#f4e7d0" stroke-width="2.4" stroke-linecap="round"/>
              <path d="M8 42h32" stroke="#b3926b" stroke-width="2.4" stroke-linecap="round"/>
            </svg>
            <div>
              <span class="eyebrow"><span class="open-dot"></span> Café ledger — week 32</span>
              <h1>The Daily Grind</h1>
            </div>
          </div>
          <div class="stat">
            <span class="stat-num">90</span>
            <span class="stat-cap">cups poured</span>
          </div>
        </header>
    
        <section class="board">
          <div class="board-head">
            <h2>Weekly pour</h2>
            <span class="scale">scale · 1 cup = 10 px</span>
          </div>
    
          <svg class="chart" viewBox="0 0 640 440" role="img"
               aria-label="Bar chart of coffee sold this week: Monday 12, Tuesday 30, Wednesday 18, Thursday 24, Friday 6.">
            <title>Coffee sold per day, Monday through Friday</title>
            <defs>
              <linearGradient id="brew" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stop-color="#f2b95f"/>
                <stop offset=".32" stop-color="#d98a35"/>
                <stop offset="1" stop-color="#5c2d12"/>
              </linearGradient>
            </defs>
    
            <g class="grid">
              <line x1="60" x2="620" y1="60"  y2="60"/>
              <line x1="60" x2="620" y1="120" y2="120"/>
              <line x1="60" x2="620" y1="180" y2="180"/>
              <line x1="60" x2="620" y1="240" y2="240"/>
              <line x1="60" x2="620" y1="300" y2="300"/>
            </g>
            <line class="vaxis" x1="60" x2="60" y1="54" y2="360"/>
    
            <g>
              <text class="y-lab" x="48" y="64">30</text>
              <text class="y-lab" x="48" y="124">24</text>
              <text class="y-lab" x="48" y="184">18</text>
              <text class="y-lab" x="48" y="244">12</text>
              <text class="y-lab" x="48" y="304">6</text>
              <text class="y-lab" x="48" y="364">0</text>
            </g>
    
            <g class="col" data-day="Monday" data-cups="12" tabindex="0">
              <g class="grow g1">
                <rect class="bar" x="87" y="240" width="56" height="120" rx="3"/>
                <rect class="crema" x="87" y="240" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v1" x="115" y="226">12</text>
              <text class="day" x="115" y="392">Monday</text>
            </g>
    
            <g class="col" data-day="Tuesday" data-cups="30" tabindex="0">
              <g class="grow g2">
                <rect class="bar" x="197" y="60" width="56" height="300" rx="3"/>
                <rect class="crema" x="197" y="60" width="56" height="7" rx="3.5"/>
              </g>
              <g class="steam" aria-hidden="true">
                <path d="M210 52c-5-9 5-13 0-24"/>
                <path class="s2" d="M240 52c5-9 -5-13 0-24"/>
              </g>
              <text class="value v2 hot" x="225" y="46">30</text>
              <text class="day" x="225" y="392">Tuesday</text>
            </g>
    
            <g class="col" data-day="Wednesday" data-cups="18" tabindex="0">
              <g class="grow g3">
                <rect class="bar" x="307" y="180" width="56" height="180" rx="3"/>
                <rect class="crema" x="307" y="180" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v3" x="335" y="166">18</text>
              <text class="day" x="335" y="392">Wednesday</text>
            </g>
    
            <g class="col" data-day="Thursday" data-cups="24" tabindex="0">
              <g class="grow g4">
                <rect class="bar" x="417" y="120" width="56" height="240" rx="3"/>
                <rect class="crema" x="417" y="120" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v4" x="445" y="106">24</text>
              <text class="day" x="445" y="392">Thursday</text>
            </g>
    
            <g class="col" data-day="Friday" data-cups="6" tabindex="0">
              <g class="grow g5">
                <rect class="bar" x="527" y="300" width="56" height="60" rx="3"/>
                <rect class="crema" x="527" y="300" width="56" height="7" rx="3.5"/>
              </g>
              <text class="value v5" x="555" y="286">6</text>
              <text class="day" x="555" y="392">Friday</text>
            </g>
    
            <line class="axis" x1="60" x2="620" y1="360" y2="360"/>
          </svg>
    
          <p class="readout" id="readout" aria-live="polite"></p>
        </section>
    
        <footer class="tape" aria-hidden="true">
          <span class="ti">MON<i></i><b>12</b></span>
          <span class="ti">TUE<i></i><b>30</b></span>
          <span class="ti">WED<i></i><b>18</b></span>
          <span class="ti">THU<i></i><b>24</b></span>
          <span class="ti">FRI<i></i><b>06</b></span>
          <span class="ti total">TOTAL<i></i><b>90</b></span>
        </footer>
    
      </div>
    
      <script>
        (function () {
          var cols = Array.prototype.slice.call(document.querySelectorAll('.col'));
          var total = cols.reduce(function (s, c) {
            return s + +c.getAttribute('data-cups');
          }, 0);
          var readout = document.getElementById('readout');
          var DEFAULT = 'Peak pour — <b>Tuesday</b> · 30 cups · 33% of the week\u2019s ' + total;
          readout.innerHTML = DEFAULT;
          var timers = {};
    
          cols.forEach(function (col, i) {
            var day = col.getAttribute('data-day');
            var cups = +col.getAttribute('data-cups');
            var pct = Math.round(cups / total * 100);
            var msg = '<b>' + day + ':</b> ' + cups + ' cup' + (cups === 1 ? '' : 's') +
                      ' \u00b7 ' + pct + '% of the week';
    
            function show()  { readout.innerHTML = msg; }
            function reset() { readout.innerHTML = DEFAULT; }
            function sip() {
              col.classList.add('sip');
              clearTimeout(timers[i]);
              timers[i] = setTimeout(function () { col.classList.remove('sip'); }, 240);
            }
    
            col.addEventListener('mouseenter', show);
            col.addEventListener('focus', show);
            col.addEventListener('mouseleave', reset);
            col.addEventListener('blur', reset);
            col.addEventListener('click', sip);
            col.addEventListener('keydown', function (e) {
              if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); sip(); }
            });
          });
        })();
      </script>
    </body>
    </html>
Building games 0 of 2
  • Build 2048Failed

    54,311 tokens: 502 read, 53,809 written · 41 min 17 s · 21.7 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.
    [0, 0, 0, 0]
    [0, 0, 0, 0]
    [0, 32, 2, 2]
    [32, 16, 4, 4]
    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
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="color-scheme" content="dark">
    <title>2048 — slide · merge · climb</title>
    <style>
      * { box-sizing: border-box; }
      html { color-scheme: dark; }
      html, body { margin: 0; padding: 0; }
    
      :root {
        --bg: #0e1421;
        --panel: #171f30;
        --board: #1b2437;
        --line: #2d3a58;
        --ink: #ecf1fb;
        --muted: #8ea0c0;
        --accent: #f3a83c;
        --accent-deep: #c97f1c;
        --gap: clamp(8px, 2.2vw, 13px);
        --r: 14px;
        --sans: "Segoe UI", "Helvetica Neue", system-ui, -apple-system, sans-serif;
        --display: "Arial Black", "Avenir Next", Futura, "Segoe UI", system-ui, sans-serif;
        --mono: ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace;
      }
    
      body {
        min-height: 100vh;
        background: var(--bg);
        color: var(--ink);
        font-family: var(--sans);
        overflow-x: hidden;
      }
      body::before,
      body::after {
        content: "";
        position: fixed;
        inset: 0;
        pointer-events: none;
        z-index: 0;
      }
      body::before {
        inset: -12%;
        background:
          radial-gradient(46% 38% at 16% 10%, rgba(243, 168, 60, .16), transparent 70%),
          radial-gradient(42% 36% at 86% 88%, rgba(84, 148, 255, .13), transparent 70%),
          radial-gradient(28% 24% at 78% 14%, rgba(255, 110, 64, .09), transparent 70%);
        animation: drift 26s ease-in-out infinite alternate;
      }
      body::after {
        background-image: radial-gradient(rgba(236, 241, 251, .05) 1px, transparent 1.4px);
        background-size: 26px 26px;
        -webkit-mask-image: radial-gradient(75% 68% at 50% 42%, #000 32%, transparent 100%);
        mask-image: radial-gradient(75% 68% at 50% 42%, #000 32%, transparent 100%);
      }
      @keyframes drift {
        from { transform: translate3d(-1.4%, -1%, 0); }
        to   { transform: translate3d(1.4%, 1.2%, 0); }
      }
    
      .stage {
        position: relative;
        z-index: 1;
        max-width: 620px;
        margin: 0 auto;
        padding: clamp(18px, 4vh, 42px) clamp(14px, 4vw, 28px) 46px;
        display: flex;
        flex-direction: column;
        gap: clamp(14px, 3vh, 22px);
      }
    
      /* ---------- header ---------- */
      .top {
        display: flex;
        align-items: flex-end;
        justify-content: space-between;
        gap: 14px 20px;
        flex-wrap: wrap;
        animation: rise .55s ease both;
      }
      .mark {
        margin: 0;
        font-family: var(--display);
        font-weight: 900;
        font-size: clamp(2.7rem, 9vw, 4.3rem);
        line-height: .85;
        letter-spacing: -0.04em;
        text-shadow: 0 3px 0 rgba(0, 0, 0, .4), 0 14px 34px rgba(243, 168, 60, .14);
      }
      .mark .hot { color: var(--accent); }
      .tag { margin: 8px 0 0; color: var(--muted); font-size: .95rem; }
      .tag b { color: var(--accent); }
    
      .hud { display: flex; align-items: stretch; gap: 10px; animation: rise .55s .1s ease both; }
      .stat {
        position: relative;
        min-width: 92px;
        padding: 8px 14px 10px;
        text-align: center;
        background: var(--panel);
        border: 1px solid var(--line);
        border-radius: 11px;
        box-shadow: 0 6px 16px rgba(0, 0, 0, .28);
      }
      .stat-label {
        display: block;
        font-size: .6rem;
        font-weight: 700;
        letter-spacing: .16em;
        text-transform: uppercase;
        color: var(--muted);
        margin-bottom: 2px;
      }
      .stat-value {
        display: block;
        font-family: var(--mono);
        font-weight: 700;
        font-size: 1.3rem;
        font-variant-numeric: tabular-nums;
        transition: color .35s ease;
      }
      .stat.flash .stat-value { color: var(--accent); }
      .float-gain {
        position: absolute;
        left: 50%;
        top: 26%;
        font-family: var(--mono);
        font-weight: 700;
        color: var(--accent);
        text-shadow: 0 2px 10px rgba(0, 0, 0, .55);
        pointer-events: none;
        animation: float-up .75s ease-out forwards;
      }
      @keyframes float-up {
        0%   { opacity: 0; transform: translate(-50%, 8px) scale(.8); }
        18%  { opacity: 1; }
        100% { opacity: 0; transform: translate(-50%, -38px) scale(1.12); }
      }
    
      .btn {
        font-family: var(--sans);
        font-size: .92rem;
        font-weight: 800;
        letter-spacing: .02em;
        color: #261602;
        background: linear-gradient(180deg, #ffca62, var(--accent));
        border: 0;
        border-radius: 11px;
        padding: 10px 18px;
        cursor: pointer;
        box-shadow: 0 3px 0 var(--accent-deep), 0 10px 20px rgba(243, 168, 60, .22);
        transition: transform .12s ease, box-shadow .12s ease, filter .12s ease;
      }
      .btn:hover { transform: translateY(-2px); filter: brightness(1.06); box-shadow: 0 5px 0 var(--accent-deep), 0 14px 26px rgba(243, 168, 60, .3); }
      .btn:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--accent-deep), 0 6px 12px rgba(243, 168, 60, .22); }
      .btn:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
    
      /* ---------- board ---------- */
      .board-zone {
        position: relative;
        width: min(100%, 520px);
        margin: 0 auto;
        animation: rise .6s .18s cubic-bezier(.22, 1, .36, 1) both;
      }
      .board {
        position: relative;
        width: 100%;
        aspect-ratio: 1;
        background: linear-gradient(180deg, #202b44, var(--board));
        border: 1px solid var(--line);
        border-radius: var(--r);
        box-shadow: 0 28px 64px rgba(0, 0, 0, .5), inset 0 1px 0 rgba(255, 255, 255, .06);
        container-type: size;
        touch-action: none;
        user-select: none;
        -webkit-user-select: none;
        transition: transform .1s ease;
      }
      .board.pushed { transform: scale(.992); }
      .cells, .tiles { position: absolute; inset: var(--gap); }
      .cells {
        display: grid;
        grid-template-columns: repeat(4, 1fr);
        grid-template-rows: repeat(4, 1fr);
        gap: var(--gap);
      }
      .cells div {
        background: rgba(8, 12, 21, .5);
        border-radius: 9px;
        box-shadow: inset 0 2px 7px rgba(0, 0, 0, .42), inset 0 -1px 0 rgba(255, 255, 255, .04);
      }
    
      .tile {
        position: absolute;
        width: calc(25% - 0.75 * var(--gap));
        height: calc(25% - 0.75 * var(--gap));
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: 9px;
        font-weight: 800;
        line-height: 1;
        font-size: 1.9rem;
        font-size: 10.5cqw;
        transition: left .12s ease-out, top .12s ease-out;
        box-shadow: 0 3px 8px rgba(0, 0, 0, .35), inset 0 1px 0 rgba(255, 255, 255, .22);
        will-change: left, top;
        z-index: 1;
      }
      .tile.d3 { font-size: 8.4cqw; }
      .tile.d4 { font-size: 6.6cqw; }
      .tile.d5 { font-size: 5.2cqw; }
      .tile.spawn { animation: tile-in .2s cubic-bezier(.34, 1.4, .64, 1) both; z-index: 2; }
      .tile.merge-pop { animation: merge-pop .22s ease-out; z-index: 3; }
      @keyframes tile-in {
        from { transform: scale(0); opacity: 0; }
        to   { transform: scale(1); opacity: 1; }
      }
      @keyframes merge-pop {
        0%   { transform: scale(1); }
        45%  { transform: scale(1.18); }
        100% { transform: scale(1); }
      }
    
      .t2    { background: #e9dfca; color: #6f5f47; }
      .t4    { background: #e2cfa8; color: #6b573b; }
      .t8    { background: #e7a63c; color: #4c3405; }
      .t16   { background: #e28a2c; color: #fff5e3; }
      .t32   { background: #dc6c28; color: #fff5e3; }
      .t64   { background: #d64a33; color: #ffeee4; }
      .t128  { background: #f6c344; color: #57400a; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 18px rgba(246,195,68,.32); }
      .t256  { background: #f4ae35; color: #533c08; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 24px rgba(244,174,53,.42); }
      .t512  { background: #ef982a; color: #4a2c04; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 28px rgba(239,152,42,.48); }
      .t1024 { background: #fb8a25; color: #3d2003; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 32px rgba(251,138,37,.55); }
      .t2048 {
        background: #ffd042; color: #634404;
        box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 30px rgba(255,208,66,.6);
        animation: breathe 2.4s ease-in-out infinite;
      }
      .tmax  { background: #fff1a8; color: #6d5308; box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 46px rgba(255,222,110,.75); }
      @keyframes breathe {
        0%, 100% { box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 24px rgba(255,208,66,.5); }
        50%      { box-shadow: 0 3px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.3), 0 0 48px rgba(255,208,66,.85); }
      }
    
      /* ---------- overlay & toast ---------- */
      .overlay {
        position: absolute;
        inset: 0;
        z-index: 5;
        display: flex;
        align-items: center;
        justify-content: center;
        border-radius: var(--r);
        background: rgba(9, 13, 22, .74);
        backdrop-filter: blur(3px);
        -webkit-backdrop-filter: blur(3px);
        animation: fade-in .28s ease both;
      }
      .overlay[hidden] { display: none; }
      .card { text-align: center; padding: 26px 38px; animation: rise .4s .05s ease both; }
      .card h2 {
        margin: 0 0 8px;
        font-family: var(--display);
        font-weight: 900;
        font-size: clamp(1.6rem, 6vw, 2.2rem);
        letter-spacing: -0.02em;
      }
      .card p { margin: 0 0 20px; color: var(--muted); }
      .card b { font-family: var(--mono); font-size: 1.25rem; color: var(--ink); }
      @keyframes fade-in { from { opacity: 0; } }
      @keyframes rise {
        from { opacity: 0; transform: translateY(14px) scale(.97); }
        to   { opacity: 1; transform: none; }
      }
    
      .toast {
        position: absolute;
        top: -16px;
        left: 50%;
        transform: translate(-50%, -100%);
        z-index: 6;
        white-space: nowrap;
        padding: 10px 22px;
        border-radius: 999px;
        font-weight: 800;
        font-size: .95rem;
        color: #2a1802;
        background: linear-gradient(180deg, #ffd062, var(--accent));
        box-shadow: 0 12px 30px rgba(243, 168, 60, .4);
        animation: toast-in .32s cubic-bezier(.34, 1.56, .64, 1) both;
      }
      .toast.out { animation: toast-out .32s ease both; }
      .toast[hidden] { display: none; }
      @keyframes toast-in {
        from { opacity: 0; transform: translate(-50%, -70%) scale(.8); }
        to   { opacity: 1; transform: translate(-50%, -100%) scale(1); }
      }
      @keyframes toast-out {
        to { opacity: 0; transform: translate(-50%, -190%) scale(.9); }
      }
    
      /* ---------- footer ---------- */
      .hint {
        display: flex;
        justify-content: space-between;
        align-items: center;
        gap: 8px 16px;
        flex-wrap: wrap;
        color: var(--muted);
        font-size: .84rem;
        animation: rise .55s .32s ease both;
      }
      kbd {
        display: inline-block;
        margin: 0 1px;
        padding: 3px 7px;
        font-family: var(--mono);
        font-size: .72rem;
        color: var(--ink);
        background: #1a2338;
        border: 1px solid #344260;
        border-bottom-width: 2px;
        border-radius: 6px;
      }
      @media (hover: none) and (pointer: coarse) { .keys { display: none; } }
      @media (hover: hover) { .swipe-hint { display: none; } }
    
      .sr-only {
        position: absolute;
        width: 1px; height: 1px;
        margin: -1px; padding: 0;
        overflow: hidden;
        clip: rect(0 0 0 0);
        white-space: nowrap;
        border: 0;
      }
    
      @media (max-width: 470px) {
        .top { flex-direction: column; align-items: stretch; }
        .hud { width: 100%; }
        .stat { flex: 1; }
        .btn { flex: 1.2; }
      }
      @media (prefers-reduced-motion: reduce) {
        *, *::before, *::after {
          animation-duration: .01ms !important;
          animation-iteration-count: 1 !important;
          transition-duration: .01ms !important;
        }
      }
    </style>
    </head>
    <body>
    <main class="stage">
      <header class="top">
        <div class="brand">
          <h1 class="mark">20<span class="hot">48</span></h1>
          <p class="tag">Slide the tiles. Merge the twins. Climb to <b>2048</b>.</p>
        </div>
        <div class="hud">
          <div class="stat" id="score-stat">
            <span class="stat-label">Score</span>
            <span class="stat-value" id="score">0</span>
          </div>
          <div class="stat" id="best-stat">
            <span class="stat-label">Best</span>
            <span class="stat-value" id="best">0</span>
          </div>
          <button class="btn" id="new-game" type="button">New&nbsp;game</button>
        </div>
      </header>
    
      <section class="board-zone" id="board-zone">
        <div class="board" id="board" role="img" aria-label="2048 board">
          <div class="cells" id="cells"></div>
          <div class="tiles" id="tiles"></div>
        </div>
        <div class="overlay" id="overlay" hidden>
          <div class="card">
            <h2>Game over</h2>
            <p>Final score <b id="final-score">0</b></p>
            <button class="btn" id="retry" type="button">Play again</button>
          </div>
        </div>
        <div class="toast" id="toast" hidden></div>
      </section>
    
      <footer class="hint">
        <span class="keys"><kbd>←</kbd><kbd>↑</kbd><kbd>↓</kbd><kbd>→</kbd>&nbsp;or&nbsp;<kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd></span>
        <span class="swipe-hint">swipe to move</span>
      </footer>
      <p class="sr-only" id="live" aria-live="polite"></p>
    </main>
    
    <script id="game">
    function createGame() {
      "use strict";
    
      const START = [
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 32, 2, 2],
        [32, 16, 4, 4]
      ];
    
      function zeroBoard() {
        return [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]];
      }
    
      function cloneBoard(b) {
        return b.map(function (row) { return row.slice(); });
      }
    
      function same(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;
      }
    
      // Rule 1 / 4: drop a 2 into each of the first `count` empty cells, reading order.
      function place(count, b) {
        let placed = 0;
        for (let r = 0; r < 4 && placed < count; r++)
          for (let c = 0; c < 4 && placed < count; c++)
            if (b[r][c] === 0) { b[r][c] = 2; placed++; }
      }
    
      // Rule 2: slide one row/column toward index 0; equal neighbors merge once,
      // nearest the target side first; a merge result never merges again.
      function slideLine(line) {
        const tiles = line.filter(function (v) { return v !== 0; });
        const out = [];
        let gained = 0;
        let i = 0;
        while (i < tiles.length) {
          if (i + 1 < tiles.length && tiles[i] === tiles[i + 1]) {
            const v = tiles[i] * 2;
            out.push(v);
            gained += v;
            i += 2;
          } else {
            out.push(tiles[i]);
            i += 1;
          }
        }
        while (out.length < 4) out.push(0);
        return { out: out, gained: gained };
      }
    
      const DIRS = ["left", "right", "up", "down"];
    
      function apply(b, dir) {
        const nb = zeroBoard();
        let gained = 0;
        for (let i = 0; i < 4; i++) {
          let line;
          if (dir === "left")        line = [b[i][0], b[i][1], b[i][2], b[i][3]];
          else if (dir === "right")  line = [b[i][3], b[i][2], b[i][1], b[i][0]];
          else if (dir === "up")     line = [b[0][i], b[1][i], b[2][i], b[3][i]];
          else                       line = [b[3][i], b[2][i], b[1][i], b[0][i]];
          const res = slideLine(line);
          gained += res.gained;
          for (let j = 0; j < 4; j++) {
            if (dir === "left")        nb[i][j] = res.out[j];
            else if (dir === "right")  nb[i][3 - j] = res.out[j];
            else if (dir === "up")     nb[j][i] = res.out[j];
            else                       nb[3 - j][i] = res.out[j];
          }
        }
        return { board: nb, gained: gained };
      }
    
      function movable(b) {
        return DIRS.some(function (d) { return !same(b, apply(b, d).board); });
      }
    
      const board = cloneBoard(START);
      place(2, board);            // rule 1
      let score = 0;
      let over = !movable(board); // rule 5
    
      function move(dir) {
        if (over || DIRS.indexOf(dir) === -1) return;
        const res = apply(board, dir);
        if (same(res.board, board)) return; // rule 4: no change -> nothing happens
        board = res.board;
        score += res.gained;               // rule 3
        place(1, board);                   // rule 4
        over = !movable(board);            // rule 5
      }
    
      return {
        move: move,
        state: function () {
          return { board: cloneBoard(board), score: score, over: over };
        }
      };
    }
    </script>
    
    <script>
    (function () {
      "use strict";
    
      const cellsEl   = document.getElementById("cells");
      const tilesEl   = document.getElementById("tiles");
      const boardEl   = document.getElementById("board");
      const zoneEl    = document.getElementById("board-zone");
      const scoreEl   = document.getElementById("score");
      const bestEl    = document.getElementById("best");
      const scoreStat = document.getElementById("score-stat");
      const bestStat  = document.getElementById("best-stat");
      const overlayEl = document.getElementById("overlay");
      const finalEl   = document.getElementById("final-score");
      const retryEl   = document.getElementById("retry");
      const newBtn    = document.getElementById("new-game");
      const toastEl   = document.getElementById("toast");
      const liveEl    = document.getElementById("live");
    
      for (let i = 0; i < 16; i++) cellsEl.appendChild(document.createElement("div"));
    
      let game    = createGame();
      let state   = game.state();
      let shown   = state.board.map(function (r) { return r.slice(); });
      let tiles   = new Map();   // "r,c" -> tile element
      let victims = [];          // merged-away elements awaiting removal
      let winShown = false;
      let toastTimers = [];
    
      const BEST_KEY = "ink2048.best";
      let best = 0;
      try { best = Math.max(0, parseInt(localStorage.getItem(BEST_KEY), 10) || 0); } catch (e) {}
      bestEl.textContent = best;
    
      const key = function (r, c) { return r + "," + c; };
    
      function pos(el, r, c) {
        el.style.left = "calc(" + c + " * (25% + 0.25 * var(--gap)))";
        el.style.top  = "calc(" + r + " * (25% + 0.25 * var(--gap)))";
      }
    
      function digitClass(v) {
        if (v >= 10000) return " d5";
        if (v >= 1000) return " d4";
        if (v >= 100) return " d3";
        return "";
      }
      function tileClasses(v, extra) {
        return "tile " + (v <= 2048 ? "t" + v : "tmax") + digitClass(v) + (extra ? " " + extra : "");
      }
      function makeTile(r, c, v, extra) {
        const el = document.createElement("div");
        el.className = tileClasses(v, extra);
        el.textContent = v;
        pos(el, r, c);
        tilesEl.appendChild(el);
        return el;
      }
      function clearTiles() {
        tiles.forEach(function (el) { el.remove(); });
        victims.forEach(function (el) { el.remove(); });
        tiles = new Map();
        victims = [];
      }
    
      // Presentation only: trace where each existing tile ends up so slides can be animated.
      function lineCoord(dir, i, j) {
        if (dir === "left")  return [i, j];
        if (dir === "right") return [i, 3 - j];
        if (dir === "up")    return [j, i];
        return [3 - j, i];
      }
      function planMove(board, dir) {
        const moves = [], victims = [], targets = new Set();
        for (let i = 0; i < 4; i++) {
          const seq = [0, 1, 2, 3].map(function (j) { return lineCoord(dir, i, j); });
          const path = seq.filter(function (rc) { return board[rc[0]][rc[1]] !== 0; });
          let out = 0, k = 0;
          while (k < path.length) {
            const v = board[path[k][0]][path[k][1]];
            let value = v, victim = null;
            if (k + 1 < path.length && board[path[k + 1][0]][path[k + 1][1]] === v) {
              value = v * 2;
              victim = path[k + 1];
            }
            const dest = seq[out];
            moves.push({ r: path[k][0], c: path[k][1], tr: dest[0], tc: dest[1], value: value });
            if (victim) victims.push({ r: victim[0], c: victim[1], tr: dest[0], tc: dest[1] });
            targets.add(dest[0] + "," + dest[1]);
            k += victim ? 2 : 1;
            out += 1;
          }
        }
        return { moves: moves, victims: victims, targets: targets };
      }
    
      function animateMove(dir, next) {
        victims.forEach(function (el) { el.remove(); });
        victims = [];
    
        const plan = planMove(shown, dir);
        const fresh = new Map();
    
        plan.moves.forEach(function (m) {
          const el = tiles.get(key(m.r, m.c));
          if (!el) return;
          pos(el, m.tr, m.tc);
          if (m.value !== shown[m.r][m.c]) {
            el.className = tileClasses(m.value, "merge-pop");
            el.textContent = m.value;
            el.addEventListener("animationend", function h(ev) {
              if (ev.animationName === "merge-pop") {
                el.classList.remove("merge-pop");
                el.removeEventListener("animationend", h);
              }
            });
          }
          fresh.set(key(m.tr, m.tc), el);
        });
    
        plan.victims.forEach(function (v) {
          const el = tiles.get(key(v.r, v.c));
          if (!el) return;
          pos(el, v.tr, v.tc);
          victims.push(el);
          setTimeout(function () {
            const i = victims.indexOf(el);
            if (i > -1) victims.splice(i, 1);
            el.remove();
          }, 170);
        });
    
        next.board.forEach(function (row, r) {
          row.forEach(function (v, c) {
            if (v && !plan.targets.has(key(r, c))) {
              const el = makeTile(r, c, v, "spawn");
              el.style.animationDelay = "90ms";
              fresh.set(key(r, c), el);
            }
          });
        });
    
        tiles = fresh;
        shown = next.board.map(function (row) { return row.slice(); });
      }
    
      let scoreRaf = 0;
      function tickScore(from, to) {
        cancelAnimationFrame(scoreRaf);
        const t0 = performance.now(), dur = 260;
        const step = function (t) {
          const p = Math.min(1, (t - t0) / dur);
          scoreEl.textContent = Math.round(from + (to - from) * (1 - Math.pow(1 - p, 3)));
          if (p < 1) scoreRaf = requestAnimationFrame(step);
        };
        scoreRaf = requestAnimationFrame(step);
      }
      function floatGain(amount) {
        const chip = document.createElement("span");
        chip.className = "float-gain";
        chip.textContent = "+" + amount;
        scoreStat.appendChild(chip);
        chip.addEventListener("animationend", function () { chip.remove(); });
      }
    
      function hideToast() {
        toastTimers.forEach(clearTimeout);
        toastTimers = [];
        toastEl.hidden = true;
        toastEl.classList.remove("out");
      }
      function showToast(msg) {
        hideToast();
        toastEl.textContent = msg;
        toastEl.hidden = false;
        toastEl.style.animation = "none";
        void toastEl.offsetWidth;
        toastEl.style.animation = "";
        toastTimers.push(setTimeout(function () {
          toastEl.classList.add("out");
          toastTimers.push(setTimeout(hideToast, 340));
        }, 2600));
      }
    
      function doMove(dir) {
        if (state.over) return;
        const before = state;
        game.move(dir);
        const next = game.state();
        let changed = false;
        for (let r = 0; r < 4 && !changed; r++)
          for (let c = 0; c < 4; c++)
            if (next.board[r][c] !== before.board[r][c]) { changed = true; break; }
        if (!changed) return;
    
        animateMove(dir, next);
    
        boardEl.classList.add("pushed");
        setTimeout(function () { boardEl.classList.remove("pushed"); }, 110);
    
        if (next.score > before.score) {
          tickScore(before.score, next.score);
          floatGain(next.score - before.score);
        } else {
          scoreEl.textContent = next.score;
        }
        if (next.score > best) {
          best = next.score;
          try { localStorage.setItem(BEST_KEY, String(best)); } catch (e) {}
          bestEl.textContent = best;
          bestStat.classList.add("flash");
          setTimeout(function () { bestStat.classList.remove("flash"); }, 700);
        }
    
        state = next;
        boardEl.setAttribute("aria-label", "2048 board. Score " + state.score + ".");
    
        if (!winShown && !next.over &&
            next.board.some(function (row) { return row.some(function (v) { return v >= 2048; }); })) {
          winShown = true;
          showToast("2048 tile! Keep climbing.");
          liveEl.textContent = "You made a 2048 tile. Keep playing.";
        }
        if (next.over) {
          liveEl.textContent = "Game over. No moves left.";
          finalEl.textContent = next.score;
          setTimeout(function () {
            overlayEl.hidden = false;
            retryEl.focus({ preventScroll: true });
          }, 450);
        }
      }
    
      function newGame() {
        game = createGame();
        state = game.state();
        winShown = false;
        overlayEl.hidden = true;
        hideToast();
        scoreEl.textContent = state.score;
        clearTiles();
        let i = 0;
        state.board.forEach(function (row, r) {
          row.forEach(function (v, c) {
            if (v) {
              const el = makeTile(r, c, v, "spawn");
              el.style.animationDelay = (i++ * 34) + "ms";
              tiles.set(key(r, c), el);
            }
          });
        });
        shown = state.board.map(function (row) { return row.slice(); });
        boardEl.setAttribute("aria-label", "2048 board. Score " + state.score + ".");
        liveEl.textContent = "New game. Score " + state.score + ".";
      }
    
      const KEYDIR = {
        arrowup: "up",     w: "up",
        arrowdown: "down", s: "down",
        arrowleft: "left", a: "left",
        arrowright: "right", d: "right"
      };
      window.addEventListener("keydown", function (e) {
        if (e.metaKey || e.ctrlKey || e.altKey) return;
        const dir = KEYDIR[e.key.toLowerCase()];
        if (!dir) return;
        e.preventDefault();
        doMove(dir);
      });
    
      let touchStart = null;
      zoneEl.addEventListener("touchstart", function (e) {
        touchStart = e.touches.length === 1
          ? { x: e.touches[0].clientX, y: e.touches[0].clientY }
          : null;
      }, { passive: true });
      zoneEl.addEventListener("touchend", function (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;
        doMove(Math.abs(dx) > Math.abs(dy) ? (dx > 0 ? "right" : "left")
                                           : (dy > 0 ? "down" : "up"));
      }, { passive: true });
    
      newBtn.addEventListener("click", newGame);
      retryEl.addEventListener("click", newGame);
    
      newGame();
    })();
    </script>
    </body>
    </html>
  • Build SnakeFailed

    16,830 tokens: 497 read, 16,333 written · 12 min 12 s · 22.4 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, 5], [2, 5], [1, 5]], moving right, food at [5, 9], 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</title>
      <style>
        :root {
          --bg: #101614;
          --panel: #1b2421;
          --line: #2a3833;
          --text: #e7f2ee;
          --muted: #9db4ab;
          --snake: #43d17a;
          --head: #a5f3c0;
          --food: #ff5d5d;
        }
    
        * {
          box-sizing: border-box;
        }
    
        body {
          min-height: 100vh;
          margin: 0;
          display: grid;
          place-items: center;
          background: radial-gradient(circle at top, #17231f, var(--bg));
          color: var(--text);
          font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
        }
    
        main {
          width: min(92vw, 540px);
        }
    
        .hud {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 1rem;
          margin-bottom: 0.75rem;
        }
    
        h1 {
          margin: 0;
          font-size: 1.5rem;
          letter-spacing: 0.04em;
        }
    
        .score {
          font-size: 1.1rem;
          color: var(--muted);
        }
    
        .score strong {
          color: var(--text);
        }
    
        button {
          font: inherit;
          border: 1px solid var(--line);
          background: #22302b;
          color: var(--text);
          padding: 0.45rem 0.8rem;
          border-radius: 8px;
          cursor: pointer;
        }
    
        button:hover {
          background: #2a3b35;
        }
    
        .board {
          display: grid;
          grid-template-columns: repeat(10, 1fr);
          grid-template-rows: repeat(10, 1fr);
          gap: 3px;
          width: 100%;
          aspect-ratio: 1;
          padding: 8px;
          background: var(--panel);
          border: 1px solid var(--line);
          border-radius: 12px;
        }
    
        .cell {
          border-radius: 5px;
          background: #212d29;
          box-shadow: inset 0 0 0 1px #293832;
        }
    
        .cell.snake {
          background: var(--snake);
          box-shadow: inset 0 0 0 2px #2a8f55;
        }
    
        .cell.head {
          background: var(--head);
          box-shadow: inset 0 0 0 2px #43d17a;
        }
    
        .cell.food {
          background: var(--food);
          border-radius: 50%;
          box-shadow: inset 0 0 0 2px #b33;
        }
    
        .status {
          margin: 0.75rem 0 0;
          color: var(--muted);
          min-height: 1.4em;
          text-align: center;
        }
      </style>
    </head>
    <body>
      <main>
        <div class="hud">
          <h1>Snake</h1>
          <div class="score">Score: <strong id="score">0</strong></div>
          <button id="restart" type="button">New Game</button>
        </div>
    
        <div id="board" class="board" aria-label="Snake game board"></div>
    
        <p id="status" class="status"></p>
      </main>
    
      <script id="game">
    function createGame() {
      var SIZE = 10;
      var snake = [[3, 5], [2, 5], [1, 5]];
      var food = [5, 9];
      var score = 0;
      var over = false;
      var dir = "right";
    
      var vectors = {
        up: [0, -1],
        down: [0, 1],
        left: [-1, 0],
        right: [1, 0]
      };
    
      var opposites = {
        up: "down",
        down: "up",
        left: "right",
        right: "left"
      };
    
      function cellIn(list, x, y) {
        for (var i = 0; i < list.length; i++) {
          if (list[i][0] === x && list[i][1] === y) {
            return true;
          }
        }
        return false;
      }
    
      function nextFood(headX, headY) {
        var total = SIZE * SIZE;
        var start = (headY * SIZE + headX + 1) % total;
    
        for (var i = 0; i < total; i++) {
          var idx = (start + i) % total;
          var x = idx % SIZE;
          var y = Math.floor(idx / SIZE);
    
          if (!cellIn(snake, x, y)) {
            return [x, y];
          }
        }
    
        return [headX, headY];
      }
    
      function step(nextDir) {
        if (over) {
          return;
        }
    
        if (!vectors[nextDir]) {
          return;
        }
    
        if (nextDir !== opposites[dir]) {
          dir = nextDir;
        }
    
        var vx = vectors[dir][0];
        var vy = vectors[dir][1];
        var head = snake[0];
        var nx = head[0] + vx;
        var ny = head[1] + vy;
    
        if (nx < 0 || nx >= SIZE || ny < 0 || ny >= SIZE) {
          over = true;
          return;
        }
    
        if (nx === food[0] && ny === food[1]) {
          if (cellIn(snake, nx, ny)) {
            over = true;
            return;
          }
    
          snake.unshift([nx, ny]);
          score = score + 1;
    
          var newFood = nextFood(nx, ny);
          food[0] = newFood[0];
          food[1] = newFood[1];
        } else {
          var body = snake.slice(0, -1);
    
          if (cellIn(body, nx, ny)) {
            over = true;
            return;
          }
    
          snake.shift();
          snake.unshift([nx, ny]);
        }
      }
    
      function state() {
        var resultSnake = [];
    
        for (var i = 0; i < snake.length; i++) {
          resultSnake.push([snake[i][0], snake[i][1]]);
        }
    
        return {
          snake: resultSnake,
          food: [food[0], food[1]],
          score: score,
          over: over
        };
      }
    
      return {
        step: step,
        state: state
      };
    }
      </script>
    
      <script>
      (function () {
        var board = document.getElementById("board");
        var scoreEl = document.getElementById("score");
        var statusEl = document.getElementById("status");
        var restartBtn = document.getElementById("restart");
    
        var game = createGame();
        var cells = [];
    
        for (var y = 0; y < 10; y++) {
          for (var x = 0; x < 10; x++) {
            var cell = document.createElement("div");
            cell.className = "cell";
            board.appendChild(cell);
            cells.push(cell);
          }
        }
    
        function render() {
          var s = game.state();
    
          scoreEl.textContent = String(s.score);
          statusEl.textContent = s.over
            ? "Game over. Click New Game to play again."
            : "Use the arrow keys. Each press moves the snake one cell.";
    
          var snakeSet = {};
          for (var i = 0; i < s.snake.length; i++) {
            snakeSet[s.snake[i][1] * 10 + s.snake[i][0]] = true;
          }
    
          var headIndex = s.snake.length ? s.snake[0][1] * 10 + s.snake[0][0] : -1;
          var foodIndex = s.food ? s.food[1] * 10 + s.food[0] : -1;
    
          for (var c = 0; c < cells.length; c++) {
            var cls = "cell";
    
            if (snakeSet[c]) {
              cls += " snake";
            }
    
            if (c === headIndex) {
              cls += " head";
            }
    
            if (c === foodIndex && !snakeSet[c]) {
              cls += " food";
            }
    
            cells[c].className = cls;
          }
        }
    
        function dirForKey(key) {
          if (key === "ArrowUp") return "up";
          if (key === "ArrowDown") return "down";
          if (key === "ArrowLeft") return "left";
          if (key === "ArrowRight") return "right";
          return null;
        }
    
        function onKeydown(event) {
          var dir = dirForKey(event.key);
    
          if (!dir) {
            return;
          }
    
          if (event.repeat) {
            return;
          }
    
          event.preventDefault();
          game.step(dir);
          render();
        }
    
        function onRestart() {
          game = createGame();
          render();
        }
    
        window.addEventListener("keydown", onKeydown);
        restartBtn.addEventListener("click", onRestart);
    
        render();
      })();
      </script>
    </body>
    </html>
Writing code 3 of 3
  • Merge intervalsPassed

    1,295 tokens: 200 read, 1,095 written · 48 s · 23.1 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 (!intervals || intervals.length === 0) {
        return [];
      }
    
      const sorted = intervals.slice().sort((a, b) => {
        if (a[0] !== b[0]) {
          return a[0] - b[0];
        }
        return a[1] - b[1];
      });
    
      const merged = [];
    
      for (const interval of sorted) {
        const start = interval[0];
        const end = interval[1];
        const last = merged[merged.length - 1];
    
        if (!last || start > last[1]) {
          merged.push([start, end]);
        } else {
          last[1] = Math.max(last[1], end);
        }
      }
    
      return merged;
    }
    ```
  • An LRU cachePassed

    804 tokens: 210 read, 594 written · 27 s · 23 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);
        this.cache.delete(key);
        this.cache.set(key, value);
    
        return value;
      }
    
      put(key, value) {
        if (this.cache.has(key)) {
          this.cache.delete(key);
        } else if (this.cache.size >= this.capacity) {
          const oldestKey = this.cache.keys().next().value;
          this.cache.delete(oldestKey);
        }
    
        this.cache.set(key, value);
      }
    }
    ```
  • Evaluate arithmeticPassed

    23,123 tokens: 236 read, 22,887 written · 17 min 38 s · 21.7 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) {
      if (typeof expr !== 'string') {
        throw new TypeError('Expression must be a string');
      }
    
      var i = 0;
      var n = expr.length;
    
      function skipSpaces() {
        while (i < n && /\s/.test(expr[i])) {
          i++;
        }
      }
    
      function parseNumber() {
        skipSpaces();
        var start = i;
        while (i < n && expr[i] >= '0' && expr[i] <= '9') {
          i++;
        }
        if (start === i) {
          throw new Error('Expected a number');
        }
        return parseInt(expr.slice(start, i), 10);
      }
    
      function parseFactor() {
        skipSpaces();
        var c = expr[i];
    
        if (c === '-') {
          i++;
          return -parseFactor();
        }
    
        if (c === '+') {
          i++;
          return parseFactor();
        }
    
        if (c === '(') {
          i++;
          var value = parseExpr();
          skipSpaces();
          if (expr[i] !== ')') {
            throw new Error('Expected closing parenthesis');
          }
          i++;
          return value;
        }
    
        return parseNumber();
      }
    
      function parseTerm() {
        var value = parseFactor();
    
        for (;;) {
          skipSpaces();
          var c = expr[i];
    
          if (c === '*') {
            i++;
            value *= parseFactor();
          } else if (c === '/') {
            i++;
            var divisor = parseFactor();
            if (divisor === 0) {
              throw new Error('Division by zero');
            }
            value = Math.trunc(value / divisor);
          } else {
            break;
          }
        }
    
        return value;
      }
    
      function parseExpr() {
        var value = parseTerm();
    
        for (;;) {
          skipSpaces();
          var c = expr[i];
    
          if (c === '+') {
            i++;
            value += parseTerm();
          } else if (c === '-') {
            i++;
            value -= parseTerm();
          } else {
            break;
          }
        }
    
        return value;
      }
    
      var result = parseExpr();
      skipSpaces();
    
      if (i !== n) {
        throw new Error('Unexpected token');
      }
    
      return result === 0 ? 0 : result;
    }
    ```
Fixing code 2 of 2
  • Fix the bookings modulePassed

    2,057 tokens: 715 read, 1,342 written · 1 min 0 s · 23.6 tok/s

    This JavaScript module has one bug. A guest who wants to check in on the day the previous guest checks out is refused, though that night is free.
    
    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 === 5 || weekday === 6 ? 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,056 tokens: 556 read, 1,500 written · 1 min 5 s · 24.1 tok/s

    This JavaScript module has one bug. A quoted field with a comma in it, such as "Smith, Jane", is split in two.
    
    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 && ch !== ',') {
          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);
    }
    ```
    
    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

    1,330 tokens: 269 read, 1,061 written · 48 s · 22.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: Every customer who has no delivered order (including customers with no orders at all), with how many orders of any status they placed. Columns: name, orders. Any order of rows.
    
    Reply with only the query, in one ```sql code block: one SELECT statement (WITH is fine), nothing that changes the database.
    ```sql
    SELECT
        c.name AS name,
        (
            SELECT COUNT(*)
            FROM orders o
            WHERE o.customer_id = c.id
        ) AS orders
    FROM customers c
    WHERE NOT EXISTS (
        SELECT 1
        FROM orders o
        WHERE o.customer_id = c.id
          AND o.status = 'delivered'
    );
    ```
  • Schedule five meetingsPassed

    3,070 tokens: 345 read, 2,725 written · 2 min 1 s · 22.8 tok/s

    Five people each have one meeting today: Eli, Hal, Kim, Lea, 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: Elm, Maple, Oak, Rowan, Willow.
    
    1. Noa meets earlier than Eli.
    2. Lea meets neither first nor last.
    3. Eli is not in Willow.
    4. Whoever is in Oak meets earlier than Kim.
    5. Kim's and Noa's rooms are not next to each other on the list of rooms.
    6. Noa's meeting is in the slot right after Kim's.
    7. Eli's and Lea's rooms are not next to each other on the list of rooms.
    8. Whoever is in Maple meets earlier than Kim.
    9. Eli's and Noa'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
    Eli: 14:00, Rowan
    Hal: 09:00, Oak
    Kim: 11:00, Willow
    Lea: 10:00, Maple
    Noa: 13:00, Elm
  • A replacement in ten calls or fewerPassed

    11,733 tokens: 10,477 read, 1,256 written · 1 min 2 s · 23.7 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?
    1. 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.
    2. user
      Hi, it's ana.silva@example.com. My kettle arrived with a cracked lid. Can you sort it out?
    3. assistant
      lookup_account({"email":"ana.silva@example.com"})
    4. tool
      {"account_id": "AC-311", "name": "Ana Silva"}
    5. assistant
      list_orders({"account_id":"AC-311"})
    6. 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"}]}
    7. assistant
      get_order({"order_id":"R-5521"})
      get_order({"order_id":"R-5530"})
    8. tool
      {"items": [{"name": "Oak table lamp", "quantity": 1, "sku": "lamp-oak"}], "order_id": "R-5521"}
    9. tool
      {"items": [{"name": "Steel kettle", "quantity": 1, "sku": "kettle-steel"}, {"name": "Grey mug", "quantity": 2, "sku": "mug-grey"}], "order_id": "R-5530"}
    10. assistant
      The kettle was on order R-5530 (Steel kettle, SKU `kettle-steel`). Now checking replacement stock:
      
      
      check_stock({"sku":"kettle-steel","warehouse":"north"})
    11. tool
      {"available": 0, "sku": "kettle-steel", "warehouse": "north"}
    12. assistant
      check_stock({"sku":"kettle-steel","warehouse":"south"})
    13. tool
      {"available": 4, "sku": "kettle-steel", "warehouse": "south"}
    14. assistant
      North is out of stock, but South has 4. Reserving a replacement from South and creating your return label:
      
      
      reserve_item({"sku":"kettle-steel","warehouse":"south","order_id":"R-5530"})
      create_return({"order_id":"R-5530","reason":"Kettle arrived with cracked lid"})
    15. tool
      {"reservation_id": "RS-4410", "ships_on": "2026-11-03"}
    16. tool
      {"error": "damaged items are not returned: open a damage claim with open_claim instead"}
    17. assistant
      open_claim({"order_id":"R-5530","reason":"Kettle arrived with cracked lid"})
    18. tool
      {"claim_id": "CL-2207", "order_id": "R-5530", "status": "open"}
    19. assistant
      Sorted, Ana! Here's what I did about your kettle (Steel kettle, order **R-5530**):
      
      - **Replacement:** I reserved a new kettle for you. The north warehouse was out of stock, so it's shipping from the **south warehouse** — reservation **RS-4410**, with a **ship date of 3 November 2026**.
      - **Damaged item:** No need to send the cracked kettle back — damage is handled through a claim, which I've opened under reference **CL-2207** (status: open). Just keep the kettle until further notice in case they need details.
      
      You'll get another update once the replacement ships. Is there anything else I can help with?

Comments

Sign in or create an account to comment.