# DESIGN.md — プレミアム不動産 / realestate-premium

> **使い方**: このファイルをプロジェクトに置き、AIに
> 「DESIGN.md に従って◯◯のサイトを作って」と指示してください。
> ライセンス: 商用利用可・改変可・クレジット不要（AIっぽくないデザイン見本帳 / サードパーティートラスト）

## 1. コンセプト

分譲マンション・邸宅の「落ち着いた高級感」を、大きな写真と品のある明朝、
低彩度の配色で組む。ただし余白と静けさに逃げず、物件サイトとしての実務
（間取り・専有面積・価格帯・立地・来場予約/資料請求のCV導線）を必ず備える。
「高級＝黒地に金グラデ＋全部大文字」の手癖を避け、金は塗らず**細線と点**でだけ使う。

**キーワード**: 静穏 / 邸宅 / 実務 / 上質 / 写真主役
**この見た目が向く場面**: 分譲マンション・注文住宅・高級賃貸・別荘地開発・
不動産デベロッパーのブランドサイト

## 2. タイポグラフィ

- 和文見出し: Zen Old Mincho / ウェイト 500（大見出しは500、価格・型番など
  強調したい語のみ600を使ってよい。本文には600を使わない）
- 和文本文: Zen Old Mincho / ウェイト 400 / サイズ 16px / 行間 1.95 / 字間 0.03em
- 欧文・数字: Cormorant Garamond（コンセプト英文・専有面積や価格の数字部分・
  節番号に使う。本文にイタリックで添える程度に留め、地の文の代替にしない。
  **Inter に置換しない**）
- 見出しスケール: h1（物件名） clamp(34px, 5vw, 56px) / h2（節見出し）
  clamp(24px, 3vw, 32px) / h3（table見出し・カード見出し） 18px /
  本文 16px / キャプション・ラベル 12.5px
- 特記: 英字ラベル（節番号・区分名）は 11.5〜12px・字間 0.16〜0.22em・
  大文字。**コンセプト見出し（h2「コンセプト」節のみ）に縦書き
  （`writing-mode: vertical-rl`）を使う。** 他の節見出しは横書きのまま
  （縦書きの多用は誌面性が強くなりすぎるため、コンセプト1箇所に限定する）。
  価格・専有面積などの数字は Cormorant Garamond で組み、桁の召喚力を出す

## 3. カラー

**low-saturation。純白・純黒・ビビッドカラーを使わない。**

| 役割 | 値 | 使用比率の目安 |
|---|---|---|
| 地（温白・ベース） | #f6f4ef | 60% |
| 地（節替え） | #efece3 | — |
| 墨茶（見出し・本文・強い要素） | #2a2723 | 30% |
| 弱文字（説明文・キャプション） | #6b6660 | — |
| 石グレー（罫線・境界・大きい要素の装飾のみ） | #8d8983 | — |
| くすみ金茶（アクセント） | #9c8961 | 5%以下 |
| 罫線（ヘアライン） | #ddd6c6 | — |

- 配色ルール: **くすみ金茶は「線」と「点」と「大きな文字」にのみ使う。**
  許される用法は ①節番号の細い下線 ②区切りの短い罫（幅16〜32px程度）
  ③価格・専有面積などの大きな数字（24px以上） ④予約状況ドットや必須項目
  マークなど、非文字の小さな点マーカー
  の4つ。**金を面（背景・カード・バッジの塗り）に使わない。金にグラデ・
  グロー・メタリック表現をかけない。金を本文サイズの文字色として使わない
  （文字自体は墨茶にし、金は文字に添える点や罫にとどめる）。**
