# 銀河 — Style Reference
> 真夜中の管制室の黒に、十万の星が渦を巻く。文字は桃色のインク

**Theme:** dark  **Series:** PARTS 035  **World:** 架空の星図サービス「Uzu」のトップ。腕は星が混み合う場所、という事実を粒子の密度で見せる。

## Concept
画面の右寄りに傾いた渦巻銀河を置く。星は 102,400 個(モバイル 25,600 個)の粒子で、GPGPU で毎フレーム差動回転させる。内側ほど速いので腕は少しずつ巻き込まれ、見ているあいだにも形が変わる。色は半径と乱数から決めた色温度(中心は暖色、腕は青白、所々に赤い塵)。カーソルの位置には弱い引力があり、腕がそちらへ引かれる。文字は Flayks の桃色インクで左下に。

## Signature Technique
WebGL2 + `EXT_color_buffer_float`。位置を RGBA32F テクスチャに持ち、ping-pong で更新、`gl_VertexID` で読み出して POINTS で描く。

- **初期配置(JS)**: 半径は指数円盤 `r = −ln(1 − u)·0.85`(上限 2.7)。腕は対数螺旋 `θ = ln(r / 0.06) / tan(0.30) + arm·π + spread·gauss`、spread = 0.30·(0.6 + 0.8r)。中心 r < 0.45 は `bulge²·2π·rand` で角度を散らしてバルジにする。厚み `y = gauss·(0.035 + 0.16·e^(−2.2r))`。8% はハローとして半径 0〜2.7 に一様、角度は ±π、厚み gauss·0.22 でばらまく。w 成分は乱数の種。
- **更新シェーダー**: `w = 0.42 / (r + 0.35)`(平坦な回転曲線)、`xz = R(w·dt)·xz`。カーソル引力 `xz += (cur − xz)·pull·e^(−5|cur − xz|²)·dt`、pull = 1.2(カーソル無しは 0)。開始時に 0.05 秒 × 40 回回して腕を少し巻いておく。
- **描画(頂点)**: `texelFetch` で位置を取り、`uRot = Rz(0.35)·Rx(1.0)` で傾けて正射影、`scale = 0.66`(高さ比)。色: `warm (1, .80, .58)` → 半径で `white / blue (.60, .74, 1)`(種 k1 ≥ 0.55 の 45% が若い星。r 0.25〜1.2 で効く)、10% を `dust (.95, .55, .45)` に 80% まで寄せる(r 0.4〜1.5)。明るさは `0.30 + 0.70·k2`。3.5% を大きい星(明るさ 3.5 倍、点 2.8 倍)。点サイズ 1.6px × dpr、中心ほど少し大きい(外周 0.9 倍〜中心 1.3 倍)。
- **描画(フラグメント)**: `smoothstep(.5, .1, d)` の丸い点、`ONE, ONE` の加算合成、係数 0.55。
- **背景パス**: ビネット `1 − 0.35·|uv|` 付きの #0e1012 と、中心に傾きに沿った楕円のコアグロー(`e^(−d²/(s²·0.12))·0.35` と `e^(−d²/(s²·0.012))·0.5`)。
- 画面上の中心位置は `core`(高さ比)で、ビューポートの平行移動で合わせる。カーソルの平面座標は 2×2 の逆行列で求める。

```glsl
// update
float w=uOmega/(r+.35); float c=cos(w*uDt),sn=sin(w*uDt); xz=mat2(c,-sn,sn,c)*xz;
vec2 d=uCur-xz; xz+=d*uPull*exp(-dot(d,d)*5.)*uDt;
// vertex
vec3 p=uRot*vec3(s.x,s.y,s.z); gl_Position=vec4(p.x*uScale*uRes.y/uRes.x,p.y*uScale,0.,1.);
vec3 col=mix(warm,mix(white,blue,young),smoothstep(.05,.9,r));
```

## Color Palette
| Token | HEX | Role |
|---|---|---|
| --void | #0e1012 | 地。背景パスも同じ値 |
| --peach | #ffe0ce | 見出し、サービス名、クレジット |
| --bone | #f6e9d9 | 星の数の数値 |
| --mist | #9aa3b0 | 本文 |
| --slate | #566171 | ヘッダー補助、操作説明 |
| 星 warm / white / blue / dust | #ffcc94 / #fff2e6 / #99bdff / #f28c73 | 色温度の4点。加算合成で白に飽和する |

