# -*- coding: utf-8 -*-
"""研究のスキル一覧（ショート専用・舞台型の試作1本目・2026-11-06）
奥のモニターに1つずつ出す。中身はエイイチが実際に使っている仕組みだけ（誇張しない）。
#6 のセリフはエイイチの言葉「今まで煩わしかったターミナルでの作業から解放されたこと」から。
"""
import json, os
from kabe_kit import shake_js

NAME, VER = "研究のスキル一覧", 5
HEAD = "ひとりで<br>ロボット研究？"
LEAD = 0.8
STAGE = "stage"
BG_NAME = "stage_bg_noring"   # 床の円を消した舞台（円は OVER で回しながら描く）
T_ON = (0, 1.3)
FOOT_L, FOOT_R, SIZE, SUB_MID = (390, 1525), (690, 1525), 0.88, 1590
SUB_ONE_LINE = True   # 字幕は1行ずつにして、箱を足もとの下に収める   # 円の奥寄りに、2人を寄せて立たせる（版2: エイイチ「距離感もっと近くて良さそう」）

ORANGE = "#E2683A"       # 暗い画面の上で読めるよう、#C4471F を明るくした。色の意味は「いま話している1つ」だけ
FACE_C, DIM, INK = "#1B2433", "#8A93A3", "#F5F3EE"
ITEMS = ["運用分析", "GPUで学習", "依頼箱", "課題地図", "事実確認", "声の単語帳", "記録→記事"]

