# -*- coding: utf-8 -*-
# 動画「メーカーのロボットは、どこまで変えられるか」— 考察回「壁打ちの型」の3本目（もとは LOG406）の場面
# 書き写した元: 現場で扱えるか/scenes_kabe.py（型の決まりは video_rules.md 10節）
#   紙の文字は真ん中にそろえる／線を使わず面で分ける（事実＝墨・エイイチの見立て＝濃い灰・ハコブくん＝薄い灰）
#   朱は判子とリアクション（却下・保留・ボツ・即答・エイイチの推し）だけ／字幕は動かさない
#   脱線（画面いっぱいの絵）は4回: 自分で改造（マンガ）・ロボ検（マンガ）・扉、全開（写真風）・課金で解放（写真風）。どれも Gemini で作った絵
#   天丼: ハコブくんが「改造申請書」を出すたびに、判子が落ちる。冒頭「却下」・やり取り2「却下」・最後だけ「保留」
import os
from kabe_kit import *
import scene_kit as _sk

_tl = Timeline()
tl = _tl
N = len(_tl.idx)
A = lambda i, off=0.0: tl.start(i) + off          # 動画の頭からの秒
S = tl.speech                                      # その文を読み終わるまでの長さ
LOGO = _sk.LOGO
BRAND = f'<img src="{LOGO}" style="position:absolute;right:56px;top:40px;height:30px;filter:invert(1);opacity:.55">'
SANS = "font-family:'Noto Sans CJK JP',sans-serif;"
BG = "src/bg/"
GEM = "絵は生成AI（Gemini）で作成"
LAW = "法律の話は、調べた範囲の理解です。専門家の判断ではありません"

BLOCKS = []


def block(name, sents, fn, video=None):
    b0 = _tl.start(sents[0])
    T = lambda i, off=0.0: tl.start(i) - b0 + off
    E = lambda i: tl.end(i) - b0
    b = dict(name=name, sents=list(sents))
    if video:
        b["video"] = video
    else:
        b["html"] = page(fn(T, E) + BRAND)
    BLOCKS.append(b)


def inkl(text, t, step=0.05):
    """「／」で行を分けて書く（1〜2文字だけ次の行に落ちないように）"""
    out, tt = [], t
    for k, part in enumerate(text.split("／")):
        out.append(ink(part, tt, step)); tt += len(part) * step
    return "<br>".join(out)


def line(text, t, size=30, weight=700, step=0.05, style=""):
    return f'<div style="{SANS}font-size:{size}px;font-weight:{weight};{style}">{ink(text, t, step)}</div>'


def qline(text, t, size=46, step=0.06, style=""):
    return f'<div class="serif" style="font-size:{size}px;font-weight:700;line-height:1.5;{style}">{ink(text, t, step)}</div>'


def src(text, t=0.0):
    return f'<div class="src" style="opacity:0;animation:show .01s linear {t:.3f}s forwards">{text}</div>'


def bigline(text, t, size=110, style=""):
    return f'<div class="serif" style="font-size:{size}px;font-weight:900;line-height:1.15;{style}">{land(text, t)}</div>'


def hanko(text, t, style, size=72, t_hide=None):
    """朱の判子（却下・保留）。四角い枠で、斜めに落ちる。t_hide で消す"""
    an = f"animation:slam .35s cubic-bezier(.5,0,.75,0) {t:.3f}s forwards" + (f", hide .2s linear {t_hide:.3f}s forwards" if t_hide else "") + ";"
    return (f'<div class="stamp" style="{an}font-family:\'Noto Serif CJK JP\',serif;font-size:{size}px;'
            f'padding:6px 26px;border:8px double #f5f3ee;outline:6px solid #c4472a;{style}">{text}</div>')


FORM_CSS = """<style>
.form { position:relative; width:760px; background:#fffdf8; border:3px solid #22201c; padding:26px 40px 30px; font-family:'Noto Sans CJK JP',sans-serif; color:#22201c; text-align:left; }
.form h3 { font-family:'Noto Serif CJK JP',serif; font-size:50px; font-weight:900; text-align:center; letter-spacing:.2em; margin-bottom:6px; }
.form .who { font-size:24px; color:#5a564f; text-align:center; margin-bottom:18px; }
.form .it { display:flex; align-items:center; gap:18px; font-size:36px; font-weight:700; padding:8px 0; border-top:2px solid #e2ddd2; opacity:0; animation:pop .4s ease-out forwards; }
.form .it b { display:inline-flex; width:40px; height:40px; border:4px solid #22201c; align-items:center; justify-content:center; font-size:30px; }
</style>"""


def form(no, items, t_in, t_items):
    """改造申請書（天丼）。items は文字のリスト、t_items はそれぞれが出る秒"""
    its = "".join(f'<div class="it" style="animation-delay:{t:.3f}s"><b>✓</b>{txt}</div>' for txt, t in zip(items, t_items))
    return (FORM_CSS + f'<div class="form" style="opacity:0;animation:pop .45s ease-out {t_in:.3f}s forwards">'
            f'<h3>改造申請書</h3><div class="who">No.{no}　申請者：ハコブくん</div>{its}</div>')


# ================= 0 つかみ（改造申請書1枚目 →「却下」） =================
def hook(T, E):
    return (paper(tag("考察ノート") + '<div class="center" style="gap:30px">'
                  + form(1, ["速さの上限を外す", "荷物を倍積む", "夜も走る"], T(0, 0.3), [T(1, 0.2), T(1, 0.6), T(1, 1.0)])
                  + card("mi", inkl("それ、事故が起きたら、誰の責任？", T(2, 0.3), 0.06), T(2, 0.1), "エイイチ", w=700, anim="wipe")
                  + '</div>', 0.0)
            + hanko("却下", T(3, 0.25), "left:1110px;top:150px", size=80)
            + shake_js([(T(3, 0.6), 0.3)]))
block("hook", [0, 1, 2, 3], hook)


