# レンズカーソル — Style Reference
> 白い紙の上の太い黒い見出し。カーソルの位置だけ凸レンズになって、小さな注記が読める

**Theme:** light  **Series:** PARTS 051  **World:** 架空の老眼鏡ブランド「Tome」のトップ。小さな文字はレンズの下で読む、という商品の約束をカーソルで体験させる。

## Concept
画面の内容(見出し・リード・9px の注記)を一度 canvas に描いてテクスチャにし、WebGL で全面に貼り直す。カーソルの位置には半径 150px の凸レンズがあり、内側は 1.7 倍に拡大、外周の帯で元の縮尺に戻る。色ずれ・縁の暗さ・細い反射・外側の柔らかい影で、ガラスの厚みを感じさせる。無操作時はレンズが注記の上をゆっくり往復するので、静止画でも「小さな文字が読める」ことが伝わる。

## Signature Technique
2D canvas をレンダーターゲットにして WebGL1 で屈折合成。

- **レンダーターゲット**: `drawScene()` が W×H×dpr の canvas に、見出し(Inter 800、幅の 8.5%、上限 118px)、リード(Noto Sans JP 500 19px)、注記(Noto Sans JP 400 9px、行間 1.75、最大幅 560px、文字単位で折り返し)、罫線とモノスペースのラベルを描く。`UNPACK_FLIP_Y_WEBGL` で上下を合わせて `texImage2D`。リサイズ時のみ再描画。
- **レンズの写像**: `d = (px − C)/R`、`r = |d|`。`f = 1/mag + (1 − 1/mag)·smoothstep(0.55, 1, r)`。サンプル位置 `uv = C/res + d·R/res·f`。中心 55% は一様に 1.7 倍、外周の帯で f → 1 に戻るので境界で絵が連続する。
- **色ずれ**: R/G/B をそれぞれ `f·(1 ∓ 0.012)` で別々にサンプル。
- **ガラスの陰影**: `N = (d, √(1 − r²))`、フレネル `(1 − z)³` で縁を暗く(係数 0.3)、`pow(N·H, 140)·0.5` の細い反射、`smoothstep(.985, 1, r)·0.35` の縁のハイライト、外周 4% を 72% に落とす暗い線。
- **外側の影**: レンズ中心から (6, −10) px ずらした円の外側 18% を 10% 減光。
- **追従**: 目標へ 14%/フレーム。無操作時は注記の矩形 `textBand` の中を周期約18秒 / 27秒で往復。初期位置は注記の上。
- 被覆は `1 − smoothstep(1 − aa, 1 + aa, r)`、aa = 1.5px。

```glsl
float f=1./uMag+(1.-1./uMag)*smoothstep(.55,1.,rr);
vec2 base2=uC/uRes; vec2 dir=d*uR/uRes;
float cr=texture2D(uT,base2+dir*(f*(1.-uChroma))).r;
float cg=texture2D(uT,base2+dir*f).g;
float cb=texture2D(uT,base2+dir*(f*(1.+uChroma))).b;
col=vec3(cr,cg,cb)*(1.-pow(1.-z,3.)*uRim*3.)+pow(max(dot(N,H),0.),140.)*.5;
```

## Color Palette
| Token | HEX | Role |
|---|---|---|
| --white | #ffffff | 紙。レンダーターゲットの塗りも同じ |
| --ink | #000000 | 見出し、リード、ヘッダーの製品名、クレジット |
| --charcoal | #333333 | 注記の文字 |
| --fog | #8c8c8c | ヘッダー補助、メタ情報、操作説明、SPEC ラベル |
| --mist | #cccccc | 注記の下の罫線 |
| --cream / --dawn | #fffcf6 / #f6c7c2 | 予備(暖かい面と夜明けの桃色) |

## Typography
- 見出し(canvas): Inter 800、`min(W·0.085, 118px)`(モバイル W·0.17)、行送り 0.98、letter-spacing −0.04em、2行。
- リード(canvas): Noto Sans JP 500、19px(モバイル 15px)。
- 注記(canvas): Noto Sans JP 400、9px(モバイル 8px)、行間 1.75。レンズで 1.7 倍にすると 15px 相当になる。
- ラベル: JetBrains Mono 400、10–12px、letter-spacing 0.06em。
- WebGL 不可時は `.fallback` に見出しとリードを HTML で表示し、`cursor` を戻す。

## Spacing & Layout
- hero `min-height: 100svh`、3行グリッド。canvas は背面全面。`cursor: none`(タッチ端末と nogl では auto)。
- canvas 内の左余白は `min(72px, max(20px, W·0.05))`(モバイル 18px)。見出しの上端は高さの 20%(モバイル 16%)。
- 余白: 横 `clamp(20px, 5vw, 72px)`、上 `clamp(20px, 3vw, 40px)`、下 `clamp(18px, 2.6vw, 32px)`。
- breakpoint 760px: ヘッダー右とメタ情報を消し、フッターを縦積み(左揃え)。横余白は 18px、`cursor` は auto に戻す。

## Motion
- レンズは目標へ 14%/フレーム。カーソルを置いた位置に少し遅れて着く。
- 無操作時・タッチ端末では注記の上を往復。初期位置も注記の上。
- rAF 1本。IntersectionObserver と visibilitychange で画面外停止。
- `prefers-reduced-motion: reduce`: 注記の上に置いた1フレームだけ描く。カーソル移動時のみ再描画し、追従が収束したら止まる。

## Components
- hero: 背面 canvas(`aria-hidden`)+ヘッダー/フッターの HTML。本文はすべて canvas 側。
- header: 左にブランド名、右に技法名。
- meta: 左下にレンズ径と倍率。
- footer tag: `VALE / PARTS 051 / レンズカーソル` と操作説明1行(タッチ端末では差し替え)。

## Do / Don't
- Do: レンズの下に置く価値のある小さな文字を用意する。拡大する対象が無いと意味が伝わらない。
- Do: 中心は平らに拡大し、歪みは外周の帯だけにする。全体を球面にすると文字が読めない。
- Do: 倍率は 1.5–2.0。
- Don't: レンズを複数にしない。
- Don't: 色ずれを 0.02 より大きくしない。文字が二重に見える。
- Don't: 紙に色やグラデーションを敷かない。

## References
- Mews: 白い壁に太い黒の文字を大きく置く構成と、暖かい白の空気を借りた。桃色の夜明けの差し色は使わず、白と黒だけにした。
- FARFETCH España: 白い箱のような売り場に、細いグレーの罫線と小さな注記を並べる考え方を借りた。商品写真の代わりに注記そのものを見せる対象にした。

## Usage
1. `<canvas id="gl">` を hero の背面に置き、`<script>` ブロック全体を移植する。
2. 画面の内容は `drawScene()` に 2D canvas で描く(HTML はレンズの下に入らない)。文言は `COPY` を書き換える。
3. 無操作時の往復範囲は `textBand`(drawScene 内で設定)で決まる。別の対象に合わせるなら矩形を差し替える。
4. 見た目は `P.radius / P.mag / P.chroma / P.rim`。

| パラメータ | 既定 | 意味 |
|---|---|---|
| radius | 150 / 92(モバイル) | レンズ半径(CSS px) |
| mag | 1.7 | 中心の倍率 |
| chroma | 0.012 | 色ずれ(サンプル比) |
| follow | 0.14 | 追従率 |
| rim | 0.10 | 縁の暗さ |
| dprMax | 2 | 表示解像度の上限(文字を描くので 2 を推奨) |