# 画面の決まり（900×600）: 左上に「番号 / 07」と名前、真ん中に言いたいことを1つ、図は面（ベタ）で。線・枠・記号のアイコンは使わない
CSS = f"""<style>
.win {{ position:absolute; inset:0; opacity:0; }}
.no {{ position:absolute; left:48px; top:40px; display:flex; align-items:baseline; gap:14px; font-weight:900; }}
.no b {{ font-size:40px; color:{ORANGE}; letter-spacing:.02em; }}
.no small {{ font-size:22px; color:{DIM}; }}
.no span {{ font-size:26px; color:{DIM}; font-weight:700; margin-left:10px; }}
.msg {{ position:absolute; left:48px; right:48px; top:110px; font-size:62px; font-weight:900; line-height:1.2; color:{INK}; }}
.rows {{ position:absolute; right:48px; top:46px; width:430px; display:flex; flex-direction:column; gap:10px; }}
.row {{ height:64px; background:{FACE_C}; border-radius:10px; display:flex; align-items:center; padding:0 22px; gap:18px; font-size:30px; font-weight:800; color:{INK}; opacity:0; }}
.row em {{ font-style:normal; font-size:22px; font-weight:900; color:{DIM}; width:30px; }}
.seven {{ position:absolute; left:48px; top:120px; opacity:0; }}
.seven b {{ display:block; font-size:300px; font-weight:900; color:{ORANGE}; line-height:.9; letter-spacing:-.04em; }}
.seven span {{ display:block; font-size:44px; font-weight:900; color:{INK}; margin-top:48px; }}
.track {{ position:absolute; left:48px; right:48px; top:330px; height:84px; display:flex; gap:6px; }}
.track i {{ display:block; height:100%; border-radius:8px; background:#2C3647; }}
.track i.st {{ background:#2C3647; }}
.head {{ position:absolute; top:312px; width:8px; height:120px; border-radius:4px; background:{INK}; left:48px; }}
.tag {{ position:absolute; top:440px; width:140px; text-align:center; font-size:28px; font-weight:900; color:{ORANGE}; opacity:0; }}
.steps {{ position:absolute; left:48px; right:48px; top:300px; display:flex; gap:16px; }}
.step {{ flex:1; height:190px; border-radius:12px; background:{FACE_C}; display:flex; flex-direction:column; justify-content:center; padding:0 28px; color:{DIM}; }}
.step em {{ font-style:normal; font-size:24px; font-weight:900; }}
.step b {{ font-size:54px; font-weight:900; margin-top:6px; }}
.chk {{ position:absolute; left:48px; right:48px; height:76px; border-radius:10px; background:{FACE_C}; display:flex; align-items:center; gap:22px; padding:0 22px;
        font-size:36px; font-weight:800; color:{INK}; opacity:0; }}
.chk i {{ font-style:normal; width:48px; height:48px; border-radius:50%; display:flex; align-items:center; justify-content:center; font-size:30px; font-weight:900; background:#3A4558; color:{INK}; }}
.chk.ng i {{ background:{ORANGE}; }} .chk.ng {{ color:{ORANGE}; }}
.lane {{ position:absolute; top:230px; width:256px; height:150px; border-radius:12px; background:{FACE_C}; }}
.lane b {{ position:absolute; left:22px; top:16px; font-size:26px; font-weight:900; color:{DIM}; }}
.job {{ position:absolute; top:296px; width:212px; height:64px; border-radius:10px; background:{ORANGE}; color:#fff; font-size:28px; font-weight:900;
        display:flex; align-items:center; justify-content:center; left:70px; }}
.term {{ position:absolute; left:48px; right:48px; top:410px; height:130px; border-radius:12px; background:#06090E; padding:22px 28px;
        font-family:'Noto Sans Mono CJK JP',monospace; font-size:28px; line-height:1.55; color:#6F7A8C; }}
.strike {{ position:absolute; left:34px; right:34px; top:470px; height:10px; border-radius:5px; background:{ORANGE}; transform-origin:left; transform:scaleX(0); }}
.free {{ position:absolute; left:60px; top:532px; background:{ORANGE}; color:#fff; font-size:44px; font-weight:900; padding:4px 24px; border-radius:10px; opacity:0; }}
.url {{ position:absolute; left:0; right:0; top:230px; text-align:center; font-size:76px; font-weight:900; color:{INK}; letter-spacing:-.01em; }}
.url span {{ color:{ORANGE}; }}
.sub {{ position:absolute; left:0; right:0; top:350px; text-align:center; font-size:36px; font-weight:800; color:{DIM}; }}
.qm {{ position:absolute; left:0; right:0; top:60px; text-align:center; font-size:400px; font-weight:900; line-height:1; color:{ORANGE}; }}
@keyframes show {{ to {{ opacity:1; }} }}
@keyframes hide {{ to {{ opacity:0; }} }}
@keyframes rowIn {{ from {{ opacity:0; transform:translateX(40px); }} to {{ opacity:1; transform:none; }} }}
@keyframes dimR {{ to {{ opacity:.22; }} }}
@keyframes hiR {{ to {{ background:{ORANGE}; color:#fff; transform:scale(1.06); }} }}
@keyframes up {{ from {{ opacity:0; transform:translateY(24px); }} to {{ opacity:1; transform:none; }} }}
@keyframes on {{ to {{ background:{ORANGE}; color:#fff; }} }}
@keyframes stopOn {{ to {{ background:{ORANGE}; }} }}
@keyframes play {{ to {{ left:844px; }} }}
@keyframes strike {{ to {{ transform:scaleX(1); }} }}
@keyframes move3 {{ 0%,15% {{ left:70px; }} 45%,60% {{ left:342px; }} 90%,100% {{ left:614px; }} }}
@keyframes slam {{ 0% {{ opacity:0; transform:rotate(-6deg) scale(2.2); }} 80% {{ opacity:1; transform:rotate(-6deg) scale(.96); }} 100% {{ opacity:1; transform:rotate(-6deg) scale(1); }} }}
@keyframes pop {{ 0% {{ opacity:0; transform:scale(.6); }} 70% {{ opacity:1; transform:scale(1.04); }} 100% {{ opacity:1; transform:none; }} }}
@keyframes breathe {{ 0%,100% {{ transform:scale(1); }} 50% {{ transform:scale(1.05); }} }}
</style>"""


def win(t0, t1, inner):
    an = f"show .15s linear {t0:.3f}s forwards" + (f", hide .15s linear {t1:.3f}s forwards" if t1 else "")
    return f'<div class="win" style="animation:{an}">{inner}</div>'


def no(n, name):
    return f'<div class="no"><b>{n:02d}</b><small>/ 07</small><span>{name}</span></div>'