- **コントラスト実測（両方向。全て背景 or 前景として使う色の組み合わせ）**:
  - `#2a2723` on `#f6f4ef` = **13.52:1**（本文・見出し、AA/AAA余裕）
  - `#2a2723` on `#efece3` = **12.58:1**（節替え背景でも同様に使える）
  - `#6b6660` on `#f6f4ef` = **5.17:1**（弱文字・キャプション、AA適合。
    これより明るいグレーを弱文字の既定色にしない）
  - `#6b6660` on `#efece3` = **4.81:1**（節替え背景でもAA適合）
  - `#9c8961`（金） on `#f6f4ef` = **3.10:1** / on `#efece3` = **2.88:1**
    — **AA本文基準(4.5:1)を満たさない。金を本文サイズ・小ラベルの文字色に
    使わない。使ってよいのは大きな数字（24px以上、AA-large基準3:1を満たす）
    と、文字ではない線・点のみ**
  - **大きな数字（`.num`、34px）に金を文字色として使う場合は、`--color-accent`
    をそのまま使わず、彩度・明度を落とした専用トークン `--color-accent-text:
    #7f6a40` を使う。** `#7f6a40` on `#f6f4ef` = **4.73:1**（AA-large基準
    3:1に対し余裕あり。`#9c8961` の 3.10:1 はぎりぎりでマージンがないため、
    このように文字用途では必ず一段暗い金を使う。線・罫・点などの非文字装飾は
    引き続き `--color-accent`（#9c8961）のままでよい）
  - `#8d8983`（石グレー） on `#f6f4ef` = **3.16:1** — 同様に本文色に
    使わない。罫線・大きな装飾要素の色として使う
  - `#f6f4ef`（温白） on `#2a2723`（墨茶地） = **13.52:1**（主ボタン・
    反転部の文字色として使用可）
  - `#9c8961`（金） on `#2a2723`（墨茶地） = **4.36:1** — AA本文基準に
    僅かに届かない。**暗い地の上でも金を本文サイズの文字に使わず、
    ホバー時の線・小さな数字ラベル（18px以上）に限定する**
- 青・紫・緑・信号色を一切追加しない。純白 #ffffff・純黒 #000000 を使わない

## 4. レイアウト・余白

- コンテンツ最大幅: 1280px（本文カラムは 640px 程度に絞る節もある）
- **split-screen** を骨格にする。ヘッダー直下から、左に幅 30%（最小280px）の
  `position: sticky` レール（物件名ロゴ・節ナビ・来場予約への短い導線）を置き、
  右70%が本文としてスクロールする。左右の境は 1px の罫線 `--color-line`
- **ヒーローは中央寄せの定型にしない。** 全幅裁ち落としの建築写真1枚を置き、
  写真下端に**不透明の墨茶帯**を敷いて「物件名（h1）＋所在地・総戸数の短い
  1行＋来場予約ボタン1つ」だけを乗せる。ボタンを2つ並べない。左サイドの
  スティッキーレールはヒーロー区間では非表示にし、写真だけを全画面に見せる
  （次節から左レールが現れる）
- セクション間余白: 上下 120px 基準（airy）。要素間は 8px の倍数
- 各節の頭に「節番号（Cormorant、例: 01）＋金の短い罫（24px）＋英字ラベル＋
  和文見出し」を置く。コンセプト節のみ見出しを縦書きにする
- プラン表・仕様リストは**罫線区切り**で組む。角丸カード＋影の3カラムに
  絶対に流用しない

## 5. コンポーネントの質感

- ヘッダー: ヒーロー区間は透明地・白文字、スクロールで不透明な `--color-bg`
  地・墨茶文字に切り替わる（`backdrop-filter` は使わず不透明背景に変える）。
  ヘッダーは `position: fixed` かつ `height: var(--header-h)` の固定高さを持ち、
  z-indexは左レールより高い値にする。**左スティッキーレールの `top` は必ず
  `var(--header-h)` を使い、`top: 0` にしてはならない**（レール上端の
  ブランド名・節ナビが不透明化後のヘッダーに恒久的に隠れる実装バグを防ぐため）。
- 左スティッキーレール: 地色 `--color-bg-alt`、物件名（小さめ、明朝）と
  節リンク（縦に並べる。現在地の節は金の短い下線が入る。他は下線なし、
  ホバーで文字が墨茶から金に変わるのではなく**字間が広がる**）。
  一番下に小さく「来場予約はこちら →」のテキストリンクを常設
- 角丸: 0px（すべて直角）/ 影: 使わない（`box-shadow` 禁止）/
  境界線: 1px solid `--color-line`。強調したい罫のみ 1px の金 `--color-accent`
- ボタン: 主ボタン（来場予約）は**墨茶地＋温白文字**、直角、ホバーで
  背景がわずかに明るくなる（`#2a2723`→`#3a352f`、0.2s）。副ボタン
  （資料請求）は透明地＋1px墨茶罫線＋墨茶文字、ホバーで罫線が金に変わる。
  **ヒーロー内は主ボタン1つのみ**。フォーム送信ボタンも同じ主ボタン様式。
  **ただしヒーロー帯（背景が既に墨茶）の上では主ボタンを反転させ、
  温白地＋墨茶文字にする**（地と同色のボタンは見えなくなるため。
  `.hero-band .btn-primary` で上書きする。§9コード例参照）
