# 文字の画像 — Style Reference
> 黒い面に、白い等幅の文字だけで描いた工業用ミシンの写真。文字の大きさがカーソルで変わり、細かいと写真、粗いと記号の列になる。

**Theme:** dark  **Series:** PARTS 094  **World:** 監視カメラの映像を文字に変えて細い回線で送る工場向けサービス。

## Concept
画像パネルの効果。主役は文字アトラスのシェーダーで、セルごとの輝度から「暗→明」に並べた16文字のうち1字を選んで描く。
カーソルの横位置でセルの大きさが 5px から 18px まで変わる。触らなければ 50% を数秒見せてから自動で往復する。
色は黒と白だけ。明るい所の文字は白、暗い所の文字は灰で、文字そのものにも濃淡がある。

## Signature Technique
WebGL1 フラグメントシェーダー + 2D canvas で描く文字アトラス。画像は `assets/photo.webp`(1600×893)。黒い背景に白い工業用ミシンを真横から置いた高コントラストの写真で、文字にしても輪郭(はずみ車、糸巻き、アーム、押さえ)で被写体が分かる。

- アトラス: `RAMP = ' .:-=+*csoxXO%#@'`(暗→明)を IBM Plex Mono 500 で、1字 64×40px(ASPECT .62)の横一列に描き、R チャンネルをアルファとして使う。フォント読込後に描き直す。
- セル: `cell = floor(px / CELL)`(CELL = (高さ, 高さ×ASPECT))。セル中心と上下左右(CELL×.3)の5点で輝度を平均。
- レベル: `l = pow(clamp((l-BLACK)/(WHITE-BLACK)), GAMMA)`(BLACK .07、WHITE .82、GAMMA .9。黒地を空白に落とし、白い機体を濃い文字で埋める)。
- 局所コントラスト: セル中心の輝度から、1.6セル離れた上下左右4点の平均を引き、`SHARP`(.9)倍して足す。機体の縁と中の部品が平らに埋まらない。
- はみ出し: 画像は contain で置き、画像の外は輝度0(空白)。被写体が切れない。
- 文字: `idx = floor(l*(N-.001))`、`a = texture2D(atlas, ((idx + inCell.x)/N, inCell.y)).r`。
- 色: `ink = mix(DIM, INK, l)`、`col = mix(BG, ink, a)`。DIM #807A75、INK #FAF7F2。
- 大きさ: `CELL_H = (CELL_MIN + (CELL_MAX - CELL_MIN)*p²) * DPR`(desktop 5〜16px、mobile 3.4〜11px)。初期の進み `START` .28 で細かい側から始める。
- 進み: カーソルの横位置へ lerp(.09)。無操作時は 3 秒後から `.5 + .45*sin(phase)`(周期 12 秒)。
- 解像度: `min(dpr, 2)`。

## Color Palette
| Token | HEX | 役割 |
|---|---|---|
| --void(BG) | #000000 | 背景 |
| --charcoal | #1D1A1A | (移植用)持ち上げた面 |
| panel-border | #2A2727 | パネルの枠線 |
| INK | #FAF7F2 | 明るい所の文字 |
| DIM | #807A75 | 暗い所の文字 |
| --bone | #FFFFFF | 見出し・ロゴ・CTA 地 |
| --stone | #B8B3B0 | 本文・タグ |
| --granite | #8A8380 | eyebrow・フッター |

## Typography
- 見出し: Noto Sans JP 700、`clamp(30px, 4vw, 56px)`、line-height 1.2、letter-spacing -.02em、中央揃え。mobile `clamp(26px, 7.2vw, 36px)`。
- 本文: Noto Sans JP 400、`clamp(14px, 1.1vw, 16px)`、line-height 1.9、最大 30em(mobile 24em)。
- eyebrow / ロゴ / フッター: Inter。eyebrow 500 / 12px / .24em、ロゴ 700 / 12px / .22em。
- タグとアトラス: IBM Plex Mono 500。タグ 11px / .14em。
- CTA: Noto Sans JP 700 / 13px / .08em。