def hook_law(T, E):
    return (paper(tag("改造した人は、どうなるか") + '<div class="stack" style="top:76px;gap:16px">'
                  + card("fact", inkl("メーカーが想定していない改変で、危険を増やした人は、／メーカーと同じ義務を負う", T(4, 0.6), 0.04), T(4, 0.3), "EU", w=900)
                  + card("fact", inkl("仕事として加工して人に渡した業者は、／作った側と同じ責任を負う側に入る", T(4, 2.8), 0.04), T(4, 2.5), "日本", w=900)
                  + '<div class="row2" style="gap:24px;align-items:center;margin-top:6px">'
                  + card("hb", "車なら、買えば好きに改造できる", T(6, 0.3), "ハコブくん", w=470, fade=T(7, 1.0), style="font-size:30px")
                  + card("mi", inkl("車も、保安基準に／合わない改造はだめ", T(7, 0.3), 0.06), T(7, 0.1), "エイイチ", w=400, anim="wipe", style="font-size:30px")
                  + '</div>'
                  + f'<div class="serif" style="font-size:38px;font-weight:700">{ink("ロボットは、買っても、", T(8, 2.6), 0.06)}'
                    f'{land("思ったほど自由じゃない", T(8, 3.8), "font-size:48px;font-weight:900")}</div>'
                  + '</div>' + src(LAW, T(4, 0.3)), 0.0))
block("hook_law", [4, 5, 6, 7, 8], hook_law)


def hook_q(T, E):
    return paper(tag("考察回　壁打ち") +
                 '<div class="center"><p class="v serif" style="font-size:58px;line-height:1.6;font-weight:300;letter-spacing:.06em;">'
                 f'<span data-ink="{T(9, 0.3):.2f},0.09" style="font-weight:700;">メーカーの</span><br>'
                 f'<span data-ink="{T(9, 0.9):.2f},0.09">ロボットは、</span><br>'
                 f'{land("どこまで", T(9, 1.8), "font-size:108px;font-weight:900")}<br>'
                 f'<span data-ink="{T(9, 2.6):.2f},0.09">変えられるか。</span></p></div>', 0.0)
block("hook_q", [9], hook_q)

# ================= 1 自己紹介（DAY124 の映像とテロップ） =================
O_TITLE = '<div class="lt"><img class="a" src="' + LOGO + '"><div class="b">フィジカルAI解説</div><div class="c">案内役：ハコブくん（AI音声）</div></div>'
O_EIICHI = '<div class="lt"><img class="a" src="' + LOGO + '"><div class="b">エイイチ</div><div class="c">デザイナー／ロボティクスの研究・開発</div></div>'
O_OUTRO = '<div class="lt"><img class="a" src="' + LOGO + '"><div class="b">相談は概要欄の連絡先から</div><div class="c">高評価・チャンネル登録／コメントはエイイチが全部読んでいます</div></div>'
OVERLAYS = {"title": _sk.overlay(O_TITLE), "title_eiichi": _sk.overlay(O_EIICHI), "outro": _sk.overlay(O_OUTRO)}
_t_title = tl.end(11) - tl.start(10)
block("title", [10, 11], None, video=("src/day124_crop.mp4", 4.5, "png/ovl/title.png"))
block("title_eiichi", [12], None, video=("src/day124_crop.mp4", 4.5 + _t_title, "png/ovl/title_eiichi.png"))


def title_kabe(T, E):
    return paper(tag("考察回") + '<div class="center" style="gap:30px">'
                 + f'<div class="serif" style="font-size:150px;font-weight:900;line-height:1.1">{land("壁打ち", T(13, 0.5))}</div>'
                 + line("エイイチの考えに、ハコブくんが付き合う", T(13, 1.3), size=34, weight=500)
                 + '</div>', 0.0)
block("title_kabe", [13], title_kabe)

# ================= 2 持ち込み：5段階の線（脱線①「自分で改造。」） =================
def bring(T, E):
    return (paper(tag("持ち込み") + '<div class="stack" style="top:96px;gap:24px">'
                  + card("mi", inkl("搬送や配膳、掃除のロボットの動きを良くしたいとき、／メーカーの製品を、ソフトもハードも触ったら？", T(15, 0.4), 0.04), T(15, 0.15), "エイイチの疑問", w=920, anim="wipe")
                  + line("実物の改造ではなく、Claude と一緒に、決まり・契約・実例を調べた話", T(16, 1.6), size=28, weight=500, style="color:#5a564f")
                  + card("hb", "店で見て、まず直したいのは？", T(17, 0.2), "ハコブくん", w=520, style="font-size:30px")
                  + card("mi", inkl("速さより、人とすれ違うときと、／迷う動きを、まず直したい", T(18, 0.4), 0.05), T(18, 0.15), "エイイチ", w=760, anim="wipe")
                  + '</div>' + src(LAW, T(16, 0.3)), 0.0)
            + stamp("覚えた", T(19, 2.4), "left:1250px;top:430px;font-size:48px"))
block("bring", [14, 15, 16, 17, 18, 19], bring)


STEP_CSS = """<style>
.st { position:absolute; display:flex; align-items:center; justify-content:center; font-family:'Noto Sans CJK JP',sans-serif; font-weight:700; font-size:30px;
      opacity:0; animation:pop .4s ease-out forwards; }
.st i { font-style:normal; font-size:22px; margin-right:12px; opacity:.7; }
@keyframes split { to { transform: translateX(var(--gx)); } }
.zone { position:absolute; font-family:'Noto Sans CJK JP',sans-serif; font-weight:900; font-size:30px; letter-spacing:.1em; opacity:0; animation:show .3s linear forwards; }
</style>"""


