---
title: "4コママンガ『ハコブくん』は1コマずつ直すと流れが崩れる ― 4コマ通しの確認を足した"
slug: hakobu-4koma-flow-check
date: 2026-11-08
day: 468
meta_description: "4コママンガ『ハコブくん』を画像生成AIで1コマずつ描いて直すと、1枚は正しくても、ロボットの向き・大きさ・配膳車の位置がコマ同士でずれ、話の流れが崩れた。第143〜147話で、4コマを並べて読む確認を手順に足した。"
tags: [4コママンガ, ハコブくん, 縦長マンガ, 画像生成AI, Gemini, YouTubeショート, AI活用]
ai_consult: true
series: "ひとり研究所の裏側"
---

**画像生成AIに4コママンガを1コマずつ描かせて直していくと、1枚ずつは正しくても、ロボットがだれを向いているか・どれくらいの大きさか・舞台の配膳車がどこにあるかがコマ同士でずれ、話の流れが崩れる。** 病院で物を運ぶロボットの4コママンガ『ハコブくん』第143〜147話（[pixelstomotors.com/manga](https://pixelstomotors.com/manga/ep143)）を Google の Gemini で描いたとき、1コマずつの確認では見つからなかったずれを、エイイチが4コマを並べて読んで5回見つけました。

これを受けて、絵を描く手順に「4コマを通して話として読む確認」を足しました。変わったことは次の3つです。

- 見る順番が決まった：舞台の物の位置、人とロボットの位置と向き、セリフと絵が合っているか、大きさ、描き直したコマの前後、細部、の6つを、4コマを並べて順に見る
- 直しの頼み方が変わった：ロボットの向きを「画面は相手のほうへ約60°・輪のある側面が見える」と、見える面の名前で書く。高さは「大城さんのみぞおちとへその間」のように体の部位で書く
- 費用と時間：5話で Gemini に描かせた絵は120枚、見積もりで約1,100円（1話あたり約220円）。前の第138〜142話の約90円より高い。描き直しの80枚のうち66枚は、4コマを並べて見つかったずれの直しだった。描いてからショート5本・サイト公開・SNS の予約までは、約2時間20分だった

『ハコブくん』は、実際の病院でロボットを使ったときに起きたことを、調べた報告をもとに毎話1つ描いている4コママンガです。絵は1コマずつ縦長（縦横比 4:5）で、[Gemini の画像生成 API](https://ai.google.dev/gemini-api/docs/image-generation)（文章と参考の絵を渡すと絵を返す仕組み）に描かせています。描き直しは1コマ単位でできるので、だめなコマだけ直せば安く済みます。ところが、直したコマが前後のコマとつながっているかは、1コマだけ見ても分かりません。

たとえば第144話「お先にどうぞの作法」では、1コマ目を直したらハコブくんが車いすの目の前に来てしまい、2コマ目の「配膳車の向こうから来る」とつながらなくなりました。1コマ目を直し直すと、今度は配膳車が2コマ目と左右逆になり、大城さんと車いすが横を向きました。さらに、3コマ目ではハコブくんが2人と同じ向きに走り去り、配膳車も消えていました。

やったことの全体は次のとおりです。

| やったこと | なぜ | 結果 |
|---|---|---|
| 第143〜147話のプロットから、5話の構図とセリフ（spec）を書いた | 下調べと検証を済ませたプロット表の続きだったため | 20コマの指示ができた |
| バッチ（まとめて頼む半額の頼み方）で40枚を頼んだ | 下書きを安く何話も一度に描くため | 2分半後には全部返っていた |
| 1コマずつ確かめて、5コマを描き直した | 腕や脚の数、字、ロボットの形を見るため | 別のエージェントの確認で2コマ、自分で3コマ |
| エイイチの指摘で、向き・大きさ・流れを直した | 4コマを並べて読むと話がおかしかったため | 66枚を描き直した |
| 4コマを通して読む確認を手順に足した | 同じずれを次の話で出さないため | チェック項目と過去の間違いの台帳に書いた |
| ショート・表紙・英語版・サイト・SNS まで出した | 5話を公開するため | 10ページを公開、SNS を予約 |

【画像】LOG468_06_final_sheet.jpg 第143〜147話の確定版。上から1話ずつ、左から1〜4コマ目

効果の数字について断っておきます。枚数と時刻は、API の記録とファイルに残った実際の値です。円の額は、1枚約11円（ふつうの頼み方の実績・LOG410）と、バッチはその半額という Google の案内から出した見積もりです。4コマを通して読む確認でどれだけ直しが減るかは、まだ測っていません。次の話で、エイイチの指摘の数と描き直しの枚数で確かめます。


## 作業ログ ― 第143〜147話のプロットから、構図とセリフの指示を書いた

### 使ったプロット（plots_134_158_直した版.tsv）

プロットは、病院のロボットの報道と研究を調べて作り、codex（別の AI）に疑ってもらって直した表から、続きの5話を使いました（作業フォルダ `記事/ハコブくん_新プロット_20261031/`）。

| 話 | 題 | 登場人物 | もとにした調べ |
|---|---|---|---|
| 143 | 止まるまで分からない | 岩田、ハコブくん | 搬送ロボットが人をよけるよう作られていても、向かってくるように見える（[KQED](https://www.kqed.org/futureofyou/121043/hospital-uses-robots-instead-of-people-to-cart-things-around)） |
| 144 | お先にどうぞの作法 | 大城、森田茂、ハコブくん | 職員が患者のために脇へ寄ってもロボットは突き進む（[病院のロボットの研究を紹介した記事](https://bakadesuyo.com/?p=18751)） |
| 145 | 電話中なの | 大城、ハコブくん（田中先生は電話の声） | 電話中の職員に到着を告げて強い言葉を返された・内科系の病棟は割り込みに厳しい（同上、[ウィスコンシン大学の研究](https://peopleandrobots.wisc.edu/publications/robots-in-organizations-the-role-of-workflow-social-and-environmental-factors-in-human-robot-interaction-inproceedings/)） |
| 146 | 小突かれる場所 | 高杉、白石、ハコブくん | いら立った職員が搬送ロボットを蹴った（同上） |
| 147 | 気送管との競争 | 白石、大城、ハコブくん | 気送管がある病院で、看護師が看護ロボットは意味がなかったと話した（[Proof News](https://www.proofnews.org/moxi/)・メーカーは反論） |

登場人物は、プロット表より少なく絞りました。白石さんが動きを直す場面は、144・145話では絵にせず、4コマ目のハコブくんのセリフで分かるようにしています。145話の4コマ目は、プロットでは「画面に『届キマシタ』と出す」でしたが、ハコブくんの画面には字を出さない決まりがあるので、「電話ガ終ワルマデ、待チマシタ」と声で落としました。

### spec.py（構図とセリフの指示）

spec.py は、1話の4コマの構図・カメラ・セリフ・大きさを書くファイルです。`vbuild.py`（LOG448 までに作った道具）がこれを読み、キャラクターの参考の絵と決まりの文を足して Gemini への頼み文を組みます。下は、描き直しを全部終えた時点の spec.py です。最初の版の PANELS のあとに、直したときの PANELS の上書きを日付とエイイチの指摘を添えて足しているので、どこをどう直したかが残っています。

ハコブくんの向きを書く関数 `face` は、向きの指摘のあとに作りました（下の「ハコブくんの向きと大きさ」の節）。

#### _face.py（ハコブくんを相手に向ける言い方）

```python
# 2026-11-08 エイイチ「ハコブくんと患者や看護師の向きや身長などの大きさ変」→ ハコブくんの向きを相手に向ける言い方（向きと見える面で書く）
def face(side, who):
    scr, ring = ("RIGHT", "LEFT") if side == "right" else ("LEFT", "RIGHT")
    return ("HAKOBUKUN FACES %s — NOT THE VIEWER:" % who.upper(), [
        "Hakobukun's front (its screen face) is turned about 60° toward camera-%s, toward %s. It does NOT look at the viewer" % (side, who),
        "in the picture, its screen is on the %s part of its silhouette, seen at an angle, and the flat side panel with the round ring is clearly visible on the %s part" % (scr, ring),
        "its eyes look toward %s" % who])
```

#### 第143話の spec.py（止まるまで分からない）

```python
EP, TITLE = 143, "止まるまで分からない"
# 2026-11-08 カメラワーク：P1 岩田さんの肩なめ、廊下の奥からまっすぐ来るハコブくん／P2 低い位置、手前に大きなハコブくん（急停止の効果線）と身を固くする岩田さん／P3 岩田さんの顔に寄る・手前にハコブくんの画面の端／P4 高い引き：早めに端へ寄るハコブくんと、歩き出す岩田さん
CRUTCH = "Mr. Iwata walks with TWO underarm crutches, one under each arm, held by his own two hands"
CORRIDOR = ("CORRIDOR", ["a long straight plain hospital corridor suggested ONLY by two thin wall base lines converging toward the far end. NO doors, NO signs, NO text"])
PANELS = [
 dict(chars=["岩田", "ハコブ"], mouth="NORMAL", no_yline=True, costume=[CRUTCH], prop=CORRIDOR,
      hscale_text=["Mr. Iwata is in the near foreground at the RIGHT, seen from his rear three-quarter, knee-up, on his crutches, cut by the right and bottom edges: x 56–100%, his head at about 22% of the image height",
                   "Hakobukun is far down the corridor in the CENTRE, small, three-quarter front view, coming straight toward the camera: its top at about 44% and wheels at about 62% of the image height, x 30–46%"],
      camera=["SHOT SIZE: over-the-shoulder — Mr. Iwata in the foreground, Hakobukun small and far", "CAMERA POSITION: behind Mr. Iwata's right shoulder, looking down the corridor", "CAMERA HEIGHT: 150 cm"],
      comp=["Mr. Iwata's head turned a little, watching the robot come",
            "there is NO speech bubble in this picture"],
      extra=[("EFFECT:", ["two short straight motion lines behind Hakobukun, showing it rolls straight ahead. NO letters"])],
      scene="A long straight corridor. Mr. Iwata walks on his crutches. From the far end, Hakobukun rolls straight toward him."),
 dict(chars=["岩田", "ハコブ"], mouth="STOPPED", no_yline=True, costume=[CRUTCH],
      hscale_text=["Hakobukun is LARGE in the near foreground at the LEFT, three-quarter front view, just stopped: x 2–56%, its top at about 34% and wheels at about 94% of the image height",
                   "Mr. Iwata is only half a step beyond it at the right, full body, leaning back stiffly on his crutches: his head at about 10% and shoes at about 82% of the image height, x 56–84%"],
      camera=["SHOT SIZE: Hakobukun large in the foreground; Mr. Iwata full body just beyond", "CAMERA POSITION: low, front-left of Hakobukun", "CAMERA HEIGHT: 70 cm, looking up"],
      band="2% and 14%",
      comp=["Mr. Iwata frozen, eyes wide, shoulders raised, crutches planted, sweat drops",
            "the bubble 「ぶつかる…！」 at the upper left, its tail down to Mr. Iwata"],
      bubbles=[("Mr. Iwata", "ぶつかる…！", ["ぶつかる…！"])],
      sfx=[("ピタッ", "just above Hakobukun's top, at about 18–28% of the image height and 14–40% of the image width")],
      extra=[("EFFECT:", ["sharp stop lines at the base of Hakobukun's wheels, and a few short vertical shock lines around Mr. Iwata. NO letters"])],
      scene="Hakobukun does not change direction until it is right in front of Mr. Iwata. He stiffens, thinking it will hit him. Hakobukun stops dead at the last moment."),
 dict(chars=["岩田", "ハコブ"], mouth="NORMAL", no_yline=True, costume=[CRUTCH],
      hscale_text=["Mr. Iwata is LARGE at the right, chest-up, three-quarter view, looking down at the robot: x 36–92%, his face between 30% and 52% of the image height",
                   "only the top-left corner of Hakobukun's screen face is in the near foreground at the bottom-left, cut by the left and bottom edges: x 0–34%, y 66–100%"],
      camera=["SHOT SIZE: close shot of Mr. Iwata's face; the edge of Hakobukun's screen in the foreground", "CAMERA POSITION: just beside Hakobukun's screen, front-left of Mr. Iwata", "CAMERA HEIGHT: 120 cm, looking up"],
      band="3% and 26%",
      comp=["Mr. Iwata tired and serious, brows drawn, a little pale, one hand gripping a crutch",
            "two bubbles: 「止マレマス。安全デス」 at the upper left, tail down to Hakobukun at the bottom-left; 「止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ」 at the upper right, tail down to Mr. Iwata"],
      bubbles=[("Hakobukun", "止マレマス。安全デス", ["止マレマス。", "安全デス"]),
               ("Mr. Iwata", "止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ", ["止まれるのは", "知ってるよ。でも、", "止まるまで分からない", "のが怖いんだ"])],
      scene="Hakobukun proudly says it can stop and it is safe. Mr. Iwata answers that he knows it can stop — what scares him is not knowing until it actually stops."),
 dict(chars=["岩田", "ハコブ"], mouth="HAPPY", no_yline=True, costume=[CRUTCH], prop=CORRIDOR,
      hscale_text=["Hakobukun is in the middle distance at the LEFT, already curving toward the left wall well ahead of time, three-quarter view: its top at about 38% and wheels at about 58% of the image height, x 12–34%",
                   "Mr. Iwata is in the near foreground at the RIGHT-CENTRE, full body, walking forward on his crutches, relaxed: his head at about 30% and shoes at about 88% of the image height, x 50–72%"],
      camera=["SHOT SIZE: wide shot down the corridor", "CAMERA POSITION: front, slightly right, high", "CAMERA HEIGHT: 180 cm, looking a little down at Mr. Iwata"],
      band="3% and 22%",
      comp=["Mr. Iwata exhaling with relief, a small smile, a little puff of breath",
            "the bubble 「早メニ、ヨケル向キヲ見セマス」 at the upper left, its tail down to Hakobukun"],
      bubbles=[("Hakobukun", "早メニ、ヨケル向キヲ見セマス", ["早メニ、", "ヨケル向キヲ見セマス"])],
      extra=[("EFFECT:", ["a long gentle curved motion line behind Hakobukun, starting far away, showing it turned toward the wall early. NO line on the floor, NO letters"])],
      scene="The robot's movement has been changed: now Hakobukun turns early, far away, and moves to the edge of the corridor. Mr. Iwata breathes out and walks on."),
]


# ===== 2026-11-08 エイイチ「向きや身長などの大きさ変」→ P2・P3・P4 を同じ床（並び図）と、相手に向く向きで描き直す =====
import os
from importlib.machinery import SourceFileLoader as _L
_F = _L("_face", os.path.expanduser("~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/縦長/_face.py")).load_module().face
PANELS[1] = dict(chars=["岩田", "ハコブ"], mouth="STOPPED", full=["岩田"], costume=[CRUTCH],
      camera=["SHOT SIZE: full-body two-shot, side-front", "CAMERA POSITION: front-left, 3 m away", "CAMERA HEIGHT: 120 cm"],
      band="3% and 20%",
      comp=["Hakobukun is centred at about 34% of the image width, just stopped, facing Mr. Iwata at close range",
            "Mr. Iwata is centred at about 68% of the image width, body turned 45° to camera-left toward the robot, frozen, leaning back stiffly on his two crutches, eyes wide, sweat drops",
            "the gap between the robot's front and Mr. Iwata's feet is only about one hand's width",
            "the bubble 「ぶつかる…！」 at the upper right, its tail down to Mr. Iwata"],
      bubbles=[("Mr. Iwata", "ぶつかる…！", ["ぶつかる…！"])],
      sfx=[("ピタッ", "at the upper left above Hakobukun, at about 24–34% of the image height and 10–36% of the image width")],
      extra=[_F("right", "Mr. Iwata"), ("EFFECT:", ["sharp stop lines at the base of Hakobukun's wheels, and short vertical shock lines around Mr. Iwata. NO letters except the sound effect"])],
      scene="Hakobukun does not change direction until it is right in front of Mr. Iwata. He stiffens, thinking it will hit him. Hakobukun stops dead at the last moment, facing him.")
PANELS[2] = dict(chars=["岩田", "ハコブ"], mouth="NORMAL", no_yline=True, back=True, costume=[CRUTCH],
      hscale_text=["Mr. Iwata is LARGE at the right, chest-up, three-quarter view, looking DOWN at the robot in front of him: x 40–94%, his face between 28% and 50% of the image height",
                   "the upper part of Hakobukun is in the near foreground at the bottom-left, seen from its REAR-LEFT three-quarter: its rounded white top and back, and the flat side panel with the round ring. Its screen faces AWAY from the viewer, toward Mr. Iwata — the screen face is NOT visible. Cut by the left and bottom edges: x 0–44%, its top at about 64% of the image height",
                   "Hakobukun's top is far BELOW Mr. Iwata's chest — he is much taller"],
      camera=["SHOT SIZE: over-the-robot shot — Hakobukun's rear-left in the foreground, Mr. Iwata chest-up beyond", "CAMERA POSITION: behind Hakobukun's rear-left corner", "CAMERA HEIGHT: 120 cm, looking up at Mr. Iwata"],
      band="3% and 24%",
      comp=["Mr. Iwata tired and serious, brows drawn, a little pale, one hand gripping a crutch",
            "two bubbles: 「止マレマス。安全デス」 at the upper left, tail down to Hakobukun at the bottom-left; 「止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ」 at the upper right, tail down to Mr. Iwata"],
      bubbles=[("Hakobukun", "止マレマス。安全デス", ["止マレマス。", "安全デス"]),
               ("Mr. Iwata", "止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ", ["止まれるのは", "知ってるよ。でも、", "止まるまで分からない", "のが怖いんだ"])],
      scene="Hakobukun, facing Mr. Iwata, proudly says it can stop and it is safe. Mr. Iwata answers that he knows it can stop — what scares him is not knowing until it actually stops.")
PANELS[3] = dict(chars=["岩田", "ハコブ"], mouth="HAPPY", full=["岩田"], costume=[CRUTCH],
      camera=["SHOT SIZE: full-body two-shot in the corridor", "CAMERA POSITION: front, slightly right", "CAMERA HEIGHT: 150 cm"],
      band="3% and 22%",
      comp=["Hakobukun is centred at about 24% of the image width, already moved to the LEFT edge of the corridor and rolling along the wall toward camera-left, giving way",
            "Mr. Iwata is centred at about 66% of the image width, walking forward on his crutches, relaxed, a small smile, a little puff of breath",
            "the bubble 「早メニ、ヨケル向キヲ見セマス」 at the upper left, its tail down to Hakobukun"],
      bubbles=[("Hakobukun", "早メニ、ヨケル向キヲ見セマス", ["早メニ、", "ヨケル向キヲ見セマス"])],
      extra=[_F("left", "the wall on the left"), ("EFFECT:", ["a long gentle curved motion line behind Hakobukun, showing it turned toward the wall early. NO line on the floor, NO letters"])],
      scene="The robot's movement has been changed: now Hakobukun turns early and keeps to the edge of the corridor. Mr. Iwata breathes out and walks on.")


# 2026-11-08 2回目：P2 のハコブくんが岩田さんの胸まで届く大きさになった → 体の部位で高さを書き足す
PANELS[1]["comp"].append("SIZE: Hakobukun's top edge reaches only Mr. Iwata's BELT, just above his navel. His whole chest, shoulders and head are clearly ABOVE the robot. Hakobukun is NOT as tall as his chest")
PANELS[1]["comp"].append("this is a plain picture: draw NO measurement lines, NO arrows, NO numbers, NO percentages anywhere")

# 2026-11-08 バッチの下書き（1コマ2枚）から選んだ。146 P2・P3 は描き直し

# 2026-11-08 3回目：P3 の手前のハコブくん（背中）が岩田さんの肩ほどに見えた → 手前に置かず、同じ床で向き合う
PANELS[2] = dict(chars=["岩田", "ハコブ"], mouth="NORMAL", full=["岩田"], costume=[CRUTCH],
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: front-right, three-quarter", "CAMERA HEIGHT: 150 cm"],
      band="3% and 24%",
      comp=["Hakobukun is centred at about 30% of the image width, facing Mr. Iwata",
            "Mr. Iwata is centred at about 68% of the image width, on his two crutches, body turned 40° to camera-left, looking DOWN at the robot, tired and serious, brows drawn",
            "SIZE: Hakobukun's top edge reaches only Mr. Iwata's BELT, just above his navel. His chest, shoulders and head are clearly ABOVE the robot",
            "two bubbles: 「止マレマス。安全デス」 at the upper left, tail down to Hakobukun; 「止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ」 at the upper right, tail down to Mr. Iwata",
            "both bubbles run HORIZONTALLY"],
      bubbles=[("Hakobukun", "止マレマス。安全デス", ["止マレマス。", "安全デス"]),
               ("Mr. Iwata", "止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ", ["止まれるのは", "知ってるよ。でも、", "止まるまで分からない", "のが怖いんだ"])],
      extra=[_F("right", "Mr. Iwata")],
      scene="Hakobukun, facing Mr. Iwata, proudly says it can stop and it is safe. Mr. Iwata answers that he knows it can stop — what scares him is not knowing until it actually stops.")

# 2026-11-08 エイイチ「向きや身長などの大きさ変」のあとに選び直した
PICK = {1: '第143話_P1_20261108_003125b2.jpg', 2: '第143話_P2_20261108_010924.jpg', 3: '第143話_P3_20261108_011239.jpg', 4: '第143話_P4_20261108_010103.jpg'}
```

#### 第144話の spec.py（お先にどうぞの作法）

```python
EP, TITLE = 144, "お先にどうぞの作法"
# 2026-11-08 カメラワーク：P1 斜め前の引き、車いすを押す大城さんと奥のハコブくん／P2 大城さんの肩なめ、狭い所をそのまま来るハコブくん／P3 低い位置の真横、車いすの前を横切るハコブくん（スピード線）／P4 ハコブくんの背中なめ（ハコブくん目線）、先に通る2人
CHAIR = "Mr. Morita sits in a plain standard hospital wheelchair with NO text on it; Oshiro pushes it by both handles"
NOBADGE = "Mr. Morita is a patient: he wears NO lanyard, NO name badge, NO ID card"
CART = "a plain hospital meal cart parked against one wall, copied from the attached cart reference, narrowing the corridor. NO text"
PANELS = [
 dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", no_yline=True, not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      hscale_text=["Oshiro and Mr. Morita in his wheelchair are in the near foreground at the RIGHT, three-quarter front view, moving toward camera-left: Oshiro full body, her head at about 24% and shoes at about 90% of the image height, x 52–86%; Mr. Morita seated in front of her, his head at about 46%",
                   "Hakobukun is far down the corridor at the LEFT, small, three-quarter front view, coming toward them: its top at about 44% and wheels at about 60% of the image height, x 14–32%"],
      camera=["SHOT SIZE: knee-to-full shot of Oshiro and the wheelchair; Hakobukun small in the distance", "CAMERA POSITION: front-left, 3 m away", "CAMERA HEIGHT: 150 cm"],
      band="3% and 18%",
      comp=["Oshiro calm, looking ahead at the robot; Mr. Morita relaxed, hands on his lap",
            "the bubble 「あ、ハコブくん」 at the upper right, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "あ、ハコブくん", ["あ、ハコブくん"])],
      scene="A hospital corridor. Oshiro pushes Mr. Morita in a wheelchair. Ahead, she sees Hakobukun coming toward them."),
 dict(chars=["大城", "ハコブ"], mouth="NORMAL", no_yline=True, costume=[CHAIR], extra_refs=[("the meal cart parked in the corridor — copy its shape", "キャラ/配膳車.jpg")],
      prop=("PARKED CART", [CART + ", at the left beyond Oshiro"]),
      hscale_text=["Oshiro is in the near foreground at the RIGHT, seen from her rear three-quarter, waist-up, stopped, her hands on the wheelchair handles: x 54–100%, her head at about 28% of the image height, cut by the right and bottom edges",
                   "the parked meal cart is at the left beyond her, x 4–30%, y 40–80%",
                   "Hakobukun is about 3 m beyond, in the narrow gap beside the cart, three-quarter front view, still rolling forward at the same speed: its top at about 40% and wheels at about 70% of the image height, x 30–54%"],
      camera=["SHOT SIZE: over-the-shoulder — Oshiro in the foreground, Hakobukun beyond", "CAMERA POSITION: behind Oshiro's right shoulder", "CAMERA HEIGHT: 150 cm"],
      band="3% and 20%",
      comp=["Oshiro leaning slightly to look at the gap, checking the width",
            "the bubble 「通れるかな…」 at the upper left, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "通れるかな…", ["通れるかな…"])],
      extra=[("EFFECT:", ["two short straight motion lines behind Hakobukun. NO letters"])],
      scene="At a narrow spot beside a parked cart, Oshiro stops to check whether the wheelchair can fit through. Hakobukun keeps rolling into the open gap at the same speed."),
 dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", no_yline=True, not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      hscale_text=["Hakobukun crosses LARGE through the middle of the picture from right to left, three-quarter view, just in front of the wheelchair: x 22–64%, its top at about 38% and wheels at about 86% of the image height",
                   "Mr. Morita in the wheelchair is in the near foreground at the LEFT edge, side view, leaning back sharply: x 0–30%, his head at about 40% of the image height",
                   "Oshiro stands behind the wheelchair at the far left-top, waist-up, both hands up in surprise: her head at about 22% of the image height, x 4–26%"],
      camera=["SHOT SIZE: side view — the robot cutting across in front of the wheelchair", "CAMERA POSITION: low, at the side of the wheelchair", "CAMERA HEIGHT: 70 cm, looking slightly up"],
      band="3% and 20%",
      comp=["Mr. Morita startled, leaning back, eyes wide; Oshiro shocked, mouth open",
            "two bubbles: 「待ってたのに、先に行くの！？」 at the upper left, tail down to Oshiro; 「道ガ、空キマシタノデ」 at the upper right, tail down to Hakobukun"],
      bubbles=[("Nurse Oshiro", "待ってたのに、先に行くの！？", ["待ってたのに、", "先に行くの！？"]), ("Hakobukun", "道ガ、空キマシタノデ", ["道ガ、", "空キマシタノデ"])],
      extra=[("EFFECT:", ["horizontal speed lines behind Hakobukun, and short shock lines around Mr. Morita's head. NO letters"])],
      scene="Hakobukun cuts straight across in front of Mr. Morita's wheelchair. He leans back in surprise. Oshiro protests that she was waiting and it went first. Hakobukun explains the path was open."),
 dict(chars=["大城", "森田茂", "ハコブ"], mouth="HAPPY", no_yline=True, back=True, not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      hscale_text=["Hakobukun is in the near foreground at the bottom-LEFT, seen from BEHIND, stopped and waiting at the side, cut by the left and bottom edges: x 0–42%, its top at about 52% of the image height",
                   "Oshiro pushes Mr. Morita past it, about 1.5 m beyond, moving from left to right, three-quarter view: Oshiro full body, her head at about 20% and shoes at about 80% of the image height, x 44–72%; Mr. Morita seated in front of her, his head at about 40%, x 54–80%",
                   "Hakobukun's top edge stays BELOW the pit of Oshiro's stomach — she is clearly taller than the robot"],
      camera=["SHOT SIZE: Hakobukun's back in the foreground; the wheelchair passing beyond", "CAMERA POSITION: just behind Hakobukun's rear-left corner, Hakobukun's point of view", "CAMERA HEIGHT: 100 cm, looking slightly up"],
      band="3% and 18%",
      comp=["Mr. Morita smiling broadly, raising one hand to the robot as he passes; Oshiro smiling",
            "two bubbles: 「森田サン、オ先ニドウゾ」 at the upper left, tail down to Hakobukun; 「よう分かっとる」 at the upper right, tail down to Mr. Morita"],
      bubbles=[("Hakobukun", "森田サン、オ先ニドウゾ", ["森田サン、", "オ先ニドウゾ"]), ("Mr. Morita", "よう分かっとる", ["よう分かっとる"])],
      scene="A new movement has been added: in narrow places, Hakobukun stops short and lets the patient pass first. Mr. Morita rolls by, pleased."),
]


# ===== 2026-11-08 エイイチ「向きや身長などの大きさ変」→ P1・P3 を同じ床（並び図）と、相手に向く向きで描き直す =====
import os
from importlib.machinery import SourceFileLoader as _L
_F = _L("_face", os.path.expanduser("~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/縦長/_face.py")).load_module().face
PANELS[0] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", full=["大城"], not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      camera=["SHOT SIZE: full-body shot of the three on the same floor", "CAMERA POSITION: front-left, 4 m away", "CAMERA HEIGHT: 150 cm"],
      band="3% and 18%",
      comp=["Hakobukun is centred at about 22% of the image width, coming toward them along the corridor",
            "Mr. Morita is centred at about 56% of the image width, seated in the wheelchair facing camera-left toward the robot, relaxed, hands on his lap",
            "Oshiro is centred at about 76% of the image width, standing behind the wheelchair pushing it, looking ahead at the robot",
            "the bubble 「あ、ハコブくん」 at the upper right, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "あ、ハコブくん", ["あ、ハコブくん"])],
      extra=[_F("right", "Oshiro and Mr. Morita")],
      scene="A hospital corridor. Oshiro pushes Mr. Morita in a wheelchair. Ahead, she sees Hakobukun coming toward them.")
PANELS[2] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", full=["大城"], not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      camera=["SHOT SIZE: full-body shot of the three on the same floor", "CAMERA POSITION: front-left, 3 m away", "CAMERA HEIGHT: 140 cm"],
      band="3% and 20%",
      comp=["Hakobukun is centred at about 26% of the image width, cutting across from right to left just in front of the wheelchair's footrests, moving fast",
            "Mr. Morita is centred at about 54% of the image width, seated in the wheelchair facing camera-left, leaning back sharply, startled, eyes wide",
            "Oshiro is centred at about 74% of the image width, standing behind the wheelchair, one hand raised in protest, mouth open",
            "two bubbles: 「待ってたのに、先に行くの！？」 at the upper right, tail down to Oshiro; 「道ガ、空キマシタノデ」 at the upper left, tail down to Hakobukun"],
      bubbles=[("Nurse Oshiro", "待ってたのに、先に行くの！？", ["待ってたのに、", "先に行くの！？"]), ("Hakobukun", "道ガ、空キマシタノデ", ["道ガ、", "空キマシタノデ"])],
      extra=[_F("left", "the direction it is moving (camera-left)"), ("EFFECT:", ["horizontal speed lines trailing to the RIGHT of Hakobukun, and short shock lines around Mr. Morita's head. NO letters"])],
      scene="Hakobukun cuts straight across in front of Mr. Morita's wheelchair. He leans back in surprise. Oshiro protests that she was waiting and it went first. Hakobukun explains the path was open.")

# 2026-11-08 バッチの下書き（1コマ2枚）から選んだ。146 P2・P3 は描き直し

# 2026-11-08 3回目：P4 の手前のハコブくん（背中）が大城さんより大きく見えた → 同じ床で、脇で止まって相手に向く
PANELS[3] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="HAPPY", full=["大城"], not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      camera=["SHOT SIZE: full-body shot of the three on the same floor", "CAMERA POSITION: front, slightly left", "CAMERA HEIGHT: 140 cm"],
      band="3% and 18%",
      comp=["Hakobukun is centred at about 22% of the image width, stopped at the side of the corridor, waiting",
            "Mr. Morita is centred at about 52% of the image width, seated in the wheelchair rolling past toward camera-right, smiling broadly, raising one hand to the robot",
            "Oshiro is centred at about 74% of the image width, pushing the wheelchair, smiling",
            "SIZE: Hakobukun's top edge reaches only midway between the pit of Oshiro's stomach and her navel. Her chest, shoulders and head are clearly ABOVE the robot",
            "two bubbles: 「森田サン、オ先ニドウゾ」 at the upper left, tail down to Hakobukun; 「よう分かっとる」 at the upper right, tail down to Mr. Morita"],
      bubbles=[("Hakobukun", "森田サン、オ先ニドウゾ", ["森田サン、", "オ先ニドウゾ"]), ("Mr. Morita", "よう分かっとる", ["よう分かっとる"])],
      extra=[_F("right", "Mr. Morita")],
      scene="A new movement has been added: in narrow places, Hakobukun stops short at the side and lets the patient pass first. Mr. Morita rolls by, pleased.")


# 2026-11-08 エイイチ「1コマ目がこれだと2コマ目と繋がりがおかしい」→ 1コマ目でハコブくんが目の前にいた。2コマ目は配膳車の向こうから来る。1コマ目は廊下の奥、配膳車のさらに向こうに小さく見せる
PANELS[0] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", no_yline=True, not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      extra_refs=[("the meal cart parked in the corridor — copy its shape", "キャラ/配膳車.jpg")],
      prop=("CORRIDOR AND PARKED CART", ["a long straight hospital corridor shown ONLY by two thin wall base lines converging toward the far end. NO doors, NO signs, NO text",
                                         "a plain hospital meal cart parked against the LEFT wall in the middle distance, copied from the attached cart reference, narrowing the corridor. NO text"]),
      hscale_text=["Oshiro and Mr. Morita in his wheelchair are in the near foreground at the RIGHT, three-quarter front view, moving forward down the corridor: Oshiro full body, her head at about 24% and shoes at about 92% of the image height, x 50–88%; Mr. Morita seated in front of her, his head at about 46%",
                   "the parked meal cart is in the middle distance at the LEFT, about 6 m away: x 14–30%, y 40–64%",
                   "Hakobukun is FAR down the corridor, about 12 m away, just beyond the cart, small because of the distance, coming toward them: its top at about 46% and wheels at about 58% of the image height, x 30–38%. It is far away, NOT next to them"],
      camera=["SHOT SIZE: full-body shot of Oshiro and the wheelchair; the corridor, the cart and Hakobukun far beyond", "CAMERA POSITION: front-left, looking down the corridor", "CAMERA HEIGHT: 150 cm"],
      band="3% and 18%",
      comp=["Oshiro looking ahead down the corridor at the far robot; Mr. Morita relaxed, hands on his lap",
            "the bubble 「あ、ハコブくん」 at the upper right, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "あ、ハコブくん", ["あ、ハコブくん"])],
      scene="A long hospital corridor. Oshiro pushes Mr. Morita in a wheelchair. Far ahead, beyond a meal cart parked against the wall, she spots Hakobukun coming toward them.")


# 2026-11-08 エイイチ「1コマ目の台車の置き場所が左右逆（大きさもおかしい）・大城さんと車椅子の向きが変」→ 2コマ目と同じ側（2人の後ろ）から撮り、配膳車は右の壁ぎわ、2人は廊下の奥へ向かう
PANELS[0] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", no_yline=True, not_standing=["森田茂"], costume=[CHAIR, NOBADGE],
      extra_refs=[("the meal cart parked in the corridor — copy its shape", "キャラ/配膳車.jpg")],
      prop=("CORRIDOR AND PARKED CART", ["a long straight hospital corridor shown ONLY by two thin wall base lines and two ceiling lines converging toward ONE vanishing point at about x 45%, y 34%. NO doors, NO signs, NO text",
                                         "a plain hospital meal cart parked against the RIGHT wall in the middle distance, copied from the attached cart reference, narrowing the corridor on the right side. It is a tall cart, about as tall as Oshiro (1.6 m), drawn smaller only because it is farther away. NO text"]),
      hscale_text=["Oshiro and Mr. Morita in the wheelchair are in the near foreground at the bottom-RIGHT-CENTRE, seen from BEHIND at a rear three-quarter, BOTH facing AWAY from the viewer, moving straight down the corridor toward the vanishing point: Oshiro stands behind the wheelchair with both hands on its handles, the back of her head and her bun visible, her head at about 30% and shoes at about 96% of the image height, x 52–80%; Mr. Morita's head and shoulders seen from behind above the wheelchair back, at about 52%, x 34–58%",
                   "the parked meal cart is about 5 m ahead against the RIGHT wall: x 60–76%, y 30–60%",
                   "Hakobukun is FAR down the corridor, about 12 m away, at the LEFT side of the corridor beyond the cart, small because of the distance, coming toward them and facing them: its top at about 34% and wheels at about 44% of the image height, x 36–42%"],
      camera=["SHOT SIZE: wide shot down the corridor from behind the two", "CAMERA POSITION: behind Oshiro, slightly to her left, looking down the corridor", "CAMERA HEIGHT: 160 cm"],
      nofront=False,
      band="3% and 18%",
      comp=["Oshiro turns her head slightly to the left in profile, looking ahead at the far robot",
            "the bubble 「あ、ハコブくん」 at the upper left, its tail down to Oshiro",
            "the bubble text runs HORIZONTALLY"],
      bubbles=[("Nurse Oshiro", "あ、ハコブくん", ["あ、ハコブくん"])],
      scene="A long hospital corridor, seen from behind Oshiro, who pushes Mr. Morita in a wheelchair away from us down the corridor. Ahead, a meal cart is parked against the right wall. Far beyond it, Hakobukun is coming toward them on the left side.")


# 2026-11-08 エイイチ「全体的に流れが変じゃない？」→ 3コマ目でハコブくんが2人と同じ向きに走り去り、配膳車も消えていた。4コマ目も配膳車がなく、どこで譲ったのか分からない
# 3・4コマ目は真横から：2人は画面の左へ進む（1・2コマ目の「奥へ」をカメラが左の壁側から見た向き）、ハコブくんは左から来て右へ進む。配膳車は奥の壁ぎわの真ん中で、そこが狭い所
SIDE_CART = ("CORRIDOR AND PARKED CART", ["seen from the side: the far wall of the corridor is shown ONLY by one thin horizontal wall base line at about 72% of the image height. NO doors, NO signs, NO text",
             "a plain hospital meal cart, copied from the attached cart reference, is parked against the FAR wall at the centre of the picture, x 40–60%, its top at about 30% and wheels at about 74% of the image height. It narrows the corridor there. NO text"])
CART_REF = [("the meal cart parked in the corridor — copy its shape", "キャラ/配膳車.jpg")]
SIZE_O = "SIZE: Hakobukun's top edge reaches only midway between the pit of Oshiro's stomach and her navel. Her chest, shoulders and head are clearly ABOVE the robot"
PANELS[2] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="NORMAL", full=["大城"], not_standing=["森田茂"], costume=[CHAIR, NOBADGE], extra_refs=CART_REF, prop=SIDE_CART, yfeet=94,
      camera=["SHOT SIZE: full-body side view of the corridor", "CAMERA POSITION: at the near wall, looking straight across the corridor", "CAMERA HEIGHT: 130 cm"],
      band="3% and 20%",
      comp=["Hakobukun is centred at about 46% of the image width, in the NEAR lane just in front of the cart, rolling to the RIGHT at speed, its screen facing right",
            "Mr. Morita is centred at about 68% of the image width, seated in the wheelchair facing LEFT; the wheelchair's footrests are only a hand's width from Hakobukun's side as it shoots past. He leans back sharply, startled, eyes wide",
            "Oshiro is centred at about 84% of the image width, standing behind the wheelchair facing LEFT, both hands on the handles, stopped, mouth open in protest",
            SIZE_O,
            "two bubbles: 「道ガ、空キマシタノデ」 at the upper left, tail down to Hakobukun; 「待ってたのに、先に行くの！？」 at the upper right, tail down to Oshiro"],
      bubbles=[("Hakobukun", "道ガ、空キマシタノデ", ["道ガ、", "空キマシタノデ"]), ("Nurse Oshiro", "待ってたのに、先に行くの！？", ["待ってたのに、", "先に行くの！？"])],
      extra=[_F("right", "Oshiro and Mr. Morita"), ("EFFECT:", ["horizontal speed lines trailing to the LEFT of Hakobukun (it moves to the right), and short shock lines around Mr. Morita's head. NO letters"])],
      scene="While Oshiro has stopped to check whether the wheelchair fits past the parked cart, Hakobukun comes through the narrow gap at full speed and cuts right past the front of Mr. Morita's wheelchair. Oshiro protests that she was waiting; Hakobukun says the way was open.")
PANELS[3] = dict(chars=["大城", "森田茂", "ハコブ"], mouth="HAPPY", full=["大城"], not_standing=["森田茂"], costume=[CHAIR, NOBADGE], extra_refs=CART_REF, prop=SIDE_CART, yfeet=94,
      camera=["SHOT SIZE: full-body side view of the corridor (the same side as the previous panel)", "CAMERA POSITION: at the near wall, looking straight across the corridor", "CAMERA HEIGHT: 130 cm"],
      band="3% and 20%",
      comp=["Hakobukun is centred at about 16% of the image width, STOPPED and waiting at the left, well before the narrow spot by the cart, its screen facing right toward them",
            "Mr. Morita is centred at about 50% of the image width, seated in the wheelchair facing LEFT, rolling through the narrow spot beside the cart, smiling broadly and raising one hand to the robot",
            "Oshiro is centred at about 68% of the image width, behind the wheelchair facing LEFT, pushing it, smiling",
            SIZE_O,
            "two bubbles: 「森田サン、オ先ニドウゾ」 at the upper left, tail down to Hakobukun; 「よう分かっとる」 at the upper right, tail down to Mr. Morita"],
      bubbles=[("Hakobukun", "森田サン、オ先ニドウゾ", ["森田サン、", "オ先ニドウゾ"]), ("Mr. Morita", "よう分かっとる", ["よう分かっとる"])],
      extra=[_F("right", "Mr. Morita")],
      scene="A new movement has been added: at narrow spots, Hakobukun stops short before the gap and lets the patient go through first. Mr. Morita rolls past the cart, pleased.")


# 2026-11-08 エイイチ「2コマ目も変。通れそうに見えるのに『通れるかな』（もっと圧迫感を）」→ 壁を両側に近く描き、配膳車が幅の大半をふさぎ、すき間は車いすがぎりぎり。そこへハコブくんが入ってくる
PANELS[1] = dict(chars=["大城", "ハコブ"], mouth="NORMAL", no_yline=True, costume=[CHAIR], extra_refs=CART_REF,
      prop=("NARROW CORRIDOR AND PARKED CART", ["a NARROW hospital corridor: the left wall and the right wall are both drawn close, as two thin vertical edge lines with base lines, converging toward the far end. NO doors, NO signs, NO text",
                                                "a large plain hospital meal cart, copied from the attached cart reference, is parked against the RIGHT wall about 2 m ahead, LARGE in the picture: x 42–82%, its top at about 16% and wheels at about 70% of the image height. It blocks MOST of the corridor width",
                                                "between the cart and the LEFT wall there is only a NARROW gap, x 18–40%, barely as wide as a wheelchair. NO text"]),
      hscale_text=["Oshiro is in the near foreground at the RIGHT, seen from her rear three-quarter, waist-up, stopped, her hands on the wheelchair handles, leaning slightly to look at the gap: x 58–100%, her head at about 28% of the image height, cut by the right and bottom edges",
                   "Hakobukun is just entering the narrow gap from the far side, between the cart and the LEFT wall, three-quarter front view, coming toward them: its top at about 34% and wheels at about 64% of the image height, x 18–40%. It fills the gap almost completely"],
      camera=["SHOT SIZE: over-the-shoulder — Oshiro in the foreground, the cart and the narrow gap beyond", "CAMERA POSITION: behind Oshiro's right shoulder", "CAMERA HEIGHT: 150 cm"],
      band="2% and 14%",
      comp=["the picture feels CRAMPED: the two walls, the big cart and the robot leave almost no free space",
            "Oshiro anxious, brows drawn, measuring the gap with her eyes",
            "the bubble 「通れるかな…」 at the upper left, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "通れるかな…", ["通れるかな…"])],
      extra=[("EFFECT:", ["a few short vertical tension lines between the cart and the left wall, and two short motion lines behind Hakobukun. NO letters"])],
      scene="At a narrow spot where a big meal cart is parked, Oshiro stops to check whether the wheelchair can squeeze through the tiny gap. Hakobukun keeps rolling into that same gap from the other side.")


# 2026-11-08 エイイチ「1コマ目も、通路が広そうに見える」→ 2コマ目と同じ狭い廊下にする。壁は両側に近く、先の配膳車が幅の大半をふさぐ
PANELS[0]["prop"] = ("NARROW CORRIDOR AND PARKED CART", [
    "a NARROW hospital corridor, only a little wider than two wheelchairs: the left wall and the right wall are both drawn CLOSE, as thin vertical edge lines and base lines that converge to ONE vanishing point at about x 44%, y 34%. The walls frame the picture tightly on both sides. NO doors, NO signs, NO text",
    "a large plain hospital meal cart, copied from the attached cart reference, is parked against the RIGHT wall about 5 m ahead: x 50–68%, its top at about 26% and wheels at about 52% of the image height. It blocks MORE THAN HALF of the corridor width, leaving only a narrow gap on the left. NO text"])
PANELS[0]["hscale_text"] = [PANELS[0]["hscale_text"][0],
    "Hakobukun is far down the corridor, about 12 m away, in the narrow gap on the LEFT beyond the cart, small because of the distance, coming toward them and facing them: its top at about 34% and wheels at about 44% of the image height, x 36–44%"]
PANELS[0]["comp"].append("the picture feels CRAMPED: the close walls and the big cart leave little free space ahead of the wheelchair")

# 2026-11-08 エイイチ「向きや身長などの大きさ変」のあとに選び直した
PICK = {1: '第144話_P1_20261108_020148.jpg', 2: '第144話_P2_20261108_015902.jpg', 3: '第144話_P3_20261108_015739.jpg', 4: '第144話_P4_20261108_015740.jpg'}
```

#### 第145話の spec.py（電話中なの）

```python
EP, TITLE = 145, "電話中なの"
# 2026-11-08 カメラワーク：P1 大城さんの腰上（電話を肩にはさむ）・手前に到着したハコブくんの画面の端／P2 真横の引き、大声のハコブくんと振り向く大城さん／P3 大城さんの顔に大きく寄る（目をつぶる）／P4 ハコブくん目線、電話を置いた大城さんと横で待つハコブくん
PHONE = ("HANDSET", ["Oshiro holds one plain cordless internal phone handset — a plain handset with NO text"])
PANELS = [
 dict(chars=["大城", "ハコブ"], mouth="NORMAL", no_yline=True, prop=PHONE,
      hscale_text=["Oshiro is at the RIGHT-CENTRE, waist-up, three-quarter view, standing at a plain low counter (only its top edge visible at about 72% of the image height): x 34–86%, her head at about 26% of the image height",
                   "the top-left part of Hakobukun is in the near foreground at the bottom-LEFT, three-quarter view, just arriving, cut by the left and bottom edges: x 0–30%, its top at about 62% of the image height"],
      camera=["SHOT SIZE: waist-up of Oshiro; the edge of Hakobukun in the foreground", "CAMERA POSITION: front-left of Oshiro", "CAMERA HEIGHT: 140 cm"],
      band="3% and 18%",
      comp=["Oshiro holds the handset pinched between her ear and her raised shoulder, writing on a small blank memo pad on the counter with a pen. The memo pad is BLANK, NO writing",
            "she is concentrating, eyes on the memo, not noticing the robot",
            "the bubble 「はい、田中先生。点滴の時間ですね」 at the upper right, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "はい、田中先生。点滴の時間ですね", ["はい、田中先生。", "点滴の時間ですね"])],
      scene="The nurses' station. Oshiro is on the phone with Dr. Tanaka, the handset pinched on her shoulder, taking notes. Hakobukun arrives."),
 dict(chars=["大城", "ハコブ"], mouth="NORMAL", full=["大城"], prop=PHONE,
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: from the side, slightly front", "CAMERA HEIGHT: 150 cm"],
      band="3% and 24%",
      comp=["Hakobukun is centred at about 32% of the image width, three-quarter view facing right toward Oshiro",
            "Oshiro is centred at about 64% of the image width, turned 30° toward the robot, pressing one palm over the handset's mouthpiece, glaring at the robot",
            "two bubbles: 「オ薬ガ、届キマシタ！受ケ取リヲ、オ願イシマス！」 at the upper left, LARGE, tail down to Hakobukun; 「今、電話中なの見えない？」 at the upper right, tail down to Oshiro"],
      bubbles=[("Hakobukun", "オ薬ガ、届キマシタ！受ケ取リヲ、オ願イシマス！", ["オ薬ガ、届キマシタ！", "受ケ取リヲ、", "オ願イシマス！"]), ("Nurse Oshiro", "今、電話中なの見えない？", ["今、電話中なの", "見えない？"])],
      extra=[("EFFECT:", ["loud-voice radiating lines around Hakobukun's screen. A small anger vein mark near Oshiro's temple. NO letters"])],
      scene="Hakobukun announces loudly that the medicine has arrived and asks her to sign for it. Oshiro covers the mouthpiece and hisses: can't you see I'm on the phone?"),
 dict(chars=["大城"], no_yline=True, prop=PHONE,
      hscale_text=["Oshiro's face is LARGE, a close-up filling most of the picture: x 14–90%, her face between 30% and 78% of the image height, the handset at her ear"],
      camera=["SHOT SIZE: close-up of Oshiro's face", "CAMERA POSITION: front-right, three-quarter", "CAMERA HEIGHT: 155 cm"],
      band="3% and 24%",
      comp=["Oshiro squeezing her eyes shut, lips pressed tight, enduring it; vertical gloom lines above her",
            "two bubbles: 「受ケ取リヲ、オ願イシマス」 at the upper left, its tail pointing out of the LEFT edge of the picture toward the robot off-screen; 「え、何？」 at the upper right, a smaller bubble whose tail points to the handset (Dr. Tanaka's voice on the phone)"],
      bubbles=[("Hakobukun (off-screen, left)", "受ケ取リヲ、オ願イシマス", ["受ケ取リヲ、", "オ願イシマス"]), ("Dr. Tanaka on the phone", "え、何？", ["え、何？"])],
      tails={"受ケ取リヲ、オ願イシマス": "out of the left edge, toward the off-screen robot", "え、何？": "the handset at Oshiro's ear"},
      extra=[("EFFECT:", ["vertical gloom lines above Oshiro's head. NO letters"]), ("KATAKANA CHECK:", ["in 受ケ取リヲ the character after 取 is the KATAKANA リ, NOT the hiragana り"])],
      scene="Hakobukun repeats its request from off-screen. Over the phone, Dr. Tanaka asks what that was. Oshiro shuts her eyes."),
 dict(chars=["大城", "ハコブ"], mouth="HAPPY", no_yline=True,
      hscale_text=["Hakobukun is in the near foreground at the LEFT, three-quarter view, waiting quietly beside the counter: x 4–48%, its top at about 46% and wheels at about 94% of the image height",
                   "Oshiro is at the right beyond it, waist-up, turned 40° toward the robot, putting the handset down on the counter (only its top edge visible at about 70% of the image height): her head at about 24% of the image height, x 50–84%",
                   "Hakobukun's top edge stays BELOW Oshiro's chest — she is clearly taller than the robot"],
      camera=["SHOT SIZE: Hakobukun in the foreground; Oshiro waist-up beyond", "CAMERA POSITION: low, front-left of Hakobukun", "CAMERA HEIGHT: 100 cm (Hakobukun's eye level), looking slightly up at Oshiro"],
      band="3% and 22%",
      comp=["Oshiro smiling warmly, one hand on top of the robot's head",
            "two bubbles: 「電話ガ終ワルマデ、待チマシタ」 at the upper left, tail down to Hakobukun; 「待っててくれて、ありがと」 at the upper right, tail down to Oshiro"],
      bubbles=[("Hakobukun", "電話ガ終ワルマデ、待チマシタ", ["電話ガ終ワルマデ、", "待チマシタ"]), ("Nurse Oshiro", "待っててくれて、ありがと", ["待っててくれて、", "ありがと"])],
      scene="The settings have been changed: when someone is on the phone, Hakobukun waits quietly beside them (urgent drugs ping the nurse's device instead). When Oshiro hangs up, she thanks it for waiting."),
]


# ===== 2026-11-08 エイイチ「向きや身長などの大きさ変」→ P1・P2・P4 を相手に向く向きと、並び図の大きさで描き直す =====
import os
from importlib.machinery import SourceFileLoader as _L
_F = _L("_face", os.path.expanduser("~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/縦長/_face.py")).load_module().face
PANELS[0] = dict(chars=["大城", "ハコブ"], mouth="NORMAL", no_yline=True, back=True, prop=PHONE,
      hscale_text=["Oshiro is at the RIGHT-CENTRE, waist-up, three-quarter view, standing at a plain low counter (only its top edge visible at about 70% of the image height): x 40–88%, her head at about 24% of the image height",
                   "Hakobukun is in the near foreground at the bottom-LEFT, seen from its REAR-LEFT three-quarter, just arriving and facing Oshiro: its back and the flat side panel with the round ring are visible; its screen faces AWAY from the viewer toward Oshiro. Cut by the left and bottom edges: x 0–40%, its top at about 60% of the image height",
                   "Hakobukun's top is BELOW Oshiro's waist level in the picture — she is clearly taller"],
      camera=["SHOT SIZE: over-the-robot shot — Hakobukun's rear-left in the foreground, Oshiro waist-up beyond", "CAMERA POSITION: behind Hakobukun's rear-left corner", "CAMERA HEIGHT: 140 cm"],
      band="3% and 18%",
      comp=["Oshiro holds the handset pinched between her ear and her raised shoulder, writing on a small blank memo pad on the counter with a pen. The memo pad is BLANK, NO writing",
            "she is concentrating, eyes on the memo, not noticing the robot",
            "the bubble 「はい、田中先生。点滴の時間ですね」 at the upper right, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "はい、田中先生。点滴の時間ですね", ["はい、田中先生。", "点滴の時間ですね"])],
      scene="The nurses' station. Oshiro is on the phone with Dr. Tanaka, the handset pinched on her shoulder, taking notes. Hakobukun arrives behind her.")
PANELS[1]["extra"] = [_F("right", "Oshiro")] + PANELS[1]["extra"]
PANELS[3] = dict(chars=["大城", "ハコブ"], mouth="HAPPY", full=["大城"], prop=PHONE,
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: front-left, three-quarter", "CAMERA HEIGHT: 120 cm"],
      band="3% and 22%",
      comp=["Hakobukun is centred at about 32% of the image width, waiting quietly beside her",
            "Oshiro is centred at about 64% of the image width, body turned 40° toward the robot, smiling warmly, resting one hand on the top of the robot's head; her other hand puts the cordless handset down",
            "two bubbles: 「電話ガ終ワルマデ、待チマシタ」 at the upper left, tail down to Hakobukun; 「待っててくれて、ありがと」 at the upper right, tail down to Oshiro"],
      bubbles=[("Hakobukun", "電話ガ終ワルマデ、待チマシタ", ["電話ガ終ワルマデ、", "待チマシタ"]), ("Nurse Oshiro", "待っててくれて、ありがと", ["待っててくれて、", "ありがと"])],
      extra=[_F("right", "Oshiro")],
      scene="The settings have been changed: when someone is on the phone, Hakobukun waits quietly beside them (urgent drugs ping the nurse's device instead). When Oshiro hangs up, she thanks it for waiting.")


# 2026-11-08 2回目：P1 は手前のハコブくんが大きすぎ、P2 も大きすぎ、P4 は「ガ」がひらがな → 同じ床で並べ、部位で高さを書く
SIZE_O = "SIZE: Hakobukun's top edge reaches only midway between the pit of Oshiro's stomach and her navel. Her chest, shoulders and head are clearly ABOVE the robot. Hakobukun is NOT as tall as her chest"
PANELS[0] = dict(chars=["大城", "ハコブ"], mouth="NORMAL", full=["大城"], prop=PHONE,
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: front-left, three-quarter", "CAMERA HEIGHT: 140 cm"],
      band="3% and 18%",
      comp=["Hakobukun is centred at about 28% of the image width, just arriving from the left",
            "Oshiro is centred at about 64% of the image width, standing at the left end of a plain waist-high counter that runs off the right edge, body turned 30° to camera-right toward the counter",
            "Oshiro holds the handset pinched between her ear and her raised shoulder, writing on a small blank memo pad on the counter with a pen. The memo pad is BLANK, NO writing. She is concentrating and does not notice the robot",
            SIZE_O,
            "the bubble 「はい、田中先生。点滴の時間ですね」 at the upper right, its tail down to Oshiro"],
      bubbles=[("Nurse Oshiro", "はい、田中先生。点滴の時間ですね", ["はい、田中先生。", "点滴の時間ですね"])],
      extra=[_F("right", "Oshiro")],
      scene="The nurses' station. Oshiro is on the phone with Dr. Tanaka, the handset pinched on her shoulder, taking notes. Hakobukun arrives beside her.")
PANELS[1]["comp"].append(SIZE_O)
PANELS[3]["comp"].append(SIZE_O)
PANELS[3]["extra"].append(("KATAKANA CHECK:", ["in 電話ガ終ワルマデ the character after 話 is the KATAKANA ガ, NOT the hiragana が", "both bubbles run HORIZONTALLY"]))

# 2026-11-08 バッチの下書き（1コマ2枚）から選んだ。146 P2・P3 は描き直し
# 2026-11-08 エイイチ「向きや身長などの大きさ変」のあとに選び直した
PICK = {1: '第145話_P1_20261108_010854.jpg', 2: '第145話_P2_20261108_010901.jpg', 3: '第145話_P3_20261108_003125b2.jpg', 4: '第145話_P4_20261108_010907.jpg'}
```

#### 第146話の spec.py（小突かれる場所）

```python
EP, TITLE = 146, "小突かれる場所"
# 2026-11-08 カメラワーク：P1 高い引き、曲がり角の手前で止まるハコブくんと走る高杉さん／P2 床すれすれ、手前に車輪を小突く足（大きく）／P3 ハコブくんの頭なめ、しゃがんで記録を見る白石さん／P4 斜め前の全身、角の先で待つハコブくんの頭をなでて通る高杉さん
CORNER = "a plain corridor corner suggested ONLY by one thin vertical wall-corner line and a short wall base line. NO doors, NO signs, NO text"
WAGON = "Takasugi pushes a plain small two-shelf nursing wagon with a few plain boxes on it. NO text"
PANELS = [
 dict(chars=["高杉", "ハコブ"], mouth="NORMAL", no_yline=True, prop=("CORRIDOR CORNER", [CORNER]),
      hscale_text=["Hakobukun is in the near foreground at the LEFT-CENTRE, stopped and waiting just before the corner, three-quarter view: x 10–44%, its top at about 52% and wheels at about 88% of the image height",
                   "Takasugi is in the distance beyond the corner at the right, full body, running across with motion lines: her head at about 30% and shoes at about 60% of the image height, x 56–76%"],
      camera=["SHOT SIZE: wide shot of the corner", "CAMERA POSITION: front-left, high", "CAMERA HEIGHT: 165 cm"],
      band="3% and 18%",
      comp=["Takasugi hurrying, a few sweat drops",
            "the bubble 「次は302号室…！」 at the upper right, its tail down to Takasugi"],
      bubbles=[("Nurse Takasugi", "次は302号室…！", ["次は302号室…！"])],
      sfx=[("ピンポーン", "at the upper left, at about 20–30% of the image height and 10–40% of the image width")],
      extra=[("EFFECT:", ["speed lines behind the running Takasugi. NO letters except the sound effect"])],
      scene="Night ward. Nurse calls keep ringing and Takasugi is running around. Just before a corner, Hakobukun stands still, waiting for someone to collect a delivery."),
 dict(chars=["高杉", "ハコブ"], mouth="TROUBLED", no_yline=True, extra_eyes=1, prop=("WAGON", [WAGON]),
      # 2026-11-08 1回目：手前に誰のものでもない大きな脚が出た（2枚とも）→ 足を手前に大きく切らず、高杉さんの全身の中で足を見せる
      hscale_text=["Hakobukun is at the LEFT, three-quarter view, standing still: x 6–46%, its top at about 40% and wheels at about 90% of the image height",
                   "Takasugi is at the RIGHT, full body, about half a step from the robot, both hands on the handle of the wagon behind her right side: her head at about 16% and her standing shoe at about 90% of the image height, x 50–80%",
                   "her LEFT foot is lifted a little and its toe lightly pokes the white bumper at the base of Hakobukun, at about 84% of the image height and 44% of the image width",
                   "Hakobukun's top edge reaches only the pit of Takasugi's stomach — she is clearly taller than the robot"],
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: low, front-left", "CAMERA HEIGHT: 60 cm, looking up"],
      band="3% and 14%",
      comp=["the leg that pokes the robot is Takasugi's OWN left leg, attached to her hip. There is NO other leg, NO large foreground leg, NO second person in the picture",
            "Takasugi exasperated, brows sharp, cheeks puffed, giving a small poke, not a violent kick",
            "the bubble 「なんでいつもここに！」 at the upper left, its tail down to Takasugi"],
      bubbles=[("Nurse Takasugi", "なんでいつもここに！", ["なんでいつもここに！"])],
      sfx=[("コツッ", "beside the toe and the bumper, at about 66–76% of the image height and 18–40% of the image width")],
      extra=[("EFFECT:", ["a small impact star where the toe touches the bumper, and a few short anger lines near Takasugi's head. NO letters except the sound effect"])],
      scene="Rushing around the corner with the wagon, Takasugi almost crashes into Hakobukun. Without thinking, she pokes its wheel lightly with her own foot. Hakobukun's EYES stay the two plain upright white ovals, fully round at both ends — only its small wavy mouth shows the trouble. NO slanted, flat-topped or worried eyes."),
 dict(chars=["白石", "ハコブ"], mouth="STOPPED", no_yline=True, not_standing=["白石"],
      # 2026-11-08 1回目：手前のハコブくんの頭が別の形／ハコブくんが2台 → 手前に頭を切って入れるのをやめ、1台だけを全身で
      hscale_text=["there is exactly ONE Hakobukun: at the LEFT, three-quarter view, full body: x 8–46%, its top at about 36% and wheels at about 82% of the image height",
                   "Shiraishi crouches on one knee beside the robot's base at the right, three-quarter view, a plain tablet in her hands: her head at about 40% of the image height, x 50–84%"],
      camera=["SHOT SIZE: medium shot — the robot and the crouching engineer", "CAMERA POSITION: front, slightly right", "CAMERA HEIGHT: 110 cm"],
      band="3% and 24%",
      comp=["the tablet screen, angled toward the viewer, shows only a column of the same small impact-star icon repeated five times. NO text, NO numbers",
            "Shiraishi thoughtful, a finger on her chin, frowning slightly",
            "two bubbles: 「衝撃ヲ、マタ検知シマシタ」 at the upper left, tail down to Hakobukun; 「故障はない…。壊れにくくする…じゃないな」 at the upper right, tail down to Shiraishi"],
      bubbles=[("Hakobukun", "衝撃ヲ、マタ検知シマシタ", ["衝撃ヲ、", "マタ検知シマシタ"]), ("Engineer Shiraishi", "故障はない…。壊れにくくする…じゃないな", ["故障はない…。", "壊れにくくする…", "じゃないな"])],
      scene="Next morning, Shiraishi checks the records. Impact detected — many times in the past few days. Nothing is broken. She thinks: making it tougher is not the answer."),
 dict(chars=["高杉", "ハコブ"], mouth="HAPPY", full=["高杉"], prop=("CORRIDOR CORNER", [CORNER + "; the corner is at the LEFT, an open wider space beyond it at the right"]),
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: front-left, three-quarter", "CAMERA HEIGHT: 150 cm"],
      band="3% and 24%",
      comp=["Hakobukun is centred at about 62% of the image width, waiting in the open space past the corner, three-quarter view",
            "Takasugi is centred at about 36% of the image width, walking past the robot with a calm step, body turned 30° to camera-right, patting the top of the robot's head with one hand, a sheepish smile",
            "two bubbles: 「ココナラ、邪魔ニナリマセン」 at the upper right, tail down to Hakobukun; 「…昨日はごめんね」 at the upper left, tail down to Takasugi"],
      bubbles=[("Hakobukun", "ココナラ、邪魔ニナリマセン", ["ココナラ、", "邪魔ニナリマセン"]), ("Nurse Takasugi", "…昨日はごめんね", ["…昨日は", "ごめんね"])],
      scene="Shiraishi plotted all the impacts on the map: every one happened just before the same corner. The waiting spot has been moved past the corner into open space. Takasugi walks by, pats the robot and apologises for yesterday."),
]


# ===== 2026-11-08 エイイチ「向きや身長などの大きさ変」→ P2・P3・P4 を相手に向く向きと、並び図の大きさで描き直す =====
import os
from importlib.machinery import SourceFileLoader as _L
_F = _L("_face", os.path.expanduser("~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/縦長/_face.py")).load_module().face
PANELS[1] = dict(chars=["高杉", "ハコブ"], mouth="TROUBLED", full=["高杉"], prop=("WAGON", [WAGON]),
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: front-left", "CAMERA HEIGHT: 100 cm, looking slightly up"],
      band="3% and 18%",
      comp=["Hakobukun is centred at about 32% of the image width, standing still",
            "Takasugi is centred at about 66% of the image width, body turned 40° toward the robot, one hand on the wagon handle behind her; her LEFT foot reaches out and its toe lightly pokes the white bumper at the base of Hakobukun",
            "the leg that pokes the robot is Takasugi's OWN left leg, attached to her hip. NO other leg, NO second person",
            "Takasugi exasperated, brows sharp, cheeks puffed, a small poke, not a violent kick",
            "the bubble 「なんでいつもここに！」 at the upper right, its tail down to Takasugi"],
      bubbles=[("Nurse Takasugi", "なんでいつもここに！", ["なんでいつもここに！"])],
      sfx=[("コツッ", "beside the toe and the bumper, at about 70–80% of the image height and 10–30% of the image width")],
      extra=[_F("right", "Takasugi"), ("EFFECT:", ["a small impact star where the toe touches the bumper. NO letters except the sound effect"])],
      scene="Rushing around the corner with the wagon, Takasugi almost crashes into Hakobukun. Without thinking, she pokes its wheel lightly with her own foot. Hakobukun's EYES stay the two plain upright white ovals — only its small wavy mouth shows the trouble.")
PANELS[2] = dict(chars=["白石", "ハコブ"], mouth="STOPPED", not_standing=["白石"],
      camera=["SHOT SIZE: medium shot — the robot and the crouching engineer on the same floor", "CAMERA POSITION: front-left", "CAMERA HEIGHT: 110 cm"],
      band="3% and 24%",
      comp=["Hakobukun is centred at about 32% of the image width",
            "Shiraishi is centred at about 68% of the image width, crouching on one knee beside the robot, three-quarter view, a plain tablet in her hands",
            "the tablet screen, angled toward the viewer, shows only a column of the same small impact-star icon repeated five times. NO text, NO numbers",
            "Shiraishi thoughtful, a finger on her chin, frowning slightly",
            "two bubbles: 「衝撃ヲ、マタ検知シマシタ」 at the upper left, tail down to Hakobukun; 「故障はない…。壊れにくくする…じゃないな」 at the upper right, tail down to Shiraishi"],
      bubbles=[("Hakobukun", "衝撃ヲ、マタ検知シマシタ", ["衝撃ヲ、", "マタ検知シマシタ"]), ("Engineer Shiraishi", "故障はない…。壊れにくくする…じゃないな", ["故障はない…。", "壊れにくくする…", "じゃないな"])],
      extra=[_F("right", "Shiraishi")],
      scene="Next morning, Shiraishi checks the records. Impact detected — many times in the past few days. Nothing is broken. She thinks: making it tougher is not the answer.")
PANELS[3]["comp"][1] = PANELS[3]["comp"][1]
PANELS[3]["extra"] = [_F("left", "Takasugi")]
PANELS[3]["comp"] = [c.replace("Hakobukun is centred at about 62% of the image width, waiting in the open space past the corner, three-quarter view", "Hakobukun is centred at about 62% of the image width, waiting in the open space past the corner") for c in PANELS[3]["comp"]]


# 2026-11-08 2回目：P2 のハコブくんがやや大きい・P4 は小さく縦書き → 部位で高さを書く
SIZE_T = "SIZE: Hakobukun's top edge reaches the pit of Takasugi's stomach, the bottom of her breastbone. Her chest, shoulders and head are clearly ABOVE the robot. NOT as tall as her chest, NOT only to her waist"
PANELS[1]["comp"].append(SIZE_T)
PANELS[3]["comp"].append(SIZE_T)
PANELS[3]["comp"].append("both bubbles run HORIZONTALLY, wider than tall")

# 2026-11-08 バッチの下書き（1コマ2枚）から選んだ。146 P2・P3 は描き直し
# 2026-11-08 エイイチ「向きや身長などの大きさ変」のあとに選び直した
PICK = {1: '第146話_P1_20261108_003125b1.jpg', 2: '第146話_P2_20261108_010944.jpg', 3: '第146話_P3_20261108_010629.jpg', 4: '第146話_P4_20261108_010943.jpg'}
```

#### 第147話の spec.py（気送管との競争）

```python
EP, TITLE = 147, "気送管との競争"
# 2026-11-08 カメラワーク：P1 白石さんの肩なめ、壁の気送管を指さす大城さん／P2 手元の大写し：カプセルを管に入れる手（手前）と、奥で走り出すハコブくん／P3 床に近い位置、手前に届いたカプセル、奥で固まるハコブくん／P4 斜め前の全身、運ぶ物を書き直した紙を持つ白石さんと、大きな箱を運ぶハコブくん
TUBE = "a pneumatic tube station on the wall: a plain wall-mounted box with a small hinged round door on the front and one round tube going up into the ceiling. NO text, NO buttons with letters"
CAPSULE = "a plain clear plastic pneumatic tube capsule, about 30 cm long, with plain caps at both ends. NO text"
PANELS = [
 dict(chars=["白石", "大城"], no_yline=True, prop=("PNEUMATIC TUBE STATION", [TUBE + ", at the upper right of the picture"]),
      hscale_text=["Shiraishi is in the near foreground at the LEFT, seen from her rear three-quarter, chest-up, cut by the left and bottom edges: x 0–40%, her head at about 30% of the image height",
                   "Oshiro is about 1.5 m beyond at the right-centre, waist-up, three-quarter view, pointing at the tube station on the wall with one hand: her head at about 26% of the image height, x 42–70%",
                   "the tube station on the wall at x 70–90%, y 30–56%"],
      camera=["SHOT SIZE: over-the-shoulder — Shiraishi in the foreground, Oshiro waist-up beyond", "CAMERA POSITION: behind Shiraishi's left shoulder", "CAMERA HEIGHT: 150 cm"],
      band="3% and 24%",
      comp=["Shiraishi holding a plain clipboard (blank, NO writing), confident",
            "Oshiro deadpan, one eyebrow raised",
            "two bubbles: 「次は、ハコブくんで検体を検査室へ運びます」 at the upper left, tail down to Shiraishi; 「…検体なら、もうあれがあるけど」 at the upper right, tail down to Oshiro"],
      bubbles=[("Engineer Shiraishi", "次は、ハコブくんで検体を検査室へ運びます", ["次は、ハコブくんで", "検体を検査室へ", "運びます"]), ("Nurse Oshiro", "…検体なら、もうあれがあるけど", ["…検体なら、", "もうあれがあるけど"])],
      scene="A meeting on the ward. Shiraishi announces that Hakobukun will carry specimens to the lab next. Oshiro points at the pneumatic tube station on the wall: they already have that."),
 dict(chars=["大城", "ハコブ"], mouth="NORMAL", no_yline=True, prop=("PNEUMATIC TUBE STATION", [TUBE, CAPSULE]),
      hscale_text=["the tube station is fixed on a plain wall (one vertical wall-edge line) at the right-centre: x 44–78%, y 28–58%",
                   "Oshiro stands at the far RIGHT, seen from her side-rear three-quarter, waist-up, cut ONLY by the right edge and by the BOTTOM edge of the picture: her head at about 14% of the image height; her body continues unbroken down to the bottom edge — NO horizontal cut, NO empty white area below her",
                   "her two hands push the capsule into the open round door of the station",
                   "Hakobukun is beyond at the LEFT, about 3 m away, three-quarter view, just setting off with its drawer closed: its top at about 50% and wheels at about 80% of the image height, x 8–38%"],
      camera=["SHOT SIZE: close-up of the hands and the tube station; Hakobukun beyond", "CAMERA POSITION: right beside the wall station", "CAMERA HEIGHT: 130 cm"],
      band="3% and 18%",
      comp=["the bubble 「よーい、どん！」 at the upper centre-right, its tail pointing to Oshiro's head",
            "the drawing fills the WHOLE picture down to the bottom edge; the lower part below the station is Oshiro's body and plain green — never blank white"],
      bubbles=[("Nurse Oshiro", "よーい、どん！", ["よーい、どん！"])],
      tails={"よーい、どん！": "Oshiro's head"},
      sfx=[("シュポン", "beside the station door, at about 58–68% of the image height and 46–76% of the image width")],
      extra=[("EFFECT:", ["speed lines behind Hakobukun as it starts off, and a whoosh line going up from the station. NO letters except the sound effect"])],
      scene="A race with a dummy specimen of the same weight. Oshiro drops the capsule into the tube — shoop! At the same moment, Hakobukun sets off carrying the same thing."),
 dict(chars=["白石", "ハコブ"], mouth="STOPPED", no_yline=True, prop=("RECEIVING STATION", ["a plain open receiving tray of a pneumatic tube station on a counter, with the capsule already lying in it. NO text", CAPSULE]),
      hscale_text=["the receiving tray with the capsule lying in it is LARGE in the near foreground at the bottom-LEFT, x 0–58%, y 70–100%",
                   "Hakobukun has just arrived beyond it at the centre-left, three-quarter view, frozen: its top at about 32% and wheels at about 70% of the image height, x 22–54%",
                   "Shiraishi stands at the right beyond, waist-up, one hand on her forehead: her head at about 22% of the image height, x 58–84%"],
      camera=["SHOT SIZE: the capsule in the foreground; Hakobukun and Shiraishi beyond", "CAMERA POSITION: low, beside the receiving tray", "CAMERA HEIGHT: 90 cm"],
      band="3% and 20%",
      comp=["Hakobukun stopped dead, a small sweat drop beside its screen",
            "Shiraishi wincing, palm pressed to her forehead",
            "two bubbles: 「…先ヲ、越サレマシタ」 at the upper left, tail down to Hakobukun; 「勝負する相手を、間違えてた」 at the upper right, tail down to Shiraishi"],
      bubbles=[("Hakobukun", "…先ヲ、越サレマシタ", ["…先ヲ、", "越サレマシタ"]), ("Engineer Shiraishi", "勝負する相手を、間違えてた", ["勝負する相手を、", "間違えてた"])],
      extra=[("EFFECT:", ["a few drifting dots of silence above Hakobukun. NO letters"])],
      scene="Hakobukun arrives at the lab. The capsule got there long ago and is lying in the tray. Hakobukun admits it was beaten; Shiraishi realises they picked the wrong opponent."),
 dict(chars=["白石", "ハコブ"], mouth="HAPPY", full=["白石"], drawer=True,
      camera=["SHOT SIZE: full-body two-shot", "CAMERA POSITION: front-left, three-quarter", "CAMERA HEIGHT: 150 cm"],
      band="3% and 22%",
      comp=["Hakobukun is centred at about 36% of the image width, three-quarter view, its drawer pulled open with ONE large plain insulated box in it — clearly too big for any tube. NO text on the box",
            "Shiraishi is centred at about 64% of the image width, body turned 30° toward the robot, holding up a plain sheet of paper with two columns of simple pictograms (a capsule icon in one column, a box icon in the other). NO text on the paper",
            "Shiraishi smiling, satisfied",
            "the bubble 「管ニ入ラナイ物ヲ、運ビマス」 at the upper left, its tail down to Hakobukun"],
      bubbles=[("Hakobukun", "管ニ入ラナイ物ヲ、運ビマス", ["管ニ入ラナイ物ヲ、", "運ビマス"])],
      scene="Shiraishi rewrites the list of what to carry, splitting the work between the tube and Hakobukun by type and container. Hakobukun now carries the things that do not fit in the tube."),
]


# ===== 2026-11-08 エイイチ「向きや身長などの大きさ変」→ P2・P3 を向きと大きさで描き直す =====
import os
from importlib.machinery import SourceFileLoader as _L
_F = _L("_face", os.path.expanduser("~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/縦長/_face.py")).load_module().face
PANELS[1]["extra"] = [_F("left", "the corridor it is heading down (camera-left), moving away from the station")] + PANELS[1]["extra"]
PANELS[2] = dict(chars=["白石", "ハコブ"], mouth="STOPPED", full=["白石"], yfeet=80, ytop=22,
      prop=("RECEIVING STATION", ["a plain open receiving tray of a pneumatic tube station on a low counter in the near foreground, with the capsule already lying in it, x 4–60%, y 82–100%. NO text", CAPSULE]),
      camera=["SHOT SIZE: full-body two-shot; the tray with the capsule in the near foreground", "CAMERA POSITION: front-left, beside the tray", "CAMERA HEIGHT: 120 cm"],
      band="3% and 18%",
      comp=["Hakobukun is centred at about 32% of the image width, just arrived, stopped dead, a small sweat drop beside its screen, looking at the capsule in the tray",
            "Shiraishi is centred at about 66% of the image width, standing, wincing, palm pressed to her forehead",
            "two bubbles: 「…先ヲ、越サレマシタ」 at the upper left, tail down to Hakobukun; 「勝負する相手を、間違えてた」 at the upper right, tail down to Shiraishi",
            "the text in both bubbles runs HORIZONTALLY"],
      bubbles=[("Hakobukun", "…先ヲ、越サレマシタ", ["…先ヲ、", "越サレマシタ"]), ("Engineer Shiraishi", "勝負する相手を、間違えてた", ["勝負する相手を、", "間違えてた"])],
      extra=[_F("left", "the capsule in the tray (camera-left, down)")],
      scene="Hakobukun arrives at the lab. The capsule got there long ago and is lying in the tray. Hakobukun admits it was beaten; Shiraishi realises they picked the wrong opponent.")

# 2026-11-08 P2 1回目：大城さんの体が下で水平に切れ、下が白い空き → 腰上で下の端まで描く
# 2026-11-08 バッチの下書き（1コマ2枚）から選んだ。146 P2・P3 は描き直し

# 2026-11-08 3回目：P3 のハコブくんが白石さんとほぼ同じ高さ → 部位で高さを書く
PANELS[2]["comp"].append("SIZE: Hakobukun's top edge reaches only the pit of Shiraishi's stomach. Her chest, shoulders and head are clearly ABOVE the robot. Hakobukun is NOT as tall as her chest or shoulders")


# 2026-11-08 エイイチ「ハコブくんのドロワーの描写がおかしい」→ 体に窓ができてカプセルが見えた。ドロワーは閉じて中は見せない
PANELS[1]["extra"].append(("HAKOBUKUN DRAWER — CLOSED:", ["Hakobukun's drawer is fully CLOSED, exactly as on the reference sheet. Its body has NO window, NO transparent panel, NO opening", "the capsule it carries is hidden inside — NOTHING is visible inside the robot"]))

# 2026-11-08 エイイチ「向きや身長などの大きさ変」のあとに選び直した
PICK = {1: '第147話_P1_20261108_003125b1.jpg', 2: '第147話_P2_20261108_013525.jpg', 3: '第147話_P3_20261108_011239.jpg', 4: '第147話_P4_20261108_003125b1.jpg'}
```


## バッチで5話40枚を頼み、1コマずつ確かめて5コマを描き直した

### 40枚のバッチは、2分半後に見たときには全部返っていた

```
$ python3 vbatch.py submit 143 144 145 146 147 -n 2
第143話 プロンプトを作った
第144話 プロンプトを作った
第145話 プロンプトを作った
第146話 プロンプトを作った
第147話 プロンプトを作った
頼んだ: batches/ij66u4mdf2es9zd5f6gh31f8l3vr44hy9jog 枚数 40 （第143・144・145・146・147話・1コマ2枚）
$ python3 vbatch.py status
20261108_002621 batches/ij66u4mdf2es9zd5f6gh31f8l3vr44hy9jog 第143・144・145・146・147話 40 枚 BATCH_STATE_SUCCEEDED {"requestCount": "40", "successfulRequestCount": "40"}
$ python3 vbatch.py fetch
batches/ij66u4mdf2es9zd5f6gh31f8l3vr44hy9jog 回収 40 枚・画像なし 0 ・入力トークン 141378 ・出力トークン 66839
```

【画像】LOG468_01_ep143_batch_candidates.jpg 第143話の下書き。上下の段が同じコマの2枚の候補。2コマ目の下の段はハコブくんの形が違う

### 2枚の候補から選び、5コマを描き直した

2枚の候補から、形・字・大きさがよいほうを選びました。両方ともだめだったのは次のコマです。

| コマ | 見つけた人 | 何がだめだったか | 直し方 |
|---|---|---|---|
| 145話 3コマ目 | Claude | 「受ケ取リヲ」のリがひらがなの「り」 | 描き直すと受話器が2つになったので、最初の候補（縦書き・字は正しい）を使った |
| 146話 2コマ目 | Claude | 手前に、だれのものでもない大きな脚が出た（2枚とも） | 足を手前に大きく切る構図をやめ、高杉さんの全身の中で足を見せた |
| 146話 3コマ目 | Claude | 手前のハコブくんの頭が別の形／ハコブくんが2台 | 手前の頭なめをやめ、1台だけ全身で描いた |
| 146話 2コマ目（2回目） | 別のエージェント | ハコブくんの目が傾いた困り目になった | 「目は縦長の楕円のまま、口だけで困りを出す」を足した |
| 147話 2コマ目 | 別のエージェント | 大城さんの体が下で水平に切れ、下が白い空き | 人は右と下の端でだけ切れ、体は下の端まで続く、と書いた |

何話もまとめて描いたときは、描いた本人とは別のエージェントに1コマずつ見てもらう決まりです（同じ目で見ると見落とすため）。20枚を見てもらい、2コマが要修正、5コマが軽微でした。この時点で、4コマを並べた確認用の絵をエイイチに見せました。

【画像】LOG468_02_first_check_sheet.jpg 最初にエイイチに見せた第143〜147話。ハコブくんがどのコマでも読者のほうを向き、手前のハコブくんが人より大きく見える


## エイイチが4コマを並べて見つけた、5つのずれ

### ハコブくんの向きと大きさがずれていた（16コマ）

> ハコブくんと患者や看護師の向きや身長などの大きさ変です。修正してください。

原因は2つありました。1つは、ハコブくんがどのコマでも読者のほうを向いていたこと。Gemini は、向きを言わないと顔を読者に向けます。相手と話しているのに、ハコブくんだけがこちらを見ていました。もう1つは、構図に動きを出すため、ハコブくんを手前に大きく入れたコマが多かったこと。手前にあるものは大きく写るので、身長115cm のハコブくんが、181cm の岩田さんの胸や肩まで届いて見えました。

直し方は3つです。

1. 人とハコブくんの関係を見せるコマは、手前と奥に重ねず、同じ床に並べる。並び図どおりの高さの枠を描いた下地を添える（vbuild の yline）
2. 向きは「画面は相手のほうへ約60°・輪のある側面が見える」と、見える面の名前で書く（_face.py）
3. 高さは体の部位でも書く（「岩田さんのベルトの高さ」「大城さんのみぞおちとへその間」）。それでも Gemini は大きめに描くので、候補を測って選ぶ

- ねらい：ハコブくんが話している相手を向き、並び図（ハコブくん115cm、人は152〜181cm）どおりの大きさに見えるようにする
- 変わったこと：描き直した16コマで、ハコブくんの画面が相手の側に来た。測った高さの比は、たとえば143話2コマ目が0.66（並び図では0.64）、144話3コマ目が0.63（0.70）
- 見込める効果：人とロボットの関係（だれに話し、どちらが大きいか）が1コマで読める。次の話から、最初の下書きで向きと大きさがそろうコマが増えるかを、描き直しの枚数で確かめる

【画像】LOG468_03_face_size_redraws.jpg 向きと大きさを直した13コマの2枚ずつの候補。同じ床に並べても、ハコブくんが人の胸まで届く候補や、別の形のロボットが混ざる

候補の大きさは `vbuild.py measure` で測りました。絵の中の人とロボットの塊を見つけ、上端と下端から高さの比を出します。カウンターや吹き出しが人とつながると塊が大きくなり、比がずれるので、そのコマは目でも測りました。

```
$ for a in "143 2" "145 1" "145 2" "145 4" "146 2" "146 4" "144 1" "144 3" "143 4"; do set -- $a; python3 vbuild.py $1 measure $2 8 2>&1 | grep "_01" ; done
第143話_P2_20261108_010101.jpg [(31, 41, 90, 49), (66, 18, 90, 72)] 高さの比 0.68
第143話_P2_20261108_010115.jpg [(29, 28, 93, 65), (75, 19, 95, 76)] 高さの比 0.86
第143話_P2_20261108_010924.jpg [(31, 41, 94, 53), (70, 15, 95, 80)] 高さの比 0.66
第143話_P2_20261108_010929.jpg [(29, 37, 95, 57), (71, 11, 96, 86)] 高さの比 0.66
第145話_P1_20261108_010727.jpg [(45, 19, 99, 79)] 
第145話_P1_20261108_010729.jpg [(44, 5, 99, 94)] 
第145話_P1_20261108_010853.jpg [(28, 31, 88, 57), (79, 17, 100, 83)] 高さの比 0.69
第145話_P1_20261108_010854.jpg [(26, 43, 95, 52), (75, 19, 100, 81)] 高さの比 0.64
第145話_P2_20261108_010722.jpg [(31, 39, 95, 56), (76, 19, 95, 76)] 高さの比 0.74
第145話_P2_20261108_010726.jpg [(29, 34, 96, 62), (69, 20, 97, 77)] 高さの比 0.81
第145話_P2_20261108_010853.jpg [(27, 34, 97, 63), (59, 15, 97, 82)] 高さの比 0.77
第145話_P2_20261108_010901.jpg [(29, 36, 97, 61), (78, 2, 97, 95)] 高さの比 0.64
第145話_P4_20261108_010723.jpg [(46, 13, 97, 83)] 
第145話_P4_20261108_010738.jpg [(50, 15, 91, 75)] 
第145話_P4_20261108_010858.jpg [(46, 11, 95, 85)] 
第145話_P4_20261108_010907.jpg [(55, 9, 97, 88)] 
第146話_P2_20261108_010629.jpg [(52, 22, 88, 66)] 
第146話_P2_20261108_010637.jpg [(51, 19, 86, 68)] 
第146話_P2_20261108_010944.jpg [(52, 14, 95, 81)] 
第146話_P2_20261108_010946.jpg [(52, 17, 96, 79)] 
第146話_P4_20261108_010631.jpg [(54, 17, 93, 76)] 
第146話_P4_20261108_010645.jpg [(56, 11, 89, 78)] 
第146話_P4_20261108_010943.jpg [(53, 20, 91, 71)] 
第146話_P4_20261108_010945.jpg [(53, 14, 96, 82)] 
第144話_P1_20261108_010238.jpg [(17, 45, 84, 39), (66, 22, 85, 63)] 高さの比 0.62
第144話_P1_20261108_010311.jpg [(48, 18, 95, 77)] 
第144話_P3_20261108_010310.jpg [(52, 25, 92, 67)] 
第144話_P3_20261108_010312.jpg [(20, 43, 82, 39), (66, 26, 88, 62)] 高さの比 0.63
第143話_P4_20261108_010103.jpg [(29, 38, 92, 54), (75, 10, 93, 83)] 高さの比 0.65
第143話_P4_20261108_010125.jpg [(31, 42, 96, 54), (74, 11, 96, 85)] 高さの比 0.64
```

塊が1つしか出ていない行は、人とハコブくんが重なって1つの塊になったコマです。145話1・2コマ目の塊は、カウンターや吹き出しとつながって下端・上端が100や2になっているので、目で測った比（0.67と0.74）で選びました。並べて見直したとき、143話3コマ目・144話4コマ目・147話3コマ目は、手前に入れたハコブくんがまだ大きく見えたので、同じ床で向き合う構図に替えて、もう一度描きました。

### 第144話の1コマ目と2コマ目がつながっていなかった

> 144話の1コマ目がこれだと2コマ目と繋がりがおかしい

大きさを直すために1コマ目を同じ床に並べたところ、ハコブくんが車いすの目の前に来ていました。2コマ目では、ハコブくんは配膳車の向こうから来ます。1コマ目を、廊下の奥・配膳車のさらに向こうに小さく見える形に描き直しました。

### 第147話の2コマ目でドロワーに窓ができていた

> 147コマ目のハコブくんのドロワーの描写がおかしい

検体を運ぶハコブくんの体に窓ができ、中のカプセルが見えていました。「ドロワーは閉じる・体に窓や透明な所はない・中は見えない」を足して描き直しました。

### 第144話の配膳車が左右逆で、2人が横を向いていた

> 1コマ目の台車の置き場所が左右逆（大きさもおかしい）
> 大城さんと車椅子の向きが変

1コマ目を前から撮っていたため、2コマ目（大城さんの後ろから撮った絵）と配膳車の左右が逆になり、大城さんと車いすは廊下の奥ではなく横を向いていました。2コマ目と同じく2人の後ろから撮り、配膳車は右の壁ぎわ、2人は奥へ進む、と書きました。

### 第144話は全体の流れと、2コマ目の狭さがおかしかった

> 全体的に流れが変じゃない？
> いや2コマ目も変だよ。通れそうに見えるのに、通れるかな（もっと圧迫感を表現しないと）
> 1コマ目ももう少し描き方修正して、通路は広そうに見えてるよ

並べて読み直すと、3コマ目ではハコブくんが2人と同じ左向きに走り去り、前を横切っていませんでした。配膳車も3・4コマ目で消え、どこで先を譲ったのかが分かりません。2コマ目は廊下が広く見え、「通れるかな…」と言うほど狭くありませんでした。

直し方は、舞台と向きを4コマで固定することです。

| コマ | カメラ | 2人の向き | ハコブくん | 配膳車 |
|---|---|---|---|---|
| 1 | 2人の後ろ | 廊下の奥へ | 奥のすき間から来る（小さく） | 右の壁ぎわ、2人のすぐ先 |
| 2 | 大城さんの肩ごし | 奥へ（止まって確かめる） | すき間に入ってくる | 右の壁ぎわ、廊下の大半をふさぐ |
| 3 | 真横 | 画面の左へ | 左から右へすり抜ける | 奥の壁ぎわの真ん中 |
| 4 | 3コマ目と同じ真横 | 画面の左へ（先に通る） | 左で止まって待つ | 奥の壁ぎわの真ん中 |

2コマ目は、両側の壁を近くに描き、配膳車で幅の大半をふさいで、すき間は車いすがぎりぎりにしました。1コマ目も同じ狭い廊下にしました。

【画像】LOG468_04_ep144_flow_broken.jpg 直す前の第144話。3コマ目でハコブくんが2人と同じ向きに走り、3・4コマ目に配膳車がない

【画像】LOG468_05_ep144_fixed.jpg 直したあとの第144話。配膳車が4コマとも同じ場所にあり、2コマ目のすき間は車いすがぎりぎり

【図解】LOG468_redraw_reasons.html


## 4コマを通して読む確認を、絵を描く手順に足した

### チェック項目と過去の間違い.md に足した手順（チェック①の2）

1コマずつの確認（チェック①）と、エイイチの確認（チェック②）の間に、Claude が4コマを並べて話として読む段を足しました。

- ねらい：エイイチがチェック②で見つけていたずれ（向き・大きさ・舞台の物の位置・流れ）を、見せる前に Claude が見つける
- 変わったこと：4コマを並べて、舞台・位置と向き・セリフと絵・大きさ・描き直したコマの前後・細部の6つを順に見る。チェック項目にも4つ足した
- 見込める効果：エイイチの指摘と描き直しの枚数が減る（見込み）。次の話で、指摘の数と描き直しの枚数を今回（指摘5回・描き直し66枚）と比べて確かめる

#### チェック項目と過去の間違い.md（4コマを通して読む確認を足したあとの全文）

```markdown
# 縦長4コマ・マンガショートの チェック項目と過去の間違い

このファイルは、4コママンガを描くとき・ショートにするときに**毎回最初に読む**。
間違いが見つかったら、下の「過去の間違い」に1行足し、直し方がプロンプトの決まりになったら「チェック項目」にも足す。
置き場所: iCloud `ハコブくん/4コママンガ作成データ/指示/チェック項目と過去の間違い.md`（正）。vbuild.py の `check` がこのファイルを読んで確認の手順を出す。

---

## 進め方（チェックの時間を必ず入れる）

1. **描く** — `python3 vbuild.py NN prompts && python3 vbuild.py NN gen && python3 vbuild.py NN finish`
   - 何話もまとめて描くときは、下書きをバッチで頼む（半額・数分〜最大24時間）: `python3 vbatch.py submit 138 139 … -n 2` → `python3 vbatch.py status` → `python3 vbatch.py fetch`。選んだ絵は spec の PICK に書き、直しはふつうの `gen コマ -n 1`（2026-11-05）
2. **チェック①（Claude・1枚ずつ）** — `python3 vbuild.py NN measure コマ` で大きさの比を測り、`python3 vbuild.py NN check` で1コマずつの確認用の絵を作り、下の「チェック項目」をすべて見る。まとめて何話も描いたときは、描いた本人とは別のエージェントに1コマずつ見てもらう（同じ目で見ると見落とす）
2.5. **チェック①の2（4コマを通して・2026-11-08）** — 1コマずつ見たあと、4コマを並べて話として読む。次の順で確かめる
   - 舞台：狭い所・配膳車・扉など話の要になる物が、毎コマ同じ場所（左右も）にあり、途中で消えていない
   - 位置と向き：誰がどこにいて、どちらへ進むかがコマをまたいでつながる。カメラを反対側に回して左右を入れ替えていない
   - セリフと絵：セリフが言う状況（狭い・急いでいる・電話中など）が絵で見える。「通れるかな」なら、すき間がぎりぎりに見える
   - 大きさと向き：ハコブくんは相手を向き、手前に大きく入れて人より大きく見せていない
   - 描き直したコマは、前後のコマと並べてもう一度読む
   - 描写の細部：ドロワー・名札・持ち物・服が場面に合っている
3. **直す** — 要修正のコマは spec を直して描き直す。描き直したコマは、もう一度チェック①を通す
4. **チェック②（エイイチ）** — 全部のコマを並べた確認用の絵を見せて、OK をもらう。**ここで止まる**。OK の前に声・動画を作らない
5. **ショートにする** — 声 → 冒頭の動き（話ごとに違う動き）→ 書き出し
6. **チェック③（Claude）** — 書き出した動画のコマを並べて、字幕・吹き出しの切れ・はみ出し・30秒以内を見てから渡す

---

## チェック項目（チェック①で1コマずつ全部見る）

### A. 体（いちばん見落としやすい）
- [ ] 人は**腕2本・手2つ・脚2本**。数える。余分な腕・誰のものでもない手・胴が2つ・同じ人が2人、がない
- [ ] 奥にいる人が、机や台の後ろで**上半身だけになっていない**（脚まであるか）
- [ ] 全身が入るコマで、**脚が切れていない**（下の端で切れて白い帯になっていない）。頭が大きすぎない（8頭身）
- [ ] 松葉杖・バッグ・道具は、その人の手で持っている。杖の本数が合っている
- [ ] 座っている人・しゃがんでいる人が、立っている人やロボットより**大きく描かれていない**

### B. 大きさ（並び図 `キャラ/並び図_主要キャラ.png` が正）
- [ ] ハコブくん 115cm・ピカリくん 150cm・高杉 155・寺島 160・白石 160・アバターさん 160・大城 165・小芝 167・槙原 172・中村拓也 175・田中 180・岩田 181・森田茂 163・森田久美子 152・林 158・中村千夏 164
- [ ] 同じ床に立つとき、ハコブくんの上端は大城さんのみぞおち（約70%）。胸や肩・あごまで来ていたら大きすぎ、腰までなら小さすぎ
- [ ] ピカリくんはハコブくんの約1.3倍（ハコブくんの上端＝ピカリくんの肩）。大人の女性と並ぶと頭頂が鼻〜目の高さ
- [ ] **吹き出しや小窓を入れるために、キャラの大きさを変えていない**（話ごとにピカリくんの大きさがバラバラになった原因）
- [ ] 同じ話の4コマで、同じキャラの大きさがそろっている
- [ ] **ハコブくんが話している相手を向いている。** 読者のほうを向いたままになっていない（画面が相手の側にあり、輪のある側面が見える）（2026-11-08）
- [ ] 人とハコブくんの関係を見せるコマで、ハコブくんを手前に大きく入れていない。手前に入れると人より大きく見える（2026-11-08）
- [ ] **測って確かめる**: `python3 vbuild.py NN measure コマ番号 [本数]` で、床に立つ人・ロボットの高さの比を出す。並び図の比（例 ピカリくん150÷千夏164＝0.91、ハコブくん115÷大城165＝0.70）から ±0.08 を超えたら描き直すか別の候補を選ぶ。目で見るだけで決めない

#### 大きさが並び図と変わる原因（2026-10-31 に特定。いつも起きる前提で作る）
1. **絵のモデルは数字の大きさを守らない。** 「150cm」「1.3倍」は文字として読むだけで、実際は「コマを埋める・構図が収まる」ように大きさを決める。主役は大きく、ロボットや脇役は余った場所（吹き出しの帯・小窓の横）に入る大きさに縮む
2. **参照の絵（キャラ表）は、どれも画像いっぱいに描かれている。** 1人ずつの絵には「ほかのキャラと比べた大きさ」が入っていない。並び図は何枚もある参照の1枚にすぎず、重みが弱い
3. **背景が緑一色で床の線がない。** 奥行きの手がかりがないので、少し奥・手前に置かれただけで見た目の大きさが変わる（遠近で縮んだのか、設定より小さいのか区別できない）
4. **座る・しゃがむ人の頭を、立っている人の目の高さに描く。** 座った人が立ったロボットより大きくなる
5. **spec に大きさの指示が何本も積み重なり、食い違っていた。** 古い hscale/pscale の文と新しい yline、「slim」の一語など。食い違うとモデルは構図のほうを取る
6. **「ひざ上」「腰上」の指定を無視して全身で描く。** 全身を入れるために全体を縮め、2人の縮み方がそろわない

#### だから作り方で決めること
- 大きさの指示は1か所だけ（yline の頭頂・足もとの位置＋薄緑の枠の下地）。spec に別の大きさの文を足さない。古い文は消す
- 並ぶ2人は**カメラから同じ距離・同じ床**に立たせる。片方を奥・手前にする構図は、大きさを比べるコマでは使わない
- 立つ人と座る人を並べて比べない。座らせるなら、ひざをつく・腰上で切るなど、比べなくてよい構図にする
- 吹き出し・小窓の場所は、キャラの立つ場所と重ならない上の帯にとる。キャラの横に空き地を作らない
- 1コマにつき候補は最初2枚（`gen` の既定）、描き直しは1枚ずつ（`gen コマ -n 1`）。`measure` で比を出して近いほうを選び、どちらもだめなら spec を直して1枚ずつ描き足す。同じ指示のまま枚数を増やさない（2026-11-05 費用を抑えるため、3〜5枚から変えた）

### C. ロボットの形
- [ ] ハコブくん: **腕・手・脚がない**。体に文字がない。顔は白い縦長の楕円の目2つと小さい口（目は形を変えない・ウインクなし）。側面の輪は**平らで真ん中に穴・点・同心円がない**。画面の頭が胴の上に乗る（低い箱・電子レンジ形にならない）
- [ ] ピカリくん: **ずんぐり**（頭が大きい・胴が幅広・脚が短く太い）。細身・小さい・子どもサイズにしない。黒いガラスに文字なし・白い縁なし
- [ ] ハコブくんの寄り（画面のアップ）でも、画面は上の丸い塔に埋め込まれた形のまま。テレビのような別の箱・太い枠・首にしない
- [ ] ハコブくんとピカリくんの体が**混ざっていない**（ハコブくんに人型の腕や脚が生える、2体がくっつく）
- [ ] ツカムくん: 作業台の上。顔は台座の正面（白い丸目・口なし）
- [ ] **後ろ向きのハコブくんは、毎回 `キャラ/hakobu_back.png` と見比べる**: 上は白い丸いドームで下の縁の真ん中に小さな切り欠き1つ（黒い網・格子なし）／その下に横長の黒い操作箱（丸ボタン・四角・スイッチの3つ）／大きな四角い扉（上に小さなつまみ・右の縁に縦の取っ手）／下の黒い面に横長の光る帯1本と、その下に左右2つの通気の溝。後ろ向きのコマは spec に `back=True` を書く（hakobu_back.png を添え、HAKOBU_BACK の節が入る）

### D. キャラの見分け
- [ ] 大城=お団子／高杉=あご丈ボブ・眼鏡なし／寺島=丸眼鏡／白石=眼鏡・ポニーテール・エンジニアの上着／槙原=黒縁の眼鏡
- [ ] 同じ話の中で服が変わっていない（夜の寝間着が次のコマで私服になる、など）
- [ ] 名札は白紙（字が崩れて出る）
- [ ] アバターさんの顔は見えない

### E. 文字
- [ ] 吹き出しの字を**1字ずつ**台本と見比べる。抜け・重複・カタカナがひらがなに変わる（リ→り、ッ→っ）
- [ ] 台本にない『』「」が付いていない。台本にない吹き出し・意味のない字の吹き出し・空の吹き出し・英字（GROPING など）がない
- [ ] 吹き出しのしっぽが話す人を向いている。吹き出しが切れていない
- [ ] 縦書きは軽微（できれば横書き）

### F. 話と絵
- [ ] プロットの場面になっている（誰が・何をしている・小道具）。めくる前のコマでオチを見せていない
- [ ] 4コマを通して、舞台の物（狭い所を作る配膳車など）が消えずに同じ場所にあり、人とロボットの進む向きがつながっている。セリフが言う状況が絵で見える（2026-11-08）
- [ ] 1コマだけ描き直したら、前後のコマと位置（誰がどこにいるか・距離）がつながっているかを並べて見る（2026-11-08）
- [ ] ハコブくんのドロワーは、開けない場面では閉じていて、体に窓や透明な所がない（2026-11-08）
- [ ] 小道具が変でない（頭の上に扉が浮く、など。扉は床から立つ）
- [ ] **何コマにも出る小道具（配膳車・ワゴン・床の線など）は、同じ話の中で形と色がそろっている。** 形がぶれる物は三面図を作って `キャラ/` に置き、spec の `extra_refs` で毎コマ添える（例 `キャラ/配膳車.jpg`）
- [ ] 床の線・マークは黒で統一し、数を絞る（1つのレーンにマーク1つ）。白抜きや、マークがずらっと並ぶ絵にしない
- [ ] ガイドの白い枠や名前の札が絵に写り込んでいない

### H. コマの動き（4コマを並べて見る・2026-11-05）
- [ ] 4コマが同じ大きさ・同じ高さ・横並びの棒立ちになっていない。隣り合うコマでショットサイズとカメラの位置を変える（`指示/カメラワーク規定_縦長版.md`）
- [ ] 1話の中に、大胆な寄り（顔・手元・画面）か、手前に人やロボットを大きく切って入れる重ね（肩なめ・前景）が1コマ以上ある
- [ ] カメラの高さを変えている（人の目線150cm だけで通さない。ハコブくん目線90〜100cm・低い60cm など）
- [ ] 動き・感情の山のコマに、意図のある効果線（スピード線・集中線・縦線）が入っている
- [ ] 大きさを比べないコマ（手前と奥の重ね）は `no_yline=True` にして、hscale_text で画像の上の位置を書く。並び図の比は、同じ床・同じ距離に立つコマで守る

### G. ショート（書き出したあと）
- [ ] 冒頭の動くハコブくんは話ごとに違う動き（同じ動きを並べない）
- [ ] 声の読み・抑揚が変でない（怪しい語は --try で聞き比べる）
- [ ] 30秒以内・字幕は1行14字・2行まで
- [ ] 冒頭（最初の3秒）から音が出ている。効果音は spec のフォルダからの相対の `se/…` で読むので、作業環境では各話のフォルダに `se` のリンクを置く（ないと音なしで書き出されても止まらない）。書き出したら ffmpeg の volumedetect で冒頭の音量を測る

---

## 過去の間違い（新しいものを上に足す）

| 日付 | 話・コマ | 間違い | 見つけた人 | 直し方（プロンプト・作り方） |
|---|---|---|---|---|
| 2026-11-08 | 第144話 全体 | 話の流れが変：3コマ目でハコブくんが2人と同じ向きに走り去り、配膳車（狭い所）が3・4コマ目で消えた。2コマ目は広く見えて「通れるかな」に合わない | エイイチ | 4コマを通して、狭い所などの舞台の物を毎コマ同じ場所に置く。進む向き（誰がどちらへ）をコマごとに書く。セリフが言う状況（狭い・圧迫感）を絵で見せる：壁を近く・物で幅をふさぐ・すき間は車いすぎりぎり |
| 2026-11-08 | 第144話 P1（2回目） | 配膳車が2コマ目と左右逆で大きさも変。大城さんと車いすが廊下の奥ではなく横を向いていた | エイイチ | 続くコマと同じ側（2人の後ろ）から撮り、配膳車は2コマ目と同じ右の壁ぎわ、2人は奥へ向かう、と書いた。前後のコマで、物の左右・人の進む向きをそろえる（カメラを反対側に回さない） |
| 2026-11-08 | 第144話 P1 | 大きさを直すために同じ床に並べたら、2コマ目では配膳車の向こうから来るハコブくんが、1コマ目で目の前にいて話がつながらない | エイイチ | コマごとに直すときも、前後のコマとの位置のつながりを見る。遠くにいる場面は、廊下の線と配膳車で奥行きを見せて小さく描く |
| 2026-11-08 | 第147話 P2 | ハコブくんの体に窓ができ、中のカプセルが見えた | エイイチ | 「ドロワーは閉じる・体に窓や透明な所はない・中は見えない」を書く |
| 2026-11-08 | 第143〜147話（13コマ） | ハコブくんがどのコマでも読者のほうを向き、相手（患者・看護師）を向いていない。手前に大きく入れたハコブくんが人の胸や肩まで届いて見え、大きさが変 | エイイチ | 人とハコブくんが出るコマは、手前と奥に重ねず同じ床で並べる（full と yline）。向きは「画面は相手のほうへ約60°・輪のある側面が見える」と書く（縦長/_face.py の face）。高さは部位で書き足す（例 大城さんのみぞおちとへその間）。それでも大きく描かれるので measure と目で比を確かめて選ぶ |
| 2026-11-08 | 第146話 P2（1回目・2枚とも） | 足で小突く場面で、手前に誰のものでもない大きな脚が出た | Claude | 足を手前に大きく切る構図をやめ、本人の全身の中で足を見せ「本人の左脚・ほかの脚なし」と書いた |
| 2026-11-08 | 第146話 P3（1回目） | 手前に頭を切って入れたハコブくんの頭が別の形／ハコブくんが2台 | Claude | 手前の頭なめをやめ、1台だけ全身で描いた |
| 2026-11-08 | 第146話 P2（2回目） | 困った場面で、ハコブくんの目が傾いた困り目になった | 別のエージェント | 場面の英文に「目は縦長の楕円のまま・口だけで困りを出す」を足した |
| 2026-11-08 | 第147話 P2 | 手元の大写しで、大城さんの体が下で水平に切れ、下が白い空き | 別のエージェント | 人は右と下の端でだけ切れ、体は下の端まで続く、と書いた |
| 2026-11-08 | 第145話 P3 | 描き直しで「受ケ取リヲ」のリがひらがなに、受話器が2つ | Claude | 1回目の候補（縦書き・字は正しい）を選んだ |
| 2026-11-05 | 第141話 ショート P4 | 高杉さん「それでいいの」が疑問形に聞こえた（納得の場面なのに語尾が上がる） | エイイチ | 納得・言い切りのセリフは、語尾を下げる読みにする。「。」で止め、前に「そうそう、」などのうなずきを足すと下がった。字幕も声の言葉に合わせる |
| 2026-11-05 | 第141話 P3（バッチの下書き） | ハコブくんが大きすぎ（高杉さんとの高さの比 約0.87。目標0.74） | エイイチ | 人とハコブくんが同じ床・同じ距離に立つコマを no_yline＋hscale_text で書いていた。full と yline（並び図の比）に戻し、大きさの文は1か所だけにして描き直した（比 約0.81）。spec を書くとき、並んで立つコマは no_yline にしない |
| 2026-11-05 | 第141話 P3（描き直し1回目） | 大きさは直ったが、高杉さんが横を通れそうで、ハコブくんが邪魔に見えない | エイイチ | 大きさを合わせるために2人を横に並べたのが原因。ふさぐ話では、ロボットを戸口の幅いっぱいに置き、人はその真後ろ（半歩奥）に立たせる。大きさは『上の端が人のみぞおち』と書いて合わせる |
| 2026-11-05 | 第134〜137話のショート | 冒頭の効果音が鳴らず、最初の数秒が無音 | エイイチ | 作り直した作業環境で、話のフォルダに se のリンクがなく効果音が読めていなかった。リンクを置き、冒頭0秒にも走る音を足して作り直した |
| 2026-11-05 | 第135話（1回目） | 配膳車の形がコマごとに違う／1コマ目のハコブくんの頭が別の箱のよう／構成が単調 | エイイチ | 配膳車の三面図（キャラ/配膳車.jpg）を作って毎コマ添えた。vbuild に HAKOBUKUN HEAD SHAPE の節を足した。顔の大写し・傾けた構図・床から見上げる・手前の取っ手と湯気、で組み直した |
| 2026-11-05 | 第136話 P1・P2・P4（1回目） | 床のマークが黒と白でばらばら・並びすぎ | エイイチ | 線とマークは黒一色、1つのレーンにマーク1つ、と spec の LANES に書いた |
| 2026-11-05 | 第134話 全コマ（1回目） | コマに動きがなく単調（4コマとも中くらいの引き・横並び・人の目線の高さ） | エイイチ | カメラワーク規定どおり、コマごとに寄り・引き・高さ・向きを変え、前景の重ねと効果線を入れて組み直した。チェック項目 H を足した |
| 2026-11-05 | 第134話 P2（1回目） | 後ろ向きのハコブくんの背中が三面図と違う（ドームに黒い格子など）。いつも違う形で描かれていた | エイイチ | hakobu_back.png を添える `back=True` と、背中の形を書いた HAKOBU_BACK の節を vbuild に足した |
| 2026-11-05 | 第134話 P2 | 後ろから見た高杉さんの袖に名札が付いた | Claude | 仕上げで近くの網点を写して消す `CLONE` を vbuild に足した |
| 2026-10-31 | 第131話 P3 | 眼鏡を足すために描き直したら、大城さんの向き（横向き→正面）まで変わった | エイイチ（公開後） | 小物や顔の一部だけ直すときは描き直さない。元の絵を Gemini の edit に渡し「ここだけ変える・ほかは同じ」で直す |
| 2026-10-31 | 第130話 P3 | 奥の白石さんがテーブルの後ろで上半身だけになり、下半身がない | エイイチ（公開後） | 奥の人は「テーブルの横に全身で立つ」と書く。人をテーブルの真後ろに置かない |
| 2026-10-31 | 第131話 P3 | 槙原くんの眼鏡がない | エイイチ（公開後） | vbuild の人物表に「黒縁の眼鏡をいつもかけている」を足した。眼鏡のキャラは毎コマ眼鏡を確かめる |
| 2026-10-31 | 第101話 P3 | 大城さんの頭身が変（頭が小さく体が長い） | エイイチ（公開後） | 「大人の女性・約7.5頭身・頭を小さくしない」を足して描き直し |
| 2026-10-31 | 第117話 P4 | ピカリくんが千夏さんより小さく描かれ続けた（5枚中0枚→再度5枚） | 別のエージェント | 原因の1〜6を特定。`measure` で比を出して選ぶ（0.83 を採用。目標0.91）・字の抜け（「向いていよ」）も同時に見る |
| 2026-10-31 | 第93話 | 声「出した方がいいと思う？」の抑揚が変 | エイイチ | 読みを --try で聞き比べて差し替える |
| 2026-10-31 | 第101話 P4 | 大城さん・小芝先生の脚が切れて下が白い帯、頭身が変 | エイイチ | 全身で描く（full と CAMERA OVERRIDE）・下の端で切らない |
| 2026-10-31 | 第109・111・114・115・117・118話 | ピカリくんの大きさがコマごとにバラバラ（吹き出し・小窓に合わせて縮んだ） | エイイチ | 並び図の身長から画像上の頭頂・足もとの位置を決める（vbuild の yline）＋枠を描いた下地を添える |
| 2026-10-31 | 第116話 P1・P4 | ハコブくんに人型の腕・脚が生える、2体が混ざる | エイイチ | 「2体は別の機械・間に隙間・ハコブくんに腕脚なし」の独立セクション（SEP） |
| 2026-10-31 | 第121話 P3 | 高杉さんの腕が3本 | エイイチ | ANATOMY CHECK（腕2本・数える）を人が出る全コマに |
| 2026-10-31 | 第121話 P4 | ハコブくんの形が違う（細長い） | エイイチ | 三面図どおり・全身で描く |
| 2026-10-31 | 第125話 P2 | 扉がハコブくんの頭の上に浮いた板になった | エイイチ | 扉は床から立つ・左の端に蝶番・閉まる途中でハコブくんの頭の角に当たる、と構造で書く |
| 2026-10-31 | 第127話 P4・第94話・第99話ほか | ハコブくんが大きすぎ／小さすぎ（話ごとにバラバラ） | エイイチ | 並び図どおり（yline）。人とハコブくんが並ぶコマは全身で描く |
| 2026-10-31 | 第76話 P4 | 岩田さんの体が崩れる（胴が2つ・松葉杖3本） | エイイチ | 「1人を1回だけ・松葉杖2本・脇の下」の独立セクション |
| 2026-10-31 | 第131話 P3 | 大城さんの腕が3本 | エイイチ | ANATOMY CHECK |
| 2026-10-31 | 第114話 P2 | 絵の中に英字「GROPING」 | 別のエージェント | 場面の英文に動詞をそのまま書かない・「文字なし」 |
| 2026-10-31 | 第130話 P2・第131話 P3 | 同じ吹き出しが2つ・意味のない字の吹き出し | 別のエージェント | 「吹き出しはこの1つだけ」 |
| 2026-10-31 | 第121話 P4・第132話 P1 | 台本にない『』が付く | 別のエージェント | プロンプトに『』の禁止を書くと逆に付くことがある。付いたら描き直して選ぶ |
| 2026-10-30 | 30話すべて | ハコブくんの側面の輪の真ん中に穴 | エイイチ | HAKOBUKUN SIDE RING — FLAT, NO HOLE（毎コマ）。描いた絵は edit で直せる（字や目が変わるので見比べる） |
| 2026-10-30 | ピカリくんの9話 | ピカリくんが細く小さい | エイイチ | 指示の「slim」を削除。PIKARIKUN BUILD — STOUT（頭は全高の1/4・胴幅広・脚短く太い） |
| 2026-10-30 | 30本すべて | 冒頭の動くハコブくんが全部同じ動き | エイイチ | video_rules 9-0b「話ごとに違う動き」。op30.py で作りためた動きを1話1つ |
| 2026-10-30 | 30本 | マンガを確かめずに動画まで作った | エイイチ | チェック②（エイイチの OK）の前に声・動画を作らない |
```


## YouTube ショート5本を作った

### 冒頭の動き（motion143x.py）

ショートは、縦長のコマを順に見せ、声と効果音を付けた30秒以内の動画です。冒頭の約3.6秒は、話ごとに違う動きをするハコブくんのアニメにしています（同じ動きを並べない決まり）。5つとも新しく作りました。

| 話 | 冒頭の動き |
|---|---|
| 143 | 突っ込んできてピタッと急停止、画面に「止マレマス」 |
| 144 | 通り過ぎかけてバックで戻り、「ドウゾ」とおじぎ |
| 145 | 大声でぴょんぴょん「オ薬デス！」のあと、縮こまって「…」 |
| 146 | 待っているところを小突かれて×目、「衝撃検知」と出て脇へ移る |
| 147 | 力をためて猛ダッシュ、戻ってきて「先ヲ越サレ…」 |

```python
"""第143〜147話の冒頭の動き（2026-11-08）。話ごとに違う動き（138〜142・134 とも重ねない）
143 右からまっすぐ突っ込んできて、ピタッと急停止（前のめりに揺れる）→「止マレ／マス」→ 得意げ
144 右から来て、左へスーッと通り過ぎかけ → 気づいてバックで戻る → 小さくおじぎ「ドウゾ」
145 来て止まり、大声でぴょんぴょん「オ薬／デス！」→ 縮こまって小さくなり、目が点で静かに待つ「…」
146 待っているところを左から小突かれ（ガクン・画面がゆれる）→ ×目 →「衝撃／検知」→ 右の空いた所へ移る
147 しゃがんで力をためる → 左へ猛ダッシュ（スピード線）→ 戻ってきて、しょんぼり「先ヲ／越サレ…」"""
import sys
import numpy as np
import motion30
import hakobu_motion as M
from hakobu_motion import T, ease, _approach

class Stop143:
    dur = 3.6; empty = True
    HIT = 0.7
    def motion(self, t):
        if t < self.HIT: return dict(dx=360 * (1 - t / self.HIT), dy=-6 * abs(np.sin(2 * np.pi * 5 * t)), rot=-3)
        v = t - self.HIT                               # ピタッ：前のめりに揺れて止まる
        k = np.exp(-v * 8) * np.cos(2 * np.pi * 4 * v)
        return dict(rot=-7 * k, dx=-6 * k)
    def face(self, t):
        if t < self.HIT: return ("open", "open"), None, "mid"
        if t < 1.5: return ("big", "big"), None, "o"
        if t < 2.6: return T("止マレ", "マス")
        return ("happy", "happy"), None, "closed"
    def ring(self, t): return 1.0 if self.HIT <= t < 1.0 else 0.6
    def blur(self, t): return t < self.HIT
    def dim(self, t): return 1.0
    def cam(self, t):
        v = t - self.HIT
        return (int(8 * np.exp(-v * 10) * np.sin(2 * np.pi * 18 * v)), 0) if 0 <= v < 0.35 else (0, 0)

class Back144:
    dur = 3.6; empty = True
    def motion(self, t):
        if t < 1.1: return dict(dx=260 - 520 * ease(t / 1.1), dy=-4 * abs(np.sin(2 * np.pi * 4 * t)))   # 左へ通り過ぎかける
        if t < 1.5: return dict(dx=-260, rot=2 * np.sin(2 * np.pi * 6 * (t - 1.1)))                       # 気づく
        if t < 2.4:                                                                                         # バックで戻る（小刻み）
            v = (t - 1.5) / 0.9
            return dict(dx=-260 + 230 * ease(v), dy=-2 * abs(np.sin(2 * np.pi * 6 * t)))
        v = t - 2.4                                                                                         # おじぎ
        nod = 0.05 * max(0.0, np.sin(np.pi * min(1, v / 0.6)))
        return dict(dx=-30, sy=1 - nod, dy=6 * nod / 0.05)
    def face(self, t):
        if t < 1.1: return ("open", "open"), None, "mid"
        if t < 1.5: return ("big", "big"), None, "o"
        if t < 2.4: return ("down", "down"), "trouble", "wave" if int(t * 8) % 2 else "wave2"
        return T("ドウゾ")
    def ring(self, t): return 0.6
    def blur(self, t): return t < 1.1
    def dim(self, t): return 1.0
    def cam(self, t): return (0, 0)

class Hush145:
    dur = 3.6; empty = True
    def motion(self, t):
        if t < 0.7: return _approach(t, 0.7, 220)
        if t < 2.0:                                    # 大声でぴょんぴょん
            v = t - 0.7
            return dict(dy=-26 * abs(np.sin(2 * np.pi * 2.2 * v)), sy=1 + 0.03 * np.sin(2 * np.pi * 4.4 * v))
        v = min(1, (t - 2.0) / 0.4)                    # 縮こまって静かに待つ
        return dict(sx=1 - 0.06 * ease(v), sy=1 - 0.1 * ease(v), dy=12 * ease(v))
    def face(self, t):
        if t < 0.7: return ("open", "open"), None, "mid"
        if t < 2.0: return T("オ薬", "デス！")
        if t < 2.6: return ("down", "down"), "trouble", "frown"
        return T("…")
    def ring(self, t): return 1.0 if 0.7 <= t < 2.0 else 0.4
    def blur(self, t): return t < 0.7
    def dim(self, t): return 1.0
    def cam(self, t):
        return (int(4 * np.sin(2 * np.pi * 15 * t)), 0) if 0.7 <= t < 2.0 else (0, 0)

class Poke146:
    dur = 3.6; empty = True
    HIT = 0.9
    def motion(self, t):
        if t < self.HIT: return dict(dy=-2 * abs(np.sin(2 * np.pi * 0.8 * t)))           # じっと待つ
        v = t - self.HIT
        if v < 0.8:                                                                       # 左から小突かれてガクン
            k = np.exp(-v * 7) * np.cos(2 * np.pi * 4 * v)
            return dict(dx=30 * (1 - np.exp(-v * 12)), rot=8 * k)
        if t < 2.7: return dict(dx=30)
        w = min(1, (t - 2.7) / 0.6)                                                       # 右の空いた所へ移る
        return dict(dx=30 + 220 * ease(w), dy=-3 * abs(np.sin(2 * np.pi * 4 * t)))
    def face(self, t):
        if t < self.HIT: return T("受ケ取リ", "待チ")
        if t < self.HIT + 0.8: return ("x", "x"), None, "o"
        if t < 2.7: return T("衝撃", "検知")
        return ("open", "open"), None, "mid"
    def ring(self, t): return 1.0 if self.HIT <= t < self.HIT + 0.5 else 0.6
    def blur(self, t): return t >= 2.7
    def dim(self, t): return 1.0
    def cam(self, t):
        v = t - self.HIT
        return (int(12 * np.exp(-v * 9) * np.sin(2 * np.pi * 20 * v)), 0) if 0 <= v < 0.4 else (0, 0)

class Race147:
    dur = 3.6; empty = True
    def motion(self, t):
        if t < 0.8:                                    # しゃがんで力をためる（小刻みにふるえる）
            v = ease(t / 0.8)
            return dict(sy=1 - 0.08 * v, dy=10 * v, dx=3 * np.sin(2 * np.pi * 12 * t))
        if t < 1.3: return dict(dx=-700 * ease((t - 0.8) / 0.5), sx=1.06, sy=0.96)      # 左へ猛ダッシュ
        if t < 1.9: return dict(dx=-700)                                                  # いなくなる
        if t < 2.6: return dict(dx=-700 + 700 * ease((t - 1.9) / 0.7), dy=-3 * abs(np.sin(2 * np.pi * 3 * t)))   # とぼとぼ戻る
        v = min(1, (t - 2.6) / 0.4)                                                       # しょんぼり沈む
        return dict(dy=10 * ease(v), sy=1 - 0.04 * ease(v))
    def face(self, t):
        if t < 0.8: return T("ヨーイ")
        if t < 1.9: return ("open", "open"), None, "o"
        if t < 2.6: return ("down", "down"), "trouble", "frown"
        return T("先ヲ", "越サレ…")
    def ring(self, t): return 1.0 if 0.8 <= t < 1.3 else 0.6
    def blur(self, t): return 0.8 <= t < 1.3
    def dim(self, t): return 1.0
    def cam(self, t): return (0, 0)

M.PATTERNS.update(ep143=Stop143, ep144=Back144, ep145=Hush145, ep146=Poke146, ep147=Race147)
if __name__ == "__main__":
    for arg in sys.argv[1:]:
        nm, _, kv = arg.partition(":")
        pat = M.PATTERNS[nm]()
        for item in filter(None, kv.split(",")):
            k, v = item.split("=", 1)
            if k in motion30.STR: setattr(pat, k, v)
            else: setattr(pat, k, float(v))
        M.render(pat, f"ハコブくん_{nm}.mp4")
```

### 声（声.sh）

声は [ElevenLabs](https://elevenlabs.io/)（文章を人の声で読み上げるサービス）で作りました。鍵は KIOXIA に置いたファイルを `tts_eleven.py` が読みます。146話の「302号室」は「さんまるに」と読ませています。


#### 第143話の声.sh

```bash
#!/bin/bash
# 第143話_止まるまで分からない のショートの声（2026-11-08）。鍵は KIOXIA の elevenlabs_api_key.txt を tts_eleven.py が読む
cd "$(dirname "$0")"
B=../../_制作ベース
[ -f "voice_eleven/岩田/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice Qs0BJHky1uZDKyp7LL2L --name 岩田 --try "ぶつかる…！｜止まれるのは知ってるよ。でも、止まるまで分からないのが怖いんだ" || echo "  ！岩田 で止まりました"
[ -f "voice_eleven/ハコブ/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice gWJpc8whMBqnF7Y0kc9f --name ハコブ --try "止まれます。安全です｜早めに、よける向きを見せます" || echo "  ！ハコブ で止まりました"
echo "第143話_止まるまで分からない: $(ls voice_eleven/*/試し_*.mp3 2>/dev/null | wc -l | tr -d " ") 本（4本でそろう）"
```

#### 第144話の声.sh

```bash
#!/bin/bash
# 第144話_お先にどうぞの作法 のショートの声（2026-11-08）。鍵は KIOXIA の elevenlabs_api_key.txt を tts_eleven.py が読む
cd "$(dirname "$0")"
B=../../_制作ベース
[ -f "voice_eleven/大城/試し_03.mp3" ] || python3 $B/tts_eleven.py --voice PrFsT9a2iZebpuiWVeug --name 大城 --try "あ、ハコブくん｜通れるかな…｜待ってたのに、先に行くの！？" || echo "  ！大城 で止まりました"
[ -f "voice_eleven/ハコブ/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice gWJpc8whMBqnF7Y0kc9f --name ハコブ --try "道が、空きましたので｜森田さん、お先にどうぞ" || echo "  ！ハコブ で止まりました"
[ -f "voice_eleven/森田茂/試し_01.mp3" ] || python3 $B/tts_eleven.py --voice ydyMD3g4SbEeNpF0wahP --name 森田茂 --try "よう分かっとる" || echo "  ！森田茂 で止まりました"
echo "第144話_お先にどうぞの作法: $(ls voice_eleven/*/試し_*.mp3 2>/dev/null | wc -l | tr -d " ") 本（6本でそろう）"
```

#### 第145話の声.sh

```bash
#!/bin/bash
# 第145話_電話中なの のショートの声（2026-11-08）。鍵は KIOXIA の elevenlabs_api_key.txt を tts_eleven.py が読む
cd "$(dirname "$0")"
B=../../_制作ベース
[ -f "voice_eleven/大城/試し_03.mp3" ] || python3 $B/tts_eleven.py --voice PrFsT9a2iZebpuiWVeug --name 大城 --try "はい、田中先生。点滴の時間ですね｜今、電話中なの見えない？｜待っててくれて、ありがと" || echo "  ！大城 で止まりました"
[ -f "voice_eleven/ハコブ/試し_03.mp3" ] || python3 $B/tts_eleven.py --voice gWJpc8whMBqnF7Y0kc9f --name ハコブ --try "お薬が、届きました！受け取りを、お願いします！｜受け取りを、お願いします｜電話が終わるまで、待ちました" || echo "  ！ハコブ で止まりました"
[ -f "voice_eleven/田中/試し_01.mp3" ] || python3 $B/tts_eleven.py --voice ydyMD3g4SbEeNpF0wahP --name 田中 --try "え、何？" || echo "  ！田中 で止まりました"
echo "第145話_電話中なの: $(ls voice_eleven/*/試し_*.mp3 2>/dev/null | wc -l | tr -d " ") 本（7本でそろう）"
```

#### 第146話の声.sh

```bash
#!/bin/bash
# 第146話_小突かれる場所 のショートの声（2026-11-08）。鍵は KIOXIA の elevenlabs_api_key.txt を tts_eleven.py が読む
cd "$(dirname "$0")"
B=../../_制作ベース
[ -f "voice_eleven/高杉/試し_03.mp3" ] || python3 $B/tts_eleven.py --voice 2zABg81whjrIKypr7XTd --name 高杉 --try "次は、さんまるに号室…！｜なんでいつもここに！｜…昨日はごめんね" || echo "  ！高杉 で止まりました"
[ -f "voice_eleven/ハコブ/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice gWJpc8whMBqnF7Y0kc9f --name ハコブ --try "衝撃を、また検知しました｜ここなら、邪魔になりません" || echo "  ！ハコブ で止まりました"
[ -f "voice_eleven/白石/試し_01.mp3" ] || python3 $B/tts_eleven.py --voice orQ2eWONBvtrTIvdS8RS --name 白石 --try "故障はない…。壊れにくくする…じゃないな" || echo "  ！白石 で止まりました"
echo "第146話_小突かれる場所: $(ls voice_eleven/*/試し_*.mp3 2>/dev/null | wc -l | tr -d " ") 本（6本でそろう）"
```

#### 第147話の声.sh

```bash
#!/bin/bash
# 第147話_気送管との競争 のショートの声（2026-11-08）。鍵は KIOXIA の elevenlabs_api_key.txt を tts_eleven.py が読む
cd "$(dirname "$0")"
B=../../_制作ベース
[ -f "voice_eleven/白石/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice orQ2eWONBvtrTIvdS8RS --name 白石 --try "次は、ハコブくんで検体を検査室へ運びます｜勝負する相手を、間違えてた" || echo "  ！白石 で止まりました"
[ -f "voice_eleven/大城/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice PrFsT9a2iZebpuiWVeug --name 大城 --try "…検体なら、もうあれがあるけど｜よーい、どん！" || echo "  ！大城 で止まりました"
[ -f "voice_eleven/ハコブ/試し_02.mp3" ] || python3 $B/tts_eleven.py --voice gWJpc8whMBqnF7Y0kc9f --name ハコブ --try "…先を、越されました｜管に入らない物を、運びます" || echo "  ！ハコブ で止まりました"
echo "第147話_気送管との競争: $(ls voice_eleven/*/試し_*.mp3 2>/dev/null | wc -l | tr -d " ") 本（6本でそろう）"
```

### ショートの台本（spec_ep143〜147.py）


#### spec_ep143.py

```python
import os, sys
sys.path.insert(0, os.environ.get("W24", os.path.expanduser("~/w")) + "/specs"); from common24 import *
import common24; common24.BGMAP[143] = "第63話.png"
HERE = os.path.dirname(os.path.abspath(__file__))
NAME, EP = "第143話_止まるまで分からない", 143
HEAD = ['止まれるのに', 'なぜ怖い？']
COMIC = C + "ep143.jpg"                    # 縦長のコマ（1080×1350）を P1〜P4 の順に積んだ画像
P1, P2, P3, P4 = [(0, 1350 * i, 1080, 1350 * (i + 1)) for i in range(4)]
DUR = 3.6
MOTION = mot("ep143", EP)                  # 冒頭の動き（第143話のフォルダの motion143x.py）
SCENES = [
  dict(kind="op", video=anim(EP, "ep143"), dur=DUR, lines=[], se=[dict(at=0.0, f="se/se_dash.mp3", db=-12), dict(at=0.7, f="se/se_skid.wav", db=-10)]),
  dict(kind="panel", crop=P1, fit=False, min=1.8, words=[W_("まっすぐ来る…", 0.1, 540, 1500, 96, "orange", 0)]),
  dict(kind="panel", crop=P2, fit=False, lines=[ns("岩田", 1, "【ぶつかる】…！")], se=[dict(at=0.0, f="se/se_skid.wav", db=-12)]),
  dict(kind="panel", crop=P3, fit=False, lines=[hk(1, "止マレマス。|【安全デス】"), ns("岩田", 2, "止まれるのは知ってるよ。||でも、【止まるまで分からない】|のが怖いんだ")]),
  dict(kind="panel", crop=P4, fit=False, lines=[hk(2, "早メニ、|【ヨケル向キ】ヲ見セマス")]),
  end(("止まれるだけでは", 74, "ink", 0.0), ("安心されない", 74, "ink", 0.25), ("早めに", 120, "orange", 0.9), ("よける", 120, "orange", 0.9), ("（遠くで向きを変えて、よける気持ちを見せる）", 34, "gray", 1.8)),
]
```

#### spec_ep144.py

```python
import os, sys
sys.path.insert(0, os.environ.get("W24", os.path.expanduser("~/w")) + "/specs"); from common24 import *
import common24; common24.BGMAP[144] = "第67話.png"
HERE = os.path.dirname(os.path.abspath(__file__))
NAME, EP = "第144話_お先にどうぞの作法", 144
HEAD = ['患者さんより', '先に行く？']
COMIC = C + "ep144.jpg"                    # 縦長のコマ（1080×1350）を P1〜P4 の順に積んだ画像
P1, P2, P3, P4 = [(0, 1350 * i, 1080, 1350 * (i + 1)) for i in range(4)]
DUR = 3.6
MOTION = mot("ep144", EP)                  # 冒頭の動き（第143話のフォルダの motion143x.py）
SCENES = [
  dict(kind="op", video=anim(EP, "ep144"), dur=DUR, lines=[], se=[dict(at=0.0, f="se/se_dash.mp3", db=-12), dict(at=1.1, f="se/se_pikon.mp3", db=-10)]),
  dict(kind="panel", crop=P1, fit=False, lines=[ns("大城", 1, "あ、【ハコブくん】")]),
  dict(kind="panel", crop=P2, fit=False, lines=[ns("大城", 2, "【通れるかな】…")]),
  dict(kind="panel", crop=P3, fit=False, se=[dict(at=0.0, f="se/se_dash.mp3", db=-12)], lines=[ns("大城", 3, "待ってたのに、|【先に行くの】！？"), hk(1, "道ガ、|【空キマシタ】ノデ")]),
  dict(kind="panel", crop=P4, fit=False, lines=[hk(2, "森田サン、|【オ先ニドウゾ】"), ns("森田茂", 1, "【よう分かっとる】")]),
  end(("狭い所では", 74, "ink", 0.0), ("患者さんが先", 74, "ink", 0.25), ("譲り合い", 120, "orange", 0.9), ("に加わる", 120, "orange", 0.9), ("（手前で止まって、先に通す）", 34, "gray", 1.8)),
]
```

#### spec_ep145.py

```python
import os, sys
sys.path.insert(0, os.environ.get("W24", os.path.expanduser("~/w")) + "/specs"); from common24 import *
import common24; common24.BGMAP[145] = "第90話.png"
HERE = os.path.dirname(os.path.abspath(__file__))
NAME, EP = "第145話_電話中なの", 145
HEAD = ['知らせたのに', 'なぜ怒られる？']
COMIC = C + "ep145.jpg"                    # 縦長のコマ（1080×1350）を P1〜P4 の順に積んだ画像
P1, P2, P3, P4 = [(0, 1350 * i, 1080, 1350 * (i + 1)) for i in range(4)]
DUR = 3.6
MOTION = mot("ep145", EP)                  # 冒頭の動き（第143話のフォルダの motion143x.py）
SCENES = [
  dict(kind="op", video=anim(EP, "ep145"), dur=DUR, lines=[], se=[dict(at=0.0, f="se/se_dash.mp3", db=-12), dict(at=0.7, f="se/se_pikon.mp3", db=-8)]),
  dict(kind="panel", crop=P1, fit=False, lines=[ns("大城", 1, "はい、田中先生。|【点滴の時間】ですね")]),
  dict(kind="panel", crop=P2, fit=False, lines=[hk(1, "オ薬ガ、届キマシタ！|【受ケ取リヲ、オ願イシマス】！"), ns("大城", 2, "今、【電話中】なの|見えない？")]),
  dict(kind="panel", crop=P3, fit=False, lines=[hk(2, "受ケ取リヲ、|【オ願イシマス】"), ns("田中", 1, "え、【何】？", phone=True)]),
  dict(kind="panel", crop=P4, fit=False, lines=[hk(3, "電話ガ終ワルマデ、|【待チマシタ】"), ns("大城", 3, "待っててくれて、|【ありがと】")]),
  end(("知らせる時は", 74, "ink", 0.0), ("相手の手を見る", 74, "ink", 0.25), ("待つ", 120, "orange", 0.9), ("のも仕事", 120, "orange", 0.9), ("（急ぎだけ、担当の端末を鳴らす）", 34, "gray", 1.8)),
]
```

#### spec_ep146.py

```python
import os, sys
sys.path.insert(0, os.environ.get("W24", os.path.expanduser("~/w")) + "/specs"); from common24 import *
import common24; common24.BGMAP[146] = "第52話.png"
HERE = os.path.dirname(os.path.abspath(__file__))
NAME, EP = "第146話_小突かれる場所", 146
HEAD = ['なぜいつも', '小突かれる？']
COMIC = C + "ep146.jpg"                    # 縦長のコマ（1080×1350）を P1〜P4 の順に積んだ画像
P1, P2, P3, P4 = [(0, 1350 * i, 1080, 1350 * (i + 1)) for i in range(4)]
DUR = 3.6
MOTION = mot("ep146", EP)                  # 冒頭の動き（第143話のフォルダの motion143x.py）
SCENES = [
  dict(kind="op", video=anim(EP, "ep146"), dur=DUR, lines=[], se=[dict(at=0.9, f="se/se_don.mp3", db=-8), dict(at=1.7, f="se/se_pikon.mp3", db=-10)]),
  dict(kind="panel", crop=P1, fit=False, se=[dict(at=0.0, f="se/se_pikon.mp3", db=-10)], lines=[ns("高杉", 1, "次は【302号室】…！")]),
  dict(kind="panel", crop=P2, fit=False, se=[dict(at=0.0, f="se/se_don.mp3", db=-10)], lines=[ns("高杉", 2, "なんで|【いつもここに】！")]),
  dict(kind="panel", crop=P3, fit=False, lines=[hk(1, "衝撃ヲ、|【マタ検知】シマシタ"), ns("白石", 1, "故障はない…。||【壊れにくくする】…|じゃないな")]),
  dict(kind="panel", crop=P4, fit=False, lines=[hk(2, "ココナラ、|【邪魔ニナリマセン】"), ns("高杉", 3, "…昨日は|【ごめんね】")]),
  end(("小突かれた記録は", 74, "ink", 0.0), ("邪魔の手がかり", 74, "ink", 0.25), ("待つ場所", 120, "orange", 0.9), ("を変える", 120, "orange", 0.9), ("（壊れにくくする前に、原因を探す）", 34, "gray", 1.8)),
]
```

#### spec_ep147.py

```python
import os, sys
sys.path.insert(0, os.environ.get("W24", os.path.expanduser("~/w")) + "/specs"); from common24 import *
import common24; common24.BGMAP[147] = "第85話.png"
HERE = os.path.dirname(os.path.abspath(__file__))
NAME, EP = "第147話_気送管との競争", 147
HEAD = ['ロボット対', '気送管']
COMIC = C + "ep147.jpg"                    # 縦長のコマ（1080×1350）を P1〜P4 の順に積んだ画像
P1, P2, P3, P4 = [(0, 1350 * i, 1080, 1350 * (i + 1)) for i in range(4)]
DUR = 3.6
MOTION = mot("ep147", EP)                  # 冒頭の動き（第143話のフォルダの motion143x.py）
SCENES = [
  dict(kind="op", video=anim(EP, "ep147"), dur=DUR, lines=[], se=[dict(at=0.0, f="se/se_rev.mp3", db=-14), dict(at=0.8, f="se/se_dash.mp3", db=-10)]),
  dict(kind="panel", crop=P1, fit=False, lines=[ns("白石", 1, "次は、ハコブくんで|【検体を検査室へ】運びます"), ns("大城", 1, "…検体なら、|【もうあれがある】けど")]),
  dict(kind="panel", crop=P2, fit=False, se=[dict(at=1.0, f="se/se_dash.mp3", db=-10)], lines=[ns("大城", 2, "【よーい、どん】！")]),
  dict(kind="panel", crop=P3, fit=False, lines=[hk(1, "…先ヲ、|【越サレマシタ】"), ns("白石", 2, "勝負する相手を、|【間違えてた】")]),
  dict(kind="panel", crop=P4, fit=False, lines=[hk(2, "【管ニ入ラナイ物】ヲ、|運ビマス")]),
  end(("比べる相手は", 74, "ink", 0.0), ("人だけじゃない", 74, "ink", 0.25), ("今ある設備", 120, "orange", 0.9), ("と分ける", 120, "orange", 0.9), ("（管に入らない物を、ロボットで運ぶ）", 34, "gray", 1.8)),
]
```

### 書き出したショートの長さと、冒頭3秒の音量

```
できあがり: out_第144話_お先にどうぞの作法.mp4 20.8 秒
できあがり: out_第143話_止まるまで分からない.mp4 22.4 秒
できあがり: out_第147話_気送管との競争.mp4 26.2 秒
できあがり: out_第146話_小突かれる場所.mp4 25.1 秒
できあがり: out_第145話_電話中なの.mp4 27.2 秒

$ for e in 143 144 145 146 147; do ... ffmpeg -t 3 -i ... -af volumedetect ...; done
143 mean_volume: -20.5 dB max_volume: -1.2 dB 
144 mean_volume: -20.9 dB max_volume: -1.3 dB 
145 mean_volume: -21.0 dB max_volume: -1.3 dB 
146 mean_volume: -18.7 dB max_volume: -1.5 dB 
147 mean_volume: -20.5 dB max_volume: -1.4 dB 
```

前に、作業環境に効果音のリンクがなく冒頭が無音で書き出されたことがあったので（第134〜137話）、冒頭3秒の音量を測っています。5本とも鳴っています。

【画像】LOG468_07_short_ep143_frames.jpg 第143話のショートを1.4秒ごとに並べたコマ。冒頭の動き、4コマ、締めの言葉の順


## 表紙・英語版を作り、サイトに10ページを公開した

### 表紙（cover_edit_143.py と make_covers.py）

表紙は、見せ場のコマから吹き出しを Gemini の編集で消し、上に「第N話 題」を重ねて作ります（LOG413）。

#### cover_edit_143.py（吹き出しを消す）

```python
"""縦長4コマの表紙用：選んだコマから吹き出しと字を消す（Gemini の API・2026-10-31）"""
import sys, os, concurrent.futures
sys.path.insert(0, os.path.expanduser('~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/道具'))
import vbuild as V
PICK = {143:2,144:3,145:2,146:2,147:2}
NOEDIT = {133,128,116,111,101,99,76}
PROMPT = """Edit this manga panel. Remove EVERY speech bubble and thought bubble completely, including their tails and all the text inside them (ALSO the speech bubble inside the small square inset window in the corner: erase that bubble entirely so the inset shows only the person in the room). Where a bubble was, redraw what would be behind it: plain white background, or the continuation of the wall, hair, body or object that the bubble covered. Keep sound-effect lettering drawn directly on the art (like バチン, カラン, ジリリリ) as it is. Keep EVERYTHING else exactly the same: same characters, faces, eyes, poses, sizes, positions, clothes, robots, screentone, line style, black-and-white manga look, white background, 4:5 framing. Do not add any new text, bubble, border, inset window, portrait or object. Where the bubble was, leave plain white background (and continue the thin vertical line if it passed behind the bubble)."""
here = os.path.dirname(os.path.abspath(__file__))
open(here + '/cover_edit/_prompt.txt', 'w', encoding='utf-8').write(PROMPT)
eps = [int(a) for a in sys.argv[1:]] or [e for e in PICK if e not in NOEDIT]
def job(e):
    src = os.path.expanduser(f'~/mnt/PixelstoMotors/ハコブくん/4コママンガ作成データ/縦長/第{e}話/仕上げ/第{e}話_P{PICK[e]}_1080x1350.png')
    return V.gen_one(here + '/cover_edit/_prompt.txt', here + f'/cover_edit/ep{e}_P{PICK[e]}', [("the manga panel to edit", src)], "4:5")
with concurrent.futures.ThreadPoolExecutor(6) as ex:
    for r in ex.map(job, eps): print(r)
```

#### make_covers.py（題を重ねて、表紙とコマの webp を書き出す）

```python
"""第143〜147話の表紙（2026-11-08）：上に「第N話 タイトル」、下に吹き出しを消したコマ（covers_v2/make_covers.py と同じ組み方）"""
from PIL import Image, ImageDraw, ImageFont
import numpy as np, glob
FB='/usr/share/fonts/opentype/noto/NotoSansCJK-Black.ttc'
T={143:'止まるまで分からない',144:'お先にどうぞの作法',145:'電話中なの',146:'小突かれる場所',147:'気送管との競争'}
PICK={143:2,144:3,145:2,146:2,147:2}
W,H=1080,1350; X0=56; MAXW=W-2*X0
font=ImageFont.truetype(FB,96,index=0)
for e,k in PICK.items():
    src=glob.glob(f'_stage143/ep{e}_P{k}_*.jpg')[0]
    p=Image.open(src).convert('RGB').resize((W,H),Image.LANCZOS)
    a=np.asarray(p).astype(np.int16); a[a.min(2)>232]=255; p=Image.fromarray(a.astype(np.uint8))
    head=f'第{e}話'; t=T[e]; one=f'{head} {t}'
    lines=[one] if font.getlength(one)<=MAXW else [head,t]
    c=Image.new('RGB',(W,H),'white'); d=ImageDraw.Draw(c); y=62
    for ln in lines:
        f=font
        while f.getlength(ln)>MAXW: f=ImageFont.truetype(FB,f.size-4,index=0)
        d.text((X0,y),ln,font=f,fill=(36,36,36)); y+=f.size+18
    top=y+40; A=H-top
    g=np.asarray(p.convert('L'))<235; ys=np.where(g.any(1))[0]; xs=np.where(g.any(0))[0]
    cy0,cy1,cx0,cx1=ys.min(),ys.max(),xs.min(),xs.max(); ch=cy1-cy0
    s=min(A*0.94/ch,1.45) if ch<A else A/ch
    q=p.resize((round(W*s),round(H*s)),Image.LANCZOS)
    mx=(cx0+cx1)/2*s; my=(cy0+cy1)/2*s
    if s>=1:
        L=int(min(max(mx-W/2,0),q.width-W)); Tp=int(min(max(my-A/2,0),q.height-A))
        b=Image.new('RGB',(W,A),'white'); b.paste(q.crop((L,Tp,min(q.width,L+W),min(q.height,Tp+A))),(0,0)); c.paste(b,(0,top))
    else:
        t0=int(cy0*s); b=Image.new('RGB',(q.width,A),'white'); b.paste(q.crop((0,t0,q.width,min(q.height,t0+A))),(0,0)); c.paste(b,((W-q.width)//2,top))
    c.save(f'media/manga_ep{e}_cover.webp','WEBP',quality=88,method=6)
    for i in range(1,5):
        Image.open(f'_stage143/第{e}話_P{i}_1080x1350.png').convert('RGB').save(f'media/manga_ep{e}_p{i}.webp','WEBP',quality=88,method=6)
S=Image.new('RGB',(5*370,460),'#888')
for i,e in enumerate(PICK): S.paste(Image.open(f'media/manga_ep{e}_cover.webp').resize((360,450)),(i*370,0))
S.save('covers.jpg',quality=85)
```

【画像】LOG468_08_covers.jpg 第143〜147話の表紙

### 英語版（en_manga.py の指定）

英語版は描き直さず、吹き出しの中の日本語を消して英語を組みます（LOG416 の en_manga.py）。`seed` は各吹き出しの白い内側の1点で、白い閉じた領域を探すプログラムで座標を出してから、どの吹き出しかを見て当てました。


#### 英語版の指定 ep143.json

```json
{"ep": 143, "cover_line": "STOPPING ISN'T THE SCARY PART.", "panels": [{"bubbles": [], "signs": []}, {"bubbles": [{"seed": [928, 215], "text": "It's going\nto hit me…!"}], "signs": []}, {"bubbles": [{"seed": [383, 409], "text": "I can stop.\nI am safe."}, {"seed": [759, 127], "text": "I know you can stop.\nWhat scares me is not knowing\nuntil you actually do."}], "signs": []}, {"bubbles": [{"seed": [431, 267], "text": "I will turn early,\nso you can see\nI'm moving aside."}], "signs": []}]}
```

#### 英語版の指定 ep144.json

```json
{"ep": 144, "cover_line": "I WAS WAITING, AND YOU WENT FIRST?!", "panels": [{"bubbles": [{"seed": [481, 196], "text": "Oh, Hakobukun."}], "signs": []}, {"bubbles": [{"seed": [624, 165], "text": "Can we fit\nthrough there…?"}], "signs": []}, {"bubbles": [{"seed": [121, 183], "text": "The way\nwas clear."}, {"seed": [731, 185], "text": "I was waiting,\nand you went first?!"}], "signs": []}, {"bubbles": [{"seed": [147, 223], "text": "Mr. Morita,\nafter you."}, {"seed": [744, 199], "text": "Now you've\ngot it."}], "signs": []}]}
```

#### 英語版の指定 ep145.json

```json
{"ep": 145, "cover_line": "CAN'T YOU SEE I'M ON THE PHONE?", "panels": [{"bubbles": [{"seed": [626, 147], "text": "Yes, Dr. Tanaka.\nTime for the IV, right?"}], "signs": []}, {"bubbles": [{"seed": [564, 180], "text": "Your medicine\nhas arrived!\nPlease sign for it!"}, {"seed": [723, 138], "text": "Can't you see\nI'm on the phone?"}], "signs": []}, {"bubbles": [{"seed": [282, 332], "text": "Please sign\nfor it."}, {"seed": [925, 250], "text": "Huh?\nWhat?"}], "signs": []}, {"bubbles": [{"seed": [447, 152], "text": "I waited until\nyour call ended."}, {"seed": [614, 342], "text": "Thanks for\nwaiting."}], "signs": []}]}
```

#### 英語版の指定 ep146.json

```json
{"ep": 146, "cover_line": "WHY ARE YOU ALWAYS RIGHT HERE?!", "panels": [{"bubbles": [{"seed": [903, 172], "text": "Room 302\nnext…!"}], "signs": []}, {"bubbles": [{"seed": [989, 246], "text": "Why are you\nalways\nright here?!"}], "signs": []}, {"bubbles": [{"seed": [163, 209], "text": "Impact detected\nagain."}, {"seed": [894, 228], "text": "Nothing's broken…\nMaking it tougher…\nisn't the answer."}], "signs": []}, {"bubbles": [{"seed": [400, 153], "text": "…Sorry about\nyesterday."}, {"seed": [897, 240], "text": "Here, I will not\nbe in the way."}], "signs": []}]}
```

#### 英語版の指定 ep147.json

```json
{"ep": 147, "cover_line": "…I WAS BEATEN.", "panels": [{"bubbles": [{"seed": [110, 177], "text": "Next, Hakobukun will\ncarry specimens\nto the lab."}, {"seed": [809, 213], "text": "…We already have\nsomething for\nspecimens."}], "signs": []}, {"bubbles": [{"seed": [683, 123], "text": "Ready… go!"}], "signs": []}, {"bubbles": [{"seed": [143, 258], "text": "…I was\nbeaten."}, {"seed": [920, 259], "text": "We picked\nthe wrong\nopponent."}], "signs": []}, {"bubbles": [{"seed": [584, 223], "text": "I will carry\nwhat doesn't fit\nin the tube."}], "signs": []}]}
```

【画像】LOG468_09_english.jpg 第143〜147話の英語版。各段の左4つが英語にしたコマ、右端が英語版の表紙

### R2 に画像を入れ、ページを公開した

```
$ python3 upload_media.py media_ep143_147 media_ep143_147_en
入れる 50・入っている 0・名前の重なり 0・合計 8.0MB
  50/50
おわり
$ for e in 143 144 145 146 147; do echo "$e $(curl ... /manga/ep$e) $(curl ... /english-4-koma/ep$e) $(curl ... /media/manga_ep${e}_cover.webp) $(curl ... /media/manga_ep${e}en_p4.webp)"; done
143 200 200 200 200
144 200 200 200 200
145 200 200 200 200
146 200 200 200 200
147 200 200 200 200
```

| 話 | 日本語のページの題 | 英語のページの題 |
|---|---|---|
| 143 | 止まれることと、止まると分かることは違う | Stopping Is Not the Same as Showing You'll Stop |
| 144 | 廊下の譲り合いに、ロボットも加わる | Robots Join the Hallway Etiquette Too |
| 145 | 知らせる時機は、相手の手のふさがり具合で選ぶ | Choosing When to Speak Up |
| 146 | 小突かれた記録は、邪魔の元を探す手がかり | When a Robot Gets Kicked, Look for the Cause |
| 147 | 置き換える相手は、人だけではない | Robots Don't Only Replace People |

ページは pixelstomotors.com/manga/ep143 〜 ep147 と pixelstomotors.com/english-4-koma/ep143 〜 ep147 です。p2m-site への commit は `18a6f4b` です。

### SNS の予約

| 話 | 日本語の X・LinkedIn（日本時間） | 英語の X（米国東部時間） |
|---|---|---|
| 143 | 12/7 20:00 | 2/8 11:00 |
| 144 | 12/8 20:00 | 2/9 11:00 |
| 145 | 12/9 20:00 | 2/10 11:00 |
| 146 | 12/12 19:30 | 2/11 11:00 |
| 147 | 12/13 19:30 | 2/12 11:00 |

マンガの投稿文は r2r2r-content の `sns/MANGA_ep143〜147_sns_posts.json`、ショートの X の型は `sns/SHORT_ep143〜147_sns_posts.json` です（commit `59cc07f`）。進捗管理表（Google スプレッドシート）の No.143〜147 は「完了」にしました。


## Gemini に描かせた枚数は120枚、見積もりで約1,100円

| 頼んだもの | 頼み方 | 枚数 | 見積もり |
|---|---|---|---|
| 5話の下書き（1コマ2枚） | バッチ | 40 | 約220円 |
| 1コマずつの確認で描き直し | ふつう | 9 | 約100円 |
| 向きと大きさの描き直し | ふつう | 47 | 約520円 |
| 第144話の流れと147話のドロワーの描き直し | ふつう | 19 | 約210円 |
| 表紙の吹き出しを消す編集 | ふつう | 5 | 約55円 |
| 合計 | | 120 | 約1,100円 |

前の第138〜142話は5話で67枚・約460円（LOG448）でした。今回は、4コマを並べて見つかったずれの直しが66枚（向きと大きさ47枚・流れとドロワー19枚）あり、2倍以上かかりました。向きと大きさの47枚には、途中で止まった頼みの3枚を含みます。


## 次の話も、描いたら4コマを並べて話として読んでから見せる

次は第148話からです。描いたら、1コマずつの確認のあとに、4コマを並べて話として読む確認を通してから、エイイチに見せます。エイイチの指摘の数と、描き直しの枚数を今回と比べます。
