# 社内SNS・社内ポータル テンプレート 要件定義書
## プロダクト名：Souzoh Portal

> シリーズ4本目。**「デザインで殴る1本」**として位置づけます。
> 前3本が題材の説明に軸足を置いていたのに対し、本アプリは**開いた瞬間の印象**を最優先の要件に置きます。
> 記事のサムネイル・OGP・営業資料に使う画像は、すべてこのアプリから撮る想定です。

---

## 1. 目的とゴール

| 項目 | 内容 |
|---|---|
| コンセプト | 全社のお知らせ・社内の動き・組織の情報が1画面に集まる、ダッシュボード型の社内ポータル |
| 一次目的 | **一目で「おっ」と思わせる。** URLを開いて2秒で判断される勝負 |
| 二次目的 | 前3本のC2Cと違い**B2B（受注につながる題材）**。読者＝事業者が「自社にこれが欲しい」と直結する |
| 成功条件 | 記事のサムネイルにこの画面を使い、クリック率が他3本を上回る |
| 非ゴール | 実運用可能な社内システム。権限管理・監査ログ・SSO |

**なぜこの題材が"すごく見える"のか**：ポータルは**数字・グラフ・人・お知らせ・写真**が同時に載る唯一の題材です。前3本は一覧とカードしか出せませんでしたが、ここはKPI・組織図・カレンダー・ランキングを1画面に同居させられる。密度と多様性が、そのまま「作り込まれている感」になります。

---

## 2. 追加要件（今回の4条件）

本書では以下を**独立した必達要件**として扱い、それぞれ専用の章を設けています。

| 条件 | 対応章 | 実現方法の要旨 |
|---|---|---|
| ① デザインにとにかくこだわる | §5 | ベントーグリッド ＋ ダークインク基調 ＋ 可変フォント。§5.6にNGリストを明記 |
| ② ネットから自動で画像を取得しデモに組み込む | §6 | **ビルド時に取得するNode製スクリプト**（`npm run fetch:images`）＋ 3層フォールバック |
| ③ 動きをつける | §7 | View Transitions API ＋ スクロール連動 ＋ 段差入場。**14種類を定義** |
| ④ 一目ですごいと思わせる | §8 | 「最初の2秒」を専用に設計。ファーストビュー演出シーケンスを秒単位で規定 |

---

## 3. スコープ

### 3.1 作るもの

| # | 画面 | 優先度 | 母艦との関係 |
|---|---|---|---|
| P1 | ホーム（ベントーダッシュボード） | **S** | **完全新規**。このアプリの本体 |
| P2 | 社内フィード（タイムライン） | A | 母艦S1を改修 |
| P3 | お知らせ一覧・詳細 | A | 母艦S1/S2を改修 |
| P4 | メンバー名鑑・組織図 | A | **新規** |
| P5 | メンバー詳細 | A | 母艦S5を改修 |
| P6 | 投稿作成（分報・称賛） | B | 母艦S6を改修 |
| P7 | 全社検索（⌘K パレット） | A | **新規**。§8の"すごい"を担う要素 |
| P8 | メッセージ | C | 母艦S7をそのまま流用 |

### 3.2 作らないもの

- ❌ バックエンド・DB・認証・SSO・権限管理
- ❌ データ永続化（リロードで初期化）
- ❌ ファイルの実アップロード・実ダウンロード
- ❌ 実際のカレンダー連携・勤怠打刻の実処理
- ❌ ダークモード切替（**ダーク固定**。§5.2で理由を記述）
- ❌ 多言語、テストコード、CI

---

## 4. 技術構成

母艦を踏襲しつつ、**このアプリのみ3点追加**します。

```
Vite + React 18（JSX）
Tailwind CSS v4
lucide-react
────────── 以下このアプリのみ ──────────
motion（motion.dev）… レイアウトアニメーションのみに使用。§7.4に使用範囲を限定
recharts …………… KPIのスパークライン・面グラフ
cmdk ………………… ⌘Kコマンドパレット（P7）
```

**方針転換の理由**：前3本ではアニメーションライブラリを外しました。試行錯誤の往復が費用を食うためです。本アプリは要件③④が最優先なので判断を変えますが、**代わりに§7で動きを14種類・数値付きで確定**させています。ライブラリを入れても、仕様が確定していれば往復は増えません。増えるのは「もう少し滑らかに」を口頭で往復するときだけです。

**recharts を入れる理由**：KPIグラフを自前SVGで書くと、軸・目盛り・ツールチップの調整で必ず往復が出ます。ここは既製品が安いです。

---

## 5. デザイン仕様 ★要件①

### 5.1 方向性

シリーズ4本の視覚的な住み分けは次の通りです。

| アプリ | 基調 | 密度 | 主役 |
|---|---|---|---|
| SNS | 明るいウォームグレー | 低 | 文章 |
| Match | ディーププラム | 極低 | カード1枚 |
| Market | 白 | 高 | 価格 |
| **Portal** | **ディープインク** | **高＋階層あり** | **情報の集合そのもの** |

Portalが狙うのは**「管制室」**の質感です。暗い地に、わずかに浮いたガラス質のパネルが不揃いなサイズで並び、そこにだけ光が入る。KPIの数字とライムの差し色が暗闇で映え、写真がパネルの中で発色する。

**中心にあるのはベントーグリッド**です。均一なカードの反復ではなく、大小・縦横比の違うタイルを組み合わせて1画面を構成します。これが「最新のアプリっぽさ」の正体で、なおかつスクリーンショット1枚に情報が凝縮されるので、記事の画像として最も強い。