- プラン表（間取りタイプ）: `<table>` を使い、横罫線 `--color-line` のみで
  区切る（縦罫線は引かない）。タイプ名は明朝・専有面積と価格帯は
  Cormorant Garamondの数字で右寄せに揃える。価格帯の数字の地色は墨茶
  （`--color-ink`）とし、金は数字の直上に置く幅24px・高さ1pxの短い罫
  （§3②の用法）としてのみ使う。金を価格の文字色として使わない
- 仕様リスト: アイコンの代わりに「番号（Cormorant）＋項目名＋説明」を
  横罫線で1行ずつ区切る。角丸カードにしない
- カード的要素: 写真＋短いキャプションの組（例: 設備写真）は面や影で
  囲わず、写真の下に罫線1本＋キャプションのみ
- フォーム要素（資料請求・来場予約）: input/textarea は地色 `--color-bg`
  背景・下線1px `--color-line` のみ（囲み枠を作らない）・角丸0、
  フォーカスで下線が金 `--color-accent` に変わる。ラベルは上に12px・
  弱文字色で常時表示（placeholder依存にしない）。送信前に同意チェック
  ボックスを1つ置く（プライバシーポリシーへの同意、実務感を出す）
- 画像の扱い: 全写真に統一トーンフィルタ（§9）を適用。裁ち落とし・
  角丸なし。写真に文字を乗せる場合は不透明の墨茶帯のみ（半透明の
  グラデーションスクリムを使わない）
- アクセント（金）の適用範囲: §3の4用法に厳密に限定する

## 6. モーション

- 量: subtle
- **可視性はJSに依存させない（progressive enhancement）。** 節の登場演出の
  「隠れて→現れる」状態は、`<html>` に `js-motion` クラスが付いた場合のみ
  CSSで有効化する（`document.documentElement.classList.add('js-motion')`
  を `<head>` の先頭で同期実行）。このクラスが付かない限り（JS無効・
  ブロック・エラー時）、対象要素は常に `opacity: 1` の可視状態がデフォルト。
  本文・表・フォームを演出のために人質に取らない。**`.hero-photo` /
  `.hero-band` も例外なくこの原則に従う**（tier-B reviseで、この2要素だけ
  「デフォルトopacity:0・is-visible付与はJSのみ」という旧パターンが残り、
  JS無効時にヒーロー写真と価格帯が恒久的に見えなくなる実バグが見つかった。
  §9のヒーローCSS例のとおり修正済み）
- **非表示側のセレクタは必ず `:where(html.js-motion) .xxx` の形で書き、
  `html.js-motion .xxx` のように :where() なしで書かない。** 後者は型セレクタ
  `html` の分だけ詳細度が上がり、`.xxx.is-visible` を上回って opacity:0 が
  固定される（§9のコメント参照。tier-B reviseで発見・修正）
- 使う動き: 節がスクロールで視界に入ると opacity 0→1・translateY→0。
  **役割で速さと移動量を変える**（主役＝見出し・写真・表・フォームは
  `.reveal-primary` で 0.85〜1s・translateY 22〜24px・ゆるやかな
  easing（`cubic-bezier(0.16,1,0.3,1)`）でゆっくり大きく。脇役＝ラベル・
  本文・リスト項目は `.reveal` で 0.4〜0.5s・translateY 10px と短く小さく。
  リスト項目はさらに 60msずつ階段状にずらす）。ヒーロー写真は初回のみ
  1.4s かけて scale(1.05)→scale(1) のゆっくりしたズームアウト＋フェード
  （`window.addEventListener('load', …)` と `setTimeout` のみで起動。
  `requestAnimationFrame` は使わない）
