# 液体の文字 — Style Reference
> 深い紺の闇の中で、一語がガラス質の液体として浮かぶ

**Theme:** dark  **Series:** PARTS 012  **World:** 架空の流体タイポ描画ツール「Nami」のトップ。製品が何をするかを、見出し自体の挙動で示す。

## Concept
見出し「Liquid」を距離場として持ち、しきい値をゆっくり上下させる。しきい値が上がると字が太り、隣の字とつながる。カーソルに追従する雫が文字に触れると smooth-min で一つの面に溶ける。背景は装飾なしの深い紺で、光るのは文字の縁だけ。画面の焦点は文字1語に限る。

## Signature Technique
2D canvas に描いた文字 → Jump Flooding Algorithm で符号付き距離場 → 最終パスで smooth-min と しきい値、法線からの反射で液体に見せる。

1. **文字のラスタライズ**: offscreen canvas(sim 幅 1536 / モバイル 640、高さは画面比)に Inter 800 で1語を描く。幅の80%(モバイル90%)に収まるよう font-size を計算し、上限は sim 高さの 52%。字の縦中心は上から 47%。
2. **シードパス**: アルファが0.5を超え、4近傍のいずれかが0.5未満のピクセルを縁として、自分の座標を RGBA8 に16bit×2で符号化して書く。それ以外は (1,1,1,1) = 無効。
3. **JFA**: step = 2^(ceil(log2(max(W,H)))-1) から 1 まで半減、最後に step 1 をもう1回(JFA+1)。9近傍から最も近い縁座標を引き継ぐ。1536×(高さ)で11〜12パス。文字が静止なので初回とリサイズ時のみ実行。
4. **最終パス**(毎フレーム、表示解像度 dpr≤1.5):
   - `sdText(p)`: 最近傍の縁までの距離。アルファで符号を付ける。サンプル位置を value noise のドメインワープで ±3px 揺らす。
   - `blob = min(length(p-mouse)-26, 小さい雫2つ(半径 26×.55 / 26×.42))`。
   - `f = smin(sdText, blob, k=34) - thick`、`thick = 1.5 + 6·sin(0.28t)`(すべて sim 幅1000基準の px)。
   - 被覆 `cov = 1 - smoothstep(-aa, aa, f)`、`aa = .7/scale + .35`(scale = 表示px / sim px)。
   - 法線: 中心差分 `g = (f(p+e)-f(p-e), f(p+e_y)-f(p-e_y))/(2e)`(e=4 sim px。JFA 距離場の1px段差を拾わず、縁のハイライトが粒にならない)、`h = clamp(-f/14, 0, 1)` のドーム形で `N = normalize(vec3(g·(1-dome)·2.2, 1))`。
   - 空の映り込み: `col += (.55,.7,1)·smoothstep(.35,.85,N.y)·(1-dome)·.22`。上を向いた縁だけ淡く明るくする。
   - 色 = 紺のガラス色(上下グラデ) + 青のフレネル縁(`pow(1-N·V, 2.2)`) + 白のスペキュラ(`pow(N·H, 70)`) + 副光源。
   - 背景は紺に弱いビネットと、文字から60px以内だけ青を5%足す。

```glsl
float smin(float a,float b,float k){ float h=clamp(.5+.5*(b-a)/k,0.,1.); return mix(b,a,h)-k*h*(1.-h); }
float field(vec2 p){
  vec2 w = vec2(noise(p*fq+vec2(0.,t*.3)), noise(p*fq+vec2(5.2,-t*.27))) - .5;
  float d = sdText(p + w*2.*warpAmp);
  float b = length(p-mouse)-blobR;
  return smin(d, b, k) - thick;
}
```

## Color Palette
| Token | HEX | Role |
|---|---|---|
| --void | #03081a | 画面全体の地。紺に寄せた近黒 |
| ink (shader) | #172060 | 液体の本体色。上端で1.35倍、下端で0.7倍に振る |
| --blue | #3d7bff | 縁のフレネル光、副光源、文字周辺のにじみ。面には使わない |
| --ink | #f5f6ff | 見出しの日本語 |
| --lavender | #aab1f2 | 本文、ヘッダーの製品名、フッターのクレジット |
| --lilac | #7a83cc | ヘッダー補助、操作説明 |
| --line | #292f66 | 罫線が必要になったとき用(未使用) |

## Typography
- 液体の文字: Inter 800、letter-spacing -0.045em、canvas 上で描画。幅は画面の80%。
- 日本語見出し: Noto Sans JP 500、`clamp(18px, 1.6vw, 22px)`、line-height 1.5。
- 本文: Noto Sans JP 400、`clamp(13px, 1.05vw, 15px)`、最大幅 30em。
- ラベル: JetBrains Mono 400、11–12px、letter-spacing 0.06em。
- WebGL 不可時は同じ語を Inter 800 の HTML テキストで中央に表示する(`.nogl .fallback`)。