### 5.2 ダーク固定にする判断

社内ポータルの実運用はライトが主流です。それでもダーク固定にします。

- 要件④（一目ですごい）に対して、暗背景 ＋ 発光する差し色が最短距離であること
- 前3本がすべて明るい基調なので、4本並べたときの差が最大化されること
- 写真がダーク地では確実に映えること（要件②の効果が最大化される）

**ただしライト版は`theme.css`の値を差し替えるだけで出せる構造にします。**記事内で「トークンを差し替えるとこうなります」と両方見せられれば、テンプレートの価値の説明そのものになります。ライト版の値も§5.3に併記します。

### 5.3 デザイントークン（`src/theme.css`）

```css
@theme {
  /* ---- Base（ダーク） ---- */
  --color-bg:        #0B0D12;  /* ほぼ黒。わずかに青 */
  --color-surface:   #131721;  /* パネル面 */
  --color-surface-2: #1B2130;  /* パネル内の入れ子・入力欄 */
  --color-surface-3: #232B3D;  /* ホバー時 */
  --color-ink:       #EEF1F7;  /* 主要テキスト */
  --color-ink-sub:   #8B93A7;  /* メタ・ラベル */
  --color-ink-dim:   #5A6175;  /* さらに弱い情報 */
  --color-line:      #262E3F;  /* 1pxの区切り */
  --color-line-lit:  #38425C;  /* パネル上端のハイライト線 */

  /* ---- Accent ---- */
  --color-accent:    #C6F24E;  /* ライム：CTA・現在地・強調 */
  --color-accent-2:  #4CC9F0;  /* シアン：リンク・進行中 */
  --color-accent-3:  #F5A623;  /* アンバー：期限・注意 */
  --color-danger:    #FF5C6C;
  --color-ok:        #3DDC97;

  /* ---- Glass ---- */
  --glass-bg:     rgba(255,255,255,.035);
  --glass-border: rgba(255,255,255,.075);
  --glass-blur:   18px;

  /* ---- Type ---- */
  --font-display: "Space Grotesk", "Zen Kaku Gothic New", sans-serif;  /* 600/700 */
  --font-body:    "Noto Sans JP", sans-serif;                          /* 400/500 */
  --font-num:     "JetBrains Mono", monospace;                         /* KPI・日付・時刻 */

  --text-hero:   clamp(32px, 4.2vw, 56px)/1.05;  /* ★可変。PCで大きく効く */
  --text-kpi:    clamp(28px, 3.2vw, 44px)/1.0;
  --text-title:  20px/1.35;
  --text-body:   14px/1.8;
  --text-label:  11px/1.3;   /* 大文字＋letter-spacing .12em で使う */

  /* ---- Space ---- */
  --space-1: 4px;  --space-2: 8px;  --space-3: 12px;
  --space-4: 16px; --space-6: 24px; --space-8: 32px; --space-12: 48px;

  /* ---- Shape ---- */
  --radius-tile:  18px;
  --radius-inner: 12px;
  --radius-pill:  999px;
  --shadow-tile:  0 1px 0 rgba(255,255,255,.05) inset,
                  0 20px 50px rgba(0,0,0,.45);
  --shadow-lift:  0 30px 70px rgba(0,0,0,.6);

  /* ---- Motion（§7で全て参照） ---- */
  --ease-out:   cubic-bezier(.16,1,.3,1);
  --ease-inout: cubic-bezier(.65,0,.35,1);
  --dur-fast:   140ms;
  --dur-base:   240ms;
  --dur-slow:   520ms;
}
```

**ライト版の差し替え値**（記事用。この6行だけで切り替わる）

```css
--color-bg:#F5F6F8; --color-surface:#FFFFFF; --color-surface-2:#EFF1F5;
--color-ink:#0E1219; --color-ink-sub:#5C6478; --color-line:#E3E6EC;
--color-accent:#5B39F5;  /* ライムは白地で沈むのでインディゴに差し替え */
```

### 5.4 タイポグラフィの決め事

- **数字は必ず `--font-num` ＋ `font-variant-numeric: tabular-nums`。** KPIが並ぶ画面で桁がずれると一気に安っぽくなります
- ラベル（`今週の動き` など）は `--text-label` ＋ 大文字/カタカナ ＋ `letter-spacing: .12em` ＋ `--color-ink-sub`
- 見出しは `--font-display` の600以上、`letter-spacing: -0.02em`
- 日本語本文の行間は1.8。ダーク地では明るい地より行間を広く取らないと詰まって見えます

### 5.5 パネル（Tile）の作り

全タイル共通の質感を1コンポーネントに閉じ込めます。

```
背景：--glass-bg ＋ backdrop-filter: blur(var(--glass-blur))
枠 ：1px solid --glass-border
上端：1pxの内側ハイライト（--shadow-tile のinset部分）
角 ：--radius-tile
ホバー：translateY(-2px) ＋ --shadow-lift ＋ 枠が --color-line-lit へ（--dur-base）
```

> **`backdrop-filter` は必ず使ってください。** 背景に極薄のグラデーション（後述）を敷いた上でガラスを重ねることで、平面的なダークUIとの差が出ます。ここが「最新っぽさ」の実体の半分です。

**背景の下地**：`--color-bg` の上に、右上から `--color-accent` を `opacity .06` で、左下から `--color-accent-2` を `opacity .05` で、巨大な楕円グラデーションとして敷きます（`filter: blur(120px)`）。スクロールしても動きません。これがガラスの下に薄く透ける。