- **IntersectionObserverの取りこぼしに対するフォールバック（必須）。**
  マウスホイールの連続大ティック・スクロールバードラッグ・Endキー等の
  高速/瞬間スクロールでは、要素が一度もビューポートと交差した状態を
  観測されないまま通過し、IOが発火せず `opacity: 0` に恒久固定される
  バグが起こり得る（実機検証で37要素中33要素が非表示のまま固定される
  再現あり）。対策として、`scroll`/`resize`/`load` のたびに（100ms
  デバウンス）、`getBoundingClientRect().top < window.innerHeight`
  （＝現在ビューポート内、またはすでに上端を通過済み）である未reveal
  要素を強制的に `is-visible` 化する関数を必ず併走させる。IOは初回の
  「動きながら現れる」演出のためだけに使い、可視性の最終保証はしない
- ヘッダーの不透明化は 0.3s。左レールの現在節インジケータ（金の下線）は
  0.25s で移動する
- 使わない動き: パララックス、自動カルーセル、数字のカウントアップ、
  マーキー、バウンス、均一0.6秒の一律フェード（全要素が同じ速さ・
  同じ移動量で現れる均一フェードは§Aの手癖に該当するため、上記の
  主役/脇役の速さ・移動量の差を必ず付ける）
- `prefers-reduced-motion: reduce` 時は全ての transition/animation を止め、
  要素は最終状態で即表示する。リセット対象には `.hero-photo` / `.hero-band`
  を含む全reveal系クラス（`.reveal` / `.reveal-primary` / `.section-label` /
  `h2.section-head`）を漏れなく列挙すること（§9コード例参照）

## 7. 写真・イラスト・図版

- 写真が主役（`ornament: photo-dominant`）。4点、Wikimedia Commonsの
  CC0 / パブリックドメインのみを使用。被写体は「建築外観・緑に包まれた
  アプローチ・石材の質感・木材の質感」。人物の顔アップは使わない
- 全写真に同一のトーン統一フィルタ `saturate(0.85) contrast(1.02)
  brightness(1.0)` を適用する（§9）。裁ち落とし・角丸なし
- ロケーション節は実写真に加え、周辺施設・徒歩分数を示す**簡易な線画の
  概念図（自作インラインSVG、駅・物件・主要施設を線と円で示す模式図）**
  を1点使ってよい。写実的な地図サービスの模倣（道路網の精密な再現、
  ピン留めアイコンの多用）はしない

## 8. 禁止事項（must not）

- Inter / Roboto / Arial / Noto Sans を使わない（Zen Old Mincho /
  Cormorant Garamond で組む）
- グラデーションを一切使わない（背景・文字・ボタン・罫すべて）。
  `box-shadow` を使わない。border-radius は常に 0
- **金（くすみ金茶）を面（背景・カード・バッジ）に塗らない。** 金に
  グラデーション・グロー・メタリック表現をかけない。金を本文サイズの
  文字色として使わない（§3のコントラスト実測に従う。線・点・大きな
  数字のみに限定）
- 純白 #ffffff・純黒 #000000を使わない。青・紫・緑・信号色を追加しない
- **「高級＝黒背景＋金グラデ＋セリフ全部大文字」を作らない。** 地は
  必ず温白 `#f6f4ef` を基調にする（暗い節替えを作る場合も #17130f 系の
  漆黒にはせず、`--color-bg-alt` の範囲に留める）
- 中央寄せの定型ヒーロー（特大見出し＋サブコピー＋ボタン2つ）を作らない。
  ヒーローの主ボタンは1つのみ
- 物件情報を省いて雰囲気だけで構成しない。**間取りタイプ・専有面積・
  価格帯・最寄り駅からの徒歩分数・総戸数を必ず数値で明記する**
- 「アイコン＋見出し＋説明」の角丸カード×3列を機能紹介・設備紹介に
  使わない（罫線区切りのリスト/表で組む）
- 絵文字・既製アイコンセットをアイコンに使わない（線画が必要な場合は
  自作インラインSVGのみ）
- 全要素を同じ0.6秒の均一フェードで登場させない（§6の速度差を守る）
- スクロール演出用の `opacity: 0` をCSSのデフォルト状態にしない。
  `js-motion` クラス（JS到達時のみ付与）でスコープし、かつIOの取りこぼしに
  備えたフォールバック強制表示（§6）を必ず実装する。「JSが発火しなければ
  本文が永久に見えない」設計を作らない（`.hero-photo` / `.hero-band` も含め
  例外を作らない）
- 非表示側のスコープ用セレクタを `:where()` なしの子孫セレクタ
  （例: `html.js-motion .reveal`）で書かない。型セレクタ`html`の分だけ
  詳細度が上がり `.reveal.is-visible` を上回って opacity:0 に固定される
  （§6・§9参照）