def steps(T, E):
    fly = (T(22, S(22) - 0.2), E(22) + 0.05)
    t_cut = T(23, 0.3)
    labels = ["設定", "公式の外部接続", "外付け", "ソフトの書き換え", "機体の改造"]
    # 階段の面: 下から上へ。左下から右上に積む。4段目から右へずれて切れ目ができる
    html = ""
    for k, lab in enumerate(labels):
        x, y, w, h = 60 + k * 150, 600 - k * 96, 300, 84
        col = ("#22201c", "#f5f3ee") if k < 3 else ("#dcd6c9", "#22201c")
        gx = "70px" if k >= 3 else "0px"
        appear = T(20, 1.2 + 0.25 * k) if k < 1 else T(21, [0.0, 1.0, 3.3, 4.8, 6.6][k])
        html += (f'<div class="st" style="left:{x}px;top:{y}px;width:{w}px;height:{h}px;background:{col[0]};color:{col[1]};--gx:{gx};'
                 f'animation:pop .4s ease-out {appear:.3f}s forwards, split .6s cubic-bezier(.3,1.4,.4,1) {t_cut:.3f}s forwards"><i>{k + 1}</i>{lab}</div>')
    zones = (f'<div class="zone" style="left:60px;top:700px;animation-delay:{T(24, 0.4):.3f}s">メーカーの想定の内側</div>'
             f'<div class="zone" style="right:40px;top:160px;color:#5a564f;animation-delay:{T(24, 2.2):.3f}s">外側</div>')
    return (dassen(BG + "自分で改造.jpg", fly[0] + 0.05, fly[1] - 0.05, words="自分で|!改造。", word_style="top:70px;font-size:130px", src_note=GEM)
            + STEP_CSS + paper(tag("触り方の5段階") + html + zones
                               + f'<div style="position:absolute;left:40px;top:110px;width:420px;animation:hide .3s linear {T(24, 3.6):.3f}s forwards">'
                               + card("hb", "5段目まで、一気に", T(22, 0.3), "ハコブくん", w=380, botsu=E(22) + 0.15, fade=T(23, 0.2), style="font-size:30px") + '</div>'
                               + f'<div style="position:absolute;left:60px;top:40px;width:540px;opacity:0;animation:show .3s linear {T(24, 4.0):.3f}s forwards">'
                               + f'<div class="serif" style="font-size:34px;font-weight:900;line-height:1.5;text-align:left">法律・保証・保守も、<br>ほぼこの切れ目で分かれる</div></div>', 0.0, flights=[fly])
            + shake_js([(t_cut, 0.3)]))
block("steps", [20, 21, 22, 23, 24], steps)


def steps2(T, E):
    return paper(tag("切れ目は、思ったより手前") + '<div class="center" style="gap:34px">'
                 + card("mi", inkl("思ったより、手前で切れてる", T(25, 0.3), 0.06), T(25, 0.1), "エイイチ", w=600, anim="wipe")
                 + f'<div class="serif" style="font-size:42px;font-weight:700;line-height:1.5">{ink("動きや操作性を良くしたい用事の多くは、", T(26, 1.2), 0.05)}<br>'
                   f'{land("3段目まで", T(26, 4.2), "font-size:96px;font-weight:900")}{ink("で届く", T(26, 4.8), 0.08)}</div>'
                 + line("Claude の分析（シミュレーションの店でも、詰まりの多くは店の形と回し方の直しで減った）", T(26, 6.0), size=24, weight=500, style="color:#5a564f")
                 + '</div>', 0.0)
block("steps2", [25, 26], steps2)

# ================= 3 やり取り1：車とサブスク（脱線②「ロボ検、開幕。」） =================
def car(T, E):
    return paper(tag("やり取り 1 ／ 車と比べると") + '<div class="center" style="gap:30px">'
                 + card("mi", inkl("車やバイクは、自分でもいじってきた。／ロボットも個人で持つようになったら、／改造したい人は必ず多い。僕もしたい", T(28, 0.4), 0.045), T(28, 0.15), "エイイチ", w=880, anim="wipe")
                 + bigline("改造したい側の人", T(29, 0.4), size=80)
                 + '</div>', 0.0)
block("car", [27, 28, 29], car)


def car2(T, E):
    col = lambda kind, head, sub, t: (f'<div class="card k-{kind}" style="width:420px;opacity:0;animation:pop .45s ease-out {t:.3f}s forwards">'
                                      f'<span class="serif" style="font-size:40px;font-weight:900;line-height:1.3;white-space:nowrap">{head}</span><br><span style="font-size:26px">{sub}</span></div>')
    return paper(tag("買う？　借りる？") + '<div class="stack" style="top:100px;gap:26px">'
                 + '<div class="row2" style="gap:34px">'
                 + col("hb", "車は、買う", "買えば、いろいろ変えられそう", T(30, 0.3))
                 + col("fact", "ロボットは、サブスク", "用意された以上は、認められない？", T(30, 1.6))
                 + '</div>'
                 + card("mi", inkl("ハードはともかく、ソフトは／アップデートが多くて、追いつかなそう", T(31, 0.4), 0.05), T(31, 0.15), "エイイチ", w=780, anim="wipe")
                 + card("fact", "サブスク：機体はメーカーか販売会社の物。契約で縛られる", T(32, 0.3), None, w=900, style="font-size:30px")
                 + bigline("では、買ったら？", T(33, 0.2), size=64)
                 + '</div>', 0.0)
block("car2", [30, 31, 32, 33], car2)


def soft(T, E):
    return (paper(tag("買っても残る縛り") + '<div class="stack" style="top:96px;gap:24px">'
                  + f'<div class="serif" style="font-size:50px;font-weight:700">{ink("ソフトは、", T(34, 0.2), 0.08)}{land("買っていない", T(34, 0.9), "font-size:90px;font-weight:900")}</div>'
                  + line("使う権利を、借りているだけ", T(34, 2.4), size=30, weight=500, style="color:#5a564f")
                  + card("fact", inkl("中古で売られた車の運転を助ける機能を、／メーカーが遠くから外した（2020年の報道）", T(35, 0.4), 0.04), T(35, 0.15), "車では", w=880)
                  + '<div class="row2" style="gap:24px;align-items:center">'
                  + card("mi", inkl("え、買ったのに？", T(36, 0.2), 0.07), T(36, 0.05), "エイイチ", w=380, anim="wipe")
                  + card("hb", "騒ぎになって、あとで戻された", T(37, 0.3), None, w=480, style="font-size:28px")
                  + '</div>'
                  + f'<div class="serif" style="font-size:52px;font-weight:900">{land("体は自分の物、機能は借り物", T(37, 3.0))}</div>'
                  + '</div>' + src("出どころ：Electrek（2020-02-07）。騒ぎのあと、機能は戻された", T(35, 0.3)), 0.0))