### 5.6 やらないこと（デザイン品質のためのNGリスト）

- ❌ 紫→ピンクのグラデーションボタン（2020年代前半の記号。古く見えます）
- ❌ 全タイルへの均一な影（§5.5のホバー時のみ）
- ❌ 絵文字をUIアイコンとして使う（lucide-reactに統一）
- ❌ 角丸の混在（`--radius-tile` / `--radius-inner` / `--radius-pill` の3種のみ）
- ❌ 中央揃えの長文（日本語の左揃えを崩さない）
- ❌ 3種を超えるフォント
- ❌ ネオン発光の多用（`--color-accent` の `box-shadow` 発光は**KPIタイル1枚とライブ表示のみ**）

---

## 6. 画像の自動取得 ★要件②

### 6.1 方針：ランタイム取得ではなく「ビルド時取得」

「ネットから自動で画像を持ってくる」の実装には2通りありますが、**ビルド時に取得してローカルに置く**方式を採ります。

| | ランタイム取得（`<img src="https://外部...">`） | **ビルド時取得（採用）** |
|---|---|---|
| 実装量 | 少ない | スクリプト1本（約80行） |
| 表示速度 | 毎回外部リクエスト。遅い・ちらつく | 自ドメイン配信。速い |
| サービス停止時 | **デモが全部壊れる** | 影響なし |
| レート制限 | 記事がバズると詰まる | 影響なし |
| 再現性 | 開くたびに絵が変わる | seedで固定 |

配布テンプレートで外部サービスに毎回問い合わせる作りにすると、**そのサービスが止まった日に全記事のデモが同時に壊れます**。実際、長年使われてきた `via.placeholder.com` は<cite index="17-1">数か月にわたって断続的にダウンし、告知も移行先の案内もないまま404や接続リセット、DNSタイムアウトを繰り返すようになりました</cite>。同様に <cite index="2-1">`source.unsplash.com` も廃止され、公式APIで同じことをする手段は用意されませんでした</cite>。この構造的リスクを、記事5本分背負う必要はありません。

### 6.2 取得元

`picsum.photos` を使います。<cite index="16-1">実在の写真を指定サイズで返す定番のプレースホルダサービスで、seedを指定すれば同じ画像を安定して取得できます。ただし本番の実行時依存にするのではなく、公開前にキャッシュか自前ホストへ移すのが前提の使い方です</cite>。<cite index="18-1">picsum.photos と placehold.co は2026年時点で稼働・保守されています</cite>が、それでも外部依存であることに変わりはないので、§6.1の方針で吸収します。

```
https://picsum.photos/seed/{seed}/{w}/{h}
```

seedを固定しているので、**何度実行しても同じ絵が返ります**。これが再現性の担保になります。

> **顔写真は取得しません。** 社内SNSにはメンバーの顔が要りますが、実在の人物の写真を架空の社員として配布テンプレートに載せるのは肖像の扱いとして避けるべきです。メンバーのアバターは母艦の**ID由来グラデーション＋頭文字**方式を使います（§6.5）。ダーク地では、この幾何学的なアバターのほうがむしろ洗練されて見えます。

### 6.3 スクリプト仕様（`scripts/fetch-images.mjs`）

```
npm run fetch:images
```

1. `src/data/imageManifest.js` を読む（seed・幅・高さ・用途の一覧）
2. 各エントリを `https://picsum.photos/seed/{seed}/{w}/{h}` から取得
3. `public/images/{seed}.jpg` に保存
4. 既に存在するファイルはスキップ（`--force` で上書き）
5. 取得失敗は握りつぶし、ログに1行出して次へ進む（**1枚の失敗でビルドを止めない**）
6. 完了後にサマリを出力：`取得 18 / スキップ 4 / 失敗 0`

**同時実行は4本まで**に絞ってください（`Promise` を4本ずつ処理）。無制限に並列化すると相手側に負荷をかけますし、レート制限にも当たります。

**`public/images/` は `.gitignore` に入れます。** 取得した写真をリポジトリにコミットして再配布すると、ライセンスの筋が曖昧になります。配るのは**スクリプトとマニフェストだけ**。利用者が自分で `npm run fetch:images` を叩く形にすれば、権利関係はその人と取得元の間で完結します。README冒頭にこの手順を書いてください。

### 6.4 マニフェスト（`src/data/imageManifest.js`）

```js
export default [
  // ヒーロー・特集
  { seed:"portal-hero",      w:1600, h:900, use:"P1ヒーロー" },
  { seed:"news-offsite",     w:1200, h:800, use:"お知らせ：全社合宿" },
  { seed:"news-office",      w:1200, h:800, use:"お知らせ：新オフィス" },
  { seed:"news-award",       w:1200, h:800, use:"お知らせ：表彰" },
  // フィード投稿
  { seed:"feed-desk",        w:1000, h:750, use:"分報：作業風景" },
  { seed:"feed-whiteboard",  w:1000, h:750, use:"分報：議論" },
  { seed:"feed-lunch",       w:1000, h:1000,use:"分報：ランチ" },
  { seed:"feed-launch",      w:1000, h:562, use:"分報：リリース" },
  { seed:"feed-training",    w:1000, h:750, use:"分報：研修" },
  { seed:"feed-site",        w:1000, h:562, use:"分報：現場" },
  // 部署カバー
  { seed:"team-dev",  w:800, h:400, use:"開発部" },
  { seed:"team-sales",w:800, h:400, use:"営業部" },
  { seed:"team-cs",   w:800, h:400, use:"CS部" },
  { seed:"team-corp", w:800, h:400, use:"管理部" },
  // イベント
  { seed:"event-allhands", w:900, h:600, use:"全社会議" },
  { seed:"event-study",    w:900, h:600, use:"勉強会" },
  { seed:"event-welcome",  w:900, h:600, use:"歓迎会" },
  { seed:"event-1on1",     w:900, h:600, use:"1on1週間" },
];
```

