# コマ送り — Style Reference
> 黒い調整室で、磨いた金属の結び目が一コマずつ回る。先頭だけ珊瑚色

**Theme:** dark  **Series:** PARTS 062  **World:** 架空のレンダー待ち行列「Studio B」。60 コマの連番をスクロールで送る。

## Concept
60 コマ(mobile 48)を Canvas で生成して配列に保持し、スクロール位置をコマ番号に写す。画像ファイルは使わず、連番は手続きで描く(金属の管でできたトーラス結び目が 1 周分回る。管は天井の照明と床の残光を映す)。左下に大きなコマ番号と珊瑚色のスクラブ線、保持しているメモリ量。読み込み時に 38% の位置から始めるので、静止画でも途中のコマが写る。

## Signature Technique
全コマ事前描画 + スクロールのスクラブ。

- 生成: コマ k の回転角 `a = k / N × 2π`、傾き 0.62rad。トーラス結び目 `(p, q) = (2, 3)`: `r = 2 + cos(qt)`, `x = r cos(pt)`, `y = r sin(pt)`, `z = sin(qt)` を 1400 点(mobile 800)。Y 回転 → X 傾け → 透視 `d = 1 / (8 + z)`、倍率 `K = 1.12`、管の半径 `.17 × d`
- 金属の管: 隣り合う 2 点ごとに管を横切る四角形を描き、奥から手前の順(z の中点でソート)に塗る。四角形の塗りは管の幅方向の線形グラデーション(9 stop)。各 stop で管の法線 `N = side·s + Nv·√(1−s²)`(side = 接線 × 視線、Nv = 法線の視線成分)を作り、視線 `I = (0,0,1)` を反射 `R = I − 2(N·I)N` して環境を引く
- 環境 `env(R)`: 上向きは天井のソフトボックス(#F6F3EE へ smoothstep .12→.8)、水平は暗い部屋 #141417、下向きは床の残光(#964638 を .8 まで)。右に縦長のストリップ光(`R.x` .72→.95)、左上の点光源に `pow(R·L, 60)` の鏡面ハイライト
- 縁の落ち: 幅方向の端ほど `.55 + .45·√(1−s²)` で暗くし、奥は霧 `.5〜1.0` で沈める。継ぎ目は四角形を接線方向に .9px 重ねて消す
- 先頭 `head = k / N × points` の前後 2% を珊瑚色の陽極酸化(R ×1、G ×.45、B ×.32)にする
- 保持: 画面に出すコマはその場で描き、残りは rAF ごとに 1 コマずつ埋める(読み込みで止まらない)。`frames[k]` は 560px(mobile 400px)の canvas。60 × 560² × 4B ≈ 72MB。読み出しに表示
- スクラブ: 420vh のラッパーの中に sticky の 100svh ステージ。`p = −top / (height − H)`、`k = min(N − 1, floor(p × N))`。同じ k なら描き直さない
- 表示: `drawImage(frames[k], x, y, s, s)`、`s = min(W, H) × .86`(mobile .92)。ステージ canvas は dpr ≤ 2
- 初期位置: `load` 後に `scrollY < 4` かつ hash 無しなら `(height − H) × .38` へ
- rAF 1 本。ラッパーが画面外なら更新しない。reduced-motion でもスクロール連動は残す(自走する動きが無いため)

```js
const k = Math.min(P.N - 1, Math.floor(p * P.N));
if (k !== cur) { cur = k; ctx.clearRect(0, 0, W, H); ctx.drawImage(frames[k], x, y, s, s); }
```

## Color Palette
| Token | HEX | Role |
|---|---|---|
| --studio | #101012 | 地色 |
| --cream | #F7F4EF | 見出し |
| softbox | #F6F3EE | 管に映る天井の照明 |
| room | #141417 | 管に映る暗い部屋(縁の黒) |
| floor | #964638 | 管の下側に映る床の残光 |
| --ash | #7C7A78 | 本文、読み出し |
| --hair | rgba(247,244,239,.12) | スクラブ線の地 |
| --coral | #FF6A4D | 管の先頭区間とコマ番号。スクラブ線と下 46% の残光(α .16)にも |

## Typography
- 見出し: Noto Sans JP 500 `clamp(17px, 1.5vw, 21px)`、tracking .01em。本文 400 `clamp(13px, 1.05vw, 15px)`、見出しの下 8px。左上のブロックは最大幅 30em
- コマ番号: JetBrains Mono 400 `clamp(40px, 6vw, 80px)` / tracking −.02em / tabular-nums、3 桁ゼロ埋め(表示は k + 1)。総数は .4em の --ash で右に 10px 空けて添える
- 読み出し・タグ: JetBrains Mono 11〜12px、tracking .06em

## Spacing & Layout
- 上の帯: `position: fixed`、padding 16px `clamp(20px, 4vw, 48px)`。640px 以下で右の案内を消す
- ステージ: grid `auto / 1fr / auto`、padding 上 `clamp(64px, 9vh, 96px)`、横 `clamp(20px, 4vw, 48px)`、下 `max(22px, safe-area)`。コマは中央から下へ高さの 2% ずらして置く
- 下段: `1fr / auto`(読み出し / タグ)。640px 以下で 1 カラム
- 読み出しの最大幅 420px、行間 10px、スクラブ線 2px + 10px の丸
- ラッパー 420vh(送る距離)。短くすると 1 コマあたりのスクロール量が減る

## Motion
- 自走なし。スクロールだけがコマを進める
- 1 コマ = (ラッパー高 − 画面高) / N px。420vh・900px なら 48px
- スクラブ線は幅 %、丸は left %
- reduced-motion: 変更なし(スクロールに従う表示は自走ではない)

## Components
- 上の帯: 名前 / 区画名、右に `scroll to scrub`
- 見出しと 1 文(左上)
- ステージ canvas(全面)+ 下 46% の珊瑚色の残光
- 読み出し: コマ番号 / 総数、スクラブ線、式と % と MB
- 右下のタグ `VALE / PARTS 062 / コマ送り`

## Do / Don't
Do
- コマは描いたら保持し、2 回目以降は `drawImage` だけにする
- 同じコマ番号なら描き直さない
- コマ数 × 解像度のメモリを計算し、読み出しに出す
- 色は先頭の区間だけ。金属の色は環境の映り込みだけで出す

Don't
- 1 回の rAF で 2 コマ以上を生成しない(かくつく)
- 1 コマを 1024px より大きくしない(60 コマで 250MB を超える)
- 連番を画像ファイルにしない(このパーツの趣旨)
- 残光を強くしない(α .16 まで)

## References
- Suno(Afterglow on a mixing console): 黒い調整室に生成りの文字、下からの夕暮れの残光、珊瑚 1 点の配分。残光を下 46% の線形グラデーションに、珊瑚を「先頭の点」と「コマ番号」に移した
- Trunk(monochrome control room with one green signal light): 近無彩の制御室に信号 1 点、display は weight 300 で細く。信号を緑から珊瑚に変え、計測値の読み出しを mono で添える構造を引いた

## Usage
1. HTML: `<section class="wrap"><div class="stage"><canvas id="c"></canvas>…</div></section>` の構造を移植する。読み出し(`#fr` `#fn` `#sb` `#sk` `#rs` `#mem`)は任意
2. CSS: `.wrap` `.stage` `.stage canvas` を移植する。`.stage::before` は残光
3. JS: `<script>` ブロックを移植する。自分の連番にするなら `renderFrame(k)` を差し替える(k 番目のコマを canvas で返す関数)。画像連番を使う場合は `frames[k]` に `Image` を入れても同じ `drawImage` で動く

| パラメータ | 既定 | 意味 |
|---|---|---|
| N | 60 / 48(mobile) | コマ数 |
| size | 560 / 400 | 1 コマの px |
| points | 1400 / 800 | 管の分割数 |
| p, q | 2, 3 | 結び目の巻き数 |
| start | .38 | 読み込み時に進めておく割合 |
| wrap の高さ | 420vh | 送る距離 |

注意: `frames` は端末のメモリに乗る。N × size² × 4B が 150MB を超えるなら size を下げる。