- グラスモーフィズム（半透明＋`backdrop-filter: blur`）を使わない
- 写真上の文字に半透明グラデーションのスクリムを使わない（不透明の
  墨茶帯のみ、§5・§9）
- stickyレールの `top` をヘッダー高さの補正なしに `0` にしない（fixedヘッダーが
  レール上端のブランド名・ナビを恒久的に覆い隠す構造バグを生むため。§9の
  コード例通り `var(--header-h)` を使うこと）

## 9. 実装ノート

```css
/* tokens.css — この変数セットをそのまま使うこと */
:root {
  --color-bg: #f6f4ef;        /* 温白ベース */
  --color-bg-alt: #efece3;    /* 節替え */
  --color-ink: #2a2723;       /* 見出し・本文・強い要素（墨茶。純黒にしない） */
  --color-text: #2a2723;
  --color-text-weak: #6b6660; /* 弱文字。#f6f4efで5.17:1・#efece3で4.81:1 */
  --color-stone: #8d8983;     /* 石グレー。罫線・大きい装飾要素のみ（本文色にしない） */
  --color-accent: #9c8961;    /* くすみ金茶。線・点のみ（文字色にしない。§3参照） */
  --color-accent-text: #7f6a40; /* 金を大きな数字(24px以上)の文字色に使う場合専用。#f6f4efで4.73:1 */
  --color-line: #ddd6c6;      /* ヘアライン罫 */

  /* 変数には総称ファミリ(serif)を含めないこと。フォールバック連結が壊れる */
  --font-heading: "Zen Old Mincho";
  --font-body: "Zen Old Mincho";
  --font-latin: "Cormorant Garamond";

  --max-width: 1280px;
  --measure: 640px;
  --radius: 0;
  --rail-width: 30%;
  --header-h: 64px; /* ヘッダーの実高さ。stickyレールのtopオフセット計算に使う */

  --photo-filter: saturate(0.85) contrast(1.02) brightness(1.0);
}
```

- フォント読み込み:
  `https://fonts.googleapis.com/css2?family=Cormorant+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&family=Zen+Old+Mincho:wght@400;500;600;700&display=swap`
- 本文の font-family は `var(--font-latin), var(--font-body), serif` の順に
  指定する（英数字が自動で Cormorant Garamond になる）

**写真のトーン統一（全写真に必ず適用。裁ち落とし・角丸なし）:**
```css
.ph, .hero-photo img { filter: var(--photo-filter); object-fit: cover; border-radius: 0; }
```

**split-screenの骨格（左レールsticky・右がスクロール本文。ヒーローは右カラム内で全幅に抜く）:**
```css
.site-header { position: fixed; top: 0; left: 0; right: 0; height: var(--header-h); z-index: 40; /* または相応の値 */ }
.shell { display: flex; align-items: flex-start; }
.rail {
  width: var(--rail-width); min-width: 280px;
  position: sticky; top: var(--header-h); height: calc(100vh - var(--header-h));
  background: var(--color-bg-alt);
  border-right: 1px solid var(--color-line);
  display: flex; flex-direction: column; justify-content: space-between;
  padding: 40px 32px;
}
.main { flex: 1; min-width: 0; }
@media (max-width: 900px) {
  .shell { flex-direction: column; }
  .rail { position: static; width: 100%; height: auto; }
}
```