## Spacing & Layout
- 1画面 = `100svh`、`grid-template-rows: auto 1fr auto`。中段は縦2段(文章 → パネル)、gap 26px、中央揃え。
- 左右余白 `clamp(20px, 5vw, 72px)`、上 26px、下 22px(+safe-area)。
- eyebrow → h1 16px、h1 → lead 14px、lead → CTA 22px。
- パネル: 幅 `min(100%, 960px, (100svh - 420px)*1.78)`、`aspect-ratio 16/9`、1px の枠線。mobile は 16:10、幅 100%。
- タグ: 左下 FINE、右下 COARSE。

## Motion
- 大きさの lerp .09(無操作時 .054)。自動往復は 3 秒後から周期 12 秒、振幅 ±.45。
- 1 rAF、`visibilitychange` と IntersectionObserver(パネル)で停止。
- `prefers-reduced-motion`: 自動往復なし。50% で静止し、カーソルを動かした時だけ追従。
- WebGL 非対応・画像失敗: `.panel.nogl` で `<img>` を `grayscale(1) contrast(1.3)` で出す。

## Components
- hero: 全画面。上にサービス名と業種、中央に eyebrow / h1 / lead / CTA、その下にパネル。
- panel(.panel): canvas + 予備の img + タグ2つ。`cursor: ew-resize`。
- button(.cta): 高さ44px の pill。白地に黒文字。hover で薄い灰(#E8E6E4)になり1px浮く。focus-visible は白のリング。
- footer(.bot): 11px、左に操作説明、右に `VALE / PARTS 094 / 文字の画像`。

## Do / Don't
- Do: RAMP は濃度順に並べる。順が狂うと階調が反転して読めない。
- Do: 被写体は単体・無地の黒背景・明るい機体の写真を使う。輪郭だけで何か分かるもの。
- Do: 暗い写真は WHITE を下げる。文字は中間調で形を作る。
- Do: 文字の色にも濃淡を付ける(DIM → INK)。単色だと平板になる。
- Don't: desktop で CELL_MIN を 4px 以下にしない。文字がつぶれて灰のノイズになる。
- Don't: 緑の文字にしない(端末風の定番)。白黒のままが写真に見える。
- Don't: 日本語の文字を RAMP に混ぜない。等幅でないと格子がずれる。

## References
- Datalands: 黒と白だけの面に等幅の記号という骨格を抽象化。文字で描く対象を写真にした。
- Factory: 黒い面に warm granite の文字という抑えた階層を抽象化。文字色の灰を同系の暖かい灰にした。

## Usage
1. `.panel` ブロック(canvas / img / cap)と `<script>`、`.panel` `.cap` の CSS を切り出す。外部ライブラリは不要。IBM Plex Mono の `<link>` を残す。
2. `<img src>` と alt を差し替える。縦横比は CSS の `aspect-ratio` を画像に合わせる。
3. パラメータは JS 冒頭の `P`:
   - `DPR` 描画解像度
   - `RAMP` 文字の並び(暗→明)、`FONT` アトラスのフォント、`ASPECT` 文字の横/縦(.62)
   - `CELL_MIN` / `CELL_MAX` セルの大きさ(5〜16px、mobile 3.4〜11px)
   - `SHARP` 局所コントラスト(.9)
   - `BLACK` / `WHITE` / `GAMMA` 輝度のレベル
   - `INK` / `DIM` / `BG` 色
   - `IDLE_PERIOD` / `IDLE_DELAY` 自動往復、`FOLLOW` 追従、`START` 初期の進み
4. 動画にするなら `img` を `video` に替え、毎フレーム `texImage2D` で更新する(`upload()` を loop 内で呼ぶ)。
5. 文字を色付きにするなら `ink` を `texture2D(uTex, c).rgb * 1.4` に替える。