block("soft", [34, 35, 36, 37], soft)


def rule(T, E):
    fly = (T(39, S(39) - 0.3), E(39) + 0.1)
    col = lambda kind, head, sub, t: (f'<div class="card k-{kind}" style="width:430px;opacity:0;animation:pop .45s ease-out {t:.3f}s forwards">'
                                      f'<span class="serif" style="font-size:48px;font-weight:900;line-height:1.3">{head}</span><br><span style="font-size:26px">{sub}</span></div>')
    return (dassen(BG + "ロボ検.jpg", fly[0] + 0.05, fly[1] - 0.05, words="ロボ検、|!開幕。", word_style="top:40px;font-size:130px", src_note=GEM)
            + paper(tag("もう1つの違い") + '<div class="stack" style="top:100px;gap:24px">'
                    + qline("ここまで変えたら、この検査", T(38, 1.6), size=44)
                    + '<div class="row2" style="gap:30px">'
                    + col("fact", "車　ある", "受け皿の制度がある", T(38, 4.0))
                    + col("hb", "屋内のロボット　ない", "決まった手続きがない", T(38, 6.0))
                    + '</div>'
                    + '<div class="row2" style="gap:24px;align-items:center">'
                    + card("hb", "ロボ検を作る", T(39, 0.4), "ハコブくん", w=360, botsu=T(41, 0.4), fade=T(41, 1.0), style="font-size:30px")
                    + card("mi", inkl("誰が検査するの、それ", T(40, 0.2), 0.06), T(40, 0.05), "エイイチ", w=460, anim="wipe")
                    + '</div>'
                    + card("fact", inkl("改造した人が、安全を自分で説明する", T(42, 0.6), 0.05), T(42, 0.3), "手続きがない分", w=760)
                    + '</div>', 0.0, flights=[fly]))
block("rule", [38, 39, 40, 41, 42], rule)


def value(T, E):
    chip = lambda txt, t: (f'<div class="card k-fact" style="width:300px;opacity:0;animation:pop .45s ease-out {t:.3f}s forwards;font-size:32px;padding:20px 10px;white-space:nowrap">{txt}</div>')
    return (paper(tag("いちばん大事な所") + '<div class="center" style="gap:30px">'
                  + f'<div class="serif" style="font-size:44px;font-weight:700">{ink("配膳ロボットの値打ちは、", T(44, 0.2), 0.06)}{land("ソフトの側", T(44, 1.4), "font-size:84px;font-weight:900")}</div>'
                  + f'<div class="row2" style="gap:22px">{chip("地図", T(44, 2.6))}{chip("よけ方", T(44, 3.3))}{chip("遠くからの見守り", T(44, 4.0))}</div>'
                  + card("mi", inkl("アップデートに追いつかない", T(43, 0.3), 0.06), T(43, 0.1), "エイイチの言葉が、本題だった", w=620, anim="wipe")
                  + card("hb", "すれ違い方・迷い方も、ここ", T(45, 0.4), "最初に直したいと言っていた所", w=620, style="font-size:34px;font-weight:700")
                  + '</div>', 0.0))
block("value", [43, 44, 45], value)

# ================= 4 やり取り2：口を開けると、穴も開く（申請書2枚目 →「却下」・脱線③「扉、全開。」） =================
def port(T, E):
    fly = (T(48, S(48) - 0.9), T(49, 0.3))
    return (dassen(BG + "扉全開.jpg", fly[0] + 0.05, fly[1] - 0.05, words="扉、|!全開。", word_style="top:60px;font-size:140px", src_note=GEM)
            + paper(tag("やり取り 2 ／ 外からつなげる口") + '<div class="stack" style="top:84px;gap:22px">'
                    + f'<div style="opacity:1;animation:hide .3s linear {T(47, 0.0):.3f}s forwards;position:absolute;top:30px;left:0;right:0;display:flex;justify-content:center">'
                    + form(2, ["外からつなげる口を、全部開ける", "誰とでも仲良くする"], T(46, 0.2), [T(46, 1.0), T(46, 3.4)]) + '</div>'
                    + f'<div style="opacity:0;animation:show .3s linear {T(47, 0.0):.3f}s forwards;display:flex;flex-direction:column;align-items:center;gap:26px">'
                    + f'<div class="serif" style="font-size:46px;font-weight:700">{ink("口を増やすほど、", T(47, 0.1), 0.07)}{land("穴も増える", T(47, 1.0), "font-size:84px;font-weight:900")}</div>'
                    + card("fact", inkl("2025年、ある配膳ロボットの会社の外部接続は、鍵は求めるのに、／その機体の持ち主かを確かめていなかった", T(48, 0.4), 0.04), T(48, 0.15), "報告されていること", w=900)
                    + '<div class="row2" style="gap:24px;align-items:center">'
                    + card("mi", inkl("よその店のロボットまで、／動かせたってこと？", T(49, 0.4), 0.06), T(49, 0.2), "エイイチ", w=480, anim="wipe")
                    + card("hb", "運ぶ物によっては、笑えない", T(50, 0.4), None, w=430, style="font-size:30px")
                    + '</div>'
                    + card("fact", inkl("開けるのは、メーカーが用意した窓口だけ。／それでも、更新で作り直しになることがある", T(51, 0.4), 0.045), T(51, 0.15), None, w=860)
                    + '</div></div>' + src("出どころ：見つけた人の報告・The Register（2025-08-29）。手口は扱わない", T(48, 0.3)), 0.0, flights=[fly])
            + hanko("却下", E(46) - 0.9, "left:1130px;top:180px", size=80, t_hide=T(47, 0.0))
            + shake_js([(E(46) - 0.55, 0.3)]))
block("port", [46, 47, 48, 49, 50, 51], port)