**ヒーロー（全幅写真＋下端の不透明帯＋ボタン1つ。中央寄せ定型にしない）:**
```html
<section class="hero">
  <img class="hero-photo" src="…" alt="緑のアプローチに包まれたクレフ代々木上原のエントランス外観">
  <div class="hero-band">
    <p class="hero-eyebrow">01 — CREF YOYOGI-UEHARA</p>
    <h1 class="hero-title">クレフ代々木上原</h1>
    <p class="hero-meta">東京都渋谷区 ／ 小田急線・千代田線「代々木上原」駅 徒歩5分 ／ 総戸数48戸</p>
    <a class="btn-primary" href="#reserve">モデルルーム来場予約</a>
  </div>
</section>
```
```css
.hero { position: relative; height: 100vh; min-height: 560px; overflow: hidden; }
/* .reveal系と同じ理由でデフォルトは常に可視。js-motion配下でのみ初期非表示にする
   （§6・§9「節の登場」参照。ここを .reveal 系と揃え忘れると、JS無効/失敗時に
   ヒーロー写真と価格帯が恒久的に見えなくなる事故が起こる） */
.hero-photo { width: 100%; height: 100%; object-fit: cover; filter: var(--photo-filter); opacity: 1; transform: none; }
:where(html.js-motion) .hero-photo { opacity: 0; transform: scale(1.08); }
.hero-photo.is-visible { opacity: 1; transform: scale(1); }
.hero-band {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: var(--color-ink);   /* 不透明。半透明グラデにしない */
  color: var(--color-bg);
  padding: 32px clamp(20px, 5vw, 64px) 40px;
  opacity: 1; transform: none;
}
:where(html.js-motion) .hero-band { opacity: 0; transform: translateY(16px); }
.hero-band.is-visible { opacity: 1; transform: translateY(0); }
.hero-title { font-family: var(--font-heading), serif; font-weight: 500; font-size: clamp(34px, 5vw, 56px); }
/* var(--color-bg) on var(--color-ink) = 13.52:1。写真の上でも常に高コントラストを確保 */

/* ヒーロー帯の地が墨茶のため、主ボタンはこの節だけ反転させる（地と同色で消えるのを防ぐ） */
.hero-band .btn-primary { background: var(--color-bg); color: var(--color-ink); border-color: var(--color-bg); }
.hero-band .btn-primary:hover { background: #e9e4d8; border-color: #e9e4d8; }
```

**コンセプト見出しの縦書き（この1箇所のみに限定）:**
```css
.concept-heading {
  writing-mode: vertical-rl; text-orientation: mixed;
  font-family: var(--font-heading), serif; font-weight: 500;
  font-size: clamp(28px, 4vw, 40px); letter-spacing: 0.14em;
}
```

**プラン表（縦罫線なし・横罫線のみ。金は価格の数字の上の短い罫にだけ）:**
```css
.plan-table { width: 100%; border-collapse: collapse; }
.plan-table th, .plan-table td { padding: 18px 12px; border-bottom: 1px solid var(--color-line); text-align: left; }
.plan-table .num { font-family: var(--font-latin), serif; text-align: right; }
.plan-table .price { color: var(--color-ink); font-family: var(--font-latin), serif; font-size: 22px; font-weight: 600; }
.plan-table .price .price-rule { display: block; width: 24px; height: 1px; background: var(--color-accent); margin: 0 0 8px auto; }
/* 価格の地文字は墨茶（13.52:1）。金は数字の直上の短い罫（幅24px、§3②の用法・非文字の
   装飾なので3.10:1でWCAG非テキストコントラスト基準を満たす）としてのみ使い、文字色にしない */
```

**必須マーク（フォームラベル。金を文字色にせず、墨茶文字＋金の小さな点に限定）:**
```css
.form-field label .req { color: var(--color-ink); margin-left: 6px; font-weight: 600; }
.form-field label .req::before {
  content: ''; display: inline-block; width: 4px; height: 4px;
  background: var(--color-accent); margin-right: 4px; vertical-align: 1px;
}
/* 「必須」の文字は墨茶（13.52:1）。金は文字の前に置く4px角の点のみ（§3④に準じる用法） */
```