**18枚**です。この規模なら取得は数秒で終わります。

### 6.5 表示側：3層フォールバック（`ui/SmartImage.jsx`）

**絶対に壊れた画像アイコンを出さない**ための構造です。

```
第1層：/images/{seed}.jpg              ← ビルド時に取得済み。通常はここ
  ↓ onError
第2層：https://picsum.photos/seed/{seed}/{w}/{h}   ← 取得し忘れた場合の保険
  ↓ onError
第3層：seed由来の決定的グラデーション（母艦 GradientImage）  ← 必ず何かは映る
```

さらに全層共通で：

- 読み込み完了まで**seed由来の2色グラデーション**を敷き、完了時に `opacity 0→1`（`--dur-slow`）でクロスフェード。**白い箱がパッと出る現象を消します**
- `width` / `height` 属性を必ず指定してレイアウトシフトを防ぐ
- ファーストビュー外は `loading="lazy"` ＋ `decoding="async"`

> このコンポーネントは前3本にも後付けできます。母艦へ還元してください。

---

## 7. 動き ★要件③

### 7.1 原則

- **意味のない動きは入れない。** 出現・状態変化・因果の3つだけを表現する
- 主要な動きは `--dur-base`（240ms）、退場は入場の6割の速さ、大きな移動のみ `--dur-slow`（520ms）
- イージングは `--ease-out` を基本とし、往復する動きだけ `--ease-inout`
- **`prefers-reduced-motion: reduce` で下表の「停止」列がすべて止まること**（受け入れ基準）

### 7.2 動きの一覧（14種）

| # | 名称 | 対象 | 仕様 | reduced時 |
|---|---|---|---|---|
| 1 | 段差入場 | P1の全タイル | 下から16px＋opacity 0→1、`--dur-slow`、**60msずつ遅延**（最大12枚） | 停止（即表示） |
| 2 | KPIカウントアップ | KPI4枚の数字 | 0→目標値、900ms、`--ease-out`。桁は`tabular-nums`で固定 | 停止（即表示） |
| 3 | スパークライン描画 | KPIタイル | SVGパスを`stroke-dasharray`で左から1.1秒描画 | 停止（完成形） |
| 4 | スクロール連動リビール | P1下部・P2 | 画面下20%に入ったら 下から24px＋fade、`--dur-slow` | 停止 |
| 5 | タイルのホバー浮上 | 全タイル | `translateY(-2px)`＋影強化＋枠色、`--dur-base` | 影のみ |
| 6 | 画面遷移 | 全ページ | **View Transitions API**でクロスフェード＋4px上移動、`--dur-base` | 即時切替 |
| 7 | 共有要素モーフ | メンバー→詳細 | `view-transition-name`でアバターと氏名が連続移動 | 停止 |
| 8 | ⌘Kパレット | P7 | 背景 blur(0→10px)＋パネルが`scale .96→1`、`--dur-base` | フェードのみ |
| 9 | 検索結果の入れ替え | P7 | 各行20ms差でスライドイン。入力中はデバウンス120ms | 停止 |
| 10 | 称賛（拍手）演出 | フィード | ボタン押下でライムの粒が8個放射、600msで消滅＋数字がスライド | 数字のみ変化 |
| 11 | ライブ在席パルス | 在席中バッジ | 緑ドットが2.4秒周期でゆっくり明滅 | 停止 |
| 12 | 組織図の展開 | P4 | 部署をクリックでメンバーが高さアニメ＋段差入場（40ms差） | 即展開 |
| 13 | 進捗リング | P1「今週の目標」 | 円弧を`stroke-dashoffset`で0→達成率、1.2秒 | 完成形 |
| 14 | トースト | 全体 | 右下から16px上＋fade、3秒後に自動退場 | フェードのみ |

### 7.3 ファーストビューの演出シーケンス ★最重要

**開いてから2.0秒で完結**させます。長いローディング演出は逆効果です。

```
0ms     背景グラデーションのみ表示（ほぼ黒）
120ms   ヘッダーとサイドナビが fade-in
200ms   ヒーロータイル（大）が下から入場
260ms   KPIタイル4枚が 60ms差で順に入場   ← #1
400ms   KPIの数字が一斉にカウントアップ開始 ← #2
        同時にスパークラインが左から描画   ← #3
900ms   残りのタイルが 60ms差で入場
1400ms  全タイル着地
2000ms  完了（以降は静止。パルス#11のみ継続）
```

> **カウントアップとスパークラインを同時に走らせる**のが要点です。数字と線が同時に動く0.9秒が、このアプリで最も"すごい"瞬間になります。ここは実装で必ず確認してください。

### 7.4 motion ライブラリの使用範囲（限定）

無制限に使うと往復が増えます。**以下4つ以外はCSSで実装**してください。

- ✅ #7 共有要素モーフ（`layoutId`）
- ✅ #12 組織図の高さアニメーション
- ✅ #8 パレットの出入り
- ✅ #10 拍手の粒
- ❌ 上記以外（#1〜#6, #9, #11, #13, #14 はCSS / WAAPI / View Transitions で書く）

---

