commit 8f3dadfc25149c6ffd79f18d3f2e85f6f0117a63
Author: Eiichi Shimazu <studio@adaptdesign.jp>
Date:   Sat Nov 14 22:50:12 2026 +0900

    判定の画面の見た目を配膳ロボット ブラウザ版 (pixelstomotors.com/sim/serving-robot-3d) に合わせる (依頼 186). index.html の CSS と並びだけ変える. 色・字・ボタン・数字・札・帯は手本の CSS から写す (--ink #1d1d1b・--sub #6b6b66・--line #d8d6d0・--bg #f2f1ee, Noto Sans JP + Inter 600, Google Fonts の読み込みも同じ). 3 列にする: 左の一覧 (白・右に線. 選んだ回は --ink で塗って白い字) / 真ん中 (下地 --bg. 上に回の名前 (Inter 600 28px)・送ったチャンク・終わり方の白い札, 映像とコマ送りの帯 (#seek と同じ形), グラフ) / 右の操作パネル (aside の形 320px. 成功率と件数を .nums の 2 列 (成功率・確定した回・まだの回・成功・失敗) → 判定する (pick/place は .seg) → ひとこと → AI の判定 (暗い札) → 今の判定 → この回の行). 札は失敗だけ #d64545 で塗り, 成功ほかは白に --line の枠, AI だけ (未確定) は 1px dashed #555. 青は既定の色に使わない. 判定の動き・キー・書き込み・数え方 (依頼 183・185) は変えない (JS は成功率の出し方・帯の済んだ所・グラフの幅だけ). テスト test_verdicts 39 OK
    
    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---
 pi05/verdict_viewer/index.html | 204 +++++++++++++++++++++++------------------
 1 file changed, 115 insertions(+), 89 deletions(-)

diff --git a/pi05/verdict_viewer/index.html b/pi05/verdict_viewer/index.html
index f9008e1..6d2b257 100644
--- a/pi05/verdict_viewer/index.html
+++ b/pi05/verdict_viewer/index.html
@@ -3,67 +3,89 @@
 <head>
 <meta charset="utf-8">
 <title>推論の記録の判定</title>
+<link rel="preconnect" href="https://fonts.googleapis.com">
+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Noto+Sans+JP:wght@400;600;700&display=swap" rel="stylesheet">
 <style>
+  /* 見た目は配膳ロボット ブラウザ版 (pixelstomotors.com/sim/serving-robot-3d) に合わせる (依頼 186) */
   :root {
-    --surface-1: #fcfcfb; --surface-2: #f3f2ee; --border: #dddcd5;
-    --text-primary: #1f1f1d; --text-secondary: #55554f; --text-muted: #8a8a82;
+    --ink: #1d1d1b; --sub: #6b6b66; --line: #d8d6d0; --bg: #f2f1ee; --stop: #d64545;
     --s1: #2a78d6; --s2: #eb6834; --s3: #1baf7a; --s4: #eda100; --s5: #e87ba4; --s6: #008300;
-    --grip: #55554f;
-    --ok: #006300; --bad: #b42318; --warn: #8a5a00; --accent: #2a78d6;
+    --grip: #6b6b66;
   }
   * { box-sizing: border-box; }
-  body { margin: 0; font: 13px/1.5 -apple-system, "Hiragino Sans", sans-serif; color: var(--text-primary);
-         background: var(--surface-1); height: 100vh; display: flex; }
-  #side { width: 300px; border-right: 1px solid var(--border); display: flex; flex-direction: column; background: var(--surface-2); }
-  #side header { padding: 10px; border-bottom: 1px solid var(--border); }
-  #side h1 { font-size: 14px; margin: 0 0 6px; }
-  #filter { width: 100%; font-size: 13px; padding: 3px; }
-  #rate { margin-top: 6px; font-size: 13px; }
-  #rate b { font-size: 16px; }
-  #count { color: var(--text-secondary); margin-top: 4px; font-size: 12px; }
+  body { margin: 0; font-family: "Noto Sans JP", "Inter", system-ui, sans-serif; font-size: 13px; line-height: 1.5; color: var(--ink);
+         background: var(--bg); height: 100vh; display: grid; grid-template-columns: 280px 1fr 320px; }
+  /* 左の一覧 */
+  #side { min-height: 0; background: #fff; border-right: 1px solid var(--line); display: flex; flex-direction: column; }
+  #side header { padding: 16px; border-bottom: 1px solid var(--line); }
+  #side h1 { margin: 0 0 8px; font-size: 15px; }
+  #filter { width: 100%; font: inherit; border: 1px solid var(--line); background: #fff; border-radius: 6px; padding: 5px 6px; color: var(--ink); cursor: pointer; }
+  #count { color: var(--sub); margin-top: 6px; font-size: 11px; }
   #list { overflow-y: auto; flex: 1; }
-  .item { padding: 5px 10px; border-bottom: 1px solid var(--border); cursor: pointer; display: flex; justify-content: space-between; gap: 6px; }
-  .item:hover { background: #ebeae4; }
-  .item.sel { background: #dbe8f8; }
-  .item .stem { font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
-  .item .sub { color: var(--text-muted); font-size: 11px; }
-  .tag { font-size: 11px; padding: 0 5px; border-radius: 3px; border: 1px solid var(--border); white-space: nowrap; align-self: center; }
-  .tag.success { color: var(--ok); border-color: var(--ok); }
-  .tag.failure { color: var(--bad); border-color: var(--bad); }
-  .tag.unknown, .tag.not_judgeable, .tag.not_applicable { color: var(--warn); border-color: var(--warn); }
-  .tag.ai { border-style: dashed; }
-  #main { flex: 1; overflow-y: auto; padding: 12px 16px; }
-  #title { font-family: ui-monospace, Menlo, monospace; font-size: 15px; font-weight: 600; }
-  #meta { color: var(--text-secondary); font-size: 12px; margin-bottom: 8px; }
-  #top { display: flex; gap: 16px; align-items: flex-start; }
-  #media { width: 640px; flex: none; }
-  #media video, #media img { width: 640px; background: #000; display: block; }
-  #framebar { display: flex; gap: 8px; align-items: center; margin-top: 4px; }
-  #framebar input { flex: 1; }
-  #panel { flex: 1; min-width: 300px; }
-  .box { border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; margin-bottom: 10px; background: #fff; }
-  .box h3 { margin: 0 0 4px; font-size: 12px; color: var(--text-secondary); font-weight: 600; }
+  .item { padding: 6px 16px; border-bottom: 1px solid var(--line); cursor: pointer; display: flex; justify-content: space-between; gap: 6px; }
+  .item:hover { background: var(--bg); }
+  .item.sel { background: var(--ink); color: #fff; }
+  .item .stem { font: 600 13px/1.3 "Inter", "Noto Sans JP", system-ui, sans-serif; }
+  .item .sub { color: var(--sub); font-size: 11px; }
+  .item.sel .sub { color: rgba(255,255,255,.7); }
+  /* 成功/失敗の札: 色は失敗だけ. AI だけ (未確定) は点線の枠 */
+  .tag { flex: none; font: 600 11px/1 "Inter", "Noto Sans JP", system-ui, sans-serif; padding: 3px 6px; border-radius: 6px;
+         background: #fff; color: var(--ink); border: 1px solid var(--line); white-space: nowrap; align-self: center; }
+  .tag.failure { background: var(--stop); color: #fff; border-color: var(--stop); }
+  .tag.ai { border: 1px dashed #555; }
+  /* 真ん中 */
+  #main { min-width: 0; overflow-y: auto; padding: 14px 16px; }
+  #hud { display: inline-block; background: rgba(255,255,255,.92); padding: 10px 14px; border-radius: 8px; margin-bottom: 12px; }
+  #title { font: 600 28px/1 "Inter", "Noto Sans JP", system-ui, sans-serif; }
+  #meta { margin: 6px 0 0; font-size: 12px; color: var(--sub); }
+  #media { width: 640px; }
+  #media video, #media img { width: 640px; background: #000; display: block; border-radius: 8px; }
+  #framebar { display: flex; gap: 10px; align-items: center; margin-top: 8px; background: rgba(255,255,255,.92); padding: 8px 12px; border-radius: 8px; }
+  #fr { flex: 1; -webkit-appearance: none; appearance: none; height: 6px; border-radius: 3px;
+        background: linear-gradient(90deg, #1d1d1b var(--done, 0%), #d8d6d0 var(--done, 0%)); }
+  #fr::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 2px solid #1d1d1b; cursor: pointer; }
+  #fr::-moz-range-thumb { width: 14px; height: 14px; border-radius: 50%; background: #fff; border: 2px solid #1d1d1b; }
+  #fl { font-size: 11px; color: var(--sub); white-space: nowrap; }
+  #media .muted { margin-top: 4px; }
+  /* 右の操作パネル */
+  #panel { min-height: 0; background: #fff; border-left: 1px solid var(--line); padding: 16px; overflow: auto; font-size: 13px; }
+  h2 { font-size: 12px; color: var(--sub); font-weight: 600; margin: 16px 0 6px; }
+  #panel > h2:first-child { margin-top: 0; }
+  .nums { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 12px; margin-top: 4px; }
+  .num { line-height: 1.25; }
+  .num b { display: block; font: 600 24px/1.1 "Inter", "Noto Sans JP", system-ui, sans-serif; }
+  .num small { font-size: 12px; font-weight: 400; margin-left: 2px; }
+  .num > span { display: block; margin-top: 1px; line-height: 1.3; font-size: 11px; color: var(--sub); }
+  .note { margin: 6px 0 0; font-size: 11px; line-height: 1.6; color: var(--sub); }
+  .seg { display: flex; flex-wrap: wrap; gap: 4px; }
+  .row { display: flex; gap: 6px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
+  .row .lbl { width: 40px; color: var(--sub); font-size: 12px; font-weight: 600; }
+  button { font: inherit; border: 1px solid var(--line); background: #fff; border-radius: 6px; padding: 5px 9px; cursor: pointer; color: var(--ink); }
+  button.on { background: var(--ink); color: #fff; border-color: var(--ink); }
+  button.key { flex: 1 1 calc(50% - 3px); text-align: left; }
+  kbd { font: 600 11px/1 "Inter", system-ui, sans-serif; border: 1px solid var(--line); border-radius: 6px; padding: 2px 5px; background: var(--bg); color: var(--ink); }
+  #note { width: 100%; font: inherit; padding: 5px 6px; border: 1px solid var(--line); border-radius: 6px; color: var(--ink); }
+  #status { min-height: 1em; margin: 6px 0 0; font-size: 12px; color: var(--sub); }
+  #status.err { color: var(--stop); font-weight: 600; }
+  #status.ok { color: var(--ink); }
+  .dark { background: rgba(60,62,66,.86); color: #fff; border-radius: 8px; padding: 10px 12px; font-size: 12px; }
+  .dark .muted { color: #b9bcc1; }
   .big { font-size: 16px; font-weight: 600; }
-  .row { display: flex; gap: 6px; align-items: center; margin: 4px 0; flex-wrap: wrap; }
-  .row .lbl { width: 46px; color: var(--text-secondary); }
-  button { font: inherit; padding: 3px 9px; border: 1px solid var(--border); border-radius: 4px; background: #fff; cursor: pointer; }
-  button.on { background: var(--accent); color: #fff; border-color: var(--accent); }
-  button.key { min-width: 110px; }
-  kbd { font: 11px ui-monospace, Menlo, monospace; border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 3px; padding: 0 4px; background: var(--surface-2); }
-  #note { width: 100%; padding: 4px; font: inherit; }
-  #status { min-height: 20px; margin: 4px 0; font-size: 12px; }
-  #status.err { color: var(--bad); font-weight: 600; }
-  #status.ok { color: var(--ok); }
-  #chart { margin-top: 10px; position: relative; }
-  #chart svg text { font-size: 11px; fill: var(--text-secondary); }
-  #legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--text-secondary); margin: 4px 0; }
+  /* グラフ */
+  #chart { margin-top: 12px; position: relative; background: #fff; border: 1px solid var(--line); border-radius: 8px; padding: 10px 12px; }
+  #chart:empty { display: none; }
+  #chart svg text { font: 400 11px "Inter", "Noto Sans JP", system-ui, sans-serif; fill: var(--sub); }
+  #legend { display: flex; gap: 12px; flex-wrap: wrap; font-size: 12px; color: var(--sub); margin: 0 0 4px; }
   #legend span::before { content: ""; display: inline-block; width: 14px; height: 2px; vertical-align: middle; margin-right: 4px; background: var(--c); }
-  #tip { position: absolute; pointer-events: none; background: #fff; border: 1px solid var(--border); border-radius: 4px;
-         padding: 4px 6px; font-size: 11px; display: none; box-shadow: 0 1px 3px rgba(0,0,0,.12); white-space: nowrap; }
+  #tip { position: absolute; pointer-events: none; background: rgba(255,255,255,.92); border: 1px solid var(--line); border-radius: 6px;
+         padding: 4px 6px; font-size: 11px; display: none; white-space: nowrap; }
   #tip i { display: inline-block; width: 10px; height: 2px; vertical-align: middle; margin-right: 4px; }
   table.hist { border-collapse: collapse; font-size: 11px; width: 100%; }
-  table.hist td, table.hist th { border-bottom: 1px solid var(--border); padding: 2px 4px; text-align: left; vertical-align: top; }
-  .muted { color: var(--text-muted); }
+  table.hist td, table.hist th { border-bottom: 1px solid var(--line); padding: 4px 4px 4px 0; text-align: left; vertical-align: top; }
+  table.hist th { color: var(--sub); font-weight: 600; }
+  table.hist td:not(:first-child), table.hist th { white-space: nowrap; }
+  .muted { color: var(--sub); }
 </style>
 </head>
 <body>
@@ -76,41 +98,41 @@
       <option value="failure">失敗だけ (今の判定)</option>
       <option value="diff">AI と人が違う回</option>
     </select>
-    <div id="rate"></div>
     <div id="count"></div>
   </header>
   <div id="list"></div>
 </div>
 <div id="main">
-  <div id="title">—</div>
-  <div id="meta"></div>
-  <div id="top">
-    <div id="media"></div>
-    <div id="panel">
-      <div class="box">
-        <h3>判定する (キー)</h3>
-        <div class="row">
-          <button class="key" data-oc="success"><kbd>y</kbd> 成功</button>
-          <button class="key" data-oc="failure"><kbd>n</kbd> 失敗</button>
-          <button class="key" data-oc="unknown"><kbd>u</kbd> 分からない</button>
-          <button class="key" data-oc="not_judgeable"><kbd>x</kbd> 判定できない</button>
-        </div>
-        <div class="row"><span class="lbl">pick</span>
-          <button data-pp="pick" data-v="yes">yes</button><button data-pp="pick" data-v="no">no</button><button data-pp="pick" data-v="unknown">unknown</button>
-          <span class="muted">選ばなければ 成功→yes / ほか→unknown</span></div>
-        <div class="row"><span class="lbl">place</span>
-          <button data-pp="place" data-v="yes">yes</button><button data-pp="place" data-v="no">no</button><button data-pp="place" data-v="unknown">unknown</button></div>
-        <div class="row"><input id="note" placeholder="ひとこと (空でよい・入力中はキーで判定しない)"></div>
-        <div id="status"></div>
-        <div class="muted"><kbd>←</kbd> <kbd>→</kbd> 前後の回 ・ <kbd>,</kbd> <kbd>.</kbd> コマ送り / 動画は <kbd>space</kbd> で再生</div>
-      </div>
-      <div class="box"><h3>AI の判定 (未確定)</h3><div id="ai"></div></div>
-      <div class="box"><h3>今の判定 (いちばん新しい行)</h3><div id="cur"></div></div>
-      <div class="box"><h3>この回の行 (古い順)</h3><div id="hist"></div></div>
-    </div>
+  <div id="hud">
+    <div id="title">—</div>
+    <div id="meta"></div>
   </div>
+  <div id="media"></div>
   <div id="chart"></div>
 </div>
+<aside id="panel">
+  <h2>成功率 (確定した回だけ)</h2>
+  <div id="rate"></div>
+  <h2>判定する (キー)</h2>
+  <div class="row">
+    <button class="key" data-oc="success"><kbd>y</kbd> 成功</button>
+    <button class="key" data-oc="failure"><kbd>n</kbd> 失敗</button>
+    <button class="key" data-oc="unknown"><kbd>u</kbd> 分からない</button>
+    <button class="key" data-oc="not_judgeable"><kbd>x</kbd> 判定できない</button>
+  </div>
+  <div class="row"><span class="lbl">pick</span>
+    <div class="seg"><button data-pp="pick" data-v="yes">yes</button><button data-pp="pick" data-v="no">no</button><button data-pp="pick" data-v="unknown">unknown</button></div></div>
+  <div class="row"><span class="lbl">place</span>
+    <div class="seg"><button data-pp="place" data-v="yes">yes</button><button data-pp="place" data-v="no">no</button><button data-pp="place" data-v="unknown">unknown</button></div></div>
+  <p class="note">選ばなければ 成功→yes / ほか→unknown</p>
+  <h2>ひとこと</h2>
+  <input id="note" placeholder="ひとこと (空でよい・入力中はキーで判定しない)">
+  <div id="status"></div>
+  <p class="note"><kbd>←</kbd> <kbd>→</kbd> 前後の回 ・ <kbd>,</kbd> <kbd>.</kbd> コマ送り / 動画は <kbd>space</kbd> で再生</p>
+  <h2>AI の判定 (未確定)</h2><div id="ai" class="dark"></div>
+  <h2>今の判定 (いちばん新しい行)</h2><div id="cur"></div>
+  <h2>この回の行 (古い順)</h2><div id="hist"></div>
+</aside>
 <script>
 const OC_JA = {success: "成功", failure: "失敗", unknown: "分からない", not_judgeable: "判定できない", not_applicable: "試行でない", "": "なし"};
 const LB_JA = {run_input: "記録時の入力", ai: "AI", human: "人 (AI と同じ)", human_fixed_ai: "人 (AI を直した)", human_only: "人 (AI なし)", rule: "決め (依頼185)", "": ""};
@@ -140,16 +162,19 @@ function applyFilter() {
 function renderRate(st) {
   if (!st) { $("#rate").textContent = ""; return; }
   const b = st.by_outcome || {};
-  const pct = st.rate === null ? "—" : (st.rate * 100).toFixed(1) + "%";
-  $("#rate").innerHTML = `成功率 <b>${pct}</b> (成功 ${st.success} / (成功 ${st.success} + 失敗 ${st.failure}))`
-    + `<div class="muted">分母に入れない: 試行でない ${b.not_applicable || 0} ・ 判定できない ${b.not_judgeable || 0} ・ 分からない ${b.unknown || 0} (確定 ${st.confirmed} 回のうち)</div>`;
+  const num = (v, unit, label) => `<div class="num"><b>${v}${unit ? `<small>${unit}</small>` : ""}</b><span>${label}</span></div>`;
+  $("#rate").innerHTML = `<div class="nums">`
+    + num(st.rate === null ? "—" : (st.rate * 100).toFixed(1), st.rate === null ? "" : "%", "成功率 (成功 / (成功 + 失敗))")
+    + num(st.confirmed, "回", "確定した回") + num(runs.filter(r => !r.confirmed).length, "回", "まだの回")
+    + num(st.success, "回", "成功") + num(st.failure, "回", "失敗") + `</div>`
+    + `<p class="note">分母に入れない: 試行でない ${b.not_applicable || 0} ・ 判定できない ${b.not_judgeable || 0} ・ 分からない ${b.unknown || 0} (確定 ${st.confirmed} 回のうち)</p>`;
 }
 
 async function loadRuns() {
   const res = await fetch("/api/runs"); const j = await res.json();
   if (!res.ok || !Array.isArray(j.runs)) throw new Error(j.error || "一覧を読めません");  // 今の一覧は残す (codex 4 回目 R1)
   runs = j.runs;
-  renderRate(j.stats);
+  renderRate(j.stats);  // まだの回は runs から数えるので runs を入れてから
   applyFilter();
 }
 
@@ -197,13 +222,13 @@ function renderMedia() {
     $("#fr").oninput = e => { frameIdx = +e.target.value; showFrame(); };
     showFrame();
   } else {
-    m.innerHTML = `<div class="box">映像がありません (top.mp4 も frames/ もない)</div>`;
+    m.innerHTML = `<div class="muted">映像がありません (top.mp4 も frames/ もない)</div>`;
   }
 }
 function showFrame() {
   const n = detail.frames.length; frameIdx = Math.max(0, Math.min(n - 1, frameIdx));
   $("#frm").src = `/media/${detail.run_stem}/frames/${detail.frames[frameIdx]}`;
-  $("#fr").value = frameIdx; $("#fl").textContent = `${frameIdx + 1} / ${n}`;
+  $("#fr").value = frameIdx; $("#fr").style.setProperty("--done", (n > 1 ? frameIdx / (n - 1) * 100 : 100) + "%"); $("#fl").textContent = `${frameIdx + 1} / ${n}`;
 }
 function stepFrame(d) { if (detail && !detail.has_mp4 && detail.frames.length && $("#frm")) { frameIdx += d; showFrame(); } }
 
@@ -217,9 +242,10 @@ function renderInfo() {
 // ---- 関節と爪のグラフ (関節は 1 本の軸・爪は下に別の帯. 2 つの縦軸は使わない) ----
 let chartGeom = null;
 function renderChart() {
+  // 幅は #main から (空のときは #chart を隠すので. 余白 32・枠と内側 26. 依頼 186)
   const s = detail.states, el = $("#chart");
   if (!s.t.length) { el.innerHTML = `<div class="muted">関節の記録がありません (recording/states.csv も log.jsonl の state もない)</div>`; chartGeom = null; return; }
-  const W = Math.max(700, el.clientWidth || 900), H = 240, GH = 44, L = 44, R = 12, T = 8, gap = 22;
+  const W = Math.max(640, ($("#main").clientWidth || 900) - 58), H = 240, GH = 44, L = 44, R = 12, T = 8, gap = 22;
   const tmax = Math.max(s.t[s.t.length - 1], ...(s.grip.map(g => g[0]))) || 1;
   let lo = Infinity, hi = -Infinity;
   s.joints.forEach(js => js.forEach(v => { lo = Math.min(lo, v); hi = Math.max(hi, v); }));
@@ -230,14 +256,14 @@ function renderChart() {
   for (let v = Math.ceil(lo / step) * step; v <= hi; v += step) ticks.push(v);
   const tstep = niceStep(tmax / 8); const tt = []; for (let v = 0; v <= tmax; v += tstep) tt.push(v);
   let svg = `<svg width="${W}" height="${gy0 + GH + 20}" id="csvg">`;
-  ticks.forEach(v => svg += `<line x1="${L}" x2="${W - R}" y1="${y(v)}" y2="${y(v)}" stroke="#ecebe6"/><text x="${L - 6}" y="${y(v) + 4}" text-anchor="end">${+v.toFixed(1)}°</text>`);
+  ticks.forEach(v => svg += `<line x1="${L}" x2="${W - R}" y1="${y(v)}" y2="${y(v)}" stroke="#eeeeea"/><text x="${L - 6}" y="${y(v) + 4}" text-anchor="end">${+v.toFixed(1)}°</text>`);
   tt.forEach(v => svg += `<text x="${x(v)}" y="${gy0 + GH + 14}" text-anchor="middle">${+v.toFixed(0)}s</text>`);
-  s.stages.forEach(([t, st]) => svg += `<line x1="${x(t)}" x2="${x(t)}" y1="${T}" y2="${gy0 + GH}" stroke="#d6d5ce" stroke-dasharray="2,3"/>`);
+  s.stages.forEach(([t, st]) => svg += `<line x1="${x(t)}" x2="${x(t)}" y1="${T}" y2="${gy0 + GH}" stroke="#d8d6d0" stroke-dasharray="2,3"/>`);
   for (let j = 0; j < 6; j++) {
     const d = s.t.map((t, i) => `${i ? "L" : "M"}${x(t).toFixed(1)},${y(s.joints[i][j]).toFixed(1)}`).join("");
     svg += `<path d="${d}" fill="none" stroke="${COLORS[j]}" stroke-width="2" stroke-linejoin="round"/>`;
   }
-  svg += `<rect x="${L}" y="${gy0}" width="${W - L - R}" height="${GH}" fill="none" stroke="#ecebe6"/>`;
+  svg += `<rect x="${L}" y="${gy0}" width="${W - L - R}" height="${GH}" fill="none" stroke="#d8d6d0"/>`;
   svg += `<text x="${L - 6}" y="${gy(1) + 4}" text-anchor="end">閉</text><text x="${L - 6}" y="${gy(0) + 4}" text-anchor="end">開</text>`;
   if (s.grip.length) {
     let d = `M${x(0)},${gy(s.grip[0][1])}`, pv = s.grip[0][1];
@@ -245,7 +271,7 @@ function renderChart() {
     d += `L${x(tmax)},${gy(pv)}`;
     svg += `<path d="${d}" fill="none" stroke="var(--grip)" stroke-width="2"/>`;
   }
-  svg += `<line id="cx" x1="0" x2="0" y1="${T}" y2="${gy0 + GH}" stroke="#1f1f1d" stroke-width="1" style="display:none"/>`;
+  svg += `<line id="cx" x1="0" x2="0" y1="${T}" y2="${gy0 + GH}" stroke="#1d1d1b" stroke-width="1" style="display:none"/>`;
   svg += `<line id="vx" x1="0" x2="0" y1="${T}" y2="${gy0 + GH}" stroke="var(--s1)" stroke-width="1" style="display:none"/>`;
   svg += `<rect id="hit" x="${L}" y="0" width="${W - L - R}" height="${gy0 + GH}" fill="transparent"/></svg>`;
   el.innerHTML = `<div id="legend">${COLORS.map((c, j) => `<span style="--c:${c}">j${j}</span>`).join("")}<span style="--c:var(--grip)">爪 (下の帯・${esc(s.grip_label)})</span><span class="muted">${esc(s.source)}</span></div>${svg}<div id="tip"></div>`;