# ================= 5 山場：満足できない人が増えたら =================
def peak(T, E):
    names = ["有料の性能解放", "非公認の解放業者", "アプリの市場", "公認の調整業者", "修理する権利", "汎用の土台＋自前のソフト", "サポート切れ機の再生", "追加学習"]
    tile = lambda k, txt, t: (f'<div class="card {"k-fact" if k == 7 else "k-hb"}" style="width:220px;height:110px;display:flex;align-items:center;justify-content:center;'
                              f'opacity:0;animation:pop .3s ease-out {t:.3f}s forwards;font-size:24px;padding:8px 10px;font-weight:700">{txt}</div>')
    grid = '<div style="display:grid;grid-template-columns:repeat(4,220px);gap:14px">' + "".join(tile(k, n, T(54, 2.6 + 0.35 * k)) for k, n in enumerate(names)) + '</div>'
    return (paper(tag("満足できない人が増えたら") + '<div class="stack" style="top:84px;gap:22px">'
                  + card("mi", inkl("ロボットが一般的になったら、／標準の性能だけじゃ、満足できない人も出てくる", T(53, 0.4), 0.045), T(53, 0.15), "見立て ・ エイイチ", w=860, anim="wipe")
                  + f'<div class="serif" style="font-size:40px;font-weight:700">{ink("考えられる形は、", T(54, 1.0), 0.06)}{land("8つ", T(54, 2.0), "font-size:72px;font-weight:900")}</div>'
                  + grid
                  + '<div class="row2" style="gap:24px;align-items:center">'
                  + card("mi", inkl("細かいのは、記事に書いといて", T(55, 0.3), 0.06), T(55, 0.1), "エイイチ", w=520, anim="wipe")
                  + card("hb", 'もう書いてあります<div style="font-size:22px;color:#5a564f;margin-top:2px">pixelstomotors.com ／ LOG406</div>', T(56, 0.2), None, w=400, style="font-size:30px")
                  + '</div></div>', 0.0))
block("peak", [52, 53, 54, 55, 56], peak)


def unlock(T, E):
    fly = (T(58, S(58) + 0.05), T(59, 0.3))
    return (dassen(BG + "課金で解放.jpg", fly[0] + 0.05, fly[1] - 0.05, words="課金で|!解放。", word_style="top:60px;font-size:140px", src_note=GEM)
            + paper(tag("車では、もう起きている") + '<div class="stack" style="top:90px;gap:22px">'
                    + line("Model Y（2025年）　部品は替えずに、ソフトの更新だけで速くなる", T(58, 0.6), size=30, weight=700)
                    + f'<div class="num" style="font-size:170px;line-height:1.05"><span style="font-size:80px">約$</span><span id="c2k">0</span></div>'
                    + f'<div class="serif" style="font-size:48px;font-weight:900">{land("同じ体のまま、お金で速くなる", T(59, 0.4))}</div>'
                    + '<div class="row2" style="gap:22px;align-items:center">'
                    + card("mi", inkl("ロボットでもやりそう", T(60, 0.2), 0.06), T(60, 0.05), "エイイチ", w=420, anim="wipe")
                    + card("hb", "払いますか？", T(61, 0.1), "ハコブくん", w=300, style="font-size:32px")
                    + card("mi", f'<span style="font-size:52px;font-weight:900">{ink("払います", T(62, 0.1), 0.06)}</span>', T(62, 0.0), "エイイチ", w=300, anim="wipe")
                    + '</div></div>' + src("出どころ：EVANNEX（2025-06-02）。米国での価格", T(58, 0.6)), 0.0, flights=[fly])
            + stamp("即答", T(63, 0.05), "left:1330px;top:300px;font-size:60px")
            + shake_js([(T(63, 0.4), 0.3)])
            + counter_js("c2k", T(58, 2.4), T(58, 4.2), 2000, dec=0))
block("unlock", [57, 58, 59, 60, 61, 62, 63], unlock)


def learn(T, E):
    return paper(tag("8つのうち、長い目で見た主役") + '<div class="center" style="gap:26px">'
                 + card("hb", inkl("動きを学習で作るロボットが広がれば、／長い目で見た主役は、最後の1つ", T(64, 0.5), 0.045), T(64, 0.2), "Claude の見立て", w=880)
                 + bigline("追加学習", T(65, 1.6), size=140)
                 + line("自分の店のお手本を足して、動きを学ばせ直す", T(65, 0.4), size=32, weight=500)
                 + f'<div class="serif" style="font-size:52px;font-weight:900;margin-top:10px">{ink("改造は、ネジから、", T(66, 0.4), 0.08)}{land("お手本へ。", T(66, 1.8), "font-size:70px")}</div>'
                 + '</div>', 0.0)
block("learn", [64, 65, 66], learn)


def learn_peak(T, E):
    return ('<div style="position:absolute;inset:0;background:#141311"></div>'
            '<div class="paper" style="opacity:.08"></div>'
            '<div class="center" style="left:430px;width:1060px;right:auto;top:64px;height:780px;bottom:auto;gap:40px">'
            '<p class="v" style="color:#e9e5dc;font-size:60px;line-height:1.7;font-weight:500;letter-spacing:.08em;">'
            f'<span data-ink="{T(67, 0.5):.2f},0.1">追加学習の形に、</span><br>'
            f'{land("魅力", T(67, 2.4), "font-weight:900;font-size:96px")}<span data-ink="{T(67, 2.9):.2f},0.1">がある。</span></p>'
            f'<div style="{SANS}font-size:26px;color:#bdb7ab;opacity:0;animation:show .3s linear {T(67, 3.4):.3f}s forwards">エイイチ</div>'
            '</div>'
            + stamp("エイイチの推し", T(67, S(67) + 0.1), "left:1080px;top:600px;font-size:44px")
            + shake_js([(T(67, S(67) + 0.45), 0.3)]))
block("learn_peak", [67, 68], learn_peak)