def rows(t_in, step, t_pick=None, pick="依頼箱"):
    out = []
    for k, name in enumerate(ITEMS):
        an = f"rowIn .3s cubic-bezier(.2,1.3,.4,1) {t_in + k * step:.3f}s forwards"
        if t_pick is not None:
            an += f", hiR .35s ease-out {t_pick:.3f}s forwards" if name == pick else f", dimR .35s linear {t_pick:.3f}s forwards"
        out.append(f'<div class="row" style="animation:{an}"><em>{k + 1:02d}</em>{name}</div>')
    return '<div class="rows">' + "".join(out) + "</div>"


def scene(T, S):
    h = CSS
    # 0: 問い（モニターがついたら ？ が息をする）
    h += win(T(0, 1.3), T(1, 0.0), f'<div class="qm" style="animation:breathe .9s ease-in-out {T(0, 1.4):.3f}s 3">?</div>')
    # 1: 仕組みは7つ（左に大きな 7、右に7行）
    h += win(T(1, 0.0), T(2, 0.0),
             f'<div class="seven" style="animation:pop .4s ease-out {T(1, 0.9):.3f}s forwards"><b>7</b><span>つの仕組み</span></div>'
             + rows(T(1, 1.1), 0.14))
    # 2: 01 運用分析（動きの帯を再生ヘッドが進み、止まりが橙に変わる）
    t2 = T(2, 0.3); d2 = S(2) - 0.5
    segs = [(0.16, 0), (0.08, 1), (0.22, 0), (0.12, 1), (0.25, 0), (0.07, 1), (0.10, 0)]
    acc, tr, tg = 0.0, "", ""
    for w, stop in segs:
        ts = t2 + d2 * (acc + w)
        tr += f'<i style="flex:{w};' + (f'animation:stopOn .15s linear {ts:.3f}s forwards' if stop else '') + '"></i>'
        if stop:
            tg += f'<div class="tag" style="left:{48 + 804 * (acc + w / 2) - 70:.0f}px;animation:up .25s ease-out {ts:.3f}s forwards">止まり</div>'
        acc += w
    h += win(T(2, 0.0), T(3, 0.0), no(1, "運用分析") + '<div class="msg">ロボットの動画から、<br>止まった所を読む</div>'
             + f'<div class="track">{tr}</div>{tg}<div class="head" style="animation:play {d2:.2f}s linear {t2:.3f}s forwards"></div>')
    # 3: 02 GPUで学習（借りる → 学習 → 消す が順に橙になる）
    t3 = T(3, 0.0); s3 = S(3)
    st = "".join(f'<div class="step" style="animation:on .25s ease-out {tt:.3f}s forwards"><em>{k + 1}</em><b>{nm}</b></div>'
                 for k, (nm, tt) in enumerate([("借りる", t3 + 0.7), ("学習", t3 + 1.6), ("消す", t3 + s3 - 0.9)]))
    h += win(t3, T(4, 0.0), no(2, "GPUで学習") + '<div class="msg">借りて学習して、<br>終わったら消す</div>' + f'<div class="steps">{st}</div>')
    # 4: 05 事実確認（別のAIが1行ずつ確かめる。言い切りだけ ✗）
    t4 = T(4, 0.0)
    rr = "".join(f'<div class="chk{" ng" if c == "ng" else ""}" style="top:{290 + k * 92}px;animation:up .3s ease-out {t4 + 0.5 + k * 0.4:.3f}s forwards"><i>{"✕" if c == "ng" else "✓"}</i>{tx}</div>'
                 for k, (c, tx) in enumerate([("ok", "数字の出どころ"), ("ng", "根拠のない言い切り"), ("ok", "引用の原文")]))
    h += win(t4, T(5, 0.0), no(5, "事実確認") + '<div class="msg">別のAIに、疑わせる</div>' + rr)
    # 5: いちばん助かってるのは…（7行に戻り、依頼箱だけ橙で残る）
    h += win(T(5, 0.0), T(6, 0.0), '<div class="msg" style="width:360px;font-size:54px">いちばん<br>助かって<br>いるのは</div>'
             + rows(T(5, 0.0), 0.0, t_pick=T(5, 1.0)).replace('top:46px', 'top:46px'))
    # 6: 03 依頼箱（依頼が 新規→実行中→済み と進み、ターミナルの行に線が引かれる）
    t6 = T(6, 0.0); s6 = S(6)
    lanes = "".join(f'<div class="lane" style="left:{48 + k * 272}px"><b>{nm}</b></div>' for k, nm in enumerate(["新規", "実行中", "済み"]))
    h += win(t6, T(7, 0.0), no(3, "依頼箱") + '<div class="msg" style="font-size:44px">Claude Code に、自動で渡す</div>'
             + lanes + f'<div class="job" style="animation:move3 {s6 - 1.0:.2f}s ease-in-out {t6 + 0.4:.3f}s forwards">依頼</div>'
             + '<div class="term">$ cd ~/Robotics_UX/ServiceSim<br>$ claude -p "……"</div>'
             f'<div class="strike" style="animation:strike .35s ease-out {t6 + s6 - 1.9:.3f}s forwards"></div>'
             f'<div class="free" style="animation:slam .3s ease-out {t6 + s6 - 1.2:.3f}s forwards">解放！</div>')
    # 7: 研究の記録は毎日サイトで
    t7 = T(7, 0.0)
    h += win(t7, None,
             f'<div class="url" style="opacity:0;animation:pop .4s ease-out {t7 + 0.3:.3f}s forwards">pixelstomotors<span>.com</span></div>'
             f'<div class="sub" style="opacity:0;animation:up .4s ease-out {t7 + 1.0:.3f}s forwards">研究の記録を、毎日公開</div>')
    return h