## 8. 「一目ですごい」の設計 ★要件④

### 8.1 最初の2秒で伝えるもの

読者はスクロールしません。**ファーストビュー1枚**で判断します。そこに以下が同時に載っている状態を作ります。

1. 大きな数字（KPI）が**動いている**
2. 写真が**発色している**（暗い地に写真が浮く）
3. 情報の**種類が多い**（数字・グラフ・人・写真・お知らせ・カレンダー）
4. タイルの**大きさが不揃い**（＝テンプレ感の否定）
5. 右上に**ライブ表示**（`在席 12名` の緑パルス）

### 8.2 ベントーグリッドの構成（P1・PC 1440px時）

12カラムグリッド。行の高さは140px単位。

```
┌─────────────────────────┬───────────┬───────────┐
│ ヒーロー：今週のハイライト      │ KPI①      │ KPI②      │
│ （写真＋見出し）  6col × 2row    │ 3col×1row │ 3col×1row │
│                                 ├───────────┼───────────┤
│                                 │ KPI③      │ KPI④      │
├─────────────┬───────────────────┴───────────┴───────────┤
│ 今週の目標      │ 社内フィード（直近5件・縦スクロール）        │
│ 進捗リング      │ 8col × 3row                              │
│ 4col × 3row    │                                           │
├─────────────┼─────────────────┬─────────────────────────┤
│ 在席メンバー    │ 今週の予定       │ 称賛ランキング             │
│ 4col × 2row    │ 4col × 2row     │ 4col × 2row              │
└─────────────┴─────────────────┴─────────────────────────┘
```

- **タブレット（769〜1279px）**：6カラムに再構成。ヒーローは6col、KPIは3col×2段
- **モバイル（〜768px）**：1カラム縦積み。KPIのみ2列。下部固定タブバー

> ★KPIを2×2の塊にしてヒーローの右に置くのが要点です。均等な4枚横並びは「ダッシュボードのテンプレ」に見えますが、**塊にすると意図のあるレイアウトに見えます**。

### 8.3 ⌘K コマンドパレット（P7）

要件④への貢献度が最も高い機能です。実装量に対して印象が跳ね上がります。

- `⌘K` / `Ctrl+K` で起動。ヘッダーにも `⌘K` の表示を出す
- 横断検索：メンバー・お知らせ・投稿・部署・申請メニュー
- 結果はカテゴリごとにグルーピングし、種別アイコン付き
- `↑↓` で移動、`Enter` で遷移、`Esc` で閉じる（**キーボードだけで完結すること**）
- 入力が空のときは「最近見たもの」5件と「クイックアクション」3件を表示
- 検索語が0件のときは「"◯◯" は見つかりませんでした」＋近い候補を提示

### 8.4 記事用スクリーンショットの撮り方（運用メモ）

- 撮影は **1440×900** のPC幅、ファーストビュー着地後（2.0秒経過後）
- ⌘Kを開いた状態のカットを**必ず1枚**押さえる（記事中盤の画像に最適）
- モバイル幅は390pxで2枚（ホーム・フィード）
- 前3本と並べるOGP用に、ライト版に切り替えたカットも1枚撮っておく

---

## 9. 画面仕様

### P1. ホーム（ベントーダッシュボード）★本体

**ヒーロータイル**：`portal-hero` の写真を背景に、下から `--color-bg` へのグラデーションを重ね、その上に今週のハイライト見出し＋日付＋`詳しく見る`。

**KPIタイル4枚**

| タイル | 数字 | 補助 |
|---|---|---|
| 今月の受注 | `¥ 8,420,000` | 前月比 `+12.4%`（`--color-ok`）＋ スパークライン |
| 稼働プロジェクト | `17` 件 | 内 `3` 件が納期3日以内（`--color-accent-3`） |
| 新規問い合わせ | `48` 件 | 今週 `+9`（スパークライン） |
| 平均残業 | `11.2` h | 前月比 `−2.8h`（`--color-ok`） |

数字は `--font-num` ＋ `--text-kpi`。**「今月の受注」タイルのみ** `--color-accent` の淡い発光を許可（§5.6の例外）。

**今週の目標**：進捗リング（#13）で `68%`。中央に大きく数字、下に `目標 25件 / 実績 17件`。

**社内フィード**：直近5件をコンパクト表示。タイル内で縦スクロール。`すべて見る` でP2へ。

**在席メンバー**：アバターを敷き詰め、在席中は緑パルス（#11）、離席は `opacity .45`。`12 / 28名` の表示。

**今週の予定**：日付ごとに3件。今日の行は `--color-accent` の縦線でマーク。

**称賛ランキング**：今月の称賛獲得数トップ5。1位のみ数字を大きく。

### P2. 社内フィード

- タブ：`全社` / `自部署` / `フォロー中`
- 投稿種別を色付きラベルで区別：`分報`（シアン）/ `称賛`（ライム）/ `お知らせ`（アンバー）/ `質問`（グレー）
- 称賛投稿は宛先メンバーのアバターを本文上に表示
- 拍手ボタン（#10）。リアクション絵文字は使わず、**拍手1種に統一**（§5.6のNGに沿う）
- スクロール連動リビール（#4）

### P3. お知らせ一覧・詳細

- 一覧：重要度で3段階（`必読` は左端にライムのバー ＋ 未読バッジ）
- カテゴリ：`人事` `総務` `システム` `イベント` `全社`
- 詳細：カバー写真 ＋ 本文 ＋ 添付ファイル風のリスト（クリックでトースト）＋ `既読にする`
- 既読にすると一覧のバッジが消え、P1の未読カウントも連動