# ================= 6 やり取り3：半分開いたロボット =================
def open_(T, E):
    box = lambda head, sub, kind, t: (f'<div class="card k-{kind}" style="width:420px;opacity:0;animation:pop .45s ease-out {t:.3f}s forwards">'
                                      f'<span class="serif" style="font-size:44px;font-weight:900;line-height:1.3">{head}</span><br><span style="font-size:26px">{sub}</span></div>')
    return (paper(tag("やり取り 3 ／ 半分開いたロボット") + '<div class="stack" style="top:76px;gap:14px">'
                  + card("mi", inkl("オープン型のロボットも出てきそう。／市場が大きくなったら、差別化で。競争が激しくなるかも", T(69, 0.4), 0.045), T(69, 0.15), "見立て ・ エイイチ", w=880, anim="wipe")
                  + bigline("もう、出ていた", T(70, 0.2), size=56)
                  + line("Unitree（中国のヒューマノイド・四足ロボットのメーカー）", T(71, 0.6), size=26, weight=500, style="color:#5a564f")
                  + line("関節ごとの細かい動きまで、開けている", T(71, 3.0), size=32, weight=700)
                  + line("でも、人型ロボット G1 の、機体の中の計算機は", T(72, 0.6), size=28, weight=500)
                  + '<div class="row2" style="gap:30px">'
                  + box("計算機 1", "メーカーのサービス専用。開発には使えない", "fact", T(72, 3.6))
                  + box("計算機 2", "開発に使える", "hb", T(72, 4.6))
                  + '</div>'
                  + '</div>' + src("出どころ：Unitree G1 開発の手引き", T(71, 0.6)), 0.0))
block("open", [69, 70, 71, 72], open_)


def open2(T, E):
    seg = lambda txt, bg, fg, t: (f'<div class="seg" style="width:440px;padding:60px 10px;font-size:54px;font-family:\'Noto Serif CJK JP\',serif;font-weight:900;'
                                  f'background:{bg};color:{fg};clip-path:inset(0 100% 0 0);animation:wipe .45s ease-out {t:.3f}s forwards">{txt}</div>')
    return paper(tag("Claude の読み") + '<div class="center" style="gap:34px">'
                 + '<div style="display:flex">' + seg("安全は、閉じる", "#22201c", "#f5f3ee", T(73, 1.6)) + seg("動きは、開く", "#ece9e2", "#22201c", T(73, 4.0)) + '</div>'
                 + line("転ばない・止まる、はメーカー。何をどう動かすか、を開ける", T(73, 5.2), size=28, weight=500, style="color:#5a564f")
                 + card("hb", "スマホの iPhone と Android の関係に近い", T(74, 0.4), "Claude の見立て", w=720, style="font-size:32px")
                 + '</div>', 0.0)
block("open2", [73, 74], open2)


def maker(T, E):
    return (paper(tag("もしメーカーなら") + '<div class="center" style="gap:28px">'
                  + card("hb", "エイイチがメーカーなら、どこまで開けますか？", T(75, 0.1), "ハコブくん", w=760, style="font-size:32px")
                  + card("mi", f'<span style="font-size:46px;font-weight:900">{inkl("全部知りたいから、／できる限り分解する", T(76, 0.3), 0.06)}</span>', T(76, 0.1), "エイイチ", w=760, anim="wipe")
                  + f'<div class="serif" style="font-size:44px;font-weight:700">{ink("それは、", T(77, 0.6), 0.08)}{land("開けたい側", T(77, 2.0), "font-size:80px;font-weight:900")}{ink("の答え", T(77, 2.6), 0.08)}</div>'
                  + '</div>', 0.0))
block("maker", [75, 76, 77], maker)


def profit(T, E):
    return (paper(tag("値打ちは、どこへ") + '<div class="center" style="gap:30px">'
                  + card("mi", f'<span style="font-size:44px;font-weight:700">{inkl("利益が残るのは、／作る側より、入れて回す側", T(78, 0.3), 0.06)}</span>', T(78, 0.1), "見立て ・ エイイチ", w=820, anim="wipe")
                  + card("hb", inkl("機体は値下がりし、値打ちは、／学習させた動きと、現場に入れて回す所へ", T(79, 0.6), 0.045), T(79, 0.3), "Claude の見立て（かもしれない）", w=860)
                  + '</div>', 0.0)
            + stamp("この見立ては、エイイチから", T(78, S(78) + 0.1), "left:1010px;top:150px")
            + shake_js([(T(78, S(78) + 0.45), 0.3)]))
block("profit", [78, 79], profit)

# ================= 7 まとめ（散らばった面が3行の表に収まる） =================
SUM_CSS = """@keyframes gather { from { opacity:0; transform: translate(var(--dx),var(--dy)) rotate(var(--r)); } to { opacity:1; transform: none; } }
.trow3 { display:grid; grid-template-columns: 200px 1fr 260px; align-items:center; width:940px; padding:14px 26px; font-family:'Noto Sans CJK JP',sans-serif; font-size:28px; font-weight:500; text-align:left; gap:14px; }
.trow3 b { font-weight:900; font-size:30px; }
.trow3 i { font-style:normal; font-weight:700; }"""


def summ(T, E):
    row = lambda a, b, c, bg, fg, dx, dy, r, t: (f'<div class="trow3" style="background:{bg};color:{fg};--dx:{dx}px;--dy:{dy}px;--r:{r}deg;opacity:0;'
                                                f'animation:gather .9s cubic-bezier(.3,1.2,.4,1) {t:.3f}s forwards"><b>{a}</b><span>{b}</span><i>{c}</i></div>')
    head = (f'<div class="trow3" style="color:#5a564f;font-size:22px;padding:0 26px;opacity:0;animation:show .3s linear {T(81, 0.0):.3f}s forwards">'
            '<b style="font-size:22px">改造の形</b><span>中心</span><i>責任を負うのは</i></div>')
    b3 = lambda txt, t: card("hb", txt, t, None, w=290, botsu=t + 0.25, style="font-size:24px;padding:10px 12px;white-space:nowrap")
    return ('<style>' + SUM_CSS + '</style>'
            + paper(tag("ここまでを、1枚に") + '<div class="stack" style="top:76px;gap:10px">'
                    + head
                    + row("これまで", "機体・ソフトの書き換え", "改造した人", "#22201c", "#f5f3ee", 420, 420, 8, T(81, 0.4))
                    + row("公認の範囲", "設定・外部接続・認められた業者", "メーカーと業者で分ける", "#dcd6c9", "#22201c", 300, -60, -6, T(82, 0.4))
                    + row("これから", "現場のお手本で追加学習", "まだ決まっていない", "#ece9e2", "#22201c", -420, 180, 10, T(83, 0.4))
                    + f'<p class="serif" style="margin-top:14px;text-align:center;font-size:36px;line-height:1.55;font-weight:900;">'
                      f'{ink("改造の行き先は、メーカーが開けた範囲で、", T(84, 2.6), 0.05)}<br>{land("自分の現場に合う動きを、学ばせること。", T(84, 5.0), "font-size:46px")}</p>'
                    + f'<div class="row2" style="gap:20px;margin-top:8px">{b3("5段目まで一気に", T(85, 1.6))}{b3("ロボ検", T(85, 2.0))}{b3("口を全部開ける", T(85, 2.4))}</div>'
                    + '</div>', 0.0))
