# WTE Business Card — WordPress テーマ 完全仕様書

> **バージョン**: 1.0.0  
> **テキストドメイン**: `wte_`  
> **テーマ名**: WTE Business Card  
> **対象 WordPress**: 6.5 以上（推奨 6.7+）  
> **対象 PHP**: 8.1 以上（推奨 8.2+）  
> **ライセンス**: GPL v2 or later  
> **対象ユーザー**: 個人事業主・フリーランス・士業  
> **コンセプト**: カスタマイザーへの情報入力だけで完結する、名刺代わりのワンページ LP 型テーマ  
> **作成日**: 2026年5月

---

## 目次

1. [プロジェクト概要・設計思想](#1-プロジェクト概要設計思想)
2. [技術スタック・開発環境](#2-技術スタック開発環境)
3. [デザインシステム](#3-デザインシステム)
4. [テーマアーキテクチャ](#4-テーマアーキテクチャ)
5. [カスタマイザー設計（全セクション詳細）](#5-カスタマイザー設計全セクション詳細)
6. [フロントページ（LP）構成](#6-フロントページlp構成)
7. [お問い合わせページ自動生成](#7-お問い合わせページ自動生成)
8. [カスタムフィールド設計（事業概要詳細・FAQ）](#8-カスタムフィールド設計事業概要詳細faq)
9. [JSON-LD 構造化データ自動生成](#9-json-ld-構造化データ自動生成)
10. [SNS 導線コンポーネント](#10-sns-導線コンポーネント)
11. [カスタマイザー・プレビュー即時反映設計](#11-カスタマイザープレビュー即時反映設計)
12. [reCAPTCHA v3 統合](#12-recaptcha-v3-統合)
13. [パフォーマンス・アクセシビリティ・SEO](#13-パフォーマンスアクセシビリティseo)
14. [セキュリティ実装要件](#14-セキュリティ実装要件)
15. [WordPress コーディング標準への準拠](#15-wordpress-コーディング標準への準拠)
16. [テーマ有効化時の自動セットアップ](#16-テーマ有効化時の自動セットアップ)
17. [多言語・国際化対応](#17-多言語国際化対応)
18. [テスト要件](#18-テスト要件)
19. [商用配布チェックリスト](#19-商用配布チェックリスト)
20. [ディレクトリ構造](#20-ディレクトリ構造)

---

## 1. プロジェクト概要・設計思想

### 1.1 テーマの目的

WTE Business Card は、個人事業主・フリーランサー・士業など、「Web制作に詳しくないが、きちんとしたオンライン名刺が欲しい」というユーザーを主要ターゲットとする WordPress クラシックテーマである。ブロックエディターを一切操作させず、**WordPress カスタマイザー（Customizer）上の入力フォームへの記入だけでサイトが完成する**ことを最大の設計目標とする。

### 1.2 設計の 5 原則

| 原則 | 内容 |
|------|------|
| **ゼロ・フリクション** | ブロックエディター操作不要。カスタマイザーだけで完結 |
| **LP 完結型** | すべてのコンテンツを 1 ページに集約（スクロール型 LP） |
| **ミニマル品質** | 要素を削ぎ落としつつ、清潔感・信頼感を最大化したデザイン |
| **即時フィードバック** | カスタマイザーの全設定が `postMessage` または `selective_refresh` でリロードなし反映 |
| **SEO ファースト** | JSON-LD 自動生成により、入力内容が即座に構造化データへ変換 |

### 1.3 テーマタイプの選択根拠

2026年時点において WordPress は Full Site Editing（FSE）をデフォルト推奨としている。しかし本テーマは以下の理由から **Classic Theme** を採用する：

- カスタマイザー API を最大限活用するため（FSE は Customizer との相性が低下）
- ブロックエディターを一切露出しない設計を担保するため
- `theme.json` の複雑性を排除し、CSS カスタムプロパティで一元管理するため
- `WP_Customize_Control` の完全制御による UX の統一を図るため

---

## 2. 技術スタック・開発環境

### 2.1 サーバーサイド

| 項目 | 要件・選択理由 |
|------|----------------|
| **PHP** | 8.1 以上必須。型宣言（`declare(strict_types=1)`）を全ファイルに記述。`match` 式・名前付き引数を積極活用 |
| **WordPress** | 6.5 以上。`add_theme_support` で Classic Theme 機能を明示宣言 |
| **テンプレート階層** | `front-page.php`, `page-contact.php`, `404.php`, `header.php`, `footer.php` |
| **データ保存** | `theme_mod`（`get_theme_mod`/`set_theme_mod`）を使用。`option` 型は使用しない |
| **Sanitize** | 全 Customizer 設定に適切な `sanitize_callback` を必須実装 |
| **Escape** | 出力時は必ず `esc_html()`, `esc_url()`, `esc_attr()`, `wp_kses_post()` を使い分け |

### 2.2 フロントエンド

| 項目 | 内容 |
|------|------|
| **CSS** | CSS カスタムプロパティ（`--wte-*`）で全デザイントークンを管理。ネスティング記法（PostCSS または純 CSS Level 4）を使用 |
| **JavaScript** | バニラ JS（ES2022+）。jQuery は**使用しない**（軽量化のため）。`wp.customize` API を直接操作 |
| **ビルドツール** | Vite（開発時）+ PostCSS（CSS 変換）+ Terser（JS 圧縮）。ビルド成果物のみテーマに含める |
| **フォント読み込み** | Google Fonts を `<link rel="preconnect">` + `display=swap` で遅延なく読み込み。フォント選択は Customizer から |
| **画像** | `srcset` + `sizes` によるレスポンシブ画像。WebP 自動生成を前提とした `wp_get_attachment_image` 使用 |
| **アイコン** | SVG インラインスプライト方式。外部 icon font ライブラリは使用しない |

### 2.3 推奨開発環境

```
Node.js    : 20.x LTS 以上
npm        : 10.x 以上
PHP        : 8.2（Local / WP-ENV / DevKinsta）
WordPress  : 6.7 以上
デバッグ   : WP_DEBUG=true, WP_DEBUG_LOG=true, SCRIPT_DEBUG=true
テスト     : Theme Check Plugin, Theme Unit Test Data（wptest.io）
```

---

## 3. デザインシステム

### 3.1 カラートークン（デフォルト値）

```css
:root {
  /* ---- ベースカラー（変更不可） ---- */
  --wte-white:          #ffffff;
  --wte-gray-50:        #f8f9fa;  /* 最薄グレー：セクション背景 */
  --wte-gray-100:       #f1f3f4;  /* 薄グレー：カード背景 */
  --wte-gray-200:       #e8eaed;  /* ボーダー */
  --wte-gray-400:       #9aa0a6;  /* プレースホルダー・補助テキスト */
  --wte-gray-600:       #5f6368;  /* 補足テキスト */
  --wte-charcoal:       #3c4043;  /* ボディテキスト（チャコールグレー） */
  --wte-black:          #1a1a1a;  /* 見出し */

  /* ---- カスタマイザー制御カラー（デフォルト） ---- */
  --wte-color-header-bg:      #ffffff;
  --wte-color-header-text:    #1a1a1a;
  --wte-color-footer-bg:      #3c4043;
  --wte-color-footer-text:    #f8f9fa;
  --wte-color-accent:         #2d6be4;  /* アクセントカラー */
  --wte-color-link:           #2d6be4;  /* リンクカラー */
  --wte-color-link-hover:     #1a4fba;

  /* ---- スペーシング ---- */
  --wte-space-xs:   0.25rem;
  --wte-space-sm:   0.5rem;
  --wte-space-md:   1rem;
  --wte-space-lg:   1.5rem;
  --wte-space-xl:   2rem;
  --wte-space-2xl:  3rem;
  --wte-space-3xl:  4rem;
  --wte-space-4xl:  6rem;

  /* ---- タイポグラフィ ---- */
  --wte-font-body:      'Noto Sans JP', sans-serif;
  --wte-font-heading:   'Noto Serif JP', serif;
  --wte-font-size-base: 1rem;          /* 16px */
  --wte-line-height:    1.75;
  --wte-font-weight-normal: 400;
  --wte-font-weight-medium: 500;
  --wte-font-weight-bold:   700;

  /* ---- ボーダー・シャドウ ---- */
  --wte-radius-sm:   4px;
  --wte-radius-md:   8px;
  --wte-radius-lg:   16px;
  --wte-shadow-sm:   0 1px 3px rgba(0,0,0,0.08);
  --wte-shadow-md:   0 4px 12px rgba(0,0,0,0.10);
  --wte-shadow-lg:   0 8px 30px rgba(0,0,0,0.12);

  /* ---- トランジション ---- */
  --wte-transition-fast:   150ms ease;
  --wte-transition-normal: 250ms ease;
  --wte-transition-slow:   400ms ease;

  /* ---- コンテナ幅 ---- */
  --wte-container-sm:  640px;
  --wte-container-md:  768px;
  --wte-container-lg:  1024px;
  --wte-container-xl:  1200px;
}
```

### 3.2 フォントオプション（カスタマイザー選択肢 5 種）

| ID | 表示名 | ファミリー | 特徴 |
|----|--------|-----------|------|
| `noto_sans_jp` | Noto Sans JP（デフォルト） | `'Noto Sans JP', sans-serif` | 可読性・親しみやすさ |
| `noto_serif_jp` | Noto Serif JP | `'Noto Serif JP', serif` | 伝統的・信頼感 |
| `zen_kaku_gothic` | Zen Kaku Gothic New | `'Zen Kaku Gothic New', sans-serif` | モダン・スタイリッシュ |
| `shippori_mincho` | Shippori Mincho | `'Shippori Mincho', serif` | 和風・高級感 |
| `m_plus_1p` | M PLUS 1p | `'M PLUS 1p', sans-serif` | シンプル・技術系 |

フォント変更時は、対応する Google Fonts URL を `wp_head` で動的エンキュー。**使用しないフォントは読み込まない**（パフォーマンス最適化）。

### 3.3 タイポグラフィスケール

```css
/* 見出し階層 */
h1 { font-size: clamp(2rem,   5vw, 3.5rem);  line-height: 1.2; }
h2 { font-size: clamp(1.5rem, 3vw, 2.25rem); line-height: 1.3; }
h3 { font-size: clamp(1.2rem, 2vw, 1.5rem);  line-height: 1.4; }
h4 { font-size: clamp(1rem,   1.5vw, 1.2rem);line-height: 1.5; }

/* ボディ・補助テキスト */
body { font-size: 1rem; line-height: 1.75; }
.wte-text-sm   { font-size: 0.875rem; }
.wte-text-xs   { font-size: 0.75rem; }
.wte-text-lead { font-size: 1.125rem; line-height: 1.8; }
```

### 3.4 レスポンシブブレークポイント

```css
/* Mobile First */
/* sm */ @media (min-width: 640px)  { ... }
/* md */ @media (min-width: 768px)  { ... }
/* lg */ @media (min-width: 1024px) { ... }
/* xl */ @media (min-width: 1200px) { ... }
```

---

## 4. テーマアーキテクチャ

### 4.1 ファイル読み込み順序（functions.php）

`functions.php` は **オーケストレーター**としてのみ機能し、直接ロジックを記述しない。すべての機能は `inc/` 以下の独立ファイルに分離する。

```php
<?php
declare(strict_types=1);

// 機能ファイルの読み込み順序（依存関係順）
require_once get_template_directory() . '/inc/helpers/sanitize.php';       // 1. サニタイズヘルパー
require_once get_template_directory() . '/inc/helpers/escape.php';         // 2. エスケープヘルパー
require_once get_template_directory() . '/inc/helpers/schema.php';         // 3. スキーマヘルパー
require_once get_template_directory() . '/inc/setup/theme-support.php';    // 4. テーマサポート
require_once get_template_directory() . '/inc/setup/enqueue.php';          // 5. アセット登録
require_once get_template_directory() . '/inc/setup/setup-pages.php';      // 6. ページ自動生成
require_once get_template_directory() . '/inc/customizer/panels.php';      // 7. パネル定義
require_once get_template_directory() . '/inc/customizer/sections.php';    // 8. セクション定義
require_once get_template_directory() . '/inc/customizer/settings.php';    // 9. 設定定義
require_once get_template_directory() . '/inc/customizer/controls.php';    // 10. コントロール定義
require_once get_template_directory() . '/inc/customizer/callbacks.php';   // 11. サニタイズコールバック
require_once get_template_directory() . '/inc/customizer/output.php';      // 12. 動的CSS出力
require_once get_template_directory() . '/inc/meta/custom-fields.php';     // 13. カスタムフィールド
require_once get_template_directory() . '/inc/meta/meta-boxes.php';        // 14. メタボックス
require_once get_template_directory() . '/inc/seo/json-ld.php';            // 15. JSON-LD生成
require_once get_template_directory() . '/inc/seo/meta-tags.php';          // 16. メタタグ
require_once get_template_directory() . '/inc/nav/nav-menus.php';          // 17. ナビゲーション
require_once get_template_directory() . '/inc/widgets/widget-areas.php';   // 18. ウィジェット（最小限）
require_once get_template_directory() . '/inc/security/security.php';      // 19. セキュリティ強化
```

### 4.2 フック設計方針

- **アクション**: `add_action()` の第3引数（priority）を明示。コアフックとの競合を避けるため、カスタム機能は priority `20` 以上を使用
- **フィルター**: 出力前の最終変換に使用。`apply_filters('wte_*')` で外部からの拡張ポイントを提供
- **条件タグ**: `is_front_page()`, `is_page('contact')` を積極使用して出力を最適化

---

## 5. カスタマイザー設計（全セクション詳細）

### 5.1 パネル・セクション階層

```
カスタマイザー
├── パネル: サイト基本情報 [priority: 10]
│   ├── セクション: サイトタイトル・ロゴ
│   ├── セクション: 運営者情報
│   └── セクション: 代表挨拶
├── パネル: デザイン設定 [priority: 20]
│   ├── セクション: カラー設定
│   ├── セクション: フォント設定
│   └── セクション: ヘッダー設定
├── パネル: 事業概要 [priority: 30]
│   ├── セクション: キャッチコピー・ヒーロー
│   ├── セクション: サービス概要
│   └── セクション: アピールポイント（3点）
├── パネル: SNS・外部リンク [priority: 40]
│   └── セクション: SNSアカウント
├── パネル: お問い合わせ [priority: 50]
│   └── セクション: お問い合わせ設定
└── パネル: SEO・高度な設定 [priority: 60]
    ├── セクション: SEO基本設定
    └── セクション: Google設定（reCAPTCHA等）
```

### 5.2 全設定項目（サニタイズ・デフォルト値付き完全リスト）

#### パネル1: サイト基本情報

---

**セクション: サイトタイトル・ロゴ**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_site_logo` | `WP_Customize_Image_Control` | ロゴ画像（推奨 400×120px） | ― | `esc_url_raw` | `selective_refresh` |
| `wte_site_logo_width` | `range` (custom) | ロゴ表示幅（px） | `200` | `absint` | `postMessage` |
| `wte_site_tagline_display` | `checkbox` | キャッチフレーズを表示 | `true` | `wte_sanitize_checkbox` | `postMessage` |
| `wte_favicon` | `WP_Customize_Image_Control` | ファビコン（32×32 推奨） | ― | `esc_url_raw` | `refresh` |

**セクション: 運営者情報**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_owner_name` | `text` | 氏名（フルネーム） | `''` | `sanitize_text_field` | `postMessage` |
| `wte_owner_name_kana` | `text` | 氏名（ふりがな） | `''` | `sanitize_text_field` | `postMessage` |
| `wte_owner_title` | `text` | 肩書き・職種 | `''` | `sanitize_text_field` | `postMessage` |
| `wte_owner_photo` | `WP_Customize_Image_Control` | プロフィール写真 | ― | `esc_url_raw` | `selective_refresh` |
| `wte_owner_bio` | `textarea` | 自己紹介文（短文） | `''` | `sanitize_textarea_field` | `postMessage` |
| `wte_business_name` | `text` | 屋号・事務所名 | `''` | `sanitize_text_field` | `postMessage` |
| `wte_business_address` | `textarea` | 住所（表示用） | `''` | `sanitize_textarea_field` | `postMessage` |
| `wte_business_tel` | `tel` (custom) | 電話番号 | `''` | `wte_sanitize_tel` | `postMessage` |
| `wte_business_hours` | `textarea` | 営業時間 | `''` | `sanitize_textarea_field` | `postMessage` |
| `wte_business_founded` | `text` | 創業年（例: 2018） | `''` | `wte_sanitize_year` | `postMessage` |

**セクション: 代表挨拶**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_greeting_heading` | `text` | 挨拶見出し | `'代表よりご挨拶'` | `sanitize_text_field` | `postMessage` |
| `wte_greeting_body` | `textarea` | 挨拶本文（400字程度） | `''` | `sanitize_textarea_field` | `postMessage` |
| `wte_greeting_signature` | `text` | 署名（例: 代表 山田 太郎） | `''` | `sanitize_text_field` | `postMessage` |
| `wte_greeting_image` | `WP_Customize_Image_Control` | 挨拶セクション添付画像 | ― | `esc_url_raw` | `selective_refresh` |

---

#### パネル2: デザイン設定

**セクション: カラー設定**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_color_header_bg` | `WP_Customize_Color_Control` | ヘッダー背景色 | `#ffffff` | `sanitize_hex_color` | `postMessage` |
| `wte_color_header_text` | `WP_Customize_Color_Control` | ヘッダーテキスト色 | `#1a1a1a` | `sanitize_hex_color` | `postMessage` |
| `wte_color_footer_bg` | `WP_Customize_Color_Control` | フッター背景色 | `#3c4043` | `sanitize_hex_color` | `postMessage` |
| `wte_color_footer_text` | `WP_Customize_Color_Control` | フッターテキスト色 | `#f8f9fa` | `sanitize_hex_color` | `postMessage` |
| `wte_color_accent` | `WP_Customize_Color_Control` | アクセントカラー | `#2d6be4` | `sanitize_hex_color` | `postMessage` |
| `wte_color_link` | `WP_Customize_Color_Control` | リンク色 | `#2d6be4` | `sanitize_hex_color` | `postMessage` |
| `wte_color_body_bg` | `WP_Customize_Color_Control` | ボディ背景色 | `#ffffff` | `sanitize_hex_color` | `postMessage` |
| `wte_color_section_alt_bg` | `WP_Customize_Color_Control` | 奇数セクション背景色 | `#f8f9fa` | `sanitize_hex_color` | `postMessage` |

> **実装注意**: カラー変更は `document.documentElement.style.setProperty('--wte-color-*', newval)` でリアルタイム反映。CSS カスタムプロパティのカスケードにより、全コンポーネントへ即時適用される。

**セクション: フォント設定**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_font_body` | `select` | 本文フォント | `noto_sans_jp` | `wte_sanitize_font_choice` | `selective_refresh` |
| `wte_font_heading` | `select` | 見出しフォント | `noto_serif_jp` | `wte_sanitize_font_choice` | `selective_refresh` |
| `wte_font_size_base` | `range` (custom) | 基本フォントサイズ（px）| `16` | `absint` | `postMessage` |

> **実装注意**: `selective_refresh` とする理由は、Google Fonts の `<link>` タグをヘッダーに出力し直す必要があるため。変更検知後に `wp.customize.selectiveRefresh` でヘッダー部分のみ更新。

**セクション: ヘッダー設定**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_header_sticky` | `checkbox` | ヘッダーを固定表示 | `true` | `wte_sanitize_checkbox` | `postMessage` |
| `wte_header_height` | `range` (custom) | ヘッダー高さ（px） | `72` | `absint` | `postMessage` |
| `wte_header_shadow` | `checkbox` | ヘッダーにシャドウ | `true` | `wte_sanitize_checkbox` | `postMessage` |
| `wte_nav_display` | `checkbox` | グローバルナビを表示 | `true` | `wte_sanitize_checkbox` | `postMessage` |
| `wte_header_cta_text` | `text` | ヘッダー CTA ボタンテキスト | `'お問い合わせ'` | `sanitize_text_field` | `postMessage` |
| `wte_header_cta_display` | `checkbox` | ヘッダー CTA ボタンを表示 | `true` | `wte_sanitize_checkbox` | `postMessage` |

---

#### パネル3: 事業概要

**セクション: キャッチコピー・ヒーローセクション**

| 設定 ID | コントロール種別 | ラベル | デフォルト | Sanitize | Transport |
|---------|----------------|--------|-----------|----------|-----------|
| `wte_hero_heading` | `text` | メインキャッチコピー | `''` | `sanitize_text_field` | `postMessage` |
| `wte_hero_subheading` | `textarea` | サブキャッチコピー | `''` | `sanitize_textarea_field` | `postMessage` |
| `wte_hero_image` | `WP_Customize_Image_Control` | ヒーロー背景画像 | ― | `esc_url_raw` | `selective_refresh` |
| `wte_hero_overlay_opacity` | `range` (custom) | 背景オーバーレイ不透明度（%） | `40` | `absint` | `postMessage` |
| `wte_hero_overlay_color` | `WP_Customize_Color_Control` | オーバーレイ色 | `#000000` | `sanitize_hex_color` | `postMessage` |
| `wte_hero_cta_primary_text` | `text` | 一次 CTA テキスト | `'詳しく見る'` | `sanitize_text_field` | `postMessage` |
| `wte_hero_cta_secondary_text` | `text` | 二次 CTA テキスト | `'お問い合わせ'` | `sanitize_text_field` | `postMessage` |
| `wte_hero_height` | `select` | ヒーロー高さ | `full` | `wte_sanitize_hero_height` | `postMessage` |

> `wte_hero_height` の選択肢: `full`（100vh）, `large`（80vh）, `medium`（60vh）, `small`（40vh）

**セクション: サービス概要（最大6件）**

| 設定 ID | コントロール種別 | ラベル | Sanitize | Transport |
|---------|----------------|--------|----------|-----------|
| `wte_service_section_heading` | `text` | セクション見出し | `sanitize_text_field` | `postMessage` |
| `wte_service_section_subtext` | `textarea` | セクション説明文 | `sanitize_textarea_field` | `postMessage` |
| `wte_service_{n}_title` (n=1〜6) | `text` | サービス名 | `sanitize_text_field` | `postMessage` |
| `wte_service_{n}_desc` (n=1〜6) | `textarea` | サービス説明文 | `sanitize_textarea_field` | `postMessage` |
| `wte_service_{n}_icon` (n=1〜6) | `select` | SVG アイコン選択 | `wte_sanitize_icon_choice` | `selective_refresh` |
| `wte_service_{n}_display` (n=1〜6) | `checkbox` | 表示する | `wte_sanitize_checkbox` | `postMessage` |

> **実装注意**: サービスカード `n=1〜6` は PHP ループで生成。設定キーは `sprintf('wte_service_%d_title', $n)` の形式で動的登録。

**セクション: アピールポイント（3点固定）**

| 設定 ID | コントロール種別 | ラベル | Sanitize | Transport |
|---------|----------------|--------|----------|-----------|
| `wte_appeal_section_heading` | `text` | セクション見出し | `sanitize_text_field` | `postMessage` |
| `wte_appeal_{n}_heading` (n=1〜3) | `text` | ポイント見出し | `sanitize_text_field` | `postMessage` |
| `wte_appeal_{n}_body` (n=1〜3) | `textarea` | ポイント説明 | `sanitize_textarea_field` | `postMessage` |
| `wte_appeal_{n}_number` (n=1〜3) | `text` | 強調数字（例: 10年） | `sanitize_text_field` | `postMessage` |

---

#### パネル4: SNS・外部リンク

**セクション: SNS アカウント**

| 設定 ID | コントロール種別 | ラベル | Sanitize | Transport |
|---------|----------------|--------|----------|-----------|
| `wte_sns_x` | `url` | X（旧 Twitter）URL | `esc_url_raw` | `postMessage` |
| `wte_sns_instagram` | `url` | Instagram URL | `esc_url_raw` | `postMessage` |
| `wte_sns_facebook` | `url` | Facebook URL | `esc_url_raw` | `postMessage` |
| `wte_sns_linkedin` | `url` | LinkedIn URL | `esc_url_raw` | `postMessage` |
| `wte_sns_youtube` | `url` | YouTube URL | `esc_url_raw` | `postMessage` |
| `wte_sns_note` | `url` | note URL | `esc_url_raw` | `postMessage` |
| `wte_sns_github` | `url` | GitHub URL | `esc_url_raw` | `postMessage` |
| `wte_sns_display_header` | `checkbox` | ヘッダーに SNS アイコン表示 | `wte_sanitize_checkbox` | `postMessage` |
| `wte_sns_display_footer` | `checkbox` | フッターに SNS アイコン表示 | `wte_sanitize_checkbox` | `postMessage` |

---

#### パネル5: お問い合わせ設定

**セクション: お問い合わせ設定**

| 設定 ID | コントロール種別 | ラベル | Sanitize | Transport |
|---------|----------------|--------|----------|-----------|
| `wte_contact_email` | `email` (custom) | メールアドレス（必須） | `wte_sanitize_email` | `selective_refresh` |
| `wte_contact_page_title` | `text` | お問い合わせページタイトル | `sanitize_text_field` | `postMessage` |
| `wte_contact_page_intro` | `textarea` | ページ導入文 | `sanitize_textarea_field` | `postMessage` |
| `wte_contact_cta_label` | `text` | メールリンクボタンのテキスト | `sanitize_text_field` | `postMessage` |
| `wte_contact_display_in_lp` | `checkbox` | LP にお問い合わせセクションを表示 | `wte_sanitize_checkbox` | `postMessage` |
| `wte_recaptcha_site_key` | `text` | reCAPTCHA v3 サイトキー | `sanitize_text_field` | `refresh` |
| `wte_recaptcha_secret_key` | `text` | reCAPTCHA v3 シークレットキー | `sanitize_text_field` | `refresh` |
| `wte_recaptcha_score_threshold` | `range` (custom) | スコア閾値（0.1〜1.0） | `wte_sanitize_float_range` | `refresh` |

> **実装注意**:  
> - `wte_contact_email` に値が入力されたとき、`after_save` フック相当のアクション（`customize_save_after`）で `wte_maybe_create_contact_page()` を呼び出し、お問い合わせページを自動生成  
> - シークレットキーは**フロントエンド出力しない**。`get_theme_mod` で PHP 内のみで参照

---

#### パネル6: SEO・高度な設定

**セクション: SEO 基本設定**

| 設定 ID | コントロール種別 | ラベル | Sanitize | Transport |
|---------|----------------|--------|----------|-----------|
| `wte_meta_description` | `textarea` | メタディスクリプション（160字以内） | `sanitize_textarea_field` | `refresh` |
| `wte_og_image` | `WP_Customize_Image_Control` | OGP 画像（1200×630推奨） | `esc_url_raw` | `refresh` |
| `wte_schema_business_type` | `select` | ビジネスタイプ | `wte_sanitize_business_type` | `refresh` |
| `wte_schema_price_range` | `select` | 価格帯（¥〜¥¥¥¥） | `sanitize_text_field` | `refresh` |
| `wte_google_analytics_id` | `text` | Google Analytics 4 計測 ID | `sanitize_text_field` | `refresh` |
| `wte_noindex` | `checkbox` | 検索エンジンにインデックスさせない | `wte_sanitize_checkbox` | `refresh` |

> `wte_schema_business_type` の選択肢（スキーマ.org 準拠）:  
> `ProfessionalService`, `LegalService`, `Accountant`, `FinancialPlanningService`,  
> `InsuranceAgency`, `Dentist`, `Physician`, `Architect`, `ITConsultant`,  
> `MarketingConsultant`, `PhotographStudio`, `TravelAgency`, `LocalBusiness`

---

### 5.3 カスタムコントロールクラス一覧

以下のカスタムコントロールを `inc/customizer/controls/` 以下に個別ファイルで実装する：

| クラス名 | ファイル | 説明 |
|---------|---------|------|
| `WTE_Range_Control` | `class-range-control.php` | スライダー入力（min/max/step/unit 対応） |
| `WTE_Email_Control` | `class-email-control.php` | email 型テキスト入力（バリデーション付き） |
| `WTE_Tel_Control` | `class-tel-control.php` | tel 型テキスト入力 |
| `WTE_Separator_Control` | `class-separator-control.php` | カスタマイザー内の区切り線（UI 改善用） |
| `WTE_Info_Control` | `class-info-control.php` | 説明文・補足情報表示用（入力なし） |
| `WTE_Toggle_Control` | `class-toggle-control.php` | ON/OFF トグルスイッチ（checkbox の視覚改善版） |
| `WTE_Icon_Select_Control` | `class-icon-select-control.php` | SVG アイコン視覚選択 |

各クラスは `WP_Customize_Control` を `extends` し、`render_content()` をオーバーライドして実装する。WordPress コーディング標準に従い、`sanitize_callback` を必ず指定する。

---

## 6. フロントページ（LP）構成

### 6.1 セクション構成（固定・変更不可）

LP は以下のセクションを**固定順序**で出力する。ユーザーは各セクションの「表示・非表示」と「コンテンツ」をカスタマイザーで制御する。

```
#wte-hero           — ヒーローセクション（フルビュー）
#wte-services       — サービス概要（カードグリッド）
#wte-appeal         — アピールポイント（3カラム）
#wte-profile        — 運営者情報 + プロフィール写真
#wte-greeting       — 代表挨拶
#wte-business-info  — 事業詳細（カスタムフィールド出力）
#wte-faq            — よくある質問（アコーディオン）
#wte-sns            — SNS 導線
#wte-contact-cta    — お問い合わせへの誘導
```

### 6.2 各セクションの実装詳細

#### `#wte-hero`

```html
<section id="wte-hero" class="wte-hero wte-hero--{height}" 
         style="--hero-bg: url('{背景画像URL}'); --hero-overlay-opacity: {不透明度};">
  <div class="wte-hero__overlay"></div>
  <div class="wte-container wte-hero__inner">
    <div class="wte-hero__content">
      <h1 class="wte-hero__heading">{メインキャッチコピー}</h1>
      <p class="wte-hero__subheading">{サブキャッチコピー}</p>
      <div class="wte-hero__cta-group">
        <a href="#wte-services" class="wte-btn wte-btn--primary">{一次CTAテキスト}</a>
        <a href="{お問い合わせページURL}" class="wte-btn wte-btn--outline">{二次CTAテキスト}</a>
      </div>
    </div>
  </div>
  <!-- スクロールダウン矢印 -->
  <button class="wte-hero__scroll-btn" aria-label="スクロールダウン">
    <svg>...</svg>
  </button>
</section>
```

ヒーロー背景は CSS `background-image` で実装。画像未設定時はグラジエントフォールバック（`--wte-color-accent` ベース）を適用。

#### `#wte-services`

```html
<section id="wte-services" class="wte-section wte-section--services">
  <div class="wte-container">
    <header class="wte-section__header">
      <h2 class="wte-section__heading">{セクション見出し}</h2>
      <p class="wte-section__subtext">{説明文}</p>
    </header>
    <div class="wte-services__grid">
      <!-- サービスカード × n（最大6） -->
      <?php foreach ( wte_get_active_services() as $service ) : ?>
      <article class="wte-service-card">
        <div class="wte-service-card__icon">
          <?php wte_render_icon( $service['icon'] ); ?>
        </div>
        <h3 class="wte-service-card__title"><?php echo esc_html( $service['title'] ); ?></h3>
        <p class="wte-service-card__desc"><?php echo esc_html( $service['desc'] ); ?></p>
      </article>
      <?php endforeach; ?>
    </div>
    <!-- 事業詳細（カスタムフィールド）の出力 -->
    <?php wte_render_business_details(); ?>
  </div>
</section>
```

グリッドは CSS Grid（`auto-fill`, `minmax(280px, 1fr)`）で実装。1〜3件: 1〜3列、4〜6件: 3列最大。

#### `#wte-profile`

プロフィール写真は `aspect-ratio: 1 / 1` の円形クロップで表示。写真未設定時はデフォルトのシルエット SVG を表示。

#### `#wte-faq`

FAQ はカスタムフィールド（`wte_faq_items`）から取得したアコーディオン形式。`<details>/<summary>` ネイティブ HTML で実装（JS 不使用）。CSS トランジションでアニメーション。JSON-LD の `FAQPage` スキーマと連動。

```html
<section id="wte-faq" class="wte-section wte-section--faq">
  <div class="wte-container wte-container--narrow">
    <h2 class="wte-section__heading">{FAQ見出し}</h2>
    <?php foreach ( wte_get_faq_items() as $item ) : ?>
    <details class="wte-faq__item">
      <summary class="wte-faq__question"><?php echo esc_html( $item['q'] ); ?></summary>
      <div class="wte-faq__answer"><?php echo wp_kses_post( $item['a'] ); ?></div>
    </details>
    <?php endforeach; ?>
  </div>
</section>
```

---

## 7. お問い合わせページ自動生成

### 7.1 生成ロジック

```php
// inc/setup/setup-pages.php

/**
 * お問い合わせメールアドレスが設定されたらページを自動生成
 * フック: customize_save_after
 */
function wte_maybe_create_contact_page(): void {
    $email = get_theme_mod('wte_contact_email', '');

    if ( empty($email) ) {
        return; // メールなし → 生成しない
    }

    // 既存のお問い合わせページを探す
    $existing = get_pages([
        'meta_key'   => '_wte_is_contact_page',
        'meta_value' => '1',
        'number'     => 1,
    ]);

    if ( ! empty($existing) ) {
        return; // 既に存在 → スキップ
    }

    // ページ生成
    $page_id = wp_insert_post([
        'post_title'   => get_theme_mod('wte_contact_page_title', 'お問い合わせ'),
        'post_name'    => 'contact',
        'post_status'  => 'publish',
        'post_type'    => 'page',
        'post_content' => '',       // コンテンツはテンプレートで出力
        'page_template' => 'page-contact.php',
    ]);

    if ( ! is_wp_error($page_id) ) {
        update_post_meta($page_id, '_wte_is_contact_page', '1');
        // Reading Settings に登録（任意）
    }
}
add_action('customize_save_after', 'wte_maybe_create_contact_page');
```

### 7.2 ページテンプレート（page-contact.php）

お問い合わせページは以下の構成で出力する。**フォームは設置しない**。

```
┌─────────────────────────────────┐
│  ページタイトル                   │  h1
│  導入文（カスタマイザー入力）       │  p
├─────────────────────────────────┤
│  メールリンクボタン                │  <a href="mailto:...">
│  （reCAPTCHA v3 スクリプト込み）  │
├─────────────────────────────────┤
│  メールアドレス テキスト表示        │  <p>（スパム防止: JS でデコード）
│  （文字列を分割して JS で結合）     │
└─────────────────────────────────┘
```

### 7.3 スパム防止メールアドレス表示

メールアドレスをそのまま HTML に埋め込まず、以下のいずれかで対応する：

**方法 A: CSS エンコード**（推奨）
```html
<span class="wte-email-protect" 
      data-user="<?php echo esc_attr( $user_part ); ?>" 
      data-domain="<?php echo esc_attr( $domain_part ); ?>">
</span>
<!-- JS で結合して表示 -->
```

**方法 B: ROT13 エンコード**
PHP 側で `str_rot13()` でエンコード、JS で復号して表示。

両方を組み合わせた実装とする。

### 7.4 reCAPTCHA v3 との連携

お問い合わせページ表示時に reCAPTCHA v3 スクリプトを読み込む（他ページでは読み込まない）。メールリンクボタンクリック時に reCAPTCHA のトークンを取得し、スコアが閾値以上の場合のみメールリンクを有効化する。

```javascript
// assets/js/recaptcha-contact.js
document.addEventListener('DOMContentLoaded', function() {
    const btn = document.querySelector('.wte-contact__email-btn');
    if (!btn) return;

    btn.addEventListener('click', function(e) {
        e.preventDefault();
        grecaptcha.ready(function() {
            grecaptcha.execute(wteRecaptcha.siteKey, {action: 'contact_view'})
                .then(function(token) {
                    return fetch(wteRecaptcha.ajaxUrl, {
                        method: 'POST',
                        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
                        body: `action=wte_verify_recaptcha&token=${token}&nonce=${wteRecaptcha.nonce}`
                    });
                })
                .then(r => r.json())
                .then(function(data) {
                    if (data.success) {
                        // メールアドレスを表示・リンクを有効化
                        wteRevealEmail();
                    } else {
                        // スコア低 → メッセージ表示
                        wteShowSpamWarning();
                    }
                });
        });
    });
});
```

PHP 側の Ajax ハンドラ（`inc/security/recaptcha.php`）：
```php
function wte_verify_recaptcha(): void {
    check_ajax_referer('wte_recaptcha_nonce', 'nonce');

    $token  = sanitize_text_field($_POST['token'] ?? '');
    $secret = get_theme_mod('wte_recaptcha_secret_key', '');
    $thresh = (float) get_theme_mod('wte_recaptcha_score_threshold', 0.5);

    $response = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', [
        'body' => ['secret' => $secret, 'response' => $token],
    ]);

    $body  = json_decode(wp_remote_retrieve_body($response), true);
    $score = (float) ($body['score'] ?? 0);

    if ( ($body['success'] ?? false) && $score >= $thresh ) {
        wp_send_json_success(['score' => $score]);
    } else {
        wp_send_json_error(['score' => $score]);
    }
}
add_action('wp_ajax_nopriv_wte_verify_recaptcha', 'wte_verify_recaptcha');
add_action('wp_ajax_wte_verify_recaptcha',        'wte_verify_recaptcha');
```

---

## 8. カスタムフィールド設計（事業概要詳細・FAQ）

### 8.1 設計方針

- ブロックエディター（Gutenberg）は**完全無効化**し、カスタムフィールド（メタボックス）のみで入力させる
- `add_filter('use_block_editor_for_post_type', '__return_false', 10, 2)` は使用しない。代わりに `post_type_supports` で `editor` を除外し、テーマが管理するフロントページのみ制御
- フロントページ（固定ページ）にカスタムメタボックスを追加し、すべての詳細情報を入力させる

### 8.2 メタボックス構成（固定ページ専用）

#### メタボックス 1: 事業詳細（繰り返しフィールド）

**ファイル**: `inc/meta/meta-boxes.php`

```
┌──────────────────────────────────────────┐
│  事業詳細の追加                            │
├──────────────────────────────────────────┤
│  [項目名]  ___________________________   │
│  [説明文]  ___________________________   │
│            ___________________________   │
│  [添付画像] [画像を選択]                   │
│  [解説URL]  ___________________________   │
│  [表示順]   ___________________________   │
│  [削除]    [+ さらに追加]                 │
└──────────────────────────────────────────┘
```

データ構造（`wte_business_details` メタキー、JSON 配列として保存）:
```json
[
  {
    "title": "実績",
    "body": "創業以来500社以上の...",
    "image_id": 123,
    "link_url": "https://example.com/works",
    "link_text": "実績一覧を見る",
    "order": 1
  }
]
```

#### メタボックス 2: FAQ 項目（繰り返しフィールド）

```
┌──────────────────────────────────────────┐
│  FAQ の追加                               │
├──────────────────────────────────────────┤
│  Q: ___________________________________  │
│  A: ___________________________________  │
│     ___________________________________  │
│  [表示順] ____  [削除] [+ さらに追加]     │
└──────────────────────────────────────────┘
```

データ構造（`wte_faq_items` メタキー、JSON 配列として保存）:
```json
[
  {
    "question": "料金はいくらですか？",
    "answer": "サービス内容により異なります。まずはお気軽に...",
    "order": 1
  }
]
```

### 8.3 繰り返しフィールドの JS 実装

`assets/js/admin-repeater.js` として分離実装：

- 「追加」ボタンクリックで新しいフィールドグループを動的生成（`cloneNode(true)` + インデックス更新）
- 「削除」ボタンクリックでグループを削除（確認ダイアログ付き）
- ドラッグ&ドロップ並び替え（`Sortable.js` を管理画面のみエンキュー）
- 保存時に全データを JSON シリアライズして hidden input に格納 → PHP でデシリアライズして保存
- WordPress nonce を用いた AJAX 保存で UX を向上（任意機能）

### 8.4 画像フィールドの実装

WordPress Media Library を使用。各画像フィールドに対して：

```javascript
// メディアライブラリを開くボタン
btn.addEventListener('click', function() {
    const frame = wp.media({
        title: '画像を選択',
        button: { text: '選択' },
        multiple: false,
    });
    frame.on('select', function() {
        const attachment = frame.state().get('selection').first().toJSON();
        hiddenInput.value = attachment.id;
        previewImg.src    = attachment.url;
    });
    frame.open();
});
```

---

## 9. JSON-LD 構造化データ自動生成

### 9.1 出力対象スキーマ

カスタマイザーの入力内容から以下のスキーマを `wp_head` アクション（priority: 1）で自動生成する。

#### スキーマ 1: LocalBusiness / ProfessionalService

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": ["ProfessionalService", "LocalBusiness"],
      "@id": "{サイトURL}/#organization",
      "name": "{屋号または氏名}",
      "url": "{サイトURL}",
      "logo": {
        "@type": "ImageObject",
        "url": "{ロゴ画像URL}",
        "width": 400,
        "height": 120
      },
      "image": "{OGP画像URL}",
      "description": "{メタディスクリプション}",
      "telephone": "{電話番号}",
      "address": {
        "@type": "PostalAddress",
        "streetAddress": "{住所}",
        "addressCountry": "JP"
      },
      "founder": {
        "@type": "Person",
        "name": "{氏名}",
        "jobTitle": "{肩書き}",
        "image": "{プロフィール写真URL}"
      },
      "foundingDate": "{創業年}",
      "priceRange": "{価格帯}",
      "sameAs": ["{X URL}", "{Instagram URL}", ...（空値は除外）]
    },
    {
      "@type": "WebSite",
      "@id": "{サイトURL}/#website",
      "url": "{サイトURL}",
      "name": "{サイトタイトル}",
      "description": "{キャッチフレーズ}"
    },
    {
      "@type": "WebPage",
      "@id": "{現在のページURL}/#webpage",
      "url": "{現在のページURL}",
      "isPartOf": {"@id": "{サイトURL}/#website"},
      "about": {"@id": "{サイトURL}/#organization"}
    }
  ]
}
```

#### スキーマ 2: FAQPage（FAQ 項目が存在する場合のみ）

```json
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "{質問文}",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "{回答文}"
      }
    }
  ]
}
```

#### スキーマ 3: Person（プロフィールセクション）

```json
{
  "@context": "https://schema.org",
  "@type": "Person",
  "name": "{氏名}",
  "jobTitle": "{肩書き}",
  "worksFor": {"@id": "{サイトURL}/#organization"},
  "url": "{サイトURL}",
  "image": "{プロフィール写真URL}"
}
```

### 9.2 PHP 実装（inc/seo/json-ld.php）

```php
declare(strict_types=1);

function wte_output_json_ld(): void {
    $schemas = [];

    // ① LocalBusiness スキーマ構築
    $org = wte_build_organization_schema();
    if ( ! empty($org) ) {
        $schemas[] = $org;
    }

    // ② FAQPage スキーマ（FAQ データが存在する場合のみ）
    if ( is_front_page() ) {
        $faq = wte_build_faqpage_schema();
        if ( ! empty($faq) ) {
            $schemas[] = $faq;
        }
    }

    // ③ Person スキーマ
    $person = wte_build_person_schema();
    if ( ! empty($person) ) {
        $schemas[] = $person;
    }

    foreach ( $schemas as $schema ) {
        printf(
            '<script type="application/ld+json">%s</script>' . PHP_EOL,
            wp_json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
        );
    }
}
add_action('wp_head', 'wte_output_json_ld', 1);
```

**ヘルパー関数群**（`inc/helpers/schema.php`）:
- `wte_build_organization_schema(): array` — 組織スキーマ配列を返す
- `wte_build_faqpage_schema(): array` — FAQ スキーマ配列を返す
- `wte_build_person_schema(): array` — Person スキーマ配列を返す
- `wte_get_schema_same_as(): array` — SNS URL を配列で返す（空値を除外）

---

## 10. SNS 導線コンポーネント

### 10.1 SVG アイコンスプライト

`assets/images/sns-sprite.svg` に全 SNS アイコンを定義。`<use xlink:href>` でインライン参照。

対応 SNS: X（旧Twitter）, Instagram, Facebook, LinkedIn, YouTube, note, GitHub

### 10.2 表示ロジック

```php
// template-parts/components/sns-icons.php
function wte_render_sns_icons(string $context = 'footer'): void {
    $sns_list = [
        'x'         => ['label' => 'X',         'key' => 'wte_sns_x'],
        'instagram' => ['label' => 'Instagram',  'key' => 'wte_sns_instagram'],
        'facebook'  => ['label' => 'Facebook',   'key' => 'wte_sns_facebook'],
        'linkedin'  => ['label' => 'LinkedIn',   'key' => 'wte_sns_linkedin'],
        'youtube'   => ['label' => 'YouTube',    'key' => 'wte_sns_youtube'],
        'note'      => ['label' => 'note',       'key' => 'wte_sns_note'],
        'github'    => ['label' => 'GitHub',     'key' => 'wte_sns_github'],
    ];

    $display_key = ($context === 'header') ? 'wte_sns_display_header' : 'wte_sns_display_footer';
    if ( ! get_theme_mod($display_key, true) ) return;

    echo '<ul class="wte-sns-list wte-sns-list--' . esc_attr($context) . '">';
    foreach ($sns_list as $id => $item) {
        $url = get_theme_mod($item['key'], '');
        if ( empty($url) ) continue;
        printf(
            '<li><a href="%s" target="_blank" rel="noopener noreferrer" aria-label="%s">%s</a></li>',
            esc_url($url),
            esc_attr($item['label']),
            wte_get_svg_icon($id)
        );
    }
    echo '</ul>';
}
```

---

## 11. カスタマイザー・プレビュー即時反映設計

### 11.1 Transport 方針の全体像

| 設定カテゴリ | Transport | 理由 |
|------------|-----------|------|
| カラー全般 | `postMessage` | CSS カスタムプロパティ 1 行変更で完結 |
| テキスト全般 | `postMessage` | DOM のテキストノード更新で完結 |
| 画像 URL | `selective_refresh` | `<img src>` または CSS `background-image` の部分再描画が必要 |
| フォント | `selective_refresh` | `<link>` タグの差し替えが必要 |
| reCAPTCHA キー | `refresh` | サーバーサイドで処理するため |
| SEO 設定 | `refresh` | `<head>` 内の変更のため |

### 11.2 postMessage JS 実装（assets/js/customizer-preview.js）

```javascript
/**
 * WTE Business Card - Customizer Preview
 * 全設定の postMessage ハンドラ
 */

(function (api) {
    'use strict';

    // ---- ユーティリティ ----
    const setCSSVar = (name, val) =>
        document.documentElement.style.setProperty(name, val);

    const setTextContent = (selector, val) => {
        document.querySelectorAll(selector).forEach(el => {
            el.textContent = val;
        });
    };

    // ---- カラー設定 ----
    const colorMap = {
        'wte_color_header_bg':      '--wte-color-header-bg',
        'wte_color_header_text':    '--wte-color-header-text',
        'wte_color_footer_bg':      '--wte-color-footer-bg',
        'wte_color_footer_text':    '--wte-color-footer-text',
        'wte_color_accent':         '--wte-color-accent',
        'wte_color_link':           '--wte-color-link',
        'wte_color_body_bg':        '--wte-color-body-bg',
        'wte_color_section_alt_bg': '--wte-color-section-alt-bg',
    };

    Object.entries(colorMap).forEach(([settingId, cssVar]) => {
        api(settingId, val => val.bind(v => setCSSVar(cssVar, v)));
    });

    // ---- テキスト設定 ----
    const textMap = {
        'wte_hero_heading':    '.wte-hero__heading',
        'wte_hero_subheading': '.wte-hero__subheading',
        'wte_owner_name':      '.wte-profile__name',
        'wte_owner_title':     '.wte-profile__title',
        'wte_owner_bio':       '.wte-profile__bio',
        'wte_business_name':   '.wte-business__name',
        'wte_greeting_heading':'#wte-greeting .wte-section__heading',
        'wte_greeting_body':   '.wte-greeting__body',
    };

    Object.entries(textMap).forEach(([settingId, selector]) => {
        api(settingId, val => val.bind(v => setTextContent(selector, v)));
    });

    // ---- ヘッダー設定 ----
    api('wte_header_sticky', val => val.bind(v => {
        document.querySelector('.wte-header')
            ?.classList.toggle('wte-header--sticky', v);
    }));

    api('wte_header_height', val => val.bind(v =>
        setCSSVar('--wte-header-height', v + 'px')
    ));

    api('wte_hero_overlay_opacity', val => val.bind(v =>
        setCSSVar('--hero-overlay-opacity', v / 100)
    ));

    api('wte_hero_overlay_color', val => val.bind(v =>
        setCSSVar('--hero-overlay-color', v)
    ));

    api('wte_font_size_base', val => val.bind(v =>
        setCSSVar('--wte-font-size-base', v + 'px')
    ));

    // ---- ロゴ幅 ----
    api('wte_site_logo_width', val => val.bind(v => {
        setCSSVar('--wte-logo-width', v + 'px');
    }));

}(wp.customize));
```

### 11.3 selective_refresh 設定（inc/customizer/settings.php 内）

```php
// プロフィール写真の selective_refresh
$wp_customize->selective_refresh->add_partial('wte_owner_photo', [
    'selector'            => '.wte-profile__photo-wrap',
    'render_callback'     => 'wte_render_profile_photo',
    'container_inclusive' => true,
]);

// ヒーロー画像
$wp_customize->selective_refresh->add_partial('wte_hero_image', [
    'selector'            => '.wte-hero',
    'render_callback'     => 'wte_render_hero_section',
    'container_inclusive' => true,
]);

// フォント（ヘッダー全体を再描画）
$wp_customize->selective_refresh->add_partial('wte_font_body', [
    'selector'            => 'head',
    'render_callback'     => 'wte_render_google_fonts_link',
    'container_inclusive' => false,
    'fallback_refresh'    => true,
]);
```

---

## 12. reCAPTCHA v3 統合

### 12.1 読み込み制御

reCAPTCHA v3 スクリプトはパフォーマンスへの影響を最小化するため、**お問い合わせページのみ**で読み込む。

```php
// inc/setup/enqueue.php
function wte_enqueue_recaptcha(): void {
    if ( ! is_page('contact') ) return;

    $site_key = get_theme_mod('wte_recaptcha_site_key', '');
    if ( empty($site_key) ) return;

    wp_enqueue_script(
        'wte-recaptcha-api',
        'https://www.google.com/recaptcha/api.js?render=' . esc_attr($site_key),
        [],
        null,
        true
    );

    wp_enqueue_script(
        'wte-recaptcha-contact',
        get_template_directory_uri() . '/assets/js/recaptcha-contact.min.js',
        ['wte-recaptcha-api'],
        WTE_THEME_VERSION,
        true
    );

    wp_localize_script('wte-recaptcha-contact', 'wteRecaptcha', [
        'siteKey'  => $site_key,
        'ajaxUrl'  => admin_url('admin-ajax.php'),
        'nonce'    => wp_create_nonce('wte_recaptcha_nonce'),
        'threshold' => get_theme_mod('wte_recaptcha_score_threshold', 0.5),
    ]);
}
add_action('wp_enqueue_scripts', 'wte_enqueue_recaptcha');
```

### 12.2 シークレットキーの保護

- `wte_recaptcha_secret_key` はカスタマイザー設定としてデータベースに保存されるが、**フロントエンド出力（HTML/JS）には一切含めない**
- PHP サーバーサイドの Ajax ハンドラ内のみで参照
- `is_customize_preview()` が true の間は reCAPTCHA を無効化（テスト時の妨げを防ぐ）

---

## 13. パフォーマンス・アクセシビリティ・SEO

### 13.1 Core Web Vitals 目標値

| 指標 | 目標 |
|------|------|
| LCP（Largest Contentful Paint） | < 2.5s |
| FID / INP（Interaction to Next Paint） | < 200ms |
| CLS（Cumulative Layout Shift） | < 0.1 |
| TTFB（Time to First Byte） | < 800ms |

### 13.2 パフォーマンス実装要件

**CSS**
- Critical CSS をインラインで `<style>` タグ出力（ヒーローセクション相当）
- 非クリティカル CSS は `<link rel="preload">` + `onload` で非同期読み込み
- 使用しないセクションの CSS は読み込まない（条件分岐でエンキュー制御）

**JavaScript**
- すべての JS は `defer` または `async` 属性付きで読み込む
- `wp_enqueue_script` の第5引数を `true`（フッター）に設定
- jQuery を含まない。バニラ JS のみ使用
- 合計 JS バイト: < 30KB（gzip後）

**画像**
- `<img>` タグには必ず `loading="lazy"` を付与（ヒーロー背景を除く）
- `width` / `height` 属性を明示して CLS を防止
- `srcset` + `sizes` でレスポンシブ画像を実装

**フォント**
- `<link rel="preconnect" href="https://fonts.googleapis.com">`
- `<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>`
- `font-display: swap` を Google Fonts パラメータで指定（`&display=swap`）

**その他**
- `add_filter('style_loader_tag', ...)` でクリティカルでない CSS を非同期化
- WordPress 標準の絵文字 CSS/JS を無効化（`remove_action('wp_head', 'print_emoji_detection_script')` 等）
- oEmbed エンドポイントを無効化（`remove_action('wp_head', 'wp_oembed_add_discovery_links')`）

### 13.3 アクセシビリティ（WCAG 2.1 AA 準拠）

- **コントラスト比**: テキストと背景のコントラスト比 4.5:1 以上（カラーピッカーの説明文に明記）
- **キーボード操作**: 全インタラクティブ要素がキーボードでアクセス可能
- **フォーカスインジケータ**: `:focus-visible` で明確なアウトライン表示
- **スクリーンリーダー**: 適切な `aria-label`, `aria-expanded`, `role` 属性
- **Skip Link**: 最初の要素としてコンテンツスキップリンクを設置
- **画像代替テキスト**: `alt` 属性の適切な設定。装飾画像は `alt=""`
- **見出し階層**: h1 → h2 → h3 の論理的な階層構造を維持
- **モーション**: `@media (prefers-reduced-motion: reduce)` でアニメーション無効化対応

### 13.4 OGP・Twitter Card

```php
// inc/seo/meta-tags.php
function wte_output_meta_tags(): void {
    $title       = get_bloginfo('name');
    $description = get_theme_mod('wte_meta_description', get_bloginfo('description'));
    $og_image    = get_theme_mod('wte_og_image', '');
    $url         = get_permalink() ?: home_url('/');

    // Open Graph
    echo '<meta property="og:title" content="' . esc_attr($title) . '">' . PHP_EOL;
    echo '<meta property="og:description" content="' . esc_attr($description) . '">' . PHP_EOL;
    echo '<meta property="og:url" content="' . esc_url($url) . '">' . PHP_EOL;
    echo '<meta property="og:type" content="website">' . PHP_EOL;
    echo '<meta property="og:site_name" content="' . esc_attr($title) . '">' . PHP_EOL;
    if ( $og_image ) {
        echo '<meta property="og:image" content="' . esc_url($og_image) . '">' . PHP_EOL;
        echo '<meta property="og:image:width" content="1200">' . PHP_EOL;
        echo '<meta property="og:image:height" content="630">' . PHP_EOL;
    }

    // Twitter Card
    echo '<meta name="twitter:card" content="summary_large_image">' . PHP_EOL;
    echo '<meta name="twitter:title" content="' . esc_attr($title) . '">' . PHP_EOL;
    echo '<meta name="twitter:description" content="' . esc_attr($description) . '">' . PHP_EOL;
    if ( $og_image ) {
        echo '<meta name="twitter:image" content="' . esc_url($og_image) . '">' . PHP_EOL;
    }
}
add_action('wp_head', 'wte_output_meta_tags', 5);
```

---

## 14. セキュリティ実装要件

### 14.1 WordPress セキュリティ標準

**入力サニタイズ（全カスタマイザー設定）**
```php
// inc/customizer/callbacks.php

function wte_sanitize_checkbox(mixed $val): bool {
    return (bool) $val;
}

function wte_sanitize_email(string $val): string {
    return sanitize_email($val);
}

function wte_sanitize_tel(string $val): string {
    return preg_replace('/[^0-9\-+() ]/', '', $val);
}

function wte_sanitize_font_choice(string $val): string {
    $allowed = ['noto_sans_jp', 'noto_serif_jp', 'zen_kaku_gothic', 'shippori_mincho', 'm_plus_1p'];
    return in_array($val, $allowed, true) ? $val : 'noto_sans_jp';
}

function wte_sanitize_business_type(string $val): string {
    $allowed = ['ProfessionalService', 'LegalService', 'Accountant', ...];
    return in_array($val, $allowed, true) ? $val : 'ProfessionalService';
}

function wte_sanitize_year(string $val): string {
    return preg_match('/^\d{4}$/', $val) ? $val : '';
}

function wte_sanitize_float_range(mixed $val): float {
    $v = (float) $val;
    return max(0.1, min(1.0, $v));
}

function wte_sanitize_hero_height(string $val): string {
    $allowed = ['full', 'large', 'medium', 'small'];
    return in_array($val, $allowed, true) ? $val : 'full';
}

function wte_sanitize_icon_choice(string $val): string {
    $allowed = ['star', 'check', 'briefcase', 'document', 'chat', 'gear', 'lock', 'chart'];
    return in_array($val, $allowed, true) ? $val : 'star';
}
```

**出力エスケープ（テンプレート全体）**
- HTML テキスト: `esc_html()`
- HTML 属性: `esc_attr()`
- URL: `esc_url()`
- リッチテキスト: `wp_kses_post()`
- JS 内文字列: `esc_js()`
- SQL: `$wpdb->prepare()`（直接クエリは原則使用しない）

### 14.2 セキュリティ強化設定（inc/security/security.php）

```php
// WordPress バージョン情報の除去
remove_action('wp_head', 'wp_generator');

// XML-RPC 無効化（不使用のため）
add_filter('xmlrpc_enabled', '__return_false');

// REST API へのアクセス制限（未ログインユーザー）
add_filter('rest_authentication_errors', function($result) {
    if (true === $result || is_wp_error($result)) return $result;
    if (!is_user_logged_in()) {
        return new WP_Error('rest_not_logged_in', 'REST API requires authentication.', ['status' => 401]);
    }
    return $result;
});

// ログインエラーメッセージを汎用化
add_filter('login_errors', fn() => __('ログイン情報が正しくありません。', 'wte_'));

// メタボックス保存時の nonce 検証
// （各メタボックスで個別に実装）

// CSRF 対策: すべての Ajax リクエストに nonce を要求
```

### 14.3 カスタムフィールド保存時のセキュリティ

```php
function wte_save_business_details(int $post_id): void {
    // nonce 検証
    if (!isset($_POST['wte_meta_nonce']) ||
        !wp_verify_nonce($_POST['wte_meta_nonce'], 'wte_save_meta_' . $post_id)) {
        return;
    }

    // 権限確認
    if (!current_user_can('edit_post', $post_id)) return;

    // 自動保存時はスキップ
    if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;

    // データサニタイズ & 保存
    $raw  = $_POST['wte_business_details'] ?? '[]';
    $data = json_decode(stripslashes($raw), true);
    if (!is_array($data)) $data = [];

    $clean = array_map(function($item) {
        return [
            'title'     => sanitize_text_field($item['title'] ?? ''),
            'body'      => sanitize_textarea_field($item['body'] ?? ''),
            'image_id'  => absint($item['image_id'] ?? 0),
            'link_url'  => esc_url_raw($item['link_url'] ?? ''),
            'link_text' => sanitize_text_field($item['link_text'] ?? ''),
            'order'     => absint($item['order'] ?? 0),
        ];
    }, $data);

    update_post_meta($post_id, 'wte_business_details', wp_json_encode($clean));
}
add_action('save_post_page', 'wte_save_business_details');
```

---

## 15. WordPress コーディング標準への準拠

### 15.1 PHP コーディング標準

- **PSR-12** + **WordPress PHP Coding Standards** の両方に準拠
- `declare(strict_types=1)` を全 PHP ファイルの先頭に記述
- 関数・メソッド名: `snake_case` にプレフィックス `wte_` を付与
- クラス名: `WTE_Class_Name` 形式（Pascal_Case）
- 定数: `WTE_CONSTANT_NAME` 形式（全大文字スネークケース）
- ファイル先頭に `<?php` のみ（短縮タグ `<?=` は使用しない）
- 全ファイルに `namespace WTE\ThemeName;` を宣言（PHP 8.1 以降の標準）

### 15.2 JavaScript コーディング標準

- WordPress JavaScript Coding Standards に準拠
- `'use strict';` を全 JS ファイルに記述
- 関数名: `camelCase` にプレフィックス `wte` を付与
- 変数: `const` / `let` のみ使用（`var` 禁止）
- テンプレートリテラル、アロー関数、分割代入を積極使用

### 15.3 CSS コーディング標準

- BEM（Block Element Modifier）命名規則を採用
  - Block: `.wte-hero`
  - Element: `.wte-hero__heading`
  - Modifier: `.wte-hero--large`
- CSS カスタムプロパティ: `--wte-` プレフィックス
- `!important` の使用は原則禁止（Customizer 動的スタイル出力時のみ許容）
- メディアクエリはモバイルファーストで記述

### 15.4 定数定義（inc/setup/theme-support.php）

```php
define('WTE_THEME_VERSION',   wp_get_theme()->get('Version'));
define('WTE_THEME_DIR',       get_template_directory());
define('WTE_THEME_URI',       get_template_directory_uri());
define('WTE_MIN_WP_VERSION',  '6.5');
define('WTE_MIN_PHP_VERSION', '8.1');
```

---

## 16. テーマ有効化時の自動セットアップ

### 16.1 after_switch_theme フック

```php
// inc/setup/setup-pages.php

function wte_theme_activation_setup(): void {
    // ① フロントページ（固定ページ）の自動生成
    wte_create_front_page();

    // ② パーマリンク構造のリセット
    flush_rewrite_rules();

    // ③ 管理者通知フラグのセット
    set_transient('wte_activation_notice', true, 30);
}
add_action('after_switch_theme', 'wte_theme_activation_setup');

/**
 * フロントページの生成
 */
function wte_create_front_page(): void {
    // 既存確認
    $existing = get_pages(['meta_key' => '_wte_is_front_page', 'meta_value' => '1', 'number' => 1]);
    if (!empty($existing)) return;

    $page_id = wp_insert_post([
        'post_title'    => 'ホーム',
        'post_name'     => 'home',
        'post_status'   => 'publish',
        'post_type'     => 'page',
        'post_content'  => '',
        'page_template' => 'front-page.php',
    ]);

    if (!is_wp_error($page_id)) {
        update_post_meta($page_id, '_wte_is_front_page', '1');

        // Reading Settings: フロントページを固定ページに設定
        update_option('show_on_front',  'page');
        update_option('page_on_front',  $page_id);
        update_option('page_for_posts', 0);
    }
}
```

### 16.2 管理者向け初期設定ガイド通知

テーマ有効化後の最初のページ表示時に、管理画面に設定ガイドの通知を表示する。

```php
function wte_show_activation_notice(): void {
    if (!get_transient('wte_activation_notice')) return;
    if (!current_user_can('manage_options')) return;

    delete_transient('wte_activation_notice');

    printf(
        '<div class="notice notice-success is-dismissible"><p><strong>WTE Business Card</strong> %s <a href="%s">%s</a></p></div>',
        esc_html__('が有効化されました！カスタマイザーから各種情報を設定してください。', 'wte_'),
        esc_url(admin_url('customize.php')),
        esc_html__('カスタマイザーを開く →', 'wte_')
    );
}
add_action('admin_notices', 'wte_show_activation_notice');
```

---

## 17. 多言語・国際化対応

### 17.1 翻訳対応

- 全テキスト文字列は `__()`, `_e()`, `esc_html__()`, `esc_html_e()` でラップ
- テキストドメイン: `wte_`（注意: WordPress ではハイフン区切りが一般的だが、本テーマはアンダースコアを使用）
- `.pot` ファイルを `languages/wte_.pot` として同梱
- 日本語翻訳ファイル `wte_-ja.po` / `wte_-ja.mo` を同梱

### 17.2 RTL 対応

- `rtl.css` を同梱し、右書き言語での表示崩れを防止
- `is_rtl()` を確認してスタイルを条件付きエンキュー

---

## 18. テスト要件

### 18.1 機能テスト

- [ ] テーマ有効化でフロントページが自動生成されること
- [ ] カスタマイザーのすべての設定が正常に保存・反映されること
- [ ] カスタマイザープレビューでリロードなしの即時反映が動作すること
- [ ] メールアドレス入力でお問い合わせページが自動生成されること
- [ ] reCAPTCHA v3 のトークン取得・検証が動作すること
- [ ] カスタムフィールドの追加・削除・並び替えが動作すること
- [ ] JSON-LD 出力が schema.org バリデーターを通過すること
- [ ] SNS リンクが設定値に従い表示・非表示されること
- [ ] フォント変更で Google Fonts が正しく読み込まれること

### 18.2 UI/UX テスト

- [ ] PC / タブレット / スマートフォン（iOS Safari, Android Chrome）での表示確認
- [ ] ブラウザ: Chrome, Firefox, Safari, Edge（最新2バージョン）
- [ ] ダークモード対応（`prefers-color-scheme: dark`）の確認
- [ ] `prefers-reduced-motion` でアニメーションが停止すること

### 18.3 パフォーマンステスト

- [ ] Google PageSpeed Insights でモバイル/デスクトップ 90 点以上
- [ ] GTmetrix Grade A 相当
- [ ] WebPageTest TTFB < 800ms（ローカル環境除く）

### 18.4 WordPress 準拠テスト

- [ ] Theme Check Plugin でエラー 0 件
- [ ] WordPress Coding Standards（PHPCS）でエラー 0 件
- [ ] WordPress 6.5 〜最新バージョンで動作確認
- [ ] PHP 8.1 / 8.2 / 8.3 で動作確認
- [ ] `WP_DEBUG=true` でエラー・警告 0 件

### 18.5 アクセシビリティテスト

- [ ] axe DevTools による自動検査でエラー 0 件
- [ ] NVDA + Chrome でのスクリーンリーダーテスト
- [ ] キーボードのみによる全操作の確認
- [ ] WAVE Tool による検査

### 18.6 セキュリティテスト

- [ ] WPScan による既知脆弱性チェック
- [ ] すべての入力に対するサニタイズ・エスケープの確認
- [ ] XSS: 全フォームフィールドへの悪意あるスクリプト入力テスト
- [ ] CSRF: nonce が正しく機能することの確認

---

## 19. 商用配布チェックリスト

### 19.1 WordPress.org テーマディレクトリ要件

- [ ] `style.css` に必須ヘッダー情報（Theme Name, Theme URI, Author, Author URI, Description, Version, License, License URI, Text Domain, Tags）を記述
- [ ] `screenshot.png`（880×660px）を同梱
- [ ] GPL v2 以上のライセンス
- [ ] 外部サービス利用時のプライバシーポリシー記載（reCAPTCHA, Google Fonts）
- [ ] `readme.txt` に更新履歴・要件・インストール方法を記述
- [ ] 非推奨関数・削除済み関数の不使用
- [ ] prefixed 名前空間（`wte_` プレフィックス）の徹底

### 19.2 style.css ヘッダー

```css
/*!
 * Theme Name:  WTE Business Card
 * Theme URI:   https://example.com/wte-business-card
 * Author:      Your Name
 * Author URI:  https://example.com
 * Description: A minimal, elegant one-page business card theme for freelancers and sole proprietors. Configure everything from the Customizer without touching the block editor.
 * Version:     1.0.0
 * Requires at least: 6.5
 * Requires PHP: 8.1
 * License:     GNU General Public License v2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: wte_
 * Tags:        one-column, custom-colors, custom-logo, custom-menu, featured-images, full-width-template, microformats, sticky-post, theme-options, translation-ready, blog, portfolio, business
 */
```

### 19.3 外部サービスの開示（readme.txt に記載必須）

**Google Fonts**:
- 目的: カスタムフォントの読み込み
- 読み込み元: `https://fonts.googleapis.com`, `https://fonts.gstatic.com`
- プライバシーポリシー: https://policies.google.com/privacy

**Google reCAPTCHA v3**:
- 目的: スパム防止のためのボット検証
- 読み込み元: `https://www.google.com/recaptcha/`
- 設定: サイトキー・シークレットキーはユーザー自身が取得・設定
- プライバシーポリシー: https://policies.google.com/privacy

**Google Analytics**:
- 目的: アクセス解析（オプション）
- 読み込み元: `https://www.googletagmanager.com`
- 設定: 計測 ID はユーザー自身が取得・設定

---

## 20. ディレクトリ構造

```
wte-business-card/                          # テーマルートディレクトリ
│
├── style.css                               # テーマ識別ヘッダー（スタイルは含めない）
├── index.php                               # フォールバックテンプレート
├── functions.php                           # オーケストレーターのみ（require のみ記述）
├── front-page.php                          # フロントページテンプレート（LP）
├── page.php                                # 通常固定ページテンプレート
├── page-contact.php                        # お問い合わせページテンプレート
├── header.php                              # ヘッダーテンプレート
├── footer.php                              # フッターテンプレート
├── sidebar.php                             # サイドバー（本テーマでは未使用・互換性のため残置）
├── 404.php                                 # 404 ページ
├── screenshot.png                          # テーマスクリーンショット（880×660px）
├── readme.txt                              # README（外部サービス開示含む）
│
├── inc/                                    # PHP ロジック
│   ├── setup/                              # テーマ基本設定
│   │   ├── theme-support.php               # add_theme_support / 定数定義
│   │   ├── enqueue.php                     # CSS / JS 登録・エンキュー
│   │   └── setup-pages.php                 # ページ自動生成 / テーマ有効化処理
│   │
│   ├── customizer/                         # Customizer 関連
│   │   ├── panels.php                      # パネル定義
│   │   ├── sections.php                    # セクション定義
│   │   ├── settings.php                    # 設定定義（sanitize_callback 含む）
│   │   ├── controls.php                    # コントロール定義（カスタムコントロール登録含む）
│   │   ├── callbacks.php                   # サニタイズ・コールバック関数群
│   │   ├── output.php                      # 動的 CSS インライン出力（wp_head）
│   │   └── controls/                       # カスタムコントロールクラス
│   │       ├── class-range-control.php     # スライダーコントロール
│   │       ├── class-email-control.php     # email 型コントロール
│   │       ├── class-tel-control.php       # tel 型コントロール
│   │       ├── class-separator-control.php # 区切り線コントロール
│   │       ├── class-info-control.php      # 情報表示コントロール
│   │       ├── class-toggle-control.php    # トグルスイッチコントロール
│   │       └── class-icon-select-control.php # SVG アイコン選択コントロール
│   │
│   ├── meta/                               # カスタムフィールド・メタボックス
│   │   ├── custom-fields.php               # カスタムフィールド定義・ヘルパー関数
│   │   └── meta-boxes.php                  # メタボックス登録・表示・保存
│   │
│   ├── seo/                                # SEO 関連
│   │   ├── json-ld.php                     # JSON-LD 構造化データ生成・出力
│   │   └── meta-tags.php                   # meta タグ / OGP / Twitter Card 出力
│   │
│   ├── nav/                                # ナビゲーション
│   │   └── nav-menus.php                   # メニュー登録・ウォーカー定義
│   │
│   ├── widgets/                            # ウィジェット
│   │   └── widget-areas.php                # ウィジェットエリア登録（最小限）
│   │
│   ├── security/                           # セキュリティ
│   │   ├── security.php                    # セキュリティ強化全般
│   │   └── recaptcha.php                   # reCAPTCHA v3 Ajax ハンドラ
│   │
│   └── helpers/                            # ユーティリティ関数
│       ├── sanitize.php                    # 追加サニタイズヘルパー
│       ├── escape.php                      # 追加エスケープヘルパー
│       └── schema.php                      # JSON-LD スキーマビルダー関数群
│
├── template-parts/                         # テンプレートパーツ（get_template_part で呼出）
│   ├── layout/                             # レイアウト共通部品
│   │   ├── header-inner.php                # ヘッダー内部（ロゴ・ナビ・CTA）
│   │   ├── footer-inner.php                # フッター内部（著作権・SNS・リンク）
│   │   └── skip-link.php                   # スキップリンク（アクセシビリティ）
│   │
│   ├── sections/                           # LP セクション
│   │   ├── hero.php                        # ヒーローセクション
│   │   ├── services.php                    # サービスセクション
│   │   ├── appeal.php                      # アピールポイントセクション
│   │   ├── profile.php                     # 運営者情報セクション
│   │   ├── greeting.php                    # 代表挨拶セクション
│   │   ├── business-details.php            # 事業詳細セクション（カスタムフィールド出力）
│   │   ├── faq.php                         # FAQ セクション
│   │   ├── sns.php                         # SNS 導線セクション
│   │   └── contact-cta.php                 # お問い合わせCTAセクション
│   │
│   └── components/                         # 再利用可能コンポーネント
│       ├── sns-icons.php                   # SNS アイコンリスト（header/footer 共用）
│       ├── section-header.php              # セクション見出し共通コンポーネント
│       ├── btn.php                         # ボタンコンポーネント
│       └── email-protect.php               # スパム防止メールアドレス表示
│
├── assets/                                 # フロントエンドアセット（ビルド成果物）
│   ├── css/                                # スタイルシート
│   │   ├── main.css                        # メインスタイル（全体）
│   │   ├── variables.css                   # CSS カスタムプロパティ定義
│   │   ├── reset.css                       # CSS リセット（modern-normalize ベース）
│   │   ├── typography.css                  # タイポグラフィ
│   │   ├── layout.css                      # グリッド・コンテナ・レイアウト
│   │   ├── header.css                      # ヘッダースタイル
│   │   ├── footer.css                      # フッタースタイル
│   │   ├── hero.css                        # ヒーローセクション
│   │   ├── services.css                    # サービスセクション
│   │   ├── appeal.css                      # アピールセクション
│   │   ├── profile.css                     # プロフィールセクション
│   │   ├── greeting.css                    # 挨拶セクション
│   │   ├── business-details.css            # 事業詳細セクション
│   │   ├── faq.css                         # FAQ セクション
│   │   ├── sns.css                         # SNS 導線
│   │   ├── contact.css                     # お問い合わせページ
│   │   ├── buttons.css                     # ボタンスタイル
│   │   ├── forms.css                       # フォーム要素スタイル
│   │   ├── animations.css                  # アニメーション・トランジション
│   │   ├── utilities.css                   # ユーティリティクラス
│   │   ├── print.css                       # 印刷スタイル
│   │   └── rtl.css                         # RTL（右書き言語）対応
│   │
│   ├── js/                                 # JavaScript
│   │   ├── main.js                         # メイン JS（ページ全体の初期化）
│   │   ├── header.js                       # ヘッダー（スクロール固定・モバイルメニュー）
│   │   ├── smooth-scroll.js                # スムーススクロール
│   │   ├── scroll-animation.js             # スクロールアニメーション（IntersectionObserver）
│   │   ├── faq-accordion.js                # FAQ アコーディオン（details/summary 補完）
│   │   ├── email-protect.js                # メールアドレスのデコード・表示
│   │   ├── recaptcha-contact.js            # reCAPTCHA v3 連携（お問い合わせページ専用）
│   │   ├── customizer-preview.js           # カスタマイザープレビュー（postMessage）
│   │   └── admin/                          # 管理画面専用 JS
│   │       ├── admin-repeater.js           # 繰り返しフィールド操作
│   │       └── admin-media-upload.js       # メディアライブラリ連携
│   │
│   ├── css/admin/                          # 管理画面専用 CSS
│   │   ├── meta-boxes.css                  # メタボックススタイル
│   │   └── customizer-controls.css         # カスタマイザーコントロールスタイル
│   │
│   └── images/                             # 静的画像
│       ├── sns-sprite.svg                  # SNS アイコン SVG スプライト
│       ├── icon-sprite.svg                 # UI アイコン SVG スプライト
│       ├── default-avatar.svg              # デフォルトプロフィール写真
│       ├── logo-placeholder.svg            # ロゴ未設定時プレースホルダー
│       └── hero-fallback.svg               # ヒーロー画像未設定時フォールバック
│
├── languages/                              # 翻訳ファイル
│   ├── wte_.pot                            # 翻訳テンプレート
│   ├── wte_-ja.po                          # 日本語翻訳ソース
│   └── wte_-ja.mo                          # 日本語翻訳バイナリ
│
└── src/                                    # ビルドソース（配布物には含めない）
    ├── css/                                # PostCSS ソース
    │   └── [assets/css/ と同構造]
    ├── js/                                 # ES2022+ ソース
    │   └── [assets/js/ と同構造]
    ├── package.json                        # npm 設定
    ├── vite.config.js                      # Vite ビルド設定
    ├── postcss.config.js                   # PostCSS 設定
    └── .phpcs.xml                          # PHP_CodeSniffer 設定
```

---

## 付録 A: AI エージェントへの実装指示補足

### A.1 実装の優先順位

AI エージェントは以下の順番で実装を進めること：

```
Priority 1（コア基盤）:
  1. style.css ヘッダー + index.php
  2. inc/setup/theme-support.php（定数・テーマサポート）
  3. inc/setup/enqueue.php（基本アセット登録）
  4. assets/css/variables.css + reset.css + typography.css
  5. header.php + footer.php

Priority 2（カスタマイザー）:
  6. inc/customizer/callbacks.php（サニタイズ関数全部）
  7. inc/customizer/panels.php + sections.php
  8. inc/customizer/settings.php（全設定）
  9. inc/customizer/controls.php + controls/ 配下クラス
  10. inc/customizer/output.php（動的 CSS）
  11. assets/js/customizer-preview.js

Priority 3（LP テンプレート）:
  12. front-page.php + 全 template-parts/sections/
  13. 各セクション CSS

Priority 4（カスタムフィールド）:
  14. inc/meta/meta-boxes.php
  15. assets/js/admin/admin-repeater.js

Priority 5（自動化・SEO）:
  16. inc/setup/setup-pages.php（ページ自動生成）
  17. inc/seo/json-ld.php + inc/helpers/schema.php
  18. inc/seo/meta-tags.php

Priority 6（セキュリティ・お問い合わせ）:
  19. inc/security/security.php + recaptcha.php
  20. page-contact.php + template-parts/components/email-protect.php
  21. assets/js/recaptcha-contact.js

Priority 7（完成・品質）:
  22. 翻訳対応（全テキストのラップ確認）
  23. アクセシビリティ対応確認
  24. 404.php + print.css + rtl.css
```

### A.2 絶対に避けるべき実装パターン

以下のアンチパターンは商用配布クオリティを損なうため、絶対に実装してはならない：

1. **jQuery の使用** — バニラ JS で実装する
2. **外部 CSS フレームワーク（Bootstrap, Tailwind など）** — 独自 CSS のみ
3. **`!important` の乱用** — Customizer 動的スタイルのみ許容
4. **ハードコードされた URL** — `get_template_directory_uri()` / `home_url()` を使用
5. **サニタイズなしの直接出力** — 全出力に適切なエスケープ関数
6. **`echo $_POST[...]` 等の直接出力** — 絶対禁止
7. **管理画面以外での `WP_DEBUG` 依存コード** — 条件分岐で分離
8. **`global $wpdb` の直接 SQL** — `$wpdb->prepare()` を使用
9. **ブロックエディター関連コードの混入** — Classic Theme として完全分離
10. **フロントエンドへのシークレットキー出力** — reCAPTCHA シークレットは PHP のみ

### A.3 動作確認用テストデータ

テーマ有効化直後、カスタマイザーに以下のデフォルト値が反映されていること：

```
サイトタイトル: [WordPress 設定から取得]
メインキャッチコピー: 「あなたのビジネスを、ここから始めよう」
サービスセクション見出し: 「サービス」
アピールセクション見出し: 「選ばれる理由」
代表挨拶見出し: 「代表よりご挨拶」
お問い合わせページタイトル: 「お問い合わせ」
ヘッダーCTAテキスト: 「お問い合わせ」
一次CTAテキスト: 「詳しく見る」
二次CTAテキスト: 「お問い合わせ」
フォント（本文）: Noto Sans JP
フォント（見出し）: Noto Serif JP
アクセントカラー: #2d6be4
```

---

*本仕様書は WTE Business Card テーマ v1.0.0 の完全設計仕様である。本書に記載されていない実装詳細については、WordPress コーディング標準および本書の設計思想・原則に従い判断すること。*