### P4. メンバー名鑑・組織図

- 切替タブ：`名鑑` / `組織図`
- **名鑑**：部署フィルタ ＋ 検索。カードグリッド（アバター・氏名・部署・役職・在席状態・スキルタグ）
- **組織図**：部署をクリックで展開（#12）。部署カバー写真を薄く背景に敷く
- カードクリック → P5（共有要素モーフ #7）

### P5. メンバー詳細

- ヘッダー：部署カバー写真 ＋ アバター（モーフ着地点）
- 基本情報：部署・役職・入社年月・内線・在席状態
- `できること` スキルタグ
- 直近の投稿3件・獲得した称賛3件
- `メッセージを送る`（P8へ）

### P6. 投稿作成

- ボトムシート。種別を先に選ぶ：`分報` / `称賛` / `質問`
- **称賛を選ぶと宛先メンバーの選択UIが出る**（名鑑から複数選択可）
- 本文500字＋画像ピッカー（マニフェストの画像から選択）
- 投稿 → P2先頭に挿入 ＋ P1のフィードにも反映 ＋ トースト

### P7. ⌘Kコマンドパレット

§8.3の通り。

### P8. メッセージ

母艦S7をそのまま流用。送信1.2秒後に定型返信。

---

## 10. データモデル

```js
// member
{ id:"m01", name:"西澤 志門", kana:"にしざわ しもん",
  dept:"経営", role:"代表", joinedAt:"2019-04",
  ext:"101", present:true,
  skills:["事業開発","ノーコード","AI活用"],
  avatarSeed:"m01" }        // ★写真ではなくグラデ生成（§6.2）

// post
{ id:"f01", type:"log"|"praise"|"question"|"notice",
  memberId:"m04", body:"...", createdAt:"...",
  toMemberIds:["m07"],       // praise のみ
  image:{ seed:"feed-desk", w:1000, h:750 } | null,
  claps:14, clapped:false, comments:[...] }

// notice
{ id:"n01", category:"人事"|"総務"|"システム"|"イベント"|"全社",
  priority:"must"|"normal", title:"...", body:"...",
  image:{ seed:"news-offsite", w:1200, h:800 },
  publishedAt:"...", read:false, attachments:["就業規則_2026版.pdf"] }

// kpi
{ key:"revenue", label:"今月の受注", value:8420000, unit:"円",
  delta:+12.4, series:[6.1,6.8,7.2,7.0,7.9,8.4] }

// event
{ id:"e01", date:"2026-07-24", time:"10:00",
  title:"全社定例", place:"大会議室 / オンライン" }
```

---

## 11. デモデータ

### 11.1 メンバー（28名／主要12名を明記）

| id | 氏名 | 部署 | 役職 | 在席 | スキル |
|---|---|---|---|---|---|
| m00 | あなた | 開発 | メンバー | ✓ | — |
| m01 | 西澤 志門 | 経営 | 代表 | ✓ | 事業開発 / ノーコード / AI活用 |
| m02 | 菊池 星哉 | 開発 | テックリード | ✓ | アーキテクチャ / React / 要件定義 |
| m03 | 海野 遥 | 開発 | エンジニア | — | 実装 / API連携 / 自動化 |
| m04 | 高木 芽衣 | 営業 | マネージャー | ✓ | 提案 / 折衝 / 与件整理 |
| m05 | 相馬 隼人 | 営業 | メンバー | ✓ | 新規開拓 / 商談 |
| m06 | 三宅 千秋 | CS | リーダー | ✓ | 導入支援 / 研修設計 |
| m07 | 大野 湊斗 | CS | メンバー | — | 問い合わせ対応 / FAQ整備 |
| m08 | 若林 詩織 | 管理 | 人事 | ✓ | 採用 / 労務 / 制度設計 |
| m09 | 堀内 樹 | 管理 | 経理 | ✓ | 経理 / 資金繰り |
| m10 | 綾部 実咲 | 開発 | デザイナー | ✓ | UI設計 / 情報設計 |
| m11 | 郡司 蓮 | 営業 | メンバー | — | インサイドセールス |

残り16名は氏名・部署・役職のみで生成（在席は約4割をtrue）。**在席は12名**に調整してください（P1の `12 / 28名` と一致させる）。

### 11.2 KPI

| key | ラベル | 値 | 前月比 | series |
|---|---|---|---|---|
| revenue | 今月の受注 | 8,420,000 | +12.4% | 6.1 / 6.8 / 7.2 / 7.0 / 7.9 / 8.4 |
| projects | 稼働プロジェクト | 17 | +2 | 12 / 13 / 15 / 14 / 16 / 17 |
| inquiries | 新規問い合わせ | 48 | +9 | 31 / 35 / 33 / 40 / 39 / 48 |
| overtime | 平均残業 | 11.2h | −2.8h | 18.4 / 17.1 / 15.9 / 14.2 / 14.0 / 11.2 |

### 11.3 お知らせ（7件）

| 重要度 | カテゴリ | タイトル | 画像 |
|---|---|---|---|
| 必読 | 人事 | 2026年度下期の目標設定について（8月8日締切） | — |
| 必読 | システム | 経費精算システムの切り替えに伴う操作説明 | — |
| 通常 | イベント | 全社合宿を10月17〜18日に開催します | news-offsite |
| 通常 | 総務 | 新オフィス（蔵前）への移転が完了しました | news-office |
| 通常 | 全社 | 上期MVPを表彰しました | news-award |
| 通常 | 総務 | 夏季休暇の取得ルールについて | — |
| 通常 | システム | 社内Wikiの構成を見直しました | — |