## Typography
- 日本語見出し: Noto Sans JP 700、`clamp(24px, 2.8vw, 40px)`、line-height 1.3、letter-spacing −0.01em、`word-break: keep-all`、`<br>` で2行。
- 本文: Noto Sans JP 300、`clamp(13px, 1.05vw, 15px)`。
- ラベル: JetBrains Mono 400、11–12px、letter-spacing 0.06em。星の数は `toLocaleString('en-US')`。

## Spacing & Layout
- hero `min-height: 100svh`、3行グリッド。canvas は背面全面。
- 銀河中心は画面中心から右に高さの 18%、上に 6%(モバイルは中央、上に 10%)。表示スケールは高さの 0.33(モバイル 0.42)。
- 見出しと本文は左下、クレジットは右下。
- 余白: 横 `clamp(20px, 5vw, 72px)`、縦 `clamp(20px, 3vw, 40px)`。モバイル左右 18px。
- breakpoint 760px: ヘッダー右を消し、フッターを縦積み。

## Motion
- 回転: 中心付近で約 1.2 rad/s、r = 2 で 0.18 rad/s(1 周約 35 秒)、外周 r = 2.7 で 1 周約 45 秒。
- カーソルの引力は半径約 0.45 の範囲で効き、離れると 0 に戻る。粒子は戻らないので腕の形が少し変わったまま残る。
- rAF 1本。dt は 0〜0.05 秒に丸める。IntersectionObserver と visibilitychange で画面外停止。
- `prefers-reduced-motion: reduce`: 初期の巻き込みまで計算して1フレームだけ描く。カーソルの引力が働いているあいだだけ更新する。

## Components
- hero: 背面 canvas(`aria-hidden`)+前面テキスト。WebGL2 か float FBO が無いときは `.fallback` にサービス名を表示。
- header: 左にサービス名、右に技法名。
- copy: 見出し2行+本文1文、左下。
- footer tag: `VALE / PARTS 035 / 銀河 · stars 102,400` と操作説明1行(タッチ端末では差し替え)。

## Do / Don't
- Do: 粒子は小さく(2px 以下)、数で見せる。加算合成で中心が白く飽和するのが正しい。
- Do: バルジは角度を散らす。螺旋のまま中心まで巻くと縞になる。
- Do: 背景は黒に近い無地。星の背景に星雲や格子を描かない。
- Don't: 腕を 3 本より増やさない。密度のコントラストが消える。
- Don't: 回転を速くしない(omega 0.6 以下)。
- Don't: 粒子に色相を散らさない。色温度の1本の軸だけ。

## References
- Mapbox: 真夜中の管制室のような暗い地と、光るデータだけが照明になる考え方を借りた。タイル画像の代わりに星の密度を光にした。
- Flayks: 深い色の紙に桃色のインクで大きく組む見出しの組み方を借りた。緑の紙は使わず黒にし、桃色は文字だけに残した。

## Usage
1. `<canvas id="gl">` を hero の背面に置き、`<script>` ブロック全体を移植する。WebGL2 と `EXT_color_buffer_float` が必要。
2. 位置は `core`(`[x, y]` の高さ比、画面中心原点。既定 [0.18, 0.06]、モバイル [0, 0.1])、大きさは `P.scale`、粒子数は `P.side²`。
3. 腕の形は `P.arms / P.pitch / P.spread / P.diskScale`。色は頂点シェーダーの `warm / white / blue / dust`。
4. 引力が不要なら `P.pull = 0`。星の数の表示が不要なら `#count` の行を消す。

| パラメータ | 既定 | 意味 |
|---|---|---|
| side | 320 / 160(モバイル) | 粒子テクスチャの一辺。粒子数は二乗 |
| arms / pitch | 2 / 0.30 | 腕の本数、螺旋のピッチ角(rad) |
| diskScale / rMax | 0.85 / 2.7 | 指数円盤のスケール、最大半径 |
| spread | 0.30 | 腕からのばらつき |
| omega | 0.42 | 回転の速さ |
| tilt / roll | 1.0 / 0.35 | 視線の傾き、画面内の回転(rad) |
| scale | 0.33 / 0.42(モバイル) | 表示スケール(高さ比) |
| pull | 1.2 | カーソルの引力 |
| pointSize | 1.6 | 点の基準サイズ(px) |
| dprMax | 1.5 | 表示解像度の上限 |
