Showcase

Will it run? A GPU shop for local models, built by DeepSeek V4.1 Flash 1017

Hostedvia OpenCodeOpenRouter
Running live in your browserNew tab
Speed
1295tok/s
First token included
Written
226.7Ktokens
Time to build
2m 55s
Cost to build
$0.20
DeepSeek V4.1 Flash 1017's builds and scores
Show the code the model wrote
HTML90,720 characters
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>GPU Store — Local Model Board</title>
</head>
<body>
  <div class="app">
    <header class="app-header">
      <div class="brand">
        <div class="brand-mark" aria-hidden="true">
          <span></span><span></span><span></span>
        </div>
        <div>
          <h1>GPU<span>store</span></h1>
          <p>Local inference, measured honestly</p>
        </div>
      </div>
      <div class="header-legend" aria-label="Memory bar legend">
        <span class="legend-title">Memory</span>
        <span class="legend-item"><i class="swatch weights"></i>Weights</span>
        <span class="legend-item"><i class="swatch context"></i>Context</span>
        <span class="legend-item"><i class="swatch overhead"></i>Overhead</span>
      </div>
      <div class="header-hints">
        <span><kbd>Enter</kbd> pick</span>
        <span><kbd>1–3</kbd> send</span>
        <span><kbd>⌥↑↓</kbd> move</span>
        <span><kbd>M</kbd> math</span>
      </div>
    </header>

    <main id="board" class="board" aria-label="GPU model board">
      <section id="modelsColumn" class="models-column" aria-label="Models">
        <div class="column-heading">
          <div>
            <span class="eyebrow">01</span>
            <h2>Models</h2>
          </div>
          <span class="column-note">Drag onto a machine</span>
        </div>
        <div id="modelList" class="model-list"></div>
      </section>
      <div id="machineColumns" class="machine-columns"></div>
    </main>

    <section id="panel" class="panel" aria-label="Picked card stream"></section>
  </div>

  <div id="toast" class="toast" role="status" aria-live="polite"></div>

  <style>
:root {
  --bg: #070b12;
  --bg-2: #0b111d;
  --panel: #0e1624;
  --panel-2: #111c2d;
  --line: #223047;
  --line-soft: #182335;
  --text: #f4f8ff;
  --muted: #8493a8;
  --muted-2: #58677d;
  --weights: #35d0ff;
  --context: #a78bfa;
  --overhead: #fbbf24;
  --over: #fb7185;
  --good: #34d399;
  --warn: #fbbf24;
  --bad: #fb7185;
  --radius: 12px;
  --mono: "SFMono-Regular", Menlo, Monaco, "Cascadia Mono", Consolas, "Liberation Mono", monospace;
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "SF Pro Text", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--sans);
  overflow: hidden;
}

body {
  background:
    radial-gradient(1100px 600px at 14% -10%, rgba(53, 208, 255, 0.12), transparent 58%),
    radial-gradient(900px 500px at 103% 16%, rgba(167, 139, 250, 0.09), transparent 60%),
    linear-gradient(180deg, #080c14 0%, #060a10 100%);
}

button, select, input { font: inherit; color: inherit; }
button { cursor: pointer; }
button:focus-visible, select:focus-visible, input:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid rgba(53, 208, 255, 0.9);
  outline-offset: 2px;
}

.app {
  height: 100dvh;
  padding: 10px 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 9px;
  max-width: 2200px;
  margin: 0 auto;
}

.app-header {
  height: 54px;
  min-height: 54px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 0 4px;
}

.brand { display: flex; align-items: center; gap: 11px; min-width: 250px; }
.brand-mark {
  width: 35px;
  height: 35px;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 3px;
  padding: 7px;
  border: 1px solid var(--line);
  border-radius: 9px;
  background: linear-gradient(155deg, rgba(53, 208, 255, 0.13), rgba(167, 139, 250, 0.08));
}
.brand-mark span { border-radius: 2px; background: var(--weights); }
.brand-mark span:nth-child(2) { background: var(--context); height: 70%; align-self: end; }
.brand-mark span:nth-child(3) { background: var(--overhead); height: 46%; align-self: end; }
.brand h1 {
  margin: 0;
  font-size: 20px;
  line-height: 1;
  font-weight: 800;
  letter-spacing: -0.8px;
}
.brand h1 span { color: var(--weights); }
.brand p {
  margin: 4px 0 0;
  color: var(--muted);
  font-size: 10.5px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  font-weight: 600;
}

.header-legend { display: flex; align-items: center; gap: 14px; color: var(--muted); font-size: 11px; }
.legend-title {
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 1.1px;
  font-size: 9px;
  color: var(--muted-2);
  margin-right: 1px;
}
.legend-item { display: inline-flex; align-items: center; gap: 5px; white-space: nowrap; }
.swatch { width: 7px; height: 7px; border-radius: 2px; display: inline-block; }
.swatch.weights { background: var(--weights); }
.swatch.context { background: var(--context); }
.swatch.overhead { background: var(--overhead); }

.header-hints { display: flex; align-items: center; gap: 11px; color: var(--muted-2); font-size: 10px; white-space: nowrap; }
.header-hints span { display: inline-flex; align-items: center; gap: 4px; }
kbd {
  display: inline-block;
  font-family: var(--mono);
  font-size: 9px;
  color: #c7d2e4;
  background: #131d2d;
  border: 1px solid #2a3a52;
  border-bottom-color: #3b4d68;
  border-radius: 4px;
  padding: 1px 4px;
  line-height: 1.45;
}

.board {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  gap: 10px;
}

.models-column {
  width: clamp(310px, 27vw, 430px);
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-soft);
  background: linear-gradient(180deg, rgba(16, 25, 40, 0.8), rgba(10, 16, 26, 0.72));
  border-radius: var(--radius);
  padding: 9px;
  transition: border-color 160ms, background 160ms;
}
.models-column.drop-active { border-color: var(--over); background: rgba(251, 113, 133, 0.08); }
.column-heading { height: 29px; min-height: 29px; display: flex; align-items: baseline; justify-content: space-between; padding: 0 2px 7px; }
.column-heading > div { display: flex; align-items: baseline; gap: 7px; }
.eyebrow { font-family: var(--mono); font-size: 9px; color: var(--weights); }
.column-heading h2 { margin: 0; font-size: 13px; letter-spacing: 0.2px; }
.column-note { color: var(--muted-2); font-size: 9.5px; }