### 11.4 フィード投稿（12件）

| 種別 | 投稿者 | 本文 | 画像 | 拍手 |
|---|---|---|---|---|
| 称賛 | m04 | 高田工業所の追加要件、m02さんが休日に整理してくれたおかげで月曜の打ち合わせが3分で終わりました | — | 47 |
| 分報 | m02 | 朝から詰まっていた同期処理、原因はこちらの設定漏れでした。手順書に追記しておきます | feed-desk | 12 |
| 分報 | m10 | 新しいポータルの配色、暗い地に振ってみました。写真がちゃんと発色するのが良い | feed-whiteboard | 31 |
| 称賛 | m06 | 導入研修の資料、m07さんが受講者の質問を全部拾って改訂してくれました | — | 38 |
| 分報 | m05 | 新規のお問い合わせが今週9件。年明け以来のペースです | — | 24 |
| 質問 | m03 | Cloudflare Workersの環境変数、本番と検証で分ける運用ってどうしてますか | — | 6 |
| 分報 | m08 | 来月の入社が2名決まりました。受け入れ準備を進めます | feed-training | 55 |
| 分報 | m09 | 上期の締めが見えてきました。経費精算は今週中にお願いします | — | 9 |
| 分報 | m01 | 新オフィス、思ったより静かで集中できます。窓際の席が人気 | feed-lunch | 41 |
| 分報 | m02 | リリース完了しました。今回は差し戻しゼロ | feed-launch | 62 |
| 称賛 | m01 | 現場対応でm03さんが即日で駆けつけてくれました。助かりました | feed-site | 33 |
| 質問 | m11 | 商談メモのテンプレート、みなさん何を使っていますか | — | 4 |

### 11.5 今週の予定（8件）

| 日付 | 時刻 | 予定 | 場所 |
|---|---|---|---|
| 7/23（木） | 10:00 | 全社定例 | 大会議室 / オンライン |
| 7/23（木） | 15:00 | 高田工業所 定例 | オンライン |
| 7/24（金） | 11:00 | 新規商談（製造業） | 先方オフィス |
| 7/24（金） | 17:30 | 開発ふりかえり | 開発エリア |
| 7/27（月） | 09:30 | 週次キックオフ | 大会議室 |
| 7/28（火） | 14:00 | 社内勉強会「AI活用の現在地」 | セミナールーム |
| 7/29（水） | 13:00 | 1on1 週間（〜7/31） | 各所 |
| 7/30（木） | 18:30 | 歓迎会 | 蔵前 |

### 11.6 称賛ランキング（今月）

| 順位 | メンバー | 獲得数 |
|---|---|---|
| 1 | m02 菊池 星哉 | 34 |
| 2 | m06 三宅 千秋 | 28 |
| 3 | m03 海野 遥 | 21 |
| 4 | m08 若林 詩織 | 19 |
| 5 | m04 高木 芽衣 | 16 |

---

## 12. ディレクトリ構成

```
souzoh-portal/
├── scripts/
│   └── fetch-images.mjs        ★新規：§6.3
├── public/images/              ← .gitignore対象。取得先
├── src/
│   ├── theme.css               ★差し替え（§5.3）
│   ├── app.config.js           ★差し替え（下記）
│   ├── data/
│   │   ├── members.js  posts.js  notices.js  kpis.js
│   │   ├── events.js   imageManifest.js
│   ├── components/ui/          ← 母艦からコピー
│   │   ├── SmartImage.jsx      ★新規：3層フォールバック（§6.5）→ 母艦へ還元
│   │   ├── Tile.jsx            ★新規：ガラスパネル（§5.5）
│   │   ├── CountUp.jsx         ← 母艦から流用
│   │   ├── ProgressRing.jsx    ★新規
│   │   └── Sparkline.jsx       ★新規（recharts薄ラッパ）
│   ├── components/shell/       ← 母艦からコピー（layout.maxWidth 対応済み）
│   ├── features/
│   │   ├── home/               ★新規：ベントーグリッド
│   │   ├── feed/  notices/  directory/  member/  composer/
│   │   ├── palette/            ★新規：⌘K
│   │   └── chat/               ← 母艦 dm/ をそのまま
│   ├── motion.js               ★新規：#1〜#14の定数を1箇所に
│   ├── store.js
│   └── main.jsx
└── README.md
```

```js
// app.config.js
export default {
  appName: "Souzoh Portal",
  tagline: "会社の「いま」が、ひと目で",
  layout: { maxWidth: 1440, bento: true },
  nav: [
    { key:"home",      label:"ホーム",     icon:"LayoutDashboard" },
    { key:"feed",      label:"フィード",   icon:"MessageSquare" },
    { key:"notices",   label:"お知らせ",   icon:"Megaphone" },
    { key:"directory", label:"メンバー",   icon:"Users" },
    { key:"me",        label:"マイページ", icon:"User" },
  ],
  labels: {
    listTitle: "社内の動き",
    itemNoun: "投稿",
    createCta: "投稿する",
    primaryAction: "拍手",
  },
};
```

---

## 13. 実装手順（Claude Codeへの投げ方）

**5分割。** Market同様に重い1本ですが、重さの理由が違います（固有画面数ではなく、演出の作り込み）。