block("sum", [80, 81, 82, 83, 84, 85], summ)

# ================= 8 残った問い =================
def rest(T, E):
    return paper(tag("残った問い") + '<div class="stack" style="top:70px;gap:14px">'
                 + f'<div class="serif" style="font-size:44px;font-weight:700;line-height:1.5">{ink("あとから学ばせた動きを、", T(87, 1.8), 0.06)}<br>'
                   f'{land("誰が確かめるのか", T(87, 3.4), "font-size:64px;font-weight:900")}</div>'
                 + line("決まりは動き始めているが、ここはまだ、はっきりしない", T(87, 0.6), size=26, weight=500, style="color:#5a564f")
                 + card("mi", inkl("エンジニアと、店の社員やスタッフが、一緒に確かめるのがいちばん。／ただ実際は、コストの面で難しそう", T(89, 0.4), 0.045), T(89, 0.15), "エイイチ", w=900, anim="wipe")
                 + f'<div class="serif" style="font-size:40px;font-weight:700">{ink("良い形と、払える形が、", T(90, 0.2), 0.06)}{land("離れている", T(90, 1.6), "font-size:60px;font-weight:900")}</div>'
                 + card("hb", '考察ノートの全文　<span style="font-size:24px;color:#5a564f">pixelstomotors.com ／ LOG406</span>', T(91, 0.3), "サイトの記事", w=700, style="font-size:28px;padding:10px 30px")
                 + '</div>', 0.0)
block("rest", [86, 87, 88, 89, 90, 91], rest)


def ask(T, E):
    return paper(tag("あなたの店では") + '<div class="center" style="gap:12px">'
                 + qline("あなたの店のロボット、", T(92, 0.2), size=56)
                 + qline("どこまで触っていいか、", T(92, 1.4), size=56)
                 + qline("契約書に書いてありますか。", T(92, 2.8), size=56)
                 + '</div>', 0.0)
block("ask", [92], ask)


def end(T, E):
    return (paper(tag("最後に") + '<div class="center" style="gap:30px">'
                  + form(3, ["病院の廊下で、人とすれ違う動きを、追加学習する"], T(93, 0.6), [T(93, 2.2)])
                  + card("mi", inkl("……それは、ありかもね", T(94, 0.3), 0.07), T(94, 0.1), "エイイチ", w=480, anim="wipe")
                  + '</div>', 0.0)
            + hanko("保留", T(94, S(94) + 0.1), "left:1120px;top:170px", size=80)
            + shake_js([(T(94, S(94) + 0.45), 0.3)]))
block("end", [93, 94, 95], end)

# ================= 9 アウトロ =================
block("outro", [96, 97, 98], None, video=("src/day124_crop.mp4", 18.0, "png/ovl/outro.png"))

assert [i for b in BLOCKS for i in b["sents"]] == list(range(N)), "場面の文の番号が抜けている・重なっている"

# ================= ハコブくん =================
# 表情は narration.py の表情が基本。ここは文の途中で替える所: (文, 文の頭から何秒, 何秒間, 表情)
HK_FACE = [
    (2, 0.0, 2.5, "doubt"),
    (3, 0.0, 1.5, "trouble"),                 # 却下
    (22, S(22), 1.2, "surprise"),             # 一気に → 脱線
    (23, 0.0, 1.0, "surprise"),
    (29, 0.0, 2.0, "surprise"),
    (36, 0.0, 1.5, "surprise"),
    (41, 0.0, 2.2, "dot"),                    # ボツ
    (46, S(46), 1.4, "trouble"),              # 却下（2枚目）
    (55, 0.0, 2.0, "trouble"),
    (62, 0.0, 1.0, "surprise"),
    (67, 0.0, 6.0, "think"),
    (76, 0.0, 3.0, "surprise"),
    (94, S(94), 1.6, "trouble"),              # 保留……
]
HK_FACE = [(A(i, a), A(i, a) + d, f) for i, a, d, f in HK_FACE]

# 体ごとの動き: jump＝驚いて全身で飛び出す / fall＝前のめりにコケる（up で起き上がる） / reflex＝のけぞる
HK_MOVES = [
    dict(kind="reflex", t0=A(3, 0.2), dur=0.9),                          # 却下でのけぞる
    dict(kind="fall", t0=A(41, 0.2), t1=A(42, 0.2), dur=0.5),             # ロボ検ボツでコケる
    dict(kind="jump", t0=A(68, 0.0), dur=1.9),                           # 「……それです」で飛び出す
]
HK_ALPHA = [(A(67, 0.0), tl.end(68) - 2.0, 0.3),                          # 暗い場面では薄く（飛び出す前まで）
            (A(22, S(22) - 0.15), A(23, -0.05), 0.0),                     # 自分で改造の絵にはハコブくんが描いてあるので、重ねない
            (A(39, S(39) - 0.25), A(40, 0.05), 0.0)]                      # ロボ検の絵も同じ