RING = json.load(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "stage", "ring_lut.json")))
RING_SEC = 48.0          # 床の円が1周する秒数（ゆっくり回す・版3）


def OVER(T):
    """モニターの外に重ねるもの（版3）。床の円を回す・紙吹雪（7つ！と解放！）・解放！で画面が揺れる。
    円は Unity の舞台から消した床（stage_bg_noring.png）の上に、同じカメラの値で描き直す（stage_meta.json の camera）。"""
    bursts = [dict(t=T(1, 1.0), n=230), dict(t=T(6, 4.4), n=130)]
    js = f"""<canvas id="fx" width="1080" height="1920" style="position:absolute;left:0;top:0;z-index:3;pointer-events:none"></canvas>
<script>
(function(){{
const LUT={json.dumps(RING["lut"])}, FY={RING["fy"]:.4f}, C={json.dumps(RING["C"])}, PIT={RING["pitch"]}*Math.PI/180, SEC={RING_SEC};
const f1=-Math.sin(PIT), f2=Math.cos(PIT), u1=Math.cos(PIT), u2=Math.sin(PIT);
function P(x,z){{ const dx=x-C[0], dy=-C[1], dz=z-C[2]; const zc=dy*f1+dz*f2, yc=dy*u1+dz*u2; return [540+FY*dx/zc, 960-FY*yc/zc]; }}
function rnd(seed){{ return function(){{ seed|=0; seed=seed+0x6D2B79F5|0; let t=Math.imul(seed^seed>>>15,1|seed); t=t+Math.imul(t^t>>>7,61|t)^t; return ((t^t>>>14)>>>0)/4294967296; }}; }}
const COLS=["#E2683A","#F2C14E","#F5F3EE","#57B5F8","#FFB38A","#F2C14E","#E2683A"];
const BURSTS={json.dumps(bursts)};
const parts=[];
BURSTS.forEach((b,bi)=>{{ const r=rnd(7+bi*101);
  for(let i=0;i<b.n;i++){{ const side=i%2?1:-1; const x0=540+side*400, y0=1470;
    const ang=(-90+side*-28+(r()-.5)*44)*Math.PI/180, sp=1300+r()*1300;
    parts.push({{t0:b.t+r()*0.18, x0, y0, vx:Math.cos(ang)*sp, vy:Math.sin(ang)*sp, k:2.6+r()*1.4, g:900+r()*300,
      sh:(r()<.12?3:(r()<.25?1:(r()<.45?2:0))), w:5+r()*6, c:COLS[Math.floor(r()*COLS.length)], rs:(r()-.5)*14, r0:r()*6.28,
      fl:6+r()*10, ph:r()*6.28, sw:16+r()*26, life:2.6+r()*1.4}}); }} }});
const cv=document.getElementById('fx'), g=cv.getContext('2d');
function ring(t){{ const N=36, rot=t*2*Math.PI/SEC;
  for(let k=0;k<N;k++){{ const a0=rot+k*2*Math.PI/N, a1=a0+Math.PI/N, am=(a0+a1)/2;
    let idx=Math.floor((((am%(2*Math.PI))+2*Math.PI)%(2*Math.PI))/(2*Math.PI)*36)%36; const c=LUT[idx];
    const pts=[]; for(let j=0;j<=6;j++){{ const a=a0+(a1-a0)*j/6; pts.push(P(1.55*Math.cos(a),1.55*Math.sin(a))); }}
    for(let j=6;j>=0;j--){{ const a=a0+(a1-a0)*j/6; pts.push(P(1.45*Math.cos(a),1.45*Math.sin(a))); }}
    g.beginPath(); g.moveTo(pts[0][0],pts[0][1]); pts.slice(1).forEach(q=>g.lineTo(q[0],q[1])); g.closePath();
    g.fillStyle=`rgb(${{Math.min(255,c[0]*1.15)}},${{Math.min(255,c[1]*1.15)}},${{Math.min(255,c[2]*1.15)}})`; g.fill(); }} }}
function confetti(t){{ for(const p of parts){{ const u=t-p.t0; if(u<0||u>p.life) continue;
    const e=(1-Math.exp(-p.k*u))/p.k; let x=p.x0+p.vx*e, y=p.y0+(p.vy+p.g/p.k)*e-p.g*u/p.k;
    x+=p.sw*Math.sin(p.fl*0.5*u+p.ph)*Math.min(1,u/0.6);
    const al=u>p.life-0.6?(p.life-u)/0.6:1; g.save(); g.globalAlpha=al; g.translate(x,y); g.rotate(p.r0+p.rs*u);
    const fs=Math.max(.15,Math.abs(Math.cos(p.fl*u+p.ph)));
    if(p.sh===3){{ const tw=0.5+0.5*Math.sin(u*18+p.ph); g.fillStyle="#FFF7E0"; g.globalAlpha=al*tw; const s=p.w*1.3;
      g.beginPath(); g.moveTo(0,-s); g.lineTo(s*.25,-s*.25); g.lineTo(s,0); g.lineTo(s*.25,s*.25); g.lineTo(0,s); g.lineTo(-s*.25,s*.25); g.lineTo(-s,0); g.lineTo(-s*.25,-s*.25); g.closePath(); g.fill(); }}
    else {{ g.scale(1,fs); g.fillStyle=p.c;
      if(p.sh===1){{ g.beginPath(); g.arc(0,0,p.w*.55,0,6.283); g.fill(); }}
      else if(p.sh===2){{ g.fillRect(-p.w*.22,-p.w*1.6,p.w*.44,p.w*3.2); }}
      else {{ g.fillRect(-p.w/2,-p.w*.8,p.w,p.w*1.6); }} }}
    g.restore(); }} }}
window.hook=function(t){{ g.clearRect(0,0,1080,1920); ring(t); confetti(t); }};
window.hookBusy=function(t){{ return true; }};
window.hook(0);
}})();
</script>"""
    return js + shake_js([(T(6, 4.4), 0.6)], amp=7)


FACE = [(-1, 0.0, 1.0, "joy"), (1, 0.0, 2.0, "joy"), (4, 0.0, 2.5, "wink"), (6, 3.5, 2.5, "joy"), ("end", 0.0, 3.0, "smile")]
POSE = [(-1, 0.0, 1), (1, 0.6, 3), (2, 0.0, 2), (5, 0.0, 1), (6, 3.2, 4)]
LEAN = []
MOVES = [(1, 0.6, "hop", 1.4), (3, 0.5, "tilt", 1.2), (5, 0.2, "turn", 2.0), (7, 0.2, "jump", 1.0)]
INTRO = "rise"
SE = [(-1, 0.0, "se_rise", -8), (0, 1.3, "se_boot", -12), (1, 1.0, "se_ding", -8), (1, 1.15, "se_jaran", -12),
      (2, 0.0, "se_whoosh", -12), (3, 0.0, "se_whoosh", -12), (4, 0.0, "se_whoosh", -12), (4, 1.0, "se_pop", -10),
      (5, 1.0, "se_tick", -10), (6, 0.0, "se_whoosh", -12), (6, 4.4, "se_pechin", -6), (7, 0.4, "se_pop", -9)]
ENDING = dict(kind="jump", at=(6, 3.4))