.model-list { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 4px; }
.model-card {
  flex: 1 1 0;
  min-height: 38px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 8px;
  border: 1px solid var(--line-soft);
  border-radius: 8px;
  background: linear-gradient(100deg, #111c2c 0%, #0d1623 100%);
  cursor: grab;
  user-select: none;
  transition: border-color 130ms, transform 130ms, background 130ms;
  position: relative;
}
.model-card:hover { border-color: #36516f; background: linear-gradient(100deg, #142237 0%, #0f1a2a 100%); }
.model-card:active { cursor: grabbing; }
.model-card.dragging { opacity: 0.35; }
.model-card .grip {
  color: #3c4e67;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1;
  letter-spacing: -2px;
  flex: 0 0 8px;
}
.model-copy { min-width: 0; flex: 1; }
.model-line { display: flex; align-items: center; gap: 6px; min-width: 0; }
.model-name {
  font-size: 13px;
  line-height: 1.2;
  font-weight: 700;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  letter-spacing: -0.15px;
}
.tag {
  flex: 0 0 auto;
  font-family: var(--mono);
  font-size: 8px;
  letter-spacing: 0.3px;
  color: #cdd8e8;
  background: #1b2a3f;
  border: 1px solid #2c415b;
  border-radius: 3px;
  padding: 0 3px;
  line-height: 1.5;
  text-transform: uppercase;
}
.tag.moe { color: #d7c8ff; background: rgba(167, 139, 250, 0.12); border-color: rgba(167, 139, 250, 0.35); }
.model-meta {
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 9.2px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.model-meta b { color: #c8d6e8; font-weight: 500; }
.model-meta .q4 { color: var(--weights); }
.model-meta .q8 { color: #a8c2df; }
.model-weights {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 1px;
  font-family: var(--mono);
  font-size: 9px;
  line-height: 1.35;
  color: var(--muted-2);
  white-space: nowrap;
}
.model-weights b { font-weight: 500; color: #d4e0ef; }
.model-weights .q4 b { color: var(--weights); }
.model-weights .q8 b { color: #b9cce3; }

.machine-columns { flex: 1 1 auto; min-width: 0; display: flex; gap: 10px; }
.machine-column {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line-soft);
  border-radius: var(--radius);
  background: linear-gradient(180deg, rgba(17, 27, 44, 0.9), rgba(10, 16, 27, 0.9));
  overflow: hidden;
  position: relative;
  transition: border-color 150ms, box-shadow 150ms;
}
.machine-column.drag-over { border-color: var(--weights); box-shadow: 0 0 0 1px rgba(53, 208, 255, 0.25), 0 16px 45px rgba(0, 0, 0, 0.3); }
.machine-column.previewing { border-color: rgba(53, 208, 255, 0.65); }

.machine-head { padding: 8px 10px 7px; border-bottom: 1px solid var(--line-soft); background: rgba(13, 22, 36, 0.7); }
.machine-title-row { display: flex; align-items: center; gap: 7px; }
.gpu-select {
  flex: 1 1 auto;
  min-width: 0;
  appearance: none;
  border: 1px solid #2a3b54;
  background: #122033 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0 0l4 5 4-5z' fill='%237b8da5'/%3E%3C/svg%3E") no-repeat right 8px center;
  border-radius: 6px;
  padding: 5px 22px 5px 8px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: -0.2px;
  cursor: pointer;
}
.gpu-select:hover { border-color: #3a5272; }
.icon-button {
  border: 1px solid #2a3b54;
  background: #0e1827;
  border-radius: 6px;
  width: 25px;
  height: 25px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 15px;
  line-height: 1;
  flex: 0 0 auto;
}
.icon-button:hover { color: var(--text); border-color: #435a77; background: #18263a; }

.machine-subrow { display: flex; align-items: center; gap: 7px; margin-top: 6px; min-width: 0; flex-wrap: wrap; row-gap: 4px; }
.count-seg { display: flex; border: 1px solid #2a3b54; border-radius: 5px; overflow: hidden; flex: 0 0 auto; }
.count-seg button {
  border: 0;
  background: #0e1827;
  color: var(--muted);
  font-family: var(--mono);
  font-size: 9.5px;
  padding: 3px 7px;
  line-height: 1.3;
}
.count-seg button + button { border-left: 1px solid #2a3b54; }
.count-seg button.active { background: #1b3d51; color: var(--weights); }
.price {
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--good);
  font-weight: 500;
  white-space: nowrap;
  letter-spacing: -0.3px;
}
.spill-control { margin-left: auto; display: flex; align-items: center; gap: 5px; color: var(--muted); font-size: 9.5px; white-space: nowrap; }
.switch {
  width: 25px;
  height: 14px;
  border-radius: 10px;
  background: #253347;
  border: 1px solid #34465e;
  position: relative;
  cursor: pointer;
  transition: background 120ms;
  padding: 0;
  flex: 0 0 auto;
}
.switch::after {
  content: "";
  position: absolute;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: #8da0b8;
  top: 1.5px;
  left: 2px;
  transition: transform 120ms, background 120ms;
}
.switch.on { background: rgba(53, 208, 255, 0.25); border-color: rgba(53, 208, 255, 0.55); }
.switch.on::after { transform: translateX(10px); background: var(--weights); }
.ram-select {
  appearance: none;
  border: 1px solid #2a3b54;
  background: #0e1827;
  border-radius: 4px;
  padding: 2px 15px 2px 5px;
  font-family: var(--mono);
  font-size: 9px;
  color: #bfd0e4;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='4'%3E%3Cpath d='M0 0l3.5 4L7 0z' fill='%237b8da5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 4px center;
}
.ram-select:disabled { opacity: 0.32; }
.cheapest-button {
  border: 1px solid #2a3b54;
  background: #101e30;
  border-radius: 5px;
  padding: 3px 6px;
  font-size: 9px;
  font-weight: 600;
  color: #c5d4e8;
  white-space: nowrap;
  flex: 0 0 auto;
  margin-left: auto;
}
.cheapest-button:hover { background: #172b40; border-color: #3e5a7b; color: #fff; }
.cheapest-msg {
  font-family: var(--mono);
  font-size: 9px;
  color: var(--bad);
  margin-top: 5px;
  line-height: 1.3;
}
.cheapest-msg:empty { display: none; }

.drop-verdict {
  display: none;
  margin: 0 10px;
  padding: 4px 7px;
  border-radius: 5px;
  border: 1px solid rgba(53, 208, 255, 0.4);
  background: rgba(53, 208, 255, 0.1);
  color: #bdf0ff;
  font-family: var(--mono);
  font-size: 10px;
  line-height: 1.25;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.drop-verdict.show { display: block; }
.drop-verdict.bad { color: #ffc1cb; border-color: rgba(251, 113, 133, 0.45); background: rgba(251, 113, 133, 0.11); }

.bars-wrap { padding: 7px 10px 5px; }
.bar-row { margin-bottom: 6px; }
.bar-row:last-child { margin-bottom: 0; }
.bar-label { display: flex; justify-content: space-between; align-items: baseline; gap: 6px; margin-bottom: 3px; font-family: var(--mono); font-size: 9px; color: var(--muted-2); }
.bar-label .bar-name { color: var(--muted); font-weight: 500; }
.bar-label .bar-value { white-space: nowrap; }
.bar-label .bar-value.ok { color: #a8bdd4; }
.bar-label .bar-value.over { color: var(--bad); }
.bar-label .bar-value.ram { color: var(--weights); }
.mem-bar {
  height: 11px;
  border-radius: 3px;
  background: #08101b;
  border: 1px solid #1e2b3d;
  overflow: hidden;
  position: relative;
  display: flex;
}
.mem-bar.ram-bar { height: 9px; }
.mem-seg { height: 100%; min-width: 0; position: relative; flex: 0 0 auto; }
.mem-seg.weights { background: var(--weights); }
.mem-seg.context { background: var(--context); }
.mem-seg.overhead { background: var(--overhead); }
.mem-seg.model-start { border-left: 1px solid rgba(4, 8, 14, 0.65); }
.mem-seg.overflow {
  background-image: repeating-linear-gradient(135deg, rgba(251, 113, 133, 0.95) 0 3px, rgba(100, 22, 42, 0.95) 3px 6px);
  background-color: rgba(251, 113, 133, 0.5);
}
.cap-mark {
  position: absolute;
  top: -1px;
  bottom: -1px;
  width: 2px;
  background: #f4f8ff;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55);
  z-index: 5;
  pointer-events: none;
}
.ram-color { background: rgba(53, 208, 255, 0.65) !important; }
.fitline {
  margin: 1px 10px 6px;
  min-height: 16px;
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 500;
  color: var(--good);
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.fitline::before { content: "●"; font-size: 7px; color: currentColor; }
.fitline.over { color: var(--bad); }
.fitline.ram { color: var(--weights); }
.fitline.preview { color: #bdf0ff; }
.fitline.preview.over { color: var(--bad); }

.inst-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 2px 8px 8px;
  scrollbar-width: thin;
  scrollbar-color: #2b3e57 transparent;
  position: relative;
}
.inst-list::-webkit-scrollbar { width: 6px; }
.inst-list::-webkit-scrollbar-thumb { background: #2b3e57; border-radius: 4px; }
.empty-machine {
  border: 1px dashed #25354b;
  border-radius: 7px;
  min-height: 65px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #4d5f76;
  font-size: 10px;
  text-align: center;
  padding: 10px;
}

.inst-card {
  border: 1px solid #253650;
  background: linear-gradient(105deg, #152237, #101a29);
  border-radius: 8px;
  padding: 6px 8px 7px;
  margin-bottom: 5px;
  cursor: grab;
  position: relative;
  transition: border-color 130ms, background 130ms;
}
.inst-card:last-child { margin-bottom: 0; }
.inst-card:hover { border-color: #3b5776; }
.inst-card.selected { border-color: rgba(53, 208, 255, 0.72); background: linear-gradient(105deg, #173047, #111e30); box-shadow: inset 0 0 0 1px rgba(53, 208, 255, 0.1); }
.inst-card.wontrun { border-color: rgba(251, 113, 133, 0.35); }
.inst-card.dragging { opacity: 0.3; }
.inst-card.drop-above::before {
  content: "";
  position: absolute;
  top: -4px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--weights);
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(53, 208, 255, 0.6);
}
.inst-card.drop-below::after {
  content: "";
  position: absolute;
  bottom: -4px;
  left: 0;
  right: 0;
  height: 2px;
  background: var(--weights);
  border-radius: 2px;
  box-shadow: 0 0 8px rgba(53, 208, 255, 0.6);
}
.inst-head { display: flex; align-items: center; gap: 5px; min-width: 0; }
.inst-title { flex: 1; min-width: 0; display: flex; align-items: center; gap: 5px; }
.inst-name { font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: -0.15px; }
.inst-remove {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  border: 0;
  border-radius: 4px;
  background: transparent;
  color: #5e7088;
  font-size: 13px;
  line-height: 1;
  padding: 0;
}
.inst-remove:hover { color: var(--bad); background: rgba(251, 113, 133, 0.1); }
.inst-metrics {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 6px;
  margin: 3px 0 5px;
  min-width: 0;
  font-family: var(--mono);
}
.inst-speed { font-size: 15px; font-weight: 500; color: var(--text); letter-spacing: -0.7px; white-space: nowrap; }
.inst-speed small { font-size: 8px; color: var(--muted-2); letter-spacing: 0; font-weight: 400; }
.inst-speed.bad { font-size: 11px; color: var(--bad); letter-spacing: -0.1px; }
.inst-verdict { font-size: 9px; color: var(--good); text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.inst-verdict.ram { color: var(--weights); }
.inst-verdict.bad { color: var(--bad); }
.inst-controls { display: flex; flex-direction: column; gap: 4px; }
.control-row { display: flex; align-items: center; gap: 4px; min-width: 0; flex-wrap: wrap; row-gap: 3px; }
.mini-seg { display: flex; border: 1px solid #2a3b54; border-radius: 4px; overflow: hidden; flex: 0 0 auto; }
.mini-seg button {
  border: 0;
  background: #0c1623;
  color: #6f8299;
  font-family: var(--mono);
  font-size: 8.5px;
  line-height: 1.55;
  padding: 1px 4px;
}
.mini-seg button + button { border-left: 1px solid #2a3b54; }
.mini-seg button.active { background: #1b3d51; color: var(--weights); }
.mini-seg.cache button.active { background: #2c2846; color: #c9b5ff; }
.ctx-summary { margin-left: auto; font-family: var(--mono); font-size: 8.8px; color: var(--muted-2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ctx-summary b { color: #baccdf; font-weight: 500; }
.ctx-summary .maxfit { color: #7f94aa; }
.slider-row { display: flex; align-items: center; gap: 6px; min-width: 0; }
input[type="range"] {
  -webkit-appearance: none;
  appearance: none;
  min-width: 0;
  flex: 1;
  height: 12px;
  background: transparent;
  cursor: pointer;
  margin: 0;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, #355270, #25384f);
}
input[type="range"]::-moz-range-track {
  height: 3px;
  border-radius: 2px;
  background: linear-gradient(90deg, #355270, #25384f);
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 9px;
  height: 9px;
  margin-top: -3px;
  border-radius: 50%;
  background: var(--weights);
  border: 1px solid #06111a;
  box-shadow: 0 0 0 1px rgba(53, 208, 255, 0.45);
}
input[type="range"]::-moz-range-thumb {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--weights);
  border: 1px solid #06111a;
}
.slider-max { flex: 0 0 auto; font-family: var(--mono); font-size: 7.5px; color: #6e839b; white-space: nowrap; }
.slider-max b { color: #a7bad0; font-weight: 500; }

.add-machine {
  flex: 0 0 132px;
  border: 1px dashed #2a3c55;
  border-radius: var(--radius);
  background: rgba(14, 23, 37, 0.4);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #60758d;
  font-size: 11px;
  font-weight: 600;
  padding: 14px;
  transition: border-color 130ms, color 130ms, background 130ms;
}
.add-machine:hover { border-color: #3e5c7c; color: #c5d9ed; background: rgba(19, 33, 52, 0.65); }
.add-machine .plus { font-size: 26px; font-weight: 300; line-height: 0.7; color: #4f6a86; }
.add-machine small { font-size: 8.5px; font-weight: 400; color: #4c5e73; line-height: 1.35; text-align: center; }

.panel {
  flex: 0 0 auto;
  height: clamp(194px, 26vh, 274px);
  min-height: 194px;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: linear-gradient(115deg, rgba(16, 26, 43, 0.95), rgba(10, 16, 27, 0.97));
  display: flex;
  gap: 13px;
  padding: 11px 13px;
  overflow: hidden;
  position: relative;
}
.panel::before {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(53, 208, 255, 0.5), rgba(167, 139, 250, 0.35), transparent);
}
.panel-empty { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--muted-2); font-size: 12px; }

.panel-stats {
  flex: 0 0 clamp(190px, 17vw, 238px);
  border-right: 1px solid var(--line-soft);
  padding-right: 13px;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.panel-kicker { display: flex; align-items: center; gap: 7px; color: var(--weights); font-family: var(--mono); font-size: 8.5px; letter-spacing: 1px; text-transform: uppercase; }
.picked-name { margin: 5px 0 0; font-size: clamp(15px, 1.3vw, 19px); line-height: 1.1; font-weight: 800; letter-spacing: -0.55px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.picked-machine { margin-top: 3px; color: var(--muted); font-size: 9.5px; line-height: 1.35; white-space: normal; overflow: hidden; }
.panel-speed-row { display: flex; align-items: baseline; gap: 7px; margin-top: auto; padding-top: 6px; }
.panel-speed { font-family: var(--mono); font-size: clamp(25px, 2.3vw, 36px); line-height: 0.95; font-weight: 500; letter-spacing: -2px; color: var(--text); }
.panel-speed-unit { font-family: var(--mono); font-size: 9px; color: var(--muted-2); }
.panel-verdict { margin-top: 5px; font-family: var(--mono); font-size: 10px; line-height: 1.35; color: var(--good); white-space: normal; overflow: hidden; }
.panel-verdict.ram { color: var(--weights); }
.panel-verdict.bad { color: var(--bad); }
.panel-spec { margin-top: 3px; color: var(--muted-2); font-family: var(--mono); font-size: 8.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.panel-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.panel-toolbar { display: flex; align-items: center; gap: 8px; min-height: 25px; margin-bottom: 4px; min-width: 0; }
.panel-title { font-size: 11px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.panel-title span { color: var(--muted-2); font-weight: 500; }
.panel-status { margin-left: auto; font-family: var(--mono); font-size: 9px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.panel-status.live { color: var(--weights); }
.panel-status.paused { color: var(--warn); }
.play-button {
  border: 1px solid rgba(53, 208, 255, 0.5);
  background: rgba(53, 208, 255, 0.12);
  color: #bdf0ff;
  border-radius: 5px;
  padding: 3px 8px;
  font-size: 9px;
  font-weight: 700;
  white-space: nowrap;
  flex: 0 0 auto;
}
.play-button:hover { background: rgba(53, 208, 255, 0.2); }
.math-button {
  border: 1px solid #2b3b52;
  background: #101b2b;
  color: var(--muted);
  border-radius: 5px;
  padding: 3px 7px;
  font-size: 9px;
  font-weight: 600;
  white-space: nowrap;
  flex: 0 0 auto;
}
.math-button:hover { color: #dce9f8; border-color: #405873; }
.math-button.active { background: rgba(167, 139, 250, 0.15); color: #d8c8ff; border-color: rgba(167, 139, 250, 0.55); }

.stream-wrap {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #2b3e57 transparent;
  padding-right: 4px;
}
.stream-wrap::-webkit-scrollbar { width: 5px; }
.stream-wrap::-webkit-scrollbar-thumb { background: #2b3e57; border-radius: 4px; }
.stream-text {
  font-size: clamp(12px, 0.96vw, 15px);
  line-height: 1.48;
  color: #dce7f5;
  letter-spacing: -0.08px;
}
.stream-text .caret {
  display: inline-block;
  width: 2px;
  height: 1em;
  background: var(--weights);
  vertical-align: -2px;
  margin-left: 2px;
  animation: blink 0.85s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0; } }
.stream-placeholder { color: var(--muted-2); font-size: 11px; line-height: 1.5; }
.stream-placeholder strong { color: var(--muted); font-weight: 600; }
.stream-meta { margin-top: 4px; font-family: var(--mono); font-size: 8.5px; color: var(--muted-2); }

.fixes {
  flex: 0 0 clamp(240px, 24vw, 320px);
  border-left: 1px solid var(--line-soft);
  padding-left: 12px;
  min-width: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: #2b3e57 transparent;
}
.fixes-title { font-size: 10.5px; font-weight: 700; color: var(--bad); margin-bottom: 2px; }
.fixes-sub { font-size: 8.5px; color: var(--muted-2); margin-bottom: 7px; line-height: 1.35; }
.fix-button {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  border: 1px solid #2c3e55;
  background: #101b2b;
  border-radius: 5px;
  padding: 4px 7px;
  margin-bottom: 4px;
  text-align: left;
  font-size: 9.5px;
  color: #c6d6e8;
}
.fix-button:hover { border-color: rgba(53, 208, 255, 0.55); background: #142438; }
.fix-button .fix-value { font-family: var(--mono); font-size: 8.5px; color: var(--weights); white-space: nowrap; }
.fix-empty { color: var(--muted-2); font-size: 9px; line-height: 1.45; }

.math-scroll { flex: 1; min-height: 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: #2b3e57 transparent; padding-right: 4px; }
.math-grid { display: grid; grid-template-columns: repeat(4, minmax(145px, 1fr)); gap: 5px; align-content: start; }
.math-cell { border: 1px solid #213047; border-radius: 5px; background: rgba(16, 27, 44, 0.72); padding: 4px 7px; min-width: 0; }
.math-cell.wide { grid-column: span 2; }
.math-cell .label { font-family: var(--mono); font-size: 7.5px; letter-spacing: 0.4px; text-transform: uppercase; color: var(--muted-2); }
.math-cell .value { font-family: var(--mono); font-size: 12px; color: #e5f0fd; margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.math-cell .formula { font-family: var(--mono); font-size: 8px; color: #71859c; margin-top: 1px; white-space: normal; line-height: 1.25; }
.math-cell.good .value { color: var(--good); }
.math-cell.bad .value { color: var(--bad); }
.math-cell.ram .value { color: var(--weights); }

.toast {
  position: fixed;
  left: 50%;
  bottom: 16px;
  transform: translate(-50%, 10px);
  background: #152235;
  border: 1px solid #334963;
  color: #d8e7f8;
  border-radius: 7px;
  padding: 7px 12px;
  font-size: 10px;
  font-family: var(--mono);
  opacity: 0;
  pointer-events: none;
  transition: opacity 160ms, transform 160ms;
  z-index: 50;
  max-width: 700px;
  text-align: center;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

@media (max-height: 860px) {
  .app { padding: 7px 11px 9px; gap: 7px; }
  .app-header { height: 46px; min-height: 46px; }
  .brand-mark { width: 30px; height: 30px; padding: 6px; }
  .brand h1 { font-size: 18px; }
  .brand p { font-size: 9px; margin-top: 2px; }
  .header-legend { gap: 9px; font-size: 10px; }
  .header-hints { gap: 7px; font-size: 9px; }
  .panel { height: clamp(178px, 25vh, 208px); min-height: 178px; padding: 9px 11px; gap: 10px; }
  .panel-stats { flex-basis: 190px; padding-right: 10px; }
  .panel-speed { font-size: 26px; }
  .panel-title { font-size: 10px; }
  .machine-head { padding: 6px 8px 5px; }
  .gpu-select { font-size: 11px; padding-top: 4px; padding-bottom: 4px; }
  .machine-subrow { margin-top: 4px; gap: 5px; }
  .bars-wrap { padding: 5px 8px 3px; }
  .bar-row { margin-bottom: 4px; }
  .fitline { margin: 0 8px 4px; font-size: 10px; min-height: 14px; }
  .inst-list { padding: 2px 7px 6px; }
  .inst-card { padding: 5px 7px 6px; margin-bottom: 4px; }
  .inst-metrics { margin: 2px 0 4px; }
  .model-card { min-height: 34px; padding: 4px 7px; }
  .model-name { font-size: 12.5px; }
  .model-meta { font-size: 9px; }
  .model-weights { font-size: 8.8px; }
  .column-heading { height: 25px; min-height: 25px; padding-bottom: 5px; }
  .stream-text { font-size: 12px; line-height: 1.4; }
  .math-cell .value { font-size: 11px; }
}

@media (max-width: 1180px) {
  .header-hints span:nth-child(3) { display: none; }
  .header-legend .legend-title { display: none; }
  .model-weights { display: none; }
}

@media (min-width: 1500px) {
  .models-column { padding: 11px; }
  .column-heading { height: 33px; min-height: 33px; }
  .column-heading h2 { font-size: 15px; }
  .column-note { font-size: 10.5px; }
  .model-card { padding: 6px 10px; }
  .model-name { font-size: 14.5px; }
  .model-meta { font-size: 10px; }
  .model-weights { font-size: 10px; }
  .machine-head { padding: 10px 12px 8px; }
  .gpu-select { font-size: 13px; padding: 6px 24px 6px 9px; }
  .machine-subrow { margin-top: 7px; gap: 8px; }
  .count-seg button { font-size: 10.5px; padding: 4px 9px; }
  .price { font-size: 13px; }
  .spill-control { font-size: 10.5px; gap: 6px; }
  .ram-select { font-size: 10px; padding: 3px 17px 3px 6px; }
  .cheapest-button { font-size: 10px; padding: 4px 8px; }
  .bars-wrap { padding: 9px 12px 6px; }
  .bar-label { font-size: 10px; }
  .mem-bar { height: 13px; }
  .fitline { font-size: 12px; margin: 1px 12px 7px; }
  .inst-list { padding: 3px 10px 10px; }
  .inst-card { padding: 8px 10px 9px; }
  .inst-name { font-size: 13px; }
  .inst-speed { font-size: 17px; }
  .inst-verdict { font-size: 9.5px; }
  .mini-seg button { font-size: 9px; padding: 2px 6px; }
  .ctx-summary { font-size: 9px; }
  .ctx-summary .maxfit { font-size: 8.5px; }
  .slider-max { font-size: 8.5px; }
  .panel-stats { flex-basis: 250px; }
  .picked-name { font-size: 20px; }
  .panel-speed { font-size: 38px; }
  .panel-verdict { font-size: 11px; }
  .panel-title { font-size: 12px; }
  .panel-status { font-size: 10px; }
  .stream-text { font-size: 16px; }
  .stream-placeholder { font-size: 12px; }
  .stream-meta { font-size: 9.5px; }
  .fixes-title { font-size: 11.5px; }
  .fixes-sub { font-size: 9.5px; }
  .fix-button { font-size: 10.5px; padding: 5px 8px; }
  .math-cell .value { font-size: 13px; }
}

  </style>
  <script>
/* GPU Store — a one-screen local-inference board. All state lives in memory. */

const GIB = 1024 ** 3;
const KIB = 1024;
const EPS = 1e-7;
const RAM_BANDWIDTH = 80e9;

const GPUS = [
  { id: "rtx3060", name: "RTX 3060 12GB", memory: 12, bandwidth: 360, price: 280 },
  { id: "rtx3090", name: "RTX 3090 24GB (used)", memory: 24, bandwidth: 936, price: 700 },
  { id: "rtx4090", name: "RTX 4090 24GB", memory: 24, bandwidth: 1008, price: 1999 },
  { id: "rtx5090", name: "RTX 5090 32GB", memory: 32, bandwidth: 1792, price: 2599 },
  { id: "a6000", name: "RTX A6000 48GB (used)", memory: 48, bandwidth: 768, price: 3300 },
];

const MODELS = [
  { id: "gemma4-12b", name: "Gemma 4 12B", total: 11.95, active: 11.95, ctxKiB: 16, longest: 262144 },
  { id: "gpt-oss-20b", name: "gpt-oss-20b", total: 20.9, active: 3.6, ctxKiB: 24, longest: 131072 },
  { id: "gemma4-26b", name: "Gemma 4 26B A4B", total: 25.2, active: 3.8, ctxKiB: 20, longest: 262144 },
  { id: "qwen38-27b", name: "Qwen3.8 27B", total: 27.8, active: 27.8, ctxKiB: 64, longest: 262144 },
  { id: "gemma4-31b", name: "Gemma 4 31B", total: 30.7, active: 30.7, ctxKiB: 80, longest: 262144 },
  { id: "qwen36-35b", name: "Qwen3.6 35B-A3B", total: 35, active: 3, ctxKiB: 20, longest: 262144 },
  { id: "gpt-oss-120b", name: "gpt-oss-120b", total: 116.8, active: 5.1, ctxKiB: 36, longest: 131072 },
  { id: "glm53-flash", name: "GLM-5.3-Flash", total: 320, active: 18, ctxKiB: 11, longest: 1048576 },
  { id: "glm53", name: "GLM-5.3", total: 753, active: 40, ctxKiB: 87.75, longest: 1048576 },
  { id: "kimi-k26", name: "Kimi K2.6", total: 1000, active: 32, ctxKiB: 68.625, longest: 262144 },
];

const ANSWER = `Every single token a local language model generates begins as a long list of numbers that must be read from memory. The arithmetic is fast and highly parallel, but the weights cannot be computed from nothing. For each token, the machine streams the active weights once through its memory system, multiplies them by the current activations, and writes a new set of activations for the next layer. That read is the bottleneck. A graphics card may perform trillions of floating point operations each second, yet its arithmetic units sit idle whenever the data they need has not arrived. Memory bandwidth, measured in gigabytes per second, sets the delivery rate. Divide the bytes that must be read for one token by that rate and you get the theoretical ceiling in tokens per second. This is why a model with fewer active parameters can feel dramatically quicker than a larger model, even when both use the same card. A mixture of experts only wakes a small fraction of its weights for each token, so it reads fewer bytes and produces tokens faster. Dense models must read all their weights every time. Quantization helps for the same reason. Storing weights in four bits instead of eight nearly halves the bytes moved, so the ceiling nearly doubles. Context also consumes memory, but the overwhelming traffic during generation comes from weights. Two cards can hold a larger model, yet generation does not become twice as fast: every token passes through both cards in turn, so the effective bandwidth remains that of one card. When weights spill into system RAM, the much slower RAM bandwidth dominates and the token rate collapses. The practical lesson is simple: count active bytes, not marketing numbers. Memory capacity decides what can run, but memory bandwidth decides how quickly it answers.`;
const WORDS = ANSWER.split(/\s+/).filter(Boolean);

let nextId = 0;
const newId = (prefix) => `${prefix}-${++nextId}`;

const initialQwen = {
  id: newId("inst"),
  modelId: "qwen38-27b",
  quant: "Q4_0",
  cache: 16,
  context: 32768,
};

const state = {
  machines: [
    {
      id: newId("machine"),
      gpuId: "rtx4090",
      count: 1,
      spill: false,
      ram: 64,
      models: [initialQwen],
      cheapestMsg: "",
    },
    {
      id: newId("machine"),
      gpuId: "rtx3060",
      count: 1,
      spill: false,
      ram: 64,
      models: [],
      cheapestMsg: "",
    },
  ],
  pickedId: initialQwen.id,
  playing: false,
  finished: false,
  wordCount: 0,
  credit: 0,
  elapsed: 0,
  lastTs: null,
  math: false,
  drag: null,
  preview: null,
  focusAfterRender: null,
};

const board = document.getElementById("board");
const modelList = document.getElementById("modelList");
const machineColumns = document.getElementById("machineColumns");
const modelsColumn = document.getElementById("modelsColumn");
const panel = document.getElementById("panel");
const toastEl = document.getElementById("toast");

/* ------------------------------------------------------------------ *
 * Model helpers
 * ------------------------------------------------------------------ */

function modelById(id) {
  return MODELS.find((m) => m.id === id);
}

function gpuById(id) {
  return GPUS.find((g) => g.id === id);
}

function bitsFor(quant) {
  return quant === "Q8_0" ? 8.5 : 4.5;
}

function weightGiB(model, quant) {
  return (model.total * 1e9 * bitsFor(quant)) / 8 / GIB;
}

function contextGiB(model, inst) {
  return (inst.context * model.ctxKiB * KIB * (inst.cache === 8 ? 0.5 : 1)) / GIB;
}

function activeBytes(model, quant) {
  return (model.active * 1e9 * bitsFor(quant)) / 8;
}

function isMoE(model) {
  return model.active < model.total - 1e-9;
}

function fmtParams(billions) {
  if (billions >= 1000) return `${(billions / 1000).toFixed(billions % 1000 === 0 ? 0 : 2)}T`;
  return `${billions.toFixed(billions < 100 ? 2 : 1)}B`;
}

function fmtGiB(n) {
  return Number(n).toFixed(2);
}

function fmtOver(n) {
  const text = Number(n).toFixed(2);
  return text === "0.00" && n > 0 ? "0.01" : text;
}

function fmtSpeed(n) {
  return Number(n).toFixed(1);
}

function fmtCtx(n) {
  return Number(n).toLocaleString("en-US");
}

function fmtPrice(n) {
  return `$${Number(n).toLocaleString("en-US")}`;
}

function esc(value) {
  return String(value)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;");
}

function locateInstance(instId) {
  for (const machine of state.machines) {
    const index = machine.models.findIndex((inst) => inst.id === instId);
    if (index !== -1) return { machine, inst: machine.models[index], index };
  }
  return null;
}

function instanceInfo(metrics, instId) {
  return metrics.info.find((x) => x.inst.id === instId) || null;
}

/* ------------------------------------------------------------------ *
 * Memory + speed math
 * ------------------------------------------------------------------ */

function evaluateMachine(machine, modelsOverride) {
  const gpu = gpuById(machine.gpuId);
  const count = machine.count;
  const models = modelsOverride || machine.models;
  const n = models.length;

  const weights = models.map((inst) => weightGiB(modelById(inst.modelId), inst.quant));
  const contexts = models.map((inst) => contextGiB(modelById(inst.modelId), inst));
  const active = models.map((inst) => activeBytes(modelById(inst.modelId), inst.quant));

  const totalWeights = weights.reduce((a, b) => a + b, 0);
  const totalContext = contexts.reduce((a, b) => a + b, 0);
  const reservedPerCard = totalContext / count + n;
  let remainingPerCard = Math.max(0, gpu.memory - reservedPerCard);

  const allocations = [];
  for (let i = 0; i < n; i++) {
    if (machine.spill) {
      const perCard = weights[i] / count;
      const onPerCard = Math.min(perCard, remainingPerCard);
      remainingPerCard -= onPerCard;
      allocations.push({
        onCard: onPerCard * count,
        perCard: onPerCard,
        ram: Math.max(0, weights[i] - onPerCard * count),
      });
    } else {
      allocations.push({
        onCard: weights[i],
        perCard: weights[i] / count,
        ram: 0,
      });
    }
  }

  const onCardWeights = allocations.reduce((a, x) => a + x.onCard, 0);
  const ramUsed = allocations.reduce((a, x) => a + x.ram, 0);
  const cardUsed = reservedPerCard + onCardWeights / count;

  let fits;
  let fitLine;
  let cardOver;
  let ramOver = 0;

  if (machine.spill) {
    cardOver = Math.max(0, reservedPerCard - gpu.memory);
    ramOver = Math.max(0, ramUsed - machine.ram);
    fits = cardOver <= EPS && ramOver <= EPS;
    if (cardOver > EPS) {
      fitLine = `Over by ${fmtOver(cardOver)} GiB`;
    } else if (ramOver > EPS) {
      fitLine = `Over by ${fmtOver(ramOver)} GiB of RAM`;
    } else {
      fitLine = `Fits, ${fmtGiB(ramUsed)} GiB in RAM`;
    }
  } else {
    cardOver = Math.max(0, cardUsed - gpu.memory);
    fits = cardOver <= EPS;
    fitLine = fits
      ? `Fits, ${fmtGiB(gpu.memory - cardUsed)} GiB to spare`
      : `Over by ${fmtOver(cardOver)} GiB`;
  }

  const info = models.map((inst, i) => {
    const model = modelById(inst.modelId);
    const alloc = allocations[i];
    let rawSpeed;
    if (machine.spill && alloc.ram > EPS && weights[i] > EPS) {
      const onShare = alloc.onCard / weights[i];
      const onBytes = active[i] * onShare;
      const ramBytes = active[i] - onBytes;
      rawSpeed = 1 / (onBytes / (gpu.bandwidth * 1e9) + ramBytes / RAM_BANDWIDTH);
    } else {
      rawSpeed = (gpu.bandwidth * 1e9) / active[i];
    }

    return {
      inst,
      model,
      weight: weights[i],
      context: contexts[i],
      activeBytes: active[i],
      allocation: alloc,
      ram: alloc.ram,
      onCard: alloc.onCard,
      onCardPerCard: alloc.perCard,
      speedRaw: rawSpeed,
      speed: Math.round(rawSpeed * 10) / 10,
      fits,
    };
  });

  const cardSegments = [];
  models.forEach((inst, i) => {
    const model = modelById(inst.modelId);
    cardSegments.push({
      type: "weights",
      amount: allocations[i].perCard,
      model: model.name,
      modelStart: true,
    });
    cardSegments.push({
      type: "context",
      amount: contexts[i] / count,
      model: model.name,
    });
    cardSegments.push({
      type: "overhead",
      amount: 1,
      model: model.name,
    });
  });

  const ramSegments = models.map((inst, i) => ({
    type: "weights",
    amount: allocations[i].ram,
    model: modelById(inst.modelId).name,
    modelStart: true,
  }));

  const needTotal =
    weights.reduce((a, b) => a + b, 0) +
    contexts.reduce((a, b) => a + b, 0) +
    n;

  return {
    gpu,
    count,
    models,
    weights,
    contexts,
    active,
    totalWeights,
    totalContext,
    reservedPerCard,
    cardUsed,
    cardTotals: Array.from({ length: count }, () => cardUsed),
    ramUsed,
    fits,
    fitLine,
    cardOver,
    ramOver,
    spare: gpu.memory - cardUsed,
    info,
    cardSegments,
    ramSegments,
    needTotal,
  };
}

function maxContextThatFits(machine, instId) {
  const located = locateInstance(instId);
  if (!located) return null;
  const inst = located.inst;
  const model = modelById(inst.modelId);
  const steps = Math.floor(model.longest / 1024);

  const test = (step) => {
    const models = machine.models.map((x) =>
      x.id === inst.id ? { ...x, context: step * 1024 } : x,
    );
    return evaluateMachine({ ...machine, models }).fits;
  };

  if (!test(1)) return null;
  let lo = 1;
  let hi = steps;
  let best = 1;
  while (lo <= hi) {
    const mid = Math.floor((lo + hi) / 2);
    if (test(mid)) {
      best = mid;
      lo = mid + 1;
    } else {
      hi = mid - 1;
    }
  }
  return best * 1024;
}

function fitOptions(machine, inst) {
  const options = [];

  const maxContext = maxContextThatFits(machine, inst.id);
  if (maxContext !== null && maxContext < inst.context && maxContext >= 1024) {
    options.push({
      kind: "context",
      label: "Most context that fits",
      value: `${fmtCtx(maxContext)} tok`,
    });
  }

  if (inst.cache !== 8) {
    const models = machine.models.map((x) => (x.id === inst.id ? { ...x, cache: 8 } : x));
    if (evaluateMachine({ ...machine, models }).fits) {
      const testInfo = instanceInfo(
        evaluateMachine({ ...machine, models }),
        inst.id,
      );
      options.push({
        kind: "cache",
        label: "8-bit cache",
        value: `${fmtGiB(testInfo ? testInfo.context : 0)} GiB ctx`,
      });
    }
  }

  if (inst.quant !== "Q4_0") {
    const models = machine.models.map((x) =>
      x.id === inst.id ? { ...x, quant: "Q4_0" } : x,
    );
    const testMetrics = evaluateMachine({ ...machine, models });
    if (testMetrics.fits) {
      const testInfo = instanceInfo(testMetrics, inst.id);
      options.push({
        kind: "quant",
        label: "Q4_0 weights",
        value: `${fmtGiB(testInfo ? testInfo.weight : 0)} GiB`,
      });
    }
  }

  if (machine.count === 1) {
    const testMetrics = evaluateMachine({ ...machine, count: 2 });
    if (testMetrics.fits) {
      options.push({
        kind: "card",
        label: "Add a second card",
        value: `+${gpuById(machine.gpuId).memory} GiB · ${fmtPrice(gpuById(machine.gpuId).price)}`,
      });
    }
  }

  if (!machine.spill) {
    const testMetrics = evaluateMachine({ ...machine, spill: true });
    if (testMetrics.fits) {
      options.push({
        kind: "spill",
        label: `Spill to ${machine.ram} GiB RAM`,
        value: `${fmtGiB(testMetrics.ramUsed)} GiB in RAM`,
      });
    }
  }

  return options;
}

/* ------------------------------------------------------------------ *
 * Formatting/render helpers
 * ------------------------------------------------------------------ */

function modelCardHtml(model) {
  const q4 = weightGiB(model, "Q4_0");
  const q8 = weightGiB(model, "Q8_0");
  const moe = isMoE(model);
  return `
    <article class="model-card" draggable="true" tabindex="0" data-model-id="${model.id}"
      title="${esc(model.name)} — drag onto a machine, or focus and press 1–3">
      <span class="grip" aria-hidden="true">⋮⋮</span>
      <div class="model-copy">
        <div class="model-line">
          <span class="model-name">${esc(model.name)}</span>
          <span class="tag ${moe ? "moe" : ""}">${moe ? "MoE" : "dense"}</span>
        </div>
        <div class="model-meta" title="Total parameters · context memory per token · longest context">${fmtParams(model.total)} · ${model.ctxKiB} KiB/tok · ${fmtCtx(model.longest)} ctx</div>
      </div>
      <div class="model-weights" title="Weights at Q4_0 and Q8_0 (GiB)">
        <span class="q4">Q4_0 <b>${fmtGiB(q4)}</b></span>
        <span class="q8">Q8_0 <b>${fmtGiB(q8)}</b></span>
      </div>
    </article>`;
}

function segmentDivisions(segments, capacity) {
  const used = segments.reduce((a, s) => a + s.amount, 0);
  const denom = Math.max(capacity, used, 0.0001);
  let cursor = 0;
  const html = [];

  for (const segment of segments) {
    const amount = Math.max(0, segment.amount);
    if (amount <= 0) continue;
    const start = cursor;
    const end = start + amount;
    const within = Math.max(0, Math.min(end, capacity) - start);
    const over = Math.max(0, end - Math.max(start, capacity));
    const title = `${segment.model} — ${segment.type} ${fmtGiB(amount)} GiB`;

    if (within > 0) {
      html.push(
        `<span class="mem-seg ${segment.type} ${segment.modelStart ? "model-start" : ""}" style="flex-basis:${(within / denom) * 100}%" title="${esc(title)}"></span>`,
      );
    }
    if (over > 0) {
      html.push(
        `<span class="mem-seg ${segment.type} overflow" style="flex-basis:${(over / denom) * 100}%" title="${esc(title + " — over capacity")}"></span>`,
      );
    }
    cursor = end;
  }
  return { html: html.join(""), used, denom };
}

function barRowHtml(name, segments, capacity, used, options = {}) {
  const { html, denom } = segmentDivisions(segments, capacity);
  const over = used > capacity + EPS;
  const capPct = Math.min(100, (capacity / denom) * 100);
  return `
    <div class="bar-row">
      <div class="bar-label">
        <span class="bar-name">${esc(name)}</span>
        <span class="bar-value ${over ? "over" : options.ram ? "ram" : "ok"}">${fmtGiB(used)} / ${fmtGiB(capacity)} GiB${over ? " over" : ""}</span>
      </div>
      <div class="mem-bar ${options.ram ? "ram-bar" : ""}" role="img" aria-label="${esc(name)} ${fmtGiB(used)} of ${fmtGiB(capacity)} GiB">
        ${html}
        ${over ? `<span class="cap-mark" style="left:${capPct}%" title="Capacity ${fmtGiB(capacity)} GiB"></span>` : ""}
      </div>
    </div>`;
}

function barsHtml(machine, metrics) {
  let html = "";
  for (let i = 0; i < metrics.count; i++) {
    html += barRowHtml(`Card ${i + 1}`, metrics.cardSegments, metrics.gpu.memory, metrics.cardUsed);
  }
  if (machine.spill) {
    html += barRowHtml("System RAM", metrics.ramSegments, machine.ram, metrics.ramUsed, { ram: true });
  }
  return html;
}

function instanceMetricsHtml(info, fits, fitLine) {
  if (!fits) {
    return `<span class="inst-speed bad">Won't run</span><span class="inst-verdict bad" title="${esc(fitLine)}">${esc(fitLine)}</span>`;
  }
  const spilled = info.ram > EPS;
  return `<span class="inst-speed">${fmtSpeed(info.speed)} <small>tok/s</small></span>
    <span class="inst-verdict ${spilled ? "ram" : ""}">${spilled ? `${fmtGiB(info.ram)} GiB in RAM` : "Fits on cards"}</span>`;
}

function instanceCardHtml(inst, metrics, selected) {
  const model = modelById(inst.modelId);
  const info = instanceInfo(metrics, inst.id);
  const maxCtx = maxContextThatFits(
    state.machines.find((m) => m.models.some((x) => x.id === inst.id)),
    inst.id,
  );
  const maxLabel = maxCtx === null ? "none fit" : `max fit ${fmtCtx(maxCtx)}`;
  return `
    <article class="inst-card ${selected ? "selected" : ""} ${metrics.fits ? "" : "wontrun"}"
      draggable="true" tabindex="0" data-inst-id="${inst.id}">
      <div class="inst-head">
        <div class="inst-title">
          <span class="inst-name">${esc(model.name)}</span>
          <span class="tag ${isMoE(model) ? "moe" : ""}">${isMoE(model) ? "MoE" : "dense"}</span>
        </div>
        <button class="inst-remove" type="button" data-action="remove-inst" data-inst="${inst.id}"
          title="Remove from machine (Delete)" aria-label="Remove ${esc(model.name)}">×</button>
      </div>
      <div class="inst-metrics">${instanceMetricsHtml(info, metrics.fits, metrics.fitLine)}</div>
      <div class="inst-controls">
        <div class="control-row">
          <div class="mini-seg" aria-label="Weight quantization">
            <button type="button" data-action="quant" data-quant="Q4_0" class="${inst.quant === "Q4_0" ? "active" : ""}">Q4_0</button>
            <button type="button" data-action="quant" data-quant="Q8_0" class="${inst.quant === "Q8_0" ? "active" : ""}">Q8_0</button>
          </div>
          <div class="mini-seg cache" aria-label="Context cache precision">
            <button type="button" data-action="cache" data-cache="16" class="${inst.cache === 16 ? "active" : ""}">16-bit</button>
            <button type="button" data-action="cache" data-cache="8" class="${inst.cache === 8 ? "active" : ""}">8-bit</button>
          </div>
          <span class="ctx-summary"><b>${fmtCtx(inst.context)}</b> tok · <span class="maxfit" title="Most context that fits">${maxLabel}</span></span>
        </div>
        <div class="slider-row">
          <input type="range" min="1024" max="${model.longest}" step="1024" value="${inst.context}"
            data-context="${inst.id}" aria-label="Context tokens for ${esc(model.name)}" />
        </div>
      </div>
    </article>`;
}

function machineHtml(machine) {
  const gpu = gpuById(machine.gpuId);
  const metrics = evaluateMachine(machine);
  const selected = state.pickedId;
  const instHtml = machine.models.length
    ? machine.models.map((inst) => instanceCardHtml(inst, metrics, inst.id === selected)).join("")
    : `<div class="empty-machine">Drop a model here<br />or press 1–3 while it is focused</div>`;

  return `
    <section class="machine-column" data-machine-id="${machine.id}" aria-label="${esc(gpu.name)} machine">
      <div class="machine-head">
        <div class="machine-title-row">
          <select class="gpu-select" data-gpu-select="${machine.id}" aria-label="Graphics card">
            ${GPUS.map((g) => `<option value="${g.id}" ${g.id === machine.gpuId ? "selected" : ""}>${esc(g.name)}</option>`).join("")}
          </select>
          <button class="icon-button" type="button" data-action="remove-machine" data-machine="${machine.id}"
            title="Remove machine" aria-label="Remove machine">×</button>
        </div>
        <div class="machine-subrow">
          <div class="count-seg" aria-label="Card count">
            <button type="button" data-action="count" data-count="1" class="${machine.count === 1 ? "active" : ""}">1×</button>
            <button type="button" data-action="count" data-count="2" class="${machine.count === 2 ? "active" : ""}">2×</button>
          </div>
          <span class="price">${fmtPrice(gpu.price * machine.count)}</span>
          <span class="spill-control">
            Spill to
            <button class="switch ${machine.spill ? "on" : ""}" type="button" data-action="toggle-spill"
              title="Spill weights to system RAM" aria-label="Spill to system RAM" aria-pressed="${machine.spill}"></button>
            <select class="ram-select" data-ram-select="${machine.id}" ${machine.spill ? "" : "disabled"} aria-label="System RAM">
              ${[32, 64, 128, 256, 512].map((r) => `<option value="${r}" ${machine.ram === r ? "selected" : ""}>${r} GiB</option>`).join("")}
            </select>
          </span>
          <button class="cheapest-button" type="button" data-action="cheapest" data-machine="${machine.id}">Cheapest fit</button>
        </div>
        <div class="cheapest-msg">${esc(machine.cheapestMsg || "")}</div>
      </div>
      <div class="drop-verdict" data-drop-verdict></div>
      <div class="bars-wrap" data-bars>${barsHtml(machine, metrics)}</div>
      <div class="fitline ${metrics.fits ? "" : "over"}" data-fitline>${esc(metrics.fitLine)}</div>
      <div class="inst-list" data-inst-list>${instHtml}</div>
    </section>`;
}

function renderBoard() {
  modelList.innerHTML = MODELS.map(modelCardHtml).join("");

  let html = state.machines.map(machineHtml).join("");
  if (state.machines.length < 3) {
    html += `
      <button class="add-machine" type="button" data-action="add-machine" aria-label="Add machine">
        <span class="plus">+</span>
        <span>Add machine</span>
        <small>One or two identical cards</small>
      </button>`;
  }
  machineColumns.innerHTML = html;

  refreshAllLive();
  renderPanel();

  if (state.focusAfterRender) {
    const id = state.focusAfterRender;
    state.focusAfterRender = null;
    requestAnimationFrame(() => focusInstance(id));
  }
}

function refreshMachineDom(machine) {
  const el = machineColumns.querySelector(`[data-machine-id="${machine.id}"]`);
  if (!el) return;
  const gpu = gpuById(machine.gpuId);
  const metrics = evaluateMachine(machine);
  el.setAttribute("aria-label", `${gpu.name} machine`);

  const select = el.querySelector("[data-gpu-select]");
  if (select && select.value !== machine.gpuId) select.value = machine.gpuId;

  const price = el.querySelector(".price");
  if (price) price.textContent = fmtPrice(gpu.price * machine.count);

  el.querySelectorAll('[data-action="count"]').forEach((button) => {
    button.classList.toggle("active", Number(button.dataset.count) === machine.count);
  });

  const spillButton = el.querySelector('[data-action="toggle-spill"]');
  if (spillButton) {
    spillButton.classList.toggle("on", machine.spill);
    spillButton.setAttribute("aria-pressed", String(machine.spill));
  }
  const ramSelect = el.querySelector("[data-ram-select]");
  if (ramSelect) {
    ramSelect.disabled = !machine.spill;
    ramSelect.value = String(machine.ram);
  }
  const cheapestMsg = el.querySelector(".cheapest-msg");
  if (cheapestMsg) cheapestMsg.textContent = machine.cheapestMsg || "";

  const bars = el.querySelector("[data-bars]");
  if (bars) bars.innerHTML = barsHtml(machine, metrics);

  const fitline = el.querySelector("[data-fitline]");
  if (fitline) {
    fitline.textContent = metrics.fitLine;
    fitline.classList.toggle("over", !metrics.fits);
    fitline.classList.toggle("ram", machine.spill && metrics.fits);
    fitline.classList.remove("preview");
  }

  const dropVerdict = el.querySelector("[data-drop-verdict]");
  if (dropVerdict) dropVerdict.classList.remove("show", "bad");

  machine.models.forEach((inst) => {
    const card = el.querySelector(`[data-inst-id="${inst.id}"]`);
    if (!card) return;
    const info = instanceInfo(metrics, inst.id);
    card.classList.toggle("selected", state.pickedId === inst.id);
    card.classList.toggle("wontrun", !metrics.fits);
    const metricsEl = card.querySelector(".inst-metrics");
    if (metricsEl) metricsEl.innerHTML = instanceMetricsHtml(info, metrics.fits, metrics.fitLine);
    card.querySelectorAll('[data-action="quant"]').forEach((button) => {
      button.classList.toggle("active", button.dataset.quant === inst.quant);
    });
    card.querySelectorAll('[data-action="cache"]').forEach((button) => {
      button.classList.toggle("active", Number(button.dataset.cache) === inst.cache);
    });
    const summary = card.querySelector(".ctx-summary");
    const maxCtx = maxContextThatFits(machine, inst.id);
    if (summary) {
      summary.innerHTML = `<b>${fmtCtx(inst.context)}</b> tok · <span class="maxfit" title="Most context that fits">${maxCtx === null ? "none fit" : `max fit ${fmtCtx(maxCtx)}`}</span>`;
    }
    const slider = card.querySelector("[data-context]");
    if (slider && Number(slider.value) !== inst.context) slider.value = String(inst.context);
  });
}

function refreshAllLive() {
  state.machines.forEach(refreshMachineDom);
}

/* ------------------------------------------------------------------ *
 * Panel + stream
 * ------------------------------------------------------------------ */

function pickedState() {
  const located = locateInstance(state.pickedId);
  if (!located) return null;
  const metrics = evaluateMachine(located.machine);
  const info = instanceInfo(metrics, state.pickedId);
  if (!info) return null;
  return { ...located, metrics, info };
}

function verdictHtml(info, metrics) {
  if (!metrics.fits) return `<span class="panel-verdict bad">Won't run · ${esc(metrics.fitLine)}</span>`;
  if (info.ram > EPS) {
    return `<span class="panel-verdict ram">${fmtGiB(info.ram)} GiB of weights in RAM</span>`;
  }
  return `<span class="panel-verdict">Fits fully on the cards</span>`;
}

function statusText(picked) {
  if (!picked) return "";
  if (state.finished) {
    const avg = state.elapsed > 0 ? 300 / state.elapsed : 0;
    return `300 words in ${state.elapsed.toFixed(2)} s · ${avg.toFixed(1)} tok/s average`;
  }
  if (state.playing) {
    if (!picked.metrics.fits) {
      return `Paused · ${state.wordCount} / 300 words · ${state.elapsed.toFixed(2)} s`;
    }
    return `${state.wordCount} / 300 words · ${state.elapsed.toFixed(2)} s`;
  }
  if (state.wordCount > 0) {
    return `Paused · ${state.wordCount} / 300 words · ${state.elapsed.toFixed(2)} s`;
  }
  return picked.metrics.fits ? "Ready · press Play" : "Won't run";
}

function streamTextHtml() {
  if (state.wordCount <= 0) {
    return "";
  }
  const text = WORDS.slice(0, state.wordCount).join(" ");
  return `${esc(text)}${state.playing ? '<span class="caret" aria-hidden="true"></span>' : ""}`;
}

function fixesHtml(machine, inst, metrics) {
  const options = fitOptions(machine, inst);
  if (!options.length) {
    return `
      <aside class="fixes">
        <div class="fixes-title">No single change fits</div>
        <div class="fixes-sub">This machine is over even after each individual fix. Remove a model or choose a larger card.</div>
      </aside>`;
  }
  return `
    <aside class="fixes">
      <div class="fixes-title">Won't run — one change fixes it</div>
      <div class="fixes-sub">${esc(metrics.fitLine)}. Every option below makes this exact machine fit on its own.</div>
      ${options
        .map(
          (option) => `
        <button class="fix-button" type="button" data-fix="${option.kind}">
          <span>${esc(option.label)}</span>
          <span class="fix-value">${esc(option.value)}</span>
        </button>`,
        )
        .join("")}
    </aside>`;
}

function mathHtml(picked) {
  const { machine, inst, metrics, info } = picked;
  const model = modelById(inst.modelId);
  const bits = bitsFor(inst.quant);
  const maxCtx = maxContextThatFits(machine, inst.id);
  const weightBytes = info.weight * GIB;
  const activeGB = info.activeBytes / 1e9;
  const ctxBytes = info.context * GIB;
  const onCardPerCard = info.onCard / metrics.count;
  const ctxPerCard = info.context / metrics.count;
  const speedFormula =
    info.ram > EPS
      ? (() => {
          const onShare = info.onCard / info.weight;
          const onGB = activeGB * onShare;
          const ramGB = activeGB - onGB;
          return `1 ÷ (${onGB.toFixed(2)} GB ÷ ${metrics.gpu.bandwidth} GB/s + ${ramGB.toFixed(2)} GB ÷ 80 GB/s)`;
        })()
      : `${metrics.gpu.bandwidth} GB/s ÷ ${activeGB.toFixed(2)} GB`;
  const readValue =
    info.ram > EPS
      ? `${(activeGB * (info.onCard / info.weight)).toFixed(2)} GB cards + ${(activeGB - activeGB * (info.onCard / info.weight)).toFixed(2)} GB RAM`
      : `${activeGB.toFixed(2)} GB`;
  const readFormula =
    info.ram > EPS
      ? `${model.active}B active × ${bits} bits ÷ 8, split by weight share`
      : `${model.active}B active × ${bits} bits ÷ 8`;
  const cardCells = metrics.cardTotals
    .map(
      (total, i) => `
        <div class="math-cell ${metrics.fits ? "good" : "bad"}">
          <div class="label">Card ${i + 1} total</div>
          <div class="value">${fmtGiB(total)} / ${fmtGiB(metrics.gpu.memory)} GiB</div>
          <div class="formula">${fmtGiB(info.onCard / metrics.count)} w + ${fmtGiB(ctxPerCard)} ctx + 1.00 overhead</div>
        </div>`,
    )
    .join("");

  return `
    <div class="math-scroll">
      <div class="math-grid">
        <div class="math-cell">
          <div class="label">Weights</div>
          <div class="value">${fmtGiB(info.weight)} GiB</div>
          <div class="formula">${fmtParams(model.total)} × ${bits} bits ÷ 8 = ${(weightBytes / 1e9).toFixed(2)} GB</div>
        </div>
        <div class="math-cell">
          <div class="label">Context</div>
          <div class="value">${fmtGiB(info.context)} GiB</div>
          <div class="formula">${fmtCtx(inst.context)} × ${model.ctxKiB} KiB × ${inst.cache === 8 ? "0.5" : "1"} = ${(ctxBytes / 1e9).toFixed(2)} GB</div>
        </div>
        <div class="math-cell">
          <div class="label">Overhead</div>
          <div class="value">${fmtGiB(metrics.models.length)} GiB / card</div>
          <div class="formula">${metrics.models.length} model${metrics.models.length === 1 ? "" : "s"} × 1.00 GiB</div>
        </div>
        <div class="math-cell ${machine.spill ? "ram" : ""}">
          <div class="label">Cards</div>
          <div class="value">${metrics.count} × ${metrics.gpu.memory} GiB</div>
          <div class="formula">${metrics.gpu.bandwidth} GB/s each · ${fmtPrice(metrics.gpu.price * metrics.count)}</div>
        </div>
        ${cardCells}
        <div class="math-cell wide ${metrics.fits ? "good" : "bad"}">
          <div class="label">Fit line</div>
          <div class="value">${esc(metrics.fitLine)}</div>
          <div class="formula">${machine.spill ? `Context + overhead reserved on cards; weights fill top-down, then RAM` : `Every card total must be ≤ ${fmtGiB(metrics.gpu.memory)} GiB`}</div>
        </div>
        <div class="math-cell ${info.ram > EPS ? "ram" : ""}">
          <div class="label">Bytes read / token</div>
          <div class="value">${readValue}</div>
          <div class="formula">${readFormula}</div>
        </div>
        <div class="math-cell ${metrics.fits ? "good" : "bad"}">
          <div class="label">Speed ceiling</div>
          <div class="value">${fmtSpeed(info.speed)} tok/s</div>
          <div class="formula">${metrics.fits ? speedFormula : "Machine must fit before it runs"}</div>
        </div>
        <div class="math-cell">
          <div class="label">Most context</div>
          <div class="value">${maxCtx === null ? "None fits" : `${fmtCtx(maxCtx)} tok`}</div>
          <div class="formula">largest 1,024 step · longest ${fmtCtx(model.longest)}</div>
        </div>
        <div class="math-cell ${info.ram > EPS ? "ram" : ""}">
          <div class="label">On-card weights</div>
          <div class="value">${fmtGiB(onCardPerCard)} GiB / card</div>
          <div class="formula">+ ${fmtGiB(ctxPerCard)} GiB context + 1.00 overhead${info.ram > EPS ? ` · ${fmtGiB(info.ram)} GiB in RAM` : ""}</div>
        </div>
        ${info.ram > EPS ? `<div class="math-cell ram">
          <div class="label">Weights in RAM</div>
          <div class="value">${fmtGiB(info.ram)} GiB</div>
          <div class="formula">${((info.ram / info.weight) * 100).toFixed(1)}% of weights · same share of active bytes</div>
        </div>` : ""}
      </div>
    </div>`;
}

function renderPanel() {
  const picked = pickedState();
  if (!picked) {
    panel.innerHTML = `<div class="panel-empty">Pick a card on a machine to follow its answer.</div>`;
    return;
  }

  const { machine, inst, metrics, info } = picked;
  const model = modelById(inst.modelId);
  const speed = metrics.fits ? fmtSpeed(info.speed) : "—";

  let main;
  if (state.math) {
    main = mathHtml(picked);
  } else {
    const content =
      state.wordCount > 0
        ? `<div class="stream-text" data-stream-text>${streamTextHtml()}</div>`
        : `<div class="stream-placeholder"><strong>${metrics.fits ? "Press Play." : "This card won't run yet."}</strong> ${metrics.fits ? "The 300-word answer streams at this card's memory-bandwidth ceiling." : "Apply one of the fixes on the right; the stream starts as soon as the machine fits."}</div>`;
    main = `
      <div class="panel-main">
        <div class="panel-toolbar">
          <span class="panel-title">Why bandwidth sets the pace <span>· 300 words</span></span>
          <span class="panel-status ${state.playing ? (metrics.fits ? "live" : "paused") : ""}" data-panel-status>${esc(statusText(picked))}</span>
          <button class="play-button" type="button" data-action="play">▶ Play</button>
          <button class="math-button ${state.math ? "active" : ""}" type="button" data-action="math" title="Show every number (M)">Math <span>M</span></button>
        </div>
        <div class="stream-wrap" data-stream-wrap>${content}</div>
        <div class="stream-meta" data-stream-meta>${state.wordCount} / 300 words · ${state.elapsed.toFixed(2)} s</div>
      </div>
      ${!metrics.fits ? fixesHtml(machine, inst, metrics) : ""}`;
  }

  panel.innerHTML = `
    <div class="panel-stats">
      <div class="panel-kicker"><span>Picked card</span><span>·</span><span>${metrics.fits ? "live" : "blocked"}</span></div>
      <div class="picked-name">${esc(model.name)}</div>
      <div class="picked-machine">${esc(metrics.gpu.name)} · ${metrics.count}× · ${inst.quant}<br />${inst.cache}-bit cache · ${fmtCtx(inst.context)} tok</div>
      <div class="panel-speed-row">
        <span class="panel-speed">${speed}</span>
        <span class="panel-speed-unit">TOK/S</span>
      </div>
      ${verdictHtml(info, metrics)}
      <div class="panel-spec">${fmtParams(model.total)} total · ${fmtParams(model.active)} active</div>
    </div>
    ${state.math
      ? `<div class="panel-main">
          <div class="panel-toolbar">
            <span class="panel-title">Math <span>· every number for this card</span></span>
            <span class="panel-status" data-panel-status>${esc(statusText(picked))}</span>
            <button class="play-button" type="button" data-action="play">▶ Play</button>
            <button class="math-button active" type="button" data-action="math" title="Show answer (M)">Math <span>M</span></button>
          </div>
          ${main}
        </div>
        ${!metrics.fits ? fixesHtml(machine, inst, metrics) : ""}`
      : main}
  `;

  if (!state.math && state.playing && metrics.fits) {
    const wrap = panel.querySelector("[data-stream-wrap]");
    if (wrap) wrap.scrollTop = wrap.scrollHeight;
  }
}

function updateStreamDom() {
  if (state.math) {
    updatePanelStatusDom();
    return;
  }
  const textEl = panel.querySelector("[data-stream-text]");
  const wrap = panel.querySelector("[data-stream-wrap]");
  if (textEl) {
    if (state.wordCount > 0) {
      textEl.innerHTML = streamTextHtml();
    }
    if (state.playing && wrap) wrap.scrollTop = wrap.scrollHeight;
  } else if (state.wordCount > 0) {
    renderPanel();
    return;
  }
  updatePanelStatusDom();
}

function updatePanelStatusDom() {
  const picked = pickedState();
  const status = panel.querySelector("[data-panel-status]");
  if (status && picked) {
    status.textContent = statusText(picked);
    status.classList.toggle("live", state.playing && picked.metrics.fits);
    status.classList.toggle("paused", state.playing && !picked.metrics.fits);
  }
  const meta = panel.querySelector("[data-stream-meta]");
  if (meta) meta.textContent = `${state.wordCount} / 300 words · ${state.elapsed.toFixed(2)} s`;
}

/* ------------------------------------------------------------------ *
 * Playback clock
 * ------------------------------------------------------------------ */

function resetStream() {
  state.playing = false;
  state.finished = false;
  state.wordCount = 0;
  state.credit = 0;
  state.elapsed = 0;
  state.lastTs = null;
}

function playPicked() {
  const picked = pickedState();
  if (!picked) return;
  resetStream();
  state.playing = true;
  state.lastTs = null;
  renderPanel();
}

function pickInstance(id, { restart = false } = {}) {
  const located = locateInstance(id);
  if (!located) return;
  const changed = state.pickedId !== id;
  const finished = state.finished || state.wordCount >= 300;
  state.pickedId = id;
  if (restart || changed || finished) resetStream();
  state.playing = true;
  state.lastTs = null;
  refreshAllLive();
  renderPanel();
}

function streamFrame(ts) {
  requestAnimationFrame(streamFrame);

  if (!state.playing) {
    state.lastTs = ts;
    return;
  }

  const picked = pickedState();
  if (!picked) {
    state.lastTs = ts;
    return;
  }

  if (!picked.metrics.fits) {
    state.lastTs = ts;
    return;
  }

  const previous = state.lastTs;
  state.lastTs = ts;
  if (previous === null || previous === undefined) return;
  const dt = (ts - previous) / 1000;
  if (dt <= 0) return;

  const speed = picked.info.speed;
  state.elapsed += dt;
  state.credit += dt * speed;
  let nextCount = Math.min(300, Math.floor(state.credit + 1e-9));
  if (nextCount <= state.wordCount) {
    const meta = panel.querySelector("[data-stream-meta]");
    if (meta) meta.textContent = `${state.wordCount} / 300 words · ${state.elapsed.toFixed(2)} s`;
    return;
  }

  if (nextCount >= 300) {
    const overshoot = Math.max(0, state.credit - 300);
    state.elapsed = Math.max(0, state.elapsed - overshoot / speed);
    state.credit = 300;
    nextCount = 300;
  }

  state.wordCount = nextCount;
  if (state.wordCount >= 300) {
    state.playing = false;
    state.finished = true;
  }
  updateStreamDom();
  updatePanelStatusDom();
}

/* ------------------------------------------------------------------ *
 * Interaction helpers
 * ------------------------------------------------------------------ */

function showToast(message) {
  toastEl.textContent = message;
  toastEl.classList.add("show");
  clearTimeout(showToast.timer);
  showToast.timer = setTimeout(() => toastEl.classList.remove("show"), 2200);
}

function focusInstance(id) {
  const el = machineColumns.querySelector(`[data-inst-id="${id}"]`);
  if (el) el.focus({ preventScroll: true });
}

function addInstanceToMachine(machine, index, inst) {
  const at = Math.max(0, Math.min(index, machine.models.length));
  machine.models.splice(at, 0, inst);
}

function makeInstance(modelId) {
  return {
    id: newId("inst"),
    modelId,
    quant: "Q4_0",
    cache: 16,
    context: 32768,
  };
}

function removeInstance(id) {
  const located = locateInstance(id);
  if (!located) return;
  const wasPicked = state.pickedId === id;
  located.machine.models.splice(located.index, 1);
  located.machine.cheapestMsg = "";
  if (wasPicked) {
    state.pickedId = null;
    resetStream();
  }
  renderBoard();
}

function cheapestFit(machineId) {
  const machine = state.machines.find((m) => m.id === machineId);
  if (!machine) return;
  let best = null;

  for (const gpu of GPUS) {
    for (const count of [1, 2]) {
      const test = { ...machine, gpuId: gpu.id, count };
      const metrics = evaluateMachine(test);
      if (!metrics.fits) continue;
      const slowest = metrics.info.length
        ? Math.min(...metrics.info.map((info) => info.speedRaw))
        : Infinity;
      const price = gpu.price * count;
      if (
        !best ||
        price < best.price ||
        (price === best.price && slowest > best.slowest)
      ) {
        best = { gpu, count, price, slowest };
      }
    }
  }

  if (best) {
    machine.gpuId = best.gpu.id;
    machine.count = best.count;
    machine.cheapestMsg = "";
    refreshAllLive();
    renderPanel();
    showToast(`Cheapest fit: ${best.gpu.name} × ${best.count} — ${fmtPrice(best.price)}`);
  } else {
    const metrics = evaluateMachine(machine);
    if (!machine.models.length) {
      machine.cheapestMsg = "Add a model first";
    } else {
      machine.cheapestMsg = `Needs ${fmtGiB(metrics.needTotal)} GiB total`;
      showToast(`Nothing fits. Models need ${fmtGiB(metrics.needTotal)} GiB total.`);
    }
    refreshMachineDom(machine);
  }
}

function applyFix(kind) {
  const located = locateInstance(state.pickedId);
  if (!located) return;
  const { machine, inst } = located;

  if (kind === "context") {
    const max = maxContextThatFits(machine, inst.id);
    if (max !== null) inst.context = max;
  } else if (kind === "cache") {
    inst.cache = 8;
  } else if (kind === "quant") {
    inst.quant = "Q4_0";
  } else if (kind === "card") {
    machine.count = 2;
  } else if (kind === "spill") {
    machine.spill = true;
  }

  machine.cheapestMsg = "";
  refreshAllLive();
  renderPanel();
}

function moveInstanceWithin(id, direction) {
  const located = locateInstance(id);
  if (!located) return;
  const newIndex = located.index + direction;
  if (newIndex < 0 || newIndex >= located.machine.models.length) return;
  const [inst] = located.machine.models.splice(located.index, 1);
  located.machine.models.splice(newIndex, 0, inst);
  located.machine.cheapestMsg = "";
  state.focusAfterRender = inst.id;
  renderBoard();
}

function sendInstanceToMachine(id, machineId) {
  const located = locateInstance(id);
  const target = state.machines.find((m) => m.id === machineId);
  if (!located || !target) return;

  const wasPicked = state.pickedId === id;
  const [inst] = located.machine.models.splice(located.index, 1);
  located.machine.cheapestMsg = "";
  target.models.push(inst);
  target.cheapestMsg = "";
  if (!wasPicked) {
    state.pickedId = inst.id;
    resetStream();
    state.playing = true;
    state.lastTs = null;
  }
  state.focusAfterRender = inst.id;
  renderBoard();
}

/* ------------------------------------------------------------------ *
 * Drag + drop
 * ------------------------------------------------------------------ */

function buildPreviewModels(machine, index) {
  const drag = state.drag;
  if (!drag) return null;

  let inst;
  if (drag.type === "model") {
    inst = makeInstance(drag.modelId);
    inst.id = "preview-model";
  } else {
    const located = locateInstance(drag.instId);
    if (!located) return null;
    inst = { ...located.inst, id: "preview-inst" };
    if (!drag.copy && located.machine.id === machine.id) {
      const models = machine.models.filter((x) => x.id !== located.inst.id);
      let at = index;
      if (located.index < index) at -= 1;
      at = Math.max(0, Math.min(at, models.length));
      models.splice(at, 0, inst);
      return { models, inst, index: at };
    }
  }

  const models = machine.models.slice();
  const at = Math.max(0, Math.min(index, models.length));
  models.splice(at, 0, inst);
  return { models, inst, index: at };
}

function dropIndexFor(machineEl, clientY) {
  const cards = Array.from(machineEl.querySelectorAll(".inst-card"));
  for (let i = 0; i < cards.length; i++) {
    const rect = cards[i].getBoundingClientRect();
    if (clientY < rect.top + rect.height / 2) return i;
  }
  return cards.length;
}

function markDrop(machineEl, index) {
  machineEl.querySelectorAll(".inst-card").forEach((card) => {
    card.classList.remove("drop-above", "drop-below");
  });
  const cards = machineEl.querySelectorAll(".inst-card");
  if (!cards.length) return;
  if (index >= cards.length) {
    cards[cards.length - 1].classList.add("drop-below");
  } else {
    cards[index].classList.add("drop-above");
  }
}

function clearPreview() {
  if (!state.preview) return;
  const previous = state.machines.find((m) => m.id === state.preview.machineId);
  if (previous) refreshMachineDom(previous);
  const el = machineColumns.querySelector(`[data-machine-id="${state.preview.machineId}"]`);
  if (el) {
    el.classList.remove("previewing");
    const verdict = el.querySelector("[data-drop-verdict]");
    if (verdict) verdict.classList.remove("show", "bad");
    el.querySelectorAll(".inst-card").forEach((c) => c.classList.remove("drop-above", "drop-below"));
  }
  state.preview = null;
}

function showPreview(machineEl, machine, index) {
  const built = buildPreviewModels(machine, index);
  if (!built) return;
  const previewMachine = { ...machine, models: built.models };
  const metrics = evaluateMachine(previewMachine);
  const info = instanceInfo(metrics, built.inst.id);
  if (!info) return;

  const verdict = machineEl.querySelector("[data-drop-verdict]");
  if (verdict) {
    let text;
    let bad = false;
    if (!metrics.fits) {
      text = metrics.fitLine;
      bad = true;
    } else if (info.ram > EPS) {
      text = `${fmtSpeed(info.speed)} tok/s · ${fmtGiB(info.ram)} GiB in RAM`;
    } else {
      text = `Fits · ${fmtSpeed(info.speed)} tok/s`;
    }
    verdict.textContent = text;
    verdict.classList.add("show");
    verdict.classList.toggle("bad", bad);
  }

  const bars = machineEl.querySelector("[data-bars]");
  if (bars) bars.innerHTML = barsHtml(previewMachine, metrics);
  const fitline = machineEl.querySelector("[data-fitline]");
  if (fitline) {
    fitline.textContent = metrics.fitLine;
    fitline.classList.toggle("over", !metrics.fits);
    fitline.classList.toggle("ram", previewMachine.spill && metrics.fits);
    fitline.classList.add("preview");
  }

  machineEl.classList.add("previewing");
  markDrop(machineEl, index);
  state.preview = { machineId: machine.id, index, instId: built.inst.id };
}
function startDrag(event, drag) {
  state.drag = drag;
  event.dataTransfer.effectAllowed = "copyMove";
  event.dataTransfer.setData("text/plain", drag.type === "model" ? drag.modelId : drag.instId);
}

/* ------------------------------------------------------------------ *
 * Event wiring
 * ------------------------------------------------------------------ */

let controlPointerDown = false;
board.addEventListener(
  "pointerdown",
  (event) => {
    controlPointerDown = Boolean(event.target.closest("input, button, select"));
  },
  true,
);
document.addEventListener("pointerup", () => {
  controlPointerDown = false;
});

board.addEventListener("click", (event) => {
  const actionEl = event.target.closest("[data-action]");
  if (actionEl) {
    const action = actionEl.dataset.action;

    if (action === "add-machine") {
      if (state.machines.length >= 3) return;
      state.machines.push({
        id: newId("machine"),
        gpuId: "rtx4090",
        count: 1,
        spill: false,
        ram: 64,
        models: [],
        cheapestMsg: "",
      });
      renderBoard();
      return;
    }

    if (action === "remove-machine") {
      const id = actionEl.dataset.machine;
      const machine = state.machines.find((m) => m.id === id);
      if (!machine) return;
      const hadPicked = machine.models.some((inst) => inst.id === state.pickedId);
      state.machines = state.machines.filter((m) => m.id !== id);
      if (hadPicked) {
        state.pickedId = null;
        resetStream();
      }
      renderBoard();
      return;
    }

    if (action === "count") {
      const machineEl = actionEl.closest("[data-machine-id]");
      const machine = state.machines.find((m) => m.id === machineEl.dataset.machineId);
      if (!machine) return;
      machine.count = Number(actionEl.dataset.count);
      machine.cheapestMsg = "";
      refreshAllLive();
      renderPanel();
      return;
    }

    if (action === "toggle-spill") {
      const machineEl = actionEl.closest("[data-machine-id]");
      const machine = state.machines.find((m) => m.id === machineEl.dataset.machineId);
      if (!machine) return;
      machine.spill = !machine.spill;
      machine.cheapestMsg = "";
      refreshAllLive();
      renderPanel();
      return;
    }

    if (action === "cheapest") {
      cheapestFit(actionEl.dataset.machine);
      return;
    }

    if (action === "remove-inst") {
      removeInstance(actionEl.dataset.inst);
      return;
    }

    if (action === "quant" || action === "cache") {
      const cardEl = actionEl.closest("[data-inst-id]");
      const located = locateInstance(cardEl.dataset.instId);
      if (!located) return;
      if (action === "quant") located.inst.quant = actionEl.dataset.quant;
      if (action === "cache") located.inst.cache = Number(actionEl.dataset.cache);
      located.machine.cheapestMsg = "";
      refreshAllLive();
      renderPanel();
      return;
    }

    return;
  }

  const instEl = event.target.closest("[data-inst-id]");
  if (instEl && !event.target.closest("input, button, select")) {
    instEl.focus({ preventScroll: true });
    pickInstance(instEl.dataset.instId);
    return;
  }

  const modelEl = event.target.closest("[data-model-id]");
  if (modelEl) {
    modelEl.focus({ preventScroll: true });
  }
});

board.addEventListener("change", (event) => {
  const gpuSelect = event.target.closest("[data-gpu-select]");
  if (gpuSelect) {
    const machine = state.machines.find((m) => m.id === gpuSelect.dataset.gpuSelect);
    if (!machine) return;
    machine.gpuId = gpuSelect.value;
    machine.cheapestMsg = "";
    refreshAllLive();
    renderPanel();
    return;
  }

  const ramSelect = event.target.closest("[data-ram-select]");
  if (ramSelect) {
    const machine = state.machines.find((m) => m.id === ramSelect.dataset.ramSelect);
    if (!machine) return;
    machine.ram = Number(ramSelect.value);
    machine.cheapestMsg = "";
    refreshAllLive();
    renderPanel();
  }
});

board.addEventListener("input", (event) => {
  const slider = event.target.closest("[data-context]");
  if (!slider) return;
  const located = locateInstance(slider.dataset.context);
  if (!located) return;
  located.inst.context = Number(slider.value);
  located.machine.cheapestMsg = "";
  refreshAllLive();
  renderPanel();
});

board.addEventListener("dragstart", (event) => {
  if (controlPointerDown || event.target.closest("input, button, select")) {
    event.preventDefault();
    return;
  }

  const instEl = event.target.closest("[data-inst-id]");
  if (instEl) {
    const located = locateInstance(instEl.dataset.instId);
    if (!located) return;
    startDrag(event, {
      type: "instance",
      instId: located.inst.id,
      copy: event.altKey,
    });
    instEl.classList.add("dragging");
    return;
  }

  const modelEl = event.target.closest("[data-model-id]");
  if (modelEl) {
    startDrag(event, {
      type: "model",
      modelId: modelEl.dataset.modelId,
      copy: true,
    });
    modelEl.classList.add("dragging");
  }
});

board.addEventListener("dragend", () => {
  board.querySelectorAll(".dragging").forEach((el) => el.classList.remove("dragging"));
  board.querySelectorAll(".drop-above, .drop-below").forEach((el) =>
    el.classList.remove("drop-above", "drop-below"),
  );
  modelsColumn.classList.remove("drop-active");
  clearPreview();
  state.drag = null;
});

board.addEventListener("dragover", (event) => {
  if (!state.drag) return;

  const machineEl = event.target.closest(".machine-column");
  if (machineEl) {
    event.preventDefault();
    event.dataTransfer.dropEffect = state.drag.copy || event.altKey ? "copy" : "move";
    const machine = state.machines.find((m) => m.id === machineEl.dataset.machineId);
    if (!machine) return;
    const index = dropIndexFor(machineEl, event.clientY);

    if (
      state.preview &&
      state.preview.machineId === machine.id &&
      state.preview.index === index
    ) {
      return;
    }
    clearPreview();
    showPreview(machineEl, machine, index);
    machineEl.classList.add("drag-over");
    machineColumns
      .querySelectorAll(".machine-column")
      .forEach((el) => el !== machineEl && el.classList.remove("drag-over"));
    return;
  }

  const modelColumn = event.target.closest(".models-column");
  if (modelColumn && state.drag.type === "instance") {
    event.preventDefault();
    event.dataTransfer.dropEffect = "move";
    modelsColumn.classList.add("drop-active");
    if (state.preview) clearPreview();
    machineColumns
      .querySelectorAll(".machine-column")
      .forEach((el) => el.classList.remove("drag-over"));
  }
});

board.addEventListener("dragleave", (event) => {
  const machineEl = event.target.closest(".machine-column");
  if (machineEl && !machineEl.contains(event.relatedTarget)) {
    machineEl.classList.remove("drag-over");
    if (state.preview && state.preview.machineId === machineEl.dataset.machineId) {
      clearPreview();
    }
  }
  const modelColumn = event.target.closest(".models-column");
  if (modelColumn && !modelColumn.contains(event.relatedTarget)) {
    modelsColumn.classList.remove("drop-active");
  }
});

board.addEventListener("drop", (event) => {
  if (!state.drag) return;

  const machineEl = event.target.closest(".machine-column");
  if (machineEl) {
    event.preventDefault();
    const machine = state.machines.find((m) => m.id === machineEl.dataset.machineId);
    if (!machine) return;
    const index = dropIndexFor(machineEl, event.clientY);
    const drag = state.drag;
    const copy = drag.copy || event.altKey;

    clearPreview();
    state.drag = null;
    modelsColumn.classList.remove("drop-active");
    machine.cheapestMsg = "";

    let focusId = null;
    let pickedNew = false;

    if (drag.type === "model") {
      const inst = makeInstance(drag.modelId);
      addInstanceToMachine(machine, index, inst);
      state.pickedId = inst.id;
      resetStream();
      state.playing = true;
      state.lastTs = null;
      focusId = inst.id;
      pickedNew = true;
    } else {
      const located = locateInstance(drag.instId);
      if (!located) {
        board.querySelectorAll(".dragging").forEach((el) => el.classList.remove("dragging"));
        renderBoard();
        return;
      }

      if (copy) {
        const copyInst = { ...located.inst, id: newId("inst") };
        addInstanceToMachine(machine, index, copyInst);
        state.pickedId = copyInst.id;
        resetStream();
        state.playing = true;
        state.lastTs = null;
        focusId = copyInst.id;
        pickedNew = true;
      } else if (located.machine.id === machine.id) {
        let at = index;
        if (located.index < index) at -= 1;
        at = Math.max(0, Math.min(at, located.machine.models.length - 1));
        const [inst] = located.machine.models.splice(located.index, 1);
        located.machine.models.splice(at, 0, inst);
        focusId = inst.id;
      } else {
        const wasPicked = state.pickedId === located.inst.id;
        const [inst] = located.machine.models.splice(located.index, 1);
        located.machine.cheapestMsg = "";
        addInstanceToMachine(machine, index, inst);
        if (!wasPicked) {
          state.pickedId = inst.id;
          resetStream();
          state.playing = true;
          state.lastTs = null;
          pickedNew = true;
        }
        focusId = inst.id;
      }
    }

    state.focusAfterRender = focusId;
    renderBoard();
    if (pickedNew) {
      const located = locateInstance(state.pickedId);
      if (located && !evaluateMachine(located.machine).fits) {
        showToast("Picked, but this machine is over. Fix it from the panel.");
      }
    }
    return;
  }

  const modelColumn = event.target.closest(".models-column");
  if (modelColumn && state.drag.type === "instance") {
    event.preventDefault();
    const id = state.drag.instId;
    clearPreview();
    state.drag = null;
    modelsColumn.classList.remove("drop-active");
    removeInstance(id);
  }
});

panel.addEventListener("click", (event) => {
  const actionEl = event.target.closest("[data-action]");
  if (actionEl) {
    if (actionEl.dataset.action === "play") {
      playPicked();
      return;
    }
    if (actionEl.dataset.action === "math") {
      state.math = !state.math;
      renderPanel();
      return;
    }
  }

  const fixEl = event.target.closest("[data-fix]");
  if (fixEl) {
    applyFix(fixEl.dataset.fix);
  }
});

document.addEventListener("keydown", (event) => {
  const editable = event.target.closest("input, select, textarea, [contenteditable='true']");

  if ((event.key === "m" || event.key === "M") && !editable) {
    event.preventDefault();
    state.math = !state.math;
    renderPanel();
    return;
  }

  if (editable) return;

  const instanceEl = event.target.closest?.("[data-inst-id]");
  const modelEl = event.target.closest?.("[data-model-id]");
  const focusedInstId = instanceEl?.dataset.instId || null;

  if (event.key === "Enter") {
    if (event.target.closest("button, select, input")) return;
    if (focusedInstId) {
      event.preventDefault();
      pickInstance(focusedInstId, { restart: true });
    }
    return;
  }

  if (["1", "2", "3"].includes(event.key)) {
    const target = state.machines[Number(event.key) - 1];
    if (!target) return;
    event.preventDefault();

    if (modelEl) {
      const inst = makeInstance(modelEl.dataset.modelId);
      target.models.push(inst);
      target.cheapestMsg = "";
      state.pickedId = inst.id;
      resetStream();
      state.playing = true;
      state.lastTs = null;
      state.focusAfterRender = inst.id;
      renderBoard();
    } else {
      const id = focusedInstId || state.pickedId;
      if (id) {
        target.cheapestMsg = "";
        sendInstanceToMachine(id, target.id);
      }
    }
    return;
  }

  if (event.key === "Delete" || event.key === "Backspace") {
    const id = focusedInstId || (event.target === document.body ? state.pickedId : null);
    if (id) {
      event.preventDefault();
      removeInstance(id);
    }
    return;
  }

  if (event.altKey && (event.key === "ArrowUp" || event.key === "ArrowDown")) {
    const id = focusedInstId || state.pickedId;
    if (!id) return;
    event.preventDefault();
    moveInstanceWithin(id, event.key === "ArrowUp" ? -1 : 1);
  }
});

/* ------------------------------------------------------------------ *
 * Boot
 * ------------------------------------------------------------------ */

if (WORDS.length !== 300) {
  console.warn(`Answer has ${WORDS.length} words, expected 300.`);
}

renderBoard();
requestAnimationFrame(streamFrame);

/* Handy for inspection and automated checks; all data remains in memory. */
window.__gpuStore = {
  state,
  GPUS,
  MODELS,
  WORDS,
  evaluateMachine,
  weightGiB,
  contextGiB,
  maxContextThatFits,
  fitOptions,
  pickedState,
  renderBoard,
};

  </script>
</body>
</html>

Watch the test

Video tests of this model on coding and agent work.

DeepSeek V4.1 Flash First Test | Better V4 Flash? | Coding with OpenCode, Frontend & Agentic Work11 Sep 2026

Same prompt, other models

2 more
Compare side by side

One email when a new model lands on the board or in the showcase. Unsubscribe anytime.