# ================= エイイチ =================
# (文, 文の頭から何秒, 姿) 1腕組み 2うなずく 3指さす 4首をかしげる 5ひらめく 6のけぞって驚く 7半目でツッコむ 8吹き出して笑う
EI_POSE = [
    (0, 0.0, 1), (1, 0.6, 6), (2, 0.0, 7), (3, 0.0, 1), (4, 0.0, 2), (6, 0.0, 4), (7, 0.0, 7), (8, 0.0, 2), (9, 0.0, 1),
    (12, 0.0, 2), (13, 0.0, 1),
    (14, 0.0, 1), (15, 0.0, 3), (16, 0.0, 2), (17, 0.0, 4), (18, 0.0, 3), (19, 0.0, 2),
    (20, 0.0, 1), (22, 0.3, 6), (23, 0.0, 1), (24, 0.0, 2), (25, 0.0, 3), (26, 0.0, 2),
    (27, 0.0, 1), (28, 0.0, 3), (29, 0.0, 8), (30, 0.0, 4), (31, 0.0, 3), (32, 0.0, 2), (33, 0.0, 4),
    (34, 0.0, 1), (35, 0.0, 4), (36, 0.0, 6), (37, 0.0, 1), (37, 3.0, 2),
    (38, 0.0, 1), (39, 0.4, 6), (40, 0.0, 7), (41, 0.3, 8), (42, 0.0, 2),
    (43, 0.0, 5), (44, 0.0, 2), (45, 0.0, 3),
    (46, 0.0, 4), (46, 2.5, 7), (47, 0.0, 1), (48, 0.0, 4), (49, 0.0, 6), (50, 0.0, 1), (51, 0.0, 2),
    (52, 0.0, 1), (53, 0.0, 3), (54, 0.0, 4), (55, 0.0, 7), (56, 0.0, 8),
    (57, 0.0, 1), (58, 0.0, 4), (59, 0.0, 6), (60, 0.0, 3), (61, 0.0, 1), (62, 0.0, 3), (63, 0.0, 8),
    (64, 0.0, 1), (65, 0.0, 2), (66, 0.0, 2), (67, 0.0, 5), (68, 0.0, 3),
    (69, 0.0, 3), (70, 0.0, 6), (71, 0.0, 1), (72, 0.0, 4), (73, 0.0, 2), (74, 0.0, 2),
    (75, 0.0, 4), (76, 0.0, 3), (77, 0.6, 8),
    (78, 0.0, 5), (79, 0.0, 2),
    (80, 0.0, 1), (81, 0.0, 2), (84, 0.0, 2), (85, 0.8, 8),
    (86, 0.0, 1), (87, 0.0, 4), (88, 0.0, 1), (89, 0.0, 3), (90, 0.0, 2), (91, 0.0, 2),
    (92, 0.0, 3), (93, 0.0, 4), (94, 0.0, 2), (95, 0.0, 3),
    (96, 0.0, 2),
]
EI_POSE = sorted((A(i, a), p) for i, a, p in EI_POSE)
# 置き方の替え: (開始, 終了, 置き方)。hide＝出さない / big＝暗い場面で大きく光らせる
EI_PLACE = [
    (A(10), tl.end(11), "hide"),
    (A(67), tl.end(67), "big"),
]

# ================= 効果音 =================
# (文, 文の頭から何秒, 音, ナレーションより何dB)
SE = [
    (0, 0.3, "se_pop", -12), (1, 0.2, "se_pop", -16), (1, 0.6, "se_pop", -16), (1, 1.0, "se_pop", -16),
    (3, 0.25, "se_pop", -6),                                                     # 却下
    (9, 1.8, "se_pop", -12),
    (22, S(22) - 0.2, "se_whoosh", -10), (23, -0.05, "se_whoosh", -14), (22, 99, "se_pop", -12),
    (23, 0.3, "se_pop", -10),                                                    # 切れ目
    (33, 0.2, "se_pop", -12),
    (39, S(39) - 0.3, "se_whoosh", -10), (40, 0.0, "se_whoosh", -14), (41, 0.2, "se_whoosh", -12), (41, 0.45, "se_pop", -6), (41, 0.4, "se_pop", -12),
    (46, 0.2, "se_pop", -12), (46, S(46) - 0.0, "se_pop", -6),                   # 申請書2枚目・却下
    (48, S(48) - 0.9, "se_whoosh", -10), (49, 0.3, "se_whoosh", -14),
    (54, 2.0, "se_pop", -12),
    (58, S(58) + 0.05, "se_whoosh", -10), (59, 0.3, "se_whoosh", -14), (63, 0.05, "se_pop", -8),
    (65, 1.6, "se_pop", -10),
    (67, S(67) + 0.1, "se_pop", -8), (68, 0.0, "se_rise", -10),
    (70, 0.2, "se_pop", -12),
    (78, S(78) + 0.1, "se_pop", -8),
    (85, 1.85, "se_pop", -14), (85, 2.25, "se_pop", -14), (85, 2.65, "se_pop", -14),
    (93, 0.6, "se_pop", -12), (94, S(94) + 0.1, "se_pop", -6),                   # 保留
]
SE = [s for s in SE if s[1] != 99]

# 一覧の画像に撮る時刻（場面の頭からの秒）。書かない場面は最後の0.4秒前
def _rel(b, i, off=0.0):
    return tl.start(i) - _tl.start(b["sents"][0]) + off
SHEET = {}
for _b in BLOCKS:
    _end = _tl.end(_b["sents"][-1]) - _tl.start(_b["sents"][0]) - 0.4
    SHEET[_b["name"]] = [_end]
_B = {b["name"]: b for b in BLOCKS}
SHEET["hook"] = [_rel(_B["hook"], 2, 1.5), SHEET["hook"][0]]
SHEET["steps"] = [_rel(_B["steps"], 21, 7.5), _rel(_B["steps"], 22, S(22) + 0.6), SHEET["steps"][0]]
SHEET["rule"] = [_rel(_B["rule"], 39, S(39) + 0.4), _rel(_B["rule"], 41, 0.6), SHEET["rule"][0]]
SHEET["port"] = [_rel(_B["port"], 46, S(46) + 0.2), _rel(_B["port"], 48, S(48) - 0.2), SHEET["port"][0]]
SHEET["unlock"] = [_rel(_B["unlock"], 58, S(58) + 0.6), SHEET["unlock"][0]]
SHEET["learn_peak"] = [_rel(_B["learn_peak"], 67, S(67) + 0.5), _rel(_B["learn_peak"], 68, 0.8)]
SHEET["end"] = [SHEET["end"][0]]
SHEET["title"] = [2.0]