| Session | やること | 冒頭で読ませるもの |
|---|---|---|
| 0 | 母艦を複製 → `theme.css` `app.config.js` `data/` 差し替え。**画面は触らずビルドを通す** | §5.3・§11・§12 |
| 1 | `scripts/fetch-images.mjs` ＋ `SmartImage` ＋ `Tile` ＋ `motion.js`。**画像18枚の取得までを完了させる** | §6全体・§5.5・§7.1 |
| 2 | P1ホーム（ベントーグリッド＋KPI＋演出シーケンス） | §5・§7.2(#1〜5,13)・§7.3・§8.2・§9(P1) |
| 3 | P7 ⌘Kパレット ＋ 画面遷移（View Transitions） ＋ 共有要素モーフ | §7.2(#6,7,8,9)・§8.3 |
| 4 | P2フィード ＋ P3お知らせ ＋ P6投稿作成 | §9(P2,P3,P6)・§7.2(#4,10) |
| 5 | P4名鑑・組織図 ＋ P5メンバー詳細 ＋ 仕上げ（reduced-motion全確認） | §9(P4,P5)・§7.2(#11,12)・§14 |

**Session 1を必ず独立させてください。** 画像が18枚ローカルに揃った状態でP1に入ると、レイアウト確認が1発で済みます。画像なしでグリッドを組むと、写真が入った瞬間に余白バランスの作り直しが発生します。**要件②を先に片付けることが、要件①のコストを下げます。**

**Session 2 の指示文テンプレ**：

```
添付の要件定義書 §5・§7.3・§8.2・§9(P1) を実装してください。
src/components/ui/ の Tile / SmartImage / CountUp と src/motion.js は完成済みです。
新規作成せず import して使ってください。
ベントーグリッドは CSS Grid の grid-template-areas で組み、
§8.2 の配置図をそのまま再現してください。
アニメーションの遅延・時間は src/motion.js の定数のみを参照し、
JSX に数値を直接書かないでください。
色・余白・角丸は src/theme.css の変数のみを使ってください。
実装前に、作成・変更するファイルの一覧だけ先に提示してください。
```

---

## 14. 受け入れ基準

**要件①（デザイン）**
- [ ] 生の色コード・生のpx値がJSXに1つも無い（全てトークン経由）
- [ ] 角丸が3種類のみ、フォントが3種類のみ
- [ ] タイルに `backdrop-filter` が効き、背景の淡いグラデーションが透けている
- [ ] KPIの数字が `tabular-nums` で桁揃えされている
- [ ] §5.6のNGリストに1つも該当しない

**要件②（画像）**
- [ ] `npm run fetch:images` で18枚が `public/images/` に取得される
- [ ] 2回目の実行でスキップされ、`--force` で再取得される
- [ ] `public/images/` を空にしても**壊れた画像アイコンが1つも出ない**（第2層・第3層が働く）
- [ ] 画像読み込み時にレイアウトシフトが起きない
- [ ] `public/images/` が `.gitignore` に入っている
- [ ] メンバーのアバターに実在人物の写真が使われていない

**要件③（動き）**
- [ ] §7.2の14種すべてが動作する
- [ ] `prefers-reduced-motion: reduce` で§7.2の「停止」列がすべて止まる
- [ ] ⌘K がキーボードのみで完結する（起動・移動・決定・閉じる）
- [ ] View Transitions 非対応ブラウザでも遷移が壊れない（即時切替にフォールバック）

**要件④（第一印象）**
- [ ] 初回表示が§7.3のシーケンス通りに進み、2.0秒で着地する
- [ ] カウントアップとスパークライン描画が同時に走る
- [ ] ファーストビューに「数字・グラフ・人・写真・お知らせ・カレンダー」が同時に見える
- [ ] タイルの大きさが不揃いで、均一グリッドに見えない

**共通**
- [ ] 390px / 1024px / 1440px の3幅で崩れない
- [ ] `npm run build` が警告なく通り、Cloudflare Pagesで表示される
- [ ] ライト版トークンに差し替えても破綻しない（記事用カット撮影のため）

---

## 15. 母艦テンプレへの還元

このアプリで作った以下は、**前3本にも後付けする価値があります**。

| 成果物 | 還元先 |
|---|---|
| `SmartImage.jsx`（3層フォールバック） | 全アプリ。特にMarketの商品写真が実写になる |
| `scripts/fetch-images.mjs` | 全アプリ。Marketの「実写推奨」がスクリプト一発で解決する |
| `motion.js`（動きの定数化） | 全アプリ |
| View Transitions による画面遷移 | 全アプリ。既存のフェード実装と置き換え可能 |

> 特に **`fetch-images.mjs` は Market の課題を直接解決します。** あちらの§4.4で「実写推奨」と書きながら手作業を前提にしていましたが、マニフェストを書いてスクリプトを叩くだけになります。Portalを先に作り、Marketに戻って適用するのが最も効率的です。

---

## 16. 補足：コスト感

シリーズ中でMarketと並んで重い1本ですが、**重さの質が違います**。Marketは状態遷移の複雑さ、Portalは演出の作り込みです。

演出は「もう少し滑らかに」「もう少し速く」の口頭往復が最も費用を食う領域なので、本書では§7.2で**14種すべてに数値**を与え、§7.3で**シーケンスをミリ秒単位**で確定させています。`motion.js` に定数を集約させる指示も同じ目的です。感覚的な指示を一切挟まずに済む状態を作ってあるので、そのまま渡してください。

調整が必要になった場合も、`motion.js` の数値を1箇所書き換えるだけで全体に反映されます。**再生成ではなく定数の編集で済む構造**にしてあることが、このアプリで最大のコスト対策です。