**節の登場（役割ごとに速さ/移動量を変える。可視性はJS非依存＝js-motionクラスでスコープし、
IOの取りこぼしをフォールバックで必ず救済する。rAFは使わずsetTimeout/IntersectionObserverのみ）:**
```html
<head>
<script>document.documentElement.classList.add('js-motion');</script>
```
```css
/* デフォルトは常に可視。js-motion配下でのみ「隠れて→現れる」演出を持つ */
.reveal, .reveal-primary { opacity: 1; transform: none; }
/*
  詳細度に関する重要な注意（tier-B reviseで発見した実バグ）: 非表示側は必ず
  :where(html.js-motion) でラップすること。`html.js-motion .section-label` のように
  :where() なしで書くと、詳細度は 型セレクタ`html`1つ＋クラス2つ＝(0,2,1)になる。
  一方 `.section-label.is-visible` はクラス2つのみ＝(0,2,0)。CSS詳細度の比較は
  (インラインstyle, ID数, クラス/属性/疑似クラス数, 型/擬似要素数) の順で行われるため、
  クラス数が同数(2=2)の場合は型セレクタの有無で決着し、(0,2,1) > (0,2,0) となって
  非表示側が勝ってしまい、`is-visible` を付与しても opacity:1 に戻らず本文が恒久的に
  見えなくなる（実機検証で37要素中33要素が固定表示のまま）。:where() は中身の詳細度を
  常にゼロにするため、非表示側を (0,1,0) まで下げれば `.xxx.is-visible`（0,2,0）が
  常に上回る。`.hero-photo` / `.hero-band` も含め、「JS到達時のみ非表示にする」
  全クラスにこの :where() パターンを例外なく適用すること
*/
:where(html.js-motion) .section-label { opacity: 0; transform: translateY(10px); transition: opacity .5s ease, transform .5s ease; }
:where(html.js-motion) h2.section-head { opacity: 0; transform: translateY(22px); transition: opacity 1s cubic-bezier(.16,1,.3,1) .12s, transform 1s cubic-bezier(.16,1,.3,1) .12s; }
:where(html.js-motion) .reveal { opacity: 0; transform: translateY(10px); transition: opacity .4s ease, transform .4s ease; } /* 脇役: ラベル・本文・リスト項目 */
:where(html.js-motion) .reveal-primary { opacity: 0; transform: translateY(24px); transition: opacity .85s cubic-bezier(.16,1,.3,1), transform .85s cubic-bezier(.16,1,.3,1); } /* 主役: 見出し・写真・表・フォーム */
.section-label.is-visible, h2.section-head.is-visible, .reveal.is-visible, .reveal-primary.is-visible { opacity: 1; transform: translateY(0); }
```
```js
window.addEventListener('load', function () {
  setTimeout(function () {
    document.querySelector('.hero-photo').classList.add('is-zoomed-out');
    document.querySelector('.hero-band').classList.add('is-visible');
  }, 80);
});

var revealTargets = document.querySelectorAll('.reveal, .reveal-primary, .section-label, h2.section-head');
var io = new IntersectionObserver(function (entries) {
  entries.forEach(function (e) {
    if (e.isIntersecting) { e.target.classList.add('is-visible'); io.unobserve(e.target); }
  });
}, { threshold: 0.12, rootMargin: '200px 0px -8% 0px' });
revealTargets.forEach(function (el, i) {
  var group = el.closest('.spec-item, .loc-access-item, .loc-facility-item');
  if (group) { el.style.transitionDelay = (i % 8) * 60 + 'ms'; }
  io.observe(el);
});

/* フォールバック救済: 高速/瞬間スクロールでIOが交差を観測できず opacity:0 のまま
   固定される事故を防ぐ。scroll/resize/load のたびに、ビューポート上端に到達済み
   （＝見えている、または通過済み）の未reveal要素を強制的にis-visible化する */
function revealPassedElements() {
  var vh = window.innerHeight;
  revealTargets.forEach(function (el) {
    if (el.classList.contains('is-visible')) { return; }
    if (el.getBoundingClientRect().top < vh) { el.classList.add('is-visible'); io.unobserve(el); }
  });
}
var revealFallbackTimer = null;
function scheduleRevealFallback() {
  if (revealFallbackTimer) { clearTimeout(revealFallbackTimer); }
  revealFallbackTimer = setTimeout(revealPassedElements, 100);
}
window.addEventListener('scroll', scheduleRevealFallback, { passive: true });
window.addEventListener('resize', scheduleRevealFallback, { passive: true });
window.addEventListener('load', revealPassedElements);
revealPassedElements();
```
```css
/* prefers-reduced-motion: リセット対象は .hero-photo / .hero-band を含む
   reveal系クラス全部を漏れなく列挙する（1つでも抜けると、その要素だけ
   OSのモーション削減設定を無視して transition したままになる） */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
  .hero-photo, .hero-band, .reveal, .reveal-primary, .section-label, h2.section-head {
    opacity: 1 !important; transform: none !important;
  }
}
```

- モバイル（900px以下）: 左レールは静的配置に戻し（`position: static`）、
  ヒーロー帯の情報は縦積みのまま維持する。プラン表は横スクロール
  `overflow-x: auto` の1つのテーブルのまま維持し、カードへの変形はしない
- `prefers-reduced-motion: reduce` では全 transition/animation を `none` にし、
  最終状態で表示する