## Spacing & Layout
- hero `min-height: 100svh`、3行グリッド(header / stage / footer)。canvas は `position:absolute; inset:0` で背面。
- 余白: 横 `clamp(20px, 5vw, 72px)`、縦 `clamp(20px, 3vw, 40px)`。モバイル左右 18px。
- 文字の中心は画面高さの47%(canvas 座標)。見出しと本文は左下、クレジットは右下、操作説明は下中央。
- breakpoint 760px: ヘッダー右を消し、フッターを縦積み、操作説明を消す。

## Motion
- しきい値: 周期約22秒(0.28 rad/s)で -4.5〜+7.5px。太いときに隣の字と橋ができる。
- 縁の揺らぎ: value noise、周波数 0.012/px、速度 0.3/s、振幅 3px。
- 雫: カーソルへ毎フレーム10%で追従。初期位置は sim の (.78, .76)。タッチ端末とカーソル不在時はリサージュ軌道 `x = .5 + .36·sin(.21T)`、`y = .47 + .22·sin(.33T + 1.2)`(画面比)で自走。小さい雫2つは (.22, .30) と (.80, .70) のまわりを常に自走。
- rAF 1本。IntersectionObserver と visibilitychange で画面外では停止。リサイズは150ms デバウンスで距離場を再構築。
- `prefers-reduced-motion: reduce`: しきい値を +3.5px に固定、揺らぎ0、時間0で1フレームだけ描く。カーソル移動時のみ再描画。

## Components
- hero: 背面 canvas + 前面テキスト。canvas は `aria-hidden`。
- header: 左に製品名(Nami / fluid type tool)、右に技法名。モノスペース 12px。
- copy: 見出し1行 + 本文2文、左下。本文は文節ごとに `<span class="ph">`(white-space:nowrap)で包み、1〜2字だけ次行に落ちる折り返しを防ぐ。
- footer tag: `VALE / PARTS 012 / 液体の文字` と書体注記、右下。
- hint: 下中央に操作説明1行。タッチ端末では文言を差し替え、モバイル幅では非表示。

## Do / Don't
- Do: 1語、太いウェイトの書体を使う。細い字は距離場で溶かすと読めなくなる。
- Do: 背景は無地に近い暗色にして、縁の光だけで立体を伝える。
- Do: 距離場の構築は文字が変わったときだけ走らせる。
- Don't: 雫を3つより増やさない。文字より雫が目立つ。
- Don't: しきい値の振幅を字間より大きくしない。語全体が一塊になり意味が消える。
- Don't: 背景にグリッドや粒子を足さない。

## References
- Galileo: 紺の近黒を地にして、電気的な青を信号のように1点だけ光らせる考え方を借りた。細いウェイトの display を使う構成は捨て、太い Inter を液体として描いた。
- Dovetail: 暗い盤面でモノスペースの小さな見出しラベルを使い、UIを後退させてデータを前に出す構造を借りた。青の使いどころを UI アクセントから文字の縁の反射に移した。

## Usage
縁の揺らぎの速さは P に置かず、FS_MAIN の `uT*.3` / `uT*.27` の係数で決める。
1. `<canvas id="gl">` を hero の背面に置き、`<link>` で Inter 800 を読み込む。
2. `<script>` ブロック全体を移植する。`document.fonts.load('800 100px Inter')` の後に `buildField()` が走る。
3. `P.word` を差し替える。日本語も可(Noto Sans JP 700 以上を推奨。`ctx.font` の書体名を変える)。
4. 色は `C = { bg, ink, blue }`(hex)と CSS 変数で揃える。
5. WebGL 不可時のために同じ語を `.fallback` に書いておく。

| パラメータ | 既定 | 意味 |
|---|---|---|
| simW | 1536 / 640(モバイル) | 距離場の横解像度。小さいほど軽いが縁が粗くなる |
| dprMax | 1.5 | 最終パスの表示解像度上限 |
| fill | 0.80 / 0.90 | 語の幅 / 画面幅 |
| thickBase / thickAmp / thickSpeed | 1.5 / 6.0 / 0.28 | しきい値の中心・振幅(px)・角速度 |
| warpAmp / warpFreq | 3.0 / 0.012 | 縁の揺らぎの振幅・周波数 |
| blobR | 26 | カーソル雫の半径(px) |
| smoothK | 34 | smooth-min の融合幅。大きいほど早く溶ける |
| follow | 0.10 | 雫の追従率 |
| bevel | 14 | 縁の立体感の幅(px) |

px はすべて sim 幅 1000 を基準とした値で、内部で `simW/1000` 倍される。
